# HG changeset patch
# User Steven Smith <ssmith@xxxxxxxxxxxxx>
# Node ID 449dcaff25513813da8bcda94ffc85f3f14c5857
# Parent c742b2ae920c45c476a4c8b7fd28870df124071e
[HVM][VNC] Add a backoff feature to the vnc server, so that if it detects
that the display hasn't changed for a while it starts scanning more slowly.
Signed-off-by: Steven Smith <sos22@xxxxxxxxx>
---
tools/ioemu/vl.c | 6 +
tools/ioemu/vl.h | 1
tools/ioemu/vnc.c | 254 ++++++++++++++++++++++++++++++++----------------------
3 files changed, 158 insertions(+), 103 deletions(-)
diff -r c742b2ae920c -r 449dcaff2551 tools/ioemu/vl.c
--- a/tools/ioemu/vl.c Thu Sep 21 17:56:14 2006 +0100
+++ b/tools/ioemu/vl.c Thu Sep 21 18:29:48 2006 +0100
@@ -723,6 +723,12 @@ void qemu_del_timer(QEMUTimer *ts)
}
pt = &t->next;
}
+}
+
+void qemu_advance_timer(QEMUTimer *ts, int64_t expire_time)
+{
+ if (ts->expire_time > expire_time)
+ qemu_mod_timer(ts, expire_time);
}
/* modify the current timer so that it will be fired when current_time
diff -r c742b2ae920c -r 449dcaff2551 tools/ioemu/vl.h
--- a/tools/ioemu/vl.h Thu Sep 21 17:56:14 2006 +0100
+++ b/tools/ioemu/vl.h Thu Sep 21 18:29:48 2006 +0100
@@ -405,6 +405,7 @@ void qemu_free_timer(QEMUTimer *ts);
void qemu_free_timer(QEMUTimer *ts);
void qemu_del_timer(QEMUTimer *ts);
void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time);
+void qemu_advance_timer(QEMUTimer *ts, int64_t expire_time);
int qemu_timer_pending(QEMUTimer *ts);
extern int64_t ticks_per_sec;
diff -r c742b2ae920c -r 449dcaff2551 tools/ioemu/vnc.c
--- a/tools/ioemu/vnc.c Thu Sep 21 17:56:14 2006 +0100
+++ b/tools/ioemu/vnc.c Thu Sep 21 18:29:48 2006 +0100
@@ -27,7 +27,19 @@
#include "vl.h"
#include "qemu_socket.h"
-#define VNC_REFRESH_INTERVAL (1000 / 30)
+/* The refresh interval starts at BASE. If we scan the buffer and
+ find no change, we increase by INC, up to MAX. If the mouse moves
+ or we get a keypress, the interval is set back to BASE. If we find
+ an update, halve the interval.
+
+ All times in milliseconds. */
+#define VNC_REFRESH_INTERVAL_BASE 30
+#define VNC_REFRESH_INTERVAL_INC 50
+#define VNC_REFRESH_INTERVAL_MAX 2000
+
+/* Wait at most one second between updates, so that we can detect a
+ minimised vncviewer reasonably quickly. */
+#define VNC_MAX_UPDATE_INTERVAL 5000
#include "vnc_keysym.h"
#include "keymaps.c"
@@ -64,10 +76,11 @@ struct VncState
struct VncState
{
QEMUTimer *timer;
+ int timer_interval;
+ int64_t last_update_time;
int lsock;
int csock;
DisplayState *ds;
- int need_update;
int width;
int height;
uint64_t *dirty_row; /* screen regions which are possibly dirty */
@@ -97,8 +110,6 @@ struct VncState
int visible_y;
int visible_w;
int visible_h;
-
- int slow_client;
int ctl_keys; /* Ctrl+Alt starts calibration */
};
@@ -380,7 +391,7 @@ static void vnc_copy(DisplayState *ds, i
int y = 0;
int pitch = ds->linesize;
VncState *vs = ds->opaque;
- int updating_client = !vs->slow_client;
+ int updating_client = 1;
if (src_x < vs->visible_x || src_y < vs->visible_y ||
dst_x < vs->visible_x || dst_y < vs->visible_y ||
@@ -389,11 +400,6 @@ static void vnc_copy(DisplayState *ds, i
(dst_x + w) > (vs->visible_x + vs->visible_w) ||
(dst_y + h) > (vs->visible_y + vs->visible_h))
updating_client = 0;
-
- if (updating_client) {
- vs->need_update = 1;
- _vnc_update_client(vs);
- }
if (dst_y > src_y) {
y = h - 1;
@@ -445,110 +451,145 @@ static void _vnc_update_client(void *opa
static void _vnc_update_client(void *opaque)
{
VncState *vs = opaque;
- int64_t now = qemu_get_clock(rt_clock);
-
- if (vs->need_update && vs->csock != -1) {
- int y;
- char *row;
- char *old_row;
- uint64_t width_mask;
- int n_rectangles;
- int saved_offset;
- int maxx, maxy;
- int tile_bytes = vs->depth * DP2X(vs, 1);
-
- qemu_mod_timer(vs->timer, now + VNC_REFRESH_INTERVAL);
-
- if (vs->width != DP2X(vs, DIRTY_PIXEL_BITS))
- width_mask = (1ULL << X2DP_UP(vs, vs->ds->width)) - 1;
- else
- width_mask = ~(0ULL);
-
- /* Walk through the dirty map and eliminate tiles that
- really aren't dirty */
- row = vs->ds->data;
- old_row = vs->old_data;
-
- for (y = 0; y < vs->ds->height; y++) {
- if (vs->dirty_row[y] & width_mask) {
- int x;
- char *ptr, *old_ptr;
-
- ptr = row;
- old_ptr = old_row;
-
- for (x = 0; x < X2DP_UP(vs, vs->ds->width); x++) {
- if (vs->dirty_row[y] & (1ULL << x)) {
- if (memcmp(old_ptr, ptr, tile_bytes)) {
- vs->has_update = 1;
- vs->update_row[y] |= (1ULL << x);
- memcpy(old_ptr, ptr, tile_bytes);
- }
- vs->dirty_row[y] &= ~(1ULL << x);
+ int64_t now;
+ int y;
+ char *row;
+ char *old_row;
+ uint64_t width_mask;
+ int n_rectangles;
+ int saved_offset;
+ int maxx, maxy;
+ int tile_bytes = vs->depth * DP2X(vs, 1);
+
+ if (vs->csock == -1)
+ return;
+
+ now = qemu_get_clock(rt_clock);
+
+ if (vs->width != DP2X(vs, DIRTY_PIXEL_BITS))
+ width_mask = (1ULL << X2DP_UP(vs, vs->ds->width)) - 1;
+ else
+ width_mask = ~(0ULL);
+
+ /* Walk through the dirty map and eliminate tiles that really
+ aren't dirty */
+ row = vs->ds->data;
+ old_row = vs->old_data;
+
+ for (y = 0; y < vs->ds->height; y++) {
+ if (vs->dirty_row[y] & width_mask) {
+ int x;
+ char *ptr, *old_ptr;
+
+ ptr = row;
+ old_ptr = old_row;
+
+ for (x = 0; x < X2DP_UP(vs, vs->ds->width); x++) {
+ if (vs->dirty_row[y] & (1ULL << x)) {
+ if (memcmp(old_ptr, ptr, tile_bytes)) {
+ vs->has_update = 1;
+ vs->update_row[y] |= (1ULL << x);
+ memcpy(old_ptr, ptr, tile_bytes);
}
-
- ptr += tile_bytes;
- old_ptr += tile_bytes;
+ vs->dirty_row[y] &= ~(1ULL << x);
}
+
+ ptr += tile_bytes;
+ old_ptr += tile_bytes;
}
-
- row += vs->ds->linesize;
- old_row += vs->ds->linesize;
}
-
- if (!vs->has_update || vs->visible_y >= vs->ds->height ||
- vs->visible_x >= vs->ds->width)
- return;
-
- /* Count rectangles */
- n_rectangles = 0;
- vnc_write_u8(vs, 0); /* msg id */
- vnc_write_u8(vs, 0);
- saved_offset = vs->output.offset;
- vnc_write_u16(vs, 0);
-
- maxy = vs->visible_y + vs->visible_h;
- if (maxy > vs->ds->height)
- maxy = vs->ds->height;
- maxx = vs->visible_x + vs->visible_w;
- if (maxx > vs->ds->width)
- maxx = vs->ds->width;
-
- for (y = vs->visible_y; y < maxy; y++) {
- int x;
- int last_x = -1;
- for (x = X2DP_DOWN(vs, vs->visible_x);
- x < X2DP_UP(vs, maxx); x++) {
- if (vs->update_row[y] & (1ULL << x)) {
- if (last_x == -1)
- last_x = x;
- vs->update_row[y] &= ~(1ULL << x);
- } else {
- if (last_x != -1) {
- int h = find_update_height(vs, y, maxy, last_x, x);
+
+ row += vs->ds->linesize;
+ old_row += vs->ds->linesize;
+ }
+
+ if (!vs->has_update || vs->visible_y >= vs->ds->height ||
+ vs->visible_x >= vs->ds->width)
+ goto backoff;
+
+ /* Count rectangles */
+ n_rectangles = 0;
+ vnc_write_u8(vs, 0); /* msg id */
+ vnc_write_u8(vs, 0);
+ saved_offset = vs->output.offset;
+ vnc_write_u16(vs, 0);
+
+ maxy = vs->visible_y + vs->visible_h;
+ if (maxy > vs->ds->height)
+ maxy = vs->ds->height;
+ maxx = vs->visible_x + vs->visible_w;
+ if (maxx > vs->ds->width)
+ maxx = vs->ds->width;
+
+ for (y = vs->visible_y; y < maxy; y++) {
+ int x;
+ int last_x = -1;
+ for (x = X2DP_DOWN(vs, vs->visible_x);
+ x < X2DP_UP(vs, maxx); x++) {
+ if (vs->update_row[y] & (1ULL << x)) {
+ if (last_x == -1)
+ last_x = x;
+ vs->update_row[y] &= ~(1ULL << x);
+ } else {
+ if (last_x != -1) {
+ int h = find_update_height(vs, y, maxy, last_x, x);
+ if (h != 0) {
send_framebuffer_update(vs, DP2X(vs, last_x), y,
DP2X(vs, (x - last_x)), h);
n_rectangles++;
}
- last_x = -1;
}
+ last_x = -1;
}
- if (last_x != -1) {
- int h = find_update_height(vs, y, maxy, last_x, x);
+ }
+ if (last_x != -1) {
+ int h = find_update_height(vs, y, maxy, last_x, x);
+ if (h != 0) {
send_framebuffer_update(vs, DP2X(vs, last_x), y,
DP2X(vs, (x - last_x)), h);
n_rectangles++;
}
}
- vs->output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF;
- vs->output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
-
- vs->has_update = 0;
- vs->need_update = 0;
- vnc_flush(vs);
- vs->slow_client = 0;
- } else
- vs->slow_client = 1;
+ }
+ vs->output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF;
+ vs->output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
+
+ if (n_rectangles == 0)
+ goto backoff;
+
+ vs->has_update = 0;
+ vnc_flush(vs);
+ vs->last_update_time = now;
+
+ vs->timer_interval /= 2;
+ if (vs->timer_interval < VNC_REFRESH_INTERVAL_BASE)
+ vs->timer_interval = VNC_REFRESH_INTERVAL_BASE;
+
+ return;
+
+ backoff:
+ /* No update -> back off a bit */
+ vs->timer_interval += VNC_REFRESH_INTERVAL_INC;
+ if (vs->timer_interval > VNC_REFRESH_INTERVAL_MAX) {
+ vs->timer_interval = VNC_REFRESH_INTERVAL_MAX;
+ if (now - vs->last_update_time >= VNC_MAX_UPDATE_INTERVAL) {
+ /* Send a null update. If the client is no longer
+ interested (e.g. minimised) it'll ignore this, and we
+ can stop scanning the buffer until it sends another
+ update request. */
+ /* Note that there are bugs in xvncviewer which prevent
+ this from actually working. Leave the code in place
+ for correct clients. */
+ vnc_write_u8(vs, 0);
+ vnc_write_u8(vs, 0);
+ vnc_write_u16(vs, 0);
+ vnc_flush(vs);
+ vs->last_update_time = now;
+ return;
+ }
+ }
+ qemu_mod_timer(vs->timer, now + vs->timer_interval);
+ return;
}
static void vnc_update_client(void *opaque)
@@ -561,8 +602,10 @@ static void vnc_update_client(void *opaq
static void vnc_timer_init(VncState *vs)
{
- if (vs->timer == NULL)
+ if (vs->timer == NULL) {
vs->timer = qemu_new_timer(rt_clock, vnc_update_client, vs);
+ vs->timer_interval = VNC_REFRESH_INTERVAL_BASE;
+ }
}
static void vnc_dpy_refresh(DisplayState *ds)
@@ -622,7 +665,6 @@ static int vnc_client_io_error(VncState
vs->csock = -1;
buffer_reset(&vs->input);
buffer_reset(&vs->output);
- vs->need_update = 0;
return 0;
}
return ret;
@@ -892,7 +934,6 @@ static void framebuffer_update_request(V
int x_position, int y_position,
int w, int h)
{
- vs->need_update = 1;
if (!incremental)
framebuffer_set_updated(vs, x_position, y_position, w, h);
vs->visible_x = x_position;
@@ -1015,6 +1056,7 @@ static int protocol_client_msg(VncState
{
int i;
uint16_t limit;
+ int64_t now;
switch (data[0]) {
case 0:
@@ -1054,12 +1096,18 @@ static int protocol_client_msg(VncState
if (len == 1)
return 8;
+ vs->timer_interval = VNC_REFRESH_INTERVAL_BASE;
+ qemu_advance_timer(vs->timer,
+ qemu_get_clock(rt_clock) + vs->timer_interval);
key_event(vs, read_u8(data, 1), read_u32(data, 4));
break;
case 5:
if (len == 1)
return 6;
+ vs->timer_interval = VNC_REFRESH_INTERVAL_BASE;
+ qemu_advance_timer(vs->timer,
+ qemu_get_clock(rt_clock) + vs->timer_interval);
pointer_event(vs, read_u8(data, 1), read_u16(data, 2), read_u16(data,
4));
break;
case 6:
@@ -1268,7 +1316,7 @@ int vnc_start_viewer(int port)
exit(1);
case 0: /* child */
- execlp("vncviewer", "vncviewer", s, 0);
+ execlp("vncviewer", "vncviewer", s, NULL);
fprintf(stderr, "vncviewer execlp failed\n");
exit(1);
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|