blob: 6f9283b83045e54a378b7dc6999372df0fd3ac85 [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>
Pierre Ossman98d7af92015-11-16 09:37:46 +010032#include <sys/time.h>
Pierre Ossman8738e8a2015-02-11 13:49:04 +010033
34#include <rdr/Exception.h>
35#include <rdr/OutStream.h>
36#include <rdr/FileInStream.h>
37
38#include <rfb/PixelFormat.h>
39
40#include <rfb/CConnection.h>
41#include <rfb/CMsgReader.h>
Pierre Ossman8738e8a2015-02-11 13:49:04 +010042#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
Pierre Ossmandd45b442018-10-31 17:08:59 +010092 virtual void initDone();
Pierre Ossman6a1a0d02017-02-19 15:48:17 +010093 virtual void setCursor(int, int, const rfb::Point&, const rdr::U8*);
Pierre Ossman8738e8a2015-02-11 13:49:04 +010094 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;
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100107 rfb::SimpleUpdateTracker updates;
108 class SConn *sc;
109};
110
111class Manager : public rfb::EncodeManager {
112public:
113 Manager(class rfb::SConnection *conn);
114
DRC77be9292015-02-21 11:44:26 -0600115 void getStats(double&, unsigned long long&, unsigned long long&);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100116};
117
118class SConn : public rfb::SConnection {
119public:
120 SConn();
121 ~SConn();
122
123 void writeUpdate(const rfb::UpdateInfo& ui, const rfb::PixelBuffer* pb);
124
DRC77be9292015-02-21 11:44:26 -0600125 void getStats(double&, unsigned long long&, unsigned long long&);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100126
127 virtual void setAccessRights(AccessRights ar);
128
129 virtual void setDesktopSize(int fb_width, int fb_height,
130 const rfb::ScreenSet& layout);
131
132protected:
133 DummyOutStream *out;
134 Manager *manager;
135};
136
137DummyOutStream::DummyOutStream()
138{
139 offset = 0;
140 ptr = buf;
141 end = buf + sizeof(buf);
142}
143
144int DummyOutStream::length()
145{
146 flush();
147 return offset;
148}
149
150void DummyOutStream::flush()
151{
152 offset += ptr - buf;
153 ptr = buf;
154}
155
156int DummyOutStream::overrun(int itemSize, int nItems)
157{
158 flush();
Pierre Ossmanfc331e62015-03-03 16:42:45 +0100159 if (itemSize * nItems > end - ptr)
160 nItems = (end - ptr) / itemSize;
161 return nItems;
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100162}
163
164CConn::CConn(const char *filename)
165{
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100166 decodeTime = 0.0;
167 encodeTime = 0.0;
168
169 in = new rdr::FileInStream(filename);
170 setStreams(in, NULL);
171
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100172 // Need to skip the initial handshake and ServerInit
173 setState(RFBSTATE_NORMAL);
174 // That also means that the reader and writer weren't setup
175 setReader(new rfb::CMsgReader(this, in));
176 // Nor the frame buffer size and format
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100177 rfb::PixelFormat pf;
178 pf.parse(format);
179 setPixelFormat(pf);
Pierre Ossman98d7af92015-11-16 09:37:46 +0100180 setDesktopSize(width, height);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100181
182 sc = new SConn();
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200183 sc->client.setPF((bool)translate ? fbPF : pf);
DRCb4c4a382015-02-21 11:28:37 -0600184 sc->setEncodings(sizeof(encodings) / sizeof(*encodings), encodings);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100185}
186
187CConn::~CConn()
188{
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100189 delete sc;
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100190 delete in;
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100191}
192
DRC77be9292015-02-21 11:44:26 -0600193void CConn::getStats(double& ratio, unsigned long long& bytes,
194 unsigned long long& rawEquivalent)
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100195{
DRC77be9292015-02-21 11:44:26 -0600196 sc->getStats(ratio, bytes, rawEquivalent);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100197}
198
Pierre Ossmandd45b442018-10-31 17:08:59 +0100199void CConn::initDone()
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100200{
Pierre Ossman98d7af92015-11-16 09:37:46 +0100201 rfb::ModifiablePixelBuffer *pb;
202
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200203 pb = new rfb::ManagedPixelBuffer((bool)translate ? fbPF : server.pf(),
204 server.width(), server.height());
Pierre Ossman98d7af92015-11-16 09:37:46 +0100205 setFramebuffer(pb);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100206}
207
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100208void CConn::setCursor(int, int, const rfb::Point&, const rdr::U8*)
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100209{
210}
211
212void CConn::framebufferUpdateStart()
213{
Pierre Ossman3da238d2015-11-12 12:20:05 +0100214 CConnection::framebufferUpdateStart();
215
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100216 updates.clear();
Pierre Ossman9f273e92015-11-09 16:34:54 +0100217 startCpuCounter();
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100218}
219
220void CConn::framebufferUpdateEnd()
221{
222 rfb::UpdateInfo ui;
Pierre Ossman9f273e92015-11-09 16:34:54 +0100223 rfb::PixelBuffer* pb = getFramebuffer();
224 rfb::Region clip(pb->getRect());
225
Pierre Ossman3da238d2015-11-12 12:20:05 +0100226 CConnection::framebufferUpdateEnd();
227
Pierre Ossman9f273e92015-11-09 16:34:54 +0100228 endCpuCounter();
229
230 decodeTime += getCpuCounter();
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100231
232 updates.getUpdateInfo(&ui, clip);
233
234 startCpuCounter();
Pierre Ossman9f273e92015-11-09 16:34:54 +0100235 sc->writeUpdate(ui, pb);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100236 endCpuCounter();
237
238 encodeTime += getCpuCounter();
239}
240
241void CConn::dataRect(const rfb::Rect &r, int encoding)
242{
Pierre Ossman9f273e92015-11-09 16:34:54 +0100243 CConnection::dataRect(r, encoding);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100244
245 if (encoding != rfb::encodingCopyRect) // FIXME
246 updates.add_changed(rfb::Region(r));
247}
248
249void CConn::setColourMapEntries(int, int, rdr::U16*)
250{
251}
252
253void CConn::bell()
254{
255}
256
257void CConn::serverCutText(const char*, rdr::U32)
258{
259}
260
261Manager::Manager(class rfb::SConnection *conn) :
262 EncodeManager(conn)
263{
264}
265
DRC77be9292015-02-21 11:44:26 -0600266void Manager::getStats(double& ratio, unsigned long long& encodedBytes,
267 unsigned long long& rawEquivalent)
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100268{
269 StatsVector::iterator iter;
270 unsigned long long bytes, equivalent;
271
272 bytes = equivalent = 0;
DRCb4c4a382015-02-21 11:28:37 -0600273 for (iter = stats.begin(); iter != stats.end(); ++iter) {
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100274 StatsVector::value_type::iterator iter2;
DRCb4c4a382015-02-21 11:28:37 -0600275 for (iter2 = iter->begin(); iter2 != iter->end(); ++iter2) {
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100276 bytes += iter2->bytes;
277 equivalent += iter2->equivalent;
278 }
279 }
280
DRC77be9292015-02-21 11:44:26 -0600281 ratio = (double)equivalent / bytes;
282 encodedBytes = bytes;
283 rawEquivalent = equivalent;
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100284}
285
286SConn::SConn()
287{
288 out = new DummyOutStream;
289 setStreams(NULL, out);
290
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200291 setWriter(new rfb::SMsgWriter(&client, out));
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100292
293 manager = new Manager(this);
294}
295
296SConn::~SConn()
297{
298 delete manager;
299 delete out;
300}
301
302void SConn::writeUpdate(const rfb::UpdateInfo& ui, const rfb::PixelBuffer* pb)
303{
304 manager->writeUpdate(ui, pb, NULL);
305}
306
DRC77be9292015-02-21 11:44:26 -0600307void SConn::getStats(double& ratio, unsigned long long& bytes,
308 unsigned long long& rawEquivalent)
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100309{
DRC77be9292015-02-21 11:44:26 -0600310 manager->getStats(ratio, bytes, rawEquivalent);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100311}
312
313void SConn::setAccessRights(AccessRights ar)
314{
315}
316
317void SConn::setDesktopSize(int fb_width, int fb_height,
318 const rfb::ScreenSet& layout)
319{
320}
321
Pierre Ossman98d7af92015-11-16 09:37:46 +0100322struct stats
323{
324 double decodeTime;
325 double encodeTime;
326 double realTime;
327
328 double ratio;
329 unsigned long long bytes;
330 unsigned long long rawEquivalent;
331};
332
333static struct stats runTest(const char *fn)
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100334{
335 CConn *cc;
Pierre Ossman98d7af92015-11-16 09:37:46 +0100336 struct stats s;
337 struct timeval start, stop;
338
339 gettimeofday(&start, NULL);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100340
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100341 try {
DRC13cfb512015-02-26 12:24:03 -0600342 cc = new CConn(fn);
Pierre Ossman8ee522a2018-05-29 15:50:08 +0200343 } catch (rdr::Exception& e) {
Pierre Ossman86475a62015-03-03 16:42:15 +0100344 fprintf(stderr, "Failed to open rfb file: %s\n", e.str());
345 exit(1);
346 }
DRC13cfb512015-02-26 12:24:03 -0600347
Pierre Ossman86475a62015-03-03 16:42:15 +0100348 try {
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100349 while (true)
350 cc->processMsg();
Pierre Ossman8ee522a2018-05-29 15:50:08 +0200351 } catch (rdr::EndOfStream& e) {
352 } catch (rdr::Exception& e) {
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100353 fprintf(stderr, "Failed to run rfb file: %s\n", e.str());
354 exit(1);
355 }
356
Pierre Ossman98d7af92015-11-16 09:37:46 +0100357 gettimeofday(&stop, NULL);
358
359 s.decodeTime = cc->decodeTime;
360 s.encodeTime = cc->encodeTime;
361 s.realTime = (double)stop.tv_sec - start.tv_sec;
362 s.realTime += ((double)stop.tv_usec - start.tv_usec)/1000000.0;
363 cc->getStats(s.ratio, s.bytes, s.rawEquivalent);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100364
365 delete cc;
366
Pierre Ossman98d7af92015-11-16 09:37:46 +0100367 return s;
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100368}
369
370static void sort(double *array, int count)
371{
372 bool sorted;
373 int i;
374 do {
375 sorted = true;
DRCb4c4a382015-02-21 11:28:37 -0600376 for (i = 1; i < count; i++) {
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100377 if (array[i-1] > array[i]) {
378 double d;
379 d = array[i];
DRCb4c4a382015-02-21 11:28:37 -0600380 array[i] = array[i - 1];
381 array[i - 1] = d;
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100382 sorted = false;
383 }
384 }
385 } while (!sorted);
386}
387
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100388static void usage(const char *argv0)
389{
390 fprintf(stderr, "Syntax: %s [options] <rfb file>\n", argv0);
391 fprintf(stderr, "Options:\n");
392 rfb::Configuration::listParams(79, 14);
393 exit(1);
394}
395
396int main(int argc, char **argv)
397{
398 int i;
399
400 const char *fn;
401
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100402 fn = NULL;
403 for (i = 1; i < argc; i++) {
404 if (rfb::Configuration::setParam(argv[i]))
405 continue;
406
407 if (argv[i][0] == '-') {
DRCb4c4a382015-02-21 11:28:37 -0600408 if (i + 1 < argc) {
409 if (rfb::Configuration::setParam(&argv[i][1], argv[i + 1])) {
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100410 i++;
411 continue;
412 }
413 }
414 usage(argv[0]);
415 }
416
417 if (fn != NULL)
418 usage(argv[0]);
419
420 fn = argv[i];
421 }
422
DRC4631a762015-02-21 11:57:27 -0600423 int runCount = count;
Pierre Ossman98d7af92015-11-16 09:37:46 +0100424 struct stats runs[runCount];
425 double values[runCount], dev[runCount];
426 double median, meddev;
DRC4631a762015-02-21 11:57:27 -0600427
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100428 if (fn == NULL) {
429 fprintf(stderr, "No file specified!\n\n");
430 usage(argv[0]);
431 }
432
Pierre Ossman135906e2015-04-27 12:48:47 +0200433 if (strcmp(format, "") == 0) {
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100434 fprintf(stderr, "Pixel format not specified!\n\n");
435 usage(argv[0]);
436 }
437
Pierre Ossman135906e2015-04-27 12:48:47 +0200438 if (width == 0 || height == 0) {
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100439 fprintf(stderr, "Frame buffer size not specified!\n\n");
440 usage(argv[0]);
441 }
442
443 // Warmup
Pierre Ossman98d7af92015-11-16 09:37:46 +0100444 runTest(fn);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100445
446 // Multiple runs to get a good average
DRCb4c4a382015-02-21 11:28:37 -0600447 for (i = 0; i < runCount; i++)
Pierre Ossman98d7af92015-11-16 09:37:46 +0100448 runs[i] = runTest(fn);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100449
Pierre Ossman98d7af92015-11-16 09:37:46 +0100450 // Calculate median and median deviation for CPU usage decoding
451 for (i = 0;i < runCount;i++)
452 values[i] = runs[i].decodeTime;
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100453
Pierre Ossman98d7af92015-11-16 09:37:46 +0100454 sort(values, runCount);
455 median = values[runCount/2];
456
457 for (i = 0;i < runCount;i++)
458 dev[i] = fabs((values[i] - median) / median) * 100;
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100459
460 sort(dev, runCount);
Pierre Ossman98d7af92015-11-16 09:37:46 +0100461 meddev = dev[runCount/2];
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100462
Pierre Ossman98d7af92015-11-16 09:37:46 +0100463 printf("CPU time (decoding): %g s (+/- %g %%)\n", median, meddev);
464
465 // And for CPU usage encoding
466 for (i = 0;i < runCount;i++)
467 values[i] = runs[i].encodeTime;
468
469 sort(values, runCount);
470 median = values[runCount/2];
471
472 for (i = 0;i < runCount;i++)
473 dev[i] = fabs((values[i] - median) / median) * 100;
474
475 sort(dev, runCount);
476 meddev = dev[runCount/2];
477
478 printf("CPU time (encoding): %g s (+/- %g %%)\n", median, meddev);
479
480 // And for CPU core usage encoding
481 for (i = 0;i < runCount;i++)
482 values[i] = (runs[i].decodeTime + runs[i].encodeTime) / runs[i].realTime;
483
484 sort(values, runCount);
485 median = values[runCount/2];
486
487 for (i = 0;i < runCount;i++)
488 dev[i] = fabs((values[i] - median) / median) * 100;
489
490 sort(dev, runCount);
491 meddev = dev[runCount/2];
492
493 printf("Core usage (total): %g (+/- %g %%)\n", median, meddev);
494
Pierre Ossman7d218b02015-03-03 16:43:05 +0100495#ifdef WIN32
Pierre Ossman98d7af92015-11-16 09:37:46 +0100496 printf("Encoded bytes: %I64d\n", runs[0].bytes);
497 printf("Raw equivalent bytes: %I64d\n", runs[0].rawEquivalent);
Pierre Ossman7d218b02015-03-03 16:43:05 +0100498#else
Pierre Ossman98d7af92015-11-16 09:37:46 +0100499 printf("Encoded bytes: %lld\n", runs[0].bytes);
500 printf("Raw equivalent bytes: %lld\n", runs[0].rawEquivalent);
Pierre Ossman7d218b02015-03-03 16:43:05 +0100501#endif
Pierre Ossman98d7af92015-11-16 09:37:46 +0100502 printf("Ratio: %g\n", runs[0].ratio);
Pierre Ossman8738e8a2015-02-11 13:49:04 +0100503
504 return 0;
505}