Thursday, November 9, 2017

Qt 5.10 QML Shape testing

When implementing component into QtQuick UI which needs something more than rectangles, images and texts, pure declarative QML hasn't been enough. Popular choices to use for items with some sort of vector drawing are QML Canvas, QQuickPaintedItem or QNanoPainter.

But with Qt 5.10 there will be supports for new Shape element with paths that contain lines, quads, arcs etc. so I decided to install Qt 5.10 beta3 and implement all tests of "qnanopainter_vs_qpainter_demo" with also QML + Shape elements. (This kinda makes it "qnanopainter_vs_qpainter_vs_qmlshape_demo" but not renaming now). So here is in all glory the same UI implemented with QNanoPainter (left), QQuickPaintedItem (center), and QML+Shape (right):

Hard to spot the differences right? If only there would be a way to prove this, some way to x-ray into these UIs... like QSG_VISUALIZE=overdraw to visualize what Qt Quick Scene Graph Renderer sees?

Here you can see that scene graph sees QNanoPainter and QQuickPaintedItem as just big unknown rectangles, while QML+Shape it sees into as that is composed of native scene graph nodes. But proof is in the pudding as they say, what looks the same doesn't perform the same. Here's a video showing all 3 running with two different Android devices:

As different rendering components can be enabled/disabled and settings changed, this demo is quite nice for doing performance comparisons of both exact drawing methods or combining all methods. But those will have to wait for another blog post and for non-beta Qt 5.10 to get fair results. In the mean time, feel free to pull latest sources from github, test yourself and provide patches or comments!


Thomas Lang said...

According to your video the QML+Shapes (hardware accelarated) performs even worse than QPainter (rasterized) in the QQuickPaintedItem ?! o.O

Kaitsu said...

@Thomas Yes, this quite heavy UI is slower there with QML+Shapes than with QQuickPaintedItem. Both are actually HW accelerated as QQuickPaintedItem was set to FramebufferObject renderTarget, Image renderTarget would be slower. Enabling individual tests reveals which are faster, which slower, in which conditions etc.

But as said that's content for another blog post or curious ones to experiment themselves :)

Gunnar Roth said...

Thanks for this comparison. It would be interesting to see how QNanopainter performs here in the Rendernode mode.
The problem with the qpainter and qnanopainter fbo mode is, that you cannot easily mix with other quickitems. But if you do, making lots of QNanopainter items or QPaintedItems, it is becoming a lot slower. The other problem with QNanoPainter is, that it is missing a lot of features like Dashes Lines, MultiStop Gradients, no nonzero fillrule for pathes etc. which makes rendering SVG hard.

Kaitsu said...

@Gunnar Thanks!

Based on my earlier experiments (see, QSGRenderNode performance is very similar to QQuickFramebufferObject so rendering through FBO wouldn't have big affect. But if you or anyone else have UI with many separate items and/or hardware which suffers more from FBOs, please test building QNanoPainter with QNANO_USE_RENDERNODE enabled.

And yes QNanoPainter (and NanoVG) API doesn't contain everything that QPainter (or Cairo, or Skia) has, which can be considered also a strength to keep rendering performant on OpenGL. Dashed lines with round caps e.g. can potentially multiply vertices count and shaders complexity. Multi-stop gradients on the other hand might be coming soonish, see

Anonymous said...

Disappointing to see shapes performance is so abysmal. Have you experimented with adding a QML API for QNP and see what kind of performance you get when making the drawing calls from QML?

Kaitsu said...

@Anonymous I think QML Shape is targeted to a bit less demanding cases than this rather heavy benchmarking demo, when you need few simplish paths and want to do them in QML side with rest of the UI.

About QML API for QNanoPainter: No I haven't tried to implement anything like that. It would have some overhead from JavaScript, shouldn't be too much. But many use cases contain some graphs or similar where getting related data into QML side would also add some overhead. Instead of moving drawing coding into QML/JavaScript I would rather improve productivity in other ways and keep final code as performant C++ as possible.

Anonymous said...

You underestimate the significant value in the form of much quicker prototyping - you can code and run without having to recompile anything, use bindings and whatnot. That is much, much faster than using C++ directly. And when you are done, you can still simply move the code to the C++ side. There is also value in running runtime generated code on the client side, without the necessity to have a compatible C++ compiler.

Kaitsu said...

I do see big value in quicker prototyping, no question about it =) About "simply move the code to the C++ side" I do slightly disagree, if you have done everything in QML&JavaScript moving it to optimal C++ takes some rewriting. I wouldn't mind additional QNanoPainter QML API, just haven't had reasons to implement it myself and now with Shape element that is kind of covered by Qt.

But this does give me some fresh thoughts, thanks for commenting!

Anonymous said...

IMO a QML API will have indisputable value, and no real drawbacks, since it will be optional. If you take care that it mirrors the C++ API it will make "porting" trivial. In my experience, most of the times all it takes is replacing a few var-s with static types, replacing a few . with ->, adding a few ; and that's about it.

Keep up the good work.

alpqr said...

Thanks for testing this, the results are definitely very interesting.

For QPainter (raster paint engine) it must be noted that the results are heavily dependent on the size of the backing QQuickPaintedItem, e.g. in a quick test on Windows the test app dropped from 32 fps to 8 fps when maximizing the window (on a 1080p screen). The other two approaches are a lot more immune to this.

It would be interesting to see results from other platforms and devices. I tried the test app on Windows (debug build and ANGLE, none of which is ideal), and the results were somewhat different when it comes to Shapes vs. QNanoPainter, see here:

BTW is there a way to get QNanoPainter use OpenGL proper on Windows (which would need going through some GL function resolver in NanoVG)? Right now things seem to be hardcoded to link to libGLESv2 from ANGLE directly.

Gunnar Roth said...

On my i5 4570 @3.2GHZ,I get 15 fps with QPainter, 12 fps with shapes and 30 fps with QNanopainter, when making the window fullscreen on my 1920x1200 monitor on windows 7.

Kaitsu said...

@Gunnar Pushed few improvements today and now it's easier to test how amount of items and QNANO_USE_RENDERNODE affects performance. With my brief test on Macbook Pro when rendering 32 QNanoQuickItems got 31fps with and 22fps without QNANO_USE_RENDERNODE so healthy ~50% improvement there. Reducing tests (to make rendering per item faster) and increasing item (FBO) size gives even more gains. But to my use cases this is more theoretical than real life gain, as not using many items in same UI.

@Laszlo Thanks for testing! As said I personally haven't really been testing on Windows side, so maybe someone else can help there. But I plan to blog about results on different platforms, probably once 5.10 RC is out.

alpqr said...

Would it be possible to rerun the tests in the video with "antialiasing" disabled?

After going through the demo a bit, it seems that the default setup is not quite fair in the sense that "antialiasing" maps to NVG_ANTIALIAS for NanoVG (just enables feather-based AA, still a normal FBO), while for Shapes it toggles the heavyweight 4x MSAA. On high resolution screens this won't be acceptable with middle-low HW.

Full story in

Kaitsu said...

@Laszlo Wow, nice testing there! Yes QNanoPainter antialiasing is shader based which performs ofter better and is available on all hardware, while MSAA requires GL extensions. QQuickPaintedItem (FBO) and Shape don't have shader based antialiasing so for smoother output they require MSAA. Please check my benchmarking results with and without antialiasing:

I will write some comment also into bug report, thanks!

alpqr said...

Oh cool, nice follow up post!