src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
branchGCC_SURGE
changeset 31 5daf16870df6
parent 30 5dc02b23752f
child 33 3e2da88830cd
--- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp	Mon Jun 21 22:38:13 2010 +0100
+++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp	Thu Jul 22 16:41:55 2010 +0100
@@ -64,6 +64,7 @@
 
 // #define QT_OPENGL_CACHE_AS_VBOS
 
+#include "qglgradientcache_p.h"
 #include "qpaintengineex_opengl2_p.h"
 
 #include <string.h> //for memcpy
@@ -77,8 +78,9 @@
 #include <private/qfontengine_p.h>
 #include <private/qpixmapdata_gl_p.h>
 #include <private/qdatabuffer_p.h>
+#include <private/qstatictext_p.h>
+#include <private/qtriangulator_p.h>
 
-#include "qglgradientcache_p.h"
 #include "qglengineshadermanager_p.h"
 #include "qgl2pexvertexarray_p.h"
 #include "qtriangulatingstroker_p.h"
@@ -107,6 +109,11 @@
         e->data = 0;
         e->engine = 0;
     }
+
+    if (elementIndicesVBOId != 0) {
+        glDeleteBuffers(1, &elementIndicesVBOId);
+        elementIndicesVBOId = 0;
+    }
 }
 
 void QGL2PaintEngineExPrivate::updateTextureFilter(GLenum target, GLenum wrapMode, bool smoothPixmapTransform, GLuint id)
@@ -210,7 +217,9 @@
         const QPixmap& texPixmap = currentBrush.texture();
 
         glActiveTexture(GL_TEXTURE0 + QT_BRUSH_TEXTURE_UNIT);
-        QGLTexture *tex = ctx->d_func()->bindTexture(texPixmap, GL_TEXTURE_2D, GL_RGBA, QGLContext::InternalBindOption);
+        QGLTexture *tex = ctx->d_func()->bindTexture(texPixmap, GL_TEXTURE_2D, GL_RGBA,
+                                                     QGLContext::InternalBindOption |
+                                                     QGLContext::CanFlipNativePixmapBindOption);
         updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, q->state()->renderHints & QPainter::SmoothPixmapTransform);
         textureInvertedY = tex->options & QGLContext::InvertedYBindOption ? -1 : 1;
     }
@@ -388,6 +397,7 @@
                         qreal(0.0001));
 
     matrixDirty = false;
+    matrixUniformDirty = true;
 
     // Set the PMV matrix attribute. As we use an attributes rather than uniforms, we only
     // need to do this once for every matrix change and persists across all shader programs.
@@ -506,6 +516,8 @@
     ensureActive();
     d->transferMode(BrushDrawingMode);
 
+    d->nativePaintingActive = true;
+
     QGLContext *ctx = d->ctx;
     glUseProgram(0);
 
@@ -521,10 +533,10 @@
 
     float mv_matrix[4][4] =
     {
-        { mtx.m11(), mtx.m12(),     0, mtx.m13() },
-        { mtx.m21(), mtx.m22(),     0, mtx.m23() },
-        {         0,         0,     1,         0 },
-        {  mtx.dx(),  mtx.dy(),     0, mtx.m33() }
+        { float(mtx.m11()), float(mtx.m12()),     0, float(mtx.m13()) },
+        { float(mtx.m21()), float(mtx.m22()),     0, float(mtx.m23()) },
+        {                0,                0,     1,                0 },
+        {  float(mtx.dx()),  float(mtx.dy()),     0, float(mtx.m33()) }
     };
 
     const QSize sz = d->device->size();
@@ -561,9 +573,9 @@
     glStencilMask(0xff);
     glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
     glStencilFunc(GL_ALWAYS, 0, 0xff);
-    glDisableVertexAttribArray(QT_TEXTURE_COORDS_ATTR);
-    glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
-    glDisableVertexAttribArray(QT_OPACITY_ATTR);
+    ctx->d_func()->setVertexAttribArrayEnabled(QT_TEXTURE_COORDS_ATTR, false);
+    ctx->d_func()->setVertexAttribArrayEnabled(QT_VERTEX_COORDS_ATTR, false);
+    ctx->d_func()->setVertexAttribArrayEnabled(QT_OPACITY_ATTR, false);
 #ifndef QT_OPENGL_ES_2
     glColor4f(1.0f, 1.0f, 1.0f, 1.0f); // color may have been changed by glVertexAttrib()
 #endif
@@ -573,6 +585,12 @@
 {
     Q_D(QGL2PaintEngineEx);
     d->needsSync = true;
+    d->nativePaintingActive = false;
+}
+
+bool QGL2PaintEngineEx::isNativePaintingActive() const {
+    Q_D(const QGL2PaintEngineEx);
+    return d->nativePaintingActive;
 }
 
 void QGL2PaintEngineExPrivate::transferMode(EngineMode newMode)
