Wednesday, December 22, 2010

Ways to improve QML performance

It was recently brought to my attention that there are some very easy ways to improve the performance of QML. I thought I would summarize those in a post.

1. Use a different graphics system

Using a different graphics system than the native one will often give superior performance (especially on X11).

A recommended configuration for embedded X11-based devices is to use a raster graphics system with a QGLWidget for the viewport of your QDeclarativeView.

Here is how to do it:
QApplication a(argc, argv);
// Use a raster graphics system
QApplication::setGraphicsSystem("raster"); // You can also try "opengl" here
QDeclarativeView *view = new QDeclarativeView;
// Use a QGLWidget for the viewport
QGLFormat format = QGLFormat::defaultFormat();
// You can comment the next line if the graphical results are not acceptable 
format.setSampleBuffers(false);
QGLWidget *glWidget = new QGLWidget(format);
// Comment the following line if you get display problems 
// (usually when the top-level element is an Item and not a Rectangle)
glWidget->setAutoFillBackground(false);
view->setViewport(glWidget);
view->setSource("qml/main.qml");

For people using the QML viewer, it seems that it already uses the raster graphics system by default on X11 and Mac OS X. You can use the -opengl command line option to use a QGLWidget as the viewport for the view (not necessary on Mac OS X).


2. Use showFullScreen()

It is advised to use QDeclarativeView::showFullScreen() instead of QDeclarativeView::show() to avoid compositing overhead. This should improve the performance a little.

For people using the QML viewer, you can use the -fullscreen command line option.

3. Limit JavaScript

JavaScript code should be kept to a minimum (You can often use C++ instead, especially for the business logic).

You should definitely avoid running JavaScript during an animation (e.g. running a complex JS expression for each frame of an x property animation).

Simplify your JavaScript expressions as much as possible and write them inline if they fit on one line. QML is compiled to an optimized bytecode-like stream and the JavaScript expressions pass through an optimized evaluator for simple expressions.

4. Enable clipping only when necessary

Item.clip is set to false by default and you should enable it only when it is strictly required. If clipping is enabled, an item will clip its own painting, as well as the painting of its children, to its bounding rectangle.

5. View delegates

The flicking performance of a view is directly affected by the size of its delegates and by how fast the data model is. You should try to keep the QML code in your delegates as small as possible and use a Loader for any functionality that is not required straight away for displaying the data (e.g. only required after clicking the item).

The value for the cacheBuffer property of the view (ListView, GridView) can also be increased to improve the smoothness of the scrolling. This property determines whether delegates are retained outside the visible area of the view.

6. Images

Large images consume a lot of memory, it is advised to set the Image.sourceSize property to a size to a size no greater than necessary to render it. The image kept in memory will be resize to sourceSize in order to  space space.

If an image does not need to be displayed immediately, you can set the Image.asynchronous property to true. As a result, the image will be loaded in a low priority thread.

Enable Image.smooth only when necessary. This property causes the image to be smoothly filtered when scaled or transformed. Smooth filtering gives better visual quality, but is slower. Instead, make sure the source image has the same size as in the software so that it does not have to be scaled (Image.smooth will then have to visual or performance impact). If you really need to enable it, it is then advised to disable it whenever the image is animated (scaling artifacts are generally not noticeable when the image is animated).

It is also advised to 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.

7. Use anchors rather than bindings to position items relatively to each other

Consider this use of bindings to position rect2 relative to rect1:
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
 }

This is achieved more efficiently using anchors:
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
 }

Recommended reading
Please comment if you know of other ways to improve the performance of QML apps, especially on mobile devices.

5 comments:

  1. I tried to use your tip to run the qml with openGL, but the CPU consumption is almost 90...
    can you suggest why?

    tnx

    ReplyDelete
  2. @גולי: Well, you did not give much information and I have never experienced such issue. Are you sure that your device supports OpenGL? Is that during an animation? Make sure you use the latest Qt as Nokia is still debugging QML and improving its performance.

    ReplyDelete
  3. i build a main application that run a QML viewer. in the qml there are a few animation.
    when i set :
    QApplication::setGraphicsSystem("raster"); there are no errors, but when i set: QApplication::setGraphicsSystem("opengl");
    i got this errors:
    QGLFramebufferObject: Framebuffer incomplete attachment.
    Failed to create pixmap texture buffer of size QSize(88, 17) , falling back to raster paint engine.
    in both case the CPU consumption is up to 90%.
    i think i have the openGL support, because the Qt Demos for opengl are running(also with 90% CPU)...
    what can i do to check this out? and how can i create a OpenGL support on the device if i don't have it?

    thank you for your help.

    ReplyDelete
  4. I get these errors: undefined reference to `QGLFormat::defaultFormat()', QGLFormat::setSampleBuffers(bool)', `QGLWidget::QGLWidget(QGLFormat const&, QWidget*, QGLWidget const*, QFlags)' and`QGLFormat::~QGLFormat()'. What could be causing this?

    ReplyDelete
  5. when i use OpenGl backend, all antialising is fuc***d up, and some texts are not centered correctly, as well as some 1px gap at some places....
    i have forced highQualityAntialiasing, and SmoothPixmapTransform but it doesn't seems to have effect in openGl

    but at least it's far smoother...

    help please !

    ReplyDelete