blob: 8c71d966942d9361a24cf0cbd3b14928aa7ae1dc [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;
75};
76
Pierre Ossman38a1c702016-12-20 12:32:37 +010077TestWindow::TestWindow() :
Pierre Ossman5b092762017-01-12 08:28:45 +010078 Fl_Window(0, 0, "Framebuffer Performance Test"),
79 fb(NULL)
80{
81}
82
83TestWindow::~TestWindow()
84{
85 stop();
86}
87
88void TestWindow::start(int width, int height)
Pierre Ossman38a1c702016-12-20 12:32:37 +010089{
90 rdr::U32 pixel;
91
Pierre Ossman5b092762017-01-12 08:28:45 +010092 stop();
93
94 resize(x(), y(), width, height);
95
Pierre Ossman38a1c702016-12-20 12:32:37 +010096 pixels = 0;
97 frames = 0;
98 time = 0;
99
Pierre Ossman403ac272017-01-02 17:00:41 +0100100 fb = new PlatformPixelBuffer(w(), h());
Pierre Ossman38a1c702016-12-20 12:32:37 +0100101
102 pixel = 0;
103 fb->fillRect(fb->getRect(), &pixel);
Pierre Ossman5b092762017-01-12 08:28:45 +0100104
105 show();
Pierre Ossman38a1c702016-12-20 12:32:37 +0100106}
107
Pierre Ossman5b092762017-01-12 08:28:45 +0100108void TestWindow::stop()
Pierre Ossman38a1c702016-12-20 12:32:37 +0100109{
Pierre Ossman5b092762017-01-12 08:28:45 +0100110 hide();
111
Pierre Ossman38a1c702016-12-20 12:32:37 +0100112 delete fb;
Pierre Ossman5b092762017-01-12 08:28:45 +0100113 fb = NULL;
Pierre Ossman38a1c702016-12-20 12:32:37 +0100114
115 Fl::remove_idle(timer, this);
116}
117
118void TestWindow::draw()
119{
120 int X, Y, W, H;
121
122 // We cannot update the damage region from inside the draw function,
123 // so delegate this to an idle function
124 Fl::add_idle(timer, this);
125
126 // Check what actually needs updating
127 fl_clip_box(0, 0, w(), h(), X, Y, W, H);
128 if ((W == 0) || (H == 0))
129 return;
130
131 fb->draw(X, Y, X, Y, W, H);
132
133 pixels += W*H;
134 frames++;
135}
136
137void TestWindow::flush()
138{
139 startTimeCounter();
140 Fl_Window::flush();
141#if !defined(WIN32) && !defined(__APPLE__)
142 // Make sure we measure any work we queue up
143 XSync(fl_display, False);
144#endif
145 endTimeCounter();
146
147 time += getTimeCounter();
148}
149
150void TestWindow::update()
151{
152 rfb::Rect r;
153
154 startTimeCounter();
155
Pierre Ossman38a1c702016-12-20 12:32:37 +0100156 changefb();
157
158 r = fb->getDamage();
159 damage(FL_DAMAGE_USER1, r.tl.x, r.tl.y, r.width(), r.height());
160
161#if !defined(WIN32) && !defined(__APPLE__)
162 // Make sure we measure any work we queue up
163 XSync(fl_display, False);
164#endif
165
166 endTimeCounter();
167
168 time += getTimeCounter();
169}
170
171void TestWindow::changefb()
172{
173 rdr::U32 pixel;
174
175 pixel = rand();
176 fb->fillRect(fb->getRect(), &pixel);
177}
178
179void TestWindow::timer(void* data)
180{
181 TestWindow* self;
182
183 Fl::remove_idle(timer, data);
184
185 self = (TestWindow*)data;
186 self->update();
187}
188
189void PartialTestWindow::changefb()
190{
191 rfb::Rect r;
192 rdr::U32 pixel;
193
194 r = fb->getRect();
195 r.tl.x += w() / 4;
196 r.tl.y += h() / 4;
197 r.br.x -= w() / 4;
198 r.br.y -= h() / 4;
199
200 pixel = rand();
201 fb->fillRect(r, &pixel);
202}
203
Pierre Ossman403ac272017-01-02 17:00:41 +0100204OverlayTestWindow::OverlayTestWindow() :
205 overlay(NULL)
206{
207}
208
209void OverlayTestWindow::start(int width, int height)
210{
211 PartialTestWindow::start(width, height);
212
213 overlay = new Surface(400, 200);
214 overlay->clear(0xff, 0x80, 0x00, 0xcc);
215}
216
217void OverlayTestWindow::stop()
218{
219 PartialTestWindow::stop();
220
221 delete overlay;
222 overlay = NULL;
223}
224
225void OverlayTestWindow::draw()
226{
227 int ox, oy, ow, oh;
228 int X, Y, W, H;
229
230 // Check what actually needs updating
231 fl_clip_box(0, 0, w(), h(), X, Y, W, H);
232 if ((W == 0) || (H == 0))
233 return;
234
235 PartialTestWindow::draw();
236
237 // We might get a redraw before we are fully ready
238 if (!overlay)
239 return;
240
241 // Simplify the clip region to a simple rectangle in order to
242 // properly draw all the layers even if they only partially overlap
243 fl_push_no_clip();
244 fl_push_clip(X, Y, W, H);
245
246 ox = (w() - overlay->width()) / 2;
247 oy = h() / 4 - overlay->height() / 2;
248 ow = overlay->width();
249 oh = overlay->height();
250 fl_clip_box(ox, oy, ow, oh, X, Y, W, H);
251 if ((W != 0) && (H != 0))
252 overlay->draw(X - ox, Y - oy, X, Y, W, H);
253
254 fl_pop_clip();
255 fl_pop_clip();
256}
257
Pierre Ossman5b092762017-01-12 08:28:45 +0100258static void dosubtest(TestWindow* win, int width, int height,
259 unsigned long long* pixels,
260 unsigned long long* frames,
261 double* time)
Pierre Ossman38a1c702016-12-20 12:32:37 +0100262{
263 struct timeval start;
Pierre Ossman38a1c702016-12-20 12:32:37 +0100264
Pierre Ossman5b092762017-01-12 08:28:45 +0100265 win->start(width, height);
Pierre Ossman38a1c702016-12-20 12:32:37 +0100266
267 gettimeofday(&start, NULL);
Pierre Ossman5b092762017-01-12 08:28:45 +0100268 while (rfb::msSince(&start) < 3000)
Pierre Ossman38a1c702016-12-20 12:32:37 +0100269 Fl::wait();
270
Pierre Ossman5b092762017-01-12 08:28:45 +0100271 win->stop();
272
273 *pixels = win->pixels;
274 *frames = win->frames;
275 *time = win->time;
276}
277
278static bool is_constant(double a, double b)
279{
280 return (fabs(a - b) / a) < 0.1;
281}
282
283static void dotest(TestWindow* win)
284{
285 unsigned long long pixels[3];
286 unsigned long long frames[3];
287 double time[3];
288
289 double delay, rate;
290 char s[1024];
291
292 // Run the test several times at different resolutions...
293 dosubtest(win, 800, 600, &pixels[0], &frames[0], &time[0]);
294 dosubtest(win, 1024, 768, &pixels[1], &frames[1], &time[1]);
295 dosubtest(win, 1280, 960, &pixels[2], &frames[2], &time[2]);
296
297 // ...in order to compute how much of the rendering time is static,
298 // and how much depends on the number of pixels
299 // (i.e. solve: time = delay * frames + rate * pixels)
300 delay = (((time[0] - (double)pixels[0] / pixels[1] * time[1]) /
301 (frames[0] - (double)pixels[0] / pixels[1] * frames[1])) +
302 ((time[1] - (double)pixels[1] / pixels[2] * time[2]) /
303 (frames[1] - (double)pixels[1] / pixels[2] * frames[2]))) / 2.0;
304 rate = (((time[0] - (double)frames[0] / frames[1] * time[1]) /
305 (pixels[0] - (double)frames[0] / frames[1] * pixels[1])) +
306 ((time[1] - (double)frames[1] / frames[2] * time[2]) /
307 (pixels[1] - (double)frames[1] / frames[2] * pixels[2]))) / 2.0;
308
309 // However, we have some corner cases:
310
311 // We are restricted by some delay, e.g. refresh rate
312 if (is_constant(frames[0]/time[0], frames[2]/time[2])) {
313 fprintf(stderr, "WARNING: Fixed delay dominating updates.\n\n");
314 delay = time[2]/frames[2];
315 rate = 0.0;
316 }
317
318 // There isn't any fixed delay, we are only restricted by pixel
319 // throughput
320 if (fabs(delay) < 0.001) {
321 delay = 0.0;
322 rate = time[2]/pixels[2];
323 }
324
325 // We can hit cache limits that causes performance to drop
326 // with increasing update size, screwing up our calculations
327 if ((pixels[2] / time[2]) < (pixels[0] / time[0] * 0.9)) {
328 fprintf(stderr, "WARNING: Unexpected behaviour. Measurement unreliable.\n\n");
329
330 // We can't determine the proportions between these, so divide the
331 // time spent evenly
332 delay = time[2] / 2.0 / frames[2];
333 rate = time[2] / 2.0 / pixels[2];
334 }
335
336 fprintf(stderr, "Rendering delay: %g ms/frame\n", delay * 1000.0);
337 if (rate == 0.0)
338 strcpy(s, "N/A pixels/s");
339 else
340 rfb::siPrefix(1.0 / rate, "pixels/s", s, sizeof(s));
Pierre Ossman38a1c702016-12-20 12:32:37 +0100341 fprintf(stderr, "Rendering rate: %s\n", s);
Pierre Ossman5b092762017-01-12 08:28:45 +0100342 fprintf(stderr, "Maximum FPS: %g fps @ 1920x1080\n",
343 1.0 / (delay + rate * 1920 * 1080));
Pierre Ossman38a1c702016-12-20 12:32:37 +0100344}
345
346int main(int argc, char** argv)
347{
348 TestWindow* win;
349
350 fprintf(stderr, "Full window update:\n\n");
351 win = new TestWindow();
352 dotest(win);
353 delete win;
354 fprintf(stderr, "\n");
355
356 fprintf(stderr, "Partial window update:\n\n");
357 win = new PartialTestWindow();
358 dotest(win);
359 delete win;
360 fprintf(stderr, "\n");
361
Pierre Ossman403ac272017-01-02 17:00:41 +0100362 fprintf(stderr, "Partial window update with overlay:\n\n");
363 win = new OverlayTestWindow();
364 dotest(win);
365 delete win;
366 fprintf(stderr, "\n");
367
Pierre Ossman38a1c702016-12-20 12:32:37 +0100368 return 0;
369}