blob: a19ee47901920103b00f05c77d3935201dd994ac [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
Pierre Ossmande6a5802017-01-02 20:07:10 +0100217 // X11 needs an off screen buffer for compositing to avoid flicker,
218 // and alpha blending doesn't work for windows on Win32
219#if !defined(__APPLE__)
Pierre Ossman3d74d882017-01-02 19:49:52 +0100220 offscreen = new Surface(w(), h());
221#else
222 offscreen = NULL;
223#endif
Pierre Ossman403ac272017-01-02 17:00:41 +0100224}
225
226void OverlayTestWindow::stop()
227{
228 PartialTestWindow::stop();
229
Pierre Ossman3d74d882017-01-02 19:49:52 +0100230 delete offscreen;
231 offscreen = NULL;
Pierre Ossman403ac272017-01-02 17:00:41 +0100232 delete overlay;
233 overlay = NULL;
234}
235
236void OverlayTestWindow::draw()
237{
238 int ox, oy, ow, oh;
239 int X, Y, W, H;
240
Pierre Ossman3d74d882017-01-02 19:49:52 +0100241 // We cannot update the damage region from inside the draw function,
242 // so delegate this to an idle function
243 Fl::add_idle(timer, this);
244
Pierre Ossman403ac272017-01-02 17:00:41 +0100245 // Check what actually needs updating
246 fl_clip_box(0, 0, w(), h(), X, Y, W, H);
247 if ((W == 0) || (H == 0))
248 return;
249
Pierre Ossman403ac272017-01-02 17:00:41 +0100250 // We might get a redraw before we are fully ready
251 if (!overlay)
252 return;
253
254 // Simplify the clip region to a simple rectangle in order to
255 // properly draw all the layers even if they only partially overlap
256 fl_push_no_clip();
257 fl_push_clip(X, Y, W, H);
258
Pierre Ossman3d74d882017-01-02 19:49:52 +0100259 if (offscreen)
260 fb->draw(offscreen, X, Y, X, Y, W, H);
261 else
262 fb->draw(X, Y, X, Y, W, H);
263
264 pixels += W*H;
265 frames++;
266
Pierre Ossman403ac272017-01-02 17:00:41 +0100267 ox = (w() - overlay->width()) / 2;
268 oy = h() / 4 - overlay->height() / 2;
269 ow = overlay->width();
270 oh = overlay->height();
271 fl_clip_box(ox, oy, ow, oh, X, Y, W, H);
Pierre Ossman3d74d882017-01-02 19:49:52 +0100272 if ((W != 0) && (H != 0)) {
273 if (offscreen)
Pierre Ossmande6a5802017-01-02 20:07:10 +0100274 overlay->blend(offscreen, X - ox, Y - oy, X, Y, W, H);
Pierre Ossman3d74d882017-01-02 19:49:52 +0100275 else
Pierre Ossmande6a5802017-01-02 20:07:10 +0100276 overlay->blend(X - ox, Y - oy, X, Y, W, H);
Pierre Ossman3d74d882017-01-02 19:49:52 +0100277 }
Pierre Ossman403ac272017-01-02 17:00:41 +0100278
279 fl_pop_clip();
280 fl_pop_clip();
Pierre Ossman3d74d882017-01-02 19:49:52 +0100281
282 if (offscreen) {
283 fl_clip_box(0, 0, w(), h(), X, Y, W, H);
284 offscreen->draw(X, Y, X, Y, W, H);
285 }
Pierre Ossman403ac272017-01-02 17:00:41 +0100286}
287
Pierre Ossman5b092762017-01-12 08:28:45 +0100288static void dosubtest(TestWindow* win, int width, int height,
289 unsigned long long* pixels,
290 unsigned long long* frames,
291 double* time)
Pierre Ossman38a1c702016-12-20 12:32:37 +0100292{
293 struct timeval start;
Pierre Ossman38a1c702016-12-20 12:32:37 +0100294
Pierre Ossman5b092762017-01-12 08:28:45 +0100295 win->start(width, height);
Pierre Ossman38a1c702016-12-20 12:32:37 +0100296
297 gettimeofday(&start, NULL);
Pierre Ossman5b092762017-01-12 08:28:45 +0100298 while (rfb::msSince(&start) < 3000)
Pierre Ossman38a1c702016-12-20 12:32:37 +0100299 Fl::wait();
300
Pierre Ossman5b092762017-01-12 08:28:45 +0100301 win->stop();
302
303 *pixels = win->pixels;
304 *frames = win->frames;
305 *time = win->time;
306}
307
308static bool is_constant(double a, double b)
309{
310 return (fabs(a - b) / a) < 0.1;
311}
312
313static void dotest(TestWindow* win)
314{
315 unsigned long long pixels[3];
316 unsigned long long frames[3];
317 double time[3];
318
319 double delay, rate;
320 char s[1024];
321
322 // Run the test several times at different resolutions...
323 dosubtest(win, 800, 600, &pixels[0], &frames[0], &time[0]);
324 dosubtest(win, 1024, 768, &pixels[1], &frames[1], &time[1]);
325 dosubtest(win, 1280, 960, &pixels[2], &frames[2], &time[2]);
326
327 // ...in order to compute how much of the rendering time is static,
328 // and how much depends on the number of pixels
329 // (i.e. solve: time = delay * frames + rate * pixels)
330 delay = (((time[0] - (double)pixels[0] / pixels[1] * time[1]) /
331 (frames[0] - (double)pixels[0] / pixels[1] * frames[1])) +
332 ((time[1] - (double)pixels[1] / pixels[2] * time[2]) /
333 (frames[1] - (double)pixels[1] / pixels[2] * frames[2]))) / 2.0;
334 rate = (((time[0] - (double)frames[0] / frames[1] * time[1]) /
335 (pixels[0] - (double)frames[0] / frames[1] * pixels[1])) +
336 ((time[1] - (double)frames[1] / frames[2] * time[2]) /
337 (pixels[1] - (double)frames[1] / frames[2] * pixels[2]))) / 2.0;
338
339 // However, we have some corner cases:
340
341 // We are restricted by some delay, e.g. refresh rate
342 if (is_constant(frames[0]/time[0], frames[2]/time[2])) {
343 fprintf(stderr, "WARNING: Fixed delay dominating updates.\n\n");
344 delay = time[2]/frames[2];
345 rate = 0.0;
346 }
347
348 // There isn't any fixed delay, we are only restricted by pixel
349 // throughput
350 if (fabs(delay) < 0.001) {
351 delay = 0.0;
352 rate = time[2]/pixels[2];
353 }
354
355 // We can hit cache limits that causes performance to drop
356 // with increasing update size, screwing up our calculations
357 if ((pixels[2] / time[2]) < (pixels[0] / time[0] * 0.9)) {
358 fprintf(stderr, "WARNING: Unexpected behaviour. Measurement unreliable.\n\n");
359
360 // We can't determine the proportions between these, so divide the
361 // time spent evenly
362 delay = time[2] / 2.0 / frames[2];
363 rate = time[2] / 2.0 / pixels[2];
364 }
365
366 fprintf(stderr, "Rendering delay: %g ms/frame\n", delay * 1000.0);
367 if (rate == 0.0)
368 strcpy(s, "N/A pixels/s");
369 else
370 rfb::siPrefix(1.0 / rate, "pixels/s", s, sizeof(s));
Pierre Ossman38a1c702016-12-20 12:32:37 +0100371 fprintf(stderr, "Rendering rate: %s\n", s);
Pierre Ossman5b092762017-01-12 08:28:45 +0100372 fprintf(stderr, "Maximum FPS: %g fps @ 1920x1080\n",
373 1.0 / (delay + rate * 1920 * 1080));
Pierre Ossman38a1c702016-12-20 12:32:37 +0100374}
375
376int main(int argc, char** argv)
377{
378 TestWindow* win;
379
380 fprintf(stderr, "Full window update:\n\n");
381 win = new TestWindow();
382 dotest(win);
383 delete win;
384 fprintf(stderr, "\n");
385
386 fprintf(stderr, "Partial window update:\n\n");
387 win = new PartialTestWindow();
388 dotest(win);
389 delete win;
390 fprintf(stderr, "\n");
391
Pierre Ossman403ac272017-01-02 17:00:41 +0100392 fprintf(stderr, "Partial window update with overlay:\n\n");
393 win = new OverlayTestWindow();
394 dotest(win);
395 delete win;
396 fprintf(stderr, "\n");
397
Pierre Ossman38a1c702016-12-20 12:32:37 +0100398 return 0;
399}