QML memory usage on big grid

只愿长相守 提交于 2020-08-08 05:14:52

问题


I have a minesweeper-style game developed as a way of practicing QML.

The issue I have is on the memory usage which grows quickly out of hands (>700 Mb) depending on the size of the grid. I made it sizeable up to 150*150 (bigger grid leads to crash as it's win32).

The grid is built like this (with bits from Expose 2D C++ Game Board to QML using QAbstractItemModel) (the image is used to show a "bomb.png", the text to show the number of mines) :

Flickable {
    id: flickable
    width: parent.width
    height: parent.height

    enabled: false
    Column {
        id: cols
        Repeater {
            id: colRepeater
            model: mapSize //defined by user
            Row {
                id: rows
                property int y_pos: index
                Repeater {
                    id: rowRepeater
                    model: mapSize
                    Tile{
                        id: tile
                        x_coord: index
                        y_coord: y_pos
                        ...
                    }
                }
            }
        }
    }

And here is my Tile object:

Rectangle {
    id: root
    ...
    Rectangle {
        id: tileDecoration
        visible: false
        anchors.centerIn: parent
        opacity:0
        Image{
            id: tileImage
            anchors.fill: parent
            visible: false
            source: ""
        }
        Text{
            id: minesText
            anchors.centerIn: parent
        }
    }
}

EDIT: My question is how can I improve memory usage ? For a Minesweeper I did not expect it to require that much. I also have the aim to test it on Android, so limited memory usage is a thing.


回答1:


When looking at QtQuick & QML performance, my key rule is generally to do is to do as little as possible. Don't create items and bindings that you don't necessarily need right now to show to the user.

I can't give you an exact number (because after all, there really isn't one - it's project and hardware dependent), but I would suggest that QtQuick is best suited to displaying a number in the single digit thousands of items at a time on screen. More than that, and things are going to start to get a little rough on resource usage and performance.

QML, for better or worse, is a very easy language to hide costs in by being very expressive. When working in it, you need to consider what happens under the hood when writing this code; each Item you create is a QQuickItem subclass that gets allocated, and each property you set is a C++ function call. For bindings, the JavaScript engine needs to evaluate that binding (evaluating other things it depends on, too), before it even makes that function call to set the property value.

Looking at your Flickable snippet, you have a Repeater of columns containing a Repeater of rows. You say that you allow up to 150x150 grid. Let's do some rough math on that number. For each tile, you have (at least) two Rectangles, an Image and a Text item, giving a total of 4 items per Tile.qml instance. In addition, you have a fixed cost of a Row and a Repeater for each.

This means that the total creation cost of a single row on your grid is: 2 + (4 * mapSize) items, giving you a total of 602 items per row. Then, you have that, multiplied by 150, giving you a total cost of 90,300 items to create that grid.

Each row has a number of bindings, too: I count 9 in tile (7 in Tile itself, excluding the "id" assignments which are a bit special, plus the two x & y coords), plus another two for the Row and Repeater bindings, that's 2 + (9 * mapSize) bindings per row -- 1352 bindings per row, 202,800 bindings for the whole grid.

All in all, this is a very large number of items and bindings to be using at a single time.

Each of these items has significant costs under the hood - for example, a hundred bytes here and there for each item itself, extra allocations for the bindings you create on each of them, allocations for the QSGNode instances the items create to actually get something on screen, and using a Repeater also means that you have a few extra allocations on the JavaScript heap for each delegate... It all can add up to a very large amount of memory, depending on how many things you are creating at once.

So, to directly answer your question, the simple answer is "don't create so much stuff". I'm not sure how you can achieve that, but here's a few possibilities:

  • You could incorporate more of the individual Tile into the Image for it, e.g. the Rectangles, and just have something like Image { Text { anchors.centerIn: parent } } as your tile
  • You could consider whether you could make use of GridView somehow to help you with this, but it's going to provide some constraints on your design.
  • If GridView does not suit your needs, I would suggest that a good step is write some kind of view item, responsible for creating and positioning Tile instances, such that you are only instantiating tiles for the part of the map that is being shown at any given point.

For the last option, it'd go something like this. You'd look into subclassing QQuickItem (on the C++ side), and have a Q_PROPERTY(QQmlComponent* tileDelegate READ tileDelegate WRITE setTileDelegate NOTIFY tileDelegateChanged). In the setTileDelegate setter, call QQuickItem::polish, and then, override QQuickItem::updatePolish and there, create (or just reposition) instances of the provided tileDelegate component, offset from the currently displayed position on screen, until your "GameViewThing" was completely covered with tiles again.

I'm a little vague here – sorry for that – but a full example would be quite a bit of code, so I hope I've given enough information & direction for you to be able to start out on your own.



来源:https://stackoverflow.com/questions/41609641/qml-memory-usage-on-big-grid

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!