3

What I would like to achieve is similar to a board game. There is a 100*100 grid, placed inside an Item which resides in a Flickable.

The individual rectangles of the "gameboard" are svg images, currently there are around 20 types of them, this might increase into the low hundreds.

As a benchmark test, I just tried filling the "world" with elements:

Component.onCompleted:
{
    var i,j;
    for (i=0; i<100; ++i) 
        for (j=0; j<100; ++j)
        {
            var type = (i+j) % 20;

            Qt.createQmlObject('import QtQuick 2.0; Image {source:"qrc:///icons/img/symbol'+type+'.svg";
                width: 32*scaling.factor;
                height: 32*scaling.factor;
                x:'+i*32+'*scaling.factor;
                y:'+j*32+'*scaling.factor}',
                mainScreen);
        }
}

This takes over 5 seconds on a moderately good PC.

However, after everything is loaded, it functions smoothly, both the scrolling and the zooming (scaling.factor is a float set by a slider or spinbox).

The svg images are very simple, they contain only a few basic shapes. If I try just loading a simple rectangle instead of the svg image

Qt.createQmlObject('import QtQuick 2.0; Rectangle {color
     : "red"; width: 32; height: 32; x:'+i*32+'; y:'+j*32+'}',
     mainScreen);

It still takes around 3 seconds. Even though there are no images, and there is no dynamic scaling.

I remember that 20 years ago, there were strategy games running on computers with single core, less than 400 MHz CPUs, and those games had much larger maps, more complicated tiles, with animations, pathfinding, etc. And we are not even talking about 3d.

How can a modern PC choke on displaying a few thousand very basic shapes? It shouldn't be the loading time from disk, as the elements are in a resource file, and even the test with rectangles instead of images was horribly slow.

Not keeping all of them displayed, and only loading the elements near the border of the visible screen is obviously a way to go, or it would be if I had a 10000*10000 map. I would expect 100*100 items should be able to be displayed at the same time, especially as on a screen with a good resolution most of them would be visible.

Did I miss something obvious, or should I forget Qt Quick completely and should I do everything manually in OpenGL or some 2d engine?

Edit:

As suggested by the comments, Qt.createQmlObject is very slow.

Therefore I tried

var component = Qt.createComponent("field.qml");
component.createObject(mainScreen, {"x": i*32, "y": j*32 });

Where field.qml contains only a single Rectangle{}, or, in a second test, just an empty Item{}. In both cases the 10000 iterations took more than 3 seconds. Adding the svg to field.qml only increased the time by 0.8 seconds.

By the way, aren't images supposed to be cached? Even if I have the same simple svg (which only contains 3 simple rectangles) repeated all over, it takes a lot of time.

Using a Repeater{} makes everything considerably faster. Empty Rectangles are rendered in approximately 0.2 to 0.3 seconds, the svg images in one second. (This method has, however, other drawbacks. I'll post an answer when I finished testing different approaches)

vsz
  • 4,811
  • 7
  • 41
  • 78
  • 1. Use profiler. 2. Create your own component. You call of `createQmlObject` is too heavy. It is necessary to create component and load correspoding image for 10000 times. – Dmitry Sazonov Aug 30 '16 at 11:14
  • 2
    That does a bytecode-compile each time the element added. And the elements are probably not sharing that code. Better do that with ListModel or a custom QML component. – Velkan Aug 30 '16 at 11:36
  • 2
    There are at least 4 ways to create dynamic objects: `Repeater`, `Qt.createQmlObject`, `Component.createObject` and C++. You use slowest from these. – folibis Aug 30 '16 at 11:36
  • The custom QML component led to a speed-up of 33%, that is still very slow. – vsz Aug 30 '16 at 12:13
  • Having a complete, minimal, reproducible example that we could run would be nice. – Mitch Aug 30 '16 at 12:16
  • 2
    You're rasterizing 10000 svg files! For sure that's going to be slow. – peppe Aug 30 '16 at 12:39
  • @peppe : that's not the bottleneck. If my Component only has a simple qml `Rectangle` in it instead of the svg, it is nearly that slow. (my svg files only contain a few basic shapes) – vsz Aug 30 '16 at 13:16
  • You have to profile it, then. Most likely you're parsing QML 10,000 times, and that's got to be slow. – Kuba hasn't forgotten Monica Aug 30 '16 at 13:17
  • @KubaOber : In that case the official qml examples aren't worth much, as they are also using `Component.createObject("somefile.qml")` in a loop. – vsz Aug 30 '16 at 13:21
  • 2
    Admittedly the documentation/examples could use lots of help. Reality trumps officiality. Nature cannot be fooled be epaulets. – Kuba hasn't forgotten Monica Aug 30 '16 at 13:32
  • Interesting. In my test the dynamic creation with JS has been faster than the one using a repeater by approx 16%. The slowest has been the creation from a string - I am using Qt5.3/QtQuick2.3 on Windows, 32bit with MingW – derM - not here for BOT dreams Aug 31 '16 at 08:09

1 Answers1

4

(Although it doesn't solve my specific problem perfectly, I'll post this as an answer because it fits the scope of the question and might be useful for others.)

Although the official examples use a dynamic creation of objects, that method is only useful if the number of objects is reasonably low. For a very large number of items, a ListView or a TableView has the advantage of only rendering the items which are actually inside the bounds of the view.

TableView
{
    anchors.fill: parent
    model: ListModel {}
    itemDelegate: MyField{}
    Component.onCompleted:
    {
        var i;
        for (i=0; i<10000; ++i)
        {
            model.append({});
        }
}

is orders of magnitude faster than Qt.createQmlObject or Component.createObject, and much easier to handle than a Repeater

vsz
  • 4,811
  • 7
  • 41
  • 78