blob: ffa304730291dcbfc5f4716c9bdc342ba058ad2b [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,
63 rfb::pseudoEncodingQualityLevel0 + 8 };
64
65class DummyOutStream : public rdr::OutStream {
66public:
67 DummyOutStream();
68
69 virtual int length();
70 virtual void flush();
71
72private:
73 virtual int overrun(int itemSize, int nItems);
74
75 int offset;
76 rdr::U8 buf[131072];
77};
78
79class CConn : public rfb::CConnection {
80public:
81 CConn(const char *filename);
82 ~CConn();
83
DRC77be9292015-02-21 11:44:26 -060084 void getStats(double& ratio, unsigned long long& bytes,
85 unsigned long long& rawEquivalent);
Pierre Ossman8738e8a2015-02-11 13:49:04 +010086
87 virtual void setDesktopSize(int w, int h);
88 virtual void setCursor(int, int, const rfb::Point&, void*, void*);
89 virtual void framebufferUpdateStart();
90 virtual void framebufferUpdateEnd();
91 virtual void dataRect(const rfb::Rect&, int);
92 virtual void setColourMapEntries(int, int, rdr::U16*);
93 virtual void bell();
94 virtual void serverCutText(const char*, rdr::U32);
95
96public:
97 double decodeTime;
98 double encodeTime;
99
100protected:
101 rdr::FileInStream *in;
DRCb4c4a382015-02-21 11:28:37 -0600102 rfb::Decoder *decoders[rfb::encodingMax + 1];
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100103 rfb::ManagedPixelBuffer pb;
104 rfb::SimpleUpdateTracker updates;
105 class SConn *sc;
106};
107
108class Manager : public rfb::EncodeManager {
109public:
110 Manager(class rfb::SConnection *conn);
111
DRC77be9292015-02-21 11:44:26 -0600112 void getStats(double&, unsigned long long&, unsigned long long&);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100113};
114
115class SConn : public rfb::SConnection {
116public:
117 SConn();
118 ~SConn();
119
120 void writeUpdate(const rfb::UpdateInfo& ui, const rfb::PixelBuffer* pb);
121
DRC77be9292015-02-21 11:44:26 -0600122 void getStats(double&, unsigned long long&, unsigned long long&);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100123
124 virtual void setAccessRights(AccessRights ar);
125
126 virtual void setDesktopSize(int fb_width, int fb_height,
127 const rfb::ScreenSet& layout);
128
129protected:
130 DummyOutStream *out;
131 Manager *manager;
132};
133
134DummyOutStream::DummyOutStream()
135{
136 offset = 0;
137 ptr = buf;
138 end = buf + sizeof(buf);
139}
140
141int DummyOutStream::length()
142{
143 flush();
144 return offset;
145}
146
147void DummyOutStream::flush()
148{
149 offset += ptr - buf;
150 ptr = buf;
151}
152
153int DummyOutStream::overrun(int itemSize, int nItems)
154{
155 flush();
156}
157
158CConn::CConn(const char *filename)
159{
160 int i;
161
162 decodeTime = 0.0;
163 encodeTime = 0.0;
164
165 in = new rdr::FileInStream(filename);
166 setStreams(in, NULL);
167
168 memset(decoders, 0, sizeof(decoders));
DRCb4c4a382015-02-21 11:28:37 -0600169 for (i = 0; i < rfb::encodingMax; i++) {
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100170 if (!rfb::Decoder::supported(i))
171 continue;
172
173 decoders[i] = rfb::Decoder::createDecoder(i, this);
174 }
175
176 pb.setPF(fbPF);
177
178 // Need to skip the initial handshake and ServerInit
179 setState(RFBSTATE_NORMAL);
180 // That also means that the reader and writer weren't setup
181 setReader(new rfb::CMsgReader(this, in));
182 // Nor the frame buffer size and format
183 setDesktopSize(width, height);
184 rfb::PixelFormat pf;
185 pf.parse(format);
186 setPixelFormat(pf);
187
188 sc = new SConn();
189 sc->cp.setPF(pb.getPF());
DRCb4c4a382015-02-21 11:28:37 -0600190 sc->setEncodings(sizeof(encodings) / sizeof(*encodings), encodings);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100191}
192
193CConn::~CConn()
194{
195 int i;
196
197 delete sc;
198
199 delete in;
200
DRCb4c4a382015-02-21 11:28:37 -0600201 for (i = 0; i < rfb::encodingMax; i++)
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100202 delete decoders[i];
203}
204
DRC77be9292015-02-21 11:44:26 -0600205void CConn::getStats(double& ratio, unsigned long long& bytes,
206 unsigned long long& rawEquivalent)
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100207{
DRC77be9292015-02-21 11:44:26 -0600208 sc->getStats(ratio, bytes, rawEquivalent);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100209}
210
211void CConn::setDesktopSize(int w, int h)
212{
213 CConnection::setDesktopSize(w, h);
214
215 pb.setSize(cp.width, cp.height);
216}
217
218void CConn::setCursor(int, int, const rfb::Point&, void*, void*)
219{
220}
221
222void CConn::framebufferUpdateStart()
223{
224 updates.clear();
225}
226
227void CConn::framebufferUpdateEnd()
228{
229 rfb::UpdateInfo ui;
230 rfb::Region clip(pb.getRect());
231
232 updates.getUpdateInfo(&ui, clip);
233
234 startCpuCounter();
235 sc->writeUpdate(ui, &pb);
236 endCpuCounter();
237
238 encodeTime += getCpuCounter();
239}
240
241void CConn::dataRect(const rfb::Rect &r, int encoding)
242{
243 if (!decoders[encoding])
244 throw rdr::Exception("Unknown encoding");
245
246 startCpuCounter();
247 decoders[encoding]->readRect(r, &pb);
248 endCpuCounter();
249
250 decodeTime += getCpuCounter();
251
252 if (encoding != rfb::encodingCopyRect) // FIXME
253 updates.add_changed(rfb::Region(r));
254}
255
256void CConn::setColourMapEntries(int, int, rdr::U16*)
257{
258}
259
260void CConn::bell()
261{
262}
263
264void CConn::serverCutText(const char*, rdr::U32)
265{
266}
267
268Manager::Manager(class rfb::SConnection *conn) :
269 EncodeManager(conn)
270{
271}
272
DRC77be9292015-02-21 11:44:26 -0600273void Manager::getStats(double& ratio, unsigned long long& encodedBytes,
274 unsigned long long& rawEquivalent)
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100275{
276 StatsVector::iterator iter;
277 unsigned long long bytes, equivalent;
278
279 bytes = equivalent = 0;
DRCb4c4a382015-02-21 11:28:37 -0600280 for (iter = stats.begin(); iter != stats.end(); ++iter) {
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100281 StatsVector::value_type::iterator iter2;
DRCb4c4a382015-02-21 11:28:37 -0600282 for (iter2 = iter->begin(); iter2 != iter->end(); ++iter2) {
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100283 bytes += iter2->bytes;
284 equivalent += iter2->equivalent;
285 }
286 }
287
DRC77be9292015-02-21 11:44:26 -0600288 ratio = (double)equivalent / bytes;
289 encodedBytes = bytes;
290 rawEquivalent = equivalent;
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100291}
292
293SConn::SConn()
294{
295 out = new DummyOutStream;
296 setStreams(NULL, out);
297
298 setWriter(new rfb::SMsgWriter(&cp, out));
299
300 manager = new Manager(this);
301}
302
303SConn::~SConn()
304{
305 delete manager;
306 delete out;
307}
308
309void SConn::writeUpdate(const rfb::UpdateInfo& ui, const rfb::PixelBuffer* pb)
310{
311 manager->writeUpdate(ui, pb, NULL);
312}
313
DRC77be9292015-02-21 11:44:26 -0600314void SConn::getStats(double& ratio, unsigned long long& bytes,
315 unsigned long long& rawEquivalent)
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100316{
DRC77be9292015-02-21 11:44:26 -0600317 manager->getStats(ratio, bytes, rawEquivalent);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100318}
319
320void SConn::setAccessRights(AccessRights ar)
321{
322}
323
324void SConn::setDesktopSize(int fb_width, int fb_height,
325 const rfb::ScreenSet& layout)
326{
327}
328
DRC77be9292015-02-21 11:44:26 -0600329static double runTest(const char *fn, double& ratio, unsigned long long& bytes,
330 unsigned long long& rawEquivalent)
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100331{
332 CConn *cc;
333 double time;
334
335 cc = new CConn(fn);
336
337 try {
338 while (true)
339 cc->processMsg();
340 } catch (rdr::EndOfStream e) {
341 } catch (rdr::Exception e) {
342 fprintf(stderr, "Failed to run rfb file: %s\n", e.str());
343 exit(1);
344 }
345
346 time = cc->encodeTime;
DRC77be9292015-02-21 11:44:26 -0600347 cc->getStats(ratio, bytes, rawEquivalent);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100348
349 delete cc;
350
351 return time;
352}
353
354static void sort(double *array, int count)
355{
356 bool sorted;
357 int i;
358 do {
359 sorted = true;
DRCb4c4a382015-02-21 11:28:37 -0600360 for (i = 1; i < count; i++) {
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100361 if (array[i-1] > array[i]) {
362 double d;
363 d = array[i];
DRCb4c4a382015-02-21 11:28:37 -0600364 array[i] = array[i - 1];
365 array[i - 1] = d;
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100366 sorted = false;
367 }
368 }
369 } while (!sorted);
370}
371
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100372static void usage(const char *argv0)
373{
374 fprintf(stderr, "Syntax: %s [options] <rfb file>\n", argv0);
375 fprintf(stderr, "Options:\n");
376 rfb::Configuration::listParams(79, 14);
377 exit(1);
378}
379
380int main(int argc, char **argv)
381{
382 int i;
383
384 const char *fn;
385
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100386 fn = NULL;
387 for (i = 1; i < argc; i++) {
388 if (rfb::Configuration::setParam(argv[i]))
389 continue;
390
391 if (argv[i][0] == '-') {
DRCb4c4a382015-02-21 11:28:37 -0600392 if (i + 1 < argc) {
393 if (rfb::Configuration::setParam(&argv[i][1], argv[i + 1])) {
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100394 i++;
395 continue;
396 }
397 }
398 usage(argv[0]);
399 }
400
401 if (fn != NULL)
402 usage(argv[0]);
403
404 fn = argv[i];
405 }
406
DRC4631a762015-02-21 11:57:27 -0600407 int runCount = count;
408 double times[runCount], dev[runCount];
409 double median, meddev, ratio;
410 unsigned long long bytes, equivalent;
411
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100412 if (fn == NULL) {
413 fprintf(stderr, "No file specified!\n\n");
414 usage(argv[0]);
415 }
416
417 if (!format.hasBeenSet()) {
418 fprintf(stderr, "Pixel format not specified!\n\n");
419 usage(argv[0]);
420 }
421
422 if (!width.hasBeenSet() || !height.hasBeenSet()) {
423 fprintf(stderr, "Frame buffer size not specified!\n\n");
424 usage(argv[0]);
425 }
426
427 // Warmup
DRC77be9292015-02-21 11:44:26 -0600428 runTest(fn, ratio, bytes, equivalent);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100429
430 // Multiple runs to get a good average
DRCb4c4a382015-02-21 11:28:37 -0600431 for (i = 0; i < runCount; i++)
DRC77be9292015-02-21 11:44:26 -0600432 times[i] = runTest(fn, ratio, bytes, equivalent);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100433
434 // Calculate median and median deviation
435 sort(times, runCount);
DRCb4c4a382015-02-21 11:28:37 -0600436 median = times[runCount / 2];
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100437
DRCb4c4a382015-02-21 11:28:37 -0600438 for (i = 0; i < runCount; i++)
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100439 dev[i] = fabs((times[i] - median) / median) * 100;
440
441 sort(dev, runCount);
DRCb4c4a382015-02-21 11:28:37 -0600442 meddev = dev[runCount / 2];
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100443
444 printf("CPU time: %g s (+/- %g %)\n", median, meddev);
DRC77be9292015-02-21 11:44:26 -0600445 printf("Encoded bytes: %lld\n", bytes);
446 printf("Raw equivalent bytes: %lld\n", equivalent);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100447 printf("Ratio: %g\n", ratio);
448
449 return 0;
450}