From patchwork Wed Aug 11 05:49:32 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Corentin Chary X-Patchwork-Id: 61442 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 EFEE8B70AB for ; Wed, 11 Aug 2010 15:54:56 +1000 (EST) Received: from localhost ([127.0.0.1]:39754 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Oj4Gv-0007Pq-AI for incoming@patchwork.ozlabs.org; Wed, 11 Aug 2010 01:54:41 -0400 Received: from [140.186.70.92] (port=37808 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Oj4Ce-0005is-0n for qemu-devel@nongnu.org; Wed, 11 Aug 2010 01:50:17 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.69) (envelope-from ) id 1Oj4Cb-0007Oo-Qs for qemu-devel@nongnu.org; Wed, 11 Aug 2010 01:50:15 -0400 Received: from relay1-v.mail.gandi.net ([217.70.178.75]:50800 helo=mrelay1-v.mgt.gandi.net) by eggs.gnu.org with esmtp (Exim 4.69) (envelope-from ) id 1Oj4Cb-0007Og-HY for qemu-devel@nongnu.org; Wed, 11 Aug 2010 01:50:13 -0400 X-Originating-IP: 217.70.178.45 Received: from mfilter4-d.gandi.net (mfilter4-d.gandi.net [217.70.178.45]) by mrelay1-v.mgt.gandi.net (Postfix) with ESMTP id 03054362BA; Wed, 11 Aug 2010 07:50:13 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at mfilter4-d.gandi.net Received: from mrelay1-v.mgt.gandi.net ([217.70.178.75]) by mfilter4-d.gandi.net (mfilter4-d.gandi.net [217.70.178.45]) (amavisd-new, port 10024) with ESMTP id g4oRnbIPs-xc; Wed, 11 Aug 2010 07:50:10 +0200 (CEST) X-Originating-IP: 82.241.209.44 Received: from tartiflon (falgoret.iksaif.net [82.241.209.44]) (Authenticated sender: fake@iksaif.net) by mrelay1-v.mgt.gandi.net (Postfix) with ESMTPSA id C8CFD362B5; Wed, 11 Aug 2010 07:50:08 +0200 (CEST) From: Corentin Chary To: Qemu-development List Date: Wed, 11 Aug 2010 07:49:32 +0200 Message-Id: <1281505785-22523-3-git-send-email-corentincj@iksaif.net> X-Mailer: git-send-email 1.7.1 In-Reply-To: <1281505785-22523-1-git-send-email-corentincj@iksaif.net> References: <1281505785-22523-1-git-send-email-corentincj@iksaif.net> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 3) Cc: Corentin Chary , Anthony Liguori , Alexander Graf , Andre Przywara Subject: [Qemu-devel] [PATCH 02/15] 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 3086d95..5c3a760 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" @@ -2253,6 +2255,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; @@ -2263,6 +2358,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. @@ -2289,6 +2389,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 9619b24..f10fa3c 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; }; @@ -478,6 +496,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);