From 9a53834f1e7fce2fc3b1ecc2a311faedbc371d37 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Wed, 26 Jun 2019 12:36:08 +0200 Subject: Fix distance field cache growing outside limit Due to the way the area allocator is used, where one area spans all the textures, it is possible to have the situation where part of a glyph is allocated to a texture N and the rest to N+1, i.e. the split happens inside the glyph. In a case like this, the cache would grow the texture in question to accommodate the glyph, which would in turn cause the height of the texture to be too large. For most systems, this would not happen, since a single texture would be enough for a font, but when it does the texture creation may fail. The fix is to reduce the height of the area allocated to each texture by one row height, so that if the glyph does happen to hit the border between two and the texture needs to extend, it will still not exceed the actual maximum height. [ChangeLog][Text] Fixed a bug when displaying many characters from the same font on a system with a low maximum texture size. Task-number: QTBUG-76528 Change-Id: I1e559aaf90552afe8531872264186daa5ee61939 Reviewed-by: Simon Hausmann --- .../qsgdefaultdistancefieldglyphcache.cpp | 40 ++++++++++++---------- .../qsgdefaultdistancefieldglyphcache_p.h | 7 ++-- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp index 8121b4559e..c51358df7c 100644 --- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp +++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp @@ -68,7 +68,8 @@ DEFINE_BOOL_CONFIG_OPTION(qsgPreferFullSizeGlyphCacheTextures, QSG_PREFER_FULLSI QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QOpenGLContext *c, const QRawFont &font) : QSGDistanceFieldGlyphCache(font) - , m_maxTextureSize(0) + , m_maxTextureWidth(0) + , m_maxTextureHeight(0) , m_maxTextureCount(3) , m_areaAllocator(nullptr) , m_blitProgram(nullptr) @@ -112,13 +113,23 @@ void QSGDefaultDistanceFieldGlyphCache::requestGlyphs(const QSet &glyph QList glyphPositions; QVector glyphsToRender; + const int padding = QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING; + const qreal scaleFactor = qreal(1) / QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution); + + if (m_maxTextureHeight == 0) { + m_funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureWidth); + + // 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 = QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING; QRectF boundingRect = glyphData(glyphIndex).boundingRect; int glyphWidth = qCeil(boundingRect.width()) + distanceFieldRadius() * 2; int glyphHeight = qCeil(boundingRect.height()) + distanceFieldRadius() * 2; @@ -151,8 +162,8 @@ void QSGDefaultDistanceFieldGlyphCache::requestGlyphs(const QSet &glyph 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); @@ -539,13 +550,6 @@ bool QSGDefaultDistanceFieldGlyphCache::createFullSizeTextures() const return qsgPreferFullSizeGlyphCacheTextures() && glyphCount() > QT_DISTANCEFIELD_HIGHGLYPHCOUNT(); } -int QSGDefaultDistanceFieldGlyphCache::maxTextureSize() const -{ - if (!m_maxTextureSize) - m_funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize); - return m_maxTextureSize; -} - namespace { struct Qtdf { // We need these structs to be tightly packed, but some compilers we use do not @@ -644,7 +648,7 @@ bool QSGDefaultDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &fo } qreal pixelSize = qreal(Qtdf::fetch(qtdfTableStart, Qtdf::pixelSize)); - m_maxTextureSize = Qtdf::fetch(qtdfTableStart, Qtdf::textureSize); + m_maxTextureWidth = m_maxTextureHeight = Qtdf::fetch(qtdfTableStart, Qtdf::textureSize); m_doubleGlyphResolution = Qtdf::fetch(qtdfTableStart, Qtdf::flags) == 1; padding = Qtdf::fetch(qtdfTableStart, Qtdf::headerPadding); @@ -653,7 +657,7 @@ bool QSGDefaultDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &fo return false; } - if (m_maxTextureSize <= 0) { + if (m_maxTextureWidth <= 0) { qWarning("Invalid texture size in '%s'", qPrintable(font.familyName())); return false; } @@ -661,11 +665,11 @@ bool QSGDefaultDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &fo int systemMaxTextureSize; m_funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &systemMaxTextureSize); - if (m_maxTextureSize > systemMaxTextureSize) { + if (m_maxTextureWidth > systemMaxTextureSize) { qWarning("System maximum texture size is %d. This is lower than the value in '%s', which is %d", systemMaxTextureSize, qPrintable(font.familyName()), - m_maxTextureSize); + m_maxTextureWidth); } if (padding != QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING) { @@ -688,12 +692,12 @@ bool QSGDefaultDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &fo 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); const char *textureRecord = allocatorData; diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h index fb07191e59..0420ef251e 100644 --- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h +++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h @@ -80,7 +80,6 @@ public: bool useTextureResizeWorkaround() const; bool useTextureUploadWorkaround() const; bool createFullSizeTextures() const; - int maxTextureSize() const; void setMaxTextureCount(int max) { m_maxTextureCount = max; } int maxTextureCount() const { return m_maxTextureCount; } @@ -106,9 +105,10 @@ private: TextureInfo *textureInfo(int index) { + Q_ASSERT(m_maxTextureWidth > 0 && m_maxTextureHeight > 0); for (int i = m_textures.count(); i <= index; ++i) { if (createFullSizeTextures()) - m_textures.append(QRect(0, 0, maxTextureSize(), maxTextureSize())); + m_textures.append(QRect(0, 0, m_maxTextureWidth, m_maxTextureWidth)); else m_textures.append(TextureInfo()); } @@ -136,7 +136,8 @@ private: m_blitProgram->link(); } - mutable int m_maxTextureSize; + int m_maxTextureWidth; + int m_maxTextureHeight; int m_maxTextureCount; bool m_coreProfile; -- cgit v1.2.3