[Development] Changes to the property system
lars.knoll at qt.io
Mon Apr 12 13:07:47 CEST 2021
As you all know, we’ve been doing a lot of work for Qt 6.0 to bring the concept of property bindings into Qt Core and make it accessible from C++ (https://www.qt.io/blog/property-bindings-in-qt-6). Unfortunately, we lacked the time to do extensive porting of existing properties to the new system before we released 6.0.
A lot of that porting effort has now happened or is ongoing for both 6.1 and 6.2. In general, the new property system does its job very well, but we did find a couple of items that required changing. Fortunately all of those changes can be done in a way that is binary compatible with 6.0.
The binding system in Qt 6.0 tries to evaluate bindings lazily, ie. when the property is being read. The advantage of such a system is that bindings would not get re-evaluated multiple times if several of the properties the binding depended upon got changed, leading in theory to less computations.
What we already found out before 6.0 times was that some of our existing properties could not easily be ported to a lazy binding system, as the property setters were executing code that triggered certain side effects. Thus we did include a private class called QObjectCompatProperty in 6.0 already that did evaluate bindings immediately, leading to a hybrid between immediate (eager) and lazy binding evaluations for the system.
During porting, we now found that
* A lot of existing properties (probably as much as 50%) require immediate binding evaluation for a straightforward port
* Changing those properties to be compatible with lazy binding evaluation requires in many cases a complete rewrite of the class in question, amounting to huge amounts of work and lots of potential regressions
* As many properties can not be lazy, there is a lot less benefit of lazy binding evaluation than we hoped for
* The Qt Quick architecture uses a rendering thread to decouple QML item changes and rendering. There is a sync point between both threads where the rendering thread reads out properties from the main thread to update itself. That sync point needs to be kept minimal to allow both threads to run as independent as possible. Lazy binding evaluation would cause the bindings to get evaluated during that sync period and from the wrong thread.
All of this lead us to revisit parts of the internal architecture of the new C++ binding system over the last two months. The conclusion we came up with was to remove the lazy binding evaluation and instead add a feature that would allow us to dynamically group property updates together and only update bindings once all property updates inside the group are done.
This system will allow us to avoid repeated binding evaluations for most cases where they appear today (e.g. when instantiating new components or when doing timed updates through animations). The advantage is that this will work for all properties, not only for part of them as with the mixed lazy/eager system that we have in Qt 6.0.
You can find those changes in https://codereview.qt-project.org/c/qt/qtbase/+/332557/34 and related patches. We’re currently working on merging those for Qt 6.2.
They should have little direct impact on anybody using C++ based property bindings and keep the main benefits we are getting from the new property system: A C++ API for bindings, faster binding execution compared to what we had in the QML engine in Qt 5.15 and the ability to use bindings without a QML engine in place.
More information about the Development