@@ -587,6 +605,9 @@
     if (newMode == TextDrawingMode) {
         setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, (GLfloat*)vertexCoordinateArray.data());
         setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, (GLfloat*)textureCoordinateArray.data());
+        shaderManager->setHasComplexGeometry(true);
+    } else {
+        shaderManager->setHasComplexGeometry(false);
     }
 
     if (newMode == ImageDrawingMode) {
@@ -611,10 +632,13 @@
 {
 #ifdef QT_OPENGL_CACHE_AS_VBOS
     GLuint vbo;
+    GLuint ibo;
 #else
     float *vertices;
+    quint32 *indices;
 #endif
     int vertexCount;
+    int indexCount;
     GLenum primitiveType;
     qreal iscale;
 };
@@ -625,9 +649,12 @@
 #ifdef QT_OPENGL_CACHE_AS_VBOS
     Q_ASSERT(engine->type() == QPaintEngine::OpenGL2);
     static_cast<QGL2PaintEngineEx *>(engine)->d_func()->unusedVBOSToClean << c->vbo;
+    if (c->ibo)
+        d->unusedIBOSToClean << c->ibo;
 #else
     Q_UNUSED(engine);
     qFree(c->vertices);
+    qFree(c->indices);
 #endif
     delete c;
 }
@@ -658,6 +685,9 @@
 
     const QPointF* const points = reinterpret_cast<const QPointF*>(path.points());
 
+    // ### Remove before release...
+    static bool do_vectorpath_cache = qgetenv("QT_OPENGL_NO_PATH_CACHE").isEmpty();
+
     // Check to see if there's any hints
     if (path.shape() == QVectorPath::RectangleHint) {
         QGLRect rect(points[0].x(), points[0].y(), points[2].x(), points[2].y());
@@ -669,6 +699,8 @@
             QVectorPath::CacheEntry *data = path.lookupCacheData(q);
             QGL2PEVectorPathCache *cache;
 
+            bool updateCache = false;
+
             if (data) {
                 cache = (QGL2PEVectorPathCache *) data->data;
                 // Check if scale factor is exceeded for curved paths and generate curves if so...
@@ -678,34 +710,39 @@
 #ifdef QT_OPENGL_CACHE_AS_VBOS
                         glDeleteBuffers(1, &cache->vbo);
                         cache->vbo = 0;
+                        Q_ASSERT(cache->ibo == 0);
 #else
                         qFree(cache->vertices);
+                        Q_ASSERT(cache->indices == 0);
 #endif
-                        cache->vertexCount = 0;
+                        updateCache = true;
                     }
                 }
             } else {
                 cache = new QGL2PEVectorPathCache;
-                cache->vertexCount = 0;
                 data = const_cast<QVectorPath &>(path).addCacheData(q, cache, cleanupVectorPath);
+                updateCache = true;
             }
 
             // Flatten the path at the current scale factor and fill it into the cache struct.
