In the following QML, the only dynamic part is the blinking rectangle. While it has no relation to the generated items, the blinking rectangle causes a heavy load and slows down the system (e.g. 100% CPU load on i.MX6 processor I am using), even when there is no overlap/binding between it and other items. Removing the Repeater solves the issue and rectangle smoothly blinks.
import QtQuick 2.3
Rectangle {
id: root
anchors.fill: parent
Repeater {
model: 10000
delegate: Rectangle {
width: 5
height: 5
x: (index % 200)*6
y: 50 + Math.floor(index / 200)*6
color: "blue"
border.color: "black"
}
}
Rectangle {
property bool blinker: false
width: 20
height: 20
color: blinker ? "green" : "red"
Timer {
running: true
interval: 100
repeat: true
onTriggered: { parent.blinker = !parent.blinker }
}
}
}
Here is the output (the red rectangle will blink in the actual application):
The model: 10000
parameter of Repeater may need to be set to a higher value in the case you have a better specification and don't experience slow down. The code is tested on Qt 5.3.2 and Qt 5.5.0 and the problem was present in both.
I have a fewer number of models (~100) in my actual application, but with more complex delegate. Therefore, the CPU (GPU?) usage depends on the complexity of the delegate + number of model items in the Repeater.
Why having a high number of items (or complex items) generated by Repeater affect the performance of application while they have no relation/overlap with the other dynamic object(s)?
Update 1
I've replaced Repeater
with the following javascript code to generate the same number of objects with the same properties:
Component.onCompleted: {
var objstr = 'import QtQuick 2.0;Rectangle{id:sample;width:5; height:5;color:"blue";border.color: "black"}';
for(var i=0;i<200;i++) {
for(var j=0;j<50;j++) {
var obj = Qt.createQmlObject(objstr,root);
obj.x = i * 6
obj.y = 50 + j*6
}
}
}
But there performance issue was still present.
Update 2
I've done some examinations based on this article.
QSG_RENDERER_DEBUG=render
Setting this flag outputs some debugging information about rendering and batching. The output for the test application
isaac@ubuntu:~$ QSG_RENDERER_DEBUG=render ./qml-test
QML debugging is enabled. Only use this in a safe environment.
Batch thresholds: nodes: 64 vertices: 1024
Using buffer strategy: static
Renderer::render() QSGAbstractRenderer(0x93b9570) "rebuild: full"
Rendering:
-> Opaque: 14002 nodes in 2 batches...
-> Alpha: 0 nodes in 0 batches...
- 0x8f0a698 [ upload] [ clip] [opaque] [ merged] Nodes: 14000 Vertices: 168000 Indices: 224000 root: 0xb3e2a90 sets: 3
- 0x8f0b310 [ upload] [noclip] [opaque] [ merged] Nodes: 2 Vertices: 8 Indices: 12 root: 0x0
Renderer::render() QSGAbstractRenderer(0x93b9570) "rebuild: none"
Rendering:
-> Opaque: 14002 nodes in 2 batches...
-> Alpha: 0 nodes in 0 batches...
- 0x8f0a698 [retained] [ clip] [opaque] [ merged] Nodes: 14000 Vertices: 168000 Indices: 224000 root: 0xb3e2a90 sets: 3
- 0x8f0b310 [retained] [noclip] [opaque] [ merged] Nodes: 2 Vertices: 8 Indices: 12 root: 0x0
Renderer::render() QSGAbstractRenderer(0x93b9570) "rebuild: none"
This tells that items are batched in 2 group; one with 14000 nodes and one with 2 nodes. This seems to be what we expect.
QSG_VISUALIZE=batches flag
This switch visualizes the batches on the UI. Running this shows a solid color covering the whole UI. This means the blinking rectangle and the small rectangles are being rendered in one batch:
Setting clip: true
didn't help to force separating the batches. By setting opacity: 0.5
for blinking rectangle, I finally succeeded to force QML engine to put it into another batch:
Interestingly, the blinking was still affected and slowed down by the high number of small rectangles!
QSG_RENDER_TIMING=1
The last flag I tried was QSG_RENDER_TIMING
which report some timing information for rendering. Based on the output, the actual time spent is for render
in the render loop. Based on the Qt documentation, render
time is
Total time spent rendering the frame, including preparing and uploading all the necessary data to the GPU. This is the gross render time. Do not confuse it with the net Render Render time below.
but this wasn't helpful to me. So far, I haven't be able to find the root cause of this issue.
isaac@ubuntu:~$ QSG_RENDER_TIMING=1 ./qml-test
QML debugging is enabled. Only use this in a safe environment.
qt.scenegraph.time.compilation: shader compiled in 3ms
qt.scenegraph.time.renderer: time in renderer: total=27ms, preprocess=0, updates=5, binding=0, rendering=21
qt.scenegraph.time.renderloop: Frame rendered with 'basic' renderloop in 107ms, polish=0, sync=65, render=27, swap=1, frameDelta=0
qt.scenegraph.time.renderer: time in renderer: total=1ms, preprocess=0, updates=0, binding=0, rendering=1
qt.scenegraph.time.renderloop: Frame rendered with 'basic' renderloop in 1ms, polish=0, sync=0, render=1, swap=0, frameDelta=2
qt.scenegraph.time.renderer: time in renderer: total=8ms, preprocess=0, updates=0, binding=0, rendering=8
qt.scenegraph.time.renderloop: Frame rendered with 'basic' renderloop in 255ms, polish=0, sync=0, render=8, swap=24, frameDelta=255
qt.scenegraph.time.renderer: time in renderer: total=1ms, preprocess=0, updates=0, binding=0, rendering=1
qt.scenegraph.time.renderloop: Frame rendered with 'basic' renderloop in 290ms, polish=0, sync=0, render=1, swap=28, frameDelta=297
qt.scenegraph.time.renderer: time in renderer: total=0ms, preprocess=0, updates=0, binding=0, rendering=0
qt.scenegraph.time.renderloop: Frame rendered with 'basic' renderloop in 296ms, polish=0, sync=0, render=0, swap=29, frameDelta=303
qt.scenegraph.time.renderer: time in renderer: total=298ms, preprocess=0, updates=0, binding=0, rendering=298
qt.scenegraph.time.renderloop: Frame rendered with 'basic' renderloop in 300ms, polish=0, sync=0, render=298, swap=0, frameDelta=306
qt.scenegraph.time.renderer: time in renderer: total=592ms, preprocess=0, updates=0, binding=0, rendering=592
qt.scenegraph.time.renderloop: Frame rendered with 'basic' renderloop in 593ms, polish=0, sync=0, render=592, swap=0, frameDelta=600
qt.scenegraph.time.renderer: time in renderer: total=292ms, preprocess=0, updates=0, binding=0, rendering=292
qt.scenegraph.time.renderloop: Frame rendered with 'basic' renderloop in 298ms, polish=0, sync=0, render=295, swap=0, frameDelta=305
qt.scenegraph.time.renderer: time in renderer: total=286ms, preprocess=0, updates=0, binding=0, rendering=286
qt.scenegraph.time.renderloop: Frame rendered with 'basic' renderloop in 291ms, polish=0, sync=0, render=286, swap=0, frameDelta=298
qt.scenegraph.time.renderer: time in renderer: total=291ms, preprocess=0, updates=0, binding=0, rendering=291
qt.scenegraph.time.renderloop: Frame rendered with 'basic' renderloop in 296ms, polish=0, sync=0, render=294, swap=0, frameDelta=305
qt.scenegraph.time.renderer: time in renderer: total=286ms, preprocess=0, updates=0, binding=0, rendering=286
qt.scenegraph.time.renderloop: Frame rendered with 'basic' renderloop in 292ms, polish=0, sync=0, render=286, swap=0, frameDelta=298
qt.scenegraph.time.renderer: time in renderer: total=290ms, preprocess=0, updates=0, binding=0, rendering=290
qt.scenegraph.time.renderloop: Frame rendered with 'basic' renderloop in 295ms, polish=0, sync=0, render=291, swap=0, frameDelta=301
qt.scenegraph.time.renderer: time in renderer: total=297ms, preprocess=0, updates=0, binding=0, rendering=297
qt.scenegraph.time.renderloop: Frame rendered with 'basic' renderloop in 302ms, polish=0, sync=0, render=298, swap=0, frameDelta=310
qt.scenegraph.time.renderer: time in renderer: total=290ms, preprocess=0, updates=0, binding=0, rendering=290
qt.scenegraph.time.renderloop: Frame rendered with 'basic' renderloop in 293ms, polish=0, sync=0, render=290, swap=0, frameDelta=316