diff options
author | Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@nokia.com> | 2010-01-29 15:53:39 (GMT) |
---|---|---|
committer | Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@nokia.com> | 2010-01-29 15:53:39 (GMT) |
commit | da388773f581e251054abd037dc410ae52cfa69c (patch) | |
tree | 5801db3f2793ff9b9d418b25814db50880427550 | |
parent | 951922772c0c5f8b8833c2793064f8c6ebeecd9c (diff) | |
download | Qt-da388773f581e251054abd037dc410ae52cfa69c.zip Qt-da388773f581e251054abd037dc410ae52cfa69c.tar.gz Qt-da388773f581e251054abd037dc410ae52cfa69c.tar.bz2 |
Improve performance of QStaticText on OpenGL by caching data on GPU
There's a big improvement to be seen in the OpenGL engine by caching
the vertex data for the QStaticText in VBOs. In order to have the
buffers properly disposed, I've implemented a userdata concept for
QStaticTextItem. By default, the optimizations will be turned off, and
can be turned on by using the useBackendOptimizations flag.
-rw-r--r-- | src/gui/painting/qpainter.cpp | 1 | ||||
-rw-r--r-- | src/gui/text/qstatictext.cpp | 43 | ||||
-rw-r--r-- | src/gui/text/qstatictext.h | 3 | ||||
-rw-r--r-- | src/gui/text/qstatictext_p.h | 59 | ||||
-rw-r--r-- | src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp | 92 |
5 files changed, 173 insertions, 25 deletions
diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index 54eed03..521072f 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -5800,6 +5800,7 @@ void QPainter::drawStaticText(const QPointF &position, const QStaticText &static textItem->glyphPositions[i].x += fx - oldX; textItem->glyphPositions[i].y += fy - oldY; } + textItem->userDataNeedsUpdate = true; } staticText_d->position = transformedPosition; diff --git a/src/gui/text/qstatictext.cpp b/src/gui/text/qstatictext.cpp index 6c960bb..7be89cb 100644 --- a/src/gui/text/qstatictext.cpp +++ b/src/gui/text/qstatictext.cpp @@ -236,6 +236,43 @@ QString QStaticText::text() const } /*! + Sets whether the QStaticText object should use optimizations specific to the paint engine + backend if they are available. If \a on is set to true, backend optimizations will be turned + on, otherwise they will be turned off. The default value is false. + + If backend optimizations are on, the paint engine used to draw the static text is allowed to + store data in the object which will assist it in future calls to drawStaticText. In particular, + when using the opengl graphics system, or when painting on a QGLWidget, turning this flag on will + improve performance, but increase the memory footprint of the QStaticText object. + + The default value is false. + + \note This function will cause the layout of the text to be recalculated. + + \sa useBackendOptimizations() +*/ +void QStaticText::setUseBackendOptimizations(bool on) +{ + if (on == d_ptr->useBackendOptimizations) + return; + + detach(); + d_ptr->useBackendOptimizations = on; + d_ptr->init(); +} + +/*! + Returns whether the QStaticText object should use optimizations specific to the paint engine + backend when possible. By default this setting is false. + + \sa setUseBackendOptimizations() +*/ +bool QStaticText::useBackendOptimizations() const +{ + return d_ptr->useBackendOptimizations; +} + +/*! Sets the maximum size of the QStaticText to \a maximumSize. \note This function will cause the layout of the text to be recalculated. @@ -470,6 +507,12 @@ void QStaticTextPrivate::init() itemCount = counterDevice.itemCount(); items = new QStaticTextItem[itemCount]; + if (useBackendOptimizations) { + for (int i=0; i<itemCount; ++i) + items[i].useBackendOptimizations = true; + } + + int glyphCount = counterDevice.glyphCount(); glyphPool = new glyph_t[glyphCount]; positionPool = new QFixedPoint[glyphCount]; diff --git a/src/gui/text/qstatictext.h b/src/gui/text/qstatictext.h index de2fdb3..d79d887 100644 --- a/src/gui/text/qstatictext.h +++ b/src/gui/text/qstatictext.h @@ -73,6 +73,9 @@ public: void prepare(const QTransform &matrix, const QFont &font); + void setUseBackendOptimizations(bool on); + bool useBackendOptimizations() const; + QStaticText &operator=(const QStaticText &); bool operator==(const QStaticText &) const; bool operator!=(const QStaticText &) const; diff --git a/src/gui/text/qstatictext_p.h b/src/gui/text/qstatictext_p.h index 78f7896..bca59e0 100644 --- a/src/gui/text/qstatictext_p.h +++ b/src/gui/text/qstatictext_p.h @@ -57,10 +57,35 @@ QT_BEGIN_NAMESPACE +class QStaticTextUserData +{ +public: + enum Type { + NoUserData, + OpenGLUserData + }; + + QStaticTextUserData(Type t) : type(t) {} + virtual ~QStaticTextUserData() {} + + Type type; +}; + class Q_GUI_EXPORT QStaticTextItem { public: - QStaticTextItem() : chars(0), numChars(0), fontEngine(0) {} + QStaticTextItem() : chars(0), numChars(0), fontEngine(0), userData(0), + useBackendOptimizations(false), userDataNeedsUpdate(0) {} + ~QStaticTextItem() { delete userData; } + + void setUserData(QStaticTextUserData *newUserData) + { + if (userData == newUserData) + return; + + delete userData; + userData = newUserData; + } QFixedPoint *glyphPositions; // 8 bytes per glyph glyph_t *glyphs; // 4 bytes per glyph @@ -73,8 +98,11 @@ public: int numChars; // 4 bytes per item QFontEngine *fontEngine; // 4 bytes per item QFont font; // 8 bytes per item + QStaticTextUserData *userData; // 8 bytes per item + char useBackendOptimizations : 1; // 1 byte per item + char userDataNeedsUpdate : 1; // // ================ - // 32 bytes per item + // 41 bytes per item }; class QStaticText; @@ -86,22 +114,23 @@ public: void init(); - QAtomicInt ref; // 4 bytes per text + QAtomicInt ref; // 4 bytes per text - QString text; // 4 bytes per text - QFont font; // 8 bytes per text - QSizeF size; // 16 bytes per text - QPointF position; // 16 bytes per text + QString text; // 4 bytes per text + QFont font; // 8 bytes per text + QSizeF size; // 16 bytes per text + QPointF position; // 16 bytes per text - QTransform matrix; // 80 bytes per text - QStaticTextItem *items; // 4 bytes per text - int itemCount; // 4 bytes per text - glyph_t *glyphPool; // 4 bytes per text - QFixedPoint *positionPool; // 4 bytes per text + QTransform matrix; // 80 bytes per text + QStaticTextItem *items; // 4 bytes per text + int itemCount; // 4 bytes per text + glyph_t *glyphPool; // 4 bytes per text + QFixedPoint *positionPool; // 4 bytes per text - char needsClipRect : 1; // 1 byte per text - // ================ - // 145 bytes per text + char needsClipRect : 1; // 1 byte per text + char useBackendOptimizations : 1; + // ================ + // 145 bytes per text static QStaticTextPrivate *get(const QStaticText *q); }; diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp index 4c3b343..f6ef827 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp @@ -1284,6 +1284,29 @@ void QGL2PaintEngineEx::drawTextItem(const QPointF &p, const QTextItem &textItem QPaintEngineEx::drawTextItem(p, ti); } +namespace { + + class QOpenGLStaticTextUserData: public QStaticTextUserData + { + public: + QOpenGLStaticTextUserData(QGLContext *glContext) + : QStaticTextUserData(OpenGLUserData), + vertexCoordVBOId(0), textureCoordVBOId(0), ctx(glContext) {} + ~QOpenGLStaticTextUserData() + { + if (vertexCoordVBOId != 0) + glDeleteBuffers(1, &vertexCoordVBOId); + + if (textureCoordVBOId != 0) + glDeleteBuffers(1, &textureCoordVBOId); + } + + QGLContext *ctx; + GLuint vertexCoordVBOId; + GLuint textureCoordVBOId; + }; +} + void QGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngineGlyphCache::Type glyphType, QStaticTextItem *staticTextItem) { Q_Q(QGL2PaintEngineEx); @@ -1309,20 +1332,66 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngineGlyphCache::Type glyp GLfloat dx = 1.0 / cache->width(); GLfloat dy = 1.0 / cache->height(); - vertexCoordinateArray.clear(); - textureCoordinateArray.clear(); + if (staticTextItem->userData == 0 + || staticTextItem->userData->type != QStaticTextUserData::OpenGLUserData + || staticTextItem->userDataNeedsUpdate) { + vertexCoordinateArray.clear(); + textureCoordinateArray.clear(); + + for (int i=0; i<staticTextItem->numGlyphs; ++i) { + const QTextureGlyphCache::Coord &c = cache->coords.value(staticTextItem->glyphs[i]); + int x = staticTextItem->glyphPositions[i].x.toInt() + c.baseLineX - margin; + int y = staticTextItem->glyphPositions[i].y.toInt() - c.baseLineY - margin; + + vertexCoordinateArray.addRect(QRectF(x, y, c.w, c.h)); + textureCoordinateArray.addRect(QRectF(c.x*dx, c.y*dy, c.w * dx, c.h * dy)); + } + + + if (staticTextItem->useBackendOptimizations) { + QOpenGLStaticTextUserData *userData = + staticTextItem->userData != 0 && staticTextItem->userData->type == QStaticTextUserData::OpenGLUserData + ? static_cast<QOpenGLStaticTextUserData *>(staticTextItem->userData) + : new QOpenGLStaticTextUserData(ctx); + + int vertexCoordinateArraySize = vertexCoordinateArray.vertexCount() * sizeof(QGLPoint); + if (userData->vertexCoordVBOId == 0) + glGenBuffers(1, &userData->vertexCoordVBOId); + + int textureCoordinateArraySize = textureCoordinateArray.vertexCount() * sizeof(QGLPoint); + if (userData->textureCoordVBOId == 0) + glGenBuffers(1, &userData->textureCoordVBOId); + + glBindBuffer(GL_ARRAY_BUFFER_ARB, userData->vertexCoordVBOId); + glBufferData(GL_ARRAY_BUFFER_ARB, vertexCoordinateArraySize, + vertexCoordinateArray.data(), GL_STATIC_DRAW_ARB); + + glBindBuffer(GL_ARRAY_BUFFER_ARB, userData->textureCoordVBOId); + glBufferData(GL_ARRAY_BUFFER_ARB, textureCoordinateArraySize, + textureCoordinateArray.data(), GL_STATIC_DRAW_ARB); - for (int i=0; i<staticTextItem->numGlyphs; ++i) { - const QTextureGlyphCache::Coord &c = cache->coords.value(staticTextItem->glyphs[i]); - int x = staticTextItem->glyphPositions[i].x.toInt() + c.baseLineX - margin; - int y = staticTextItem->glyphPositions[i].y.toInt() - c.baseLineY - margin; + // If a new user data has been created, make sure we delete the old + staticTextItem->setUserData(userData); + staticTextItem->userDataNeedsUpdate = false; - vertexCoordinateArray.addRect(QRectF(x, y, c.w, c.h)); - textureCoordinateArray.addRect(QRectF(c.x*dx, c.y*dy, c.w * dx, c.h * dy)); + } else { + setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, (GLfloat*)vertexCoordinateArray.data()); + setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, (GLfloat*)textureCoordinateArray.data()); + } } - setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, (GLfloat*)vertexCoordinateArray.data()); - setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, (GLfloat*)textureCoordinateArray.data()); + if (staticTextItem->useBackendOptimizations) { + Q_ASSERT(staticTextItem->userData != 0); + Q_ASSERT(staticTextItem->userData->type == QStaticTextUserData::OpenGLUserData); + + QOpenGLStaticTextUserData *userData = static_cast<QOpenGLStaticTextUserData *>(staticTextItem->userData); + + glBindBuffer(GL_ARRAY_BUFFER_ARB, userData->vertexCoordVBOId); + glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, 0); + + glBindBuffer(GL_ARRAY_BUFFER_ARB, userData->textureCoordVBOId); + glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, 0); + } if (addOffset) { addOffset = false; @@ -1420,6 +1489,9 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngineGlyphCache::Type glyp shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::MaskTexture), QT_MASK_TEXTURE_UNIT); glDrawArrays(GL_TRIANGLES, 0, 6 * staticTextItem->numGlyphs); + + // Reset bindings + glBindBuffer(GL_ARRAY_BUFFER_ARB, 0); } void QGL2PaintEngineEx::drawPixmaps(const QDrawPixmaps::Data *drawingData, int dataCount, const QPixmap &pixmap, QDrawPixmaps::DrawingHints hints) |