-            if (!cache->vertexCount) {
+            if (updateCache) {
                 vertexCoordinateArray.clear();
                 vertexCoordinateArray.addPath(path, inverseScale, false);
                 int vertexCount = vertexCoordinateArray.vertexCount();
                 int floatSizeInBytes = vertexCount * 2 * sizeof(float);
                 cache->vertexCount = vertexCount;
+                cache->indexCount = 0;
                 cache->primitiveType = GL_TRIANGLE_FAN;
                 cache->iscale = inverseScale;
 #ifdef QT_OPENGL_CACHE_AS_VBOS
                 glGenBuffers(1, &cache->vbo);
                 glBindBuffer(GL_ARRAY_BUFFER, cache->vbo);
                 glBufferData(GL_ARRAY_BUFFER, floatSizeInBytes, vertexCoordinateArray.data(), GL_STATIC_DRAW);
+                cache->ibo = 0;
 #else
                 cache->vertices = (float *) qMalloc(floatSizeInBytes);
                 memcpy(cache->vertices, vertexCoordinateArray.data(), floatSizeInBytes);
+                cache->indices = 0;
 #endif
             }
 
@@ -721,8 +758,6 @@
         } else {
       //        printf(" - Marking path as cachable...\n");
             // Tag it for later so that if the same path is drawn twice, it is assumed to be static and thus cachable
-            // ### Remove before release...
-            static bool do_vectorpath_cache = qgetenv("QT_OPENGL_NO_PATH_CACHE").isEmpty();
             if (do_vectorpath_cache)
                 path.makeCacheable();
             vertexCoordinateArray.clear();
@@ -732,31 +767,117 @@
         }
 
     } else {
-        // The path is too complicated & needs the stencil technique
-        vertexCoordinateArray.clear();
-        vertexCoordinateArray.addPath(path, inverseScale, false);
+        bool useCache = path.isCacheable();
+        if (useCache) {
+            QRectF bbox = path.controlPointRect();
+            // If the path doesn't fit within these limits, it is possible that the triangulation will fail.
+            useCache &= (bbox.left() > -0x8000 * inverseScale)
+                     && (bbox.right() < 0x8000 * inverseScale)
+                     && (bbox.top() > -0x8000 * inverseScale)
+                     && (bbox.bottom() < 0x8000 * inverseScale);
+        }
+
+        if (useCache) {
+            QVectorPath::CacheEntry *data = path.lookupCacheData(q);
+            QGL2PEVectorPathCache *cache;
+
+            bool updateCache = false;
 
-        fillStencilWithVertexArray(vertexCoordinateArray, path.hasWindingFill());
+            if (data) {
+                cache = (QGL2PEVectorPathCache *) data->data;
+                // Check if scale factor is exceeded for curved paths and generate curves if so...
+                if (path.isCurved()) {
+                    qreal scaleFactor = cache->iscale / inverseScale;
+                    if (scaleFactor < 0.5 || scaleFactor > 2.0) {
+#ifdef QT_OPENGL_CACHE_AS_VBOS
+                        glDeleteBuffers(1, &cache->vbo);
+                        glDeleteBuffers(1, &cache->ibo);
+#else
+                        qFree(cache->vertices);
+                        qFree(cache->indices);
+#endif
+                        updateCache = true;
+                    }
+                }
+            } else {
+                cache = new QGL2PEVectorPathCache;
+                data = const_cast<QVectorPath &>(path).addCacheData(q, cache, cleanupVectorPath);
+                updateCache = true;
+            }
 
-        glStencilMask(0xff);
-        glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
+            // Flatten the path at the current scale factor and fill it into the cache struct.
+            if (updateCache) {
+                QTriangleSet polys = qTriangulate(path, QTransform().scale(1 / inverseScale, 1 / inverseScale));
+                cache->vertexCount = polys.vertices.size() / 2;
+                cache->indexCount = polys.indices.size();
+                cache->primitiveType = GL_TRIANGLES;
+                cache->iscale = inverseScale;
+
+#ifdef QT_OPENGL_CACHE_AS_VBOS
+                glGenBuffers(1, &cache->vbo);
+                glGenBuffers(1, &cache->ibo);
+                glBindBuffer(GL_ARRAY_BUFFER, cache->vbo);
+                glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cache->ibo);
+                glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(quint32) * polys.indices.size(), polys.indices.data(), GL_STATIC_DRAW);
 
-        if (q->state()->clipTestEnabled) {
-            // Pass when high bit is set, replace stencil value with current clip
-            glStencilFunc(GL_NOTEQUAL, q->state()->currentClip, GL_STENCIL_HIGH_BIT);
-        } else if (path.hasWindingFill()) {
-            // Pass when any bit is set, replace stencil value with 0
-            glStencilFunc(GL_NOTEQUAL, 0, 0xff);
+                QVarLengthArray<float> vertices(polys.vertices.size());
+                for (int i = 0; i < polys.vertices.size(); ++i)
+                    vertices[i] = float(inverseScale * polys.vertices.at(i));
+                glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vertices.size(), vertices.data(), GL_STATIC_DRAW);
+#else
+                cache->vertices = (float *) qMalloc(sizeof(float) * polys.vertices.size());
+                cache->indices = (quint32 *) qMalloc(sizeof(quint32) * polys.indices.size());
+                memcpy(cache->indices, polys.indices.data(), sizeof(quint32) * polys.indices.size());
+                for (int i = 0; i < polys.vertices.size(); ++i)
+                    cache->vertices[i] = float(inverseScale * polys.vertices.at(i));
+#endif
+            }
+
+            prepareForDraw(currentBrush.isOpaque());
+#ifdef QT_OPENGL_CACHE_AS_VBOS
+            glBindBuffer(GL_ARRAY_BUFFER, cache->vbo);
+            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cache->ibo);
+            setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, 0);
+            glDrawElements(cache->primitiveType, cache->indexCount, GL_UNSIGNED_INT, 0);
+            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+            glBindBuffer(GL_ARRAY_BUFFER, 0);
+#else
+            setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, cache->vertices);
+            glDrawElements(cache->primitiveType, cache->indexCount, GL_UNSIGNED_INT, cache->indices);
+#endif
+
         } else {
-            // Pass when high bit is set, replace stencil value with 0
-            glStencilFunc(GL_NOTEQUAL, 0, GL_STENCIL_HIGH_BIT);
-        }
-        prepareForDraw(currentBrush.isOpaque());
+      //        printf(" - Marking path as cachable...\n");
+            // Tag it for later so that if the same path is drawn twice, it is assumed to be static and thus cachable
+            if (do_vectorpath_cache)
+                path.makeCacheable();
+
+            // The path is too complicated & needs the stencil technique
+            vertexCoordinateArray.clear();
+            vertexCoordinateArray.addPath(path, inverseScale, false);
+
+            fillStencilWithVertexArray(vertexCoordinateArray, path.hasWindingFill());
+
+            glStencilMask(0xff);
+            glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
 
