blob: 7f1e2c1d1abc0381f8353c349fd1fe95bc353901 [file] [log] [blame]
Pierre Ossman38a1c702016-12-20 12:32:37 +01001/* Copyright 2016 Pierre Ossman <ossman@cendio.se> for Cendio AB
2 *
3 * This is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This software is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16 * USA.
17 */
18
Pierre Ossman5b092762017-01-12 08:28:45 +010019#include <math.h>
Pierre Ossman38a1c702016-12-20 12:32:37 +010020#include <sys/time.h>
21
22#include <FL/Fl.H>
23#include <FL/Fl_Window.H>
24#include <FL/fl_draw.H>
25
26#include <rdr/Exception.h>
27#include <rfb/util.h>
28
29#include "../vncviewer/PlatformPixelBuffer.h"
Pierre Ossman38a1c702016-12-20 12:32:37 +010030
31#include "util.h"
32
33class TestWindow: public Fl_Window {
34public:
35 TestWindow();
36 ~TestWindow();
37
Pierre Ossman5b092762017-01-12 08:28:45 +010038 virtual void start(int width, int height);
39 virtual void stop();
40
Pierre Ossman38a1c702016-12-20 12:32:37 +010041 virtual void draw();
42
43protected:
44 virtual void flush();
45
46 void update();
47 virtual void changefb();
48
49 static void timer(void* data);
50
51public:
52 unsigned long long pixels, frames;
53 double time;
54
55protected:
56 PlatformPixelBuffer* fb;
57};
58
59class PartialTestWindow: public TestWindow {
60protected:
61 virtual void changefb();
62};
63
Pierre Ossman403ac272017-01-02 17:00:41 +010064class OverlayTestWindow: public PartialTestWindow {
65public:
66 OverlayTestWindow();
67
68 virtual void start(int width, int height);
69 virtual void stop();
70
71 virtual void draw();
72
73protected:
74 Surface* overlay;
Pierre Ossman3d74d882017-01-02 19:49:52 +010075 Surface* offscreen;
Pierre Ossman403ac272017-01-02 17:00:41 +010076};
77
Pierre Ossman38a1c702016-12-20 12:32:37 +010078TestWindow::TestWindow() :
Pierre Ossman5b092762017-01-12 08:28:45 +010079 Fl_Window(0, 0, "Framebuffer Performance Test"),
80 fb(NULL)
81{
82}
83
84TestWindow::~TestWindow()
85{
86 stop();
87}
88
89void TestWindow::start(int width, int height)
Pierre Ossman38a1c702016-12-20 12:32:37 +010090{
91 rdr::U32 pixel;
92
Pierre Ossman5b092762017-01-12 08:28:45 +010093 stop();
94
95 resize(x(), y(), width, height);
96
Pierre Ossman38a1c702016-12-20 12:32:37 +010097 pixels = 0;
98 frames = 0;
99 time = 0;
100
Pierre Ossman403ac272017-01-02 17:00:41 +0100101 fb = new PlatformPixelBuffer(w(), h());
Pierre Ossman38a1c702016-12-20 12:32:37 +0100102
103 pixel = 0;
104 fb->fillRect(fb->getRect(), &pixel);
Pierre Ossman5b092762017-01-12 08:28:45 +0100105
106 show();
Pierre Ossman38a1c702016-12-20 12:32:37 +0100107}
108
Pierre Ossman5b092762017-01-12 08:28:45 +0100109void TestWindow::stop()
Pierre Ossman38a1c702016-12-20 12:32:37 +0100110{
Pierre Ossman5b092762017-01-12 08:28:45 +0100111 hide();
112
Pierre Ossman38a1c702016-12-20 12:32:37 +0100113 delete fb;
Pierre Ossman5b092762017-01-12 08:28:45 +0100114 fb = NULL;
Pierre Ossman38a1c702016-12-20 12:32:37 +0100115
116 Fl::remove_idle(timer, this);
117}
118
119void TestWindow::draw()
120{
121 int X, Y, W, H;
122
123 // We cannot update the damage region from inside the draw function,
124 // so delegate this to an idle function
125 Fl::add_idle(timer, this);
126
127 // Check what actually needs updating
128 fl_clip_box(0, 0, w(), h(), X, Y, W, H);
129 if ((W == 0) || (H == 0))
130 return;
131
132 fb->draw(X, Y, X, Y, W, H);
133
134 pixels += W*H;
135 frames++;
136}
137
138void TestWindow::flush()
139{
140 startTimeCounter();
141 Fl_Window::flush();
142#if !defined(WIN32) && !defined(__APPLE__)
143 // Make sure we measure any work we queue up
144 XSync(fl_display, False);
145#endif
146 endTimeCounter();
147
148 time += getTimeCounter();
149}
150
151void TestWindow::update()
152{
153 rfb::Rect r;
154
155 startTimeCounter();
156
Pierre Ossman38a1c702016-12-20 12:32:37 +0100157 changefb();
158
159 r = fb->getDamage();
160 damage(FL_DAMAGE_USER1, r.tl.x, r.tl.y, r.width(), r.height());
161
162#if !defined(WIN32) && !defined(__APPLE__)
163 // Make sure we measure any work we queue up
164 XSync(fl_display, False);
165#endif
166
167 endTimeCounter();
168
169 time += getTimeCounter();
170}
171
172void TestWindow::changefb()
173{
174 rdr::U32 pixel;
175
176 pixel = rand();
177 fb->fillRect(fb->getRect(), &pixel);
178}
179
180void TestWindow::timer(void* data)
181{
182 TestWindow* self;
183
184 Fl::remove_idle(timer, data);
185
186 self = (TestWindow*)data;
187 self->update();
188}
189
190void PartialTestWindow::changefb()
191{
192 rfb::Rect r;
193 rdr::U32 pixel;
194
195 r = fb->getRect();
196 r.tl.x += w() / 4;
197 r.tl.y += h() / 4;
198 r.br.x -= w() / 4;
199 r.br.y -= h() / 4;
200
201 pixel = rand();
202 fb->fillRect(r, &pixel);
203}
204
Pierre Ossman403ac272017-01-02 17:00:41 +0100205OverlayTestWindow::OverlayTestWindow() :
Pierre Ossman3d74d882017-01-02 19:49:52 +0100206 overlay(NULL), offscreen(NULL)
Pierre Ossman403ac272017-01-02 17:00:41 +0100207{
208}
209
210void OverlayTestWindow::start(int width, int height)
211{
212 PartialTestWindow::start(width, height);
213
214 overlay = new Surface(400, 200);
215 overlay->clear(0xff, 0x80, 0x00, 0xcc);
Pierre Ossman3d74d882017-01-02 19:49:52 +0100216
217 // X11 needs an off screen buffer for compositing to avoid flicker
218#if !defined(WIN32) && !defined(__APPLE__)
219 offscreen = new Surface(w(), h());
220#else
221 offscreen = NULL;
222#endif
Pierre Ossman403ac272017-01-02 17:00:41 +0100223}
224
225void OverlayTestWindow::stop()
226{
227 PartialTestWindow::stop();
228
Pierre Ossman3d74d882017-01-02 19:49:52 +0100229 delete offscreen;
230 offscreen = NULL;
Pierre Ossman403ac272017-01-02 17:00:41 +0100231 delete overlay;
232 overlay = NULL;
233}
234
235void OverlayTestWindow::draw()
236{
237 int ox, oy, ow, oh;
238 int X, Y, W, H;
239
Pierre Ossman3d74d882017-01-02 19:49:52 +0100240 // We cannot update the damage region from inside the draw function,
241 // so delegate this to an idle function
242 Fl::add_idle(timer, this);
243
Pierre Ossman403ac272017-01-02 17:00:41 +0100244 // Check what actually needs updating
245 fl_clip_box(0, 0, w(), h(), X, Y, W, H);
246 if ((W == 0) || (H == 0))
247 return;
248
Pierre Ossman403ac272017-01-02 17:00:41 +0100249 // We might get a redraw before we are fully ready
250 if (!overlay)
251 return;
252
253 // Simplify the clip region to a simple rectangle in order to
254 // properly draw all the layers even if they only partially overlap
255 fl_push_no_clip();
256 fl_push_clip(X, Y, W, H);
257
Pierre Ossman3d74d882017-01-02 19:49:52 +0100258 if (offscreen)
259 fb->draw(offscreen, X, Y, X, Y, W, H);
260 else
261 fb->draw(X, Y, X, Y, W, H);
262
263 pixels += W*H;
264 frames++;
265
Pierre Ossman403ac272017-01-02 17:00:41 +0100266 ox = (w() - overlay->width()) / 2;
267 oy = h() / 4 - overlay->height() / 2;
268 ow = overlay->width();
269 oh = overlay->height();
270 fl_clip_box(ox, oy, ow, oh, X, Y, W, H);
Pierre Ossman3d74d882017-01-02 19:49:52 +0100271 if ((W != 0) && (H != 0)) {
272 if (offscreen)
273 overlay->draw(offscreen, X - ox, Y - oy, X, Y, W, H);
274 else
275 overlay->draw(X - ox, Y - oy, X, Y, W, H);
276 }
Pierre Ossman403ac272017-01-02 17:00:41 +0100277
278 fl_pop_clip();
279 fl_pop_clip();
Pierre Ossman3d74d882017-01-02 19:49:52 +0100280
281 if (offscreen) {
282 fl_clip_box(0, 0, w(), h(), X, Y, W, H);
283 offscreen->draw(X, Y, X, Y, W, H);
284 }
Pierre Ossman403ac272017-01-02 17:00:41 +0100285}
286
Pierre Ossman5b092762017-01-12 08:28:45 +0100287static void dosubtest(TestWindow* win, int width, int height,
288 unsigned long long* pixels,
289 unsigned long long* frames,
290 double* time)
Pierre Ossman38a1c702016-12-20 12:32:37 +0100291{
292 struct timeval start;
Pierre Ossman38a1c702016-12-20 12:32:37 +0100293
Pierre Ossman5b092762017-01-12 08:28:45 +0100294 win->start(width, height);
Pierre Ossman38a1c702016-12-20 12:32:37 +0100295
296 gettimeofday(&start, NULL);
Pierre Ossman5b092762017-01-12 08:28:45 +0100297 while (rfb::msSince(&start) < 3000)
Pierre Ossman38a1c702016-12-20 12:32:37 +0100298 Fl::wait();
299
Pierre Ossman5b092762017-01-12 08:28:45 +0100300 win->stop();
301
302 *pixels = win->pixels;
303 *frames = win->frames;
304 *time = win->time;
305}
306
307static bool is_constant(double a, double b)
308{
309 return (fabs(a - b) / a) < 0.1;
310}
311
312static void dotest(TestWindow* win)
313{
314 unsigned long long pixels[3];
315 unsigned long long frames[3];
316 double time[3];
317
318 double delay, rate;
319 char s[1024];
320
321 // Run the test several times at different resolutions...
322 dosubtest(win, 800, 600, &pixels[0], &frames[0], &time[0]);
323 dosubtest(win, 1024, 768, &pixels[1], &frames[1], &time[1]);
324 dosubtest(win, 1280, 960, &pixels[2], &frames[2], &time[2]);
325
326 // ...in order to compute how much of the rendering time is static,
327 // and how much depends on the number of pixels
328 // (i.e. solve: time = delay * frames + rate * pixels)
329 delay = (((time[0] - (double)pixels[0] / pixels[1] * time[1]) /
330 (frames[0] - (double)pixels[0] / pixels[1] * frames[1])) +
331 ((time[1] - (double)pixels[1] / pixels[2] * time[2]) /
332 (frames[1] - (double)pixels[1] / pixels[2] * frames[2]))) / 2.0;
333 rate = (((time[0] - (double)frames[0] / frames[1] * time[1]) /
334 (pixels[0] - (double)frames[0] / frames[1] * pixels[1])) +
335 ((time[1] - (double)frames[1] / frames[2] * time[2]) /
336 (pixels[1] - (double)frames[1] / frames[2] * pixels[2]))) / 2.0;
337
338 // However, we have some corner cases:
339
340 // We are restricted by some delay, e.g. refresh rate
341 if (is_constant(frames[0]/time[0], frames[2]/time[2])) {
342 fprintf(stderr, "WARNING: Fixed delay dominating updates.\n\n");
343 delay = time[2]/frames[2];
344 rate = 0.0;
345 }
346
347 // There isn't any fixed delay, we are only restricted by pixel
348 // throughput
349 if (fabs(delay) < 0.001) {
350 delay = 0.0;
351 rate = time[2]/pixels[2];
352 }
353
354 // We can hit cache limits that causes performance to drop
355 // with increasing update size, screwing up our calculations
356 if ((pixels[2] / time[2]) < (pixels[0] / time[0] * 0.9)) {
357 fprintf(stderr, "WARNING: Unexpected behaviour. Measurement unreliable.\n\n");
358
359 // We can't determine the proportions between these, so divide the
360 // time spent evenly
361 delay = time[2] / 2.0 / frames[2];
362 rate = time[2] / 2.0 / pixels[2];
363 }
364
365 fprintf(stderr, "Rendering delay: %g ms/frame\n", delay * 1000.0);
366 if (rate == 0.0)
367 strcpy(s, "N/A pixels/s");
368 else
369 rfb::siPrefix(1.0 / rate, "pixels/s", s, sizeof(s));
Pierre Ossman38a1c702016-12-20 12:32:37 +0100370 fprintf(stderr, "Rendering rate: %s\n", s);
Pierre Ossman5b092762017-01-12 08:28:45 +0100371 fprintf(stderr, "Maximum FPS: %g fps @ 1920x1080\n",
372 1.0 / (delay + rate * 1920 * 1080));
Pierre Ossman38a1c702016-12-20 12:32:37 +0100373}
374
375int main(int argc, char** argv)
376{
377 TestWindow* win;
378
379 fprintf(stderr, "Full window update:\n\n");
380 win = new TestWindow();
381 dotest(win);
382 delete win;
383 fprintf(stderr, "\n");
384
385 fprintf(stderr, "Partial window update:\n\n");
386 win = new PartialTestWindow();
387 dotest(win);
388 delete win;
389 fprintf(stderr, "\n");
390
Pierre Ossman403ac272017-01-02 17:00:41 +0100391 fprintf(stderr, "Partial window update with overlay:\n\n");
392 win = new OverlayTestWindow();
393 dotest(win);
394 delete win;
395 fprintf(stderr, "\n");
396
Pierre Ossman38a1c702016-12-20 12:32:37 +0100397 return 0;
398}