大网格

时间:2017-01-12 09:39:19

标签: c++ memory-management qml

我开发了一种扫雷式游戏,作为练习QML的一种方式。

我遇到的问题是内存使用情况会根据网格的大小快速增长(> 700 Mb)。 我的尺寸可以达到150 * 150(更大的网格会因为它的win32而崩溃)。

网格是这样构建的(来自Expose 2D C++ Game Board to QML using QAbstractItemModel的位)(该图像用于显示" 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
                        ...
                    }
                }
            }
        }
    }

这是我的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
        }
    }
}
编辑:我的问题是如何改善内存使用?对于扫雷而言,我没想到它需要那么多。我的目标也是在Android上进行测试,因此内存使用量有限。

1 个答案:

答案 0 :(得分:2)

在看QtQuick& QML性能,我的关键规则一般是尽可能少做。不要创建您现在不一定需要向用户显示的项目和绑定。

我不能给你一个确切的数字(因为毕竟,确实没有一个 - 它的项目和硬件依赖),但我建议QtQuick最适合显示数千个数字的数字屏幕上一次显示的项目。更重要的是,事情将开始在资源使用和性能方面略微粗略。

无论好坏,QML都是一种非常容易通过表达来隐藏成本的语言。在使用它时,你需要考虑在编写这段代码时发生的事情;您创建的每个Item都是一个QQuickItem子类,它被分配,您设置的每个属性都是C ++函数调用。对于绑定,JavaScript引擎需要先评估该绑定(还要评估其依赖的其他内容),然后才能调用该函数来设置属性值。

查看您的Flickable片段,您有一个包含Repeater行的Repeater列。你说你允许高达150x150的网格。让我们对这个数字做一些粗略的数学运算。对于每个图块,您(至少)有两个矩形,一个图像和一个文本项,每个Tile.qml实例总共提供4个项目。此外,每个行都有一个固定的行和一个中继器。

这意味着网格上单行的总创建成本为: 2 + (4 * mapSize)件商品,每行共有602件商品。然后,你有了它,乘以150,总共花费 90,300项来创建该网格。

每一行都有许多绑定:我在tile中计数9(Tile本身为7,不包括有点特殊的“id”赋值,再加上两个x& y coords),再加上另外两个Row和Repeater绑定,即每行2 + (9 * mapSize)个绑定 - 每行1352个绑定,整个网格 202,800个绑定

总而言之,这是一个非常大量的项目和绑定,可以一次使用。

这些项目中的每一项都有很大的成本 - 例如,每个项目本身就有一百个字节,为每个项目创建的绑定的额外分配,项目实际创建的QSGNode实例的分配在屏幕上获取内容,并且使用Repeater也意味着您在每个委托的JavaScript堆上都有一些额外的分配...这可以添加到非常大的内存,具体取决于您在一次。

所以,要直接回答你的问题,简单的答案就是“不要创造那么多东西”。我不确定你是如何实现这一目标的,但这里有几个可能性:

  • 您可以将更多的单个Tile合并到图像中,例如矩形,只有Image { Text { anchors.centerIn: parent } }作为你的瓷砖
  • 您可以考虑是否可以以某种方式使用GridView来帮助您解决此问题,但它会对您的设计提供一些限制。
  • 如果GridView不适合您的需求,我建议一个好的步骤是编写某种视图项,负责创建和定位Tile实例,这样您只是实例化地图部分的切片在任何给定的点上显示。

对于最后一个选项,它会像这样。您将研究子类化QQuickItem(在C ++方面),并拥有Q_PROPERTY(QQmlComponent* tileDelegate READ tileDelegate WRITE setTileDelegate NOTIFY tileDelegateChanged)。在setTileDelegate setter中,调用QQuickItem::polish,然后覆盖QQuickItem::updatePolish然后创建(或只是重新定位)提供的tileDelegate组件的实例,偏离屏幕上当前显示的位置,直到你的“GameViewThing” “完全被瓷砖覆盖了。

我在这里有点模糊 - 对不起 - 但是一个完整的例子将是相当多的代码,所以我希望我已经提供了足够的信息和你可以自己开始的方向。