-        // Stencil the brush onto the dest buffer
-        composite(vertexCoordinateArray.boundingRect());
-        glStencilMask(0);
-        updateClipScissorTest();
+            if (q->state()->clipTestEnabled) {
+                // Pass when high bit is set, replace stencil value with current clip
+                glStencilFunc(GL_NOTEQUAL, q->state()->currentClip, GL_STENCIL_HIGH_BIT);
+            } else if (path.hasWindingFill()) {
+                // Pass when any bit is set, replace stencil value with 0
+                glStencilFunc(GL_NOTEQUAL, 0, 0xff);
+            } else {
+                // Pass when high bit is set, replace stencil value with 0
+                glStencilFunc(GL_NOTEQUAL, 0, GL_STENCIL_HIGH_BIT);
+            }
+            prepareForDraw(currentBrush.isOpaque());
+
+            // Stencil the brush onto the dest buffer
+            composite(vertexCoordinateArray.boundingRect());
+            glStencilMask(0);
+            updateClipScissorTest();
+        }
     }
 }
 
@@ -938,6 +1059,7 @@
         // The shader program has changed so mark all uniforms as dirty:
         brushUniformsDirty = true;
         opacityUniformDirty = true;
+        matrixUniformDirty = true;
     }
 
     if (brushUniformsDirty && mode != ImageDrawingMode && mode != ImageArrayDrawingMode)
@@ -948,6 +1070,12 @@
         opacityUniformDirty = false;
     }
 
+    if (matrixUniformDirty && shaderManager->hasComplexGeometry()) {
+        shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::Matrix),
+                                                         pmvMatrix);
+        matrixUniformDirty = false;
+    }
+
     return changed;
 }
 
@@ -1048,16 +1176,20 @@
     // prepareForDraw() down below.
     updateMatrix();
 
+    QRectF clip = q->state()->matrix.inverted().mapRect(q->state()->clipEnabled
+                                                        ? q->state()->rectangleClip
+                                                        : QRectF(0, 0, width, height));
+
     if (penStyle == Qt::SolidLine) {
-        stroker.process(path, pen);
+        stroker.process(path, pen, clip);
 
     } else { // Some sort of dash
-        dasher.process(path, pen);
+        dasher.process(path, pen, clip);
 
         QVectorPath dashStroke(dasher.points(),
                                dasher.elementCount(),
                                dasher.elementTypes());
-        stroker.process(dashStroke, pen);
+        stroker.process(dashStroke, pen, clip);
     }
 
     if (opaque) {
@@ -1192,9 +1324,25 @@
     d->drawTexture(dest, src, image.size(), !image.hasAlphaChannel());
 }
 
-void QGL2PaintEngineEx::drawTexture(const QRectF &dest, GLuint textureId, const QSize &size, const QRectF &src)
+void QGL2PaintEngineEx::drawStaticTextItem(QStaticTextItem *textItem)
 {
     Q_D(QGL2PaintEngineEx);
+
+    ensureActive();
+
+    QFontEngineGlyphCache::Type glyphType = textItem->fontEngine->glyphFormat >= 0
+                                            ? QFontEngineGlyphCache::Type(textItem->fontEngine->glyphFormat)
+                                            : d->glyphCacheType;
+
+    d->drawCachedGlyphs(glyphType, textItem, true);
+}
+
+bool QGL2PaintEngineEx::drawTexture(const QRectF &dest, GLuint textureId, const QSize &size, const QRectF &src)
+{
+    Q_D(QGL2PaintEngineEx);
+    if (!d->shaderManager)
+        return false;
+
     ensureActive();
     d->transferMode(ImageDrawingMode);
 
@@ -1209,6 +1357,7 @@
     d->updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE,
                            state()->renderHints & QPainter::SmoothPixmapTransform, textureId);
     d->drawTexture(dest, srcRect, size, false);
+    return true;
 }
 
 void QGL2PaintEngineEx::drawTextItem(const QPointF &p, const QTextItem &textItem)
