Skip to content

Commit

Permalink
Support for multiple textures in the default distance field glyph cache.
Browse files Browse the repository at this point in the history
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 <eskil.abrahamsen-blomfeldt@nokia.com>
  • Loading branch information
Yoann Lopes authored and Qt by Nokia committed Mar 15, 2012
1 parent 1c8e65a commit b634c19
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 51 deletions.
86 changes: 50 additions & 36 deletions src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp
Expand Up @@ -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);
}
Expand All @@ -77,32 +78,40 @@ void QSGDefaultDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &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);
}
}

if (p.position.y() < maxTextureSize()) {
glyphPositions.append(p);
glyphsToRender.append(glyphIndex);
m_textureData->glyphsTexture.insert(glyphIndex, tex);
}
}

Expand All @@ -114,41 +123,46 @@ void QSGDefaultDistanceFieldGlyphCache::storeGlyphs(const QHash<glyph_t, QImage>
{
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<glyph_t> glyphTextures;
QHash<DistanceFieldTextureData::TextureInfo *, QVector<glyph_t> > glyphTextures;

QHash<glyph_t, QImage>::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<DistanceFieldTextureData::TextureInfo *, QVector<glyph_t> >::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<glyph_t> &glyphs)
Expand All @@ -161,51 +175,51 @@ void QSGDefaultDistanceFieldGlyphCache::releaseGlyphs(const QSet<glyph_t> &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);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
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;
}
Expand Down Expand Up @@ -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);

Expand Down
59 changes: 44 additions & 15 deletions src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h
Expand Up @@ -59,37 +59,55 @@ class Q_QUICK_EXPORT QSGDefaultDistanceFieldGlyphCache : public QSGDistanceField
void referenceGlyphs(const QSet<glyph_t> &glyphs);
void releaseGlyphs(const QSet<glyph_t> &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<TextureInfo> textures;
QHash<glyph_t, TextureInfo *> glyphsTexture;
GLuint fbo;
QSize size;
QSet<glyph_t> 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;
Expand All @@ -111,19 +129,26 @@ class Q_QUICK_EXPORT QSGDefaultDistanceFieldGlyphCache : public QSGDistanceField

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()
Expand Down Expand Up @@ -155,6 +180,10 @@ class Q_QUICK_EXPORT QSGDefaultDistanceFieldGlyphCache : public QSGDistanceField
}
};

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<QString, QOpenGLMultiGroupSharedResource> m_textures_data;
Expand Down

0 comments on commit b634c19

Please sign in to comment.