aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>2025-06-10 11:34:56 +0200
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2025-07-28 14:54:28 +0000
commit90ae749db94c9595ad83c196916ec33a39f583e1 (patch)
treea87fe9eb7e7edd1e26c284217b1445e4a7e72c39
parent0cf7d97ed9d05d5f314810ffd9238697ada8d0ca (diff)
Make sure we propely dereference all glyphs in distance field cache
When we reference glyphs in QSGDistanceFieldGlyphCache::populate(), we do this before they are added to the cache, so the cache coords have yet to be registered. So even for empty glyphs that cover a 0x0 area, they will be still be registered as referenced in the cache. However, when dereferencing the glyphs, we would skip if the glyph did not occupy any space in the cache. This asymmetrical behavior could lead to a glyph cache mistakenly being counted as active even after deleting all nodes that reference it were gone. Thus it would stay around in the glyph cache when it's invalidated, even though all references are actually gone. As a simple fix, we make sure the referenceGlyphs() and releaseGlyphs() are always called for all glyphs. This means that the list of unused glyphs may sometimes contain empty glyphs, so when we recycle the unused glyphs, we need to make sure we skip that for glyphs that do not occupy any space. Pick-to: 6.8 Task-number: QTBUG-132108 Change-Id: I919d8b6a6a2de9bb1f0fccd7c6ffa00436e6ffa5 Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io> (cherry picked from commit baaa6f2171f2830cfbde60b7902dd167123350c0) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> (cherry picked from commit 96fe524bab9aa7386ffad9aaa81fadc9a3567180)
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer.cpp2
-rw-r--r--src/quick/scenegraph/qsgrhidistancefieldglyphcache.cpp16
-rw-r--r--tests/auto/quick/scenegraph/data/distanceFieldCacheInvalidation.qml10
-rw-r--r--tests/auto/quick/scenegraph/tst_scenegraph.cpp31
4 files changed, 51 insertions, 8 deletions
diff --git a/src/quick/scenegraph/qsgadaptationlayer.cpp b/src/quick/scenegraph/qsgadaptationlayer.cpp
index b62919c2e9..a8d826166b 100644
--- a/src/quick/scenegraph/qsgadaptationlayer.cpp
+++ b/src/quick/scenegraph/qsgadaptationlayer.cpp
@@ -135,7 +135,7 @@ void QSGDistanceFieldGlyphCache::release(const QVector<glyph_t> &glyphs)
for (int i = 0; i < count; ++i) {
glyph_t glyphIndex = glyphs.at(i);
GlyphData &gd = glyphData(glyphIndex);
- if (--gd.ref == 0 && !gd.texCoord.isNull())
+ if (--gd.ref == 0)
unusedGlyphs.insert(glyphIndex);
}
releaseGlyphs(unusedGlyphs);
diff --git a/src/quick/scenegraph/qsgrhidistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgrhidistancefieldglyphcache.cpp
index 54cf298943..a7adafe16b 100644
--- a/src/quick/scenegraph/qsgrhidistancefieldglyphcache.cpp
+++ b/src/quick/scenegraph/qsgrhidistancefieldglyphcache.cpp
@@ -62,13 +62,15 @@ void QSGRhiDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyphs)
glyph_t unusedGlyph = *m_unusedGlyphs.constBegin();
TexCoord unusedCoord = glyphTexCoord(unusedGlyph);
- QRectF unusedGlyphBoundingRect = glyphData(unusedGlyph).boundingRect;
- int unusedGlyphWidth = qCeil(unusedGlyphBoundingRect.width() + distanceFieldRadius() * 2);
- int unusedGlyphHeight = qCeil(unusedGlyphBoundingRect.height() + distanceFieldRadius() * 2);
- m_areaAllocator->deallocate(QRect(unusedCoord.x - padding,
- unusedCoord.y - padding,
- padding * 2 + unusedGlyphWidth,
- padding * 2 + unusedGlyphHeight));
+ if (!unusedCoord.isNull()) {
+ QRectF unusedGlyphBoundingRect = glyphData(unusedGlyph).boundingRect;
+ int unusedGlyphWidth = qCeil(unusedGlyphBoundingRect.width() + distanceFieldRadius() * 2);
+ int unusedGlyphHeight = qCeil(unusedGlyphBoundingRect.height() + distanceFieldRadius() * 2);
+ m_areaAllocator->deallocate(QRect(unusedCoord.x - padding,
+ unusedCoord.y - padding,
+ padding * 2 + unusedGlyphWidth,
+ padding * 2 + unusedGlyphHeight));
+ }
m_unusedGlyphs.remove(unusedGlyph);
m_glyphsTexture.remove(unusedGlyph);
diff --git a/tests/auto/quick/scenegraph/data/distanceFieldCacheInvalidation.qml b/tests/auto/quick/scenegraph/data/distanceFieldCacheInvalidation.qml
new file mode 100644
index 0000000000..539d4b64a6
--- /dev/null
+++ b/tests/auto/quick/scenegraph/data/distanceFieldCacheInvalidation.qml
@@ -0,0 +1,10 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Rectangle {
+ width: 200
+ height: 200
+ color: "steelblue"
+}
diff --git a/tests/auto/quick/scenegraph/tst_scenegraph.cpp b/tests/auto/quick/scenegraph/tst_scenegraph.cpp
index ec1d3037a4..bc3923f94f 100644
--- a/tests/auto/quick/scenegraph/tst_scenegraph.cpp
+++ b/tests/auto/quick/scenegraph/tst_scenegraph.cpp
@@ -16,6 +16,8 @@
#include <private/qopenglcontext_p.h>
#endif
+#include <QtGui/qrawfont.h>
+
#include <private/qsgcontext_p.h>
#include <private/qsgrenderloop_p.h>
#include <private/qsgrhisupport_p.h>
@@ -97,6 +99,7 @@ private slots:
void withAdoptedRhi();
void resizeTextureFromImage();
void textureNativeInterface();
+ void distanceFieldCacheInvalidation();
private:
QQuickView *createView(const QString &file, QWindow *parent = nullptr, int x = -1, int y = -1, int w = -1, int h = -1);
@@ -858,6 +861,34 @@ bool tst_SceneGraph::isRunningOnRhi()
return retval;
}
+void tst_SceneGraph::distanceFieldCacheInvalidation()
+{
+ if (!isRunningOnRhi())
+ QSKIP("Skipping complex rendering tests due to not running with QRhi");
+
+ QFont font(QStringLiteral("Arial"));
+ QRawFont rawFont = QRawFont::fromFont(font);
+
+ QQuickView view;
+ view.setSource(testFileUrl(QLatin1String("distanceFieldCacheInvalidation.qml")));
+ view.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+
+ {
+ QSGRenderContext *rc = QQuickItemPrivate::get(view.rootObject())->sceneGraphRenderContext();
+
+ QSGDistanceFieldGlyphCache *cache = rc->distanceFieldGlyphCache(rawFont, 1);
+ QVERIFY(cache != nullptr);
+
+ auto glyphIndexes = rawFont.glyphIndexesForString(QStringLiteral("a b"));
+ cache->populate(glyphIndexes);
+ QVERIFY(cache->isActive());
+
+ cache->release(glyphIndexes);
+ QVERIFY(!cache->isActive());
+ }
+}
+
#include "tst_scenegraph.moc"
QTEST_MAIN(tst_SceneGraph)