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

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