diff options
author | Pierre Ossman <ossman@cendio.se> | 2018-04-20 13:57:41 +0200 |
---|---|---|
committer | Pierre Ossman <ossman@cendio.se> | 2018-04-20 13:57:41 +0200 |
commit | 105177fc07d07521eafdcf373c1825a59b9c554d (patch) | |
tree | b88c95aceee02386c05faff686c7f550d901bad4 /common | |
parent | fa646bc6e2fdabae4b8f85829691f33327baea16 (diff) | |
parent | 8efc7b47cc744b3ee4b7ae20f88c9e3aa775eefe (diff) | |
download | tigervnc-105177fc07d07521eafdcf373c1825a59b9c554d.tar.gz tigervnc-105177fc07d07521eafdcf373c1825a59b9c554d.zip |
Merge branch 'alr' of https://github.com/CendioOssman/tigervnc
Diffstat (limited to 'common')
-rw-r--r-- | common/rfb/Congestion.cxx | 15 | ||||
-rw-r--r-- | common/rfb/Congestion.h | 8 | ||||
-rw-r--r-- | common/rfb/EncodeManager.cxx | 140 | ||||
-rw-r--r-- | common/rfb/EncodeManager.h | 23 | ||||
-rw-r--r-- | common/rfb/Encoder.h | 2 | ||||
-rw-r--r-- | common/rfb/Region.cxx | 4 | ||||
-rw-r--r-- | common/rfb/Region.h | 1 | ||||
-rw-r--r-- | common/rfb/TightJPEGEncoder.cxx | 2 | ||||
-rw-r--r-- | common/rfb/Timer.cxx | 10 | ||||
-rw-r--r-- | common/rfb/Timer.h | 6 | ||||
-rw-r--r-- | common/rfb/VNCSConnectionST.cxx | 94 | ||||
-rw-r--r-- | common/rfb/VNCServerST.cxx | 53 | ||||
-rw-r--r-- | common/rfb/VNCServerST.h | 4 |
13 files changed, 269 insertions, 93 deletions
diff --git a/common/rfb/Congestion.cxx b/common/rfb/Congestion.cxx index a2f7a256..4d36d9f1 100644 --- a/common/rfb/Congestion.cxx +++ b/common/rfb/Congestion.cxx @@ -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 diff --git a/common/rfb/Congestion.h b/common/rfb/Congestion.h index fd57c22e..d2935128 100644 --- a/common/rfb/Congestion.h +++ b/common/rfb/Congestion.h @@ -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; diff --git a/common/rfb/EncodeManager.cxx b/common/rfb/EncodeManager.cxx index 0cd52060..0ce611e9 100644 --- a/common/rfb/EncodeManager.cxx +++ b/common/rfb/EncodeManager.cxx @@ -1,6 +1,6 @@ /* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved. * Copyright (C) 2011 D. R. Commander. All Rights Reserved. - * Copyright 2014 Pierre Ossman for Cendio AB + * Copyright 2014-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 @@ -17,6 +17,9 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. */ + +#include <stdlib.h> + #include <rfb/EncodeManager.h> #include <rfb/Encoder.h> #include <rfb/Palette.h> @@ -245,52 +248,79 @@ bool EncodeManager::supported(int encoding) } } +bool EncodeManager::needsLosslessRefresh(const Region& req) +{ + return !lossyRegion.intersect(req).is_empty(); +} + +void EncodeManager::pruneLosslessRefresh(const Region& limits) +{ + lossyRegion.assign_intersect(limits); +} + void EncodeManager::writeUpdate(const UpdateInfo& ui, const PixelBuffer* pb, const RenderedCursor* renderedCursor) { + doUpdate(true, ui.changed, ui.copied, ui.copy_delta, pb, renderedCursor); +} + +void EncodeManager::writeLosslessRefresh(const Region& req, const PixelBuffer* pb, + const RenderedCursor* renderedCursor, + size_t maxUpdateSize) +{ + doUpdate(false, getLosslessRefresh(req, maxUpdateSize), + Region(), Point(), pb, renderedCursor); +} + +void EncodeManager::doUpdate(bool allowLossy, const Region& changed_, + const Region& copied, const Point& copyDelta, + const PixelBuffer* pb, + const RenderedCursor* renderedCursor) +{ int nRects; - Region changed; + Region changed, cursorRegion; updates++; - prepareEncoders(); + prepareEncoders(allowLossy); + + changed = changed_; + + /* + * We need to render the cursor seperately as it has its own + * magical pixel buffer, so split it out from the changed region. + */ + if (renderedCursor != NULL) { + cursorRegion = changed.intersect(renderedCursor->getEffectiveRect()); + changed.assign_subtract(renderedCursor->getEffectiveRect()); + } if (conn->cp.supportsLastRect) nRects = 0xFFFF; else { - nRects = ui.copied.numRects(); - nRects += computeNumRects(ui.changed); - - if (renderedCursor != NULL) - nRects += 1; + nRects = copied.numRects(); + nRects += computeNumRects(changed); + nRects += computeNumRects(cursorRegion); } conn->writer()->writeFramebufferUpdateStart(nRects); - writeCopyRects(ui); + writeCopyRects(copied, copyDelta); /* * We start by searching for solid rects, which are then removed * from the changed region. */ - changed.copyFrom(ui.changed); - if (conn->cp.supportsLastRect) writeSolidRects(&changed, pb); writeRects(changed, pb); - - if (renderedCursor != NULL) { - Rect renderedCursorRect; - - renderedCursorRect = renderedCursor->getEffectiveRect(); - writeSubRect(renderedCursorRect, renderedCursor); - } + writeRects(cursorRegion, renderedCursor); conn->writer()->writeFramebufferUpdateEnd(); } -void EncodeManager::prepareEncoders() +void EncodeManager::prepareEncoders(bool allowLossy) { enum EncoderClass solid, bitmap, bitmapRLE; enum EncoderClass indexed, indexedRLE, fullColour; @@ -315,7 +345,7 @@ void EncodeManager::prepareEncoders() break; case encodingTight: if (encoders[encoderTightJPEG]->isSupported() && - (conn->cp.pf().bpp >= 16)) + (conn->cp.pf().bpp >= 16) && allowLossy) fullColour = encoderTightJPEG; else fullColour = encoderTight; @@ -333,7 +363,7 @@ void EncodeManager::prepareEncoders() if (fullColour == encoderRaw) { if (encoders[encoderTightJPEG]->isSupported() && - (conn->cp.pf().bpp >= 16)) + (conn->cp.pf().bpp >= 16) && allowLossy) fullColour = encoderTightJPEG; else if (encoders[encoderZRLE]->isSupported()) fullColour = encoderZRLE; @@ -373,7 +403,7 @@ void EncodeManager::prepareEncoders() // JPEG is the only encoder that can reduce things to grayscale if ((conn->cp.subsampling == subsampleGray) && - encoders[encoderTightJPEG]->isSupported()) { + encoders[encoderTightJPEG]->isSupported() && allowLossy) { solid = bitmap = bitmapRLE = encoderTightJPEG; indexed = indexedRLE = fullColour = encoderTightJPEG; } @@ -397,6 +427,52 @@ void EncodeManager::prepareEncoders() } } +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()) { + size_t idx; + Rect rect; + + // Grab a random rect so we don't keep damaging and restoring the + // same rect over and over + idx = rand() % rects.size(); + + rect = rects[idx]; + + // Add rects until we exceed the threshold, then include as much as + // possible of the final rect + if ((area + rect.area()) > maxUpdateSize) { + // Use the narrowest axis to avoid getting to thin rects + if (rect.width() > rect.height()) { + int width = (maxUpdateSize - area) / rect.height(); + rect.br.x = rect.tl.x + __rfbmax(1, width); + } else { + int height = (maxUpdateSize - area) / rect.width(); + rect.br.y = rect.tl.y + __rfbmax(1, height); + } + refresh.assign_union(Region(rect)); + break; + } + + area += rect.area(); + refresh.assign_union(Region(rect)); + + rects.erase(rects.begin() + idx); + } + + return refresh; +} + int EncodeManager::computeNumRects(const Region& changed) { int numRects; @@ -449,6 +525,11 @@ Encoder *EncodeManager::startRect(const Rect& rect, int type) encoder = encoders[klass]; conn->writer()->startRect(rect, encoder->encoding); + if (encoder->flags & EncoderLossy) + lossyRegion.assign_union(Region(rect)); + else + lossyRegion.assign_subtract(Region(rect)); + return encoder; } @@ -465,14 +546,16 @@ void EncodeManager::endRect() stats[klass][activeType].bytes += length; } -void EncodeManager::writeCopyRects(const UpdateInfo& ui) +void EncodeManager::writeCopyRects(const Region& copied, const Point& delta) { std::vector<Rect> rects; std::vector<Rect>::const_iterator rect; + Region lossyCopy; + beforeLength = conn->getOutStream()->length(); - ui.copied.get_rects(&rects, ui.copy_delta.x <= 0, ui.copy_delta.y <= 0); + copied.get_rects(&rects, delta.x <= 0, delta.y <= 0); for (rect = rects.begin(); rect != rects.end(); ++rect) { int equiv; @@ -481,11 +564,16 @@ void EncodeManager::writeCopyRects(const UpdateInfo& ui) equiv = 12 + rect->area() * conn->cp.pf().bpp/8; copyStats.equivalent += equiv; - conn->writer()->writeCopyRect(*rect, rect->tl.x - ui.copy_delta.x, - rect->tl.y - ui.copy_delta.y); + conn->writer()->writeCopyRect(*rect, rect->tl.x - delta.x, + rect->tl.y - delta.y); } copyStats.bytes += conn->getOutStream()->length() - beforeLength; + + lossyCopy = lossyRegion; + lossyCopy.translate(delta); + lossyCopy.assign_intersect(copied); + lossyRegion.assign_union(lossyCopy); } void EncodeManager::writeSolidRects(Region *changed, const PixelBuffer* pb) diff --git a/common/rfb/EncodeManager.h b/common/rfb/EncodeManager.h index 4319b427..a91c544e 100644 --- a/common/rfb/EncodeManager.h +++ b/common/rfb/EncodeManager.h @@ -1,6 +1,6 @@ /* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved. * Copyright (C) 2011 D. R. Commander. All Rights Reserved. - * Copyright 2014 Pierre Ossman for Cendio AB + * Copyright 2014-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 @@ -24,6 +24,7 @@ #include <rdr/types.h> #include <rfb/PixelBuffer.h> +#include <rfb/Region.h> namespace rfb { class SConnection; @@ -31,7 +32,6 @@ namespace rfb { class UpdateInfo; class PixelBuffer; class RenderedCursor; - class Region; struct Rect; struct RectInfo; @@ -46,18 +46,31 @@ namespace rfb { // Hack to let ConnParams calculate the client's preferred encoding static bool supported(int encoding); + bool needsLosslessRefresh(const Region& req); + void pruneLosslessRefresh(const Region& limits); + void writeUpdate(const UpdateInfo& ui, const PixelBuffer* pb, const RenderedCursor* renderedCursor); + void writeLosslessRefresh(const Region& req, const PixelBuffer* pb, + const RenderedCursor* renderedCursor, + size_t maxUpdateSize); + protected: - void prepareEncoders(); + void doUpdate(bool allowLossy, const Region& changed, + const Region& copied, const Point& copy_delta, + const PixelBuffer* pb, + const RenderedCursor* renderedCursor); + void prepareEncoders(bool allowLossy); + + Region getLosslessRefresh(const Region& req, size_t maxUpdateSize); int computeNumRects(const Region& changed); Encoder *startRect(const Rect& rect, int type); void endRect(); - void writeCopyRects(const UpdateInfo& ui); + void writeCopyRects(const Region& copied, const Point& delta); void writeSolidRects(Region *changed, const PixelBuffer* pb); void findSolidRect(const Rect& rect, Region *changed, const PixelBuffer* pb); void writeRects(const Region& changed, const PixelBuffer* pb); @@ -103,6 +116,8 @@ namespace rfb { std::vector<Encoder*> encoders; std::vector<int> activeEncoders; + Region lossyRegion; + struct EncoderStats { unsigned rects; unsigned long long bytes; diff --git a/common/rfb/Encoder.h b/common/rfb/Encoder.h index a8a447e2..66a10d26 100644 --- a/common/rfb/Encoder.h +++ b/common/rfb/Encoder.h @@ -35,6 +35,8 @@ namespace rfb { // Give us the raw frame buffer, and not something converted to // the what the client is asking for. EncoderUseNativePF = 1 << 0, + // Encoder does not encode pixels perfectly accurate + EncoderLossy = 1 << 1, }; class Encoder { diff --git a/common/rfb/Region.cxx b/common/rfb/Region.cxx index 995f8c5b..c17c5d48 100644 --- a/common/rfb/Region.cxx +++ b/common/rfb/Region.cxx @@ -143,10 +143,6 @@ void rfb::Region::setExtentsAndOrderedRects(const ShortRect* extents, } } -void rfb::Region::copyFrom(const rfb::Region& r) { - XUnionRegion(r.xrgn, r.xrgn, xrgn); -} - void rfb::Region::assign_intersect(const rfb::Region& r) { XIntersectRegion(xrgn, r.xrgn, xrgn); } diff --git a/common/rfb/Region.h b/common/rfb/Region.h index 93375569..9e53d362 100644 --- a/common/rfb/Region.h +++ b/common/rfb/Region.h @@ -52,7 +52,6 @@ namespace rfb { void setOrderedRects(const std::vector<Rect>& rects); void setExtentsAndOrderedRects(const ShortRect* extents, int nRects, const ShortRect* rects); - void copyFrom(const Region& r); void assign_intersect(const Region& r); void assign_union(const Region& r); diff --git a/common/rfb/TightJPEGEncoder.cxx b/common/rfb/TightJPEGEncoder.cxx index 7bb61265..385207f7 100644 --- a/common/rfb/TightJPEGEncoder.cxx +++ b/common/rfb/TightJPEGEncoder.cxx @@ -64,7 +64,7 @@ static const struct TightJPEGConfiguration conf[10] = { TightJPEGEncoder::TightJPEGEncoder(SConnection* conn) : - Encoder(conn, encodingTight, EncoderUseNativePF, -1), + Encoder(conn, encodingTight, (EncoderFlags)(EncoderUseNativePF | EncoderLossy), -1), qualityLevel(-1), fineQuality(-1), fineSubsampling(subsampleUndefined) { } diff --git a/common/rfb/Timer.cxx b/common/rfb/Timer.cxx index 7179cd87..fd1a87ae 100644 --- a/common/rfb/Timer.cxx +++ b/common/rfb/Timer.cxx @@ -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) && diff --git a/common/rfb/Timer.h b/common/rfb/Timer.h index 78687d1a..15b5d03d 100644 --- a/common/rfb/Timer.h +++ b/common/rfb/Timer.h @@ -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); diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx index e707e492..126fb4e8 100644 --- a/common/rfb/VNCSConnectionST.cxx +++ b/common/rfb/VNCSConnectionST.cxx @@ -1,5 +1,5 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. - * Copyright 2009-2016 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 @@ -223,6 +223,9 @@ void VNCSConnectionST::pixelBufferChange() } } } + + // Drop any lossy tracking that is now outside the framebuffer + encodeManager.pruneLosslessRefresh(Region(server->pb->getRect())); } // Just update the whole screen at the moment because we're too lazy to // work out what's actually changed. @@ -962,18 +965,13 @@ void VNCSConnectionST::writeNoDataUpdate() void VNCSConnectionST::writeDataUpdate() { - Region req; + Region req, pending; UpdateInfo ui; bool needNewUpdateInfo; const RenderedCursor *cursor; updates.enable_copyrect(cp.useCopyRect); - // See if we are allowed to send anything right now (the framebuffer - // might have changed in ways we haven't yet been informed of). - if (!server->checkUpdate()) - return; - // See what the client has requested (if anything) if (continuousUpdates) req = cuRegion.union_(requested); @@ -983,6 +981,9 @@ void VNCSConnectionST::writeDataUpdate() if (req.is_empty()) return; + // Get any framebuffer changes we haven't yet been informed of + pending = server->getPendingRegion(); + // Get the lists of updates. Prior to exporting the data to the `ui' object, // getUpdateInfo() will normalize the `updates' object such way that its // `changed' and `copied' regions would not intersect. @@ -996,7 +997,7 @@ void VNCSConnectionST::writeDataUpdate() if (!ui.copied.is_empty() && !damagedCursorRegion.is_empty()) { Region bogusCopiedCursor; - bogusCopiedCursor.copyFrom(damagedCursorRegion); + bogusCopiedCursor = damagedCursorRegion; bogusCopiedCursor.translate(ui.copy_delta); bogusCopiedCursor.assign_intersect(server->pb->getRect()); if (!ui.copied.intersect(bogusCopiedCursor).is_empty()) { @@ -1015,53 +1016,78 @@ void VNCSConnectionST::writeDataUpdate() removeRenderedCursor = false; } - // Return if there is nothing to send the client. + // If we need a full cursor update then make sure its entire region + // is marked as changed. - if (updates.is_empty() && !writer()->needFakeUpdate() && !updateRenderedCursor) - return; + if (updateRenderedCursor) { + updates.add_changed(server->getRenderedCursor()->getEffectiveRect()); + needNewUpdateInfo = true; + updateRenderedCursor = false; + } // The `updates' object could change, make sure we have valid update info. if (needNewUpdateInfo) updates.getUpdateInfo(&ui, req); - // If the client needs a server-side rendered cursor, work out the cursor - // rectangle. If it's empty then don't bother drawing it, but if it overlaps - // with the update region, we need to draw the rendered cursor regardless of - // whether it has changed. + // If there are queued updates then we cannot safely send an update + // without risking a partially updated screen + + if (!pending.is_empty()) { + // However we might still be able to send a lossless refresh + req.assign_subtract(pending); + req.assign_subtract(ui.changed); + req.assign_subtract(ui.copied); + + ui.changed.clear(); + ui.copied.clear(); + } + + // Does the client need a server-side rendered cursor? cursor = NULL; if (needRenderedCursor()) { Rect renderedCursorRect; cursor = server->getRenderedCursor(); - - renderedCursorRect - = cursor->getEffectiveRect().intersect(req.get_bounding_rect()); - - if (renderedCursorRect.is_empty()) { - cursor = NULL; - } else if (!updateRenderedCursor && - ui.changed.union_(ui.copied) - .intersect(renderedCursorRect).is_empty()) { - cursor = NULL; + renderedCursorRect = cursor->getEffectiveRect(); + + // Check that we don't try to copy over the cursor area, and + // if that happens we need to treat it as changed so that we can + // re-render it + if (!ui.copied.intersect(renderedCursorRect).is_empty()) { + ui.changed.assign_union(ui.copied.intersect(renderedCursorRect)); + ui.copied.assign_subtract(renderedCursorRect); } - if (cursor) { - updates.subtract(renderedCursorRect); - updates.getUpdateInfo(&ui, req); - } - - damagedCursorRegion.assign_union(renderedCursorRect); - updateRenderedCursor = false; + // Track where we've rendered the cursor + damagedCursorRegion.assign_union(ui.changed.intersect(renderedCursorRect)); } - if (ui.is_empty() && !writer()->needFakeUpdate() && !cursor) + // Return if there is nothing to send the client. + + if (ui.is_empty() && !writer()->needFakeUpdate() && + !encodeManager.needsLosslessRefresh(req)) return; writeRTTPing(); - encodeManager.writeUpdate(ui, server->getPixelBuffer(), cursor); + if (!ui.is_empty()) + encodeManager.writeUpdate(ui, 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(); diff --git a/common/rfb/VNCServerST.cxx b/common/rfb/VNCServerST.cxx index 0008dc41..95870c92 100644 --- a/common/rfb/VNCServerST.cxx +++ b/common/rfb/VNCServerST.cxx @@ -1,5 +1,5 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. - * Copyright 2009-2017 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 @@ -105,10 +105,7 @@ VNCServerST::~VNCServerST() } // Stop the desktop object if active, *only* after deleting all clients! - if (desktopStarted) { - desktopStarted = false; - desktop->stop(); - } + stopDesktop(); if (comparer) comparer->logStats(); @@ -154,12 +151,8 @@ void VNCServerST::removeSocket(network::Socket* sock) { delete *ci; // - Check that the desktop object is still required - if (authClientCount() == 0 && desktopStarted) { - slog.debug("no authenticated clients - stopping desktop"); - desktopStarted = false; - desktop->stop(); - stopFrameClock(); - } + if (authClientCount() == 0) + stopDesktop(); if (comparer) comparer->logStats(); @@ -552,6 +545,16 @@ void VNCServerST::startDesktop() } } +void VNCServerST::stopDesktop() +{ + if (desktopStarted) { + slog.debug("stopping desktop"); + desktopStarted = false; + desktop->stop(); + stopFrameClock(); + } +} + int VNCServerST::authClientCount() { int count = 0; std::list<VNCSConnectionST*>::iterator ci; @@ -576,6 +579,8 @@ void VNCServerST::startFrameClock() return; if (blockCounter > 0) return; + if (!desktopStarted) + return; // The first iteration will be just half a frame as we get a very // unstable update rate if we happen to be perfectly in sync with @@ -588,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 @@ -603,6 +619,7 @@ void VNCServerST::writeUpdate() std::list<VNCSConnectionST*>::iterator ci, ci_next; assert(blockCounter == 0); + assert(desktopStarted); comparer->getUpdateInfo(&ui, pb->getRect()); toCheck = ui.changed.union_(ui.copied); @@ -639,17 +656,21 @@ void VNCServerST::writeUpdate() // checkUpdate() is called by clients to see if it is safe to read from // the framebuffer at this time. -bool VNCServerST::checkUpdate() +Region VNCServerST::getPendingRegion() { + UpdateInfo ui; + // Block clients as the frame buffer cannot be safely accessed if (blockCounter > 0) - return false; + return pb->getRect(); // Block client from updating if there are pending updates - if (!comparer->is_empty()) - return false; + if (comparer->is_empty()) + return Region(); + + comparer->getUpdateInfo(&ui, pb->getRect()); - return true; + return ui.changed.union_(ui.copied); } const RenderedCursor* VNCServerST::getRenderedCursor() diff --git a/common/rfb/VNCServerST.h b/common/rfb/VNCServerST.h index e00a1f7e..b7845ddd 100644 --- a/common/rfb/VNCServerST.h +++ b/common/rfb/VNCServerST.h @@ -195,6 +195,7 @@ namespace rfb { // - Internal methods void startDesktop(); + void stopDesktop(); static LogWriter connectionsLog; Blacklist blacklist; @@ -226,8 +227,9 @@ namespace rfb { bool needRenderedCursor(); void startFrameClock(); void stopFrameClock(); + int msToNextUpdate(); void writeUpdate(); - bool checkUpdate(); + Region getPendingRegion(); const RenderedCursor* getRenderedCursor(); void notifyScreenLayoutChange(VNCSConnectionST *requester); |