From c9dd3a40234b54c7cc9bade71a6cd84af5325fb2 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Wed, 18 Nov 2015 16:24:16 +0100 Subject: [PATCH] Throttle overlapping screen updates We need to make sure the display server has finished reading our previous update before we overwrite the buffer with the next update. --- vncviewer/CConn.cxx | 20 ++++++++++++ vncviewer/PlatformPixelBuffer.cxx | 5 +++ vncviewer/PlatformPixelBuffer.h | 2 ++ vncviewer/X11PixelBuffer.cxx | 53 ++++++++++++++++++++++++++++--- vncviewer/X11PixelBuffer.h | 9 ++++++ 5 files changed, 85 insertions(+), 4 deletions(-) diff --git a/vncviewer/CConn.cxx b/vncviewer/CConn.cxx index fb136b5f..262dd2ce 100644 --- a/vncviewer/CConn.cxx +++ b/vncviewer/CConn.cxx @@ -46,6 +46,7 @@ #include "CConn.h" #include "OptionsDialog.h" #include "DesktopWindow.h" +#include "PlatformPixelBuffer.h" #include "i18n.h" #include "parameters.h" #include "vncviewer.h" @@ -336,6 +337,9 @@ void CConn::setName(const char* name) // one. void CConn::framebufferUpdateStart() { + ModifiablePixelBuffer* pb; + PlatformPixelBuffer* ppb; + CConnection::framebufferUpdateStart(); // Note: This might not be true if sync fences are supported @@ -343,6 +347,22 @@ void CConn::framebufferUpdateStart() requestNewUpdate(); + // We might still be rendering the previous update + pb = getFramebuffer(); + assert(pb != NULL); + ppb = dynamic_cast(pb); + assert(ppb != NULL); + if (ppb->isRendering()) { + // Need to stop monitoring the socket or we'll just busy loop + assert(sock != NULL); + Fl::remove_fd(sock->getFd()); + + while (ppb->isRendering()) + run_mainloop(); + + Fl::add_fd(sock->getFd(), FL_READ | FL_EXCEPT, socketEvent, this); + } + // Update the screen prematurely for very slow updates Fl::add_timeout(1.0, handleUpdateTimeout, this); } diff --git a/vncviewer/PlatformPixelBuffer.cxx b/vncviewer/PlatformPixelBuffer.cxx index 522bad3f..876ab94a 100644 --- a/vncviewer/PlatformPixelBuffer.cxx +++ b/vncviewer/PlatformPixelBuffer.cxx @@ -44,3 +44,8 @@ rfb::Rect PlatformPixelBuffer::getDamage(void) return r; } + +bool PlatformPixelBuffer::isRendering(void) +{ + return false; +} diff --git a/vncviewer/PlatformPixelBuffer.h b/vncviewer/PlatformPixelBuffer.h index 795273a9..9f0e3b16 100644 --- a/vncviewer/PlatformPixelBuffer.h +++ b/vncviewer/PlatformPixelBuffer.h @@ -34,6 +34,8 @@ public: virtual void draw(int src_x, int src_y, int x, int y, int w, int h) = 0; rfb::Rect getDamage(void); + virtual bool isRendering(void); + protected: os::Mutex mutex; rfb::Region damage; diff --git a/vncviewer/X11PixelBuffer.cxx b/vncviewer/X11PixelBuffer.cxx index 9196fdca..046676eb 100644 --- a/vncviewer/X11PixelBuffer.cxx +++ b/vncviewer/X11PixelBuffer.cxx @@ -21,8 +21,10 @@ #include #endif +#include #include +#include #include #include @@ -35,6 +37,8 @@ using namespace rfb; static rfb::LogWriter vlog("X11PixelBuffer"); +std::list X11PixelBuffer::shmList; + static PixelFormat display_pf() { int i; @@ -97,7 +101,7 @@ static PixelFormat display_pf() X11PixelBuffer::X11PixelBuffer(int width, int height) : PlatformPixelBuffer(display_pf(), width, height, NULL, 0), - shminfo(NULL), xim(NULL) + shminfo(NULL), xim(NULL), pendingPutImage(0) { // Might not be open at this point fl_open_display(); @@ -122,6 +126,8 @@ X11PixelBuffer::~X11PixelBuffer() { if (shminfo) { vlog.debug("Freeing shared memory XImage"); + shmList.remove(this); + Fl::remove_system_handler(handleSystemEvent); shmdt(shminfo->shmaddr); shmctl(shminfo->shmid, IPC_RMID, 0); delete shminfo; @@ -137,12 +143,18 @@ X11PixelBuffer::~X11PixelBuffer() void X11PixelBuffer::draw(int src_x, int src_y, int x, int y, int w, int h) { - if (shminfo) - XShmPutImage(fl_display, fl_window, fl_gc, xim, src_x, src_y, x, y, w, h, False); - else + if (shminfo) { + XShmPutImage(fl_display, fl_window, fl_gc, xim, src_x, src_y, x, y, w, h, True); + pendingPutImage++; + } else { XPutImage(fl_display, fl_window, fl_gc, xim, src_x, src_y, x, y, w, h); + } } +bool X11PixelBuffer::isRendering(void) +{ + return pendingPutImage > 0; +} static bool caughtError; @@ -202,6 +214,11 @@ int X11PixelBuffer::setupShm() if (caughtError) goto free_shmaddr; + // FLTK is a bit stupid and unreliable if you register the same + // callback with different data values. + Fl::add_system_handler(handleSystemEvent, NULL); + shmList.push_back(this); + vlog.debug("Using shared memory XImage"); return 1; @@ -222,3 +239,31 @@ free_shminfo: return 0; } + +int X11PixelBuffer::handleSystemEvent(void* event, void* data) +{ + XEvent* xevent; + XShmCompletionEvent* shmevent; + + std::list::iterator iter; + + xevent = (XEvent*)event; + assert(xevent); + + if (xevent->type != XShmGetEventBase(fl_display)) + return 0; + + shmevent = (XShmCompletionEvent*)event; + + for (iter = shmList.begin();iter != shmList.end();++iter) { + if (shmevent->shmseg != (*iter)->shminfo->shmseg) + continue; + + (*iter)->pendingPutImage--; + assert((*iter)->pendingPutImage >= 0); + + return 1; + } + + return 0; +} diff --git a/vncviewer/X11PixelBuffer.h b/vncviewer/X11PixelBuffer.h index c2ffdc28..115984da 100644 --- a/vncviewer/X11PixelBuffer.h +++ b/vncviewer/X11PixelBuffer.h @@ -24,6 +24,8 @@ #include #include +#include + #include "PlatformPixelBuffer.h" class X11PixelBuffer: public PlatformPixelBuffer { @@ -33,12 +35,19 @@ public: virtual void draw(int src_x, int src_y, int x, int y, int w, int h); + virtual bool isRendering(void); + protected: int setupShm(); + static int handleSystemEvent(void* event, void* data); + protected: XShmSegmentInfo *shminfo; XImage *xim; + int pendingPutImage; + + static std::list shmList; }; -- 2.39.5