ui/vnc: optimize dirty bitmap tracking

vnc_update_client currently scans the dirty bitmap of each client
bitwise which is a very costly operation if only few bits are dirty.
vnc_refresh_server_surface does almost the same.
this patch optimizes both by utilizing the heavily optimized
function find_next_bit to find the offset of the next dirty
bit in the dirty bitmaps.

The following artifical test (just the bitmap operation part) running
vnc_update_client 65536 times on a 2560x2048 surface illustrates the
performance difference:

All bits clean - vnc_update_client_new: 0.07 secs
 vnc_update_client_old: 10.98 secs

All bits dirty - vnc_update_client_new: 11.26 secs
 vnc_update_client_old: 20.19 secs

Few bits dirty - vnc_update_client_new: 0.08 secs
 vnc_update_client_old: 10.98 secs

The case for all bits dirty is still rather slow, this
is due to the implementation of find_and_clear_dirty_height.
This will be addresses in a separate patch.

Signed-off-by: Peter Lieven <pl@kamp.de>
Reviewed-by: Wenchao Xia <xiawenc@linux.vnet.ibm.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
This commit is contained in:
Peter Lieven 2014-01-08 10:08:35 +01:00 committed by Gerd Hoffmann
parent 6cd859aa8a
commit 12b316d4c1
2 changed files with 88 additions and 71 deletions

155
ui/vnc.c
View File

