summaryrefslogtreecommitdiffstats
path: root/tests/perf/fbperf.cxx
diff options
context:
space:
mode:
authorPierre Ossman <ossman@cendio.se>2019-09-09 12:48:25 +0200
committerPierre Ossman <ossman@cendio.se>2019-09-25 15:50:07 +0200
commit94de4dd0c46483477706002e128a61f74278c0f6 (patch)
tree7e51c88049452f0a46faa54177ffd96dc2903efb /tests/perf/fbperf.cxx
parentd06906db59a6aa6434a5dd602b8d3c987d291ca8 (diff)
downloadtigervnc-94de4dd0c46483477706002e128a61f74278c0f6.tar.gz
tigervnc-94de4dd0c46483477706002e128a61f74278c0f6.zip
Split test programs to benchmarking and unit tests
They have very different purpose, so make things easier to work with by having multiple directories.
Diffstat (limited to 'tests/perf/fbperf.cxx')
-rw-r--r--tests/perf/fbperf.cxx399
1 files changed, 399 insertions, 0 deletions
diff --git a/tests/perf/fbperf.cxx b/tests/perf/fbperf.cxx
new file mode 100644
index 00000000..a19ee479
--- /dev/null
+++ b/tests/perf/fbperf.cxx
@@ -0,0 +1,399 @@
+/* Copyright 2016 Pierre Ossman <ossman@cendio.se> for Cendio AB
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <math.h>
+#include <sys/time.h>
+
+#include <FL/Fl.H>
+#include <FL/Fl_Window.H>
+#include <FL/fl_draw.H>
+
+#include <rdr/Exception.h>
+#include <rfb/util.h>
+
+#include "../vncviewer/PlatformPixelBuffer.h"
+
+#include "util.h"
+
+class TestWindow: public Fl_Window {
+public:
+ TestWindow();
+ ~TestWindow();
+
+ virtual void start(int width, int height);
+ virtual void stop();
+
+ virtual void draw();
+
+protected:
+ virtual void flush();
+
+ void update();
+ virtual void changefb();
+
+ static void timer(void* data);
+
+public:
+ unsigned long long pixels, frames;
+ double time;
+
+protected:
+ PlatformPixelBuffer* fb;
+};
+
+class PartialTestWindow: public TestWindow {
+protected:
+ virtual void changefb();
+};
+
+class OverlayTestWindow: public PartialTestWindow {
+public:
+ OverlayTestWindow();
+
+ virtual void start(int width, int height);
+ virtual void stop();
+
+ virtual void draw();
+
+protected:
+ Surface* overlay;
+ Surface* offscreen;
+};
+
+TestWindow::TestWindow() :
+ Fl_Window(0, 0, "Framebuffer Performance Test"),
+ fb(NULL)
+{
+}
+
+TestWindow::~TestWindow()
+{
+ stop();
+}
+
+void TestWindow::start(int width, int height)
+{
+ rdr::U32 pixel;
+
+ stop();
+
+ resize(x(), y(), width, height);
+
+ pixels = 0;
+ frames = 0;
+ time = 0;
+
+ fb = new PlatformPixelBuffer(w(), h());
+
+ pixel = 0;
+ fb->fillRect(fb->getRect(), &pixel);
+
+ show();
+}
+
+void TestWindow::stop()
+{
+ hide();
+
+ delete fb;
+ fb = NULL;
+
+ Fl::remove_idle(timer, this);
+}
+
+void TestWindow::draw()
+{
+ int X, Y, W, H;
+
+ // We cannot update the damage region from inside the draw function,
+ // so delegate this to an idle function
+ Fl::add_idle(timer, this);
+
+ // Check what actually needs updating
+ fl_clip_box(0, 0, w(), h(), X, Y, W, H);
+ if ((W == 0) || (H == 0))
+ return;
+
+ fb->draw(X, Y, X, Y, W, H);
+
+ pixels += W*H;
+ frames++;
+}
+
+void TestWindow::flush()
+{
+ startTimeCounter();
+ Fl_Window::flush();
+#if !defined(WIN32) && !defined(__APPLE__)
+ // Make sure we measure any work we queue up
+ XSync(fl_display, False);
+#endif
+ endTimeCounter();
+
+ time += getTimeCounter();
+}
+
+void TestWindow::update()
+{
+ rfb::Rect r;
+
+ startTimeCounter();
+
+ changefb();
+
+ r = fb->getDamage();
+ damage(FL_DAMAGE_USER1, r.tl.x, r.tl.y, r.width(), r.height());
+
+#if !defined(WIN32) && !defined(__APPLE__)
+ // Make sure we measure any work we queue up
+ XSync(fl_display, False);
+#endif
+
+ endTimeCounter();
+
+ time += getTimeCounter();
+}
+
+void TestWindow::changefb()
+{
+ rdr::U32 pixel;
+
+ pixel = rand();
+ fb->fillRect(fb->getRect(), &pixel);
+}
+
+void TestWindow::timer(void* data)
+{
+ TestWindow* self;
+
+ Fl::remove_idle(timer, data);
+
+ self = (TestWindow*)data;
+ self->update();
+}
+
+void PartialTestWindow::changefb()
+{
+ rfb::Rect r;
+ rdr::U32 pixel;
+
+ r = fb->getRect();
+ r.tl.x += w() / 4;
+ r.tl.y += h() / 4;
+ r.br.x -= w() / 4;
+ r.br.y -= h() / 4;
+
+ pixel = rand();
+ fb->fillRect(r, &pixel);
+}
+
+OverlayTestWindow::OverlayTestWindow() :
+ overlay(NULL), offscreen(NULL)
+{
+}
+
+void OverlayTestWindow::start(int width, int height)
+{
+ PartialTestWindow::start(width, height);
+
+ overlay = new Surface(400, 200);
+ overlay->clear(0xff, 0x80, 0x00, 0xcc);
+
+ // X11 needs an off screen buffer for compositing to avoid flicker,
+ // and alpha blending doesn't work for windows on Win32
+#if !defined(__APPLE__)
+ offscreen = new Surface(w(), h());
+#else
+ offscreen = NULL;
+#endif
+}
+
+void OverlayTestWindow::stop()
+{
+ PartialTestWindow::stop();
+
+ delete offscreen;
+ offscreen = NULL;
+ delete overlay;
+ overlay = NULL;
+}
+
+void OverlayTestWindow::draw()
+{
+ int ox, oy, ow, oh;
+ int X, Y, W, H;
+
+ // We cannot update the damage region from inside the draw function,
+ // so delegate this to an idle function
+ Fl::add_idle(timer, this);
+
+ // Check what actually needs updating
+ fl_clip_box(0, 0, w(), h(), X, Y, W, H);
+ if ((W == 0) || (H == 0))
+ return;
+
+ // We might get a redraw before we are fully ready
+ if (!overlay)
+ return;
+
+ // Simplify the clip region to a simple rectangle in order to
+ // properly draw all the layers even if they only partially overlap
+ fl_push_no_clip();
+ fl_push_clip(X, Y, W, H);
+
+ if (offscreen)
+ fb->draw(offscreen, X, Y, X, Y, W, H);
+ else
+ fb->draw(X, Y, X, Y, W, H);
+
+ pixels += W*H;
+ frames++;
+
+ ox = (w() - overlay->width()) / 2;
+ oy = h() / 4 - overlay->height() / 2;
+ ow = overlay->width();
+ oh = overlay->height();
+ fl_clip_box(ox, oy, ow, oh, X, Y, W, H);
+ if ((W != 0) && (H != 0)) {
+ if (offscreen)
+ overlay->blend(offscreen, X - ox, Y - oy, X, Y, W, H);
+ else
+ overlay->blend(X - ox, Y - oy, X, Y, W, H);
+ }
+
+ fl_pop_clip();
+ fl_pop_clip();
+
+ if (offscreen) {
+ fl_clip_box(0, 0, w(), h(), X, Y, W, H);
+ offscreen->draw(X, Y, X, Y, W, H);
+ }
+}
+
+static void dosubtest(TestWindow* win, int width, int height,
+ unsigned long long* pixels,
+ unsigned long long* frames,
+ double* time)
+{
+ struct timeval start;
+
+ win->start(width, height);
+
+ gettimeofday(&start, NULL);
+ while (rfb::msSince(&start) < 3000)
+ Fl::wait();
+
+ win->stop();
+
+ *pixels = win->pixels;
+ *frames = win->frames;
+ *time = win->time;
+}
+
+static bool is_constant(double a, double b)
+{
+ return (fabs(a - b) / a) < 0.1;
+}
+
+static void dotest(TestWindow* win)
+{
+ unsigned long long pixels[3];
+ unsigned long long frames[3];
+ double time[3];
+
+ double delay, rate;
+ char s[1024];
+
+ // Run the test several times at different resolutions...
+ dosubtest(win, 800, 600, &pixels[0], &frames[0], &time[0]);
+ dosubtest(win, 1024, 768, &pixels[1], &frames[1], &time[1]);
+ dosubtest(win, 1280, 960, &pixels[2], &frames[2], &time[2]);
+
+ // ...in order to compute how much of the rendering time is static,
+ // and how much depends on the number of pixels
+ // (i.e. solve: time = delay * frames + rate * pixels)
+ delay = (((time[0] - (double)pixels[0] / pixels[1] * time[1]) /
+ (frames[0] - (double)pixels[0] / pixels[1] * frames[1])) +
+ ((time[1] - (double)pixels[1] / pixels[2] * time[2]) /
+ (frames[1] - (double)pixels[1] / pixels[2] * frames[2]))) / 2.0;
+ rate = (((time[0] - (double)frames[0] / frames[1] * time[1]) /
+ (pixels[0] - (double)frames[0] / frames[1] * pixels[1])) +
+ ((time[1] - (double)frames[1] / frames[2] * time[2]) /
+ (pixels[1] - (double)frames[1] / frames[2] * pixels[2]))) / 2.0;
+
+ // However, we have some corner cases:
+
+ // We are restricted by some delay, e.g. refresh rate
+ if (is_constant(frames[0]/time[0], frames[2]/time[2])) {
+ fprintf(stderr, "WARNING: Fixed delay dominating updates.\n\n");
+ delay = time[2]/frames[2];
+ rate = 0.0;
+ }
+
+ // There isn't any fixed delay, we are only restricted by pixel
+ // throughput
+ if (fabs(delay) < 0.001) {
+ delay = 0.0;
+ rate = time[2]/pixels[2];
+ }
+
+ // We can hit cache limits that causes performance to drop
+ // with increasing update size, screwing up our calculations
+ if ((pixels[2] / time[2]) < (pixels[0] / time[0] * 0.9)) {
+ fprintf(stderr, "WARNING: Unexpected behaviour. Measurement unreliable.\n\n");
+
+ // We can't determine the proportions between these, so divide the
+ // time spent evenly
+ delay = time[2] / 2.0 / frames[2];
+ rate = time[2] / 2.0 / pixels[2];
+ }
+
+ fprintf(stderr, "Rendering delay: %g ms/frame\n", delay * 1000.0);
+ if (rate == 0.0)
+ strcpy(s, "N/A pixels/s");
+ else
+ rfb::siPrefix(1.0 / rate, "pixels/s", s, sizeof(s));
+ fprintf(stderr, "Rendering rate: %s\n", s);
+ fprintf(stderr, "Maximum FPS: %g fps @ 1920x1080\n",
+ 1.0 / (delay + rate * 1920 * 1080));
+}
+
+int main(int argc, char** argv)
+{
+ TestWindow* win;
+
+ fprintf(stderr, "Full window update:\n\n");
+ win = new TestWindow();
+ dotest(win);
+ delete win;
+ fprintf(stderr, "\n");
+
+ fprintf(stderr, "Partial window update:\n\n");
+ win = new PartialTestWindow();
+ dotest(win);
+ delete win;
+ fprintf(stderr, "\n");
+
+ fprintf(stderr, "Partial window update with overlay:\n\n");
+ win = new OverlayTestWindow();
+ dotest(win);
+ delete win;
+ fprintf(stderr, "\n");
+
+ return 0;
+}