如何在不导致内存泄漏的情况下使用 QSGGeometryNode 并确保正确清理

How to use QSGGeometryNode without causing a memory leak and ensure correct clean up

我使用 QSGGeometryQSGVertexColorMaterialQSGGeometryNode 在我的 QQuickItem 派生的 class 上实时绘制一些东西 MyQuickItem 这里

以下是我的updatePaintNode方法,重绘逻辑的症结所在。

QSGNode * MyQuickItem::updatePaintNode(QSGNode * oldNode, UpdatePaintNodeData * updatePaintNodeData) {

  if (!oldNode) {
    oldNode = new QSGNode;
  }

  oldNode->removeAllChildNodes();

  QSGGeometry * geometry = GetMyGeometry();
  QSGVertexColorMaterial * material = new QSGVertexColorMaterial;
  QSGGeometryNode * child_node = new QSGGeometryNode;

  child_node->setGeometry(geometry);
  child_node->setMaterial(material);
  child_node->setFlag(QSGNode::OwnsMaterial);
  oldNode->appendChildNode(child_node);

  return oldNode;
}

问题:
以上逻辑非常有效。功能完全没有问题。也没有性能问题。 但我担心我会导致内存泄漏。查看上面方法 updatePaintNode 中的以下两行,我在其中分配了原始指针。

QSGVertexColorMaterial * material = new QSGVertexColorMaterial;
QSGGeometryNode * child_node = new QSGGeometryNode;

我分配它们并且我不删除它们。这是因为它们应该被删除的点是在 updatePaintNode 完成之后。 那不在我的控制范围内。

问题:
如何确保从内存中正确清除 2 个指针 materialchild_node
像我上面那样做 child_node->setFlag(QSGNode::OwnsMaterial) 是否会将指针的 所有权 设置为 QtSceneGraph 并减轻我删除指针的负担?

次要问题:
我正在使用 oldNode->removeAllChildNodes() 清除上一帧中绘制的数据。这是在绘制新数据之前清除屏幕上以前数据的好方法吗?

PS:
我重申:这个实现没有性能问题。我只是想确定我不会造成任何内存泄漏。我试过使用 material & child_node 作为智能指针,如下所示:

auto material = std::make_shared<QSGVertexColorMaterial>();
auto child_node = new std::make_shared<QSGGeometryNode>();

但是当 materialchild_node 稍后从内存中自动清除时,这会导致崩溃。

是的,在您的示例代码中,您可以依赖节点的自动清理。您没有从 updatePaintNode 泄漏内存。

旧节点和child_node

从 QQuickItem::updatePaintNode() 返回的

oldnode 会在正确的时间在正确的线程上自动删除。 QSGNode 实例的树是通过使用默认设置的 QSGNode::OwnedByParent 来管理的。

material

因为你为你的child_node设置了标志QSGNode::OwnsMaterial child_node被删除时material被删除。

第二个问题:这个方法好吗?

答案是否定的。每次渲染场景时都没有必要创建和删除节点。相反,您应该重用 node/nodes。在下面的示例代码中,我假设几何形状发生变化,但 material 在 QQuickItem 的生命周期内不会发生变化。如果 material 发生变化,您可能需要调用 node->markDirty(QSGNode::DirtyMaterial)。请注意,只创建一个节点,并且只创建一次(除非例如 window 隐藏然后带回 fg 或其他)。

QSGNode * MyQuickItem::updatePaintNode(QSGNode * oldNode, UpdatePaintNodeData * updatePaintNodeData) {

    QSGGeometryNode *node = static_cast<QSGGeometryNode *>(oldNode);
    if (!node) {
        node = new QSGGeometryNode;

        QSGVertexColorMaterial * material = new QSGVertexColorMaterial;
        node->setMaterial(material);
        node->setFlag(QSGNode::OwnsMaterial);
    }

    // if GetMyGeometry returns every time a new dynamically allocated object then you should
    // call node->setFlag(QSGNode::OwnsGeometry) to not leak memory here:
    QSGGeometry * geometry = GetMyGeometry(); 
    node->setGeometry(geometry);
    // No need to call node->markDirty(QSGNode::DirtyGeometry) because setGeometry is called.

    return node;
}