@ -572,6 +572,15 @@ void *vnc_server_fb_ptr(VncDisplay *vd, int x, int y)
ptr += x * VNC_SERVER_FB_BYTES; ptr += x * VNC_SERVER_FB_BYTES;
return ptr; return ptr;
} }
/* this sets only the visible pixels of a dirty bitmap */
#define VNC_SET_VISIBLE_PIXELS_DIRTY(bitmap, w, h) {\
int y;\
memset(bitmap, 0x00, sizeof(bitmap));\
for (y = 0; y < h; y++) {\
bitmap_set(bitmap[y], 0,\
DIV_ROUND_UP(w, VNC_DIRTY_PIXELS_PER_BIT));\
} \
}
static void vnc_dpy_switch(DisplayChangeListener *dcl, static void vnc_dpy_switch(DisplayChangeListener *dcl,
DisplaySurface *surface) DisplaySurface *surface)
@ -597,7 +606,9 @@ static void vnc_dpy_switch(DisplayChangeListener *dcl,
qemu_pixman_image_unref(vd->guest.fb); qemu_pixman_image_unref(vd->guest.fb);
vd->guest.fb = pixman_image_ref(surface->image); vd->guest.fb = pixman_image_ref(surface->image);
vd->guest.format = surface->format; vd->guest.format = surface->format;
memset(vd->guest.dirty, 0xFF, sizeof(vd->guest.dirty)); VNC_SET_VISIBLE_PIXELS_DIRTY(vd->guest.dirty,
surface_width(vd->ds),
surface_height(vd->ds));
QTAILQ_FOREACH(vs, &vd->clients, next) { QTAILQ_FOREACH(vs, &vd->clients, next) {
vnc_colordepth(vs); vnc_colordepth(vs);
@ -605,7 +616,9 @@ static void vnc_dpy_switch(DisplayChangeListener *dcl,
if (vs->vd->cursor) { if (vs->vd->cursor) {
vnc_cursor_define(vs); vnc_cursor_define(vs);
} }
memset(vs->dirty, 0xFF, sizeof(vs->dirty)); VNC_SET_VISIBLE_PIXELS_DIRTY(vs->dirty,
surface_width(vd->ds),
surface_height(vd->ds));
} }
} }
@ -884,10 +897,9 @@ static int vnc_update_client(VncState *vs, int has_dirty, bool sync)
VncDisplay *vd = vs->vd; VncDisplay *vd = vs->vd;
VncJob *job; VncJob *job;
int y; int y;
int width, height; int height;
int n = 0; int n = 0;
if (vs->output.offset && !vs->audio_cap && !vs->force_update) if (vs->output.offset && !vs->audio_cap && !vs->force_update)
/* kernel send buffers are full -> drop frames to throttle */ /* kernel send buffers are full -> drop frames to throttle */
return 0; return 0;
@ -903,39 +915,27 @@ static int vnc_update_client(VncState *vs, int has_dirty, bool sync)
*/ */
job = vnc_job_new(vs); job = vnc_job_new(vs);
width = MIN(pixman_image_get_width(vd->server), vs->client_width);
height = MIN(pixman_image_get_height(vd->server), vs->client_height); height = MIN(pixman_image_get_height(vd->server), vs->client_height);
for (y = 0; y < height; y++) { y = 0;
int x; for (;;) {
int last_x = -1; int x, h;
for (x = 0; x < width / VNC_DIRTY_PIXELS_PER_BIT; x++) { unsigned long x2;
if (test_and_clear_bit(x, vs->dirty[y])) { unsigned long offset = find_next_bit((unsigned long *) &vs->dirty,
if (last_x == -1) { height * VNC_DIRTY_BPL(vs),
last_x = x; y * VNC_DIRTY_BPL(vs));
} if (offset == height * VNC_DIRTY_BPL(vs)) {
} else { /* no more dirty bits */
if (last_x != -1) { break;
int h = find_and_clear_dirty_height(vs, y, last_x, x,
height);
n += vnc_job_add_rect(job,
last_x * VNC_DIRTY_PIXELS_PER_BIT,
y,
(x - last_x) *
VNC_DIRTY_PIXELS_PER_BIT,
h);
}
last_x = -1;
}
}
if (last_x != -1) {
int h = find_and_clear_dirty_height(vs, y, last_x, x, height);
n += vnc_job_add_rect(job, last_x * VNC_DIRTY_PIXELS_PER_BIT,
y,
(x - last_x) * VNC_DIRTY_PIXELS_PER_BIT,
h);
} }
y = offset / VNC_DIRTY_BPL(vs);
x = offset % VNC_DIRTY_BPL(vs);
x2 = find_next_zero_bit((unsigned long *) &vs->dirty[y],
VNC_DIRTY_BPL(vs), x);
bitmap_clear(vs->dirty[y], x, x2 - x);
h = find_and_clear_dirty_height(vs, y, x, x2, height);
n += vnc_job_add_rect(job, x * VNC_DIRTY_PIXELS_PER_BIT, y,
(x2 - x) * VNC_DIRTY_PIXELS_PER_BIT, h);
} }
vnc_job_push(job); vnc_job_push(job);
@ -2660,8 +2660,8 @@ static int vnc_refresh_server_surface(VncDisplay *vd)
int width = pixman_image_get_width(vd->guest.fb); int width = pixman_image_get_width(vd->guest.fb);
int height = pixman_image_get_height(vd->guest.fb); int height = pixman_image_get_height(vd->guest.fb);
int y; int y;
uint8_t *guest_row; uint8_t *guest_row0 = NULL, *server_row0;
uint8_t *server_row; int guest_stride = 0, server_stride;
int cmp_bytes; int cmp_bytes;
VncState *vs; VncState *vs;
int has_dirty = 0; int has_dirty = 0;
@ -2686,44 +2686,57 @@ static int vnc_refresh_server_surface(VncDisplay *vd)
if (vd->guest.format != VNC_SERVER_FB_FORMAT) { if (vd->guest.format != VNC_SERVER_FB_FORMAT) {
int width = pixman_image_get_width(vd->server); int width = pixman_image_get_width(vd->server);
tmpbuf = qemu_pixman_linebuf_create(VNC_SERVER_FB_FORMAT, width); tmpbuf = qemu_pixman_linebuf_create(VNC_SERVER_FB_FORMAT, width);
} else {
guest_row0 = (uint8_t *)pixman_image_get_data(vd->guest.fb);
guest_stride = pixman_image_get_stride(vd->guest.fb);
} }
guest_row = (uint8_t *)pixman_image_get_data(vd->guest.fb); server_row0 = (uint8_t *)pixman_image_get_data(vd->server);
server_row = (uint8_t *)pixman_image_get_data(vd->server); server_stride = pixman_image_get_stride(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;
if (vd->guest.format != VNC_SERVER_FB_FORMAT) { y = 0;
qemu_pixman_linebuf_fill(tmpbuf, vd->guest.fb, width, 0, y); for (;;) {
guest_ptr = (uint8_t *)pixman_image_get_data(tmpbuf); int x;
} else { uint8_t *guest_ptr, *server_ptr;
guest_ptr = guest_row; unsigned long offset = find_next_bit((unsigned long *) &vd->guest.dirty,
} height * VNC_DIRTY_BPL(&vd->guest),
server_ptr = server_row; y * VNC_DIRTY_BPL(&vd->guest));
if (offset == height * VNC_DIRTY_BPL(&vd->guest)) {
for (x = 0; x + VNC_DIRTY_PIXELS_PER_BIT - 1 < width; /* no more dirty bits */
x += VNC_DIRTY_PIXELS_PER_BIT, guest_ptr += cmp_bytes, break;
server_ptr += cmp_bytes) {
if (!test_and_clear_bit((x / VNC_DIRTY_PIXELS_PER_BIT),
vd->guest.dirty[y])) {
continue;
}
if (memcmp(server_ptr, guest_ptr, cmp_bytes) == 0) {
continue;
}
memcpy(server_ptr, guest_ptr, cmp_bytes);
if (!vd->non_adaptive)
vnc_rect_updated(vd, x, y, &tv);
QTAILQ_FOREACH(vs, &vd->clients, next) {
set_bit((x / VNC_DIRTY_PIXELS_PER_BIT), vs->dirty[y]);
}
has_dirty++;
}
} }
guest_row += pixman_image_get_stride(vd->guest.fb); y = offset / VNC_DIRTY_BPL(&vd->guest);
server_row += pixman_image_get_stride(vd->server); x = offset % VNC_DIRTY_BPL(&vd->guest);
server_ptr = server_row0 + y * server_stride + x * cmp_bytes;
if (vd->guest.format != VNC_SERVER_FB_FORMAT) {
qemu_pixman_linebuf_fill(tmpbuf, vd->guest.fb, width, 0, y);
guest_ptr = (uint8_t *)pixman_image_get_data(tmpbuf);
} else {
guest_ptr = guest_row0 + y * guest_stride;
}
guest_ptr += x * cmp_bytes;
for (; x < DIV_ROUND_UP(width, VNC_DIRTY_PIXELS_PER_BIT);
x++, guest_ptr += cmp_bytes, server_ptr += cmp_bytes) {
if (!test_and_clear_bit(x, vd->guest.dirty[y])) {
continue;
}
if (memcmp(server_ptr, guest_ptr, cmp_bytes) == 0) {
continue;
}
memcpy(server_ptr, guest_ptr, cmp_bytes);
if (!vd->non_adaptive) {
vnc_rect_updated(vd, x * VNC_DIRTY_PIXELS_PER_BIT,
y, &tv);
}
QTAILQ_FOREACH(vs, &vd->clients, next) {
set_bit(x, vs->dirty[y]);
}
has_dirty++;
}
y++;
} }
qemu_pixman_image_unref(tmpbuf); qemu_pixman_image_unref(tmpbuf);
return has_dirty; return has_dirty;

