From 27cb7c27dedbd4978202b6718905997ac6f92625 Mon Sep 17 00:00:00 2001
From: Corentin Chary <corentincj@iksaif.net>
Date: Fri, 4 Feb 2011 09:05:55 +0100
Subject: [PATCH] vnc: add a way to get the update frequency for a given region

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 <corentincj@iksaif.net>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
(cherry picked from commit 999342a0fe959dcd549396a255a04d000678e910)
[LM: BNC#1074701]
Signed-off-by: Lin Ma <lma@suse.com>
---
 vnc.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vnc.h |  20 +++++++++++++
 2 files changed, 121 insertions(+)

diff --git a/vnc.c b/vnc.c
index f49dd3f9fa..8522e06f00 100644
--- a/vnc.c
+++ b/vnc.c
@@ -34,6 +34,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"
@@ -2083,6 +2085,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;
@@ -2093,6 +2188,11 @@ static int vnc_refresh_server_surface(VncDisplay *vd)
     VncState *vs = NULL;
     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.
@@ -2119,6 +2219,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);
                 vs = vd->clients;
                 while (vs != NULL) {
                     vnc_set_bit(vs->dirty[y], (x / 16));
diff --git a/vnc.h b/vnc.h
index be374eefcc..34c1fff838 100644
--- a/vnc.h
+++ b/vnc.h
@@ -32,6 +32,7 @@
 #include "monitor.h"
 #include "audio/audio.h"
 #include <zlib.h>
+#include <stdbool.h>
 
 #include "keymaps.h"
 
@@ -72,6 +73,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;
@@ -84,9 +89,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;
 };
 
@@ -405,6 +424,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_raw_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
