From b634c19680cd1d4dd925a18e616844ed420d18ae Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Wed, 14 Mar 2012 16:11:10 +0100 Subject: [PATCH] Support for multiple textures in the default distance field glyph cache. The default implementation of the distance field glyph cache can now store the glyphs of one font in several textures, allowing to cache more glyphs at once. The default maximum number of textures per cache is 3. Glyphs are recycled when all textures are full. Change-Id: I28d2d6cf5aa409141e2700b505023f15d3c2cd26 Reviewed-by: Eskil Abrahamsen Blomfeldt --- .../qsgdefaultdistancefieldglyphcache.cpp | 86 +++++++++++++--------- .../qsgdefaultdistancefieldglyphcache_p.h | 59 +++++++++++---- 2 files changed, 94 insertions(+), 51 deletions(-) diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp index 05d076e..5864f35 100644 --- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp +++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp @@ -62,6 +62,7 @@ QSGDefaultDistanceFieldGlyphCache::DistanceFieldTextureData *QSGDefaultDistanceF QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font) : QSGDistanceFieldGlyphCache(man, c, font) , m_maxTextureSize(0) + , m_maxTextureCount(3) { m_textureData = textureData(c); } @@ -77,25 +78,32 @@ void QSGDefaultDistanceFieldGlyphCache::requestGlyphs(const QSet &glyph if (cacheIsFull() && m_textureData->unusedGlyphs.isEmpty()) continue; + if (textureIsFull(m_textureData->currentTexture) && m_textureData->textures.count() < m_maxTextureCount) + m_textureData->currentTexture = m_textureData->addTexture(); + m_textureData->unusedGlyphs.remove(glyphIndex); + DistanceFieldTextureData::TextureInfo *tex = m_textureData->currentTexture; + GlyphPosition p; p.glyph = glyphIndex; - p.position = QPointF(m_textureData->currX, m_textureData->currY); + p.position = QPointF(tex->currX, tex->currY); if (!cacheIsFull()) { - m_textureData->currX += QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution()); - if (m_textureData->currX >= maxTextureSize()) { - m_textureData->currX = 0; - m_textureData->currY += QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution()); + tex->currX += QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution()); + if (tex->currX >= maxTextureSize()) { + tex->currX = 0; + tex->currY += QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution()); } } else { // Recycle glyphs if (!m_textureData->unusedGlyphs.isEmpty()) { glyph_t unusedGlyph = *m_textureData->unusedGlyphs.constBegin(); TexCoord unusedCoord = glyphTexCoord(unusedGlyph); + tex = m_textureData->glyphsTexture.value(unusedGlyph); p.position = QPointF(unusedCoord.x, unusedCoord.y); m_textureData->unusedGlyphs.remove(unusedGlyph); + m_textureData->glyphsTexture.remove(unusedGlyph); removeGlyph(unusedGlyph); } } @@ -103,6 +111,7 @@ void QSGDefaultDistanceFieldGlyphCache::requestGlyphs(const QSet &glyph if (p.position.y() < maxTextureSize()) { glyphPositions.append(p); glyphsToRender.append(glyphIndex); + m_textureData->glyphsTexture.insert(glyphIndex, tex); } } @@ -114,41 +123,46 @@ void QSGDefaultDistanceFieldGlyphCache::storeGlyphs(const QHash { int requiredWidth = maxTextureSize(); int rows = 128 / (requiredWidth / QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution())); // Enough rows to fill the latin1 set by default.. - int requiredHeight = qMin(maxTextureSize(), - qMax(m_textureData->currY + QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution()), - QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution()) * rows)); - - resizeTexture((requiredWidth), (requiredHeight)); - glBindTexture(GL_TEXTURE_2D, m_textureData->texture); - QVector glyphTextures; + QHash > glyphTextures; QHash::const_iterator it; for (it = glyphs.constBegin(); it != glyphs.constEnd(); ++it) { glyph_t glyphIndex = it.key(); TexCoord c = glyphTexCoord(glyphIndex); + DistanceFieldTextureData::TextureInfo *texInfo = m_textureData->glyphsTexture.value(glyphIndex); - glyphTextures.append(glyphIndex); + int requiredHeight = qMin(maxTextureSize(), + qMax(texInfo->currY + QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution()), + QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution()) * rows)); + + resizeTexture(texInfo, requiredWidth, requiredHeight); + glBindTexture(GL_TEXTURE_2D, texInfo->texture); + + glyphTextures[texInfo].append(glyphIndex); QImage glyph = it.value(); if (useWorkaroundBrokenFBOReadback()) { uchar *inBits = glyph.scanLine(0); - uchar *outBits = m_textureData->image.scanLine(int(c.y)) + int(c.x); + uchar *outBits = texInfo->image.scanLine(int(c.y)) + int(c.x); for (int y = 0; y < glyph.height(); ++y) { qMemCopy(outBits, inBits, glyph.width()); inBits += glyph.bytesPerLine(); - outBits += m_textureData->image.bytesPerLine(); + outBits += texInfo->image.bytesPerLine(); } } glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, glyph.width(), glyph.height(), GL_ALPHA, GL_UNSIGNED_BYTE, glyph.constBits()); } - Texture t; - t.textureId = m_textureData->texture; - t.size = m_textureData->size; - setGlyphsTexture(glyphTextures, t); + QHash >::const_iterator i; + for (i = glyphTextures.constBegin(); i != glyphTextures.constEnd(); ++i) { + Texture t; + t.textureId = i.key()->texture; + t.size = i.key()->size; + setGlyphsTexture(i.value(), t); + } } void QSGDefaultDistanceFieldGlyphCache::referenceGlyphs(const QSet &glyphs) @@ -161,15 +175,15 @@ void QSGDefaultDistanceFieldGlyphCache::releaseGlyphs(const QSet &glyph m_textureData->unusedGlyphs += glyphs; } -void QSGDefaultDistanceFieldGlyphCache::createTexture(int width, int height) +void QSGDefaultDistanceFieldGlyphCache::createTexture(DistanceFieldTextureData::TextureInfo *texInfo, int width, int height) { - if (useWorkaroundBrokenFBOReadback() && m_textureData->image.isNull()) - m_textureData->image = QImage(width, height, QImage::Format_Indexed8); + if (useWorkaroundBrokenFBOReadback() && texInfo->image.isNull()) + texInfo->image = QImage(width, height, QImage::Format_Indexed8); while (glGetError() != GL_NO_ERROR) { } - glGenTextures(1, &m_textureData->texture); - glBindTexture(GL_TEXTURE_2D, m_textureData->texture); + glGenTextures(1, &texInfo->texture); + glBindTexture(GL_TEXTURE_2D, texInfo->texture); glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); @@ -177,35 +191,35 @@ void QSGDefaultDistanceFieldGlyphCache::createTexture(int width, int height) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - m_textureData->size = QSize(width, height); + texInfo->size = QSize(width, height); GLuint error = glGetError(); if (error != GL_NO_ERROR) { glBindTexture(GL_TEXTURE_2D, 0); - glDeleteTextures(1, &m_textureData->texture); - m_textureData->texture = 0; + glDeleteTextures(1, &texInfo->texture); + texInfo->texture = 0; } } -void QSGDefaultDistanceFieldGlyphCache::resizeTexture(int width, int height) +void QSGDefaultDistanceFieldGlyphCache::resizeTexture(DistanceFieldTextureData::TextureInfo *texInfo, int width, int height) { - int oldWidth = m_textureData->size.width(); - int oldHeight = m_textureData->size.height(); + int oldWidth = texInfo->size.width(); + int oldHeight = texInfo->size.height(); if (width == oldWidth && height == oldHeight) return; - GLuint oldTexture = m_textureData->texture; - createTexture(width, height); + GLuint oldTexture = texInfo->texture; + createTexture(texInfo, width, height); if (!oldTexture) return; - updateTexture(oldTexture, m_textureData->texture, m_textureData->size); + updateTexture(oldTexture, texInfo->texture, texInfo->size); if (useWorkaroundBrokenFBOReadback()) { - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, oldWidth, oldHeight, GL_ALPHA, GL_UNSIGNED_BYTE, m_textureData->image.constBits()); - m_textureData->image = m_textureData->image.copy(0, 0, width, height); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, oldWidth, oldHeight, GL_ALPHA, GL_UNSIGNED_BYTE, texInfo->image.constBits()); + texInfo->image = texInfo->image.copy(0, 0, width, height); glDeleteTextures(1, &oldTexture); return; } @@ -267,7 +281,7 @@ void QSGDefaultDistanceFieldGlyphCache::resizeTexture(int width, int height) glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - glBindTexture(GL_TEXTURE_2D, m_textureData->texture); + glBindTexture(GL_TEXTURE_2D, texInfo->texture); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, oldWidth, oldHeight); diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h index 76e4a64..12bbcce 100644 --- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h +++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h @@ -59,37 +59,55 @@ public: void referenceGlyphs(const QSet &glyphs); void releaseGlyphs(const QSet &glyphs); - bool cacheIsFull() const { return m_textureData->currY >= maxTextureSize(); } + bool cacheIsFull() const { + return m_textureData->textures.count() == m_maxTextureCount + && textureIsFull(m_textureData->currentTexture); + } bool useWorkaroundBrokenFBOReadback() const; int maxTextureSize() const; -private: - void createTexture(int width, int height); - void resizeTexture(int width, int height); + void setMaxTextureCount(int max) { m_maxTextureCount = max; } + int maxTextureCount() const { return m_maxTextureCount; } +private: mutable int m_maxTextureSize; + int m_maxTextureCount; struct DistanceFieldTextureData : public QOpenGLSharedResource { - GLuint texture; + struct TextureInfo { + GLuint texture; + QSize size; + int currX; + int currY; + QImage image; + + TextureInfo() : texture(0), currX(0), currY(0) + { } + }; + + TextureInfo *currentTexture; + QList textures; + QHash glyphsTexture; GLuint fbo; - QSize size; QSet unusedGlyphs; - int currX; - int currY; - QImage image; QOpenGLShaderProgram *blitProgram; GLfloat blitVertexCoordinateArray[8]; GLfloat blitTextureCoordinateArray[8]; + TextureInfo *addTexture() + { + textures.append(TextureInfo()); + return &textures.last(); + } + DistanceFieldTextureData(QOpenGLContext *ctx) : QOpenGLSharedResource(ctx->shareGroup()) - , texture(0) , fbo(0) - , currX(0) - , currY(0) , blitProgram(0) { + currentTexture = addTexture(); + blitVertexCoordinateArray[0] = -1.0f; blitVertexCoordinateArray[1] = -1.0f; blitVertexCoordinateArray[2] = 1.0f; @@ -111,19 +129,26 @@ private: void invalidateResource() { - texture = 0; + glyphsTexture.clear(); + textures.clear(); fbo = 0; - size = QSize(); delete blitProgram; blitProgram = 0; + + currentTexture = addTexture(); } void freeResource(QOpenGLContext *ctx) { - glDeleteTextures(1, &texture); + glyphsTexture.clear(); + for (int i = 0; i < textures.count(); ++i) + glDeleteTextures(1, &textures[i].texture); + textures.clear(); ctx->functions()->glDeleteFramebuffers(1, &fbo); delete blitProgram; blitProgram = 0; + + currentTexture = addTexture(); } void createBlitProgram() @@ -155,6 +180,10 @@ private: } }; + void createTexture(DistanceFieldTextureData::TextureInfo * texInfo, int width, int height); + void resizeTexture(DistanceFieldTextureData::TextureInfo * texInfo, int width, int height); + bool textureIsFull (const DistanceFieldTextureData::TextureInfo *tex) const { return tex->currY >= maxTextureSize(); } + DistanceFieldTextureData *textureData(QOpenGLContext *c); DistanceFieldTextureData *m_textureData; static QHash m_textures_data; -- 2.7.4