src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
changeset 3 41300fa6a67c
parent 0 1918ee327afb
child 4 3b1da2848fc7
child 7 f7bc934e204c
child 18 2f34d5167611
--- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp	Tue Jan 26 12:42:25 2010 +0200
+++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp	Tue Feb 02 00:43:10 2010 +0200
@@ -62,6 +62,8 @@
     and use the correct program when we really need it.
 */
 
+// #define QT_OPENGL_CACHE_AS_VBOS
+
 #include "qpaintengineex_opengl2_p.h"
 
 #include <string.h> //for memcpy
@@ -158,8 +160,8 @@
     , m_height(0)
 {
     glGenFramebuffers(1, &m_fbo);
-    connect(QGLSignalProxy::instance(), SIGNAL(aboutToDestroyContext(const QGLContext *)),
-            SLOT(contextDestroyed(const QGLContext *)));
+    connect(QGLSignalProxy::instance(), SIGNAL(aboutToDestroyContext(const QGLContext*)),
+            SLOT(contextDestroyed(const QGLContext*)));
 }
 
 QGLTextureGlyphCache::~QGLTextureGlyphCache()
@@ -246,7 +248,7 @@
     glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, vertexCoordinateArray);
     glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, textureCoordinateArray);
 
-    pex->shaderManager->blitProgram()->enable();
+    pex->shaderManager->blitProgram()->bind();
     pex->shaderManager->blitProgram()->setUniformValue("imageTexture", QT_IMAGE_TEXTURE_UNIT);
     pex->shaderManager->setDirty();
 
@@ -344,6 +346,13 @@
 QGL2PaintEngineExPrivate::~QGL2PaintEngineExPrivate()
 {
     delete shaderManager;
+
+    while (pathCaches.size()) {
+        QVectorPath::CacheEntry *e = *(pathCaches.constBegin());
+        e->cleanup(e->engine, e->data);
+        e->data = 0;
+        e->engine = 0;
+    }
 }
 
 void QGL2PaintEngineExPrivate::updateTextureFilter(GLenum target, GLenum wrapMode, bool smoothPixmapTransform, GLuint id)
@@ -377,25 +386,31 @@
 }
 
 
