Send updates with a fixed interval
This redesigns the old "deferred updates" mechanism in to a frame
clock that governs how often updates are sent out. The goal is still
the same, to aggregate updates and avoid pointless updates, all in
the name of efficiency. This model should however be more robust
against delays that sometimes causes us to miss the desired rate.
diff --git a/common/rfb/VNCServerST.cxx b/common/rfb/VNCServerST.cxx
index 80c79fc..80c992f 100644
--- a/common/rfb/VNCServerST.cxx
+++ b/common/rfb/VNCServerST.cxx
@@ -66,12 +66,6 @@
static LogWriter slog("VNCServerST");
LogWriter VNCServerST::connectionsLog("Connections");
-rfb::IntParameter deferUpdateTime("DeferUpdate",
- "Time in milliseconds to defer updates",1);
-
-rfb::BoolParameter alwaysSetDeferUpdateTimer("AlwaysSetDeferUpdateTimer",
- "Always reset the defer update timer on every change",false);
-
//
// -=- VNCServerST Implementation
//
@@ -85,7 +79,7 @@
renderedCursorInvalid(false),
queryConnectionHandler(0), keyRemapper(&KeyRemapper::defInstance),
lastConnectionTime(0), disableclients(false),
- deferTimer(this), deferPending(false)
+ frameTimer(this)
{
lastUserInputTime = lastDisconnectTime = time(0);
slog.debug("creating single-threaded server %s", name.buf);
@@ -98,6 +92,9 @@
// Close any active clients, with appropriate logging & cleanup
closeClients("Server shutdown");
+ // Stop trying to render things
+ stopFrameClock();
+
// Delete all the clients, and their sockets, and any closing sockets
// NB: Deleting a client implicitly removes it from the clients list
while (!clients.empty()) {
@@ -283,6 +280,8 @@
void VNCServerST::blockUpdates()
{
blockCounter++;
+
+ stopFrameClock();
}
void VNCServerST::unblockUpdates()
@@ -291,9 +290,11 @@
blockCounter--;
- // Flush out any updates we might have blocked
- if (blockCounter == 0)
- tryUpdate();
+ // Restart the frame clock if we have updates
+ if (blockCounter == 0) {
+ if (!comparer->is_empty())
+ startFrameClock();
+ }
}
void VNCServerST::setPixelBuffer(PixelBuffer* pb_, const ScreenSet& layout)
@@ -415,8 +416,7 @@
return;
comparer->add_changed(region);
- startDefer();
- tryUpdate();
+ startFrameClock();
}
void VNCServerST::add_copied(const Region& dest, const Point& delta)
@@ -425,8 +425,7 @@
return;
comparer->add_copied(dest, delta);
- startDefer();
- tryUpdate();
+ startFrameClock();
}
void VNCServerST::setCursor(int width, int height, const Point& newHotspot,
@@ -508,10 +507,14 @@
bool VNCServerST::handleTimeout(Timer* t)
{
- if (t != &deferTimer)
- return false;
+ if (t == &frameTimer) {
+ // We keep running until we go a full interval without any updates
+ if (comparer->is_empty())
+ return false;
- tryUpdate();
+ writeUpdate();
+ return true;
+ }
return false;
}
@@ -547,77 +550,41 @@
return false;
}
-inline void VNCServerST::startDefer()
+void VNCServerST::startFrameClock()
{
- if (deferUpdateTime == 0)
+ if (frameTimer.isStarted())
return;
-
- if (deferPending && !alwaysSetDeferUpdateTimer)
- return;
-
- gettimeofday(&deferStart, NULL);
- deferTimer.start(deferUpdateTime);
-
- deferPending = true;
-}
-
-inline bool VNCServerST::checkDefer()
-{
- if (!deferPending)
- return true;
-
- if (msSince(&deferStart) >= (unsigned)deferUpdateTime)
- return true;
-
- return false;
-}
-
-void VNCServerST::tryUpdate()
-{
- std::list<VNCSConnectionST*>::iterator ci, ci_next;
-
if (blockCounter > 0)
return;
- if (!checkDefer())
- return;
-
- for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
- ci_next = ci; ci_next++;
- (*ci)->writeFramebufferUpdateOrClose();
- }
+ frameTimer.start(1000/rfb::Server::frameRate);
}
-// checkUpdate() is called just before sending an update. It checks to see
-// what updates are pending and propagates them to the update tracker for each
-// client. It uses the ComparingUpdateTracker's compare() method to filter out
-// areas of the screen which haven't actually changed. It also checks the
-// state of the (server-side) rendered cursor, if necessary rendering it again
-// with the correct background.
+void VNCServerST::stopFrameClock()
+{
+ frameTimer.stop();
+}
-bool VNCServerST::checkUpdate()
+// 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
+// to filter out areas of the screen which haven't actually changed. It
+// also checks the state of the (server-side) rendered cursor, if
+// necessary rendering it again with the correct background.
+
+void VNCServerST::writeUpdate()
{
UpdateInfo ui;
+ Region toCheck;
+
+ std::list<VNCSConnectionST*>::iterator ci, ci_next;
+
+ assert(blockCounter == 0);
+
comparer->getUpdateInfo(&ui, pb->getRect());
+ toCheck = ui.changed.union_(ui.copied);
- bool renderCursor = needRenderedCursor();
-
- if (ui.is_empty() && !(renderCursor && renderedCursorInvalid))
- return true;
-
- // Block clients as the frame buffer cannot be safely accessed
- if (blockCounter > 0)
- return false;
-
- // Block client from updating if we are currently deferring updates
- if (!checkDefer())
- return false;
-
- deferPending = false;
-
- Region toCheck = ui.changed.union_(ui.copied);
-
- if (renderCursor) {
+ if (needRenderedCursor()) {
Rect clippedCursorRect
= cursor.getRect(cursorPos.subtract(cursor.hotspot)).intersect(pb->getRect());
@@ -635,14 +602,28 @@
if (comparer->compare())
comparer->getUpdateInfo(&ui, pb->getRect());
- std::list<VNCSConnectionST*>::iterator ci, ci_next;
+ comparer->clear();
+
for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
ci_next = ci; ci_next++;
(*ci)->add_copied(ui.copied, ui.copy_delta);
(*ci)->add_changed(ui.changed);
+ (*ci)->writeFramebufferUpdateOrClose();
}
+}
- comparer->clear();
+// checkUpdate() is called by clients to see if it is safe to read from
+// the framebuffer at this time.
+
+bool VNCServerST::checkUpdate()
+{
+ // Block clients as the frame buffer cannot be safely accessed
+ if (blockCounter > 0)
+ return false;
+
+ // Block client from updating if there are pending updates
+ if (!comparer->is_empty())
+ return false;
return true;
}