static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) { VGACommonState *vga = &qxl->vga; int i; if (qxl->guest_primary.resized) { qxl->guest_primary.resized = 0; qxl->guest_primary.data = qxl_phys2virt(qxl, qxl->guest_primary.surface.mem, MEMSLOT_GROUP_GUEST); if (!qxl->guest_primary.data) { return; } qxl_set_rect_to_surface(qxl, &qxl->dirty[0]); qxl->num_dirty_rects = 1; trace_qxl_render_guest_primary_resized( qxl->guest_primary.surface.width, qxl->guest_primary.surface.height, qxl->guest_primary.qxl_stride, qxl->guest_primary.bytes_pp, qxl->guest_primary.bits_pp); if (qxl->guest_primary.qxl_stride > 0) { qemu_free_displaysurface(vga->ds); vga->ds->surface = qemu_create_displaysurface_from (qxl->guest_primary.surface.width, qxl->guest_primary.surface.height, qxl->guest_primary.bits_pp, qxl->guest_primary.abs_stride, qxl->guest_primary.data); } else { qemu_resize_displaysurface(vga->ds, qxl->guest_primary.surface.width, qxl->guest_primary.surface.height); } dpy_resize(vga->ds); } if (!qxl->guest_primary.data) { return; } for (i = 0; i < qxl->num_dirty_rects; i++) { if (qemu_spice_rect_is_empty(qxl->dirty+i)) { break; } if (qxl->dirty[i].left < 0 || qxl->dirty[i].top < 0 || qxl->dirty[i].left > qxl->dirty[i].right || qxl->dirty[i].top > qxl->dirty[i].bottom || qxl->dirty[i].right > qxl->guest_primary.surface.width || qxl->dirty[i].bottom > qxl->guest_primary.surface.height) { continue; } qxl_blit(qxl, qxl->dirty+i); dpy_update(vga->ds, qxl->dirty[i].left, qxl->dirty[i].top, qxl->dirty[i].right - qxl->dirty[i].left, qxl->dirty[i].bottom - qxl->dirty[i].top); } qxl->num_dirty_rects = 0; }
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); }
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->conv == NULL) { PixelFormat dst = qemu_default_pixelformat(32); ssd->conv = qemu_pf_conv_get(&dst, &ssd->ds->surface->pf); assert(ssd->conv); } if (ssd->ds_mirror == NULL) { int size = ds_get_height(ssd->ds) * ds_get_linesize(ssd->ds); ssd->ds_mirror = g_malloc0(size); } for (blk = 0; blk < blocks; blk++) { dirty_top[blk] = -1; } guest = ds_get_data(ssd->ds); mirror = ssd->ds_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; } } } }
static void qemu_spice_create_update(SimpleSpiceDisplay *ssd) { static const int blksize = 32; int blocks = (surface_width(ssd->ds) + blksize - 1) / blksize; int dirty_top[blocks]; int y, yoff, x, xoff, blk, bw; int bpp = surface_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(ssd->ds->image); ssd->mirror = qemu_pixman_mirror_create(ssd->ds->format, ssd->ds->image); } for (blk = 0; blk < blocks; blk++) { dirty_top[blk] = -1; } guest = surface_data(ssd->ds); mirror = (void *)pixman_image_get_data(ssd->mirror); for (y = ssd->dirty.top; y < ssd->dirty.bottom; y++) { yoff = y * surface_stride(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; } } } }
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; if (qemu_spice_rect_is_empty(&ssd->dirty)) { ssd->notify++; } qemu_spice_rect_union(&ssd->dirty, &update_area); }
static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) { VGACommonState *vga = &qxl->vga; DisplaySurface *surface; int i; if (qxl->guest_primary.resized) { qxl->guest_primary.resized = 0; qxl->guest_primary.data = memory_region_get_ram_ptr(&qxl->vga.vram); qxl_set_rect_to_surface(qxl, &qxl->dirty[0]); qxl->num_dirty_rects = 1; trace_qxl_render_guest_primary_resized( qxl->guest_primary.surface.width, qxl->guest_primary.surface.height, qxl->guest_primary.qxl_stride, qxl->guest_primary.bytes_pp, qxl->guest_primary.bits_pp); if (qxl->guest_primary.qxl_stride > 0) { surface = qemu_create_displaysurface_from (qxl->guest_primary.surface.width, qxl->guest_primary.surface.height, qxl->guest_primary.bits_pp, qxl->guest_primary.abs_stride, qxl->guest_primary.data, false); } else { surface = qemu_create_displaysurface (qxl->guest_primary.surface.width, qxl->guest_primary.surface.height); } dpy_gfx_replace_surface(vga->con, surface); } for (i = 0; i < qxl->num_dirty_rects; i++) { if (qemu_spice_rect_is_empty(qxl->dirty+i)) { break; } qxl_blit(qxl, qxl->dirty+i); dpy_gfx_update(vga->con, qxl->dirty[i].left, qxl->dirty[i].top, qxl->dirty[i].right - qxl->dirty[i].left, qxl->dirty[i].bottom - qxl->dirty[i].top); } qxl->num_dirty_rects = 0; }
static SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *ssd) { SimpleSpiceUpdate *update; QXLDrawable *drawable; QXLImage *image; QXLCommand *cmd; uint8_t *src, *dst; int by, bw, bh; struct timespec time_space; if (qemu_spice_rect_is_empty(&ssd->dirty)) { return NULL; }; trace_qemu_spice_create_update( ssd->dirty.left, ssd->dirty.right, ssd->dirty.top, ssd->dirty.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; update->bitmap = g_malloc(bw * bh * 4); drawable->bbox = ssd->dirty; drawable->clip.type = SPICE_CLIP_TYPE_NONE; drawable->effect = QXL_EFFECT_OPAQUE; drawable->release_info.id = (uintptr_t)update; drawable->type = QXL_DRAW_COPY; drawable->surfaces_dest[0] = -1; drawable->surfaces_dest[1] = -1; drawable->surfaces_dest[2] = -1; clock_gettime(CLOCK_MONOTONIC, &time_space); /* time in milliseconds from epoch. */ drawable->mm_time = time_space.tv_sec * 1000 + time_space.tv_nsec / 1000 / 1000; drawable->u.copy.rop_descriptor = SPICE_ROPD_OP_PUT; drawable->u.copy.src_bitmap = (uintptr_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 = (uintptr_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 = (uintptr_t)drawable; memset(&ssd->dirty, 0, sizeof(ssd->dirty)); return update; }
void qxl_render_update(PCIQXLDevice *qxl) { VGACommonState *vga = &qxl->vga; QXLRect dirty[32], update; void *ptr; int i; if (qxl->guest_primary.resized) { qxl->guest_primary.resized = 0; if (qxl->guest_primary.flipped) { qemu_free(qxl->guest_primary.flipped); qxl->guest_primary.flipped = NULL; } qemu_free_displaysurface(vga->ds); qxl->guest_primary.data = qemu_get_ram_ptr(qxl->vga.vram_offset); if (qxl->guest_primary.stride < 0) { /* spice surface is upside down -> need extra buffer to flip */ qxl->guest_primary.stride = -qxl->guest_primary.stride; qxl->guest_primary.flipped = qemu_malloc(qxl->guest_primary.surface.width * qxl->guest_primary.stride); ptr = qxl->guest_primary.flipped; } else { ptr = qxl->guest_primary.data; } dprint(qxl, 1, "%s: %dx%d, stride %d, bpp %d, depth %d, flip %s\n", __FUNCTION__, qxl->guest_primary.surface.width, qxl->guest_primary.surface.height, qxl->guest_primary.stride, qxl->guest_primary.bytes_pp, qxl->guest_primary.bits_pp, qxl->guest_primary.flipped ? "yes" : "no"); vga->ds->surface = qemu_create_displaysurface_from(qxl->guest_primary.surface.width, qxl->guest_primary.surface.height, qxl->guest_primary.bits_pp, qxl->guest_primary.stride, ptr); dpy_resize(vga->ds); } if (!qxl->guest_primary.commands) { return; } qxl->guest_primary.commands = 0; update.left = 0; update.right = qxl->guest_primary.surface.width; update.top = 0; update.bottom = qxl->guest_primary.surface.height; memset(dirty, 0, sizeof(dirty)); qxl->ssd.worker->update_area(qxl->ssd.worker, 0, &update, dirty, ARRAY_SIZE(dirty), 1); for (i = 0; i < ARRAY_SIZE(dirty); i++) { if (qemu_spice_rect_is_empty(dirty+i)) { break; } if (qxl->guest_primary.flipped) { qxl_flip(qxl, dirty+i); } dpy_update(vga->ds, dirty[i].left, dirty[i].top, dirty[i].right - dirty[i].left, dirty[i].bottom - dirty[i].top); } }
void qxl_render_update(PCIQXLDevice *qxl) { VGACommonState *vga = &qxl->vga; QXLRect dirty[32], update; void *ptr; int i, redraw = 0; if (!is_buffer_shared(vga->ds->surface)) { dprint(qxl, 1, "%s: restoring shared displaysurface\n", __func__); qxl->guest_primary.resized++; qxl->guest_primary.commands++; redraw = 1; } if (qxl->guest_primary.resized) { qxl->guest_primary.resized = 0; if (qxl->guest_primary.flipped) { g_free(qxl->guest_primary.flipped); qxl->guest_primary.flipped = NULL; } qemu_free_displaysurface(vga->ds); qxl->guest_primary.data = memory_region_get_ram_ptr(&qxl->vga.vram); if (qxl->guest_primary.qxl_stride < 0) { /* spice surface is upside down -> need extra buffer to flip */ qxl->guest_primary.flipped = g_malloc(qxl->guest_primary.surface.width * qxl->guest_primary.abs_stride); ptr = qxl->guest_primary.flipped; } else { ptr = qxl->guest_primary.data; } dprint(qxl, 1, "%s: %dx%d, stride %d, bpp %d, depth %d, flip %s\n", __FUNCTION__, qxl->guest_primary.surface.width, qxl->guest_primary.surface.height, qxl->guest_primary.qxl_stride, qxl->guest_primary.bytes_pp, qxl->guest_primary.bits_pp, qxl->guest_primary.flipped ? "yes" : "no"); vga->ds->surface = qemu_create_displaysurface_from(qxl->guest_primary.surface.width, qxl->guest_primary.surface.height, qxl->guest_primary.bits_pp, qxl->guest_primary.abs_stride, ptr); dpy_resize(vga->ds); } if (!qxl->guest_primary.commands) { return; } qxl->guest_primary.commands = 0; update.left = 0; update.right = qxl->guest_primary.surface.width; update.top = 0; update.bottom = qxl->guest_primary.surface.height; memset(dirty, 0, sizeof(dirty)); qxl_spice_update_area(qxl, 0, &update, dirty, ARRAY_SIZE(dirty), 1, QXL_SYNC); if (redraw) { memset(dirty, 0, sizeof(dirty)); dirty[0] = update; } for (i = 0; i < ARRAY_SIZE(dirty); i++) { if (qemu_spice_rect_is_empty(dirty+i)) { break; } if (qxl->guest_primary.flipped) { qxl_flip(qxl, dirty+i); } dpy_update(vga->ds, dirty[i].left, dirty[i].top, dirty[i].right - dirty[i].left, dirty[i].bottom - dirty[i].top); } }