Important: Please read the Qt Code of Conduct -

What causes QSGRenderThread to have constant CPU usage on a static scene?

  • Hi.
    I'm experiencing weird occurences of constant CPU usage by the QSGRenderThread even when the scene is static. It uses about 22~25% of one of the 4 cores.

    It seems to happen after the first scene update following the presentation of the default stack item to the user.

    It occurs more often than not, but not always, and sometimes goes away for some reason, maybe by pushing and popping some other item on the StackView.

    I tried to confirm that the QSG is not constantly rendering by adding the following debug messages in my main.qml:

    Connections {
        target: mainWindow   
        function onBeforeSynchronizing() { console.debug("onBeforeSynchronizing") }
        function onAfterSynchronizing() { console.debug("onAfterSynchronizing") }

    But that seems to work as expected: the debug messages are only printed when the scene changes.

    What is happening here?
    I found the old bug report about this situation, but got none the wiser, as it was closed and marked as solved with no explanation.

    Thanks for any insights you may provide on how to tackle this problem ~

  • Lifetime Qt Champion


    Sorry I do not have an answer for that but you might want to give more information about your hardware and Qt version.

  • Thanks, @SGaist
    I have isolated the problem.
    As for:

    you might want to give more information about your hardware and Qt version.

    It is on Pi OS Lite Linux raspberrypi 5.4.72-v7l+ on Pi 4B with Qt 5.15.2 but also can be reproduced on Debian Buster:

    Linux buster 4.19.0-9-amd64 #1 SMP Debian 4.19.118-2 (2020-04-29) x86_64 GNU/Linux

    The problem lies with the use of BusyIndicator when the interval between setting running to true then to false again is too short.

    The following minimal example reproduces the problem, both on my Pi and my Debian Buster VM build host (in my app, the running property is controlled by the status of a FolderListModel):

    import QtQuick 2.15
    import QtQuick.Controls 2.15
    import QtQuick.Window 2.15
    Window {
        id: root
        visible: true
        property int winWidth: 640
        property int winHeight: 480
        width: winWidth
        height: winHeight
        title: qsTr("Minimal QML")
        Button {
            width: 300
            height: 100
            anchors.centerIn: parent
            text: "Start/Stop"
            font.pixelSize: 48
            onClicked: {
                //Forcing running to false only does work to stop
                //the high cpu usage. It must be started/stopped here.
                busyIndicator.running = !busyIndicator.running
        BusyIndicator {
            id: busyIndicator
            width: 170
            height: 170
            running: false
            anchors.centerIn: parent
            anchors.verticalCenterOffset: -35
            clip: false
            property string color: "blue"
            palette.dark: color
            onRunningChanged: {
                busyText.text = running ? "running" : ""
                console.debug("BusyIndicator: ", running)
            Text {
                id: busyText
                visible: busyIndicator.running
                font.pixelSize: 64
                color: busyIndicator.color
                anchors.horizontalCenter: parent.horizontalCenter
                anchors.topMargin: 10
        Timer {
            id: timer
            interval: 5
            repeat: false
            onTriggered: busyIndicator.running = false
        Component.onCompleted: {
            busyIndicator.running = true

    At program startup, we get the following debug output:

    qml: BusyIndicator:  true
    qml: BusyIndicator:  false

    But from then on, QGSRenderThread cpu usage stays high:


    The only way to stop it is to press the button twice to start/stop it;
    setting only running = false upon button press does not work to stop the high CPU usage.

    I suspect that in the above use case non-interactive case, the interval between the start/stop states is too short to display anything, and somehow messes up the state of the BusyIndicator animation or so. It only seems to work correctly if the animation has effectively been shown on-screen for some time, as achieved with the Start/Stop button.

    Setting the timer interval to e.g. 50 briefly flashes the busy indicator, and then QSGRenderThread reverts back to 0 activity.

    EDIT: Apparently, this is a reported bug. ~ "BusyIndicator"

    If I use a custom BusyIndicator such as the following, the problem does not occur, even for
    running intervals as short as 1ms.

    //This is essentially what the Controls 1 version of BusyIndicator does:
    import QtQuick 2.15
    Item {
        id: root   
        property bool running: true
        Image {
            id: image
            anchors.fill: parent
            opacity: root.running ? 1 : 0
            Behavior on opacity { OpacityAnimator { duration: 250 } }
            source: "busyindicator.png"
            RotationAnimator on rotation {
                from: 0
                to: 360
                duration: 6000
                loops: Animation.Infinite
                running: image.visible && (root.running || image.opacity > 0)

Log in to reply