-void QGL2PaintEngineExPrivate::setBrush(const QBrush* brush)
+void QGL2PaintEngineExPrivate::setBrush(const QBrush& brush)
 {
+    Q_ASSERT(brush.style() != Qt::NoBrush);
+
+    if (qbrush_fast_equals(currentBrush, brush))
+        return;
+
     currentBrush = brush;
+
     brushTextureDirty = true;
     brushUniformsDirty = true;
-    if (currentBrush->style() == Qt::TexturePattern
-        && qHasPixmapTexture(*brush) && brush->texture().isQBitmap())
+    if (currentBrush.style() == Qt::TexturePattern
+        && qHasPixmapTexture(brush) && brush.texture().isQBitmap())
     {
         shaderManager->setSrcPixelType(QGLEngineShaderManager::TextureSrcWithPattern);
     } else {
-        shaderManager->setSrcPixelType(currentBrush->style());
+        shaderManager->setSrcPixelType(currentBrush.style());
     }
-    shaderManager->optimiseForBrushTransform(currentBrush->transform());
+    shaderManager->optimiseForBrushTransform(currentBrush.transform());
 }
 
 
 void QGL2PaintEngineExPrivate::useSimpleShader()
 {
-    shaderManager->simpleProgram()->enable();
+    shaderManager->simpleProgram()->bind();
     shaderManager->setDirty();
 
     if (matrixDirty)
@@ -411,7 +426,7 @@
 {
     Q_Q(QGL2PaintEngineEx);
 //     qDebug("QGL2PaintEngineExPrivate::updateBrushTexture()");
-    Qt::BrushStyle style = currentBrush->style();
+    Qt::BrushStyle style = currentBrush.style();
 
     if ( (style >= Qt::Dense1Pattern) && (style <= Qt::DiagCrossPattern) ) {
         // Get the image data for the pattern
@@ -424,7 +439,7 @@
     else if (style >= Qt::LinearGradientPattern && style <= Qt::ConicalGradientPattern) {
         // Gradiant brush: All the gradiants use the same texture
 
-        const QGradient* g = currentBrush->gradient();
+        const QGradient* g = currentBrush.gradient();
 
         // We apply global opacity in the fragment shaders, so we always pass 1.0
         // for opacity to the cache.
@@ -441,7 +456,7 @@
             updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE, q->state()->renderHints & QPainter::SmoothPixmapTransform);
     }
     else if (style == Qt::TexturePattern) {
-        const QPixmap& texPixmap = currentBrush->texture();
+        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);
@@ -455,15 +470,15 @@
 void QGL2PaintEngineExPrivate::updateBrushUniforms()
 {
 //     qDebug("QGL2PaintEngineExPrivate::updateBrushUniforms()");
-    Qt::BrushStyle style = currentBrush->style();
+    Qt::BrushStyle style = currentBrush.style();
 
     if (style == Qt::NoBrush)
         return;
 
-    QTransform brushQTransform = currentBrush->transform();
+    QTransform brushQTransform = currentBrush.transform();
 
     if (style == Qt::SolidPattern) {
-        QColor col = qt_premultiplyColor(currentBrush->color(), (GLfloat)q->state()->opacity);
+        QColor col = qt_premultiplyColor(currentBrush.color(), (GLfloat)q->state()->opacity);
         shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::FragmentColor), col);
     }
     else {
@@ -471,7 +486,7 @@
         QPointF translationPoint;
 
         if (style <= Qt::DiagCrossPattern) {
-            QColor col = qt_premultiplyColor(currentBrush->color(), (GLfloat)q->state()->opacity);
+            QColor col = qt_premultiplyColor(currentBrush.color(), (GLfloat)q->state()->opacity);
 
             shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::PatternColor), col);
 
@@ -479,7 +494,7 @@
             shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::HalfViewportSize), halfViewportSize);
         }
         else if (style == Qt::LinearGradientPattern) {
-            const QLinearGradient *g = static_cast<const QLinearGradient *>(currentBrush->gradient());
+            const QLinearGradient *g = static_cast<const QLinearGradient *>(currentBrush.gradient());
 
             QPointF realStart = g->start();
             QPointF realFinal = g->finalStop();
@@ -499,7 +514,7 @@
             shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::HalfViewportSize), halfViewportSize);
         }
         else if (style == Qt::ConicalGradientPattern) {
-            const QConicalGradient *g = static_cast<const QConicalGradient *>(currentBrush->gradient());
+            const QConicalGradient *g = static_cast<const QConicalGradient *>(currentBrush.gradient());
             translationPoint   = g->center();
 
             GLfloat angle = -(g->angle() * 2 * Q_PI) / 360.0;
@@ -510,7 +525,7 @@
             shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::HalfViewportSize), halfViewportSize);
         }
         else if (style == Qt::RadialGradientPattern) {
-            const QRadialGradient *g = static_cast<const QRadialGradient *>(currentBrush->gradient());
+            const QRadialGradient *g = static_cast<const QRadialGradient *>(currentBrush.gradient());
             QPointF realCenter = g->center();
             QPointF realFocal  = g->focalPoint();
             qreal   realRadius = g->radius();
@@ -528,14 +543,14 @@
             shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::HalfViewportSize), halfViewportSize);
         }
         else if (style == Qt::TexturePattern) {
-            const QPixmap& texPixmap = currentBrush->texture();
-
-            if (qHasPixmapTexture(*currentBrush) && currentBrush->texture().isQBitmap()) {
-                QColor col = qt_premultiplyColor(currentBrush->color(), (GLfloat)q->state()->opacity);
+            const QPixmap& texPixmap = currentBrush.texture();
+
+            if (qHasPixmapTexture(currentBrush) && currentBrush.texture().isQBitmap()) {
+                QColor col = qt_premultiplyColor(currentBrush.color(), (GLfloat)q->state()->opacity);
                 shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::PatternColor), col);
             }
 
-            QSizeF invertedTextureSize(1.0 / texPixmap.width(), 1.0 * textureInvertedY / texPixmap.height());
+            QSizeF invertedTextureSize(1.0 / texPixmap.width(), 1.0 / texPixmap.height());
             shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::InvertedTextureSize), invertedTextureSize);
 
             QVector2D halfViewportSize(width*0.5, height*0.5);
