diff options
| author | Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io> | 2025-06-10 11:34:56 +0200 |
|---|---|---|
| committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2025-07-28 14:54:28 +0000 |
| commit | 90ae749db94c9595ad83c196916ec33a39f583e1 (patch) | |
| tree | a87fe9eb7e7edd1e26c284217b1445e4a7e72c39 | |
| parent | 0cf7d97ed9d05d5f314810ffd9238697ada8d0ca (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)
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) |
