diff options
author | Pierre Ossman <ossman@cendio.se> | 2019-09-09 12:48:25 +0200 |
---|---|---|
committer | Pierre Ossman <ossman@cendio.se> | 2019-09-25 15:50:07 +0200 |
commit | 94de4dd0c46483477706002e128a61f74278c0f6 (patch) | |
tree | 7e51c88049452f0a46faa54177ffd96dc2903efb /tests/perf/fbperf.cxx | |
parent | d06906db59a6aa6434a5dd602b8d3c987d291ca8 (diff) | |
download | tigervnc-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.cxx | 399 |
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; +} |