aboutsummaryrefslogtreecommitdiffstats
path: root/doc/src/qml/performance.qdoc
blob: c5db0e0b2df4fc13ee1db5b4d05d0075fe4aa9bf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** GNU Free Documentation License
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of
** this file.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms
** and conditions contained in a signed written agreement between you
** and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

/*!
\page qtquick2-performance.html
\title QML Performance
\brief performance issues and suggestions

\tableofcontents

\section1 Timing Considerations

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.

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 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
\li Avoid using eval() if at all possible
\li Do not delete properties of objects
\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
relative to rect1:

\code
Rectangle {
    id: rect1
    x: 20
    width: 200; height: 200
}
Rectangle {
    id: rect2
    x: rect1.x
    y: rect1.y + rect1.height
    width: rect1.width - 20
    height: 200
}
\endcode

This is achieved more efficiently using anchors:

\code
Rectangle {
    id: rect1
    x: 20
    width: 200; height: 200
}
Rectangle {
    id: rect2
    height: 200
    anchors.left: rect1.left
    anchors.top: rect1.bottom
    anchors.right: rect1.right
    anchors.rightMargin: 20
}
\endcode

\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
\li Be as asynchronous as possible
\li Do all processing in a (low priority) worker thread
\li Batch up backend operations so that (potentially slow) I/O and IPC is minimised
\li Use a sliding slice window to cache results, whose parameters are determined with the help of profiling
\endlist

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.

\section2 ListModel

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.

\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
\li The fewer elements that are in a delegate, the faster they can be created, and thus
   the faster the view can be scrolled.
\li Keep the number of bindings in a delegate to a minimum; in particular, use anchors
   rather than bindings for relative positioning within a delegate.
\li 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.
\li Avoid using ShaderEffect elements within delegates.
\li Never enable clipping on a delegate.
\endlist

\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.

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

\section2 Particles

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.

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.

See the \l{Qt Quick Particle System Performance Guide} for more in-depth information.

\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}.

\section3 Using Loader

The Loader is an element which allows dynamic loading and unloading of components.

\list
\li Using the "active" property of a Loader, initialisation can be delayed until required.
\li Using the overloaded version of the "setSource()" function, initial property values can
   be supplied.
\li Setting the Loader \l {Loader::asynchronous}{asynchronous} property to true may also
   improve fluidity while a component is instantiated.
\endlist

\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.

Therefore, application developers should use the Row, Column, Grid, GridView and ListView
elements instead of manual layouts wherever possible.

*/