blob: 206449604d6425ec2ae65b35cee5d524d1fe1275 [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>
41#include <rfb/Decoder.h>
42#include <rfb/UpdateTracker.h>
43
44#include <rfb/EncodeManager.h>
45#include <rfb/SConnection.h>
46#include <rfb/SMsgWriter.h>
47
48#include "util.h"
49
50static rfb::IntParameter width("width", "Frame buffer width", 0);
51static rfb::IntParameter height("height", "Frame buffer height", 0);
DRC4631a762015-02-21 11:57:27 -060052static rfb::IntParameter count("count", "Number of benchmark iterations", 9);
Pierre Ossman8738e8a2015-02-11 13:49:04 +010053
54static rfb::StringParameter format("format", "Pixel format (e.g. bgr888)", "");
55
56// The frame buffer (and output) is always this format
57static const rfb::PixelFormat fbPF(32, 24, false, true, 255, 255, 255, 0, 8, 16);
58
59// Encodings to use
60static const rdr::S32 encodings[] = {
61 rfb::encodingTight, rfb::encodingCopyRect, rfb::encodingRRE,
62 rfb::encodingHextile, rfb::encodingZRLE, rfb::pseudoEncodingLastRect,
DRC562eb712015-02-21 12:01:47 -060063 rfb::pseudoEncodingQualityLevel0 + 8,
64 rfb::pseudoEncodingCompressLevel0 + 2};
Pierre Ossman8738e8a2015-02-11 13:49:04 +010065
66class DummyOutStream : public rdr::OutStream {
67public:
68 DummyOutStream();
69
70 virtual int length();
71 virtual void flush();
72
73private:
74 virtual int overrun(int itemSize, int nItems);
75
76 int offset;
77 rdr::U8 buf[131072];
78};
79
80class CConn : public rfb::CConnection {
81public:
82 CConn(const char *filename);
83 ~CConn();
84
DRC77be9292015-02-21 11:44:26 -060085 void getStats(double& ratio, unsigned long long& bytes,
86 unsigned long long& rawEquivalent);
Pierre Ossman8738e8a2015-02-11 13:49:04 +010087
88 virtual void setDesktopSize(int w, int h);
89 virtual void setCursor(int, int, const rfb::Point&, void*, void*);
90 virtual void framebufferUpdateStart();
91 virtual void framebufferUpdateEnd();
92 virtual void dataRect(const rfb::Rect&, int);
93 virtual void setColourMapEntries(int, int, rdr::U16*);
94 virtual void bell();
95 virtual void serverCutText(const char*, rdr::U32);
96
97public:
98 double decodeTime;
99 double encodeTime;
100
101protected:
102 rdr::FileInStream *in;
DRCb4c4a382015-02-21 11:28:37 -0600103 rfb::Decoder *decoders[rfb::encodingMax + 1];
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100104 rfb::ManagedPixelBuffer pb;
105 rfb::SimpleUpdateTracker updates;
106 class SConn *sc;
107};
108
109class Manager : public rfb::EncodeManager {
110public:
111 Manager(class rfb::SConnection *conn);
112
DRC77be9292015-02-21 11:44:26 -0600113 void getStats(double&, unsigned long long&, unsigned long long&);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100114};
115
116class SConn : public rfb::SConnection {
117public:
118 SConn();
119 ~SConn();
120
121 void writeUpdate(const rfb::UpdateInfo& ui, const rfb::PixelBuffer* pb);
122
DRC77be9292015-02-21 11:44:26 -0600123 void getStats(double&, unsigned long long&, unsigned long long&);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100124
125 virtual void setAccessRights(AccessRights ar);
126
127 virtual void setDesktopSize(int fb_width, int fb_height,
128 const rfb::ScreenSet& layout);
129
130protected:
131 DummyOutStream *out;
132 Manager *manager;
133};
134
135DummyOutStream::DummyOutStream()
136{
137 offset = 0;
138 ptr = buf;
139 end = buf + sizeof(buf);
140}
141
142int DummyOutStream::length()
143{
144 flush();
145 return offset;
146}
147
148void DummyOutStream::flush()
149{
150 offset += ptr - buf;
151 ptr = buf;
152}
153
154int DummyOutStream::overrun(int itemSize, int nItems)
155{
156 flush();
157}
158
159CConn::CConn(const char *filename)
160{
161 int i;
162
163 decodeTime = 0.0;
164 encodeTime = 0.0;
165
166 in = new rdr::FileInStream(filename);
167 setStreams(in, NULL);
168
169 memset(decoders, 0, sizeof(decoders));
DRCb4c4a382015-02-21 11:28:37 -0600170 for (i = 0; i < rfb::encodingMax; i++) {
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100171 if (!rfb::Decoder::supported(i))
172 continue;
173
174 decoders[i] = rfb::Decoder::createDecoder(i, this);
175 }
176
177 pb.setPF(fbPF);
178
179 // Need to skip the initial handshake and ServerInit
180 setState(RFBSTATE_NORMAL);
181 // That also means that the reader and writer weren't setup
182 setReader(new rfb::CMsgReader(this, in));
183 // Nor the frame buffer size and format
184 setDesktopSize(width, height);
185 rfb::PixelFormat pf;
186 pf.parse(format);
187 setPixelFormat(pf);
188
189 sc = new SConn();
190 sc->cp.setPF(pb.getPF());
DRCb4c4a382015-02-21 11:28:37 -0600191 sc->setEncodings(sizeof(encodings) / sizeof(*encodings), encodings);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100192}
193
194CConn::~CConn()
195{
196 int i;
197
198 delete sc;
199
200 delete in;
201
DRCb4c4a382015-02-21 11:28:37 -0600202 for (i = 0; i < rfb::encodingMax; i++)
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100203 delete decoders[i];
204}
205
DRC77be9292015-02-21 11:44:26 -0600206void CConn::getStats(double& ratio, unsigned long long& bytes,
207 unsigned long long& rawEquivalent)
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100208{
DRC77be9292015-02-21 11:44:26 -0600209 sc->getStats(ratio, bytes, rawEquivalent);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100210}
211
212void CConn::setDesktopSize(int w, int h)
213{
214 CConnection::setDesktopSize(w, h);
215
216 pb.setSize(cp.width, cp.height);
217}
218
219void CConn::setCursor(int, int, const rfb::Point&, void*, void*)
220{
221}
222
223void CConn::framebufferUpdateStart()
224{
225 updates.clear();
226}
227
228void CConn::framebufferUpdateEnd()
229{
230 rfb::UpdateInfo ui;
231 rfb::Region clip(pb.getRect());
232
233 updates.getUpdateInfo(&ui, clip);
234
235 startCpuCounter();
236 sc->writeUpdate(ui, &pb);
237 endCpuCounter();
238
239 encodeTime += getCpuCounter();
240}
241
242void CConn::dataRect(const rfb::Rect &r, int encoding)
243{
244 if (!decoders[encoding])
245 throw rdr::Exception("Unknown encoding");
246
247 startCpuCounter();
248 decoders[encoding]->readRect(r, &pb);
249 endCpuCounter();
250
251 decodeTime += getCpuCounter();
252
253 if (encoding != rfb::encodingCopyRect) // FIXME
254 updates.add_changed(rfb::Region(r));
255}
256
257void CConn::setColourMapEntries(int, int, rdr::U16*)
258{
259}
260
261void CConn::bell()
262{
263}
264
265void CConn::serverCutText(const char*, rdr::U32)
266{
267}
268
269Manager::Manager(class rfb::SConnection *conn) :
270 EncodeManager(conn)
271{
272}
273
DRC77be9292015-02-21 11:44:26 -0600274void Manager::getStats(double& ratio, unsigned long long& encodedBytes,
275 unsigned long long& rawEquivalent)
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100276{
277 StatsVector::iterator iter;
278 unsigned long long bytes, equivalent;
279
280 bytes = equivalent = 0;
DRCb4c4a382015-02-21 11:28:37 -0600281 for (iter = stats.begin(); iter != stats.end(); ++iter) {
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100282 StatsVector::value_type::iterator iter2;
DRCb4c4a382015-02-21 11:28:37 -0600283 for (iter2 = iter->begin(); iter2 != iter->end(); ++iter2) {
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100284 bytes += iter2->bytes;
285 equivalent += iter2->equivalent;
286 }
287 }
288
DRC77be9292015-02-21 11:44:26 -0600289 ratio = (double)equivalent / bytes;
290 encodedBytes = bytes;
291 rawEquivalent = equivalent;
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100292}
293
294SConn::SConn()
295{
296 out = new DummyOutStream;
297 setStreams(NULL, out);
298
299 setWriter(new rfb::SMsgWriter(&cp, out));
300
301 manager = new Manager(this);
302}
303
304SConn::~SConn()
305{
306 delete manager;
307 delete out;
308}
309
310void SConn::writeUpdate(const rfb::UpdateInfo& ui, const rfb::PixelBuffer* pb)
311{
312 manager->writeUpdate(ui, pb, NULL);
313}
314
DRC77be9292015-02-21 11:44:26 -0600315void SConn::getStats(double& ratio, unsigned long long& bytes,
316 unsigned long long& rawEquivalent)
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100317{
DRC77be9292015-02-21 11:44:26 -0600318 manager->getStats(ratio, bytes, rawEquivalent);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100319}
320
321void SConn::setAccessRights(AccessRights ar)
322{
323}
324
325void SConn::setDesktopSize(int fb_width, int fb_height,
326 const rfb::ScreenSet& layout)
327{
328}
329
DRC77be9292015-02-21 11:44:26 -0600330static double runTest(const char *fn, double& ratio, unsigned long long& bytes,
331 unsigned long long& rawEquivalent)
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100332{
333 CConn *cc;
334 double time;
335
336 cc = new CConn(fn);
337
338 try {
339 while (true)
340 cc->processMsg();
341 } catch (rdr::EndOfStream e) {
342 } catch (rdr::Exception e) {
343 fprintf(stderr, "Failed to run rfb file: %s\n", e.str());
344 exit(1);
345 }
346
347 time = cc->encodeTime;
DRC77be9292015-02-21 11:44:26 -0600348 cc->getStats(ratio, bytes, rawEquivalent);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100349
350 delete cc;
351
352 return time;
353}
354
355static void sort(double *array, int count)
356{
357 bool sorted;
358 int i;
359 do {
360 sorted = true;
DRCb4c4a382015-02-21 11:28:37 -0600361 for (i = 1; i < count; i++) {
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100362 if (array[i-1] > array[i]) {
363 double d;
364 d = array[i];
DRCb4c4a382015-02-21 11:28:37 -0600365 array[i] = array[i - 1];
366 array[i - 1] = d;
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100367 sorted = false;
368 }
369 }
370 } while (!sorted);
371}
372
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100373static void usage(const char *argv0)
374{
375 fprintf(stderr, "Syntax: %s [options] <rfb file>\n", argv0);
376 fprintf(stderr, "Options:\n");
377 rfb::Configuration::listParams(79, 14);
378 exit(1);
379}
380
381int main(int argc, char **argv)
382{
383 int i;
384
385 const char *fn;
386
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100387 fn = NULL;
388 for (i = 1; i < argc; i++) {
389 if (rfb::Configuration::setParam(argv[i]))
390 continue;
391
392 if (argv[i][0] == '-') {
DRCb4c4a382015-02-21 11:28:37 -0600393 if (i + 1 < argc) {
394 if (rfb::Configuration::setParam(&argv[i][1], argv[i + 1])) {
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100395 i++;
396 continue;
397 }
398 }
399 usage(argv[0]);
400 }
401
402 if (fn != NULL)
403 usage(argv[0]);
404
405 fn = argv[i];
406 }
407
DRC4631a762015-02-21 11:57:27 -0600408 int runCount = count;
409 double times[runCount], dev[runCount];
410 double median, meddev, ratio;
411 unsigned long long bytes, equivalent;
412
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100413 if (fn == NULL) {
414 fprintf(stderr, "No file specified!\n\n");
415 usage(argv[0]);
416 }
417
418 if (!format.hasBeenSet()) {
419 fprintf(stderr, "Pixel format not specified!\n\n");
420 usage(argv[0]);
421 }
422
423 if (!width.hasBeenSet() || !height.hasBeenSet()) {
424 fprintf(stderr, "Frame buffer size not specified!\n\n");
425 usage(argv[0]);
426 }
427
428 // Warmup
DRC77be9292015-02-21 11:44:26 -0600429 runTest(fn, ratio, bytes, equivalent);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100430
431 // Multiple runs to get a good average
DRCb4c4a382015-02-21 11:28:37 -0600432 for (i = 0; i < runCount; i++)
DRC77be9292015-02-21 11:44:26 -0600433 times[i] = runTest(fn, ratio, bytes, equivalent);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100434
435 // Calculate median and median deviation
436 sort(times, runCount);
DRCb4c4a382015-02-21 11:28:37 -0600437 median = times[runCount / 2];
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100438
DRCb4c4a382015-02-21 11:28:37 -0600439 for (i = 0; i < runCount; i++)
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100440 dev[i] = fabs((times[i] - median) / median) * 100;
441
442 sort(dev, runCount);
DRCb4c4a382015-02-21 11:28:37 -0600443 meddev = dev[runCount / 2];
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100444
445 printf("CPU time: %g s (+/- %g %)\n", median, meddev);
DRC77be9292015-02-21 11:44:26 -0600446 printf("Encoded bytes: %lld\n", bytes);
447 printf("Raw equivalent bytes: %lld\n", equivalent);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100448 printf("Ratio: %g\n", ratio);
449
450 return 0;
451}