As you will have read, a new module called Qt Quick 3D will begin offering 3D capabilities to Qt Quick via a QML API (and a planned C++ API for Qt 6). What does this mean for Qt 3D and where will it fit in the Qt ecosystem? Hopefully this blog post and the following one will help answer that question as well as give some insights into what we are working on in Qt 3D. This blog post will focus on the changes coming with Qt 5.x and the following article will details some of the research we are doing to improve Qt 3D on the Qt 6 timescale.

What place does Qt 3D have?

Qt 3D was designed to be a flexible framework. To that end it makes minimal assumptions about what it is you want to render and how you wish to render it. This, like any engineering decision has pros and cons. There are trade-offs and compromises.

On the downside, Qt 3D requires a bit more domain-specific knowledge than some other Qt modules and you may reach the point of having to customise materials or rendering algorithms via the frame graph sooner than you may have hoped. On the upside, you have the ability to fully control the rendering algorithm and map data through to your completely custom shader pipeline.

Qt Quick 3D tries to overcome the downsides by making adding simple 3D content to your Qt Quick 2 applications easier. Again, this comes with pros and cons. Yes, it will be easier to get going with 3D content in your applications, but if Qt Quick 3D doesn’t provide what you want by setting some options, then you are on your own again.

If you want to show a 3D model using one of the built-in materials with some 2D content overlaid, then Qt Quick 3D may well be fine for you. However, if you want to do more complex things such as custom reflections that involve messing with the stencil buffer to properly clip the upside down objects used to fake the reflections, then you may find yourself writing custom 3D rendering code or switching to another framework such as Qt 3D.

Likewise, if you want to do deferred rendering or stencil shadows or volume rendering or one of a thousand other techniques. Don’t get me wrong, there is a need for Qt Quick 3D, but there is still also a place for Qt 3D and that place is:

for more complex use cases as described above;

for people that are happy to roll their sleeves up and get their hands dirty;

for people who want to use a more complete C++ API instead of or in addition to QML

for those who cannot use GPL or commercial licensing.

There is also another option to simplify the process of getting 3D content into your application. One that uses and exposes the power of Qt 3D but is very simple to use. At KDAB we have built Kuesa () which has a runtime that sits on top of Qt 3D and allows you to easily import any glTF 2 () file with ease. We also have exporters to help transition content from designers in Blender and 3DS Max into C++/QML applications powered by Kuesa.

Kuesa Runtime 1.1 will shortly be released or you can try the current version from GitHub now. This will feature full glTF 2 compliance including morph target animations, skeletal animations and PBR metal-rough materials. It will allow you to load a wide range of models available on the Internet and to very easily introspect them and control pieces of them from your application data.

There is partial support built into Qt 3D thanks to an external contribution but reaching full compliance with the glTF 2 specification was quite a major undertaking and required some different approaches to the materials bundled with Qt3DExtras.

What is the Plan for Qt 3D?

First off let me make it very clear that Qt 3D is not going away. We at KDAB are using it for Kuesa and other projects and others out there in the wild are using Qt 3D in some very big commercial applications.

We know that there are a number of performance issues with Qt 3D and we are working hard on these already as you may have seen by the flurry of patches to gerrit. Let’s describe briefly what is going on and what is planned for the future.

Threading Architecture

Qt 3D was designed from the outset to be multi-threaded. This is usually a good thing, but it can make things trickier with the added level of asynchronicity. On some low-end embedded hardware with less than ideal memory allocators or semaphore implementations, the threading can actually get in the way.

To combat some of this we have removed the so-called Aspect Thread for Qt 5.14. There is still a thread pool for horizontal scaling of task processing, but this can be controlled via an environment variable.

Remove FBO in Common Case

When performing the common case of rendering some 3D content to the screen and then overlaying some 2D UI over the top, Qt 3D has to date gone via a framebuffer object (FBO). The way this works is that the 3D content would first be rendered into a colour texture (and depth texture) attached to an FBO which is what OpenGL requires for setting up render targets. This colour texture would then be drawn by Qt Quick by applying it to a simple quad which then gets composited with the rest of the Qt Quick 2 scene.

That is fine on desktop and many devices. Some devices, however, have very poor implementations of FBOs which kills the performance in this case. If the 3D content is strictly an underlay and is using a simple rendering method (typically forward rendering) then we can optimise this and avoid the FBO code path entirely. In this situation we can tell Qt 3D to draw directly to the screen since we know all Qt Quick content will be placed above it. To enable this optimisation, use the new compositingModeproperty on Scene3D(new in Qt 5.14) and set it to Underlay. A big thank you to Giuseppe D’Angelo for implementing this and to Paul Lemire for integrating it.

Change Notification System

The third large area of changes, and one which is still ongoing, is altering how we send/receive property changes between the frontend and backend of the Qt 3D aspects. Until now this has been implemented via passing around event-like packets of data for each property change. In large scenes with thousands of entities and with many animating properties this was bogging down and becoming a performance bottleneck.

After considering various options, KDAB engineers Mike Krus and Paul Lemire have been working very hard to re-engineer this important subsystem to improve performance. The upcoming mechanism is based on directly synchronising the frontend and backend objects when they become dirty. This allows all properties on an object to be updated in one go rather than one property per call. Current benchmarks are showing this to give a 200-300% speedup in the property change distribution in large scenes (thousands of entities).

If all goes well, these changes should land in a future Qt 5.14.x release.

Summary

In addition to the above, we are also introducing a lot of smaller improvements throughout the codebase. For example, traversal of the frame graph now only happens when something changes in the frame graph that materially affects the output. Work has also been done to only update uniforms when their values change.

We have made a lot of optimisations in Qt 3D in the 5.14 series and there are still more to come. These improvements nicely reduce the CPU overhead involved in rendering a frame as well as reducing he amount of thread synchronisation that can result in wasted time on some systems.

In the next article we will delve deeper into the research being done to improve Qt 3D for Qt 6 and how we hope to take advantage of modern graphics APIs to extract even more performance.