Add test to measure framebuffer performance
diff --git a/tests/fbperf.cxx b/tests/fbperf.cxx
new file mode 100644
index 0000000..53334a6
--- /dev/null
+++ b/tests/fbperf.cxx
@@ -0,0 +1,225 @@
+/* Copyright 2016 Pierre Ossman <ossman@cendio.se> 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+#include <sys/time.h>
+
+#include <FL/Fl.H>
+#include <FL/Fl_Window.H>
+#include <FL/fl_draw.H>
+
+#include <rdr/Exception.h>
+#include <rfb/util.h>
+
+#include "../vncviewer/PlatformPixelBuffer.h"
+#include "../vncviewer/FLTKPixelBuffer.h"
+
+#if defined(WIN32)
+#include "../vncviewer/Win32PixelBuffer.h"
+#elif defined(__APPLE__)
+#include "../vncviewer/OSXPixelBuffer.h"
+#else
+#include "../vncviewer/X11PixelBuffer.h"
+#endif
+
+#include "util.h"
+
+class TestWindow: public Fl_Window {
+public:
+  TestWindow();
+  ~TestWindow();
+
+  virtual void draw();
+
+protected:
+  virtual void flush();
+
+  void update();
+  virtual void changefb();
+
+  static void timer(void* data);
+
+public:
+  unsigned long long pixels, frames;
+  double time;
+
+protected:
+  PlatformPixelBuffer* fb;
+};
+
+class PartialTestWindow: public TestWindow {
+protected:
+  virtual void changefb();
+};
+
+TestWindow::TestWindow() :
+  Fl_Window(1024, 768, "Framebuffer Performance Test")
+{
+  rdr::U32 pixel;
+
+  pixels = 0;
+  frames = 0;
+  time = 0;
+
+  try {
+#if defined(WIN32)
+    fb = new Win32PixelBuffer(w(), h());
+#elif defined(__APPLE__)
+    fb = new OSXPixelBuffer(w(), h());
+#else
+    fb = new X11PixelBuffer(w(), h());
+#endif
+  } catch (rdr::Exception& e) {
+    fb = new FLTKPixelBuffer(w(), h());
+  }
+
+  pixel = 0;
+  fb->fillRect(fb->getRect(), &pixel);
+}
+
+TestWindow::~TestWindow()
+{
+  delete fb;
+
+  Fl::remove_idle(timer, this);
+}
+
+void TestWindow::draw()
+{
+  int X, Y, W, H;
+
+  // We cannot update the damage region from inside the draw function,
+  // so delegate this to an idle function
+  Fl::add_idle(timer, this);
+
+  // Check what actually needs updating
+  fl_clip_box(0, 0, w(), h(), X, Y, W, H);
+  if ((W == 0) || (H == 0))
+    return;
+
+  fb->draw(X, Y, X, Y, W, H);
+
+  pixels += W*H;
+  frames++;
+}
+
+void TestWindow::flush()
+{
+  startTimeCounter();
+  Fl_Window::flush();
+#if !defined(WIN32) && !defined(__APPLE__)
+  // Make sure we measure any work we queue up
+  XSync(fl_display, False);
+#endif
+  endTimeCounter();
+
+  time += getTimeCounter();
+}
+
+void TestWindow::update()
+{
+  rfb::Rect r;
+
+  startTimeCounter();
+
+  while (fb->isRendering())
+    Fl::wait();
+
+  changefb();
+
+  r = fb->getDamage();
+  damage(FL_DAMAGE_USER1, r.tl.x, r.tl.y, r.width(), r.height());
+
+#if !defined(WIN32) && !defined(__APPLE__)
+  // Make sure we measure any work we queue up
+  XSync(fl_display, False);
+#endif
+
+  endTimeCounter();
+
+  time += getTimeCounter();
+}
+
+void TestWindow::changefb()
+{
+  rdr::U32 pixel;
+
+  pixel = rand();
+  fb->fillRect(fb->getRect(), &pixel);
+}
+
+void TestWindow::timer(void* data)
+{
+  TestWindow* self;
+
+  Fl::remove_idle(timer, data);
+
+  self = (TestWindow*)data;
+  self->update();
+}
+
+void PartialTestWindow::changefb()
+{
+  rfb::Rect r;
+  rdr::U32 pixel;
+
+  r = fb->getRect();
+  r.tl.x += w() / 4;
+  r.tl.y += h() / 4;
+  r.br.x -= w() / 4;
+  r.br.y -= h() / 4;
+
+  pixel = rand();
+  fb->fillRect(r, &pixel);
+}
+
+static void dotest(TestWindow* win)
+{
+  struct timeval start;
+  char s[1024];
+
+  win->show();
+
+  gettimeofday(&start, NULL);
+  while (rfb::msSince(&start) < 10000)
+    Fl::wait();
+
+  fprintf(stderr, "Rendering time: %g ms/frame\n",
+          win->time * 1000.0 / win->frames);
+  rfb::siPrefix(win->pixels / win->time,
+                "pixels/s", s, sizeof(s));
+  fprintf(stderr, "Rendering rate: %s\n", s);
+}
+
+int main(int argc, char** argv)
+{
+  TestWindow* win;
+
+  fprintf(stderr, "Full window update:\n\n");
+  win = new TestWindow();
+  dotest(win);
+  delete win;
+  fprintf(stderr, "\n");
+
+  fprintf(stderr, "Partial window update:\n\n");
+  win = new PartialTestWindow();
+  dotest(win);
+  delete win;
+  fprintf(stderr, "\n");
+
+  return 0;
+}