13

Is there a "simple" way to show the FPS (frame rate) in a QML/c++ application. All animations and views are done in QML and the application logic is in c++.

I already tried setting QML_SHOW_FRAMERATE in Linux before starting the application but it didn't help:

export QML_SHOW_FRAMERATE=1

3 Answers 3

13

You have to create your own FPS QQuickItem (or QQuickPaintedItem) and register in your main.cpp to be available in your QML code.

Here an example.

class FPSText: public QQuickPaintedItem
{
    Q_OBJECT
    Q_PROPERTY(int fps READ fps NOTIFY fpsChanged)
public:
    FPSText(QQuickItem *parent = 0);
    ~FPSText();
    void paint(QPainter *);
    Q_INVOKABLE int fps()const;

signals:
    void fpsChanged(int);

private:
    void recalculateFPS();
    int _currentFPS;
    int _cacheCount;
    QVector<qint64> _times;
};

FPSText::FPSText(QQuickItem *parent): QQuickPaintedItem(parent), _currentFPS(0), _cacheCount(0)
{
    _times.clear();
    setFlag(QQuickItem::ItemHasContents);
}

FPSText::~FPSText()
{
}

void FPSText::recalculateFPS()
{
    qint64 currentTime = QDateTime::currentDateTime().toMSecsSinceEpoch();
    _times.push_back(currentTime);

    while (_times[0] < currentTime - 1000) {
        _times.pop_front();
    }

    int currentCount = _times.length();
    _currentFPS = (currentCount + _cacheCount) / 2;
    qDebug() << _currentFPS;

    if (currentCount != _cacheCount) fpsChanged(_currentFPS);

    _cacheCount = currentCount;
}

int FPSText::fps()const
{
    return _currentFPS;
}

void FPSText::paint(QPainter *painter)
{
    recalculateFPS();
    //qDebug() << __FUNCTION__;
    QBrush brush(Qt::yellow);

    painter->setBrush(brush);
    painter->setPen(Qt::NoPen);
    painter->setRenderHint(QPainter::Antialiasing);
    painter->drawRoundedRect(0, 0, boundingRect().width(), boundingRect().height(), 0, 0);
    update();
}

qml:

FPSText{
        id: fps_text
        x:0
        y: 0;
        width: 200
        height: 100
        Text {
                anchors.centerIn: parent
                text: fps_text.fps.toFixed(2)
            }
    }

You can get any other implementation in Internet with a quick search.

Sign up to request clarification or add additional context in comments.

4 Comments

thank you very much! This seems to work. Is the number shown representative of the fps of the whole view/application, or only of the painted rectangle? Could you maybe provide a quick explanation of how it works?
@luffy FPSText is managed by QML Scene Graph: doc.qt.io/qt-5/qtquick-visualcanvas-scenegraph.html, and the point on above code to scdule a redraw each frame is FPSText::update() doc.qt.io/qt-5/qquickpainteditem.html#update
Fix me if I'm wrong but.. This is potentially going to create an infinite loop with the consecuence of resource drain.
@forlayo yes, you can create more performant component using QQuickWindow:: beforeRendering() signal without doing anything in every frame. something like vikingsoftware.com/qtquick-custom-item-performance
8

QML FPS Counter, without affecting performance.

Project of QNanoPainter and others in qt-labs are using the refresh of an animation of a QML Item to create an FPS counter. It's so easy to being done, attached a project that uses this technique ( modified from QNanoPainter FPS counter ).

FpsItem code:

import QtQuick 2.0
import QtQuick.Window 2.2

Rectangle {
    id: root
    property int frameCounter: 0
    property int frameCounterAvg: 0
    property int counter: 0
    property int fps: 0
    property int fpsAvg: 0

    readonly property real dp: Screen.pixelDensity * 25.4/160

    color: "black"
    width:  childrenRect.width + 10*dp;
    height: childrenRect.height + 10*dp;

    Image {
        id: spinnerImage
        anchors.verticalCenter: parent.verticalCenter
        x: 4 * dp
        width: 36 * dp
        height: width
        source: "images/spinner.png"
        NumberAnimation on rotation {
            from:0
            to: 360
            duration: 800
            loops: Animation.Infinite
        }
        onRotationChanged: frameCounter++;
    }

    Text {
        anchors.left: spinnerImage.right
        anchors.leftMargin: 8 * dp
        anchors.verticalCenter: spinnerImage.verticalCenter
        color: "#c0c0c0"
        font.pixelSize: 18 * dp
        text: "Ø " + root.fpsAvg + " | " + root.fps + " fps"
    }

    Timer {
        interval: 2000
        repeat: true
        running: true
        onTriggered: {
            frameCounterAvg += frameCounter;
            root.fps = frameCounter/2;
            counter++;
            frameCounter = 0;
            if (counter >= 3) {
                root.fpsAvg = frameCounterAvg/(2*counter)
                frameCounterAvg = 0;
                counter = 0;
            }
        }
    }
}

Using it as:

import QtQuick 2.9
import QtQuick.Window 2.2

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    FpsItem {
        id: fpsItem
        anchors.centerIn: parent
    }

}

Comments

6

I studied to topic a bit. I typically used the approach described by @Miguel Angel, but I recently implemented a simpler one using the QQuickWindow::frameSwapped() signal. Code and info can be found here: https://github.com/carlonluca/lqtutils#lqtutils_uih. It is simple to integrate in another app.

I compared these two techniques with the one from @forlayo: the first two seem to always agree on the measurement, the other is typically different.

This is the comparison: https://youtu.be/p_y_kL85R4A.

I also wrote a blog post with some more info: https://thebugfreeblog.blogspot.com/2021/09/measure-framerate-qt.html

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.