View File

@ -88,6 +88,10 @@ typedef void VncSendHextileTile(VncState *vs,
/* VNC_DIRTY_BITS is the number of bits in the dirty bitmap. */ /* VNC_DIRTY_BITS is the number of bits in the dirty bitmap. */
#define VNC_DIRTY_BITS (VNC_MAX_WIDTH / VNC_DIRTY_PIXELS_PER_BIT) #define VNC_DIRTY_BITS (VNC_MAX_WIDTH / VNC_DIRTY_PIXELS_PER_BIT)
/* VNC_DIRTY_BPL (BPL = bits per line) might be greater than
* VNC_DIRTY_BITS due to alignment */
#define VNC_DIRTY_BPL(x) (sizeof((x)->dirty) / VNC_MAX_HEIGHT * BITS_PER_BYTE)
#define VNC_STAT_RECT 64 #define VNC_STAT_RECT 64
#define VNC_STAT_COLS (VNC_MAX_WIDTH / VNC_STAT_RECT) #define VNC_STAT_COLS (VNC_MAX_WIDTH / VNC_STAT_RECT)
#define VNC_STAT_ROWS (VNC_MAX_HEIGHT / VNC_STAT_RECT) #define VNC_STAT_ROWS (VNC_MAX_HEIGHT / VNC_STAT_RECT)