@@ -1245,33 +1394,72 @@
     }
 
     if (drawCached) {
-        d->drawCachedGlyphs(p, glyphType, ti);
+        QVarLengthArray<QFixedPoint> positions;
+        QVarLengthArray<glyph_t> glyphs;
+        QTransform matrix = QTransform::fromTranslate(p.x(), p.y());
+        ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
+
+        {
+            QStaticTextItem staticTextItem;
+            staticTextItem.chars = ti.chars;
+            staticTextItem.fontEngine = ti.fontEngine;
+            staticTextItem.glyphs = glyphs.data();
+            staticTextItem.numChars = ti.num_chars;
+            staticTextItem.numGlyphs = glyphs.size();
+            staticTextItem.glyphPositions = positions.data();
+
+            d->drawCachedGlyphs(glyphType, &staticTextItem, false);
+        }
         return;
     }
 
     QPaintEngineEx::drawTextItem(p, ti);
 }
 
-void QGL2PaintEngineExPrivate::drawCachedGlyphs(const QPointF &p, QFontEngineGlyphCache::Type glyphType,
-                                                const QTextItemInt &ti)
+namespace {
+
+    class QOpenGLStaticTextUserData: public QStaticTextUserData
+    {
+    public:
+        QOpenGLStaticTextUserData()
+            : QStaticTextUserData(OpenGLUserData)
+        {
+        }
+
+        ~QOpenGLStaticTextUserData()
+        {
+        }
+
+        QGL2PEXVertexArray vertexCoordinateArray;
+        QGL2PEXVertexArray textureCoordinateArray;
+    };
+
+}
+
+// #define QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO
+
+void QGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngineGlyphCache::Type glyphType,
+                                                QStaticTextItem *staticTextItem,
+                                                bool includeMatrixInCache)
 {
     Q_Q(QGL2PaintEngineEx);
 
-    QVarLengthArray<QFixedPoint> positions;
-    QVarLengthArray<glyph_t> glyphs;
-    QTransform matrix = QTransform::fromTranslate(p.x(), p.y());
-    ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
+    QOpenGL2PaintEngineState *s = q->state();
 
     QGLTextureGlyphCache *cache =
-            (QGLTextureGlyphCache *) ti.fontEngine->glyphCache(ctx, glyphType, QTransform());
-
+        (QGLTextureGlyphCache *) staticTextItem->fontEngine->glyphCache(ctx, glyphType,
+                                                                        includeMatrixInCache
+                                                                          ? s->matrix
+                                                                          : QTransform());
     if (!cache || cache->cacheType() != glyphType) {
-        cache = new QGLTextureGlyphCache(ctx, glyphType, QTransform());
-        ti.fontEngine->setGlyphCache(ctx, cache);
+        cache = new QGLTextureGlyphCache(ctx, glyphType,
+                                         includeMatrixInCache ? s->matrix : QTransform());
+        staticTextItem->fontEngine->setGlyphCache(ctx, cache);
     }
 
     cache->setPaintEnginePrivate(this);
-    cache->populate(ti, glyphs, positions);
+    cache->populate(staticTextItem->fontEngine, staticTextItem->numGlyphs, staticTextItem->glyphs,
+                    staticTextItem->glyphPositions);
 
     if (cache->width() == 0 || cache->height() == 0)
         return;
@@ -1283,20 +1471,83 @@
     GLfloat dx = 1.0 / cache->width();
     GLfloat dy = 1.0 / cache->height();
 
-    vertexCoordinateArray.clear();
-    textureCoordinateArray.clear();
+    bool recreateVertexArrays = false;
+    if (staticTextItem->userDataNeedsUpdate)
+        recreateVertexArrays = true;
+    else if (staticTextItem->userData == 0)
+        recreateVertexArrays = true;
+    else if (staticTextItem->userData->type != QStaticTextUserData::OpenGLUserData)
+        recreateVertexArrays = true;
+
+    // Use global arrays by default
+    QGL2PEXVertexArray *vertexCoordinates = &vertexCoordinateArray;
+    QGL2PEXVertexArray *textureCoordinates = &textureCoordinateArray;
 
-    for (int i=0; i<glyphs.size(); ++i) {
-        const QTextureGlyphCache::Coord &c = cache->coords.value(glyphs[i]);
-        int x = positions[i].x.toInt() + c.baseLineX - margin;
-        int y = positions[i].y.toInt() - c.baseLineY - margin;
+    if (staticTextItem->useBackendOptimizations) {
+        QOpenGLStaticTextUserData *userData = 0;
+
+        if (staticTextItem->userData == 0
+            || staticTextItem->userData->type != QStaticTextUserData::OpenGLUserData) {
 
-        vertexCoordinateArray.addRect(QRectF(x, y, c.w, c.h));
-        textureCoordinateArray.addRect(QRectF(c.x*dx, c.y*dy, c.w * dx, c.h * dy));
+            userData = new QOpenGLStaticTextUserData();
+            staticTextItem->setUserData(userData);
+
+        } else {
+            userData = static_cast<QOpenGLStaticTextUserData*>(staticTextItem->userData);
+        }
+
+        // Use cache if backend optimizations is turned on
+        vertexCoordinates = &userData->vertexCoordinateArray;
+        textureCoordinates = &userData->textureCoordinateArray;
     }
 
-    setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, (GLfloat*)vertexCoordinateArray.data());
-    setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, (GLfloat*)textureCoordinateArray.data());
+
+    if (recreateVertexArrays) {
+        vertexCoordinates->clear();
+        textureCoordinates->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;
+
+            vertexCoordinates->addQuad(QRectF(x, y, c.w, c.h));
+            textureCoordinates->addQuad(QRectF(c.x*dx, c.y*dy, c.w * dx, c.h * dy));
+        }
+
+        staticTextItem->userDataNeedsUpdate = false;
+    }
+
+    if (elementIndices.size() < staticTextItem->numGlyphs*6) {
+        Q_ASSERT(elementIndices.size() % 6 == 0);
+        int j = elementIndices.size() / 6 * 4;
+        while (j < staticTextItem->numGlyphs*4) {
+            elementIndices.append(j + 0);
+            elementIndices.append(j + 0);
+            elementIndices.append(j + 1);
+            elementIndices.append(j + 2);
+            elementIndices.append(j + 3);
+            elementIndices.append(j + 3);
+
+            j += 4;
+        }
+
+#if defined(QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO)
+        if (elementIndicesVBOId == 0)
+            glGenBuffers(1, &elementIndicesVBOId);
+
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementIndicesVBOId);
+        glBufferData(GL_ELEMENT_ARRAY_BUFFER, elementIndices.size() * sizeof(GLushort),
+                     elementIndices.constData(), GL_STATIC_DRAW);
+#endif
+    } else {
+#if defined(QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO)
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementIndicesVBOId);
+#endif
+    }
+
+    setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, (GLfloat*)vertexCoordinates->data());
+    setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, (GLfloat*)textureCoordinates->data());
 
     if (addOffset) {
         addOffset = false;
@@ -1310,6 +1561,13 @@
     QBrush pensBrush = q->state()->pen.brush();
     setBrush(pensBrush);
 
+    // When painting a QStaticTextItem, the glyph positions are already in device coordinates,
+    // therefore we temporarily set an identity matrix on the painter for the draw call to
+    // avoid transforming the positions twice.
+    QTransform old = s->matrix;
+    if (includeMatrixInCache)
+        s->matrix = QTransform();
+
     if (glyphType == QFontEngineGlyphCache::Raster_RGBMask) {
 
         // Subpixel antialiasing without gamma correction
@@ -1363,7 +1621,11 @@
             updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, false);
 
             shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::MaskTexture), QT_MASK_TEXTURE_UNIT);