@@ -550,7 +565,11 @@
 
         QTransform translate(1, 0, 0, 1, -translationPoint.x(), -translationPoint.y());
         QTransform gl_to_qt(1, 0, 0, -1, 0, height);
-        QTransform inv_matrix = gl_to_qt * (brushQTransform * matrix).inverted() * translate;
+        QTransform inv_matrix;
+        if (style == Qt::TexturePattern && textureInvertedY == -1)
+            inv_matrix = gl_to_qt * (QTransform(1, 0, 0, -1, 0, currentBrush.texture().height()) * brushQTransform * matrix).inverted() * translate;
+        else
+            inv_matrix = gl_to_qt * (brushQTransform * matrix).inverted() * translate;
 
         shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::BrushTransform), inv_matrix);
         shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::BrushTexture), QT_BRUSH_TEXTURE_UNIT);
@@ -576,53 +595,31 @@
     // matrix multiplication as most of the components are trivial.
     const QTransform& transform = q->state()->matrix;
 
-    if (mode == TextDrawingMode) {
-        // Text drawing mode is only used for non-scaling transforms
-        pmvMatrix[0][0] = 2.0 / width;
-        pmvMatrix[0][1] = 0.0;
-        pmvMatrix[0][2] = 0.0;
-        pmvMatrix[0][3] = 0.0;
-        pmvMatrix[1][0] = 0.0;
-        pmvMatrix[1][1] = -2.0 / height;
-        pmvMatrix[1][2] = 0.0;
-        pmvMatrix[1][3] = 0.0;
-        pmvMatrix[2][0] = 0.0;
-        pmvMatrix[2][1] = 0.0;
-        pmvMatrix[2][2] = -1.0;
-        pmvMatrix[2][3] = 0.0;
-        pmvMatrix[3][0] = pmvMatrix[0][0] * qRound(transform.dx()) - 1.0;
-        pmvMatrix[3][1] = pmvMatrix[1][1] * qRound(transform.dy()) + 1.0;
-        pmvMatrix[3][2] = 0.0;
-        pmvMatrix[3][3] = 1.0;
-
-        inverseScale = 1;
-    } else {
-        qreal wfactor = 2.0 / width;
-        qreal hfactor = -2.0 / height;
-
-        pmvMatrix[0][0] = wfactor * transform.m11() - transform.m13();
-        pmvMatrix[0][1] = hfactor * transform.m12() + transform.m13();
-        pmvMatrix[0][2] = 0.0;
-        pmvMatrix[0][3] = transform.m13();
-        pmvMatrix[1][0] = wfactor * transform.m21() - transform.m23();
-        pmvMatrix[1][1] = hfactor * transform.m22() + transform.m23();
-        pmvMatrix[1][2] = 0.0;
-        pmvMatrix[1][3] = transform.m23();
-        pmvMatrix[2][0] = 0.0;
-        pmvMatrix[2][1] = 0.0;
-        pmvMatrix[2][2] = -1.0;
-        pmvMatrix[2][3] = 0.0;
-        pmvMatrix[3][0] = wfactor * transform.dx() - transform.m33();
-        pmvMatrix[3][1] = hfactor * transform.dy() + transform.m33();
-        pmvMatrix[3][2] = 0.0;
-        pmvMatrix[3][3] = transform.m33();
-
-        // 1/10000 == 0.0001, so we have good enough res to cover curves
-        // that span the entire widget...
-        inverseScale = qMax(1 / qMax( qMax(qAbs(transform.m11()), qAbs(transform.m22())),
-                    qMax(qAbs(transform.m12()), qAbs(transform.m21())) ),
-                qreal(0.0001));
-    }
+    qreal wfactor = 2.0 / width;
+    qreal hfactor = -2.0 / height;
+
+    pmvMatrix[0][0] = wfactor * transform.m11() - transform.m13();
+    pmvMatrix[0][1] = hfactor * transform.m12() + transform.m13();
+    pmvMatrix[0][2] = 0.0;
+    pmvMatrix[0][3] = transform.m13();
+    pmvMatrix[1][0] = wfactor * transform.m21() - transform.m23();
+    pmvMatrix[1][1] = hfactor * transform.m22() + transform.m23();
+    pmvMatrix[1][2] = 0.0;
+    pmvMatrix[1][3] = transform.m23();
+    pmvMatrix[2][0] = 0.0;
+    pmvMatrix[2][1] = 0.0;
+    pmvMatrix[2][2] = -1.0;
+    pmvMatrix[2][3] = 0.0;
+    pmvMatrix[3][0] = wfactor * transform.dx() - transform.m33();
+    pmvMatrix[3][1] = hfactor * transform.dy() + transform.m33();
+    pmvMatrix[3][2] = 0.0;
+    pmvMatrix[3][3] = transform.m33();
+
+    // 1/10000 == 0.0001, so we have good enough res to cover curves
+    // that span the entire widget...
+    inverseScale = qMax(1 / qMax( qMax(qAbs(transform.m11()), qAbs(transform.m22())),
+                                  qMax(qAbs(transform.m12()), qAbs(transform.m21())) ),
+                        qreal(0.0001));
 
     matrixDirty = false;
 
