From 6d11ee1b0511e9985f1b88a6aa660f1bc0fe2e09 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Wed, 15 Jun 2022 09:50:57 +0200 Subject: Fix crash when expanding glyph cache beyond one texture The textureInfo() function was returning pointers to the m_textures array, which would then be stored in the m_glyphTextures map and other places in the renderer. Then the function then later resized it, the renderer would be riddled with dangling pointers and crash pretty quickly. There is a preset maximum texture count, so we just make sure the array is the correct size to begin with and never resize it. In addition, this contains a port of 46d72a117df642135718b38995346267312c4808 and 9a53834f1e7fce2fc3b1ecc2a311faedbc371d37 from Qt Quick. Note: Glyph recycling does not seem to work, so you may end up running out of cache space. However, it will no longer crash, but glyphs will be missing instead. Fixes: QT3DS-4235 Change-Id: I7d9707ff95ac0838e7e6574714a06ac1c23986fa Reviewed-by: Janne Koskinen --- product_dependencies.yaml | 2 +- src/runtimerender/Qt3DSDistanceFieldGlyphCache.cpp | 48 +++++++++++++--------- src/runtimerender/Qt3DSDistanceFieldGlyphCache_p.h | 8 ++-- 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/product_dependencies.yaml b/product_dependencies.yaml index b6035e8..fa5342c 100644 --- a/product_dependencies.yaml +++ b/product_dependencies.yaml @@ -1,3 +1,3 @@ dependencies: ../../qt/tqtc-qt5.git: - ref: "b76f628f02c32c28fecb7b7cfa561f9861fd3326" + ref: "tqtc/lts-5.15" diff --git a/src/runtimerender/Qt3DSDistanceFieldGlyphCache.cpp b/src/runtimerender/Qt3DSDistanceFieldGlyphCache.cpp index d19c9f6..658c402 100644 --- a/src/runtimerender/Qt3DSDistanceFieldGlyphCache.cpp +++ b/src/runtimerender/Qt3DSDistanceFieldGlyphCache.cpp @@ -59,9 +59,11 @@ Q3DSDistanceFieldGlyphCache::Q3DSDistanceFieldGlyphCache( : QSGDistanceFieldGlyphCache(font) , m_context(context) { - m_maxTextureSize = Q3DSDISTANCEFIELDGLYPHCACHE_MAXIMUM_TEXURE_SIZE; + m_maxTextureWidth = Q3DSDISTANCEFIELDGLYPHCACHE_MAXIMUM_TEXURE_SIZE; loadPregeneratedCache(font); + if (m_textures.size() < m_maxTextureCount) + m_textures.resize(m_maxTextureCount); } Q3DSDistanceFieldGlyphCache::~Q3DSDistanceFieldGlyphCache() @@ -71,16 +73,14 @@ Q3DSDistanceFieldGlyphCache::~Q3DSDistanceFieldGlyphCache() delete m_areaAllocator; } -int Q3DSDistanceFieldGlyphCache::maxTextureSize() const -{ - return m_maxTextureSize; -} - Q3DSDistanceFieldGlyphCache::TextureInfo *Q3DSDistanceFieldGlyphCache::textureInfo(int index) const { - while (index >= m_textures.size()) - m_textures.append(TextureInfo()); - return &m_textures[index]; + Q_ASSERT(index < m_textures.size()); + + if (index < m_textures.size()) + return &m_textures[index]; + else + return nullptr; } void Q3DSDistanceFieldGlyphCache::referenceGlyphs(const QSet &glyphs) @@ -145,7 +145,7 @@ void Q3DSDistanceFieldGlyphCache::storeGlyphs(const QList &glyph TexCoord c = glyphTexCoord(glyphIndex); TextureInfo *texInfo = m_glyphsTexture.value(glyphIndex); - resizeTexture(texInfo, maxTextureSize(), texInfo->allocatedArea.height()); + resizeTexture(texInfo, m_maxTextureWidth, texInfo->allocatedArea.height()); Q_ASSERT(!glyphTextures[texInfo].contains(glyphIndex)); glyphTextures[texInfo].append(glyphIndex); @@ -187,15 +187,22 @@ void Q3DSDistanceFieldGlyphCache::requestGlyphs(const QSet &glyphs) QList glyphPositions; QVector glyphsToRender; + const int padding = Q3DSDISTANCEFIELDGLYPHCACHE_PADDING; + if (m_maxTextureHeight == 0) { + const qreal scaleFactor = qreal(1) / QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution); + // We need to add a buffer to avoid glyphs that overlap the border between two + // textures causing the height of the textures to extend beyond the limit. + m_maxTextureHeight = m_maxTextureWidth - (qCeil(m_referenceFont.pixelSize() * scaleFactor) + distanceFieldRadius() * 2 + padding * 2); + } + if (m_areaAllocator == nullptr) { - m_areaAllocator = new QSGAreaAllocator(QSize(maxTextureSize(), - m_maxTextureCount * maxTextureSize())); + m_areaAllocator = new QSGAreaAllocator(QSize(m_maxTextureWidth, + m_maxTextureCount * m_maxTextureHeight)); } for (QSet::const_iterator it = glyphs.constBegin(); it != glyphs.constEnd() ; ++it) { glyph_t glyphIndex = *it; - int padding = Q3DSDISTANCEFIELDGLYPHCACHE_PADDING; QRectF boundingRect = glyphData(glyphIndex).boundingRect; int glyphWidth = qCeil(boundingRect.width()) + distanceFieldRadius() * 2; int glyphHeight = qCeil(boundingRect.height()) + distanceFieldRadius() * 2; @@ -230,8 +237,8 @@ void Q3DSDistanceFieldGlyphCache::requestGlyphs(const QSet &glyphs) continue; } - TextureInfo *tex = textureInfo(alloc.y() / maxTextureSize()); - alloc = QRect(alloc.x(), alloc.y() % maxTextureSize(), alloc.width(), alloc.height()); + TextureInfo *tex = textureInfo(alloc.y() / m_maxTextureHeight); + alloc = QRect(alloc.x(), alloc.y() % m_maxTextureHeight, alloc.width(), alloc.height()); tex->allocatedArea |= alloc; Q_ASSERT(tex->padding == padding || tex->padding < 0); @@ -243,6 +250,7 @@ void Q3DSDistanceFieldGlyphCache::requestGlyphs(const QSet &glyphs) glyphPositions.append(p); glyphsToRender.append(glyphIndex); + m_glyphsTexture.insert(glyphIndex, tex); } @@ -361,7 +369,7 @@ bool Q3DSDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &font) } qreal pixelSize = qreal(Qtdf::fetch(qtdfTableStart, Qtdf::pixelSize)); - m_maxTextureSize = int(Qtdf::fetch(qtdfTableStart, Qtdf::textureSize)); + m_maxTextureWidth = m_maxTextureHeight = int(Qtdf::fetch(qtdfTableStart, Qtdf::textureSize)); m_doubleGlyphResolution = Qtdf::fetch(qtdfTableStart, Qtdf::flags) == 1; padding = Qtdf::fetch(qtdfTableStart, Qtdf::headerPadding); @@ -370,7 +378,7 @@ bool Q3DSDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &font) return false; } - if (m_maxTextureSize <= 0) { + if (m_maxTextureWidth <= 0) { qWarning("Invalid texture size in '%s'", qPrintable(font.familyName())); return false; } @@ -396,13 +404,15 @@ bool Q3DSDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &font) return false; } - if (m_areaAllocator->size().height() % m_maxTextureSize != 0) { + if (m_areaAllocator->size().height() % m_maxTextureHeight != 0) { qWarning("Area allocator size mismatch in '%s'", qPrintable(font.familyName())); return false; } - textureCount = m_areaAllocator->size().height() / m_maxTextureSize; + textureCount = m_areaAllocator->size().height() / m_maxTextureHeight; m_maxTextureCount = qMax(m_maxTextureCount, textureCount); + if (m_textures.size() < m_maxTextureCount) + m_textures.resize(m_maxTextureCount); const char *textureRecord = allocatorData; for (int i = 0; i < textureCount; ++i, textureRecord += Qtdf::TextureRecordSize) { diff --git a/src/runtimerender/Qt3DSDistanceFieldGlyphCache_p.h b/src/runtimerender/Qt3DSDistanceFieldGlyphCache_p.h index fb4826e..2eca726 100644 --- a/src/runtimerender/Qt3DSDistanceFieldGlyphCache_p.h +++ b/src/runtimerender/Qt3DSDistanceFieldGlyphCache_p.h @@ -54,10 +54,10 @@ class Q3DSDistanceFieldGlyphCache : public QSGDistanceFieldGlyphCache { public: struct TextureInfo { - qt3ds::render::NVRenderTexture2D *texture; + qt3ds::render::NVRenderTexture2D *texture = nullptr; int padding = -1; - QRect allocatedArea; + QRect allocatedArea = QRect(0, 0, 1, 1); QImage copy; }; @@ -84,12 +84,12 @@ private: bool loadPregeneratedCache(const QRawFont &font); TextureInfo *textureInfo(int index) const; - int maxTextureSize() const; void resizeTexture(TextureInfo *info, int width, int height); void setTextureData(qt3ds::render::NVRenderTexture2D *texture, QImage &image); QSGAreaAllocator *m_areaAllocator = nullptr; - int m_maxTextureSize = 0; + int m_maxTextureWidth = 0; + int m_maxTextureHeight = 0; int m_maxTextureCount = 3; mutable QVector m_textures; -- cgit v1.2.3