├── README.md ├── RectangleGLWidget.cpp ├── RectangleGLWidget.h ├── RectangleWidget.cpp ├── RectangleWidget.h ├── assets └── qmake.conf ├── main.cpp └── qt-rectangles.pro /README.md: -------------------------------------------------------------------------------- 1 | # TL;DR 2 | 3 | Do not use `eglfs` with Qt if you're using QWidgets. QWidgets use the software 4 | renderer (writing this as of Qt 5.6.1). If you use `eglfs`, Qt renders them first 5 | to an image and then uses OpenGL ES to render that to the display. As I found 6 | out below, using `linuxfb` worked much better since it didn't have to do the OpenGL ES 7 | step. The `xcb` plugin for X11 also worked fine. 8 | 9 | See [Qt's embedded Linux guide](http://doc.qt.io/qt-5/embedded-linux.html) and 10 | specifically the 2nd paragraph of the EGLFS section. 11 | 12 | # qt-rectangles 13 | 14 | This is a simple test program to demonstrate performance differences on the 15 | Raspberry Pi. 16 | 17 | To run: 18 | 19 | qmake qt-rectangles.pro 20 | make 21 | ./qt-rectangles 22 | 23 | # Measurements 24 | 25 | Key: 26 | * xcb = X11 w/ OpenGL ES 27 | * eglfs = Full screen OpenGL ES (no X11) 28 | * linuxfb = Full screen software rendered 29 | * noalpha = No alpha blending of rectangles 30 | * gl = Use QOpenGLWidget version 31 | 32 | See http://doc.qt.io/qt-5/embedded-linux.html for more information on xcb, 33 | eglfs, and linuxfb. 34 | 35 | 36 | ## 16x16 rectangles (8100 total) 37 | 38 | | Platform | Commandline | Redraw time | 39 | |------------|----------------------------------------------|-------------| 40 | | Pi 3 | ./qt-rectangles -platform linuxfb noalpha | 17 ms | 41 | | Pi 3 | ./qt-rectangles -platform xcb noalpha | 18 ms | 42 | | Pi 3 | ./qt-rectangles -platform eglfs noalpha | 23 ms | 43 | | Pi 3 | ./qt-rectangles -platform xcb | 29 ms | 44 | | Pi 3 | ./qt-rectangles -platform linuxfb | Oscillates between 29 and 51 ms | 45 | | Pi 3 | ./qt-rectangles -platform eglfs | 133 ms | 46 | | Pi 3 | ./qt-rectangles -platform eglfs gl noalpha | 273 ms | 47 | | Pi 3 | ./qt-rectangles -platform eglfs gl | 273 ms | 48 | | Pi Model B | ./qt-rectangles -platform xcb | 96 ms | 49 | | Pi Model B | ./qt-rectangles -platform linuxfb | 94 ms | 50 | | Pi Model B | ./qt-rectangles -platform eglfs | 422 ms | 51 | | Pi Model B | ./qt-rectangles -platform xcb noalpha | 34 ms | 52 | | Pi Model B | ./qt-rectangles -platform linuxfb noalpha | 36 ms | 53 | | Pi Model B | ./qt-rectangles -platform eglfs noalpha | 65 ms | 54 | 55 | ## 120x120 rectangles (144 total) 56 | 57 | | Platform | Commandline | Redraw time | 58 | |------------|----------------------------------------------|-------------| 59 | | Pi 3 | ./qt-rectangles -platform eglfs gl noalpha 120 | 0 ms | 60 | | Pi 3 | ./qt-rectangles -platform eglfs gl 120 | 1 ms | 61 | | Pi 3 | ./qt-rectangles -platform xcb noalpha 120 | 7 ms | 62 | | Pi 3 | ./qt-rectangles -platform linuxfb noalpha 120 | 8 ms | 63 | | Pi 3 | ./qt-rectangles -platform eglfs noalpha 120 | 15 ms | 64 | | Pi 3 | ./qt-rectangles -platform linuxfb 120 | 15 ms | 65 | | Pi 3 | ./qt-rectangles -platform xcb 120 | 29 ms | 66 | | Pi 3 | ./qt-rectangles -platform eglfs 120 | 84 ms | 67 | | Pi 3 | ./qt-rectangles -platform linuxfb gl 120 | Didn't work | 68 | | Pi 3 | ./qt-rectangles -platform linuxfb gl noalpha 120 | Didn't work | 69 | 70 | ## 1920x1080 rectangle (1 total) 71 | 72 | | Platform | Commandline | Redraw time | 73 | |------------|-----------------------------------------------|-------------| 74 | | Pi 3 | ./qt-rectangles -platform eglfs gl 1920 | 0 ms | 75 | | Pi 3 | ./qt-rectangles -platform eglfs gl noalpha 1920 | 0 ms | 76 | | Pi 3 | ./qt-rectangles -platform xcb noalpha 1920 | 6 ms | 77 | | Pi 3 | ./qt-rectangles -platform linuxfb noalpha 1920 | 6 ms | 78 | | Pi 3 | ./qt-rectangles -platform eglfs noalpha 1920 | 14 ms | 79 | | Pi 3 | ./qt-rectangles -platform xcb 1920 | 26 ms | 80 | | Pi 3 | ./qt-rectangles -platform linuxfb 1920 | 26 ms | 81 | | Pi 3 | ./qt-rectangles -platform eglfs 1920 | 80 ms | 82 | | Pi 3 | ./qt-rectangles -platform linuxfb gl 1920 | Didn't work | 83 | | Pi 3 | ./qt-rectangles -platform linuxfb gl noalpha 1920 | Didn't work | 84 | 85 | # Notes to self 86 | 87 | ## Compiling the eglfs version of Qt 5 88 | 89 | 1. Download [qtbase-opensource-src-5.6.1-1.tar.xz](http://download.qt.io/official_releases/qt/5.6/5.6.1-1/submodules/qtbase-opensource-src-5.6.1-1.tar.xz) and 90 | untar it on the Raspberry Pi. 91 | 92 | 2. Copy [qmake.conf](assets/qmake.conf) to `$QT/mkspecs/linux-g++` 93 | 94 | 3. Uninstall any X11 dev libraries. They seemed to confuse Qt's `./configure` 95 | script and since I didn't want them to link in anyway, I removed them. This 96 | was a manual process of running `apt list --installed | grep -- -dev`, 97 | looking for the X11 ones and then uninstalling them. 98 | 99 | 4. Run `./configure -eglfs -no-xcb -no-xcb-xlib -no-nis -opensource -confirm-license -no-qml-debug -no-linuxfb -no-gif -opengl es2 -no-pch` 100 | (See log at bottom) 101 | 102 | 5. Run `make` 103 | 104 | 6. Come back tomorrow 105 | 106 | ## Running 107 | 108 | Make sure that the paths are right to the proper Qt versions. The easy way is to 109 | compile one version of Qt with only eglfs support and the other version with 110 | only xcb support. Then if you get the wrong library at runtime, the `-platform` 111 | argument will result in an error. I.e., do this: 112 | 113 | QT_PLUGIN_PATH=~/qt-eglfs/plugins LD_LIBRARY_PATH=~/qt-eglfs/lib ./qt-rectangles -platform eglfs 114 | 115 | To get an xcb version of Qt5 quickly, run `sudo apt-get install qt5-default` 116 | 117 | ## Qt's ./configure log for enabling eglfs 118 | 119 | ``` 120 | $ ./configure -eglfs -no-xcb -no-xcb-xlib -no-nis -opensource -confirm-license 121 | -no-qml-debug -no-linuxfb -no-gif -opengl es2 -no-pch 122 | 123 | This is the Qt Open Source Edition. 124 | 125 | You are licensed to use this software under the terms of 126 | the Lesser GNU General Public License (LGPL) versions 2.1. 127 | You are also licensed to use this software under the terms of 128 | the GNU Lesser General Public License (LGPL) versions 3. 129 | 130 | You have already accepted the terms of the Open Source license. 131 | 132 | Running configuration tests (phase 1)... 133 | Done running configuration tests. 134 | Creating qmake... 135 | .............................................................................Done. 136 | Running configuration tests (phase 2)... 137 | Done running configuration tests. 138 | 139 | Configure summary 140 | 141 | Build type: linux-g++ (arm, CPU features: none detected) 142 | Platform notes: 143 | 144 | - Also available for Linux: linux-clang linux-kcc linux-icc 145 | linux-cxx 146 | 147 | Build options: 148 | Configuration .......... accessibility audio-backend c++11 c++14 clock-gettime 149 | clock-monotonic compile_examples concurrent dbus dbus-linked egl eglfs 150 | eglfs_brcm enable_new_dtags evdev eventfd fontconfig full-config getaddrinfo 151 | getifaddrs harfbuzz iconv icu inotify ipv6ifname large-config largefile libudev 152 | medium-config minimal-config mremap no-gif no-qml-debug opengl opengles2 openvg 153 | pcre png posix_fallocate qpa qpa reduce_exports release rpath shared 154 | small-config system-freetype system-jpeg system-png system-zlib 155 | threadsafe-cloexec use_gold_linker 156 | Build parts ............ libs tools examples 157 | Mode ................... release 158 | Using sanitizer(s)...... none 159 | Using C++ standard ..... c++14 160 | Using gold linker....... yes 161 | Using new DTAGS ........ yes 162 | Using PCH .............. no 163 | Using LTCG ............. no 164 | Target compiler supports: 165 | Neon ................. no 166 | 167 | Qt modules and options: 168 | Qt D-Bus ............... yes (linked to dbus-1) 169 | Qt Concurrent .......... yes 170 | Qt GUI ................. yes 171 | Qt Widgets ............. yes 172 | Large File ............. yes 173 | QML debugging .......... no 174 | Use system proxies ..... no 175 | 176 | Support enabled for: 177 | Accessibility .......... yes 178 | ALSA ................... no 179 | CUPS ................... no 180 | Evdev .................. yes 181 | FontConfig ............. yes 182 | FreeType ............... yes (system library) 183 | Glib ................... no 184 | GStreamer .............. no 185 | GTK theme .............. no 186 | HarfBuzz ............... yes (bundled copy) 187 | Iconv .................. yes 188 | ICU .................... yes 189 | Image formats: 190 | GIF .................. no 191 | JPEG ................. yes (plugin, using system library) 192 | PNG .................. yes (in QtGui, using system library) 193 | libinput................ no 194 | Logging backends: 195 | journald ............... no 196 | syslog ............... no 197 | mtdev .................. no 198 | Networking: 199 | getaddrinfo .......... yes 200 | getifaddrs ........... yes 201 | IPv6 ifname .......... yes 202 | libproxy.............. no 203 | OpenSSL .............. no 204 | NIS .................... no 205 | OpenGL / OpenVG: 206 | EGL .................. yes 207 | OpenGL ............... yes (OpenGL ES 2.0+) 208 | OpenVG ............... yes-auto 209 | PCRE ................... yes (bundled copy) 210 | pkg-config ............. yes 211 | PulseAudio ............. no 212 | QPA backends: 213 | DirectFB ............. no 214 | EGLFS ................ yes 215 | EGLFS i.MX6 ........ no 216 | EGLFS i.MX6 Wayland. no 217 | EGLFS EGLDevice .... no 218 | EGLFS GBM .......... no 219 | EGLFS Mali ......... no 220 | EGLFS Raspberry Pi . yes 221 | EGLFS X11 .......... no 222 | LinuxFB .............. no 223 | Mir client............ no 224 | XCB .................. no 225 | Session management ..... yes 226 | SQL drivers: 227 | DB2 .................. no 228 | InterBase ............ no 229 | MySQL ................ no 230 | OCI .................. no 231 | ODBC ................. no 232 | PostgreSQL ........... no 233 | SQLite 2 ............. no 234 | SQLite ............... yes (plugin, using bundled copy) 235 | TDS .................. no 236 | tslib .................. no 237 | udev ................... yes 238 | xkbcommon-x11........... no 239 | xkbcommon-evdev......... no 240 | zlib ................... yes (system library) 241 | 242 | 243 | NOTE: Qt is using double for qreal on this system. This is binary incompatible 244 | against Qt 5.1. 245 | Configure with '-qreal float' to create a build that is binary compatible with 246 | 5.1. 247 | 248 | Qt is now configured for building. Just run 'make'. 249 | Once everything is built, you must run 'make install'. 250 | Qt will be installed into /usr/local/Qt-5.6.1 251 | 252 | Prior to reconfiguration, make sure you remove any leftovers from 253 | the previous build. 254 | ``` 255 | -------------------------------------------------------------------------------- /RectangleGLWidget.cpp: -------------------------------------------------------------------------------- 1 | #include "RectangleGLWidget.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | RectangleGLWidget::RectangleGLWidget(int side, bool withAlpha, QWidget *parent) 8 | : QOpenGLWidget(parent) 9 | , font_("DejaVu Sans Mono", 16) 10 | , withAlpha_(withAlpha) 11 | , side_(side) 12 | { 13 | startTimer(500); 14 | } 15 | 16 | void RectangleGLWidget::paintEvent(QPaintEvent *) 17 | { 18 | QElapsedTimer timer; 19 | timer.start(); 20 | QPainter p(this); 21 | 22 | int w = width(); 23 | int h = height(); 24 | int gray = 128; // Start at non-zero since if doing only 1 rectangle, 25 | // we don't want it to be completely transparent 26 | 27 | p.fillRect(0, 0, w, h, Qt::blue); 28 | 29 | for (int y = 0; y < h; y += side_) { 30 | int rh = qMin(h - y, side_); 31 | for (int x = 0; x < w; x += side_) { 32 | int rw = qMin(w - x, side_); 33 | p.fillRect(x, y, rw, rh, QColor(gray, gray, gray, withAlpha_ ? gray : 255)); 34 | gray += 3; 35 | if (gray >= 256) 36 | gray -= 256; 37 | } 38 | } 39 | 40 | p.setFont(font_); 41 | p.setPen(Qt::green); 42 | p.drawText(10, 100, QString("Redraw time: %1 ms").arg(timer.elapsed())); 43 | } 44 | 45 | void RectangleGLWidget::timerEvent(QTimerEvent *) 46 | { 47 | update(); 48 | } 49 | 50 | void RectangleGLWidget::keyPressEvent(QKeyEvent *) 51 | { 52 | // Exit on any key 53 | QApplication::exit(); 54 | } 55 | -------------------------------------------------------------------------------- /RectangleGLWidget.h: -------------------------------------------------------------------------------- 1 | #ifndef RECTANGLEGLWIDGET_H 2 | #define RECTANGLEGLWIDGET_H 3 | 4 | #include 5 | 6 | class RectangleGLWidget : public QOpenGLWidget 7 | { 8 | Q_OBJECT 9 | public: 10 | RectangleGLWidget(int side, bool withAlpha, QWidget *parent = 0); 11 | 12 | protected: 13 | void paintEvent(QPaintEvent *); 14 | void timerEvent(QTimerEvent *); 15 | void keyPressEvent(QKeyEvent *); 16 | 17 | private: 18 | QFont font_; 19 | bool withAlpha_; 20 | int side_; // yes, the rectangles became squares... 21 | }; 22 | 23 | #endif // RECTANGLEGLWIDGET_H 24 | -------------------------------------------------------------------------------- /RectangleWidget.cpp: -------------------------------------------------------------------------------- 1 | #include "RectangleWidget.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | RectangleWidget::RectangleWidget(int side, bool withAlpha, QWidget *parent) 8 | : QWidget(parent) 9 | , font_("DejaVu Sans Mono", 16) 10 | , withAlpha_(withAlpha) 11 | , side_(side) 12 | { 13 | startTimer(500); 14 | } 15 | 16 | void RectangleWidget::paintEvent(QPaintEvent *) 17 | { 18 | QElapsedTimer timer; 19 | timer.start(); 20 | QPainter p(this); 21 | 22 | int w = width(); 23 | int h = height(); 24 | int gray = 128; // Start at non-zero since if doing only 1 rectangle, 25 | // we don't want it to be completely transparent 26 | 27 | p.fillRect(0, 0, w, h, Qt::blue); 28 | 29 | for (int y = 0; y < h; y += side_) { 30 | int rh = qMin(h - y, side_); 31 | for (int x = 0; x < w; x += side_) { 32 | int rw = qMin(w - x, side_); 33 | p.fillRect(x, y, rw, rh, QColor(gray, gray, gray, withAlpha_ ? gray : 255)); 34 | gray += 3; 35 | if (gray >= 256) 36 | gray -= 256; 37 | } 38 | } 39 | 40 | p.setFont(font_); 41 | p.setPen(Qt::green); 42 | p.drawText(10, 100, QString("Redraw time: %1 ms").arg(timer.elapsed())); 43 | } 44 | 45 | void RectangleWidget::timerEvent(QTimerEvent *) 46 | { 47 | update(); 48 | } 49 | 50 | void RectangleWidget::keyPressEvent(QKeyEvent *) 51 | { 52 | // Exit on any key 53 | QApplication::exit(); 54 | } 55 | -------------------------------------------------------------------------------- /RectangleWidget.h: -------------------------------------------------------------------------------- 1 | #ifndef RECTANGLEWIDGET_H 2 | #define RECTANGLEWIDGET_H 3 | 4 | #include 5 | 6 | class RectangleWidget : public QWidget 7 | { 8 | Q_OBJECT 9 | public: 10 | RectangleWidget(int side, bool withAlpha, QWidget *parent = 0); 11 | 12 | protected: 13 | void paintEvent(QPaintEvent *); 14 | void timerEvent(QTimerEvent *); 15 | void keyPressEvent(QKeyEvent *); 16 | 17 | private: 18 | QFont font_; 19 | bool withAlpha_; 20 | int side_; // yes, the rectangles became squares... 21 | }; 22 | 23 | #endif // RECTANGLEWIDGET_H 24 | -------------------------------------------------------------------------------- /assets/qmake.conf: -------------------------------------------------------------------------------- 1 | # 2 | # qmake configuration for linux-g++ 3 | # 4 | 5 | MAKEFILE_GENERATOR = UNIX 6 | CONFIG += incremental 7 | QMAKE_INCREMENTAL_STYLE = sublib 8 | 9 | include(../common/linux.conf) 10 | include(../common/gcc-base-unix.conf) 11 | include(../common/g++-unix.conf) 12 | 13 | QMAKE_LFLAGS += -Wl,-rpath-link,/opt/vc/lib 14 | 15 | QMAKE_LIBDIR_OPENGL_ES2 = /opt/vc/lib 16 | QMAKE_LIBDIR_EGL = $$QMAKE_LIBDIR_OPENGL_ES2 17 | QMAKE_LIBDIR_OPENVG = $$QMAKE_LIBDIR_OPENGL_ES2 18 | 19 | QMAKE_INCDIR_EGL = /opt/vc/include \ 20 | /opt/vc/include/interface/vcos/pthreads \ 21 | /opt/vc/include/interface/vmcs_host/linux 22 | QMAKE_INCDIR_OPENGL_ES2 = $${QMAKE_INCDIR_EGL} 23 | QMAKE_INCDIR_OPENVG = $${QMAKE_INCDIR_EGL} 24 | 25 | QMAKE_LIBS_EGL = -lEGL -lGLESv2 26 | QMAKE_LIBS_OPENVG = -lEGL -lOpenVG -lGLESv2 27 | 28 | load(qt_config) 29 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "RectangleWidget.h" 3 | #include "RectangleGLWidget.h" 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | QApplication a(argc, argv); 8 | 9 | bool useGL = false; 10 | bool withAlpha = true; 11 | int side = 16; 12 | 13 | foreach (QString arg, a.arguments()) { 14 | bool ok; 15 | if (arg == "noalpha") 16 | withAlpha = false; 17 | else if (arg == "alpha") 18 | withAlpha = true; 19 | else if (arg == "gl") 20 | useGL = true; 21 | else if (arg.toInt(&ok) && ok) 22 | side = arg.toInt(); 23 | } 24 | if (argc > 1 && qstrcmp(argv[1], "noalpha") == 0) 25 | withAlpha = false; 26 | 27 | if (!useGL) { 28 | RectangleWidget rectangles(side, withAlpha); 29 | rectangles.setMinimumSize(1280, 720); 30 | rectangles.showFullScreen(); 31 | 32 | return a.exec(); 33 | } else { 34 | RectangleGLWidget rectangles(side, withAlpha); 35 | rectangles.setMinimumSize(1280, 720); 36 | rectangles.showFullScreen(); 37 | 38 | return a.exec(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /qt-rectangles.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2016-08-05T20:00:10 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core gui 8 | 9 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 10 | 11 | TARGET = qt-rectangles 12 | TEMPLATE = app 13 | 14 | 15 | SOURCES += main.cpp\ 16 | RectangleWidget.cpp \ 17 | RectangleGLWidget.cpp 18 | 19 | HEADERS += RectangleWidget.h \ 20 | RectangleGLWidget.h 21 | 22 | --------------------------------------------------------------------------------