-            glDrawArrays(GL_TRIANGLES, 0, 6 * glyphs.size());
+#if defined(QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO)
+            glDrawElements(GL_TRIANGLE_STRIP, 6 * staticTextItem->numGlyphs, GL_UNSIGNED_SHORT, 0);
+#else
+            glDrawElements(GL_TRIANGLE_STRIP, 6 * staticTextItem->numGlyphs, GL_UNSIGNED_SHORT, elementIndices.data());
+#endif
 
             shaderManager->setMaskType(QGLEngineShaderManager::SubPixelMaskPass2);
 
@@ -1389,28 +1651,42 @@
     //### TODO: Gamma correction
 
     glActiveTexture(GL_TEXTURE0 + QT_MASK_TEXTURE_UNIT);
-    glBindTexture(GL_TEXTURE_2D, cache->texture());
-    updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, false);
+    if (lastMaskTextureUsed != cache->texture()) {
+        glBindTexture(GL_TEXTURE_2D, cache->texture());
+        lastMaskTextureUsed = cache->texture();
+    }
+    updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, s->matrix.type() > QTransform::TxTranslate);
+    shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::MaskTexture), QT_MASK_TEXTURE_UNIT);
 
-    shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::MaskTexture), QT_MASK_TEXTURE_UNIT);
-    glDrawArrays(GL_TRIANGLES, 0, 6 * glyphs.size());
+#if defined(QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO)
+    glDrawElements(GL_TRIANGLE_STRIP, 6 * staticTextItem->numGlyphs, GL_UNSIGNED_SHORT, 0);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+#else
+    glDrawElements(GL_TRIANGLE_STRIP, 6 * staticTextItem->numGlyphs, GL_UNSIGNED_SHORT, elementIndices.data());
+#endif
+
+    if (includeMatrixInCache)
+        s->matrix = old;
 }
 
