Sunday, March 9, 2014

QML and Recursive Shaders

Let's say that you want your Qt5/QML UI to contain sort of a graph. Instead of presenting just the current value, your want user to see few seconds into history how the value has changed. Something like this:

To implement the above component with Qt5 & QML, there are at least 3 possibilities:
1) Use QML Canvas and draw the graph using JavaScript.
2) Use QQuickPaintedItem and draw the graph using C++ QPainter APIs.
3) Use QQuickItem and draw using OpenGL and QSG* helpers.

First of these is "easiest", no C++ is required but performance and features go along HTML5 canvas API. Second one is good for those familiar with QPainter, but it also has a small performance cost as rendering goes either through QImage or FBO. Third QQuickItem option performs the best and is optimal for Qt5 scene graph, but working with scene graph QSG* classes and OpenGL can feel a bit too low level.

As the title of this blog post suggest, there is also a candidate number 4: Use a recursive shader.

QML ShaderEffectSource provides a property "recursive" which doesn't seem to be widely known. Story goes that Kim #1 asked Kim #2 to provide this feature and it was implemented during summer 11' with this commit. But none of the Qt examples (even now at Qt 5.2.1) demonstrate the usage so it got lost somehow... until now, my precious *gollum* *gollum*.

When setting the recursive property to true, ShaderEffectSource allocates an extra texture to store the rendering result and this texture can then be used as a source for ShaderEffect. Effectively this means that instead of rendering into a clean sheet, ShaderEffect can utilize the previous frame. And this of course opens up many possibilities.

Our component which demonstrates recursive shader usage is called 'QUItMeterComponent'. As explained, recursive ShaderEffectSource needs to allocate an extra texture of its size. Keeping the texture small increases performance and reduces graphics memory usage, so this sounds like a perfect opportunity to utilize modified LedScreen component presented earlier. As each pixel equals to a led, it allows presenting small source (left) as a nice ledscreen (right).

Now we just add a bit sugar with a mirror effect and feed our QUItMeterComponent with QtSensors accelerometer data. Outcome is G-force app which looks in Nexus4 like this:

If you think this component might suit your needs and/or want get a small example of using recursive ShaderEffectSource, sources of this G-force app are available from: