diff options
-rw-r--r-- | ui/vnc.c | 101 | ||||
-rw-r--r-- | ui/vnc.h | 19 |
2 files changed, 120 insertions, 0 deletions
@@ -35,6 +35,8 @@ #define VNC_REFRESH_INTERVAL_BASE 30 #define VNC_REFRESH_INTERVAL_INC 50 #define VNC_REFRESH_INTERVAL_MAX 2000 +static const struct timeval VNC_REFRESH_STATS = { 0, 500000 }; +static const struct timeval VNC_REFRESH_LOSSY = { 2, 0 }; #include "vnc_keysym.h" #include "d3des.h" @@ -2258,6 +2260,99 @@ static int protocol_version(VncState *vs, uint8_t *version, size_t len) return 0; } +static VncRectStat *vnc_stat_rect(VncDisplay *vd, int x, int y) +{ + struct VncSurface *vs = &vd->guest; + + return &vs->stats[y / VNC_STAT_RECT][x / VNC_STAT_RECT]; +} + +static void vnc_update_stats(VncDisplay *vd, struct timeval * tv) +{ + int x, y; + struct timeval res; + + for (y = 0; y < vd->guest.ds->height; y += VNC_STAT_RECT) { + for (x = 0; x < vd->guest.ds->width; x += VNC_STAT_RECT) { + VncRectStat *rect = vnc_stat_rect(vd, x, y); + + rect->updated = false; + } + } + + timersub(tv, &VNC_REFRESH_STATS, &res); + + if (timercmp(&vd->guest.last_freq_check, &res, >)) { + return ; + } + 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) { + VncRectStat *rect= vnc_stat_rect(vd, x, y); + int count = ARRAY_SIZE(rect->times); + struct timeval min, max; + + if (!timerisset(&rect->times[count - 1])) { + continue ; + } + + max = rect->times[(rect->idx + count - 1) % count]; + timersub(tv, &max, &res); + + if (timercmp(&res, &VNC_REFRESH_LOSSY, >)) { + rect->freq = 0; + memset(rect->times, 0, sizeof (rect->times)); + continue ; + } + + min = rect->times[rect->idx]; + max = rect->times[(rect->idx + count - 1) % count]; + timersub(&max, &min, &res); + + rect->freq = res.tv_sec + res.tv_usec / 1000000.; + rect->freq /= count; + rect->freq = 1. / rect->freq; + } + } +} + +double vnc_update_freq(VncState *vs, int x, int y, int w, int h) +{ + int i, j; + double total = 0; + int num = 0; + + x = (x / VNC_STAT_RECT) * VNC_STAT_RECT; + y = (y / VNC_STAT_RECT) * VNC_STAT_RECT; + + for (j = y; j <= y + h; j += VNC_STAT_RECT) { + for (i = x; i <= x + w; i += VNC_STAT_RECT) { + total += vnc_stat_rect(vs->vd, i, j)->freq; + num++; + } + } + + if (num) { + return total / num; + } else { + return 0; + } +} + +static void vnc_rect_updated(VncDisplay *vd, int x, int y, struct timeval * tv) +{ + VncRectStat *rect; + + rect = vnc_stat_rect(vd, x, y); + if (rect->updated) { + return ; + } + rect->times[rect->idx] = *tv; + rect->idx = (rect->idx + 1) % ARRAY_SIZE(rect->times); + rect->updated = true; +} + static int vnc_refresh_server_surface(VncDisplay *vd) { int y; @@ -2268,6 +2363,11 @@ static int vnc_refresh_server_surface(VncDisplay *vd) VncState *vs; int has_dirty = 0; + struct timeval tv; + + gettimeofday(&tv, NULL); + vnc_update_stats(vd, &tv); + /* * Walk through the guest dirty map. * Check and copy modified bits from guest to server surface. @@ -2294,6 +2394,7 @@ static int vnc_refresh_server_surface(VncDisplay *vd) if (memcmp(server_ptr, guest_ptr, cmp_bytes) == 0) continue; memcpy(server_ptr, guest_ptr, cmp_bytes); + vnc_rect_updated(vd, x, y, &tv); QTAILQ_FOREACH(vs, &vd->clients, next) { vnc_set_bit(vs->dirty[y], (x / 16)); } @@ -80,6 +80,10 @@ typedef void VncSendHextileTile(VncState *vs, #define VNC_MAX_HEIGHT 2048 #define VNC_DIRTY_WORDS (VNC_MAX_WIDTH / (16 * 32)) +#define VNC_STAT_RECT 64 +#define VNC_STAT_COLS (VNC_MAX_WIDTH / VNC_STAT_RECT) +#define VNC_STAT_ROWS (VNC_MAX_HEIGHT / VNC_STAT_RECT) + #define VNC_AUTH_CHALLENGE_SIZE 16 typedef struct VncDisplay VncDisplay; @@ -92,9 +96,23 @@ typedef struct VncDisplay VncDisplay; #include "vnc-auth-sasl.h" #endif +struct VncRectStat +{ + /* time of last 10 updates, to find update frequency */ + struct timeval times[10]; + int idx; + + double freq; /* Update frequency (in Hz) */ + bool updated; /* Already updated during this refresh */ +}; + +typedef struct VncRectStat VncRectStat; + struct VncSurface { + struct timeval last_freq_check; uint32_t dirty[VNC_MAX_HEIGHT][VNC_DIRTY_WORDS]; + VncRectStat stats[VNC_STAT_ROWS][VNC_STAT_COLS]; DisplaySurface *ds; }; @@ -479,6 +497,7 @@ void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h, int32_t encoding); void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v); +double vnc_update_freq(VncState *vs, int x, int y, int w, int h); /* Encodings */ int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h); |