-void QGL2PaintEngineEx::drawPixmaps(const QDrawPixmaps::Data *drawingData, int dataCount, const QPixmap &pixmap, QDrawPixmaps::DrawingHints hints)
+void QGL2PaintEngineEx::drawPixmapFragments(const QPainter::PixmapFragment *fragments, int fragmentCount, const QPixmap &pixmap,
+                                            QPainter::PixmapFragmentHints hints)
 {
     Q_D(QGL2PaintEngineEx);
     // Use fallback for extended composition modes.
     if (state()->composition_mode > QPainter::CompositionMode_Plus) {
-        QPaintEngineEx::drawPixmaps(drawingData, dataCount, pixmap, hints);
+        QPaintEngineEx::drawPixmapFragments(fragments, fragmentCount, pixmap, hints);
         return;
     }
 
     ensureActive();
-    d->drawPixmaps(drawingData, dataCount, pixmap, hints);
+    d->drawPixmapFragments(fragments, fragmentCount, pixmap, hints);
 }
 
 
-void QGL2PaintEngineExPrivate::drawPixmaps(const QDrawPixmaps::Data *drawingData, int dataCount, const QPixmap &pixmap, QDrawPixmaps::DrawingHints hints)
+void QGL2PaintEngineExPrivate::drawPixmapFragments(const QPainter::PixmapFragment *fragments,
+                                                   int fragmentCount, const QPixmap &pixmap,
+                                                   QPainter::PixmapFragmentHints hints)
 {
     GLfloat dx = 1.0f / pixmap.size().width();
     GLfloat dy = 1.0f / pixmap.size().height();
@@ -1431,37 +1707,38 @@
 
     bool allOpaque = true;
 
-    for (int i = 0; i < dataCount; ++i) {
+    for (int i = 0; i < fragmentCount; ++i) {
         qreal s = 0;
         qreal c = 1;
-        if (drawingData[i].rotation != 0) {
-            s = qFastSin(drawingData[i].rotation * Q_PI / 180);
-            c = qFastCos(drawingData[i].rotation * Q_PI / 180);
+        if (fragments[i].rotation != 0) {
+            s = qFastSin(fragments[i].rotation * Q_PI / 180);
+            c = qFastCos(fragments[i].rotation * Q_PI / 180);
         }
 
-        qreal right = 0.5 * drawingData[i].scaleX * drawingData[i].source.width();
-        qreal bottom = 0.5 * drawingData[i].scaleY * drawingData[i].source.height();
+        qreal right = 0.5 * fragments[i].scaleX * fragments[i].width;
+        qreal bottom = 0.5 * fragments[i].scaleY * fragments[i].height;
         QGLPoint bottomRight(right * c - bottom * s, right * s + bottom * c);
         QGLPoint bottomLeft(-right * c - bottom * s, -right * s + bottom * c);
 
-        vertexCoordinateArray.lineToArray(bottomRight.x + drawingData[i].point.x(), bottomRight.y + drawingData[i].point.y());
-        vertexCoordinateArray.lineToArray(-bottomLeft.x + drawingData[i].point.x(), -bottomLeft.y + drawingData[i].point.y());
-        vertexCoordinateArray.lineToArray(-bottomRight.x + drawingData[i].point.x(), -bottomRight.y + drawingData[i].point.y());
-        vertexCoordinateArray.lineToArray(-bottomRight.x + drawingData[i].point.x(), -bottomRight.y + drawingData[i].point.y());
-        vertexCoordinateArray.lineToArray(bottomLeft.x + drawingData[i].point.x(), bottomLeft.y + drawingData[i].point.y());
-        vertexCoordinateArray.lineToArray(bottomRight.x + drawingData[i].point.x(), bottomRight.y + drawingData[i].point.y());
+        vertexCoordinateArray.addVertex(bottomRight.x + fragments[i].x, bottomRight.y + fragments[i].y);
+        vertexCoordinateArray.addVertex(-bottomLeft.x + fragments[i].x, -bottomLeft.y + fragments[i].y);
+        vertexCoordinateArray.addVertex(-bottomRight.x + fragments[i].x, -bottomRight.y + fragments[i].y);
+        vertexCoordinateArray.addVertex(-bottomRight.x + fragments[i].x, -bottomRight.y + fragments[i].y);
+        vertexCoordinateArray.addVertex(bottomLeft.x + fragments[i].x, bottomLeft.y + fragments[i].y);
+        vertexCoordinateArray.addVertex(bottomRight.x + fragments[i].x, bottomRight.y + fragments[i].y);
 
-        QGLRect src(drawingData[i].source.left() * dx, drawingData[i].source.top() * dy,
-                    drawingData[i].source.right() * dx, drawingData[i].source.bottom() * dy);
+        QGLRect src(fragments[i].sourceLeft * dx, fragments[i].sourceTop * dy,
+                    (fragments[i].sourceLeft + fragments[i].width) * dx,
+                    (fragments[i].sourceTop + fragments[i].height) * dy);
 
-        textureCoordinateArray.lineToArray(src.right, src.bottom);
-        textureCoordinateArray.lineToArray(src.right, src.top);
-        textureCoordinateArray.lineToArray(src.left, src.top);
-        textureCoordinateArray.lineToArray(src.left, src.top);
-        textureCoordinateArray.lineToArray(src.left, src.bottom);
-        textureCoordinateArray.lineToArray(src.right, src.bottom);
+        textureCoordinateArray.addVertex(src.right, src.bottom);
+        textureCoordinateArray.addVertex(src.right, src.top);
+        textureCoordinateArray.addVertex(src.left, src.top);
+        textureCoordinateArray.addVertex(src.left, src.top);
+        textureCoordinateArray.addVertex(src.left, src.bottom);
+        textureCoordinateArray.addVertex(src.right, src.bottom);
 
-        qreal opacity = drawingData[i].opacity * q->state()->opacity;
+        qreal opacity = fragments[i].opacity * q->state()->opacity;
         opacityArray << opacity << opacity << opacity << opacity << opacity << opacity;
         allOpaque &= (opacity >= 0.99f);
     }
@@ -1474,21 +1751,22 @@
     if (texture->options & QGLContext::InvertedYBindOption) {
         // Flip texture y-coordinate.
         QGLPoint *data = textureCoordinateArray.data();
-        for (int i = 0; i < 6 * dataCount; ++i)
+        for (int i = 0; i < 6 * fragmentCount; ++i)
             data[i].y = 1 - data[i].y;
     }
 
     transferMode(ImageArrayDrawingMode);
 
     bool isBitmap = pixmap.isQBitmap();
-    bool isOpaque = !isBitmap && (!pixmap.hasAlphaChannel() || (hints & QDrawPixmaps::OpaqueHint)) && allOpaque;
+    bool isOpaque = !isBitmap && (!pixmap.hasAlphaChannel() || (hints & QPainter::OpaqueHint)) && allOpaque;
 
     updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE,
                            q->state()->renderHints & QPainter::SmoothPixmapTransform, texture->id);
 
     // Setup for texture drawing
     currentBrush = noBrush;
-    shaderManager->setSrcPixelType(isBitmap ? QGLEngineShaderManager::PatternSrc : QGLEngineShaderManager::ImageSrc);
+    shaderManager->setSrcPixelType(isBitmap ? QGLEngineShaderManager::PatternSrc
+                                            : QGLEngineShaderManager::ImageSrc);
     if (prepareForDraw(isOpaque))
         shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::ImageTexture), QT_IMAGE_TEXTURE_UNIT);
 
