aboutsummaryrefslogtreecommitdiffstats
path: root/doc/src
diff options
context:
space:
mode:
Diffstat (limited to 'doc/src')
-rw-r--r--doc/src/declarative/qdeclarativeperformance.qdoc694
1 files changed, 645 insertions, 49 deletions
diff --git a/doc/src/declarative/qdeclarativeperformance.qdoc b/doc/src/declarative/qdeclarativeperformance.qdoc
index 12b345a3ff..a2e72ce156 100644
--- a/doc/src/declarative/qdeclarativeperformance.qdoc
+++ b/doc/src/declarative/qdeclarativeperformance.qdoc
@@ -30,17 +30,471 @@
\inqmlmodule QtQuick 2
\title QML Performance
-\section1 Opaque Items
+\tableofcontents
-Items hidden behind an opaque item incur a cost. If an item will be enitrely
-obscured by an opaque item, set its opacity to 0. One common example of
-this is when a "details" page is shown over the main application view.
+\section1 Timing Considerations
-\section1 Clipping
+As an application developer, you must strive to allow the rendering engine
+to achieve a consistent 60 frames-per-second refresh rate. 60 FPS means
+that there is approximately 16 milliseconds between each frame in which
+processing can be done, which includes the processing required to upload
+the draw primitives to the graphics hardware.
-\i clip is set to false by default. Enable clipping only when necessary.
+In practice, this means that the application developer should use asynchronous,
+event driven programming wherever possible, should use worker threads to do
+significant processing, should never manually spin the event loop, and should
+never spend more than a couple of milliseconds per frame within blocking functions.
+Failure to do so will result in skipped frames, which has a drastic effect on the
+user experience.
-\section1 Anchors vs. Binding
+\section1 Profiling
+
+The most important tip is: use the QML profiler included with Qt Creator. Knowing
+where time is spent in an application will allow you to focus on problem areas which
+actually exist, rather than problem areas which potentially exist. See the Qt Creator
+manual for more information on how to use the QML profiling tool.
+
+Determining which bindings are being run the most often, or which functions your
+application is spending the most time in, will allow you to decide whether you need
+to optimize the problem areas, or redesign some implementation details of your
+application so that the performance is improved. Attempting to optimize code without
+profiling is likely to result in very minor rather than significant performance
+improvements.
+
+\section1 JavaScript
+
+Most QML applications will have a large amount of JavaScript code in them, in the
+form of dynamic functions, signal handlers, and property binding expressions.
+This is not generally a problem (in fact, due to some optimizations in the QML engine
+(bindings compiler, etc) it can for some use-cases be faster than calling a C++ function)
+however care must be taken to ensure that unnecessary processing isn't triggered
+accidentally.
+
+\section2 Type-Conversion
+
+One major cost of using JavaScript is that in most cases when a property from a QML
+element is accessed, a JavaScript object with an external resource containing the
+underlying C++ data (or a reference to it) is created. In most cases, this is fairly
+inexpensive, but in others it can be quite expensive. One example of where it is
+expensive is assigning a C++ QVariantMap Q_PROPERTY to a QML "variant" property.
+Lists can also be expensive, although sequences of specific types (QList of int,
+qreal, bool, QString, and QUrl) should be inexpensive; other list types involve an
+expensive conversion cost (creating a new JavaScript Array, and adding new elements
+one by one, with per-element conversion from C++ type instance to JavaScript value).
+
+Converting between some basic property types (such as "string" and "url" properties)
+can also be expensive. Using the closest matching property type will avoid unnecessary
+conversion.
+
+If you must expose a QVariantMap to QML, use a "var" property rather than a "variant"
+property. In general, "property var" should be considered to be superior to
+"property variant" for every use-case in QtQuick 2.0 (note that "property variant"
+is marked as obsolete in the QtQuick 2.0 documentation), as it allows a true JavaScript
+reference to be stored (which can reduce the number of conversions required in certain
+expressions).
+
+\section2 Resolving Properties
+
+Property resolution takes time. While in some cases the result of a lookup can be
+cached and reused, it is always best to avoid doing unnecessary work altogether, if
+possible.
+
+In the following example, we have a block of code which is run often (in this case, it
+is the contents of an explicit loop; but it could be a commonly-evaluated binding expression,
+for example) and in it, we resolve the element with the "rect" id and its "color" property
+multiple times:
+
+\qml
+// bad.qml
+import QtQuick 2.0
+
+Item {
+ width: 400
+ height: 200
+ Rectangle {
+ id: rect
+ anchors.fill: parent
+ color: "blue"
+ }
+
+ function printValue(which, value) {
+ console.log(which + " = " + value);
+ }
+
+ Component.onCompleted: {
+ var t0 = new Date();
+ for (var i = 0; i < 1000; ++i) {
+ printValue("red", rect.color.r);
+ printValue("green", rect.color.g);
+ printValue("blue", rect.color.b);
+ printValue("alpha", rect.color.a);
+ }
+ var t1 = new Date();
+ console.log("Took: " + (t1.valueOf() - t0.valueOf()) + " milliseconds for 1000 iterations");
+ }
+}
+\endqml
+
+We could instead resolve the common base just once in the block:
+
+\qml
+// good.qml
+import QtQuick 2.0
+
+Item {
+ width: 400
+ height: 200
+ Rectangle {
+ id: rect
+ anchors.fill: parent
+ color: "blue"
+ }
+
+ function printValue(which, value) {
+ console.log(which + " = " + value);
+ }
+
+ Component.onCompleted: {
+ var t0 = new Date();
+ for (var i = 0; i < 1000; ++i) {
+ var rectColor = rect.color; // resolve the common base.
+ printValue("red", rectColor.r);
+ printValue("green", rectColor.g);
+ printValue("blue", rectColor.b);
+ printValue("alpha", rectColor.a);
+ }
+ var t1 = new Date();
+ console.log("Took: " + (t1.valueOf() - t0.valueOf()) + " milliseconds for 1000 iterations");
+
+ }
+}
+\endqml
+
+Just this simple change results in a significant performance improvement.
+
+\section2 Property Bindings
+
+A property binding expression will be re-evaluated if any of the properties
+it references are changed. As such, binding expressions should be kept as
+simple as possible.
+
+If you have a loop where you do some processing, but only the final result
+of the processing is important, it is often better to update a temporary
+accumulator which you afterwards assign to the property you need to update,
+rather than incrementally updating the property itself, in order to avoid
+triggering re-evaluation of binding expressions during the intermediate
+stages of accumulation.
+
+The following contrived example illustrates this point:
+
+\qml
+// bad.qml
+import QtQuick 2.0
+
+Item {
+ id: root
+ width: 200
+ height: 200
+ property int accumulatedValue: 0
+
+ Text {
+ anchors.fill: parent
+ text: root.accumulatedValue.toString()
+ onTextChanged: console.log("text binding re-evaluated")
+ }
+
+ Component.onCompleted: {
+ var someData = [ 1, 2, 3, 4, 5, 20 ];
+ for (var i = 0; i < someData.length; ++i) {
+ accumulatedValue = accumulatedValue + someData[i];
+ }
+ }
+}
+\endqml
+
+The loop in the onCompleted handler causes the "text" property binding to
+be re-evaluated six times (which then results in any other property bindings
+which rely on the text value, as well as the onTextChanged signal handler,
+to be re-evaluated each time, and lays out the text for display each time).
+This is clearly unnecessary in this case, since we really only care about
+the final value of the accumulation.
+
+It could be rewritten as follows:
+
+\qml
+// good.qml
+import QtQuick 2.0
+
+Item {
+ id: root
+ width: 200
+ height: 200
+ property int accumulatedValue: 0
+
+ Text {
+ anchors.fill: parent
+ text: root.accumulatedValue.toString()
+ onTextChanged: console.log("text binding re-evaluated")
+ }
+
+ Component.onCompleted: {
+ var someData = [ 1, 2, 3, 4, 5, 20 ];
+ var temp = accumulatedValue;
+ for (var i = 0; i < someData.length; ++i) {
+ temp = temp + someData[i];
+ }
+ accumulatedValue = temp;
+ }
+}
+\endqml
+
+\section2 Sequence tips
+
+As mentioned earlier, some sequence types are fast (eg, QList<int>, QList<qreal>,
+QList<bool>, QList<QString>, QStringList and QList<QUrl>) while others will be
+much slower. Aside from using these types wherever possible instead of slower types,
+there are some other performance-related semantics you need to be aware of to achieve
+the best performance.
+
+Firstly, there are two different implementations for sequence types: one for where
+the sequence is a Q_PROPERTY of a QObject (we'll call this a reference sequence),
+and another for where the sequence is returned from a Q_INVOKABLE function of a
+QObject (we'll call this a copy sequence).
+
+A reference sequence is read and written via QMetaObject::property() and thus is read
+and written as a QVariant. This means that changing the value of any element in the
+sequence from JavaScript will result in three steps occurring: the complete sequence
+will be read from the QObject (as a QVariant, but then cast to a sequence of the correct
+type); the element at the specified index will be changed in that sequence; and the
+complete sequence will be written back to the QObject (as a QVariant).
+
+A copy sequence is far simpler as the actual sequence is stored in the JavaScript
+object's resource data, so no read/modify/write cycle occurs (instead, the resource
+data is modified directly).
+
+Therefore, writes to elements of a reference sequence will be much slower than writes
+to elements of a copy sequence. In fact, writing to a single element of an N-element
+reference sequence is equivalent in cost to assigning a N-element copy sequence to that
+reference sequence, so you're usually better off modifying a temporary copy sequence
+and then assigning the result to a reference sequence, during computation.
+
+Assume the existence (and prior registration into the "Qt.example 1.0" namespace) of the
+following C++ type:
+
+\code
+class SequenceTypeExample : public QQuickItem
+{
+ Q_OBJECT
+ Q_PROPERTY (QList<qreal> qrealListProperty READ qrealListProperty WRITE setQrealListProperty NOTIFY qrealListPropertyChanged)
+
+public:
+ SequenceTypeExample() : QQuickItem() { m_list << 1.1 << 2.2 << 3.3; }
+ ~SequenceTypeExample() {}
+
+ QList<qreal> qrealListProperty() const { return m_list; }
+ void setQrealListProperty(const QList<qreal> &list) { m_list = list; emit qrealListPropertyChanged(); }
+
+signals:
+ void qrealListPropertyChanged();
+
+private:
+ QList<qreal> m_list;
+};
+\endcode
+
+The following example writes to elements of a reference sequence in a
+tight loop, resulting in bad performance:
+
+\qml
+// bad.qml
+import QtQuick 2.0
+import Qt.example 1.0
+
+SequenceTypeExample {
+ id: root
+ width: 200
+ height: 200
+
+ Component.onCompleted: {
+ var t0 = new Date();
+ qrealListProperty.length = 100;
+ for (var i = 0; i < 500; ++i) {
+ for (var j = 0; j < 100; ++j) {
+ qrealListProperty[j] = j;
+ }
+ }
+ var t1 = new Date();
+ console.log("elapsed: " + (t1.valueOf() - t0.valueOf()) + " milliseconds");
+ }
+}
+\endqml
+
+The QObject property read and write in the inner loop caused by the
+\c{"qrealListProperty[j] = j"} expression makes this code very suboptimal. Instead,
+something functionally equivalent but much faster would be:
+
+\qml
+// good.qml
+import QtQuick 2.0
+import Qt.example 1.0
+
+SequenceTypeExample {
+ id: root
+ width: 200
+ height: 200
+
+ Component.onCompleted: {
+ var t0 = new Date();
+ var someData = [1.1, 2.2, 3.3]
+ someData.length = 100;
+ for (var i = 0; i < 500; ++i) {
+ for (var j = 0; j < 100; ++j) {
+ someData[j] = j;
+ }
+ qrealListProperty = someData;
+ }
+ var t1 = new Date();
+ console.log("elapsed: " + (t1.valueOf() - t0.valueOf()) + " milliseconds");
+ }
+}
+\endqml
+
+Secondly, a change signal for the property is emitted if any element in it changes.
+If you have many bindings to a particular element in a sequence property, it is better
+to create a dynamic property which is bound to that element, and use that dynamic
+property as the symbol in the binding expressions instead of the sequence element,
+as it will only cause re-evaluation of bindings if its value changes.
+
+This is an unusual use-case which most clients should never hit, but is worth being
+aware of, in case you find yourself doing something like this:
+
+\qml
+// bad.qml
+import QtQuick 2.0
+import Qt.example 1.0
+
+SequenceTypeExample {
+ id: root
+
+ property int firstBinding: qrealListProperty[1] + 10;
+ property int secondBinding: qrealListProperty[1] + 20;
+ property int thirdBinding: qrealListProperty[1] + 30;
+
+ Component.onCompleted: {
+ var t0 = new Date();
+ for (var i = 0; i < 1000; ++i) {
+ qrealListProperty[2] = i;
+ }
+ var t1 = new Date();
+ console.log("elapsed: " + (t1.valueOf() - t0.valueOf()) + " milliseconds");
+ }
+}
+\endqml
+
+Note that even though only the element at index 2 is modified in the loop, the three
+bindings will all be re-evaluated since the granularity of the change signal is that
+the entire property has changed. As such, adding an intermediate binding can
+sometimes be beneficial:
+
+\qml
+// good.qml
+import QtQuick 2.0
+import Qt.example 1.0
+
+SequenceTypeExample {
+ id: root
+
+ property int intermediateBinding: qrealListProperty[1]
+ property int firstBinding: intermediateBinding + 10;
+ property int secondBinding: intermediateBinding + 20;
+ property int thirdBinding: intermediateBinding + 30;
+
+ Component.onCompleted: {
+ var t0 = new Date();
+ for (var i = 0; i < 1000; ++i) {
+ qrealListProperty[2] = i;
+ }
+ var t1 = new Date();
+ console.log("elapsed: " + (t1.valueOf() - t0.valueOf()) + " milliseconds");
+ }
+}
+\endqml
+
+In the above example, only the intermediate binding will be re-evaluated each time,
+resulting in a significant performance increase.
+
+\section2 Value-Type tips
+
+Value-type properties (font, color, vector3d, etc) have similar QObject property
+and change notification semantics to sequence type properties. As such, the tips
+given above for sequences are also applicable for value-type properties. While
+they are usually less of a problem with value-types (since the number of
+sub-properties of a value-type is usually far less than the number of elements
+in a sequence), any increase in the number of bindings being re-evaluated needlessly
+will have a negative impact on performance.
+
+\section2 Other JavaScript Objects
+
+Different JavaScript engines provide different optimizations. The JavaScript engine
+which QtQuick 2 uses is optimized for object instantiation and property lookup, but
+the optimizations which it provides relies on certain criteria. If your application
+does not meet the criteria, the JavaScript engine falls back to a "slow-path" mode
+with much worse performance. As such, always try to ensure you meet the following
+criteria:
+
+\list
+\o Avoid using eval() if at all possible
+\o Do not delete properties of objects
+\o Try to maintain property-assignment order in different constructor functions
+\endlist
+
+\section1 Common Interface Elements
+
+\section2 Text Elements
+
+Calculating text layouts can be a slow operation. Consider using the \c PlainText
+format instead of \c StyledText wherever possible, as this reduces the amount of work
+required of the layout engine. If you cannot use \c PlainText (as you need to embed
+images, or use tags to specify ranges of characters to have certain formatting (bold,
+italic, etc) as opposed to the entire text) then you should use \c StyledText.
+
+You should only use \c AutoText if the text might be (but probably isn't)
+\c StyledText as this mode will incur a parsing cost. The \c RichText mode should
+not be used, as \c StyledText provides almost all of its features at a fraction of
+its cost.
+
+\section2 Images
+
+Images are a vital part of any user interface. Unfortunately, they are also a big
+source of problems due to the time it takes to load them, the amount of memory they
+consume, and the way in which they are used.
+
+\section3 Asynchronous Loading
+
+Images are often quite large, and so it is wise to ensure that loading an image doesn't
+block the UI thread. Set the "asynchronous" property of the QML Image element to
+\c true to enable asynchronous loading of images from the local file system (remote
+images are always loaded asynchronously) where this would not result in a negative impact
+upon the aesthetics of the user interface.
+
+Image elements with the "asynchronous" property set to \c true will load images in
+a low-priority worker thread.
+
+\section3 Explicit Source Size
+
+If your application loads a large image but displays it in a small-sized element, set
+the "sourceSize" property to the size of the element being rendered to ensure that the
+smaller-scaled version of the image is kept in memory, rather than the large one.
+
+Beware that changing the sourceSize will cause the image to be reloaded.
+
+\section3 Avoid Run-time Composition
+
+Also remember that you can avoid doing composition work at run-time by providing the
+pre-composed image resource with your application (e.g., providing elements with shadow
+effects).
+
+\section2 Position Elements With Anchors
It is more efficient to use anchors rather than bindings to position items
relative to each other. Consider this use of bindings to position rect2
@@ -79,67 +533,209 @@ Rectangle {
}
\endcode
-\section1 Images
+\section1 Models and Views
+
+Most applications will have at least one model feeding data to a view. There are
+some semantics which application developers need to be aware of, in order to achieve
+maximal performance.
+
+\section2 Custom C++ Models
+
+It is often desirable to write your own custom model in C++ for use with a view in
+QML. While the optimal implementation of any such model will depend heavily on the
+use-case it must fulfil, some general guidelines are as follows:
+
+\list
+\o Be as asynchronous as possible
+\o Do all processing in a (low priority) worker thread
+\o Batch up backend operations so that (potentially slow) I/O and IPC is minimised
+\o Use a sliding slice window to cache results, whose parameters are determined with the help of profiling
+\endlist
-Images consume a great deal of memory and may also be costly to load. In order
-to deal with large images efficiently it is recommended that the Image::sourceSize
-property be set to a size no greater than that necessary to render it. Beware that
-changing the sourceSize will cause the image to be reloaded.
+It is important to note that using a low-priority worker thread is recommended to
+minimise the risk of starving the GUI thread (which could result in worse perceived
+performance). Also, remember that synchronisation and locking mechanisms can be a
+significant cause of slow performance, and so care should be taken to avoid
+unnecessary locking.
-Images on the local filesystem are usually loaded synchronously. This is usually
-the desired behavior for user interface elements, however for large images that
-do not necessarily need to be visible immediately, set the Image::asynchronous
-property to true. This will load the image in a low priority thread.
+\section2 ListModel
-\section1 View Delegates
+QML provides a ListModel element which can be used to feed data to a ListView.
+It should suffice for most use-cases and be relatively performant so long as
+it is used correctly.
-Delegates must be created quickly as the view is flicked. There are two important
-aspects to maintaining a smooth view:
+\section3 Populate Within A Worker Thread
+ListModel elements can be populated in a (low priority) worker thread in JavaScript. The
+developer must explicitly call "sync()" on the ListModel from within the WorkerScript to
+have the changes synchronised to the main thread. See the WorkerScript documentation
+for more information.
+
+\section3 Don't Use Dynamic Roles
+
+The ListModel element in QtQuick 2.0 is much more performant than in QtQuick 1.0. The
+performance improvements mainly come from assumptions about the type of roles within each
+element in a given model - if the type doesn't change, the caching performance improves
+dramatically. If the type can change dynamically from element to element, this optimization
+becomes impossible, and the performance of the model will be an order of magnitude worse.
+
+Therefore, dynamic typing is disabled by default; the developer must specifically set
+the boolean "dynamicRoles" property of the model to enable dynamic typing (and suffer
+the attendant performance degradation). We recommend that you do not use dynamic typing
+if it is possible to redesign your application to avoid it.
+
+\section2 Views
+
+View delegates should be kept as simple as possible. Have just enough QML in the delegate
+to display the necessary information. Any additional functionality which is not immediately
+required (e.g., if it displays more information when clicked) should not be created until
+needed (see the upcoming section on lazy initialisation).
+
+The following list is a good summary of things to keep in mind when designing a delegate:
\list
-\o Small delegates - keep the amount of QML to a minimum. Have just enough
-QML in the delegate to display the necessary information. Any additional functionality
-that is only needed when the delegate is clicked, for example, should be created by
-a Loader as needed.
-\o Fast data access - ensure the data model is as fast as possible.
+\o The fewer elements that are in a delegate, the faster they can be created, and thus
+ the faster the view can be scrolled.
+\o Keep the number of bindings in a delegate to a minimum; in particular, use anchors
+ rather than bindings for relative positioning within a delegate.
+\o Set a cacheBuffer to allow asynchronous creation of delegates outside the visible area.
+ Be mindful that this creates additional delegates and therefore the size of the
+ cacheBuffer must be balanced against additional memory usage.
+\o Avoid using ShaderEffect elements within delegates.
+\o Never enable clipping on a delegate.
\endlist
-\section1 Image resources over composition
+\section1 Visual Effects
+
+QtQuick 2 includes several features which allow developers and designers to create
+exceptionally appealing user interfaces. Fluidity and dynamic transitions as well
+as visual effects can be used to great effect in an application, but some care must
+be taken when using some of the features in QML as they can have performance implications.
+
+\section2 Animations
+
+In general, animating a property will cause any bindings which reference that property
+to be re-evaluated. Usually, this is what is desired but in other cases it may be better
+to disable the binding prior to performing the animation, and then reassign the binding
+once the animation has completed.
+
+Avoid running JavaScript during animation. For example, running a complex JavaScript
+expression for each frame of an x property animation should be avoided.
-If possible, provide a single image resource, rather than using composition
-of a number of elements. For example, a frame with a shadow could be created using
-a Rectangle placed over an Image providing the shadow. It is more efficient to
-provide an image that includes the frame and the shadow.
+Developers should be especially careful using script animations, as these are run in the main
+thread (and therefore can cause frames to be skipped if they take too long to complete).
-\section1 Limit JavaScript
+\section2 Particles
-Avoid running JavaScript during animation. For example, running a complex
-JavaScript expression for each frame of an x property animation.
+The QtQuick 2.0 Particles module allows beautiful particle effects to be integrated
+seamlessly into user interfaces. However every platform has different graphics hardware
+capabilities, and the Particles module is unable to limit parameters to what your hardware
+can gracefully support. The more particles you attempt to render (and the larger they are),
+the faster your graphics hardware will need to be in order to render at 60 FPS. Affecting
+more particles requires a faster CPU. It is therefore important to test all
+particle effects on your target platform carefully, to calibrate the number and size of
+particles you can render at 60 FPS.
-\section1 Loading later
+It should be noted that a particle system can be disabled when not in use
+(e.g., on a non-visible element) to avoid doing unnecessary simulation.
-Startup time is influenced by the amount of QML that must be loaded. Breaking your
-application into components which can be loaded when needed will allow faster startup time.
-This allows better runtime memory management by unloading the components when no
-longer needed.
+See the \l{Qt Quick Particle System Performance Guide} for more in-depth information.
-This may be achieved by using either \l Loader or creating components
+\section2 Shaders
+
+Shaders written in GLSL allow for complex transformations and visual effects to be written,
+however they should be used with care. Using a ShaderEffectSource causes a scene to
+prerendered into an FBO before it can be drawn. This extra overhead is quite expensive.
+
+A ShaderEffect element can imply a ShaderEffectSource (and the indirect rendering costs
+associated with that) and also involves uploading a vertex and fragment shader program
+(which is then compiled into a GLSL shader). Each fragment shader runs once for every
+pixel of the scene, and so these should be kept as simple as possible.
+
+\section1 Controlling Element Lifetime
+
+By partitioning an application into simple, modular components, each contained in a single
+QML file, you can achieve faster application startup time and better control over memory
+usage, and reduce the number of active-but-invisible elements in your application.
+
+\section2 Lazy Initialisation
+
+The QML engine does some tricky things to try to ensure that loading and initialisation of
+components doesn't cause frames to be skipped, however there is no better way to reduce
+startup time than to avoid doing work you don't need to do, and delaying the work until
+it is necessary. This may be achieved by using either \l Loader or creating components
\l {Dynamic Object Management in QML}{dynamically}.
-\section1 Property Types
+\section3 Using Loader
-When possible use a specific type, rather than variant, when declaring properties.
+The Loader is an element which allows dynamic loading and unloading of components.
-\code
-Item {
- property variant foo: 10
- property real bar: 10
+\list
+\o Using the "active" property of a Loader, initialisation can be delayed until required.
+\o Using the overloaded version of the "setSource()" function, initial property values can
+ be supplied.
+\o Setting the Loader \l {Loader::asynchronous}{asynchronous} property to true may also
+ improve fluidity while a component is instantiated.
+\endlist
- x: foo * 2
- y: bar *3
-}
-\endcode
+\section3 Using Dynamic Creation
+
+Developers can use the Qt.createComponent() function to create a component dynamically at
+runtime from within JavaScript, and then call createObject() to instantiate it. Depending
+on the ownership semantics specified in the call, the developer may have to delete the
+created object manually. See \l {Dynamic Object Management in QML} for more information.
+
+\section2 Destroy Unused Elements
+
+Elements which are invisible because they are a child of a non-visible element (e.g., the
+second tab in a tab-widget, while the first tab is shown) should be initialised lazily in
+most cases, and deleted when no longer in use, to avoid the ongoing cost of leaving them
+active (e.g., rendering, animations, property binding evaluation, etc).
+
+An item loaded with a Loader element may be released by resetting the "source" or
+"sourceComponent" property of the Loader, while other items may be explicitly
+released by calling destroy() on them. In some cases, it may be necessary to
+leave the item active, in which case it should be made invisible at the very least.
+
+See the upcoming section on Rendering for more information on active but invisible elements.
+
+\section1 Rendering
+
+The scene graph used for rendering in QtQuick 2.0 allows highly dynamic, animated user
+interfaces to be rendered fluidly at 60 FPS. There are some things which can
+dramatically decrease rendering performance, however, and developers should be careful
+to avoid these pitfalls wherever possible.
+
+\section2 Clipping
+
+Clipping is disabled by default, and should only be enabled when required.
+
+Clipping is a visual effect, NOT an optimization. It increases (rather than reduces)
+complexity for the renderer. If clipping is enabled, an item will clip its own painting,
+as well as the painting of its children, to its bounding rectangle. This stops the renderer
+from being able to reorder the drawing order of elements freely, resulting in a sub-optimal
+best-case scene graph traversal.
+
+Clipping inside a delegate is especially bad and should be avoided at all costs.
+
+\section2 Over-drawing and Invisible Elements
+
+If you have elements which are totally covered by other (opaque) elements, it is best to
+set their "visible" property to \c false or they will be needlessly drawn.
+
+Similarly, elements which are invisible (e.g., the second tab in a tab widget, while the
+first tab is shown) but need to be initialised at startup time (e.g., if the cost of
+instantiating the second tab takes too long to be able to do it only when the tab is
+activated), should have their "visible" property set to \c false, in order to avoid the
+cost of drawing them (although as previously explained, they will still incur the cost of
+any animations or bindings evaluation since they are still active).
+
+\section2 Manual Layouts
+
+The scene graph renderer is able to batch up certain operations to minimise the number of
+OpenGL state changes required. However, this optimization is only possible for the
+built-in layout elements provided by QtQuick 2.0, and cannot be applied to manual layouts.
-bar is faster than foo.
+Therefore, application developers should use the Row, Column, Grid, GridView and ListView
+elements instead of manual layouts wherever possible.
*/