How to process a huge number of tiles in QML?

Imagine a huge rectangular grid filled with tiles. Individual tiles are not very complex, they are svg images containing a small number of shapes.

The number of different types of tiles in not very large, I estimate in the low hundreds. However, the grid can become very large, so the number of full tiles is huge (at least tens of thousands, and maybe more).

I should be able to smoothly scroll the grid both horizontally and vertically, as well as smoothly scale it. I should also be able to move to a certain position.

It would be nice if I could fill it asynchronously, first the elements that are actually visible, and then the rest. This means that the table processing class, in which I must first add rows and columns in the loop, will not be the best solution, because the starting position is not necessarily in the upper left corner.

Scaling is simply achieved due to the presence of all the width and height properties of the elements inside the tile, indicated as a multiple of the scaling factor. svg should not be a problem, since the number of different images is small, it should be cached. In the unlikely event svg became a bottleneck, I could just use different png sets in different resolutions.

I tried (or reviewed) the following approaches:

  • Using the SameGame Example methods , dynamically creating QML objects ( Component.createObject ). This works if the number of objects is small, but very slow with a large number of objects. Even if the objects are completely empty, this method takes a very long time .

  • Using Repeater inside a Flickable . Flickable contains a Grid , which is populated by a Repater . Grid , of course, is huge. This method is faster than creating objects dynamically, but is still inefficient as the number of tiles grows. The QML engine tracks every element, even those that are not visible. Scaling is also rather slow, since the properties of each element are recalculated, and not just visible.

  • Using GridView . It looks like the perfect solution at first glance. GridView inherits Flickable , and also only renders content that is within visibility. Even a test case with millions of svg images is fast enough, and it smoothly scrolls and changes. There is only one problem: GridView is only available horizontally or vertically, but not both. Since 2012, there has been a request for this , but it is still ignored.

  • Using QGraphicsView directly. It is able to display, scroll and scale the required number of elements, but it is not QML-based. The rest of my GUI is in QML, and I only read horrible stories about combining QML and QGraphicsView . I have never seen any reasonable examples of this.

What other solutions exist? Some awful hack of using Javascript to add and remove rows and columns of a simple GridLayout (which is only a couple of rows and columns larger than the visible area) when it moves to Flickable ? Or just embed the OpenGL window and draw everything manually?

Hope this should not be an impossible task. There were strategic games written over 20 years ago for DOS and Windows 95 that could handle this number of fragments, as well as have textures and animations.

+7
qt graphics qml
source share
1 answer

Yes, Qt very well ignores community advice for many years, even if they are extremely useful, are considered important and, as it turned out, will be the most voted, such as zip support .

I personally would not β€œfix” the GridView , but rather implement something from scratch, which meets my specific requirements, in C ++, so it’s fast and efficient. And it will be very easy if your tiles are uniform squares, and it seems that you can get away from this, even if the actual images inside are not square. This will allow you to easily determine your positions programmatically, as well as determine the upper left corner tile, the number of tiles per line and the step for subsequent lines. Then, when the visibility rectangle moves you, you iterate over the container and signal to create QML elements for those who enter visibility. Easy peasy.

You do not need anything fantastic, just inherit a QObject , register the type in QML, then go and fill it with an internal β€œmodel”. You definitely do not want to have all the objects in memory, even if the scene graph is smart enough not to display them, it will still process them, I suspect that your drop in FPS is not a GPU product, but a CPU bottleneck,

The actual mesh object can generate creation and destruction signals with their data Q_SIGNAL void create(x, y, imgPath); , so you bind custom handlers on the QML side, which will give you flexibility and ease of use, for example, it is easy to specify the "delegate" object, it will be more elegant than doing the actual creation / destruction in C ++. You can use QML-side bindings for several elements that are visible for tracking when they exit the screen to self-destruct, which minimizes complexity since you do not have to track all the "live" objects.

 Component { id: objComponent Image { property bool isVisible: { is in grid.visibleRect ??? } onIsVisibleChanged: if (!isVisible) destroy() } } MyGrid { id: grid contentX: flickable.contentX contentY: flickable.contentY onCreate: objComponent.createObject(flickable.contentItem, {"x" : x, "y" : y, "source" : imgPath}) } Flickable { id: flickable contentWidth: grid.contentWidth contentHeight: grid.contentHeight } 

Usually, when a user has a question that is important enough to offer generosity, I would create working code, but unfortunately I'm too busy right now. The concept is quite simple, although it should not be too complicated to implement.

+1
source share

All Articles