Overdraw happens when one QML item fully eclipses another QML item. The QML renderer always draws both items, although there is no need to draw the eclipsed item. You must help out the renderer by explicitly setting visible: false
on the eclipsed item.
On embedded systems, heavy overdraw makes animations or flicking jerky. In the worst case, it freezes your HMI. Fortunately, the Qt experts at KDAB developed a tool, GammaRay, which makes detecting overdraw a piece of cake. I’ll show you how to build GammaRay, how to detect overdraw in the home screen of a harvester HMI, and how to fix the overdraw.
Building GammaRay
You must build GammaRay against the same Qt version used by your QML application, because GammaRay uses private Qt headers. So, it’s best to build GammaRay from sources (see also Getting GammaRay). Here are the commands to build GammaRay on Ubuntu 16.04.
$ git clone git://github.com/KDAB/GammaRay.git
$ cd GammaRay
$ mkdir build-gammaray-with-qt5.13.1
$ cd build-gammaray-with-qt5.13.1
$ cmake -D CMAKE_PREFIX_PATH=/path/to/qt \
-D CMAKE_INSTALL_PREFIX=/path/to/gammaray ..
$ make -j8
$ make install
CMAKE_PREFIX_PATH
specifies the base directory of your Qt installation and binds GammaRay to a specific Qt version. ${CMAKE_PREFIX_PATH}/bin/qmake
points to the qmake executable. CMAKE_INSTALL_PREFIX
determines the installation location of GammaRay. If you skip CMAKE_INSTALL_PREFIX
, GammaRay will be installed into /usr/local.
Detecting Overdraw
Start GammaRay with
$ /path/to/gammaray/bin/gammaray &
You’ll see the launcher window.
Enter the path to your Executable, the Working Directory and the Program arguments. Then, press the Launch button. You’ll see GammaRay’s main window.
Select the Visualize Overdraw tool marked by the red circle. GammaRay shows a 3D view of the application launched, the home screen of a harvester HMI. The 3D view visualises the z-order (stacking order) of the QML items.
The dark-grey background item is in the lowest layer 0. The upper and lower three green items belong to a list view each. The items in the list view are opaque and cover the background below. So, they are in layer 1. Only the middle list view item is visible to users. The left and right list view items are clipped and not visible to users. Additionally, the right list view items are outside the monitor. The yellow items on top of the green items are in layer 2.
Although the left and right list view items are not visible to the user, the QML renderer still draws them and wastes GPU and CPU cycles on them. This is called overdraw. As you can see from the 3D visualisation, the home screen suffers from heavy overdraw.
Each list view item has a size of 596 x 324 pixels. The six list view items cover an area of 1788 x 648 pixels (1.2 million pixels). The display has a resolution of 1280 x 1028 (1.0 million pixels). The overdraw more than doubles the rendering area from 1.0 million to 2.2 million pixels. You shouldn’t be surprised that the list views stutter and freeze when flicked.
Fixing the Overdraw
My first idea for fixing the overdraw was to set cacheBuffer: 0
in the two list views. The Qt documentation says about cacheBuffer
:
This property determines whether delegates are retained outside the visible area of the view.
So, setting cacheBuffer
to 0 should should stop ListView
from creating the previous and the next item in advance, from caching and rendering them. Unfortunately, it does not, as a look with GammaRay reveals.
My next idea was to set
visible: ListView.isCurrentItem
in the delegates of the list views – as told by GammaRay. This setting ensures that all items in the list views but the current item are invisible. A look with GammaRay confirms that.
This solution leaves a little problem. When you flick an item, say, to the left, the right half of the item’s area stays empty. It stays empty, because the next item to the right is invisible until it covers more than half of the list view. At the half point, the current item becomes invisible and the next item visible. Users don’t see in advance to which item they are moving. The new item comes as a surprise.
You get rid of the sudden switch of items by extending the visible condition.
visible: ListView.isCurrentItem ||
droot.ListView.view.moving
In addition to the current item, the list view makes the moving item visible. The moving item gradually replaces the current item. While the flicking goes on, the QML engine renders two list view items, but never three.
As soon as the user stops flicking, the QML engine only renders one item in each list view. The GPU included in the SoC (an i.MX6) easily handles the temporary overdraw.
Acknowledgement
Guiseppe d’Angelo, KDAB, gave a fantastic talk about Optimizing the Rendering of Qt Quick 2 Applications with GammaRay at the Qt Day Italy 2019. Just follow this link to the video. Guiseppe shows that GammaRay makes it easy to detect Overdraw, Clipping, OpenGL batches and other rendering problems in QML applications. My thanks go to Guiseppe for a very instructive talk and to KDAB for building GammaRay.