简体   繁体   English

大网格上的QML内存使用情况

[英]QML memory usage on big grid

I have a minesweeper-style game developed as a way of practicing QML. 我开发了一种扫雷游戏,作为练习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. 我遇到的问题是取决于网格大小的,无法控制的内存使用量迅速增长(> 700 Mb)。 I made it sizeable up to 150*150 (bigger grid leads to crash as it's win32). 我将其调整为最大150 * 150(更大的网格会导致崩溃,因为它是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) : 网格是这样构建的( 使用QAbstractItemModelExpose 2D C ++游戏板到QML的位 )(图像用于显示“ bomb.png”,文本用于显示地雷数量):

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: 这是我的Tile对象:

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. 我还打算在Android上对其进行测试,因此有限的内存使用情况是一回事。

When looking at QtQuick & QML performance, my key rule is generally to do is to do as little as possible. 在查看QtQuick和QML性能时,我的主要原则通常是做得尽可能少。 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. 我无法提供确切的数字(毕竟,实际上并没有一个数字-它取决于项目和硬件),但是我建议QtQuick最适合以个位数显示数千个项目中的数字。屏幕上的时间。 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. 不论好坏,QML是一种非常容易表达的语言,它很容易隐藏成本。 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. 您创建的每个Item都是分配的QQuickItem子类,而您设置的每个属性都是一个C ++函数调用。 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. 对于绑定,JavaScript引擎甚至需要进行该函数调用来设置属性值之前,还需要评估该绑定(还评估其依赖的其他内容)。

Looking at your Flickable snippet, you have a Repeater of columns containing a Repeater of rows. 查看您的Flickable代码片段,您将获得一个列的重复器,其中包含行的重复器。 You say that you allow up to 150x150 grid. 您说您最多允许使用150x150的网格。 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. 对于每个图块,您(至少)有两个矩形,一个图像和一个文本项,每个Tile.qml实例总共提供4个项。 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. 这意味着网格上单行的总创建成本为: 2 + (4 * mapSize)项目,每行总计602个项目。 Then, you have that, multiplied by 150, giving you a total cost of 90,300 items to create that grid. 然后,将其乘以150, 得出创建该网格的总成本为90,300

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. 每行也有许多绑定:我在tile中算9(在Tile本身中算7,不包括有些特殊的“ id”分配,再加上两个x&y坐标),另外两个分别用于Row和Repeater绑定,即每行2 + (9 * mapSize)绑定-每行1352个绑定,整个网格的202,800个绑定

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. 这些项目中的每一项都具有很高的成本-例如,每个项目本身到处都是一百字节,为您在每个项目上创建的绑定分配了额外的分配,为这些项目创建以实际获得收益的QSGNode实例的分配屏幕,并且使用Repeater还意味着您在JavaScript堆上为每个委托分配了一些额外的分配...这全部可能会增加非常大的内存,具体取决于您一次创建的对象数量。

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, eg the Rectangles, and just have something like Image { Text { anchors.centerIn: parent } } as your tile 您可以将更多单独的Tile合并到Image中,例如矩形,而将Image { Text { anchors.centerIn: parent } }作为您的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. 您可以考虑是否可以以某种方式利用GridView来帮助您,但这将对您的设计提供一些限制。
  • 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. 如果GridView不满足您的需求,我建议您采取一个好步骤,编写某种视图项,负责创建和定位Tile实例,以便您仅实例化在任何位置显示的地图部分的tile给定点。

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) . 您将研究QQuickItem子类QQuickItem (在C ++方面),并具有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. 在setTileDelegate设置器中,调用QQuickItem::polish ,然后重写QQuickItem::updatePolish然后在其中创建(或重新定位)所提供tileDelegate组件的实例,该实例从屏幕上当前显示的位置偏移,直到您的“ GameViewThing”为完全用瓷砖覆盖。

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. 我在这里有点含糊-抱歉-但是完整的示例将包含很多代码,所以我希望我已给您足够的信息和指导,使您能够自己开始。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM