Sunday, October 4, 2015

Introducing QNanoPainter

Qt World Summit is just about to start! I’m not participating this time, but wanted to contribute one-more-thing to discuss in UI groups at the heart of Berlin ;)

During the past about six months we at QUIt Coding have had a side-side-project called QNanoPainter. This library is designed for implementing custom QQuickItems into Qt5 scene graph. Currently if you want to implement custom Qt Quick item, options are at least:

  • QQuickItem: Offers best performance due to Qt5 scene graph integration. But QSG* classes are relatively low-level with vertices, indices & materials. Painting more complicated things requires quite an expert and even then productivity is not very high.
  • QQuickFramebufferObject: This is a good option if you want to draw with OpenGL into QQuickItem. But as we all know, OpenGL is also quite low-level API so no productivity wins here.
  • QQuickPaintedItem: This means painting with good old QPainter C++ API. Weakness of QPainter with modern graphics accelerated hardware is that you either get good antialiased quality (QImage rendering target) or fast OpenGL painting (FBO rendering target) but not both. Qt raster engine is very good CPU backend but GPUs are ruling the world these days. And QPainter OpenGL backend doesn’t itself support antialiasing, it requires multisampling with GL_EXT_framebuffer_multisample and GL_EXT_framebuffer_blit extensions as explained here by Laszlo. In case you set FBO target and antialiasing on with hardware that doesn’t support these (e.g. Nexus6 and iPhone6), target with automatically switch back to QImage & raster with performance dipping on most devices.
  • QtQuick Canvas: Canvas item is implemented on top of QPainter so it has mostly same cons and pros as QQuickPaintedItem. HTML5 Canvas API is nice and popular but there is some overhead from (QPainter) API mismatch and JavaScript especially if more drawing data needs to be exported from C++ side.
  • QML ShaderEffect: As you can’t really mess with the mesh, this approach suits for simple cases where fragment shader is enough. But we are now thinking a bit more complicated items where this option isn’t enough or at least becomes complicated and slow.

So isn’t these options enough, still need something else? Yes, I think so.

QNanoPainter is our novel approach for trying to offer performance, productivity and rendering quality all in one. In a nutshell QNanoPainter is:

  • Excellent NanoVG OpenGL library as a drawing backend with some patches for performance & features.
  • QNanoPainter C++ API on top of NanoVG (which is written with C). This API is mixture of QPainter and HTML5 canvas APIs. Offers vector drawing, images and text rendering with classes such as QNanoPainter, QNanoFont, QNanoImagePattern, QNanoLinearGradient etc.
  • QNanoQuickItem to implement items into Qt Quick scene graph. Basically it’s like QQuickPaintedItem, but instead of QPainter offers QNanoPainter API for drawing.
  • Some examples like QNanoPainter vs. QPainter demo and Gallery, more to come.

One thing we have tried is to implement demo with same painting result using QPainter (QQuickPaintedItem) and QNanoPainter (QNanoQuickItem). Performance comparison is always tricky (and it’s not just about performance), but with comparable antialiased painting we are seeing 3-10 times better performance with QNanoPainter depending on hardware, content, item sizes etc. Here’s a short video showing this demo running on iPhone6 and Nexus6 with all tests on (iPhone6 in fact painting everything twice to get more juice out of it):

(best viewed in HD)

QNanoPainter is still work-in-progress and not ready for releasing. But we would like to hear what others think: Which (of the above) options do you use for custom QML items? Have you had issues finding suitable option for some use-cases? If yes, what features have you missed and would like to have?

Just comment here, tweet with #qnanopainter or email to info{at}, thanks!