@@ -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));
}
@@ -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);
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> --- ui/vnc.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ui/vnc.h | 19 +++++++++++ 2 files changed, 120 insertions(+), 0 deletions(-)