summaryrefslogtreecommitdiffstats
path: root/vncviewer/DesktopWindow.cxx
diff options
context:
space:
mode:
authorPierre Ossman <ossman@cendio.se>2017-02-24 12:33:09 +0100
committerPierre Ossman <ossman@cendio.se>2017-02-24 12:34:27 +0100
commit921f6c86ba2a6761ac4f640dd36903dc8bb9eed7 (patch)
tree6d8359699444ae08ffd82a27a387266aed13d76e /vncviewer/DesktopWindow.cxx
parent2e7c744426e4dff6c630e973a1464df814f24abe (diff)
downloadtigervnc-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/DesktopWindow.cxx')
-rw-r--r--vncviewer/DesktopWindow.cxx155
1 files changed, 154 insertions, 1 deletions
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);
+}