如何在不导致内存泄漏的情况下使用 QSGGeometryNode 并确保正确清理
How to use QSGGeometryNode without causing a memory leak and ensure correct clean up
我使用 QSGGeometry
、QSGVertexColorMaterial
和 QSGGeometryNode
在我的 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 个指针 material
和 child_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>();
但是当 material
和 child_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;
}
我使用 QSGGeometry
、QSGVertexColorMaterial
和 QSGGeometryNode
在我的 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 个指针 material
和 child_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>();
但是当 material
和 child_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;
}