diff options
Diffstat (limited to 'ui/spice-display.c')
-rw-r--r-- | ui/spice-display.c | 239 |
1 files changed, 167 insertions, 72 deletions
diff --git a/ui/spice-display.c b/ui/spice-display.c index 3e8f0b3ad5..dc7e58d0ed 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -16,15 +16,15 @@ */ #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 "ui/qemu-spice.h" +#include "qemu/timer.h" +#include "qemu/queue.h" +#include "monitor/monitor.h" +#include "ui/console.h" +#include "sysemu/sysemu.h" #include "trace.h" -#include "spice-display.h" +#include "ui/spice-display.h" static int debug = 0; @@ -126,46 +126,48 @@ void qemu_spice_wakeup(SimpleSpiceDisplay *ssd) ssd->worker->wakeup(ssd->worker); } -void qemu_spice_start(SimpleSpiceDisplay *ssd) +static int spice_display_is_running; + +void qemu_spice_display_start(void) +{ + spice_display_is_running = true; +} + +void qemu_spice_display_stop(void) { - trace_qemu_spice_start(ssd->qxl.id); - ssd->worker->start(ssd->worker); + spice_display_is_running = false; } -void qemu_spice_stop(SimpleSpiceDisplay *ssd) +int qemu_spice_display_is_running(SimpleSpiceDisplay *ssd) { - trace_qemu_spice_stop(ssd->qxl.id); - ssd->worker->stop(ssd->worker); + return spice_display_is_running; } -static SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *ssd) +static void qemu_spice_create_one_update(SimpleSpiceDisplay *ssd, + QXLRect *rect) { SimpleSpiceUpdate *update; QXLDrawable *drawable; QXLImage *image; QXLCommand *cmd; - uint8_t *src, *dst; - int by, bw, bh; + int bw, bh; struct timespec time_space; - - if (qemu_spice_rect_is_empty(&ssd->dirty)) { - return NULL; - }; + pixman_image_t *dest; trace_qemu_spice_create_update( - ssd->dirty.left, ssd->dirty.right, - ssd->dirty.top, ssd->dirty.bottom); + rect->left, rect->right, + rect->top, rect->bottom); update = g_malloc0(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; + bw = rect->right - rect->left; + bh = rect->bottom - rect->top; update->bitmap = g_malloc(bw * bh * 4); - drawable->bbox = ssd->dirty; + drawable->bbox = *rect; drawable->clip.type = SPICE_CLIP_TYPE_NONE; drawable->effect = QXL_EFFECT_OPAQUE; drawable->release_info.id = (uintptr_t)update; @@ -193,31 +195,94 @@ static SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *ssd) 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); + dest = pixman_image_create_bits(PIXMAN_x8r8g8b8, bw, bh, + (void *)update->bitmap, bw * 4); + pixman_image_composite(PIXMAN_OP_SRC, ssd->surface, NULL, ssd->mirror, + rect->left, rect->top, 0, 0, + rect->left, rect->top, bw, bh); + pixman_image_composite(PIXMAN_OP_SRC, ssd->mirror, NULL, dest, + rect->left, rect->top, 0, 0, + 0, 0, bw, bh); + pixman_image_unref(dest); + + cmd->type = QXL_CMD_DRAW; + cmd->data = (uintptr_t)drawable; + + QTAILQ_INSERT_TAIL(&ssd->updates, update, next); +} + +static void qemu_spice_create_update(SimpleSpiceDisplay *ssd) +{ + static const int blksize = 32; + int blocks = (ds_get_width(ssd->ds) + blksize - 1) / blksize; + int dirty_top[blocks]; + int y, yoff, x, xoff, blk, bw; + int bpp = ds_get_bytes_per_pixel(ssd->ds); + uint8_t *guest, *mirror; + + if (qemu_spice_rect_is_empty(&ssd->dirty)) { + return; + }; + + if (ssd->surface == NULL) { + ssd->surface = pixman_image_ref(ds_get_image(ssd->ds)); + ssd->mirror = qemu_pixman_mirror_create(ds_get_format(ssd->ds), + ds_get_image(ssd->ds)); } - 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; + for (blk = 0; blk < blocks; blk++) { + dirty_top[blk] = -1; } - cmd->type = QXL_CMD_DRAW; - cmd->data = (uintptr_t)drawable; + guest = ds_get_data(ssd->ds); + mirror = (void *)pixman_image_get_data(ssd->mirror); + for (y = ssd->dirty.top; y < ssd->dirty.bottom; y++) { + yoff = y * ds_get_linesize(ssd->ds); + for (x = ssd->dirty.left; x < ssd->dirty.right; x += blksize) { + xoff = x * bpp; + blk = x / blksize; + bw = MIN(blksize, ssd->dirty.right - x); + if (memcmp(guest + yoff + xoff, + mirror + yoff + xoff, + bw * bpp) == 0) { + if (dirty_top[blk] != -1) { + QXLRect update = { + .top = dirty_top[blk], + .bottom = y, + .left = x, + .right = x + bw, + }; + qemu_spice_create_one_update(ssd, &update); + dirty_top[blk] = -1; + } + } else { + if (dirty_top[blk] == -1) { + dirty_top[blk] = y; + } + } + } + } + + for (x = ssd->dirty.left; x < ssd->dirty.right; x += blksize) { + blk = x / blksize; + bw = MIN(blksize, ssd->dirty.right - x); + if (dirty_top[blk] != -1) { + QXLRect update = { + .top = dirty_top[blk], + .bottom = ssd->dirty.bottom, + .left = x, + .right = x + bw, + }; + qemu_spice_create_one_update(ssd, &update); + dirty_top[blk] = -1; + } + } memset(&ssd->dirty, 0, sizeof(ssd->dirty)); - return update; } /* - * Called from spice server thread context (via interface_release_ressource) + * Called from spice server thread context (via interface_release_resource) * We do *not* hold the global qemu mutex here, so extra care is needed * when calling qemu functions. QEMU interfaces used: * - g_free (underlying glibc free is re-entrant). @@ -269,26 +334,16 @@ void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd) qemu_spice_destroy_primary_surface(ssd, 0, QXL_SYNC); } -void qemu_spice_vm_change_state_handler(void *opaque, int running, - RunState state) -{ - SimpleSpiceDisplay *ssd = opaque; - - if (running) { - ssd->running = true; - qemu_spice_start(ssd); - } else { - qemu_spice_stop(ssd); - ssd->running = false; - } -} - void qemu_spice_display_init_common(SimpleSpiceDisplay *ssd, DisplayState *ds) { ssd->ds = ds; qemu_mutex_init(&ssd->lock); + QTAILQ_INIT(&ssd->updates); ssd->mouse_x = -1; ssd->mouse_y = -1; + if (ssd->num_surfaces == 0) { + ssd->num_surfaces = 1024; + } ssd->bufsize = (16 * 1024 * 1024); ssd->buf = g_malloc(ssd->bufsize); } @@ -314,16 +369,22 @@ void qemu_spice_display_update(SimpleSpiceDisplay *ssd, void qemu_spice_display_resize(SimpleSpiceDisplay *ssd) { + SimpleSpiceUpdate *update; + dprint(1, "%s:\n", __FUNCTION__); memset(&ssd->dirty, 0, sizeof(ssd->dirty)); - qemu_pf_conv_put(ssd->conv); - ssd->conv = NULL; + if (ssd->surface) { + pixman_image_unref(ssd->surface); + ssd->surface = NULL; + pixman_image_unref(ssd->mirror); + ssd->mirror = NULL; + } qemu_mutex_lock(&ssd->lock); - if (ssd->update != NULL) { - qemu_spice_destroy_update(ssd, ssd->update); - ssd->update = NULL; + while ((update = QTAILQ_FIRST(&ssd->updates)) != NULL) { + QTAILQ_REMOVE(&ssd->updates, update, next); + qemu_spice_destroy_update(ssd, update); } qemu_mutex_unlock(&ssd->lock); qemu_spice_destroy_host_primary(ssd); @@ -336,12 +397,12 @@ void qemu_spice_display_resize(SimpleSpiceDisplay *ssd) void qemu_spice_cursor_refresh_unlocked(SimpleSpiceDisplay *ssd) { if (ssd->cursor) { - ssd->ds->cursor_define(ssd->cursor); + dpy_cursor_define(ssd->ds, ssd->cursor); cursor_put(ssd->cursor); ssd->cursor = NULL; } if (ssd->mouse_x != -1 && ssd->mouse_y != -1) { - ssd->ds->mouse_set(ssd->mouse_x, ssd->mouse_y, 1); + dpy_mouse_set(ssd->ds, ssd->mouse_x, ssd->mouse_y, 1); ssd->mouse_x = -1; ssd->mouse_y = -1; } @@ -353,8 +414,8 @@ void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd) vga_hw_update(); qemu_mutex_lock(&ssd->lock); - if (ssd->update == NULL) { - ssd->update = qemu_spice_create_update(ssd); + if (QTAILQ_EMPTY(&ssd->updates)) { + qemu_spice_create_update(ssd); ssd->notify++; } qemu_spice_cursor_refresh_unlocked(ssd); @@ -399,7 +460,7 @@ static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info) info->num_memslots_groups = NUM_MEMSLOTS_GROUPS; info->internal_groupslot_id = 0; info->qxl_ram_size = ssd->bufsize; - info->n_surfaces = NUM_SURFACES; + info->n_surfaces = ssd->num_surfaces; } static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext) @@ -411,9 +472,9 @@ static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext) dprint(3, "%s:\n", __FUNCTION__); qemu_mutex_lock(&ssd->lock); - if (ssd->update != NULL) { - update = ssd->update; - ssd->update = NULL; + update = QTAILQ_FIRST(&ssd->updates); + if (update != NULL) { + QTAILQ_REMOVE(&ssd->updates, update, next); *ext = update->ext; ret = true; } @@ -464,6 +525,37 @@ static int interface_flush_resources(QXLInstance *sin) return 0; } +static void interface_update_area_complete(QXLInstance *sin, + uint32_t surface_id, + QXLRect *dirty, uint32_t num_updated_rects) +{ + /* should never be called, used in qxl native mode only */ + fprintf(stderr, "%s: abort()\n", __func__); + abort(); +} + +/* 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(); +} + +static void interface_set_client_capabilities(QXLInstance *sin, + uint8_t client_present, + uint8_t caps[58]) +{ + dprint(3, "%s:\n", __func__); +} + +static int interface_client_monitors_config(QXLInstance *sin, + VDAgentMonitorsConfig *monitors_config) +{ + dprint(3, "%s:\n", __func__); + return 0; /* == not supported by guest */ +} + static const QXLInterface dpy_interface = { .base.type = SPICE_INTERFACE_QXL, .base.description = "qemu simple display", @@ -483,6 +575,10 @@ static const QXLInterface dpy_interface = { .req_cursor_notification = interface_req_cursor_notification, .notify_update = interface_notify_update, .flush_resources = interface_flush_resources, + .async_complete = interface_async_complete, + .update_area_complete = interface_update_area_complete, + .set_client_capabilities = interface_set_client_capabilities, + .client_monitors_config = interface_client_monitors_config, }; static SimpleSpiceDisplay sdpy; @@ -503,8 +599,8 @@ static void display_refresh(struct DisplayState *ds) } static DisplayChangeListener display_listener = { - .dpy_update = display_update, - .dpy_resize = display_resize, + .dpy_gfx_update = display_update, + .dpy_gfx_resize = display_resize, .dpy_refresh = display_refresh, }; @@ -512,13 +608,12 @@ void qemu_spice_display_init(DisplayState *ds) { assert(sdpy.ds == NULL); qemu_spice_display_init_common(&sdpy, ds); - register_displaychangelistener(ds, &display_listener); sdpy.qxl.base.sif = &dpy_interface.base; qemu_spice_add_interface(&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); + register_displaychangelistener(ds, &display_listener); } |