]> source.dussan.org Git - tigervnc.git/commitdiff
Add test to measure high level encoding performance
authorPierre Ossman <ossman@cendio.se>
Wed, 11 Feb 2015 12:49:04 +0000 (13:49 +0100)
committerPierre Ossman <ossman@cendio.se>
Fri, 13 Feb 2015 12:20:45 +0000 (13:20 +0100)
tests/CMakeLists.txt
tests/encperf.cxx [new file with mode: 0644]

index 7e3310d5345ae44f8ecb04367dc175dfd445df29..e99c825a94aa3391a30669659797477c757e28a0 100644 (file)
@@ -10,3 +10,6 @@ target_link_libraries(conv rfb)
 
 add_executable(decperf decperf.cxx)
 target_link_libraries(decperf test_util rfb)
+
+add_executable(encperf encperf.cxx)
+target_link_libraries(encperf test_util rfb)
diff --git a/tests/encperf.cxx b/tests/encperf.cxx
new file mode 100644 (file)
index 0000000..60905c8
--- /dev/null
@@ -0,0 +1,439 @@
+/* Copyright 2015 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.
+ */
+
+/*
+ * This program reads files produced by TightVNC's/TurboVNC's
+ * fbs-dump, which in turn takes files from rfbproxy. It is
+ * basically a dump of the RFB protocol from the server side after
+ * the ServerInit message. Mostly this consists of FramebufferUpdate
+ * message using the HexTile encoding. Screen size and pixel format
+ * are not encoded in the file and must be specified by the user.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include <rdr/Exception.h>
+#include <rdr/OutStream.h>
+#include <rdr/FileInStream.h>
+
+#include <rfb/PixelFormat.h>
+
+#include <rfb/CConnection.h>
+#include <rfb/CMsgReader.h>
+#include <rfb/Decoder.h>
+#include <rfb/UpdateTracker.h>
+
+#include <rfb/EncodeManager.h>
+#include <rfb/SConnection.h>
+#include <rfb/SMsgWriter.h>
+
+#include "util.h"
+
+static rfb::IntParameter width("width", "Frame buffer width", 0);
+static rfb::IntParameter height("height", "Frame buffer height", 0);
+
+static rfb::StringParameter format("format", "Pixel format (e.g. bgr888)", "");
+
+// The frame buffer (and output) is always this format
+static const rfb::PixelFormat fbPF(32, 24, false, true, 255, 255, 255, 0, 8, 16);
+
+// Encodings to use
+static const rdr::S32 encodings[] = {
+  rfb::encodingTight, rfb::encodingCopyRect, rfb::encodingRRE,
+  rfb::encodingHextile, rfb::encodingZRLE, rfb::pseudoEncodingLastRect,
+  rfb::pseudoEncodingQualityLevel0 + 8 };
+
+class DummyOutStream : public rdr::OutStream {
+public:
+  DummyOutStream();
+
+  virtual int length();
+  virtual void flush();
+
+private:
+  virtual int overrun(int itemSize, int nItems);
+
+  int offset;
+  rdr::U8 buf[131072];
+};
+
+class CConn : public rfb::CConnection {
+public:
+  CConn(const char *filename);
+  ~CConn();
+
+  double getRatio();
+
+  virtual void setDesktopSize(int w, int h);
+  virtual void setCursor(int, int, const rfb::Point&, void*, void*);
+  virtual void framebufferUpdateStart();
+  virtual void framebufferUpdateEnd();
+  virtual void dataRect(const rfb::Rect&, int);
+  virtual void setColourMapEntries(int, int, rdr::U16*);
+  virtual void bell();
+  virtual void serverCutText(const char*, rdr::U32);
+
+public:
+  double decodeTime;
+  double encodeTime;
+
+protected:
+  rdr::FileInStream *in;
+  rfb::Decoder *decoders[rfb::encodingMax+1];
+  rfb::ManagedPixelBuffer pb;
+  rfb::SimpleUpdateTracker updates;
+  class SConn *sc;
+};
+
+class Manager : public rfb::EncodeManager {
+public:
+  Manager(class rfb::SConnection *conn);
+
+  double getRatio();
+};
+
+class SConn : public rfb::SConnection {
+public:
+  SConn();
+  ~SConn();
+
+  void writeUpdate(const rfb::UpdateInfo& ui, const rfb::PixelBuffer* pb);
+
+  double getRatio();
+
+  virtual void setAccessRights(AccessRights ar);
+
+  virtual void setDesktopSize(int fb_width, int fb_height,
+                              const rfb::ScreenSet& layout);
+
+protected:
+  DummyOutStream *out;
+  Manager *manager;
+};
+
+DummyOutStream::DummyOutStream()
+{
+  offset = 0;
+  ptr = buf;
+  end = buf + sizeof(buf);
+}
+
+int DummyOutStream::length()
+{
+  flush();
+  return offset;
+}
+
+void DummyOutStream::flush()
+{
+  offset += ptr - buf;
+  ptr = buf;
+}
+
+int DummyOutStream::overrun(int itemSize, int nItems)
+{
+  flush();
+}
+
+CConn::CConn(const char *filename)
+{
+  int i;
+
+  decodeTime = 0.0;
+  encodeTime = 0.0;
+
+  in = new rdr::FileInStream(filename);
+  setStreams(in, NULL);
+
+  memset(decoders, 0, sizeof(decoders));
+  for (i = 0;i < rfb::encodingMax;i++) {
+    if (!rfb::Decoder::supported(i))
+      continue;
+
+    decoders[i] = rfb::Decoder::createDecoder(i, this);
+  }
+
+  pb.setPF(fbPF);
+
+  // Need to skip the initial handshake and ServerInit
+  setState(RFBSTATE_NORMAL);
+  // That also means that the reader and writer weren't setup
+  setReader(new rfb::CMsgReader(this, in));
+  // Nor the frame buffer size and format
+  setDesktopSize(width, height);
+  rfb::PixelFormat pf;
+  pf.parse(format);
+  setPixelFormat(pf);
+
+  sc = new SConn();
+  sc->cp.setPF(pb.getPF());
+  sc->setEncodings(sizeof(encodings)/sizeof(*encodings), encodings);
+}
+
+CConn::~CConn()
+{
+  int i;
+
+  delete sc;
+
+  delete in;
+
+  for (i = 0;i < rfb::encodingMax;i++)
+    delete decoders[i];
+}
+
+double CConn::getRatio()
+{
+  return sc->getRatio();
+}
+
+void CConn::setDesktopSize(int w, int h)
+{
+  CConnection::setDesktopSize(w, h);
+
+  pb.setSize(cp.width, cp.height);
+}
+
+void CConn::setCursor(int, int, const rfb::Point&, void*, void*)
+{
+}
+
+void CConn::framebufferUpdateStart()
+{
+  updates.clear();
+}
+
+void CConn::framebufferUpdateEnd()
+{
+  rfb::UpdateInfo ui;
+  rfb::Region clip(pb.getRect());
+
+  updates.getUpdateInfo(&ui, clip);
+
+  startCpuCounter();
+  sc->writeUpdate(ui, &pb);
+  endCpuCounter();
+
+  encodeTime += getCpuCounter();
+}
+
+void CConn::dataRect(const rfb::Rect &r, int encoding)
+{
+  if (!decoders[encoding])
+    throw rdr::Exception("Unknown encoding");
+
+  startCpuCounter();
+  decoders[encoding]->readRect(r, &pb);
+  endCpuCounter();
+
+  decodeTime += getCpuCounter();
+
+  if (encoding != rfb::encodingCopyRect) // FIXME
+    updates.add_changed(rfb::Region(r));
+}
+
+void CConn::setColourMapEntries(int, int, rdr::U16*)
+{
+}
+
+void CConn::bell()
+{
+}
+
+void CConn::serverCutText(const char*, rdr::U32)
+{
+}
+
+Manager::Manager(class rfb::SConnection *conn) :
+  EncodeManager(conn)
+{
+}
+
+double Manager::getRatio()
+{
+  StatsVector::iterator iter;
+  unsigned long long bytes, equivalent;
+
+  bytes = equivalent = 0;
+  for (iter = stats.begin();iter != stats.end();++iter) {
+    StatsVector::value_type::iterator iter2;
+    for (iter2 = iter->begin();iter2 != iter->end();++iter2) {
+      bytes += iter2->bytes;
+      equivalent += iter2->equivalent;
+    }
+  }
+
+  return (double)equivalent / bytes;
+}
+
+SConn::SConn()
+{
+  out = new DummyOutStream;
+  setStreams(NULL, out);
+
+  setWriter(new rfb::SMsgWriter(&cp, out));
+
+  manager = new Manager(this);
+}
+
+SConn::~SConn()
+{
+  delete manager;
+  delete out;
+}
+
+void SConn::writeUpdate(const rfb::UpdateInfo& ui, const rfb::PixelBuffer* pb)
+{
+  manager->writeUpdate(ui, pb, NULL);
+}
+
+double SConn::getRatio()
+{
+  return manager->getRatio();
+}
+
+void SConn::setAccessRights(AccessRights ar)
+{
+}
+
+void SConn::setDesktopSize(int fb_width, int fb_height,
+                           const rfb::ScreenSet& layout)
+{
+}
+
+static double runTest(const char *fn, double *ratio)
+{
+  CConn *cc;
+  double time;
+
+  cc = new CConn(fn);
+
+  try {
+    while (true)
+      cc->processMsg();
+  } catch (rdr::EndOfStream e) {
+  } catch (rdr::Exception e) {
+    fprintf(stderr, "Failed to run rfb file: %s\n", e.str());
+    exit(1);
+  }
+
+  time = cc->encodeTime;
+  *ratio = cc->getRatio();
+
+  delete cc;
+
+  return time;
+}
+
+static void sort(double *array, int count)
+{
+  bool sorted;
+  int i;
+  do {
+    sorted = true;
+    for (i = 1;i < count;i++) {
+      if (array[i-1] > array[i]) {
+        double d;
+        d = array[i];
+        array[i] = array[i-1];
+        array[i-1] = d;
+        sorted = false;
+      }
+    }
+  } while (!sorted);
+}
+
+static const int runCount = 9;
+
+static void usage(const char *argv0)
+{
+  fprintf(stderr, "Syntax: %s [options] <rfb file>\n", argv0);
+  fprintf(stderr, "Options:\n");
+  rfb::Configuration::listParams(79, 14);
+  exit(1);
+}
+
+int main(int argc, char **argv)
+{
+  int i;
+
+  const char *fn;
+
+  double times[runCount], dev[runCount];
+  double median, meddev, ratio;
+
+  fn = NULL;
+  for (i = 1; i < argc; i++) {
+    if (rfb::Configuration::setParam(argv[i]))
+      continue;
+
+    if (argv[i][0] == '-') {
+      if (i+1 < argc) {
+        if (rfb::Configuration::setParam(&argv[i][1], argv[i+1])) {
+          i++;
+          continue;
+        }
+      }
+      usage(argv[0]);
+    }
+
+    if (fn != NULL)
+      usage(argv[0]);
+
+    fn = argv[i];
+  }
+
+  if (fn == NULL) {
+    fprintf(stderr, "No file specified!\n\n");
+    usage(argv[0]);
+  }
+
+  if (!format.hasBeenSet()) {
+    fprintf(stderr, "Pixel format not specified!\n\n");
+    usage(argv[0]);
+  }
+
+  if (!width.hasBeenSet() || !height.hasBeenSet()) {
+    fprintf(stderr, "Frame buffer size not specified!\n\n");
+    usage(argv[0]);
+  }
+
+  // Warmup
+  runTest(fn, &ratio);
+
+  // Multiple runs to get a good average
+  for (i = 0;i < runCount;i++)
+    times[i] = runTest(fn, &ratio);
+
+  // Calculate median and median deviation
+  sort(times, runCount);
+  median = times[runCount/2];
+
+  for (i = 0;i < runCount;i++)
+    dev[i] = fabs((times[i] - median) / median) * 100;
+
+  sort(dev, runCount);
+  meddev = dev[runCount/2];
+
+  printf("CPU time: %g s (+/- %g %)\n", median, meddev);
+  printf("Ratio: %g\n", ratio);
+
+  return 0;
+}