Fix crash when combining QOpenGLWidget, QStaticText and Qt Quick

Under certain circumstances, if you had a widget with a QOpenGLPaintEngine,
and drew QStaticText into this, and then later had Qt Quick access the same
cache and try to resize it, we would get a crash because the resize function
would have a pointer to the paint engine and try to access its shader manager
(which would now be null, since this is outside the begin()/end() phase of the
paint engine.

The solution is to reset the paint engine pointer to null on the cache once it
has been populated and it is no longer needed.

[ChangeLog][QtGui][Text] Fixed a possible crash when combining QStaticText,
QOpenGLWidget and Qt Quick in the same application.

Task-number: QTBUG-70096
Change-Id: I7383ad7456d1a72499cfcd2da09a5a808d4b3eff
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
This commit is contained in:
Eskil Abrahamsen Blomfeldt 2018-08-22 13:02:04 +02:00
parent 981b16d9ba
commit f71048a531
3 changed files with 60 additions and 0 deletions

View File

@ -1741,6 +1741,7 @@ void QOpenGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngine::GlyphFormat gly
// we may have to re-bind brush textures after filling in the cache. // we may have to re-bind brush textures after filling in the cache.
brushTextureDirty = (QT_BRUSH_TEXTURE_UNIT == glypchCacheTextureUnit); brushTextureDirty = (QT_BRUSH_TEXTURE_UNIT == glypchCacheTextureUnit);
} }
cache->setPaintEnginePrivate(nullptr);
} }
if (cache->width() == 0 || cache->height() == 0) if (cache->width() == 0 || cache->height() == 0)

View File

@ -152,6 +152,11 @@ public:
void clear(); void clear();
QOpenGL2PaintEngineExPrivate *paintEnginePrivate() const
{
return pex;
}
private: private:
void setupVertexAttribs(); void setupVertexAttribs();

View File

@ -30,6 +30,7 @@
#include <QtGui/QOpenGLFunctions> #include <QtGui/QOpenGLFunctions>
#include <QtGui/QPainter> #include <QtGui/QPainter>
#include <QtGui/QScreen> #include <QtGui/QScreen>
#include <QtGui/QStaticText>
#include <QtWidgets/QDesktopWidget> #include <QtWidgets/QDesktopWidget>
#include <QtWidgets/QGraphicsView> #include <QtWidgets/QGraphicsView>
#include <QtWidgets/QGraphicsScene> #include <QtWidgets/QGraphicsScene>
@ -40,6 +41,8 @@
#include <QtTest/QtTest> #include <QtTest/QtTest>
#include <QSignalSpy> #include <QSignalSpy>
#include <private/qguiapplication_p.h> #include <private/qguiapplication_p.h>
#include <private/qstatictext_p.h>
#include <private/qopengltextureglyphcache_p.h>
#include <qpa/qplatformintegration.h> #include <qpa/qplatformintegration.h>
class tst_QOpenGLWidget : public QObject class tst_QOpenGLWidget : public QObject
@ -64,6 +67,10 @@ private slots:
void stackWidgetOpaqueChildIsVisible(); void stackWidgetOpaqueChildIsVisible();
void offscreen(); void offscreen();
void offscreenThenOnscreen(); void offscreenThenOnscreen();
#ifdef QT_BUILD_INTERNAL
void staticTextDanglingPointer();
#endif
}; };
void tst_QOpenGLWidget::initTestCase() void tst_QOpenGLWidget::initTestCase()
@ -675,6 +682,53 @@ void tst_QOpenGLWidget::offscreenThenOnscreen()
QVERIFY(image.pixel(30, 40) == qRgb(0, 0, 255)); QVERIFY(image.pixel(30, 40) == qRgb(0, 0, 255));
} }
class StaticTextPainterWidget : public QOpenGLWidget
{
public:
StaticTextPainterWidget(QWidget *parent = nullptr)
: QOpenGLWidget(parent)
{
}
void paintEvent(QPaintEvent *)
{
QPainter p(this);
text.setText(QStringLiteral("test"));
p.drawStaticText(0, 0, text);
ctx = QOpenGLContext::currentContext();
}
QStaticText text;
QOpenGLContext *ctx;
};
#ifdef QT_BUILD_INTERNAL
void tst_QOpenGLWidget::staticTextDanglingPointer()
{
QWidget w;
StaticTextPainterWidget *glw = new StaticTextPainterWidget(&w);
w.resize(640, 480);
glw->resize(320, 200);
w.show();
QVERIFY(QTest::qWaitForWindowExposed(&w));
QStaticTextPrivate *d = QStaticTextPrivate::get(&glw->text);
QCOMPARE(d->itemCount, 1);
QFontEngine *fe = d->items->fontEngine();
for (int i = QFontEngine::Format_None; i <= QFontEngine::Format_ARGB; ++i) {
QOpenGLTextureGlyphCache *cache =
(QOpenGLTextureGlyphCache *) fe->glyphCache(glw->ctx,
QFontEngine::GlyphFormat(i),
QTransform());
if (cache != nullptr)
QCOMPARE(cache->paintEnginePrivate(), nullptr);
}
}
#endif
QTEST_MAIN(tst_QOpenGLWidget) QTEST_MAIN(tst_QOpenGLWidget)
#include "tst_qopenglwidget.moc" #include "tst_qopenglwidget.moc"