@@ -804,17 +801,12 @@
         lastTexture = GLuint(-1);
     }
 
-    if (mode == TextDrawingMode)
-        matrixDirty = true;
-
     if (newMode == TextDrawingMode) {
         glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
         glEnableVertexAttribArray(QT_TEXTURE_COORDS_ATTR);
 
         glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, vertexCoordinateArray.data());
         glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, textureCoordinateArray.data());
-
-        matrixDirty = true;
     }
 
     if (newMode == ImageDrawingMode) {
@@ -842,6 +834,30 @@
     mode = newMode;
 }
 
+struct QGL2PEVectorPathCache
+{
+#ifdef QT_OPENGL_CACHE_AS_VBOS
+    GLuint vbo;
+#else
+    float *vertices;
+#endif
+    int vertexCount;
+    GLenum primitiveType;
+    qreal iscale;
+};
+
+void qopengl2paintengine_cleanup_vectorpath(QPaintEngineEx *engine, void *data)
+{
+    QGL2PEVectorPathCache *c = (QGL2PEVectorPathCache *) data;
+#ifdef QT_OPENGL_CACHE_AS_VBOS
+    QGL2PaintEngineExPrivate *d = QGL2PaintEngineExPrivate::getData((QGL2PaintEngineEx *) engine);
+    d->unusedVBOSToClean << c->vbo;
+#else
+    qFree(c->vertices);
+#endif
+    delete c;
+}
+
 // Assumes everything is configured for the brush you want to use
 void QGL2PaintEngineExPrivate::fill(const QVectorPath& path)
 {
@@ -856,15 +872,77 @@
     // 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());
-        prepareForDraw(currentBrush->isOpaque());
+        prepareForDraw(currentBrush.isOpaque());
         composite(rect);
-    } else if (path.shape() == QVectorPath::EllipseHint
-             || path.shape() == QVectorPath::ConvexPolygonHint)
-    {
-        vertexCoordinateArray.clear();
-        vertexCoordinateArray.addPath(path, inverseScale, false);
-        prepareForDraw(currentBrush->isOpaque());
-        drawVertexArrays(vertexCoordinateArray, GL_TRIANGLE_FAN);
+    } else if (path.isConvex()) {
+
+        if (path.isCacheable()) {
+            QVectorPath::CacheEntry *data = path.lookupCacheData(q);
+            QGL2PEVectorPathCache *cache;
+
+            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);
+                        cache->vbo = 0;
+#else
+                        qFree(cache->vertices);
+#endif
+                        cache->vertexCount = 0;
+                    }
+                }
+            } else {
+                cache = new QGL2PEVectorPathCache;
+                cache->vertexCount = 0;
+                data = const_cast<QVectorPath &>(path).addCacheData(q, cache, qopengl2paintengine_cleanup_vectorpath);
+            }
+
+            // Flatten the path at the current scale factor and fill it into the cache struct.
+            if (!cache->vertexCount) {
+                vertexCoordinateArray.clear();
+                vertexCoordinateArray.addPath(path, inverseScale, false);
+                int vertexCount = vertexCoordinateArray.vertexCount();
+                int floatSizeInBytes = vertexCount * 2 * sizeof(float);
+                cache->vertexCount = vertexCount;
+                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);
+#else
+                cache->vertices = (float *) qMalloc(floatSizeInBytes);
+                memcpy(cache->vertices, vertexCoordinateArray.data(), floatSizeInBytes);
+#endif
+            }
+
+            prepareForDraw(currentBrush.isOpaque());
+            glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
+#ifdef QT_OPENGL_CACHE_AS_VBOS
+            glBindBuffer(GL_ARRAY_BUFFER, cache->vbo);
+            glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, false, 0, 0);
+#else
+            glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, false, 0, cache->vertices);
+#endif
+            glDrawArrays(cache->primitiveType, 0, cache->vertexCount);
+
+        } 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();
+            vertexCoordinateArray.addPath(path, inverseScale, false);
+            prepareForDraw(currentBrush.isOpaque());
+            drawVertexArrays(vertexCoordinateArray, GL_TRIANGLE_FAN);
+        }
+
     } else {
         // The path is too complicated & needs the stencil technique
         vertexCoordinateArray.clear();
@@ -886,7 +964,7 @@
             glStencilFunc(GL_NOTEQUAL, 0, GL_STENCIL_HIGH_BIT);
         }
 
