blob: 211160896670a3047e0c2b903bd84d82069bbff7 [file] [log] [blame]
Pierre Ossman8738e8a2015-02-11 13:49:04 +01001/* Copyright 2015 Pierre Ossman <ossman@cendio.se> for Cendio AB
DRC77be9292015-02-21 11:44:26 -06002 * Copyright (C) 2015 D. R. Commander. All Rights Reserved.
DRCb4c4a382015-02-21 11:28:37 -06003 *
Pierre Ossman8738e8a2015-02-11 13:49:04 +01004 * This is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
DRCb4c4a382015-02-21 11:28:37 -06008 *
Pierre Ossman8738e8a2015-02-11 13:49:04 +01009 * This software is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
DRCb4c4a382015-02-21 11:28:37 -060013 *
Pierre Ossman8738e8a2015-02-11 13:49:04 +010014 * You should have received a copy of the GNU General Public License
15 * along with this software; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17 * USA.
18 */
19
20/*
21 * This program reads files produced by TightVNC's/TurboVNC's
22 * fbs-dump, which in turn takes files from rfbproxy. It is
23 * basically a dump of the RFB protocol from the server side after
24 * the ServerInit message. Mostly this consists of FramebufferUpdate
25 * message using the HexTile encoding. Screen size and pixel format
26 * are not encoded in the file and must be specified by the user.
27 */
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <math.h>
32
33#include <rdr/Exception.h>
34#include <rdr/OutStream.h>
35#include <rdr/FileInStream.h>
36
37#include <rfb/PixelFormat.h>
38
39#include <rfb/CConnection.h>
40#include <rfb/CMsgReader.h>
Pierre Ossman8738e8a2015-02-11 13:49:04 +010041#include <rfb/UpdateTracker.h>
42
43#include <rfb/EncodeManager.h>
44#include <rfb/SConnection.h>
45#include <rfb/SMsgWriter.h>
46
47#include "util.h"
48
49static rfb::IntParameter width("width", "Frame buffer width", 0);
50static rfb::IntParameter height("height", "Frame buffer height", 0);
DRC4631a762015-02-21 11:57:27 -060051static rfb::IntParameter count("count", "Number of benchmark iterations", 9);
Pierre Ossman8738e8a2015-02-11 13:49:04 +010052
53static rfb::StringParameter format("format", "Pixel format (e.g. bgr888)", "");
54
DRC2a172c92015-02-25 14:18:07 -060055static rfb::BoolParameter translate("translate",
56 "Translate 8-bit and 16-bit datasets into 24-bit",
57 true);
58
Pierre Ossman8738e8a2015-02-11 13:49:04 +010059// The frame buffer (and output) is always this format
60static const rfb::PixelFormat fbPF(32, 24, false, true, 255, 255, 255, 0, 8, 16);
61
62// Encodings to use
63static const rdr::S32 encodings[] = {
64 rfb::encodingTight, rfb::encodingCopyRect, rfb::encodingRRE,
65 rfb::encodingHextile, rfb::encodingZRLE, rfb::pseudoEncodingLastRect,
DRC562eb712015-02-21 12:01:47 -060066 rfb::pseudoEncodingQualityLevel0 + 8,
67 rfb::pseudoEncodingCompressLevel0 + 2};
Pierre Ossman8738e8a2015-02-11 13:49:04 +010068
69class DummyOutStream : public rdr::OutStream {
70public:
71 DummyOutStream();
72
73 virtual int length();
74 virtual void flush();
75
76private:
77 virtual int overrun(int itemSize, int nItems);
78
79 int offset;
80 rdr::U8 buf[131072];
81};
82
83class CConn : public rfb::CConnection {
84public:
85 CConn(const char *filename);
86 ~CConn();
87
DRC77be9292015-02-21 11:44:26 -060088 void getStats(double& ratio, unsigned long long& bytes,
89 unsigned long long& rawEquivalent);
Pierre Ossman8738e8a2015-02-11 13:49:04 +010090
91 virtual void setDesktopSize(int w, int h);
92 virtual void setCursor(int, int, const rfb::Point&, void*, void*);
93 virtual void framebufferUpdateStart();
94 virtual void framebufferUpdateEnd();
95 virtual void dataRect(const rfb::Rect&, int);
96 virtual void setColourMapEntries(int, int, rdr::U16*);
97 virtual void bell();
98 virtual void serverCutText(const char*, rdr::U32);
99
100public:
101 double decodeTime;
102 double encodeTime;
103
104protected:
105 rdr::FileInStream *in;
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100106 rfb::SimpleUpdateTracker updates;
107 class SConn *sc;
108};
109
110class Manager : public rfb::EncodeManager {
111public:
112 Manager(class rfb::SConnection *conn);
113
DRC77be9292015-02-21 11:44:26 -0600114 void getStats(double&, unsigned long long&, unsigned long long&);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100115};
116
117class SConn : public rfb::SConnection {
118public:
119 SConn();
120 ~SConn();
121
122 void writeUpdate(const rfb::UpdateInfo& ui, const rfb::PixelBuffer* pb);
123
DRC77be9292015-02-21 11:44:26 -0600124 void getStats(double&, unsigned long long&, unsigned long long&);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100125
126 virtual void setAccessRights(AccessRights ar);
127
128 virtual void setDesktopSize(int fb_width, int fb_height,
129 const rfb::ScreenSet& layout);
130
131protected:
132 DummyOutStream *out;
133 Manager *manager;
134};
135
136DummyOutStream::DummyOutStream()
137{
138 offset = 0;
139 ptr = buf;
140 end = buf + sizeof(buf);
141}
142
143int DummyOutStream::length()
144{
145 flush();
146 return offset;
147}
148
149void DummyOutStream::flush()
150{
151 offset += ptr - buf;
152 ptr = buf;
153}
154
155int DummyOutStream::overrun(int itemSize, int nItems)
156{
157 flush();
Pierre Ossmanfc331e62015-03-03 16:42:45 +0100158 if (itemSize * nItems > end - ptr)
159 nItems = (end - ptr) / itemSize;
160 return nItems;
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100161}
162
163CConn::CConn(const char *filename)
164{
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100165 decodeTime = 0.0;
166 encodeTime = 0.0;
167
168 in = new rdr::FileInStream(filename);
169 setStreams(in, NULL);
170
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100171 // Need to skip the initial handshake and ServerInit
172 setState(RFBSTATE_NORMAL);
173 // That also means that the reader and writer weren't setup
174 setReader(new rfb::CMsgReader(this, in));
175 // Nor the frame buffer size and format
176 setDesktopSize(width, height);
177 rfb::PixelFormat pf;
178 pf.parse(format);
179 setPixelFormat(pf);
180
181 sc = new SConn();
Pierre Ossman9f273e92015-11-09 16:34:54 +0100182 sc->cp.setPF((bool)translate ? fbPF : pf);
DRCb4c4a382015-02-21 11:28:37 -0600183 sc->setEncodings(sizeof(encodings) / sizeof(*encodings), encodings);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100184}
185
186CConn::~CConn()
187{
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100188 delete sc;
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100189 delete in;
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100190}
191
DRC77be9292015-02-21 11:44:26 -0600192void CConn::getStats(double& ratio, unsigned long long& bytes,
193 unsigned long long& rawEquivalent)
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100194{
DRC77be9292015-02-21 11:44:26 -0600195 sc->getStats(ratio, bytes, rawEquivalent);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100196}
197
198void CConn::setDesktopSize(int w, int h)
199{
200 CConnection::setDesktopSize(w, h);
201
Pierre Ossman9f273e92015-11-09 16:34:54 +0100202 setFramebuffer(new rfb::ManagedPixelBuffer(sc->cp.pf(), cp.width, cp.height));
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100203}
204
205void CConn::setCursor(int, int, const rfb::Point&, void*, void*)
206{
207}
208
209void CConn::framebufferUpdateStart()
210{
Pierre Ossman3da238d2015-11-12 12:20:05 +0100211 CConnection::framebufferUpdateStart();
212
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100213 updates.clear();
Pierre Ossman9f273e92015-11-09 16:34:54 +0100214 startCpuCounter();
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100215}
216
217void CConn::framebufferUpdateEnd()
218{
219 rfb::UpdateInfo ui;
Pierre Ossman9f273e92015-11-09 16:34:54 +0100220 rfb::PixelBuffer* pb = getFramebuffer();
221 rfb::Region clip(pb->getRect());
222
Pierre Ossman3da238d2015-11-12 12:20:05 +0100223 CConnection::framebufferUpdateEnd();
224
Pierre Ossman9f273e92015-11-09 16:34:54 +0100225 endCpuCounter();
226
227 decodeTime += getCpuCounter();
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100228
229 updates.getUpdateInfo(&ui, clip);
230
231 startCpuCounter();
Pierre Ossman9f273e92015-11-09 16:34:54 +0100232 sc->writeUpdate(ui, pb);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100233 endCpuCounter();
234
235 encodeTime += getCpuCounter();
236}
237
238void CConn::dataRect(const rfb::Rect &r, int encoding)
239{
Pierre Ossman9f273e92015-11-09 16:34:54 +0100240 CConnection::dataRect(r, encoding);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100241
242 if (encoding != rfb::encodingCopyRect) // FIXME
243 updates.add_changed(rfb::Region(r));
244}
245
246void CConn::setColourMapEntries(int, int, rdr::U16*)
247{
248}
249
250void CConn::bell()
251{
252}
253
254void CConn::serverCutText(const char*, rdr::U32)
255{
256}
257
258Manager::Manager(class rfb::SConnection *conn) :
259 EncodeManager(conn)
260{
261}
262
DRC77be9292015-02-21 11:44:26 -0600263void Manager::getStats(double& ratio, unsigned long long& encodedBytes,
264 unsigned long long& rawEquivalent)
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100265{
266 StatsVector::iterator iter;
267 unsigned long long bytes, equivalent;
268
269 bytes = equivalent = 0;
DRCb4c4a382015-02-21 11:28:37 -0600270 for (iter = stats.begin(); iter != stats.end(); ++iter) {
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100271 StatsVector::value_type::iterator iter2;
DRCb4c4a382015-02-21 11:28:37 -0600272 for (iter2 = iter->begin(); iter2 != iter->end(); ++iter2) {
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100273 bytes += iter2->bytes;
274 equivalent += iter2->equivalent;
275 }
276 }
277
DRC77be9292015-02-21 11:44:26 -0600278 ratio = (double)equivalent / bytes;
279 encodedBytes = bytes;
280 rawEquivalent = equivalent;
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100281}
282
283SConn::SConn()
284{
285 out = new DummyOutStream;
286 setStreams(NULL, out);
287
288 setWriter(new rfb::SMsgWriter(&cp, out));
289
290 manager = new Manager(this);
291}
292
293SConn::~SConn()
294{
295 delete manager;
296 delete out;
297}
298
299void SConn::writeUpdate(const rfb::UpdateInfo& ui, const rfb::PixelBuffer* pb)
300{
301 manager->writeUpdate(ui, pb, NULL);
302}
303
DRC77be9292015-02-21 11:44:26 -0600304void SConn::getStats(double& ratio, unsigned long long& bytes,
305 unsigned long long& rawEquivalent)
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100306{
DRC77be9292015-02-21 11:44:26 -0600307 manager->getStats(ratio, bytes, rawEquivalent);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100308}
309
310void SConn::setAccessRights(AccessRights ar)
311{
312}
313
314void SConn::setDesktopSize(int fb_width, int fb_height,
315 const rfb::ScreenSet& layout)
316{
317}
318
DRC77be9292015-02-21 11:44:26 -0600319static double runTest(const char *fn, double& ratio, unsigned long long& bytes,
320 unsigned long long& rawEquivalent)
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100321{
322 CConn *cc;
323 double time;
324
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100325 try {
DRC13cfb512015-02-26 12:24:03 -0600326 cc = new CConn(fn);
Pierre Ossman86475a62015-03-03 16:42:15 +0100327 } catch (rdr::Exception e) {
328 fprintf(stderr, "Failed to open rfb file: %s\n", e.str());
329 exit(1);
330 }
DRC13cfb512015-02-26 12:24:03 -0600331
Pierre Ossman86475a62015-03-03 16:42:15 +0100332 try {
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100333 while (true)
334 cc->processMsg();
335 } catch (rdr::EndOfStream e) {
336 } catch (rdr::Exception e) {
337 fprintf(stderr, "Failed to run rfb file: %s\n", e.str());
338 exit(1);
339 }
340
341 time = cc->encodeTime;
DRC77be9292015-02-21 11:44:26 -0600342 cc->getStats(ratio, bytes, rawEquivalent);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100343
344 delete cc;
345
346 return time;
347}
348
349static void sort(double *array, int count)
350{
351 bool sorted;
352 int i;
353 do {
354 sorted = true;
DRCb4c4a382015-02-21 11:28:37 -0600355 for (i = 1; i < count; i++) {
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100356 if (array[i-1] > array[i]) {
357 double d;
358 d = array[i];
DRCb4c4a382015-02-21 11:28:37 -0600359 array[i] = array[i - 1];
360 array[i - 1] = d;
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100361 sorted = false;
362 }
363 }
364 } while (!sorted);
365}
366
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100367static void usage(const char *argv0)
368{
369 fprintf(stderr, "Syntax: %s [options] <rfb file>\n", argv0);
370 fprintf(stderr, "Options:\n");
371 rfb::Configuration::listParams(79, 14);
372 exit(1);
373}
374
375int main(int argc, char **argv)
376{
377 int i;
378
379 const char *fn;
380
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100381 fn = NULL;
382 for (i = 1; i < argc; i++) {
383 if (rfb::Configuration::setParam(argv[i]))
384 continue;
385
386 if (argv[i][0] == '-') {
DRCb4c4a382015-02-21 11:28:37 -0600387 if (i + 1 < argc) {
388 if (rfb::Configuration::setParam(&argv[i][1], argv[i + 1])) {
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100389 i++;
390 continue;
391 }
392 }
393 usage(argv[0]);
394 }
395
396 if (fn != NULL)
397 usage(argv[0]);
398
399 fn = argv[i];
400 }
401
DRC4631a762015-02-21 11:57:27 -0600402 int runCount = count;
403 double times[runCount], dev[runCount];
404 double median, meddev, ratio;
405 unsigned long long bytes, equivalent;
406
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100407 if (fn == NULL) {
408 fprintf(stderr, "No file specified!\n\n");
409 usage(argv[0]);
410 }
411
Pierre Ossman135906e2015-04-27 12:48:47 +0200412 if (strcmp(format, "") == 0) {
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100413 fprintf(stderr, "Pixel format not specified!\n\n");
414 usage(argv[0]);
415 }
416
Pierre Ossman135906e2015-04-27 12:48:47 +0200417 if (width == 0 || height == 0) {
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100418 fprintf(stderr, "Frame buffer size not specified!\n\n");
419 usage(argv[0]);
420 }
421
422 // Warmup
DRC77be9292015-02-21 11:44:26 -0600423 runTest(fn, ratio, bytes, equivalent);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100424
425 // Multiple runs to get a good average
DRCb4c4a382015-02-21 11:28:37 -0600426 for (i = 0; i < runCount; i++)
DRC77be9292015-02-21 11:44:26 -0600427 times[i] = runTest(fn, ratio, bytes, equivalent);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100428
429 // Calculate median and median deviation
430 sort(times, runCount);
DRCb4c4a382015-02-21 11:28:37 -0600431 median = times[runCount / 2];
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100432
DRCb4c4a382015-02-21 11:28:37 -0600433 for (i = 0; i < runCount; i++)
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100434 dev[i] = fabs((times[i] - median) / median) * 100;
435
436 sort(dev, runCount);
DRCb4c4a382015-02-21 11:28:37 -0600437 meddev = dev[runCount / 2];
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100438
DRCe46dda62015-02-25 14:08:34 -0600439 printf("CPU time: %g s (+/- %g %%)\n", median, meddev);
Pierre Ossman7d218b02015-03-03 16:43:05 +0100440#ifdef WIN32
441 printf("Encoded bytes: %I64d\n", bytes);
442 printf("Raw equivalent bytes: %I64d\n", equivalent);
443#else
DRC77be9292015-02-21 11:44:26 -0600444 printf("Encoded bytes: %lld\n", bytes);
445 printf("Raw equivalent bytes: %lld\n", equivalent);
Pierre Ossman7d218b02015-03-03 16:43:05 +0100446#endif
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100447 printf("Ratio: %g\n", ratio);
448
449 return 0;
450}