ia64/xen-unstable
changeset 11566:449dcaff2551
[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@cam.ac.uk>
that the display hasn't changed for a while it starts scanning more slowly.
Signed-off-by: Steven Smith <sos22@cam.ac.uk>
author | Steven Smith <ssmith@xensource.com> |
---|---|
date | Thu Sep 21 18:29:48 2006 +0100 (2006-09-21) |
parents | c742b2ae920c |
children | 8860eba3dcad ebb1982c03a1 |
files | tools/ioemu/vl.c tools/ioemu/vl.h tools/ioemu/vnc.c |
line diff
1.1 --- a/tools/ioemu/vl.c Thu Sep 21 17:56:14 2006 +0100 1.2 +++ b/tools/ioemu/vl.c Thu Sep 21 18:29:48 2006 +0100 1.3 @@ -725,6 +725,12 @@ void qemu_del_timer(QEMUTimer *ts) 1.4 } 1.5 } 1.6 1.7 +void qemu_advance_timer(QEMUTimer *ts, int64_t expire_time) 1.8 +{ 1.9 + if (ts->expire_time > expire_time) 1.10 + qemu_mod_timer(ts, expire_time); 1.11 +} 1.12 + 1.13 /* modify the current timer so that it will be fired when current_time 1.14 >= expire_time. The corresponding callback will be called. */ 1.15 void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time)
2.1 --- a/tools/ioemu/vl.h Thu Sep 21 17:56:14 2006 +0100 2.2 +++ b/tools/ioemu/vl.h Thu Sep 21 18:29:48 2006 +0100 2.3 @@ -405,6 +405,7 @@ QEMUTimer *qemu_new_timer(QEMUClock *clo 2.4 void qemu_free_timer(QEMUTimer *ts); 2.5 void qemu_del_timer(QEMUTimer *ts); 2.6 void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time); 2.7 +void qemu_advance_timer(QEMUTimer *ts, int64_t expire_time); 2.8 int qemu_timer_pending(QEMUTimer *ts); 2.9 2.10 extern int64_t ticks_per_sec;
3.1 --- a/tools/ioemu/vnc.c Thu Sep 21 17:56:14 2006 +0100 3.2 +++ b/tools/ioemu/vnc.c Thu Sep 21 18:29:48 2006 +0100 3.3 @@ -27,7 +27,19 @@ 3.4 #include "vl.h" 3.5 #include "qemu_socket.h" 3.6 3.7 -#define VNC_REFRESH_INTERVAL (1000 / 30) 3.8 +/* The refresh interval starts at BASE. If we scan the buffer and 3.9 + find no change, we increase by INC, up to MAX. If the mouse moves 3.10 + or we get a keypress, the interval is set back to BASE. If we find 3.11 + an update, halve the interval. 3.12 + 3.13 + All times in milliseconds. */ 3.14 +#define VNC_REFRESH_INTERVAL_BASE 30 3.15 +#define VNC_REFRESH_INTERVAL_INC 50 3.16 +#define VNC_REFRESH_INTERVAL_MAX 2000 3.17 + 3.18 +/* Wait at most one second between updates, so that we can detect a 3.19 + minimised vncviewer reasonably quickly. */ 3.20 +#define VNC_MAX_UPDATE_INTERVAL 5000 3.21 3.22 #include "vnc_keysym.h" 3.23 #include "keymaps.c" 3.24 @@ -64,10 +76,11 @@ typedef void VncSendHextileTile(VncState 3.25 struct VncState 3.26 { 3.27 QEMUTimer *timer; 3.28 + int timer_interval; 3.29 + int64_t last_update_time; 3.30 int lsock; 3.31 int csock; 3.32 DisplayState *ds; 3.33 - int need_update; 3.34 int width; 3.35 int height; 3.36 uint64_t *dirty_row; /* screen regions which are possibly dirty */ 3.37 @@ -98,8 +111,6 @@ struct VncState 3.38 int visible_w; 3.39 int visible_h; 3.40 3.41 - int slow_client; 3.42 - 3.43 int ctl_keys; /* Ctrl+Alt starts calibration */ 3.44 }; 3.45 3.46 @@ -380,7 +391,7 @@ static void vnc_copy(DisplayState *ds, i 3.47 int y = 0; 3.48 int pitch = ds->linesize; 3.49 VncState *vs = ds->opaque; 3.50 - int updating_client = !vs->slow_client; 3.51 + int updating_client = 1; 3.52 3.53 if (src_x < vs->visible_x || src_y < vs->visible_y || 3.54 dst_x < vs->visible_x || dst_y < vs->visible_y || 3.55 @@ -390,11 +401,6 @@ static void vnc_copy(DisplayState *ds, i 3.56 (dst_y + h) > (vs->visible_y + vs->visible_h)) 3.57 updating_client = 0; 3.58 3.59 - if (updating_client) { 3.60 - vs->need_update = 1; 3.61 - _vnc_update_client(vs); 3.62 - } 3.63 - 3.64 if (dst_y > src_y) { 3.65 y = h - 1; 3.66 pitch = -pitch; 3.67 @@ -445,110 +451,145 @@ static int find_update_height(VncState * 3.68 static void _vnc_update_client(void *opaque) 3.69 { 3.70 VncState *vs = opaque; 3.71 - int64_t now = qemu_get_clock(rt_clock); 3.72 + int64_t now; 3.73 + int y; 3.74 + char *row; 3.75 + char *old_row; 3.76 + uint64_t width_mask; 3.77 + int n_rectangles; 3.78 + int saved_offset; 3.79 + int maxx, maxy; 3.80 + int tile_bytes = vs->depth * DP2X(vs, 1); 3.81 3.82 - if (vs->need_update && vs->csock != -1) { 3.83 - int y; 3.84 - char *row; 3.85 - char *old_row; 3.86 - uint64_t width_mask; 3.87 - int n_rectangles; 3.88 - int saved_offset; 3.89 - int maxx, maxy; 3.90 - int tile_bytes = vs->depth * DP2X(vs, 1); 3.91 + if (vs->csock == -1) 3.92 + return; 3.93 + 3.94 + now = qemu_get_clock(rt_clock); 3.95 3.96 - qemu_mod_timer(vs->timer, now + VNC_REFRESH_INTERVAL); 3.97 - 3.98 - if (vs->width != DP2X(vs, DIRTY_PIXEL_BITS)) 3.99 - width_mask = (1ULL << X2DP_UP(vs, vs->ds->width)) - 1; 3.100 - else 3.101 - width_mask = ~(0ULL); 3.102 + if (vs->width != DP2X(vs, DIRTY_PIXEL_BITS)) 3.103 + width_mask = (1ULL << X2DP_UP(vs, vs->ds->width)) - 1; 3.104 + else 3.105 + width_mask = ~(0ULL); 3.106 3.107 - /* Walk through the dirty map and eliminate tiles that 3.108 - really aren't dirty */ 3.109 - row = vs->ds->data; 3.110 - old_row = vs->old_data; 3.111 + /* Walk through the dirty map and eliminate tiles that really 3.112 + aren't dirty */ 3.113 + row = vs->ds->data; 3.114 + old_row = vs->old_data; 3.115 3.116 - for (y = 0; y < vs->ds->height; y++) { 3.117 - if (vs->dirty_row[y] & width_mask) { 3.118 - int x; 3.119 - char *ptr, *old_ptr; 3.120 + for (y = 0; y < vs->ds->height; y++) { 3.121 + if (vs->dirty_row[y] & width_mask) { 3.122 + int x; 3.123 + char *ptr, *old_ptr; 3.124 3.125 - ptr = row; 3.126 - old_ptr = old_row; 3.127 + ptr = row; 3.128 + old_ptr = old_row; 3.129 3.130 - for (x = 0; x < X2DP_UP(vs, vs->ds->width); x++) { 3.131 - if (vs->dirty_row[y] & (1ULL << x)) { 3.132 - if (memcmp(old_ptr, ptr, tile_bytes)) { 3.133 - vs->has_update = 1; 3.134 - vs->update_row[y] |= (1ULL << x); 3.135 - memcpy(old_ptr, ptr, tile_bytes); 3.136 - } 3.137 - vs->dirty_row[y] &= ~(1ULL << x); 3.138 + for (x = 0; x < X2DP_UP(vs, vs->ds->width); x++) { 3.139 + if (vs->dirty_row[y] & (1ULL << x)) { 3.140 + if (memcmp(old_ptr, ptr, tile_bytes)) { 3.141 + vs->has_update = 1; 3.142 + vs->update_row[y] |= (1ULL << x); 3.143 + memcpy(old_ptr, ptr, tile_bytes); 3.144 } 3.145 - 3.146 - ptr += tile_bytes; 3.147 - old_ptr += tile_bytes; 3.148 + vs->dirty_row[y] &= ~(1ULL << x); 3.149 } 3.150 - } 3.151 3.152 - row += vs->ds->linesize; 3.153 - old_row += vs->ds->linesize; 3.154 + ptr += tile_bytes; 3.155 + old_ptr += tile_bytes; 3.156 + } 3.157 } 3.158 + 3.159 + row += vs->ds->linesize; 3.160 + old_row += vs->ds->linesize; 3.161 + } 3.162 3.163 - if (!vs->has_update || vs->visible_y >= vs->ds->height || 3.164 - vs->visible_x >= vs->ds->width) 3.165 - return; 3.166 + if (!vs->has_update || vs->visible_y >= vs->ds->height || 3.167 + vs->visible_x >= vs->ds->width) 3.168 + goto backoff; 3.169 3.170 - /* Count rectangles */ 3.171 - n_rectangles = 0; 3.172 - vnc_write_u8(vs, 0); /* msg id */ 3.173 - vnc_write_u8(vs, 0); 3.174 - saved_offset = vs->output.offset; 3.175 - vnc_write_u16(vs, 0); 3.176 + /* Count rectangles */ 3.177 + n_rectangles = 0; 3.178 + vnc_write_u8(vs, 0); /* msg id */ 3.179 + vnc_write_u8(vs, 0); 3.180 + saved_offset = vs->output.offset; 3.181 + vnc_write_u16(vs, 0); 3.182 + 3.183 + maxy = vs->visible_y + vs->visible_h; 3.184 + if (maxy > vs->ds->height) 3.185 + maxy = vs->ds->height; 3.186 + maxx = vs->visible_x + vs->visible_w; 3.187 + if (maxx > vs->ds->width) 3.188 + maxx = vs->ds->width; 3.189 3.190 - maxy = vs->visible_y + vs->visible_h; 3.191 - if (maxy > vs->ds->height) 3.192 - maxy = vs->ds->height; 3.193 - maxx = vs->visible_x + vs->visible_w; 3.194 - if (maxx > vs->ds->width) 3.195 - maxx = vs->ds->width; 3.196 - 3.197 - for (y = vs->visible_y; y < maxy; y++) { 3.198 - int x; 3.199 - int last_x = -1; 3.200 - for (x = X2DP_DOWN(vs, vs->visible_x); 3.201 - x < X2DP_UP(vs, maxx); x++) { 3.202 - if (vs->update_row[y] & (1ULL << x)) { 3.203 - if (last_x == -1) 3.204 - last_x = x; 3.205 - vs->update_row[y] &= ~(1ULL << x); 3.206 - } else { 3.207 - if (last_x != -1) { 3.208 - int h = find_update_height(vs, y, maxy, last_x, x); 3.209 + for (y = vs->visible_y; y < maxy; y++) { 3.210 + int x; 3.211 + int last_x = -1; 3.212 + for (x = X2DP_DOWN(vs, vs->visible_x); 3.213 + x < X2DP_UP(vs, maxx); x++) { 3.214 + if (vs->update_row[y] & (1ULL << x)) { 3.215 + if (last_x == -1) 3.216 + last_x = x; 3.217 + vs->update_row[y] &= ~(1ULL << x); 3.218 + } else { 3.219 + if (last_x != -1) { 3.220 + int h = find_update_height(vs, y, maxy, last_x, x); 3.221 + if (h != 0) { 3.222 send_framebuffer_update(vs, DP2X(vs, last_x), y, 3.223 DP2X(vs, (x - last_x)), h); 3.224 n_rectangles++; 3.225 } 3.226 - last_x = -1; 3.227 } 3.228 + last_x = -1; 3.229 } 3.230 - if (last_x != -1) { 3.231 - int h = find_update_height(vs, y, maxy, last_x, x); 3.232 + } 3.233 + if (last_x != -1) { 3.234 + int h = find_update_height(vs, y, maxy, last_x, x); 3.235 + if (h != 0) { 3.236 send_framebuffer_update(vs, DP2X(vs, last_x), y, 3.237 DP2X(vs, (x - last_x)), h); 3.238 n_rectangles++; 3.239 } 3.240 } 3.241 - vs->output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF; 3.242 - vs->output.buffer[saved_offset + 1] = n_rectangles & 0xFF; 3.243 + } 3.244 + vs->output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF; 3.245 + vs->output.buffer[saved_offset + 1] = n_rectangles & 0xFF; 3.246 + 3.247 + if (n_rectangles == 0) 3.248 + goto backoff; 3.249 + 3.250 + vs->has_update = 0; 3.251 + vnc_flush(vs); 3.252 + vs->last_update_time = now; 3.253 + 3.254 + vs->timer_interval /= 2; 3.255 + if (vs->timer_interval < VNC_REFRESH_INTERVAL_BASE) 3.256 + vs->timer_interval = VNC_REFRESH_INTERVAL_BASE; 3.257 + 3.258 + return; 3.259 3.260 - vs->has_update = 0; 3.261 - vs->need_update = 0; 3.262 - vnc_flush(vs); 3.263 - vs->slow_client = 0; 3.264 - } else 3.265 - vs->slow_client = 1; 3.266 + backoff: 3.267 + /* No update -> back off a bit */ 3.268 + vs->timer_interval += VNC_REFRESH_INTERVAL_INC; 3.269 + if (vs->timer_interval > VNC_REFRESH_INTERVAL_MAX) { 3.270 + vs->timer_interval = VNC_REFRESH_INTERVAL_MAX; 3.271 + if (now - vs->last_update_time >= VNC_MAX_UPDATE_INTERVAL) { 3.272 + /* Send a null update. If the client is no longer 3.273 + interested (e.g. minimised) it'll ignore this, and we 3.274 + can stop scanning the buffer until it sends another 3.275 + update request. */ 3.276 + /* Note that there are bugs in xvncviewer which prevent 3.277 + this from actually working. Leave the code in place 3.278 + for correct clients. */ 3.279 + vnc_write_u8(vs, 0); 3.280 + vnc_write_u8(vs, 0); 3.281 + vnc_write_u16(vs, 0); 3.282 + vnc_flush(vs); 3.283 + vs->last_update_time = now; 3.284 + return; 3.285 + } 3.286 + } 3.287 + qemu_mod_timer(vs->timer, now + vs->timer_interval); 3.288 + return; 3.289 } 3.290 3.291 static void vnc_update_client(void *opaque) 3.292 @@ -561,8 +602,10 @@ static void vnc_update_client(void *opaq 3.293 3.294 static void vnc_timer_init(VncState *vs) 3.295 { 3.296 - if (vs->timer == NULL) 3.297 + if (vs->timer == NULL) { 3.298 vs->timer = qemu_new_timer(rt_clock, vnc_update_client, vs); 3.299 + vs->timer_interval = VNC_REFRESH_INTERVAL_BASE; 3.300 + } 3.301 } 3.302 3.303 static void vnc_dpy_refresh(DisplayState *ds) 3.304 @@ -622,7 +665,6 @@ static int vnc_client_io_error(VncState 3.305 vs->csock = -1; 3.306 buffer_reset(&vs->input); 3.307 buffer_reset(&vs->output); 3.308 - vs->need_update = 0; 3.309 return 0; 3.310 } 3.311 return ret; 3.312 @@ -892,7 +934,6 @@ static void framebuffer_update_request(V 3.313 int x_position, int y_position, 3.314 int w, int h) 3.315 { 3.316 - vs->need_update = 1; 3.317 if (!incremental) 3.318 framebuffer_set_updated(vs, x_position, y_position, w, h); 3.319 vs->visible_x = x_position; 3.320 @@ -1015,6 +1056,7 @@ static int protocol_client_msg(VncState 3.321 { 3.322 int i; 3.323 uint16_t limit; 3.324 + int64_t now; 3.325 3.326 switch (data[0]) { 3.327 case 0: 3.328 @@ -1054,12 +1096,18 @@ static int protocol_client_msg(VncState 3.329 if (len == 1) 3.330 return 8; 3.331 3.332 + vs->timer_interval = VNC_REFRESH_INTERVAL_BASE; 3.333 + qemu_advance_timer(vs->timer, 3.334 + qemu_get_clock(rt_clock) + vs->timer_interval); 3.335 key_event(vs, read_u8(data, 1), read_u32(data, 4)); 3.336 break; 3.337 case 5: 3.338 if (len == 1) 3.339 return 6; 3.340 3.341 + vs->timer_interval = VNC_REFRESH_INTERVAL_BASE; 3.342 + qemu_advance_timer(vs->timer, 3.343 + qemu_get_clock(rt_clock) + vs->timer_interval); 3.344 pointer_event(vs, read_u8(data, 1), read_u16(data, 2), read_u16(data, 4)); 3.345 break; 3.346 case 6: 3.347 @@ -1268,7 +1316,7 @@ int vnc_start_viewer(int port) 3.348 exit(1); 3.349 3.350 case 0: /* child */ 3.351 - execlp("vncviewer", "vncviewer", s, 0); 3.352 + execlp("vncviewer", "vncviewer", s, NULL); 3.353 fprintf(stderr, "vncviewer execlp failed\n"); 3.354 exit(1); 3.355