-        prepareForDraw(currentBrush->isOpaque());
+        prepareForDraw(currentBrush.isOpaque());
 
         if (inRenderText)
             prepareDepthRangeForRenderText();
@@ -906,7 +984,8 @@
 
 void QGL2PaintEngineExPrivate::fillStencilWithVertexArray(const float *data,
                                                           int count,
-                                                          const QVector<int> *stops,
+                                                          int *stops,
+                                                          int stopCount,
                                                           const QGLRect &bounds,
                                                           StencilFillMode mode)
 {
@@ -964,7 +1043,7 @@
         // Dec. for back-facing "holes"
         glStencilOpSeparate(GL_BACK, GL_KEEP, GL_DECR_WRAP, GL_DECR_WRAP);
         glStencilMask(~GL_STENCIL_HIGH_BIT);
-        drawVertexArrays(data, stops, GL_TRIANGLE_FAN);
+        drawVertexArrays(data, stops, stopCount, GL_TRIANGLE_FAN);
 
         if (q->state()->clipTestEnabled) {
             // Clear high bit of stencil outside of path
@@ -976,7 +1055,7 @@
     } else if (mode == OddEvenFillMode) {
         glStencilMask(GL_STENCIL_HIGH_BIT);
         glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT); // Simply invert the stencil bit
-        drawVertexArrays(data, stops, GL_TRIANGLE_FAN);
+        drawVertexArrays(data, stops, stopCount, GL_TRIANGLE_FAN);
 
     } else { // TriStripStrokeFillMode
         Q_ASSERT(count && !stops); // tristrips generated directly, so no vertexArray or stops
@@ -1082,10 +1161,10 @@
                                       : QGLEngineShaderManager::NoOpacity;
         if (stateHasOpacity && (mode != ImageDrawingMode)) {
             // Using a brush
-            bool brushIsPattern = (currentBrush->style() >= Qt::Dense1Pattern) &&
-                                  (currentBrush->style() <= Qt::DiagCrossPattern);
-
-            if ((currentBrush->style() == Qt::SolidPattern) || brushIsPattern)
+            bool brushIsPattern = (currentBrush.style() >= Qt::Dense1Pattern) &&
+                                  (currentBrush.style() <= Qt::DiagCrossPattern);
+
+            if ((currentBrush.style() == Qt::SolidPattern) || brushIsPattern)
                 opacityMode = QGLEngineShaderManager::NoOpacity; // Global opacity handled by srcPixel shader
         }
     }
@@ -1135,7 +1214,7 @@
 }
 
 // Draws the vertex array as a set of <vertexArrayStops.size()> triangle fans.
