From 9f64916da20eea67121d544698676295bbb105a7 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 10 Oct 2012 13:29:43 +0200 Subject: pixman/vnc: use pixman images in vnc. The vnc code uses *three* DisplaySurfaces: First is the surface of the actual QemuConsole, usually the guest screen, but could also be a text console (monitor/serial reachable via Ctrl-Alt- keys). This is left as-is. Second is the current server's view of the screen content. The vnc code uses this to figure which parts of the guest screen did _really_ change to reduce the amount of updates sent to the vnc clients. It is also used as data source when sending out the updates to the clients. This surface gets replaced by a pixman image. The format changes too, instead of using the guest screen format we'll use fixed 32bit rgb framebuffer and convert the pixels on the fly when comparing and updating the server framebuffer. Third surface carries the format expected by the vnc client. That isn't used to store image data. This surface is switched to PixelFormat and a boolean for bigendian byte order. Signed-off-by: Gerd Hoffmann --- ui/vnc.c | 235 +++++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 132 insertions(+), 103 deletions(-) (limited to 'ui/vnc.c') diff --git a/ui/vnc.c b/ui/vnc.c index 0ae1c74..15aef86 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -436,6 +436,8 @@ static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h) int i; VncDisplay *vd = ds->opaque; struct VncSurface *s = &vd->guest; + int width = ds_get_width(ds); + int height = ds_get_height(ds); h += y; @@ -446,10 +448,10 @@ static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h) w += (x % 16); x -= (x % 16); - x = MIN(x, s->ds->width); - y = MIN(y, s->ds->height); - w = MIN(x + w, s->ds->width) - x; - h = MIN(h, s->ds->height); + x = MIN(x, width); + y = MIN(y, height); + w = MIN(x + w, width) - x; + h = MIN(h, height); for (; y < h; y++) for (i = 0; i < w; i += 16) @@ -550,6 +552,21 @@ static void vnc_abort_display_jobs(VncDisplay *vd) } } +int vnc_server_fb_stride(VncDisplay *vd) +{ + return pixman_image_get_stride(vd->server); +} + +void *vnc_server_fb_ptr(VncDisplay *vd, int x, int y) +{ + uint8_t *ptr; + + ptr = (uint8_t *)pixman_image_get_data(vd->server); + ptr += y * vnc_server_fb_stride(vd); + ptr += x * VNC_SERVER_FB_BYTES; + return ptr; +} + static void vnc_dpy_resize(DisplayState *ds) { VncDisplay *vd = ds->opaque; @@ -558,20 +575,20 @@ static void vnc_dpy_resize(DisplayState *ds) vnc_abort_display_jobs(vd); /* server surface */ - if (!vd->server) - vd->server = g_malloc0(sizeof(*vd->server)); - if (vd->server->data) - g_free(vd->server->data); - *(vd->server) = *(ds->surface); - vd->server->data = g_malloc0(vd->server->linesize * - vd->server->height); + qemu_pixman_image_unref(vd->server); + vd->server = pixman_image_create_bits(VNC_SERVER_FB_FORMAT, + ds_get_width(ds), + ds_get_height(ds), + NULL, 0); /* guest surface */ - if (!vd->guest.ds) - vd->guest.ds = g_malloc0(sizeof(*vd->guest.ds)); +#if 0 /* FIXME */ if (ds_get_bytes_per_pixel(ds) != vd->guest.ds->pf.bytes_per_pixel) console_color_init(ds); - *(vd->guest.ds) = *(ds->surface); +#endif + qemu_pixman_image_unref(vd->guest.fb); + vd->guest.fb = pixman_image_ref(ds->surface->image); + vd->guest.format = ds->surface->format; memset(vd->guest.dirty, 0xFF, sizeof(vd->guest.dirty)); QTAILQ_FOREACH(vs, &vd->clients, next) { @@ -585,7 +602,7 @@ static void vnc_dpy_resize(DisplayState *ds) } /* fastest code */ -static void vnc_write_pixels_copy(VncState *vs, struct PixelFormat *pf, +static void vnc_write_pixels_copy(VncState *vs, void *pixels, int size) { vnc_write(vs, pixels, size); @@ -595,23 +612,23 @@ static void vnc_write_pixels_copy(VncState *vs, struct PixelFormat *pf, void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v) { uint8_t r, g, b; - VncDisplay *vd = vs->vd; - r = ((((v & vd->server->pf.rmask) >> vd->server->pf.rshift) << vs->clientds.pf.rbits) >> - vd->server->pf.rbits); - g = ((((v & vd->server->pf.gmask) >> vd->server->pf.gshift) << vs->clientds.pf.gbits) >> - vd->server->pf.gbits); - b = ((((v & vd->server->pf.bmask) >> vd->server->pf.bshift) << vs->clientds.pf.bbits) >> - vd->server->pf.bbits); - v = (r << vs->clientds.pf.rshift) | - (g << vs->clientds.pf.gshift) | - (b << vs->clientds.pf.bshift); - switch(vs->clientds.pf.bytes_per_pixel) { +#if VNC_SERVER_FB_FORMAT == PIXMAN_FORMAT(32, PIXMAN_TYPE_ARGB, 0, 8, 8, 8) + r = (((v & 0x00ff0000) >> 16) << vs->client_pf.rbits) >> 8; + g = (((v & 0x0000ff00) >> 8) << vs->client_pf.gbits) >> 8; + b = (((v & 0x000000ff) >> 0) << vs->client_pf.bbits) >> 8; +#else +# error need some bits here if you change VNC_SERVER_FB_FORMAT +#endif + v = (r << vs->client_pf.rshift) | + (g << vs->client_pf.gshift) | + (b << vs->client_pf.bshift); + switch (vs->client_pf.bytes_per_pixel) { case 1: buf[0] = v; break; case 2: - if (vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) { + if (vs->client_be) { buf[0] = v >> 8; buf[1] = v; } else { @@ -621,7 +638,7 @@ void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v) break; default: case 4: - if (vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) { + if (vs->client_be) { buf[0] = v >> 24; buf[1] = v >> 16; buf[2] = v >> 8; @@ -636,37 +653,37 @@ void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v) } } -static void vnc_write_pixels_generic(VncState *vs, struct PixelFormat *pf, +static void vnc_write_pixels_generic(VncState *vs, void *pixels1, int size) { uint8_t buf[4]; - if (pf->bytes_per_pixel == 4) { + if (VNC_SERVER_FB_BYTES == 4) { uint32_t *pixels = pixels1; int n, i; n = size >> 2; - for(i = 0; i < n; i++) { + for (i = 0; i < n; i++) { vnc_convert_pixel(vs, buf, pixels[i]); - vnc_write(vs, buf, vs->clientds.pf.bytes_per_pixel); + vnc_write(vs, buf, vs->client_pf.bytes_per_pixel); } - } else if (pf->bytes_per_pixel == 2) { + } else if (VNC_SERVER_FB_BYTES == 2) { uint16_t *pixels = pixels1; int n, i; n = size >> 1; - for(i = 0; i < n; i++) { + for (i = 0; i < n; i++) { vnc_convert_pixel(vs, buf, pixels[i]); - vnc_write(vs, buf, vs->clientds.pf.bytes_per_pixel); + vnc_write(vs, buf, vs->client_pf.bytes_per_pixel); } - } else if (pf->bytes_per_pixel == 1) { + } else if (VNC_SERVER_FB_BYTES == 1) { uint8_t *pixels = pixels1; int n, i; n = size; - for(i = 0; i < n; i++) { + for (i = 0; i < n; i++) { vnc_convert_pixel(vs, buf, pixels[i]); - vnc_write(vs, buf, vs->clientds.pf.bytes_per_pixel); + vnc_write(vs, buf, vs->client_pf.bytes_per_pixel); } } else { - fprintf(stderr, "vnc_write_pixels_generic: VncState color depth not supported\n"); + fprintf(stderr, "%s: VncState color depth not supported\n", __func__); } } @@ -676,10 +693,10 @@ int vnc_raw_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) uint8_t *row; VncDisplay *vd = vs->vd; - row = vd->server->data + y * ds_get_linesize(vs->ds) + x * ds_get_bytes_per_pixel(vs->ds); + row = vnc_server_fb_ptr(vd, x, y); for (i = 0; i < h; i++) { - vs->write_pixels(vs, &vd->server->pf, row, w * ds_get_bytes_per_pixel(vs->ds)); - row += ds_get_linesize(vs->ds); + vs->write_pixels(vs, row, w * VNC_SERVER_FB_BYTES); + row += vnc_server_fb_stride(vd); } return 1; } @@ -736,7 +753,7 @@ static void vnc_dpy_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int VncState *vs, *vn; uint8_t *src_row; uint8_t *dst_row; - int i,x,y,pitch,depth,inc,w_lim,s; + int i, x, y, pitch, inc, w_lim, s; int cmp_bytes; vnc_refresh_server_surface(vd); @@ -749,10 +766,9 @@ static void vnc_dpy_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int } /* do bitblit op on the local surface too */ - pitch = ds_get_linesize(vd->ds); - depth = ds_get_bytes_per_pixel(vd->ds); - src_row = vd->server->data + pitch * src_y + depth * src_x; - dst_row = vd->server->data + pitch * dst_y + depth * dst_x; + pitch = vnc_server_fb_stride(vd); + src_row = vnc_server_fb_ptr(vd, src_x, src_y); + dst_row = vnc_server_fb_ptr(vd, dst_x, dst_y); y = dst_y; inc = 1; if (dst_y > src_y) { @@ -780,7 +796,7 @@ static void vnc_dpy_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int } else { s = 16; } - cmp_bytes = s * depth; + cmp_bytes = s * VNC_SERVER_FB_BYTES; if (memcmp(src_row, dst_row, cmp_bytes) == 0) continue; memmove(dst_row, src_row, cmp_bytes); @@ -790,8 +806,8 @@ static void vnc_dpy_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int } } } - src_row += pitch - w * depth; - dst_row += pitch - w * depth; + src_row += pitch - w * VNC_SERVER_FB_BYTES; + dst_row += pitch - w * VNC_SERVER_FB_BYTES; y += inc; } @@ -810,7 +826,6 @@ static void vnc_mouse_set(DisplayState *ds, int x, int y, int visible) static int vnc_cursor_define(VncState *vs) { QEMUCursor *c = vs->vd->cursor; - PixelFormat pf = qemu_default_pixelformat(32); int isize; if (vnc_has_feature(vs, VNC_FEATURE_RICH_CURSOR)) { @@ -820,8 +835,8 @@ static int vnc_cursor_define(VncState *vs) vnc_write_u16(vs, 1); /* # of rects */ vnc_framebuffer_update(vs, c->hot_x, c->hot_y, c->width, c->height, VNC_ENCODING_RICH_CURSOR); - isize = c->width * c->height * vs->clientds.pf.bytes_per_pixel; - vnc_write_pixels_generic(vs, &pf, c->data, isize); + isize = c->width * c->height * vs->client_pf.bytes_per_pixel; + vnc_write_pixels_generic(vs, c->data, isize); vnc_write(vs, vs->vd->cursor_mask, vs->vd->cursor_msize); vnc_unlock_output(vs); return 0; @@ -898,8 +913,8 @@ static int vnc_update_client(VncState *vs, int has_dirty) */ job = vnc_job_new(vs); - width = MIN(vd->server->width, vs->client_width); - height = MIN(vd->server->height, vs->client_height); + width = MIN(pixman_image_get_width(vd->server), vs->client_width); + height = MIN(pixman_image_get_height(vd->server), vs->client_height); for (y = 0; y < height; y++) { int x; @@ -1861,9 +1876,9 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) static void set_pixel_conversion(VncState *vs) { - if ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) == - (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG) && - !memcmp(&(vs->clientds.pf), &(vs->ds->surface->pf), sizeof(PixelFormat))) { + pixman_format_code_t fmt = qemu_pixman_get_format(&vs->client_pf); + + if (fmt == VNC_SERVER_FB_FORMAT) { vs->write_pixels = vnc_write_pixels_copy; vnc_hextile_set_pixel_conversion(vs, 0); } else { @@ -1883,23 +1898,22 @@ static void set_pixel_format(VncState *vs, return; } - vs->clientds = *(vs->vd->guest.ds); - vs->clientds.pf.rmax = red_max; - vs->clientds.pf.rbits = hweight_long(red_max); - vs->clientds.pf.rshift = red_shift; - vs->clientds.pf.rmask = red_max << red_shift; - vs->clientds.pf.gmax = green_max; - vs->clientds.pf.gbits = hweight_long(green_max); - vs->clientds.pf.gshift = green_shift; - vs->clientds.pf.gmask = green_max << green_shift; - vs->clientds.pf.bmax = blue_max; - vs->clientds.pf.bbits = hweight_long(blue_max); - vs->clientds.pf.bshift = blue_shift; - vs->clientds.pf.bmask = blue_max << blue_shift; - vs->clientds.pf.bits_per_pixel = bits_per_pixel; - vs->clientds.pf.bytes_per_pixel = bits_per_pixel / 8; - vs->clientds.pf.depth = bits_per_pixel == 32 ? 24 : bits_per_pixel; - vs->clientds.flags = big_endian_flag ? QEMU_BIG_ENDIAN_FLAG : 0x00; + vs->client_pf.rmax = red_max; + vs->client_pf.rbits = hweight_long(red_max); + vs->client_pf.rshift = red_shift; + vs->client_pf.rmask = red_max << red_shift; + vs->client_pf.gmax = green_max; + vs->client_pf.gbits = hweight_long(green_max); + vs->client_pf.gshift = green_shift; + vs->client_pf.gmask = green_max << green_shift; + vs->client_pf.bmax = blue_max; + vs->client_pf.bbits = hweight_long(blue_max); + vs->client_pf.bshift = blue_shift; + vs->client_pf.bmask = blue_max << blue_shift; + vs->client_pf.bits_per_pixel = bits_per_pixel; + vs->client_pf.bytes_per_pixel = bits_per_pixel / 8; + vs->client_pf.depth = bits_per_pixel == 32 ? 24 : bits_per_pixel; + vs->client_be = big_endian_flag; set_pixel_conversion(vs); @@ -1910,8 +1924,10 @@ static void set_pixel_format(VncState *vs, static void pixel_format_message (VncState *vs) { char pad[3] = { 0, 0, 0 }; - vnc_write_u8(vs, vs->ds->surface->pf.bits_per_pixel); /* bits-per-pixel */ - vnc_write_u8(vs, vs->ds->surface->pf.depth); /* depth */ + vs->client_pf = qemu_default_pixelformat(32); + + vnc_write_u8(vs, vs->client_pf.bits_per_pixel); /* bits-per-pixel */ + vnc_write_u8(vs, vs->client_pf.depth); /* depth */ #ifdef HOST_WORDS_BIGENDIAN vnc_write_u8(vs, 1); /* big-endian-flag */ @@ -1919,27 +1935,25 @@ static void pixel_format_message (VncState *vs) { vnc_write_u8(vs, 0); /* big-endian-flag */ #endif vnc_write_u8(vs, 1); /* true-color-flag */ - vnc_write_u16(vs, vs->ds->surface->pf.rmax); /* red-max */ - vnc_write_u16(vs, vs->ds->surface->pf.gmax); /* green-max */ - vnc_write_u16(vs, vs->ds->surface->pf.bmax); /* blue-max */ - vnc_write_u8(vs, vs->ds->surface->pf.rshift); /* red-shift */ - vnc_write_u8(vs, vs->ds->surface->pf.gshift); /* green-shift */ - vnc_write_u8(vs, vs->ds->surface->pf.bshift); /* blue-shift */ + vnc_write_u16(vs, vs->client_pf.rmax); /* red-max */ + vnc_write_u16(vs, vs->client_pf.gmax); /* green-max */ + vnc_write_u16(vs, vs->client_pf.bmax); /* blue-max */ + vnc_write_u8(vs, vs->client_pf.rshift); /* red-shift */ + vnc_write_u8(vs, vs->client_pf.gshift); /* green-shift */ + vnc_write_u8(vs, vs->client_pf.bshift); /* blue-shift */ + vnc_write(vs, pad, 3); /* padding */ vnc_hextile_set_pixel_conversion(vs, 0); - - vs->clientds = *(vs->ds->surface); - vs->clientds.flags &= ~QEMU_ALLOCATED_FLAG; vs->write_pixels = vnc_write_pixels_copy; - - vnc_write(vs, pad, 3); /* padding */ } static void vnc_dpy_setdata(DisplayState *ds) { VncDisplay *vd = ds->opaque; - *(vd->guest.ds) = *(ds->surface); + qemu_pixman_image_unref(vd->guest.fb); + vd->guest.fb = pixman_image_ref(ds->surface->image); + vd->guest.format = ds->surface->format; vnc_dpy_update(ds, 0, 0, ds_get_width(ds), ds_get_height(ds)); } @@ -2443,12 +2457,14 @@ static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y) static int vnc_update_stats(VncDisplay *vd, struct timeval * tv) { + int width = pixman_image_get_width(vd->guest.fb); + int height = pixman_image_get_height(vd->guest.fb); int x, y; struct timeval res; int has_dirty = 0; - for (y = 0; y < vd->guest.ds->height; y += VNC_STAT_RECT) { - for (x = 0; x < vd->guest.ds->width; x += VNC_STAT_RECT) { + for (y = 0; y < height; y += VNC_STAT_RECT) { + for (x = 0; x < width; x += VNC_STAT_RECT) { VncRectStat *rect = vnc_stat_rect(vd, x, y); rect->updated = false; @@ -2462,8 +2478,8 @@ static int vnc_update_stats(VncDisplay *vd, struct timeval * tv) } vd->guest.last_freq_check = *tv; - for (y = 0; y < vd->guest.ds->height; y += VNC_STAT_RECT) { - for (x = 0; x < vd->guest.ds->width; x += VNC_STAT_RECT) { + for (y = 0; y < height; y += VNC_STAT_RECT) { + for (x = 0; x < width; x += VNC_STAT_RECT) { VncRectStat *rect= vnc_stat_rect(vd, x, y); int count = ARRAY_SIZE(rect->times); struct timeval min, max; @@ -2532,12 +2548,15 @@ static void vnc_rect_updated(VncDisplay *vd, int x, int y, struct timeval * tv) static int vnc_refresh_server_surface(VncDisplay *vd) { + int width = pixman_image_get_width(vd->guest.fb); + int height = pixman_image_get_height(vd->guest.fb); int y; uint8_t *guest_row; uint8_t *server_row; int cmp_bytes; VncState *vs; int has_dirty = 0; + pixman_image_t *tmpbuf = NULL; struct timeval tv = { 0, 0 }; @@ -2551,22 +2570,31 @@ static int vnc_refresh_server_surface(VncDisplay *vd) * Check and copy modified bits from guest to server surface. * Update server dirty map. */ - cmp_bytes = 16 * ds_get_bytes_per_pixel(vd->ds); - if (cmp_bytes > vd->ds->surface->linesize) { - cmp_bytes = vd->ds->surface->linesize; + cmp_bytes = 64; + if (cmp_bytes > vnc_server_fb_stride(vd)) { + cmp_bytes = vnc_server_fb_stride(vd); + } + if (vd->guest.format != VNC_SERVER_FB_FORMAT) { + int width = pixman_image_get_width(vd->server); + tmpbuf = qemu_pixman_linebuf_create(VNC_SERVER_FB_FORMAT, width); } - guest_row = vd->guest.ds->data; - server_row = vd->server->data; - for (y = 0; y < vd->guest.ds->height; y++) { + guest_row = (uint8_t *)pixman_image_get_data(vd->guest.fb); + server_row = (uint8_t *)pixman_image_get_data(vd->server); + for (y = 0; y < height; y++) { if (!bitmap_empty(vd->guest.dirty[y], VNC_DIRTY_BITS)) { int x; uint8_t *guest_ptr; uint8_t *server_ptr; - guest_ptr = guest_row; + if (vd->guest.format != VNC_SERVER_FB_FORMAT) { + qemu_pixman_linebuf_fill(tmpbuf, vd->guest.fb, width, y); + guest_ptr = (uint8_t *)pixman_image_get_data(tmpbuf); + } else { + guest_ptr = guest_row; + } server_ptr = server_row; - for (x = 0; x + 15 < vd->guest.ds->width; + for (x = 0; x + 15 < width; x += 16, guest_ptr += cmp_bytes, server_ptr += cmp_bytes) { if (!test_and_clear_bit((x / 16), vd->guest.dirty[y])) continue; @@ -2581,9 +2609,10 @@ static int vnc_refresh_server_surface(VncDisplay *vd) has_dirty++; } } - guest_row += ds_get_linesize(vd->ds); - server_row += ds_get_linesize(vd->ds); + guest_row += pixman_image_get_stride(vd->guest.fb); + server_row += pixman_image_get_stride(vd->server); } + qemu_pixman_image_unref(tmpbuf); return has_dirty; } -- cgit v1.1