From 23183692f615ef6dcf790ece69518cc661aca576 Mon Sep 17 00:00:00 2001 From: Robert Löhning Date: Wed, 27 May 2026 12:05:45 +0200 Subject: [PATCH] Speed up QSvgStructureNode::releaseDescendants() The previous implementation traversed the tree of objects until it found something it may delete. Then it deleted that and started from the root of the tree again to find the next object. Instead, this code traverses the tree only once and deletes what it finds on the way. Amends 22d503993d156d89086f1a68c76670cbc7130be0 Task-number: QTBUG-145916 Pick-to: 6.10 6.8 Change-Id: If693f62ea469d85536eed0fcf7a87103c335c68c Reviewed-by: Hatem ElKharashy (cherry picked from commit 32680b86192ea67d9e273061108439166e7db3d6) Reviewed-by: Qt Cherry-pick Bot --- diff --git a/src/svg/qsvgstructure.cpp b/src/svg/qsvgstructure.cpp index 2cde60e..83296c6 100644 --- a/src/svg/qsvgstructure.cpp +++ b/src/svg/qsvgstructure.cpp @@ -731,33 +731,37 @@ // happen in deeply nested trees. // This function does not allocate any memory at the cost of sacrificing some performance to // make it safe to be called from a destructor. - while (!m_renderers.empty()) { - auto nodes = &m_renderers; - bool isSubtree = true; - while (isSubtree) { - switch (nodes->front()->type()) { - case QSvgNode::Doc: - case QSvgNode::Defs: - case QSvgNode::Group: - case QSvgNode::Mask: - case QSvgNode::Pattern: - case QSvgNode::Symbol: - case QSvgNode::Switch: - case QSvgNode::Filter: - { - QSvgStructureNode *subtree = static_cast(nodes->front().get()); - isSubtree = !subtree->m_renderers.empty(); - if (isSubtree) - nodes = &subtree->m_renderers; - } - break; - default: - isSubtree = false; - break; + QSvgNode *currentParent = this; + while (currentParent) { + switch (currentParent->type()) { + case QSvgNode::Doc: + case QSvgNode::Defs: + case QSvgNode::Group: + case QSvgNode::Mask: + case QSvgNode::Pattern: + case QSvgNode::Symbol: + case QSvgNode::Switch: + case QSvgNode::Filter: + { + QSvgStructureNode *currentParentSN = static_cast(currentParent); + if (currentParentSN->m_renderers.empty()) { + currentParent = currentParent->parent(); + if (currentParent) + static_cast(currentParent)->m_renderers.pop_front(); + } else { + Q_ASSERT(currentParentSN->m_renderers.front().get()->parent() == currentParent); + currentParent = currentParentSN->m_renderers.front().get(); } } - nodes->pop_front(); + break; + default: + currentParent = currentParent->parent(); + if (currentParent) + static_cast(currentParent)->m_renderers.pop_front(); + break; + } } + Q_ASSERT(this->m_renderers.empty()); } QSvgMask::QSvgMask(QSvgNode *parent, QSvgRectF bounds, diff --git a/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp b/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp index a4402f5..d5e7f9a 100644 --- a/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp +++ b/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp @@ -2622,14 +2622,10 @@ QTest::addColumn("deeplyNested"); QTest::addColumn("trusted"); - QTest::newRow("8 groups") << generateNests(8) << true; - QTest::newRow("16 groups") << generateNests(16) << true; - QTest::newRow("32 groups") << generateNests(32) << true; - QTest::newRow("64 groups") << generateNests(64) << false; - QTest::newRow("128 groups") << generateNests(128) << false; - QTest::newRow("256 groups") << generateNests(256) << false; - QTest::newRow("512 groups") << generateNests(512) << false; - QTest::newRow("1024 groups") << generateNests(1024) << false; + for (int i = 8; i <= 4096; i *= 2) { + QTest::newRow((QString::number(i) + " groups").toStdString().c_str()) + << generateNests(i) << (i <= 32); + } } void tst_QSvgRenderer::testDeeplyNested()