blob: d93c771f90d38d1acf009b1fc42fc249a1bea202 [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
DRC2a172c92015-02-25 14:18:07 -060056static rfb::BoolParameter translate("translate",
57 "Translate 8-bit and 16-bit datasets into 24-bit",
58 true);
59
Pierre Ossman8738e8a2015-02-11 13:49:04 +010060// The frame buffer (and output) is always this format
61static const rfb::PixelFormat fbPF(32, 24, false, true, 255, 255, 255, 0, 8, 16);
62
63// Encodings to use
64static const rdr::S32 encodings[] = {
65 rfb::encodingTight, rfb::encodingCopyRect, rfb::encodingRRE,
66 rfb::encodingHextile, rfb::encodingZRLE, rfb::pseudoEncodingLastRect,
DRC562eb712015-02-21 12:01:47 -060067 rfb::pseudoEncodingQualityLevel0 + 8,
68 rfb::pseudoEncodingCompressLevel0 + 2};
Pierre Ossman8738e8a2015-02-11 13:49:04 +010069
70class DummyOutStream : public rdr::OutStream {
71public:
72 DummyOutStream();
73
74 virtual int length();
75 virtual void flush();
76
77private:
78 virtual int overrun(int itemSize, int nItems);
79
80 int offset;
81 rdr::U8 buf[131072];
82};
83
84class CConn : public rfb::CConnection {
85public:
86 CConn(const char *filename);
87 ~CConn();
88
DRC77be9292015-02-21 11:44:26 -060089 void getStats(double& ratio, unsigned long long& bytes,
90 unsigned long long& rawEquivalent);
Pierre Ossman8738e8a2015-02-11 13:49:04 +010091
92 virtual void setDesktopSize(int w, int h);
93 virtual void setCursor(int, int, const rfb::Point&, void*, void*);
94 virtual void framebufferUpdateStart();
95 virtual void framebufferUpdateEnd();
96 virtual void dataRect(const rfb::Rect&, int);
97 virtual void setColourMapEntries(int, int, rdr::U16*);
98 virtual void bell();
99 virtual void serverCutText(const char*, rdr::U32);
100
101public:
102 double decodeTime;
103 double encodeTime;
104
105protected:
106 rdr::FileInStream *in;
DRCb4c4a382015-02-21 11:28:37 -0600107 rfb::Decoder *decoders[rfb::encodingMax + 1];
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100108 rfb::ManagedPixelBuffer pb;
109 rfb::SimpleUpdateTracker updates;
110 class SConn *sc;
111};
112
113class Manager : public rfb::EncodeManager {
114public:
115 Manager(class rfb::SConnection *conn);
116
DRC77be9292015-02-21 11:44:26 -0600117 void getStats(double&, unsigned long long&, unsigned long long&);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100118};
119
120class SConn : public rfb::SConnection {
121public:
122 SConn();
123 ~SConn();
124
125 void writeUpdate(const rfb::UpdateInfo& ui, const rfb::PixelBuffer* pb);
126
DRC77be9292015-02-21 11:44:26 -0600127 void getStats(double&, unsigned long long&, unsigned long long&);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100128
129 virtual void setAccessRights(AccessRights ar);
130
131 virtual void setDesktopSize(int fb_width, int fb_height,
132 const rfb::ScreenSet& layout);
133
134protected:
135 DummyOutStream *out;
136 Manager *manager;
137};
138
139DummyOutStream::DummyOutStream()
140{
141 offset = 0;
142 ptr = buf;
143 end = buf + sizeof(buf);
144}
145
146int DummyOutStream::length()
147{
148 flush();
149 return offset;
150}
151
152void DummyOutStream::flush()
153{
154 offset += ptr - buf;
155 ptr = buf;
156}
157
158int DummyOutStream::overrun(int itemSize, int nItems)
159{
160 flush();
Pierre Ossmanfc331e62015-03-03 16:42:45 +0100161 if (itemSize * nItems > end - ptr)
162 nItems = (end - ptr) / itemSize;
163 return nItems;
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100164}
165
166CConn::CConn(const char *filename)
167{
168 int i;
169
170 decodeTime = 0.0;
171 encodeTime = 0.0;
172
173 in = new rdr::FileInStream(filename);
174 setStreams(in, NULL);
175
176 memset(decoders, 0, sizeof(decoders));
DRCb4c4a382015-02-21 11:28:37 -0600177 for (i = 0; i < rfb::encodingMax; i++) {
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100178 if (!rfb::Decoder::supported(i))
179 continue;
180
181 decoders[i] = rfb::Decoder::createDecoder(i, this);
182 }
183
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100184 // Need to skip the initial handshake and ServerInit
185 setState(RFBSTATE_NORMAL);
186 // That also means that the reader and writer weren't setup
187 setReader(new rfb::CMsgReader(this, in));
188 // Nor the frame buffer size and format
189 setDesktopSize(width, height);
190 rfb::PixelFormat pf;
191 pf.parse(format);
192 setPixelFormat(pf);
193
DRC2a172c92015-02-25 14:18:07 -0600194 pb.setPF((bool)translate ? fbPF : pf);
195
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100196 sc = new SConn();
197 sc->cp.setPF(pb.getPF());
DRCb4c4a382015-02-21 11:28:37 -0600198 sc->setEncodings(sizeof(encodings) / sizeof(*encodings), encodings);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100199}
200
201CConn::~CConn()
202{
203 int i;
204
205 delete sc;
206
207 delete in;
208
DRCb4c4a382015-02-21 11:28:37 -0600209 for (i = 0; i < rfb::encodingMax; i++)
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100210 delete decoders[i];
211}
212
DRC77be9292015-02-21 11:44:26 -0600213void CConn::getStats(double& ratio, unsigned long long& bytes,
214 unsigned long long& rawEquivalent)
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100215{
DRC77be9292015-02-21 11:44:26 -0600216 sc->getStats(ratio, bytes, rawEquivalent);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100217}
218
219void CConn::setDesktopSize(int w, int h)
220{
221 CConnection::setDesktopSize(w, h);
222
223 pb.setSize(cp.width, cp.height);
224}
225
226void CConn::setCursor(int, int, const rfb::Point&, void*, void*)
227{
228}
229
230void CConn::framebufferUpdateStart()
231{
232 updates.clear();
233}
234
235void CConn::framebufferUpdateEnd()
236{
237 rfb::UpdateInfo ui;
238 rfb::Region clip(pb.getRect());
239
240 updates.getUpdateInfo(&ui, clip);
241
242 startCpuCounter();
243 sc->writeUpdate(ui, &pb);
244 endCpuCounter();
245
246 encodeTime += getCpuCounter();
247}
248
249void CConn::dataRect(const rfb::Rect &r, int encoding)
250{
251 if (!decoders[encoding])
252 throw rdr::Exception("Unknown encoding");
253
254 startCpuCounter();
255 decoders[encoding]->readRect(r, &pb);
256 endCpuCounter();
257
258 decodeTime += getCpuCounter();
259
260 if (encoding != rfb::encodingCopyRect) // FIXME
261 updates.add_changed(rfb::Region(r));
262}
263
264void CConn::setColourMapEntries(int, int, rdr::U16*)
265{
266}
267
268void CConn::bell()
269{
270}
271
272void CConn::serverCutText(const char*, rdr::U32)
273{
274}
275
276Manager::Manager(class rfb::SConnection *conn) :
277 EncodeManager(conn)
278{
279}
280
DRC77be9292015-02-21 11:44:26 -0600281void Manager::getStats(double& ratio, unsigned long long& encodedBytes,
282 unsigned long long& rawEquivalent)
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100283{
284 StatsVector::iterator iter;
285 unsigned long long bytes, equivalent;
286
287 bytes = equivalent = 0;
DRCb4c4a382015-02-21 11:28:37 -0600288 for (iter = stats.begin(); iter != stats.end(); ++iter) {
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100289 StatsVector::value_type::iterator iter2;
DRCb4c4a382015-02-21 11:28:37 -0600290 for (iter2 = iter->begin(); iter2 != iter->end(); ++iter2) {
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100291 bytes += iter2->bytes;
292 equivalent += iter2->equivalent;
293 }
294 }
295
DRC77be9292015-02-21 11:44:26 -0600296 ratio = (double)equivalent / bytes;
297 encodedBytes = bytes;
298 rawEquivalent = equivalent;
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100299}
300
301SConn::SConn()
302{
303 out = new DummyOutStream;
304 setStreams(NULL, out);
305
306 setWriter(new rfb::SMsgWriter(&cp, out));
307
308 manager = new Manager(this);
309}
310
311SConn::~SConn()
312{
313 delete manager;
314 delete out;
315}
316
317void SConn::writeUpdate(const rfb::UpdateInfo& ui, const rfb::PixelBuffer* pb)
318{
319 manager->writeUpdate(ui, pb, NULL);
320}
321
DRC77be9292015-02-21 11:44:26 -0600322void SConn::getStats(double& ratio, unsigned long long& bytes,
323 unsigned long long& rawEquivalent)
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100324{
DRC77be9292015-02-21 11:44:26 -0600325 manager->getStats(ratio, bytes, rawEquivalent);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100326}
327
328void SConn::setAccessRights(AccessRights ar)
329{
330}
331
332void SConn::setDesktopSize(int fb_width, int fb_height,
333 const rfb::ScreenSet& layout)
334{
335}
336
DRC77be9292015-02-21 11:44:26 -0600337static double runTest(const char *fn, double& ratio, unsigned long long& bytes,
338 unsigned long long& rawEquivalent)
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100339{
340 CConn *cc;
341 double time;
342
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100343 try {
DRC13cfb512015-02-26 12:24:03 -0600344 cc = new CConn(fn);
Pierre Ossman86475a62015-03-03 16:42:15 +0100345 } catch (rdr::Exception e) {
346 fprintf(stderr, "Failed to open rfb file: %s\n", e.str());
347 exit(1);
348 }
DRC13cfb512015-02-26 12:24:03 -0600349
Pierre Ossman86475a62015-03-03 16:42:15 +0100350 try {
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100351 while (true)
352 cc->processMsg();
353 } catch (rdr::EndOfStream e) {
354 } catch (rdr::Exception e) {
355 fprintf(stderr, "Failed to run rfb file: %s\n", e.str());
356 exit(1);
357 }
358
359 time = cc->encodeTime;
DRC77be9292015-02-21 11:44:26 -0600360 cc->getStats(ratio, bytes, rawEquivalent);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100361
362 delete cc;
363
364 return time;
365}
366
367static void sort(double *array, int count)
368{
369 bool sorted;
370 int i;
371 do {
372 sorted = true;
DRCb4c4a382015-02-21 11:28:37 -0600373 for (i = 1; i < count; i++) {
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100374 if (array[i-1] > array[i]) {
375 double d;
376 d = array[i];
DRCb4c4a382015-02-21 11:28:37 -0600377 array[i] = array[i - 1];
378 array[i - 1] = d;
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100379 sorted = false;
380 }
381 }
382 } while (!sorted);
383}
384
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100385static void usage(const char *argv0)
386{
387 fprintf(stderr, "Syntax: %s [options] <rfb file>\n", argv0);
388 fprintf(stderr, "Options:\n");
389 rfb::Configuration::listParams(79, 14);
390 exit(1);
391}
392
393int main(int argc, char **argv)
394{
395 int i;
396
397 const char *fn;
398
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100399 fn = NULL;
400 for (i = 1; i < argc; i++) {
401 if (rfb::Configuration::setParam(argv[i]))
402 continue;
403
404 if (argv[i][0] == '-') {
DRCb4c4a382015-02-21 11:28:37 -0600405 if (i + 1 < argc) {
406 if (rfb::Configuration::setParam(&argv[i][1], argv[i + 1])) {
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100407 i++;
408 continue;
409 }
410 }
411 usage(argv[0]);
412 }
413
414 if (fn != NULL)
415 usage(argv[0]);
416
417 fn = argv[i];
418 }
419
DRC4631a762015-02-21 11:57:27 -0600420 int runCount = count;
421 double times[runCount], dev[runCount];
422 double median, meddev, ratio;
423 unsigned long long bytes, equivalent;
424
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100425 if (fn == NULL) {
426 fprintf(stderr, "No file specified!\n\n");
427 usage(argv[0]);
428 }
429
Pierre Ossman135906e2015-04-27 12:48:47 +0200430 if (strcmp(format, "") == 0) {
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100431 fprintf(stderr, "Pixel format not specified!\n\n");
432 usage(argv[0]);
433 }
434
Pierre Ossman135906e2015-04-27 12:48:47 +0200435 if (width == 0 || height == 0) {
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100436 fprintf(stderr, "Frame buffer size not specified!\n\n");
437 usage(argv[0]);
438 }
439
440 // Warmup
DRC77be9292015-02-21 11:44:26 -0600441 runTest(fn, ratio, bytes, equivalent);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100442
443 // Multiple runs to get a good average
DRCb4c4a382015-02-21 11:28:37 -0600444 for (i = 0; i < runCount; i++)
DRC77be9292015-02-21 11:44:26 -0600445 times[i] = runTest(fn, ratio, bytes, equivalent);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100446
447 // Calculate median and median deviation
448 sort(times, runCount);
DRCb4c4a382015-02-21 11:28:37 -0600449 median = times[runCount / 2];
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100450
DRCb4c4a382015-02-21 11:28:37 -0600451 for (i = 0; i < runCount; i++)
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100452 dev[i] = fabs((times[i] - median) / median) * 100;
453
454 sort(dev, runCount);
DRCb4c4a382015-02-21 11:28:37 -0600455 meddev = dev[runCount / 2];
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100456
DRCe46dda62015-02-25 14:08:34 -0600457 printf("CPU time: %g s (+/- %g %%)\n", median, meddev);
Pierre Ossman7d218b02015-03-03 16:43:05 +0100458#ifdef WIN32
459 printf("Encoded bytes: %I64d\n", bytes);
460 printf("Raw equivalent bytes: %I64d\n", equivalent);
461#else
DRC77be9292015-02-21 11:44:26 -0600462 printf("Encoded bytes: %lld\n", bytes);
463 printf("Raw equivalent bytes: %lld\n", equivalent);
Pierre Ossman7d218b02015-03-03 16:43:05 +0100464#endif
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100465 printf("Ratio: %g\n", ratio);
466
467 return 0;
468}