From patchwork Fri Feb 4 08:05:55 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Corentin Chary X-Patchwork-Id: 81817 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id DE37AB7106 for ; Fri, 4 Feb 2011 19:13:31 +1100 (EST) Received: from localhost ([127.0.0.1]:60688 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PlGn1-0006LO-9C for incoming@patchwork.ozlabs.org; Fri, 04 Feb 2011 03:13:11 -0500 Received: from [140.186.70.92] (port=49302 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PlGev-0003el-4Q for qemu-devel@nongnu.org; Fri, 04 Feb 2011 03:04:50 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1PlGet-0001gm-Sq for qemu-devel@nongnu.org; Fri, 04 Feb 2011 03:04:49 -0500 Received: from smtp5-g21.free.fr ([212.27.42.5]:51245) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1PlGet-0001gT-6G for qemu-devel@nongnu.org; Fri, 04 Feb 2011 03:04:47 -0500 Received: from localhost.localdomain (unknown [82.241.209.44]) by smtp5-g21.free.fr (Postfix) with ESMTP id A468ED4819A; Fri, 4 Feb 2011 09:04:41 +0100 (CET) From: Corentin Chary To: Anthony Liguori Date: Fri, 4 Feb 2011 09:05:55 +0100 Message-Id: <1296806768-27787-4-git-send-email-corentincj@iksaif.net> X-Mailer: git-send-email 1.7.4 In-Reply-To: <1296806768-27787-1-git-send-email-corentincj@iksaif.net> References: <1296806768-27787-1-git-send-email-corentincj@iksaif.net> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 3) X-Received-From: 212.27.42.5 Cc: Corentin Chary , Andre Przywara , Qemu-development List , Alexander Graf Subject: [Qemu-devel] [PATCH v3 03/16] vnc: add a way to get the update frequency for a given region X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org This patch compute the update frequency (in Hz) for each 64x64 rects. Any adaptive encoding can get this value using vnc_update_freq(), and switch to a lossy encoding if the value is too high. The frequency is pre-calculated every 500ms, based on the last 10 updates per 64x64 rect. If a 64x64 rect was not updated in the last 2 second, then the frequency became 0, and all the stored timestamp are reseted. Signed-off-by: Corentin Chary --- ui/vnc.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ui/vnc.h | 19 +++++++++++ 2 files changed, 120 insertions(+), 0 deletions(-) diff --git a/ui/vnc.c b/ui/vnc.c index 9c5c5b3..6eacd1d 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -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)); } diff --git a/ui/vnc.h b/ui/vnc.h index 4f895be..c0e5ff3 100644 --- a/ui/vnc.h +++ b/ui/vnc.h @@ -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);