Browse Source

Limit lossless refresh update to safe size

We don't want to waste bandwidth on the lossless refresh if we might
need that bandwidth for a normal update. Try to estimate how much
data we can safely send without interfering.
tags/v1.8.90
Pierre Ossman 6 years ago
parent
commit
a2b80d6ae7

+ 12
- 3
common/rfb/Congestion.cxx View File

@@ -1,4 +1,4 @@
/* Copyright 2009-2015 Pierre Ossman for Cendio AB
/* Copyright 2009-2018 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -73,7 +73,7 @@ static LogWriter vlog("Congestion");
Congestion::Congestion() :
lastPosition(0), extraBuffer(0),
baseRTT(-1), congWindow(INITIAL_WINDOW), inSlowStart(true),
measurements(0), minRTT(-1), minCongestedRTT(-1)
safeBaseRTT(-1), measurements(0), minRTT(-1), minCongestedRTT(-1)
{
gettimeofday(&lastUpdate, NULL);
gettimeofday(&lastSent, NULL);
@@ -170,7 +170,7 @@ void Congestion::gotPong()

// Try to estimate wire latency by tracking lowest seen latency
if (rtt < baseRTT)
baseRTT = rtt;
safeBaseRTT = baseRTT = rtt;

// Pings sent before the last adjustment aren't interesting as they
// aren't a measurement of the current congestion window
@@ -284,6 +284,15 @@ int Congestion::getUncongestedETA()
}
}

size_t Congestion::getBandwidth()
{
// No measurements yet? Guess RTT of 60 ms
if (safeBaseRTT == (unsigned)-1)
return congWindow * 1000 / 60;

return congWindow * 1000 / safeBaseRTT;
}

void Congestion::debugTrace(const char* filename, int fd)
{
#ifdef CONGESTION_TRACE

+ 7
- 1
common/rfb/Congestion.h View File

@@ -1,4 +1,4 @@
/* Copyright 2009-2015 Pierre Ossman for Cendio AB
/* Copyright 2009-2018 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -47,6 +47,10 @@ namespace rfb {
// longer be congested.
int getUncongestedETA();

// getBandwidth() returns the current bandwidth estimation in bytes
// per second.
size_t getBandwidth();

// debugTrace() writes the current congestion window, as well as the
// congestion window of the underlying TCP layer, to the specified
// file
@@ -68,6 +72,8 @@ namespace rfb {
unsigned congWindow;
bool inSlowStart;

unsigned safeBaseRTT;

struct RTTInfo {
struct timeval tv;
unsigned pos;

+ 11
- 8
common/rfb/EncodeManager.cxx View File

@@ -50,8 +50,6 @@ static const int SolidSearchBlock = 16;
// Don't bother with blocks smaller than this
static const int SolidBlockMinArea = 2048;

static const int LosslessRefreshMaxArea = 4096;

namespace rfb {

enum EncoderClass {
@@ -267,9 +265,10 @@ void EncodeManager::writeUpdate(const UpdateInfo& ui, const PixelBuffer* pb,
}

void EncodeManager::writeLosslessRefresh(const Region& req, const PixelBuffer* pb,
const RenderedCursor* renderedCursor)
const RenderedCursor* renderedCursor,
size_t maxUpdateSize)
{
doUpdate(false, getLosslessRefresh(req),
doUpdate(false, getLosslessRefresh(req, maxUpdateSize),
Region(), Point(), pb, renderedCursor);
}

@@ -428,12 +427,16 @@ void EncodeManager::prepareEncoders(bool allowLossy)
}
}

Region EncodeManager::getLosslessRefresh(const Region& req)
Region EncodeManager::getLosslessRefresh(const Region& req,
size_t maxUpdateSize)
{
std::vector<Rect> rects;
Region refresh;
size_t area;

// We make a conservative guess at the compression ratio at 2:1
maxUpdateSize *= 2;

area = 0;
lossyRegion.intersect(req).get_rects(&rects);
while (!rects.empty()) {
@@ -448,13 +451,13 @@ Region EncodeManager::getLosslessRefresh(const Region& req)

// Add rects until we exceed the threshold, then include as much as
// possible of the final rect
if ((area + rect.area()) > LosslessRefreshMaxArea) {
if ((area + rect.area()) > maxUpdateSize) {
// Use the narrowest axis to avoid getting to thin rects
if (rect.width() > rect.height()) {
int width = (LosslessRefreshMaxArea - area) / rect.height();
int width = (maxUpdateSize - area) / rect.height();
rect.br.x = rect.tl.x + __rfbmax(1, width);
} else {
int height = (LosslessRefreshMaxArea - area) / rect.width();
int height = (maxUpdateSize - area) / rect.width();
rect.br.y = rect.tl.y + __rfbmax(1, height);
}
refresh.assign_union(Region(rect));

+ 3
- 2
common/rfb/EncodeManager.h View File

@@ -53,7 +53,8 @@ namespace rfb {
const RenderedCursor* renderedCursor);

void writeLosslessRefresh(const Region& req, const PixelBuffer* pb,
const RenderedCursor* renderedCursor);
const RenderedCursor* renderedCursor,
size_t maxUpdateSize);

protected:
void doUpdate(bool allowLossy, const Region& changed,
@@ -62,7 +63,7 @@ namespace rfb {
const RenderedCursor* renderedCursor);
void prepareEncoders(bool allowLossy);

Region getLosslessRefresh(const Region& req);
Region getLosslessRefresh(const Region& req, size_t maxUpdateSize);

int computeNumRects(const Region& changed);


+ 8
- 2
common/rfb/Timer.cxx View File

@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2016 Pierre Ossman for Cendio AB
* Copyright 2016-2018 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -94,7 +94,7 @@ int Timer::checkTimeouts() {
int Timer::getNextTimeout() {
timeval now;
gettimeofday(&now, 0);
int toWait = __rfbmax(1, diffTimeMillis(pending.front()->dueTime, now));
int toWait = __rfbmax(1, pending.front()->getRemainingMs());
if (toWait > pending.front()->timeoutMs) {
if (toWait - pending.front()->timeoutMs < 1000) {
vlog.info("gettimeofday is broken...");
@@ -148,6 +148,12 @@ int Timer::getTimeoutMs() {
return timeoutMs;
}

int Timer::getRemainingMs() {
timeval now;
gettimeofday(&now, 0);
return __rfbmax(0, diffTimeMillis(pending.front()->dueTime, now));
}

bool Timer::isBefore(timeval other) {
return (dueTime.tv_sec < other.tv_sec) ||
((dueTime.tv_sec == other.tv_sec) &&

+ 6
- 0
common/rfb/Timer.h View File

@@ -1,4 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2018 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -85,6 +86,11 @@ namespace rfb {
// Usually used with isStarted() to get the _current_ timeout.
int getTimeoutMs();

// getRemainingMs
// Determines how many milliseconds are left before the Timer
// will timeout. Only valid for an active timer.
int getRemainingMs();

// isBefore
// Determine whether the Timer will timeout before the specified time.
bool isBefore(timeval other);

+ 14
- 2
common/rfb/VNCSConnectionST.cxx View File

@@ -1067,8 +1067,20 @@ void VNCSConnectionST::writeDataUpdate()

if (!ui.is_empty())
encodeManager.writeUpdate(ui, server->getPixelBuffer(), cursor);
else
encodeManager.writeLosslessRefresh(req, server->getPixelBuffer(), cursor);
else {
size_t maxUpdateSize;

// FIXME: If continuous updates aren't used then the client might
// be slower than frameRate in its requests and we could
// afford a larger update size

// FIXME: Bandwidth estimation without congestion control
maxUpdateSize = congestion.getBandwidth() *
server->msToNextUpdate() / 1000;

encodeManager.writeLosslessRefresh(req, server->getPixelBuffer(),
cursor, maxUpdateSize);
}

writeRTTPing();


+ 11
- 0
common/rfb/VNCServerST.cxx View File

@@ -593,6 +593,17 @@ void VNCServerST::stopFrameClock()
frameTimer.stop();
}

int VNCServerST::msToNextUpdate()
{
// FIXME: If the application is updating slower than frameRate then
// we could allow the clients more time here

if (!frameTimer.isStarted())
return 1000/rfb::Server::frameRate/2;
else
return frameTimer.getRemainingMs();
}

// writeUpdate() is called on a regular interval in order to see what
// updates are pending and propagates them to the update tracker for
// each client. It uses the ComparingUpdateTracker's compare() method

+ 1
- 0
common/rfb/VNCServerST.h View File

@@ -227,6 +227,7 @@ namespace rfb {
bool needRenderedCursor();
void startFrameClock();
void stopFrameClock();
int msToNextUpdate();
void writeUpdate();
bool checkUpdate();
const RenderedCursor* getRenderedCursor();

Loading…
Cancel
Save