When you want to draw a custom item in QtQuick, then you essentially have 3 options:

  • draw using OpenGL by using QQuickItem (or a built-in item)
  • draw using QPainter by using QQuickPaintedItem
  • draw using HTML5 like canvas by using the QtQuick Canvas item

One can read here and there that OpenGL is the fastest. QQuickPaintedItem is slow. And Canvas is really slow. But I couldn’t find any information on how fast or slow each solution is. So I decided to do a quick comparison, to get an idea of their performances. So I did a quick and simple rendering of many rectangles using each technology. And measured the frames per second.

The measurement was very simple like this in main.cpp:

    QQuickView view;
    int totalDuration = 0;
    int loops = 0;
    QElapsedTimer timer;
    timer.start();
    QObject::connect(&view, &QQuickView::beforeRendering, [&](){
        totalDuration += timer.elapsed();
        ++loops;
        if (totalDuration > 10*1000) {
            qDebug() << (1000.0 * loops) / totalDuration << "fps";
            totalDuration = 0;
            loops = 0;
        }
        timer.restart();
    });


And here the QML code and the code for the QQuickPaintedItem:

import QtQuick 2.6
import test 1.0
Rectangle {
    id: root
    width: 1200
    height: 1100
    property color col: "black"
    // 0 for QtQuick, 1 for paintedItem, 2 for canvas
    property int solution: 2
    property int rectangleSize: 512
    property int numberOfRectangles: 1000
    ColorAnimation on col {
        from: "red"
        to: "green"
        duration: 8000
        loops: 100
    }
    Repeater {
        model: root.numberOfRectangles
        Loader {
            width: root.rectangleSize
            height:root.rectangleSize
            opacity: 0.9 // even if overlap, drawing is needed
            sourceComponent: root.solution == 0 ? qtquickComponent :
                                  root.solution == 1 ? paintedComponent : canvasComponent
        }
    }
    // QtQuick OpenGL based solution
    Component {
        id: qtquickComponent
        Rectangle {
            color: root.col
        }
    }
    // QQuickPaintedItem based solution
    Component {
        id: paintedComponent
        PainterRectItem {
            color: root.col
        }
    }
    // Canvas based solution
    Component {
        id: canvasComponent
        Canvas {
            id: canvasRect
            Connections {
                target: root
                onColChanged: canvasRect.requestPaint();
            }
            onPaint: {
                var ctx = getContext("2d");
                ctx.fillStyle = root.col;
                ctx.fillRect(0, 0, width, height);
            }
        }
    }
}
class PainterRectItem : public QQuickPaintedItem
{
    Q_OBJECT
    Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
public:
    explicit PainterRectItem(QQuickItem* parent = nullptr);
    void paint(QPainter* painter) override
    {
        painter->save();
        painter->fillRect(QRectF(0.0, 0.0, width(), height()), m_color);
        painter->restore();
    }
    QColor color() const
    {
        return m_color;
    }
public slots:
    void setColor(QColor color)
    {
        m_color = color;
        emit colorChanged(m_color);
        update();
    }
signals:
    void colorChanged(QColor color);
private:
    QColor m_color;
};

The results for 10,000 rectangles of size 4×4 are:

  • QtQuick OpenGL 31 fps
  • QQuickPaintetItem 10.4 fps
  • Canvas 1.0 fps

The results for 10,000 rectangles of size 64×64 are:

  • QtQuick OpenGL 31 fps
  • QQuickPaintetItem 4.2 fps
  • Canvas 2.2 fps

The results for 1,000 rectangles of size 512×512 are:

  • QtQuick OpenGL 18 fps
  • QQuickPaintetItem 1.2 fps
  • Canvas 1.2 fps

Of course this is only a very limited test on my Linux X11 box using Qt 5.6. And real performance depends on many more things and can vary a lot. But at least you get an idea of the performance.

And one can see that QQuickPaintetItem heavily depends on the size of the item, as the item is drawn on a texture which needs to be transferred to the GPU.