@@ -1497,7 +1775,7 @@
         shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::PatternColor), col);
     }
 
-    glDrawArrays(GL_TRIANGLES, 0, 6 * dataCount);
+    glDrawArrays(GL_TRIANGLES, 0, 6 * fragmentCount);
 }
 
 bool QGL2PaintEngineEx::begin(QPaintDevice *pdev)
@@ -1522,6 +1800,7 @@
     d->mode = BrushDrawingMode;
     d->brushTextureDirty = true;
     d->brushUniformsDirty = true;
+    d->matrixUniformDirty = true;
     d->matrixDirty = true;
     d->compositionModeDirty = true;
     d->opacityUniformDirty = true;
@@ -1604,6 +1883,10 @@
         glDeleteBuffers(d->unusedVBOSToClean.size(), d->unusedVBOSToClean.constData());
         d->unusedVBOSToClean.clear();
     }
+    if (!d->unusedIBOSToClean.isEmpty()) {
+        glDeleteBuffers(d->unusedIBOSToClean.size(), d->unusedIBOSToClean.constData());
+        d->unusedIBOSToClean.clear();
+    }
 #endif
 
     return false;
@@ -1625,6 +1908,7 @@
         d->transferMode(BrushDrawingMode);
         glViewport(0, 0, d->width, d->height);
         d->needsSync = false;
+        d->lastMaskTextureUsed = 0;
         d->shaderManager->setDirty();
         d->ctx->d_func()->syncGlState();
         for (int i = 0; i < 3; ++i)