-void QGL2PaintEngineExPrivate::drawVertexArrays(const float *data, const QVector<int> *stops,
+void QGL2PaintEngineExPrivate::drawVertexArrays(const float *data, int *stops, int stopCount,
                                                 GLenum primitive)
 {
     // Now setup the pointer to the vertex array:
@@ -1143,7 +1222,8 @@
     glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, data);
 
     int previousStop = 0;
-    foreach(int stop, *stops) {
+    for (int i=0; i<stopCount; ++i) {
+        int stop = stops[i];
 /*
         qDebug("Drawing triangle fan for vertecies %d -> %d:", previousStop, stop-1);
         for (int i=previousStop; i<stop; ++i)
@@ -1201,7 +1281,9 @@
         ensureActive();
 
     QOpenGL2PaintEngineState *s = state();
-    bool doOffset = !(s->renderHints & QPainter::Antialiasing) && style == Qt::SolidPattern;
+    bool doOffset = !(s->renderHints & QPainter::Antialiasing) &&
+                    (style == Qt::SolidPattern) &&
+                    !d->multisamplingAlwaysEnabled;
 
     if (doOffset) {
         d->temporaryTransform = s->matrix;
@@ -1210,7 +1292,7 @@
         d->matrixDirty = true;
     }
 
-    d->setBrush(&brush);
+    d->setBrush(brush);
     d->fill(path);
 
     if (doOffset) {
@@ -1219,6 +1301,9 @@
     }
 }
 
+extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
+
+
 void QGL2PaintEngineEx::stroke(const QVectorPath &path, const QPen &pen)
 {
     Q_D(QGL2PaintEngineEx);
@@ -1229,10 +1314,15 @@
         return;
 
     QOpenGL2PaintEngineState *s = state();
+    if (pen.isCosmetic() && !qt_scaleForTransform(s->transform(), 0)) {
+        // QTriangulatingStroker class is not meant to support cosmetically sheared strokes.
+        QPaintEngineEx::stroke(path, pen);
+        return;
+    }
 
     ensureActive();
 
-    bool doOffset = !(s->renderHints & QPainter::Antialiasing);
+    bool doOffset = !(s->renderHints & QPainter::Antialiasing) && !d->multisamplingAlwaysEnabled;
     if (doOffset) {
         d->temporaryTransform = s->matrix;
         QTransform tx = QTransform::fromTranslate(0.49, .49);
@@ -1241,7 +1331,7 @@
     }
 
     bool opaque = penBrush.isOpaque() && s->opacity > 0.99;
-    d->setBrush(&penBrush);
+    d->setBrush(penBrush);
     d->transferMode(BrushDrawingMode);
 
     // updateMatrix() is responsible for setting the inverse scale on
@@ -1263,6 +1353,7 @@
 
 
     QGLContext *ctx = d->ctx;
+    Q_UNUSED(ctx);
 
     if (opaque) {
         d->prepareForDraw(opaque);
@@ -1291,7 +1382,7 @@
         QRectF bounds = path.controlPointRect().adjusted(-extra, -extra, extra, extra);
 
         d->fillStencilWithVertexArray(d->stroker.vertices(), d->stroker.vertexCount() / 2,
-                                      0, bounds, QGL2PaintEngineExPrivate::TriStripStrokeFillMode);
+                                      0, 0, bounds, QGL2PaintEngineExPrivate::TriStripStrokeFillMode);
 
         glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
 
@@ -1433,20 +1524,21 @@
 
     const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
 
-    bool drawCached = true;
-
-    if (s->matrix.type() > QTransform::TxTranslate)
-        drawCached = false;
-
-    // don't try to cache huge fonts
-    if (ti.fontEngine->fontDef.pixelSize * qSqrt(s->matrix.determinant()) >= 64)
+    QTransform::TransformationType txtype = s->matrix.type();
+
+    float det = s->matrix.determinant();
+    bool drawCached = txtype < QTransform::TxProject;
+
+    // don't try to cache huge fonts or vastly transformed fonts
+    const qreal pixelSize = ti.fontEngine->fontDef.pixelSize;
+    if (pixelSize * pixelSize * qAbs(det) >= 64 * 64 || det < 0.25f || det > 4.f)
         drawCached = false;
 
     QFontEngineGlyphCache::Type glyphType = ti.fontEngine->glyphFormat >= 0
                                             ? QFontEngineGlyphCache::Type(ti.fontEngine->glyphFormat)
                                             : d->glyphCacheType;
 
-    if (d->inRenderText)
+    if (d->inRenderText || txtype > QTransform::TxTranslate)
         glyphType = QFontEngineGlyphCache::Raster_A8;
 
     if (glyphType == QFontEngineGlyphCache::Raster_RGBMask
@@ -1468,7 +1560,6 @@
                                                 const QTextItemInt &ti)
 {
     Q_Q(QGL2PaintEngineEx);
-    QOpenGL2PaintEngineState *s = q->state();
 
     QVarLengthArray<QFixedPoint> positions;
     QVarLengthArray<glyph_t> glyphs;
@@ -1476,10 +1567,10 @@
     ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
 
     QGLTextureGlyphCache *cache =
-        (QGLTextureGlyphCache *) ti.fontEngine->glyphCache(ctx, s->matrix);
+            (QGLTextureGlyphCache *) ti.fontEngine->glyphCache(ctx, glyphType, QTransform());
 
     if (!cache || cache->cacheType() != glyphType) {
-        cache = new QGLTextureGlyphCache(ctx, glyphType, s->matrix);
+        cache = new QGLTextureGlyphCache(ctx, glyphType, QTransform());
         ti.fontEngine->setGlyphCache(ctx, cache);
     }
 
@@ -1519,7 +1610,7 @@
         glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, textureCoordinateArray.data());
 
     QBrush pensBrush = q->state()->pen.brush();
-    setBrush(&pensBrush);
+    setBrush(pensBrush);
 
     if (inRenderText)
         prepareDepthRangeForRenderText();
@@ -1564,7 +1655,7 @@
                 q->state()->opacity = 1;
                 opacityUniformDirty = true;
                 pensBrush = Qt::white;
-                setBrush(&pensBrush);
+                setBrush(pensBrush);
             }
 
             compositionModeDirty = false; // I can handle this myself, thank you very much
@@ -1585,7 +1676,7 @@
                 q->state()->opacity = oldOpacity;
                 opacityUniformDirty = true;
                 pensBrush = q->state()->pen.brush();
-                setBrush(&pensBrush);
+                setBrush(pensBrush);
             }
 
             compositionModeDirty = false;
@@ -1730,6 +1821,7 @@
     d->opacityUniformDirty = true;
     d->needsSync = true;
     d->use_system_clip = !systemClip().isEmpty();
+    d->currentBrush = QBrush();
 
     d->dirtyStencilRegion = QRect(0, 0, d->width, d->height);
     d->stencilClean = true;
@@ -1740,7 +1832,8 @@
     d->device->beginPaint();
 
 #if !defined(QT_OPENGL_ES_2)
-    bool success = qt_resolve_version_2_0_functions(d->ctx);
+    bool success = qt_resolve_version_2_0_functions(d->ctx)
+                   && qt_resolve_buffer_extensions(d->ctx);
     Q_ASSERT(success);
     Q_UNUSED(success);
 #endif
@@ -1760,13 +1853,18 @@
     d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
 
 #if !defined(QT_OPENGL_ES_2)
-    if (!d->device->format().alpha()
 #if defined(Q_WS_WIN)
-        && qt_cleartype_enabled
+    if (qt_cleartype_enabled)
+#endif
+        d->glyphCacheType = QFontEngineGlyphCache::Raster_RGBMask;
 #endif
-       ) {
-        d->glyphCacheType = QFontEngineGlyphCache::Raster_RGBMask;
-    }
+
+#if defined(QT_OPENGL_ES_2)
+    // OpenGL ES can't switch MSAA off, so if the gl paint device is
+    // multisampled, it's always multisampled.
+    d->multisamplingAlwaysEnabled = d->device->format().sampleBuffers();
+#else
+    d->multisamplingAlwaysEnabled = false;
 #endif
 
     return true;
@@ -1796,6 +1894,13 @@
     delete d->shaderManager;
     d->shaderManager = 0;
 
+#ifdef QT_OPENGL_CACHE_AS_VBOS
+    if (!d->unusedVBOSToClean.isEmpty()) {
+        glDeleteBuffers(d->unusedVBOSToClean.size(), d->unusedVBOSToClean.constData());
+        d->unusedVBOSToClean.clear();
+    }
+#endif
+
     return false;
 }
 
@@ -2086,7 +2191,7 @@
     q->state()->rectangleClip = use_system_clip ? systemClip.boundingRect() : QRect(0, 0, width, height);
     updateClipScissorTest();
 
-    if (systemClip.numRects() == 1) {
+    if (systemClip.rectCount() == 1) {
         if (systemClip.boundingRect() == QRect(0, 0, width, height))
             use_system_clip = false;
 #ifndef QT_GL_NO_SCISSOR_TEST