QML Performance when updating an element in the presence of many non-overlapping elements

In the next QML, the only dynamic part is the blinking rectangle. Although it is not related to the generated elements, a flashing rectangle causes a large load and slows down the system (for example, 100% CPU utilization on the i.MX6 processor that I use), even if there is no overlap / snapping between it and other Items. Removing the repeater solves the problem and the rectangle flashes smoothly.

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 result (the red rectangle will flash in the real application): enter image description here

The model: 10000 parameter of the Repeater parameter may need to be set higher if you have a higher specification and you cannot slow down. The code is tested on Qt 5.3.2 and Qt 5.5.0, and a problem was present in both.

I have fewer models (~ 100) in my real application, but with a more complex delegate. Therefore, the use of a CPU (GPU?) Depends on the complexity of the delegate + the number of model elements in the Repeater.

Why does the presence of a large number of elements (or complex elements) created by Repeater affect the performance of the application until they have a relationship / overlap with another dynamic object?

Update 1

I replaced Repeater 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 the performance issue was still present.

Update 2

I conducted several exams based on this article .

QSG_RENDERER_DEBUG = renderings

Setting this flag displays some debugging information about rendering and batch processing. Exit for 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 suggests that the elements are grouped into 2 groups; one with 14,000 nodes and one with two nodes. This seems to be what we expect.

QSG_VISUALIZE = batch flag

This switch renders batches in the user interface. Doing this shows a solid color spanning the entire user interface. This means that the flashing rectangle and the small rectangles are displayed in the same batch:

enter image description here

Setting clip: true did not help separate the parties. By setting opacity: 0.5 for the blinking rectangle, I finally managed to get the QML engine to put it in another batch:

enter image description here

Interestingly, the blinking was still affected and slowed down by a large number of small rectangles!

QSG_RENDER_TIMING = 1

The last flag I checked was QSG_RENDER_TIMING , which reports some temporary information for rendering. Based on the result, the actual elapsed time for render in the render loop. Based on Qt render documentation time

Total time spent rendering a frame, including preparing and loading all the necessary data on the GPU. This is a gross render time. Do not confuse it with the render time in the render mode below.

but it didn’t help me. So far, I have not been able to find the root cause of this problem.

 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 
+5
source share
1 answer

This is an old question, but it seems that there is no real solution here, so I will do my best to call back with useful bits and parts.

So, you are definitely partly on the right track, looking at dosing, a great start. I suppose the reason you didn't notice any effect from setting clip: true was because you might have installed it in the wrong place - you need to either set it to the bottom rectangle (containing the timer), or you need contain the repeater in something else that you can copy, for example:

 Item { anchors.fill: parent clip: true Repeater { ... } } 

This is because although Repeater inherits the Item type, this is a slightly special item. The children he created were born from the parent of the repeater, and not the repeater itself, so the repeater will have a clipping set, but there are no visual children who will use this clip in your case.

The ideal solution here would be to set clip: true on both something containing Repeater (as done above) and the bottom rectangle to ensure that neither of the two subtrees affects the performance of the other.

However, you noticed that this did not solve your problem directly, so we are moving from batch processing to other things.

Quick observation: I notice that you are using basic 'renderloop instead of' threaded 'one. Is there a reason for this? It won’t buy you a lot with the example you have here (since you don’t have a lot of binding ratings and no other application to talk about), but in the real world this should be a little better, so I would recommend trying it if it is possible.

Once you are done with this, you need to know that the QtQuick script will run with blocking vsync. Animations and everything else are tied to the vsync of your display. When you work at this level, you need to know how your graphical installation works, and pay particular attention to allowing you to do this.

So, now let's talk about the hardware of the image. I do not know exactly what your installation is on imx6, but I assume that you are using the Linux and Vivante drivers on fbdev and the eglfs QPA plugin from Qt. First, you should play FB_MULTI_BUFFER with the FB_MULTI_BUFFER environment variable to make sure you are tied to the display vsync (i.e. you probably want FB_MULTI_BUFFER=2 or FB_MULTI_BUFFER=3 ). I don’t know if it was installed automatically, but it wasn’t when I had to work in such a system.

Assuming you are using fbdev, the display waiting mechanism is ioctl. You want to look at your display driver in the kernel and see if it FBIO_WAITFORVSYNC ioctl, and compile Qt for use (grep qtbase for FBIO_WAITFORVSYNC - it should be somewhere in the eglfs platform plugin). You will also notice that it is “hidden” behind the environment variable: QT_QPA_EGLFS_FORCEVSYNC , so you will want export QT_QPA_EGLFS_FORCEVSYNC=1 after you have provided it to release this ioctl. While you are on it, you should check that FBIOGET_VSCREENINFO ioctl returns useful and correct information, since eglfs will use the returned information to determine the display refresh rate (see Q_refreshRateFromFb in the eglfs plugin).

After all, something can improve for you. If they do not, I can say that in a similar installation I ran into situations where there was no possibility of forced-throttle rendering (where FBIO_WAITFORVSYNC was actually unusable), which means that you remain to do it yourself. I do not know how universal this problem is, but it may well apply to you, therefore:

If you are in this situation, you can set the environment variable QT_QPA_UPDATE_IDLE_TIME=x to tell Qt to wait a minimum duration of at least x ms before drawing another frame, for example, export QT_QPA_UPDATE_IDLE_TIME=32 will wait 32 ms between frames at least, giving you approximately 30 FPS. You should be careful about this, though, since this is far from an ideal scenario, and it’s not quite what I would call widely “supported”.

+1
source

Source: https://habr.com/ru/post/1238860/


All Articles