diff options
author | Pierre Ossman <ossman@cendio.se> | 2017-02-24 12:33:09 +0100 |
---|---|---|
committer | Pierre Ossman <ossman@cendio.se> | 2017-02-24 12:34:27 +0100 |
commit | 921f6c86ba2a6761ac4f640dd36903dc8bb9eed7 (patch) | |
tree | 6d8359699444ae08ffd82a27a387266aed13d76e /vncviewer | |
parent | 2e7c744426e4dff6c630e973a1464df814f24abe (diff) | |
download | tigervnc-921f6c86ba2a6761ac4f640dd36903dc8bb9eed7.tar.gz tigervnc-921f6c86ba2a6761ac4f640dd36903dc8bb9eed7.zip |
Display performance statistics in viewer
Adds an optional graph to the viewer to display current frame rate,
pixel rate and network bandwidth. Makes it easier to debug and test
performance related issues.
Diffstat (limited to 'vncviewer')
-rw-r--r-- | vncviewer/CConn.cxx | 21 | ||||
-rw-r--r-- | vncviewer/CConn.h | 7 | ||||
-rw-r--r-- | vncviewer/DesktopWindow.cxx | 155 | ||||
-rw-r--r-- | vncviewer/DesktopWindow.h | 18 |
4 files changed, 199 insertions, 2 deletions
diff --git a/vncviewer/CConn.cxx b/vncviewer/CConn.cxx index a692732c..addc30df 100644 --- a/vncviewer/CConn.cxx +++ b/vncviewer/CConn.cxx @@ -73,7 +73,7 @@ static const PixelFormat mediumColourPF(8, 8, false, true, CConn::CConn(const char* vncServerName, network::Socket* socket=NULL) : serverHost(0), serverPort(0), desktop(NULL), - pendingPFChange(false), + frameCount(0), pixelCount(0), pendingPFChange(false), currentEncoding(encodingTight), lastServerEncoding((unsigned int)-1), formatChange(false), encodingChange(false), firstUpdate(true), pendingUpdate(false), continuousUpdates(false), @@ -223,6 +223,21 @@ const char *CConn::connectionInfo() return infoText; } +unsigned CConn::getFrameCount() +{ + return frameCount; +} + +unsigned CConn::getPixelCount() +{ + return pixelCount; +} + +unsigned CConn::getPosition() +{ + return sock->inStream().pos(); +} + // The RFB core is not properly asynchronous, so it calls this callback // whenever it needs to block to wait for more data. Since FLTK is // monitoring the socket, we just make sure FLTK gets to run. @@ -365,6 +380,8 @@ void CConn::framebufferUpdateEnd() { CConnection::framebufferUpdateEnd(); + frameCount++; + Fl::remove_timeout(handleUpdateTimeout, this); desktop->updateWindow(); @@ -441,6 +458,8 @@ void CConn::dataRect(const Rect& r, int encoding) CConnection::dataRect(r, encoding); sock->inStream().stopTiming(); + + pixelCount += r.area(); } void CConn::setCursor(int width, int height, const Point& hotspot, diff --git a/vncviewer/CConn.h b/vncviewer/CConn.h index d6dd4a75..93cc278f 100644 --- a/vncviewer/CConn.h +++ b/vncviewer/CConn.h @@ -40,6 +40,10 @@ public: const char *connectionInfo(); + unsigned getFrameCount(); + unsigned getPixelCount(); + unsigned getPosition(); + // FdInStreamBlockCallback methods void blockCallback(); @@ -89,6 +93,9 @@ private: DesktopWindow *desktop; + unsigned frameCount; + unsigned pixelCount; + rfb::PixelFormat serverPF; rfb::PixelFormat fullColourPF; diff --git a/vncviewer/DesktopWindow.cxx b/vncviewer/DesktopWindow.cxx index 8fdd59b7..1f0f55f2 100644 --- a/vncviewer/DesktopWindow.cxx +++ b/vncviewer/DesktopWindow.cxx @@ -64,7 +64,9 @@ DesktopWindow::DesktopWindow(int w, int h, const char *name, CConn* cc_) : Fl_Window(w, h), cc(cc_), offscreen(NULL), overlay(NULL), firstUpdate(true), - delayedFullscreen(false), delayedDesktopSize(false) + delayedFullscreen(false), delayedDesktopSize(false), + statsLastFrame(0), statsLastPixels(0), statsLastPosition(0), + statsGraph(NULL) { Fl_Group* group; @@ -174,6 +176,12 @@ DesktopWindow::DesktopWindow(int w, int h, const char *name, fullscreen_on(); } + // Throughput graph for debugging + if (vlog.getLevel() >= LogWriter::LEVEL_DEBUG) { + memset(&stats, 0, sizeof(stats)); + Fl::add_timeout(0, handleStatsTimeout, this); + } + // Show hint about menu key Fl::add_timeout(0.5, menuOverlay, this); } @@ -187,6 +195,7 @@ DesktopWindow::~DesktopWindow() Fl::remove_timeout(handleResizeTimeout, this); Fl::remove_timeout(handleFullscreenTimeout, this); Fl::remove_timeout(handleEdgeScroll, this); + Fl::remove_timeout(handleStatsTimeout, this); Fl::remove_timeout(menuOverlay, this); Fl::remove_timeout(updateOverlay, this); @@ -195,6 +204,8 @@ DesktopWindow::~DesktopWindow() delete overlay; delete offscreen; + delete statsGraph; + // FLTK automatically deletes all child widgets, so we shouldn't touch // them ourselves here } @@ -325,6 +336,23 @@ void DesktopWindow::draw() update_child(*viewport); } + // Debug graph (if active) + if (statsGraph) { + int ox, oy, ow, oh; + + ox = X = w() - statsGraph->width() - 30; + oy = Y = h() - statsGraph->height() - 30; + ow = statsGraph->width(); + oh = statsGraph->height(); + + fl_clip_box(ox, oy, ow, oh, ox, oy, ow, oh); + + if (offscreen) + statsGraph->blend(offscreen, ox - X, oy - Y, ox, oy, ow, oh, 204); + else + statsGraph->blend(ox - X, oy - Y, ox, oy, ow, oh, 204); + } + // Overlay (if active) if (overlay) { int ox, oy, ow, oh; @@ -1161,3 +1189,128 @@ void DesktopWindow::handleEdgeScroll(void *data) Fl::repeat_timeout(0.1, handleEdgeScroll, data); } + +void DesktopWindow::handleStatsTimeout(void *data) +{ + DesktopWindow *self = (DesktopWindow*)data; + + const size_t statsCount = sizeof(stats)/sizeof(stats[0]); + + unsigned frame, pixels, pos; + unsigned elapsed; + + const unsigned statsWidth = 200; + const unsigned statsHeight = 100; + const unsigned graphWidth = statsWidth - 10; + const unsigned graphHeight = statsHeight - 25; + + Fl_Image_Surface *surface; + Fl_RGB_Image *image; + + unsigned maxFPS, maxPPS, maxBPS; + size_t i; + + char buffer[256]; + + frame = self->cc->getFrameCount(); + pixels = self->cc->getPixelCount(); + pos = self->cc->getPosition(); + elapsed = msSince(&self->statsLastTime); + if (elapsed < 1) + elapsed = 1; + + memmove(&self->stats[0], &self->stats[1], sizeof(stats[0])*(statsCount-1)); + + self->stats[statsCount-1].fps = (frame - self->statsLastFrame) * 1000 / elapsed; + self->stats[statsCount-1].pps = (pixels - self->statsLastPixels) * 1000 / elapsed; + self->stats[statsCount-1].bps = (pos - self->statsLastPosition) * 1000 / elapsed; + + gettimeofday(&self->statsLastTime, NULL); + self->statsLastFrame = frame; + self->statsLastPixels = pixels; + self->statsLastPosition = pos; + +#if !defined(WIN32) && !defined(__APPLE__) + // FLTK < 1.3.5 crashes if fl_gc is unset + if (!fl_gc) + fl_gc = XDefaultGC(fl_display, 0); +#endif + + surface = new Fl_Image_Surface(statsWidth, statsHeight); + surface->set_current(); + + fl_rectf(0, 0, statsWidth, statsHeight, FL_BLACK); + + fl_rect(5, 5, graphWidth, graphHeight, FL_WHITE); + + maxFPS = maxPPS = maxBPS = 0; + for (i = 0;i < statsCount;i++) { + if (self->stats[i].fps > maxFPS) + maxFPS = self->stats[i].fps; + if (self->stats[i].pps > maxPPS) + maxPPS = self->stats[i].pps; + if (self->stats[i].bps > maxBPS) + maxBPS = self->stats[i].bps; + } + + if (maxFPS != 0) { + fl_color(FL_GREEN); + for (i = 0;i < statsCount-1;i++) { + fl_line(5 + i * graphWidth / statsCount, + 5 + graphHeight - graphHeight * self->stats[i].fps / maxFPS, + 5 + (i+1) * graphWidth / statsCount, + 5 + graphHeight - graphHeight * self->stats[i+1].fps / maxFPS); + } + } + + if (maxPPS != 0) { + fl_color(FL_YELLOW); + for (i = 0;i < statsCount-1;i++) { + fl_line(5 + i * graphWidth / statsCount, + 5 + graphHeight - graphHeight * self->stats[i].pps / maxPPS, + 5 + (i+1) * graphWidth / statsCount, + 5 + graphHeight - graphHeight * self->stats[i+1].pps / maxPPS); + } + } + + if (maxBPS != 0) { + fl_color(FL_RED); + for (i = 0;i < statsCount-1;i++) { + fl_line(5 + i * graphWidth / statsCount, + 5 + graphHeight - graphHeight * self->stats[i].bps / maxBPS, + 5 + (i+1) * graphWidth / statsCount, + 5 + graphHeight - graphHeight * self->stats[i+1].bps / maxBPS); + } + } + + fl_font(FL_HELVETICA, 10); + + fl_color(FL_GREEN); + snprintf(buffer, sizeof(buffer), "%u fps", self->stats[statsCount-1].fps); + fl_draw(buffer, 5, statsHeight - 5); + + fl_color(FL_YELLOW); + siPrefix(self->stats[statsCount-1].pps * 8, "pix/s", + buffer, sizeof(buffer), 3); + fl_draw(buffer, 5 + (statsWidth-10)/3, statsHeight - 5); + + fl_color(FL_RED); + iecPrefix(self->stats[statsCount-1].bps * 8, "Bps", + buffer, sizeof(buffer), 3); + fl_draw(buffer, 5 + (statsWidth-10)*2/3, statsHeight - 5); + + image = surface->image(); + delete surface; + + Fl_Display_Device::display_device()->set_current(); + + delete self->statsGraph; + self->statsGraph = new Surface(image); + delete image; + + self->damage(FL_DAMAGE_CHILD, self->w() - statsWidth - 30, + self->h() - statsHeight - 30, + statsWidth, statsHeight); + + Fl::repeat_timeout(0.5, handleStatsTimeout, data); +} diff --git a/vncviewer/DesktopWindow.h b/vncviewer/DesktopWindow.h index 11f3dc20..4224699c 100644 --- a/vncviewer/DesktopWindow.h +++ b/vncviewer/DesktopWindow.h @@ -22,6 +22,8 @@ #include <map> +#include <sys/time.h> + #include <rfb/Rect.h> #include <rfb/Pixel.h> @@ -103,6 +105,8 @@ private: static void handleScroll(Fl_Widget *wnd, void *data); static void handleEdgeScroll(void *data); + static void handleStatsTimeout(void *data); + private: CConn* cc; Fl_Scrollbar *hscroll, *vscroll; @@ -115,6 +119,20 @@ private: bool firstUpdate; bool delayedFullscreen; bool delayedDesktopSize; + + struct statsEntry { + unsigned fps; + unsigned pps; + unsigned bps; + }; + struct statsEntry stats[100]; + + struct timeval statsLastTime; + unsigned statsLastFrame; + unsigned statsLastPixels; + unsigned statsLastPosition; + + Surface *statsGraph; }; #endif |