You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

fbperf.cxx 8.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. /* 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. #ifdef HAVE_CONFIG_H
  19. #include <config.h>
  20. #endif
  21. #include <math.h>
  22. #include <sys/time.h>
  23. #include <FL/Fl.H>
  24. #include <FL/Fl_Window.H>
  25. #include <FL/fl_draw.H>
  26. #include <rdr/Exception.h>
  27. #include <rfb/util.h>
  28. #include "../vncviewer/PlatformPixelBuffer.h"
  29. #include "util.h"
  30. class TestWindow: public Fl_Window {
  31. public:
  32. TestWindow();
  33. ~TestWindow();
  34. virtual void start(int width, int height);
  35. virtual void stop();
  36. virtual void draw();
  37. protected:
  38. virtual void flush();
  39. void update();
  40. virtual void changefb();
  41. static void timer(void* data);
  42. public:
  43. unsigned long long pixels, frames;
  44. double time;
  45. protected:
  46. PlatformPixelBuffer* fb;
  47. };
  48. class PartialTestWindow: public TestWindow {
  49. protected:
  50. virtual void changefb();
  51. };
  52. class OverlayTestWindow: public PartialTestWindow {
  53. public:
  54. OverlayTestWindow();
  55. virtual void start(int width, int height);
  56. virtual void stop();
  57. virtual void draw();
  58. protected:
  59. Surface* overlay;
  60. Surface* offscreen;
  61. };
  62. TestWindow::TestWindow() :
  63. Fl_Window(0, 0, "Framebuffer Performance Test"),
  64. fb(NULL)
  65. {
  66. }
  67. TestWindow::~TestWindow()
  68. {
  69. stop();
  70. }
  71. void TestWindow::start(int width, int height)
  72. {
  73. rdr::U32 pixel;
  74. stop();
  75. resize(x(), y(), width, height);
  76. pixels = 0;
  77. frames = 0;
  78. time = 0;
  79. fb = new PlatformPixelBuffer(w(), h());
  80. pixel = 0;
  81. fb->fillRect(fb->getRect(), &pixel);
  82. show();
  83. }
  84. void TestWindow::stop()
  85. {
  86. hide();
  87. delete fb;
  88. fb = NULL;
  89. Fl::remove_idle(timer, this);
  90. }
  91. void TestWindow::draw()
  92. {
  93. int X, Y, W, H;
  94. // We cannot update the damage region from inside the draw function,
  95. // so delegate this to an idle function
  96. Fl::add_idle(timer, this);
  97. // Check what actually needs updating
  98. fl_clip_box(0, 0, w(), h(), X, Y, W, H);
  99. if ((W == 0) || (H == 0))
  100. return;
  101. fb->draw(X, Y, X, Y, W, H);
  102. pixels += W*H;
  103. frames++;
  104. }
  105. void TestWindow::flush()
  106. {
  107. startTimeCounter();
  108. Fl_Window::flush();
  109. #if !defined(WIN32) && !defined(__APPLE__)
  110. // Make sure we measure any work we queue up
  111. XSync(fl_display, False);
  112. #endif
  113. endTimeCounter();
  114. time += getTimeCounter();
  115. }
  116. void TestWindow::update()
  117. {
  118. rfb::Rect r;
  119. startTimeCounter();
  120. changefb();
  121. r = fb->getDamage();
  122. damage(FL_DAMAGE_USER1, r.tl.x, r.tl.y, r.width(), r.height());
  123. #if !defined(WIN32) && !defined(__APPLE__)
  124. // Make sure we measure any work we queue up
  125. XSync(fl_display, False);
  126. #endif
  127. endTimeCounter();
  128. time += getTimeCounter();
  129. }
  130. void TestWindow::changefb()
  131. {
  132. rdr::U32 pixel;
  133. pixel = rand();
  134. fb->fillRect(fb->getRect(), &pixel);
  135. }
  136. void TestWindow::timer(void* data)
  137. {
  138. TestWindow* self;
  139. Fl::remove_idle(timer, data);
  140. self = (TestWindow*)data;
  141. self->update();
  142. }
  143. void PartialTestWindow::changefb()
  144. {
  145. rfb::Rect r;
  146. rdr::U32 pixel;
  147. r = fb->getRect();
  148. r.tl.x += w() / 4;
  149. r.tl.y += h() / 4;
  150. r.br.x -= w() / 4;
  151. r.br.y -= h() / 4;
  152. pixel = rand();
  153. fb->fillRect(r, &pixel);
  154. }
  155. OverlayTestWindow::OverlayTestWindow() :
  156. overlay(NULL), offscreen(NULL)
  157. {
  158. }
  159. void OverlayTestWindow::start(int width, int height)
  160. {
  161. PartialTestWindow::start(width, height);
  162. overlay = new Surface(400, 200);
  163. overlay->clear(0xff, 0x80, 0x00, 0xcc);
  164. // X11 needs an off screen buffer for compositing to avoid flicker,
  165. // and alpha blending doesn't work for windows on Win32
  166. #if !defined(__APPLE__)
  167. offscreen = new Surface(w(), h());
  168. #else
  169. offscreen = NULL;
  170. #endif
  171. }
  172. void OverlayTestWindow::stop()
  173. {
  174. PartialTestWindow::stop();
  175. delete offscreen;
  176. offscreen = NULL;
  177. delete overlay;
  178. overlay = NULL;
  179. }
  180. void OverlayTestWindow::draw()
  181. {
  182. int ox, oy, ow, oh;
  183. int X, Y, W, H;
  184. // We cannot update the damage region from inside the draw function,
  185. // so delegate this to an idle function
  186. Fl::add_idle(timer, this);
  187. // Check what actually needs updating
  188. fl_clip_box(0, 0, w(), h(), X, Y, W, H);
  189. if ((W == 0) || (H == 0))
  190. return;
  191. // We might get a redraw before we are fully ready
  192. if (!overlay)
  193. return;
  194. // Simplify the clip region to a simple rectangle in order to
  195. // properly draw all the layers even if they only partially overlap
  196. fl_push_no_clip();
  197. fl_push_clip(X, Y, W, H);
  198. if (offscreen)
  199. fb->draw(offscreen, X, Y, X, Y, W, H);
  200. else
  201. fb->draw(X, Y, X, Y, W, H);
  202. pixels += W*H;
  203. frames++;
  204. ox = (w() - overlay->width()) / 2;
  205. oy = h() / 4 - overlay->height() / 2;
  206. ow = overlay->width();
  207. oh = overlay->height();
  208. fl_clip_box(ox, oy, ow, oh, X, Y, W, H);
  209. if ((W != 0) && (H != 0)) {
  210. if (offscreen)
  211. overlay->blend(offscreen, X - ox, Y - oy, X, Y, W, H);
  212. else
  213. overlay->blend(X - ox, Y - oy, X, Y, W, H);
  214. }
  215. fl_pop_clip();
  216. fl_pop_clip();
  217. if (offscreen) {
  218. fl_clip_box(0, 0, w(), h(), X, Y, W, H);
  219. offscreen->draw(X, Y, X, Y, W, H);
  220. }
  221. }
  222. static void dosubtest(TestWindow* win, int width, int height,
  223. unsigned long long* pixels,
  224. unsigned long long* frames,
  225. double* time)
  226. {
  227. struct timeval start;
  228. win->start(width, height);
  229. gettimeofday(&start, NULL);
  230. while (rfb::msSince(&start) < 3000)
  231. Fl::wait();
  232. win->stop();
  233. *pixels = win->pixels;
  234. *frames = win->frames;
  235. *time = win->time;
  236. }
  237. static bool is_constant(double a, double b)
  238. {
  239. return (fabs(a - b) / a) < 0.1;
  240. }
  241. static void dotest(TestWindow* win)
  242. {
  243. unsigned long long pixels[3];
  244. unsigned long long frames[3];
  245. double time[3];
  246. double delay, rate;
  247. char s[1024];
  248. // Run the test several times at different resolutions...
  249. dosubtest(win, 800, 600, &pixels[0], &frames[0], &time[0]);
  250. dosubtest(win, 1024, 768, &pixels[1], &frames[1], &time[1]);
  251. dosubtest(win, 1280, 960, &pixels[2], &frames[2], &time[2]);
  252. // ...in order to compute how much of the rendering time is static,
  253. // and how much depends on the number of pixels
  254. // (i.e. solve: time = delay * frames + rate * pixels)
  255. delay = (((time[0] - (double)pixels[0] / pixels[1] * time[1]) /
  256. (frames[0] - (double)pixels[0] / pixels[1] * frames[1])) +
  257. ((time[1] - (double)pixels[1] / pixels[2] * time[2]) /
  258. (frames[1] - (double)pixels[1] / pixels[2] * frames[2]))) / 2.0;
  259. rate = (((time[0] - (double)frames[0] / frames[1] * time[1]) /
  260. (pixels[0] - (double)frames[0] / frames[1] * pixels[1])) +
  261. ((time[1] - (double)frames[1] / frames[2] * time[2]) /
  262. (pixels[1] - (double)frames[1] / frames[2] * pixels[2]))) / 2.0;
  263. // However, we have some corner cases:
  264. // We are restricted by some delay, e.g. refresh rate
  265. if (is_constant(frames[0]/time[0], frames[2]/time[2])) {
  266. fprintf(stderr, "WARNING: Fixed delay dominating updates.\n\n");
  267. delay = time[2]/frames[2];
  268. rate = 0.0;
  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. // We can hit cache limits that causes performance to drop
  277. // with increasing update size, screwing up our calculations
  278. if ((pixels[2] / time[2]) < (pixels[0] / time[0] * 0.9)) {
  279. fprintf(stderr, "WARNING: Unexpected behaviour. Measurement unreliable.\n\n");
  280. // We can't determine the proportions between these, so divide the
  281. // time spent evenly
  282. delay = time[2] / 2.0 / frames[2];
  283. rate = time[2] / 2.0 / pixels[2];
  284. }
  285. fprintf(stderr, "Rendering delay: %g ms/frame\n", delay * 1000.0);
  286. if (rate == 0.0)
  287. strcpy(s, "N/A pixels/s");
  288. else
  289. rfb::siPrefix(1.0 / rate, "pixels/s", s, sizeof(s));
  290. fprintf(stderr, "Rendering rate: %s\n", s);
  291. fprintf(stderr, "Maximum FPS: %g fps @ 1920x1080\n",
  292. 1.0 / (delay + rate * 1920 * 1080));
  293. }
  294. int main(int argc, char** argv)
  295. {
  296. TestWindow* win;
  297. fprintf(stderr, "Full window update:\n\n");
  298. win = new TestWindow();
  299. dotest(win);
  300. delete win;
  301. fprintf(stderr, "\n");
  302. fprintf(stderr, "Partial window update:\n\n");
  303. win = new PartialTestWindow();
  304. dotest(win);
  305. delete win;
  306. fprintf(stderr, "\n");
  307. fprintf(stderr, "Partial window update with overlay:\n\n");
  308. win = new OverlayTestWindow();
  309. dotest(win);
  310. delete win;
  311. fprintf(stderr, "\n");
  312. return 0;
  313. }