blob: 5c37b0f74b747855bd7ba3d1c336b1ace4faca54 [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"
30#include "../vncviewer/FLTKPixelBuffer.h"
31
32#if defined(WIN32)
33#include "../vncviewer/Win32PixelBuffer.h"
34#elif defined(__APPLE__)
35#include "../vncviewer/OSXPixelBuffer.h"
36#else
37#include "../vncviewer/X11PixelBuffer.h"
38#endif
39
40#include "util.h"
41
42class TestWindow: public Fl_Window {
43public:
44 TestWindow();
45 ~TestWindow();
46
Pierre Ossman5b092762017-01-12 08:28:45 +010047 virtual void start(int width, int height);
48 virtual void stop();
49
Pierre Ossman38a1c702016-12-20 12:32:37 +010050 virtual void draw();
51
52protected:
53 virtual void flush();
54
55 void update();
56 virtual void changefb();
57
58 static void timer(void* data);
59
60public:
61 unsigned long long pixels, frames;
62 double time;
63
64protected:
65 PlatformPixelBuffer* fb;
66};
67
68class PartialTestWindow: public TestWindow {
69protected:
70 virtual void changefb();
71};
72
73TestWindow::TestWindow() :
Pierre Ossman5b092762017-01-12 08:28:45 +010074 Fl_Window(0, 0, "Framebuffer Performance Test"),
75 fb(NULL)
76{
77}
78
79TestWindow::~TestWindow()
80{
81 stop();
82}
83
84void TestWindow::start(int width, int height)
Pierre Ossman38a1c702016-12-20 12:32:37 +010085{
86 rdr::U32 pixel;
87
Pierre Ossman5b092762017-01-12 08:28:45 +010088 stop();
89
90 resize(x(), y(), width, height);
91
Pierre Ossman38a1c702016-12-20 12:32:37 +010092 pixels = 0;
93 frames = 0;
94 time = 0;
95
96 try {
97#if defined(WIN32)
98 fb = new Win32PixelBuffer(w(), h());
99#elif defined(__APPLE__)
100 fb = new OSXPixelBuffer(w(), h());
101#else
102 fb = new X11PixelBuffer(w(), h());
103#endif
104 } catch (rdr::Exception& e) {
105 fb = new FLTKPixelBuffer(w(), h());
106 }
107
108 pixel = 0;
109 fb->fillRect(fb->getRect(), &pixel);
Pierre Ossman5b092762017-01-12 08:28:45 +0100110
111 show();
Pierre Ossman38a1c702016-12-20 12:32:37 +0100112}
113
Pierre Ossman5b092762017-01-12 08:28:45 +0100114void TestWindow::stop()
Pierre Ossman38a1c702016-12-20 12:32:37 +0100115{
Pierre Ossman5b092762017-01-12 08:28:45 +0100116 hide();
117
Pierre Ossman38a1c702016-12-20 12:32:37 +0100118 delete fb;
Pierre Ossman5b092762017-01-12 08:28:45 +0100119 fb = NULL;
Pierre Ossman38a1c702016-12-20 12:32:37 +0100120
121 Fl::remove_idle(timer, this);
122}
123
124void TestWindow::draw()
125{
126 int X, Y, W, H;
127
128 // We cannot update the damage region from inside the draw function,
129 // so delegate this to an idle function
130 Fl::add_idle(timer, this);
131
132 // Check what actually needs updating
133 fl_clip_box(0, 0, w(), h(), X, Y, W, H);
134 if ((W == 0) || (H == 0))
135 return;
136
137 fb->draw(X, Y, X, Y, W, H);
138
139 pixels += W*H;
140 frames++;
141}
142
143void TestWindow::flush()
144{
145 startTimeCounter();
146 Fl_Window::flush();
147#if !defined(WIN32) && !defined(__APPLE__)
148 // Make sure we measure any work we queue up
149 XSync(fl_display, False);
150#endif
151 endTimeCounter();
152
153 time += getTimeCounter();
154}
155
156void TestWindow::update()
157{
158 rfb::Rect r;
159
160 startTimeCounter();
161
Pierre Ossman38a1c702016-12-20 12:32:37 +0100162 changefb();
163
164 r = fb->getDamage();
165 damage(FL_DAMAGE_USER1, r.tl.x, r.tl.y, r.width(), r.height());
166
167#if !defined(WIN32) && !defined(__APPLE__)
168 // Make sure we measure any work we queue up
169 XSync(fl_display, False);
170#endif
171
172 endTimeCounter();
173
174 time += getTimeCounter();
175}
176
177void TestWindow::changefb()
178{
179 rdr::U32 pixel;
180
181 pixel = rand();
182 fb->fillRect(fb->getRect(), &pixel);
183}
184
185void TestWindow::timer(void* data)
186{
187 TestWindow* self;
188
189 Fl::remove_idle(timer, data);
190
191 self = (TestWindow*)data;
192 self->update();
193}
194
195void PartialTestWindow::changefb()
196{
197 rfb::Rect r;
198 rdr::U32 pixel;
199
200 r = fb->getRect();
201 r.tl.x += w() / 4;
202 r.tl.y += h() / 4;
203 r.br.x -= w() / 4;
204 r.br.y -= h() / 4;
205
206 pixel = rand();
207 fb->fillRect(r, &pixel);
208}
209
Pierre Ossman5b092762017-01-12 08:28:45 +0100210static void dosubtest(TestWindow* win, int width, int height,
211 unsigned long long* pixels,
212 unsigned long long* frames,
213 double* time)
Pierre Ossman38a1c702016-12-20 12:32:37 +0100214{
215 struct timeval start;
Pierre Ossman38a1c702016-12-20 12:32:37 +0100216
Pierre Ossman5b092762017-01-12 08:28:45 +0100217 win->start(width, height);
Pierre Ossman38a1c702016-12-20 12:32:37 +0100218
219 gettimeofday(&start, NULL);
Pierre Ossman5b092762017-01-12 08:28:45 +0100220 while (rfb::msSince(&start) < 3000)
Pierre Ossman38a1c702016-12-20 12:32:37 +0100221 Fl::wait();
222
Pierre Ossman5b092762017-01-12 08:28:45 +0100223 win->stop();
224
225 *pixels = win->pixels;
226 *frames = win->frames;
227 *time = win->time;
228}
229
230static bool is_constant(double a, double b)
231{
232 return (fabs(a - b) / a) < 0.1;
233}
234
235static void dotest(TestWindow* win)
236{
237 unsigned long long pixels[3];
238 unsigned long long frames[3];
239 double time[3];
240
241 double delay, rate;
242 char s[1024];
243
244 // Run the test several times at different resolutions...
245 dosubtest(win, 800, 600, &pixels[0], &frames[0], &time[0]);
246 dosubtest(win, 1024, 768, &pixels[1], &frames[1], &time[1]);
247 dosubtest(win, 1280, 960, &pixels[2], &frames[2], &time[2]);
248
249 // ...in order to compute how much of the rendering time is static,
250 // and how much depends on the number of pixels
251 // (i.e. solve: time = delay * frames + rate * pixels)
252 delay = (((time[0] - (double)pixels[0] / pixels[1] * time[1]) /
253 (frames[0] - (double)pixels[0] / pixels[1] * frames[1])) +
254 ((time[1] - (double)pixels[1] / pixels[2] * time[2]) /
255 (frames[1] - (double)pixels[1] / pixels[2] * frames[2]))) / 2.0;
256 rate = (((time[0] - (double)frames[0] / frames[1] * time[1]) /
257 (pixels[0] - (double)frames[0] / frames[1] * pixels[1])) +
258 ((time[1] - (double)frames[1] / frames[2] * time[2]) /
259 (pixels[1] - (double)frames[1] / frames[2] * pixels[2]))) / 2.0;
260
261 // However, we have some corner cases:
262
263 // We are restricted by some delay, e.g. refresh rate
264 if (is_constant(frames[0]/time[0], frames[2]/time[2])) {
265 fprintf(stderr, "WARNING: Fixed delay dominating updates.\n\n");
266 delay = time[2]/frames[2];
267 rate = 0.0;
268 }
269
270 // There isn't any fixed delay, we are only restricted by pixel
271 // throughput
272 if (fabs(delay) < 0.001) {
273 delay = 0.0;
274 rate = time[2]/pixels[2];
275 }
276
277 // We can hit cache limits that causes performance to drop
278 // with increasing update size, screwing up our calculations
279 if ((pixels[2] / time[2]) < (pixels[0] / time[0] * 0.9)) {
280 fprintf(stderr, "WARNING: Unexpected behaviour. Measurement unreliable.\n\n");
281
282 // We can't determine the proportions between these, so divide the
283 // time spent evenly
284 delay = time[2] / 2.0 / frames[2];
285 rate = time[2] / 2.0 / pixels[2];
286 }
287
288 fprintf(stderr, "Rendering delay: %g ms/frame\n", delay * 1000.0);
289 if (rate == 0.0)
290 strcpy(s, "N/A pixels/s");
291 else
292 rfb::siPrefix(1.0 / rate, "pixels/s", s, sizeof(s));
Pierre Ossman38a1c702016-12-20 12:32:37 +0100293 fprintf(stderr, "Rendering rate: %s\n", s);
Pierre Ossman5b092762017-01-12 08:28:45 +0100294 fprintf(stderr, "Maximum FPS: %g fps @ 1920x1080\n",
295 1.0 / (delay + rate * 1920 * 1080));
Pierre Ossman38a1c702016-12-20 12:32:37 +0100296}
297
298int main(int argc, char** argv)
299{
300 TestWindow* win;
301
302 fprintf(stderr, "Full window update:\n\n");
303 win = new TestWindow();
304 dotest(win);
305 delete win;
306 fprintf(stderr, "\n");
307
308 fprintf(stderr, "Partial window update:\n\n");
309 win = new PartialTestWindow();
310 dotest(win);
311 delete win;
312 fprintf(stderr, "\n");
313
314 return 0;
315}