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.

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):

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}quitcoding.com, thanks!

Imo, Qt5's QQuick hasn't yet reached the level of the Qt4's QPainter or QGraphicsItem: it offers new possibilities, but still doesn't cover the scope of Qt4 What you are doing seems therefore very interesting. I hope you'll fill the gap !

An important point too, is the ability to describe a geometry in a space without consideration of the QQuickItem size: The geometry might be much much larger than the rendering area and should not be constrained by a limited (framebuffer) size. This is a killer criteria for QQuickCanvas. QQuickItem is OK for that, but to low level and painfull (not really "code less, create more").

I like your analysis on the current situation. So I'm sure your lib will be a big step forward.Keep going, good luke!

I just remember an issue i encountered while implementing QQuickItem derived shapes: an efficient :

virtual bool contains(const QPointF);

This is required for many interaction related functions : mouseover, select...When you draw a shape, which contour isn't a rectangle (curves, stars...), you have to reimplement this "contains" function. For each shape you have to define the correct specialized algorithm. This is a real pain. Moreover it has to be very efficient because the function is called after every mouse move for an interactive item.My dream feature request is a generic function : you define what to draw, it implements the correct and efficient "contains".I Don't know if nanoVG embbed the required function. If yes, please propagate it to the QNanoItems!

Hey... I 'm using QCustomPlot for my own project. But QPainter output quality is not good enough when it comes to the export to image process. Can you at least share the QNanoPainter in gist? or whetevery you like? I can integrate rest of it i think.

It would be great if some one tell me about the implementation of antialiasing in qnanopainter. I had gone through nanovg.c but couldnt able to find the real implementation.I am thinking there may be a glsl program for making it run on GPU for that, since qnanopainter is giving a very good performance.