Added log and status properties to ShaderEffect.
authorKim Motoyoshi Kalland <kim.kalland@nokia.com>
Wed, 11 Jan 2012 16:34:58 +0000 (17:34 +0100)
committerQt by Nokia <qt-info@nokia.com>
Mon, 16 Jan 2012 14:07:33 +0000 (15:07 +0100)
Task-number: QTBUG-23531

Change-Id: I136f6d9642ff9d4074fe8dae1f5714a05349107a
Reviewed-by: Gunnar Sletta <gunnar.sletta@nokia.com>

src/quick/items/qquickshadereffect.cpp
src/quick/items/qquickshadereffect_p.h
src/quick/items/qquickshadereffectmesh.cpp
src/quick/items/qquickshadereffectmesh_p.h
src/quick/items/qquickshadereffectnode.cpp
src/quick/items/qquickshadereffectnode_p.h
tests/auto/qtquick2/qquickshadereffect/tst_qquickshadereffect.cpp

index be01338..7e52988 100644 (file)
@@ -185,6 +185,7 @@ QQuickShaderEffect::QQuickShaderEffect(QQuickItem *parent)
     , m_meshResolution(1, 1)
     , m_mesh(0)
     , m_cullMode(NoCulling)
+    , m_status(Uncompiled)
     , m_blending(true)
     , m_dirtyData(true)
     , m_programDirty(true)
@@ -215,6 +216,10 @@ void QQuickShaderEffect::setFragmentShader(const QByteArray &code)
     m_source.fragmentCode = code;
     update();
     m_complete = false;
+    if (m_status != Uncompiled) {
+        m_status = Uncompiled;
+        emit statusChanged();
+    }
     emit fragmentShaderChanged();
 }
 
@@ -234,6 +239,10 @@ void QQuickShaderEffect::setVertexShader(const QByteArray &code)
     m_source.vertexCode = code;
     update();
     m_complete = false;
+    if (m_status != Uncompiled) {
+        m_status = Uncompiled;
+        emit statusChanged();
+    }
     emit vertexShaderChanged();
 }
 
@@ -334,6 +343,34 @@ void QQuickShaderEffect::setCullMode(CullMode face)
     emit cullModeChanged();
 }
 
+/*!
+    \qmlproperty enumeration QtQuick2::ShaderEffect::status
+
+    This property tells the current status of the OpenGL shader program.
+
+    \list
+    \o ShaderEffect.Compiled - the shader program was successfully compiled and linked.
+    \o ShaderEffect.Uncompiled - the shader program has not yet been compiled.
+    \o ShaderEffect.Error - the shader program failed to compile or link.
+    \endlist
+
+    When setting the fragment or vertex shader source code, the status will become Uncompiled.
+    The first time the ShaderEffect is rendered with new shader source code, the shaders are
+    compiled and linked, and the status is updated to Compiled or Error.
+
+    \sa log
+*/
+
+/*!
+    \qmlproperty string QtQuick2::ShaderEffect::log
+
+    This property holds a log of warnings and errors from the latest attempt at compiling and
+    linking the OpenGL shader program. It is updated at the same time \l status is set to Compiled
+    or Error.
+
+    \sa status
+*/
+
 void QQuickShaderEffect::changeSource(int index)
 {
     Q_ASSERT(index >= 0 && index < m_sources.size());
@@ -353,6 +390,14 @@ void QQuickShaderEffect::updateGeometry()
     update();
 }
 
+void QQuickShaderEffect::updateLogAndStatus(const QString &log, int status)
+{
+    m_log = m_parseLog + log;
+    m_status = Status(status);
+    emit logChanged();
+    emit statusChanged();
+}
+
 void QQuickShaderEffect::setSource(const QVariant &var, int index)
 {
     Q_ASSERT(index >= 0 && index < m_sources.size());
@@ -467,7 +512,8 @@ void QQuickShaderEffect::reset()
         delete source.mapper;
     }
     m_sources.clear();
-
+    m_log.clear();
+    m_parseLog.clear();
     m_programDirty = true;
     m_dirtyMesh = true;
 }
@@ -494,14 +540,22 @@ void QQuickShaderEffect::updateProperties()
         lookThroughShaderCode(m_source.fragmentCode);
     }
 
-    if (!m_mesh && !m_source.attributeNames.contains(qt_position_attribute_name))
-        qWarning("QQuickShaderEffect: Missing reference to \'%s\'.", qt_position_attribute_name);
-    if (!m_mesh && !m_source.attributeNames.contains(qt_texcoord_attribute_name))
-        qWarning("QQuickShaderEffect: Missing reference to \'%s\'.", qt_texcoord_attribute_name);
-    if (!m_source.respectsMatrix)
-        qWarning("QQuickShaderEffect: Missing reference to \'qt_Matrix\'.");
-    if (!m_source.respectsOpacity)
-        qWarning("QQuickShaderEffect: Missing reference to \'qt_Opacity\'.");
+    if (!m_mesh && !m_source.attributeNames.contains(qt_position_attribute_name)) {
+        m_parseLog += QLatin1String("Warning: Missing reference to \'");
+        m_parseLog += QLatin1String(qt_position_attribute_name);
+        m_parseLog += QLatin1String("\'.\n");
+    }
+    if (!m_mesh && !m_source.attributeNames.contains(qt_texcoord_attribute_name)) {
+        m_parseLog += QLatin1String("Warning: Missing reference to \'");
+        m_parseLog += QLatin1String(qt_texcoord_attribute_name);
+        m_parseLog += QLatin1String("\'.\n");
+    }
+    if (!m_source.respectsMatrix) {
+        m_parseLog += QLatin1String("Warning: Missing reference to \'qt_Matrix\'.\n");
+    }
+    if (!m_source.respectsOpacity) {
+        m_parseLog += QLatin1String("Warning: Missing reference to \'qt_Opacity\'.\n");
+    }
 
     for (int i = 0; i < m_sources.size(); ++i) {
         QVariant v = property(m_sources.at(i).name);
@@ -688,6 +742,7 @@ QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDa
         m_programDirty = true;
         m_dirtyData = true;
         m_dirtyGeometry = true;
+        connect(node, SIGNAL(logAndStatusChanged(QString,int)), this, SLOT(updateLogAndStatus(QString,int)));
     }
 
     QQuickShaderEffectMaterial *material = node->shaderMaterial();
@@ -706,6 +761,15 @@ QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDa
 
         geometry = mesh->updateGeometry(geometry, m_source.attributeNames, rect);
         if (!geometry) {
+            QString log = mesh->log();
+            if (!log.isNull()) {
+                m_log = m_parseLog;
+                m_log += QLatin1String("*** Mesh ***\n");
+                m_log += log;
+                m_status = Error;
+                emit logChanged();
+                emit statusChanged();
+            }
             delete node;
             return 0;
         }
index 7536b42..4c575d9 100644 (file)
@@ -70,7 +70,10 @@ class Q_AUTOTEST_EXPORT QQuickShaderEffect : public QQuickItem
     Q_PROPERTY(bool blending READ blending WRITE setBlending NOTIFY blendingChanged)
     Q_PROPERTY(QVariant mesh READ mesh WRITE setMesh NOTIFY meshChanged)
     Q_PROPERTY(CullMode culling READ cullMode WRITE setCullMode NOTIFY cullModeChanged)
+    Q_PROPERTY(QString log READ log NOTIFY logChanged)
+    Q_PROPERTY(Status status READ status NOTIFY statusChanged)
     Q_ENUMS(CullMode)
+    Q_ENUMS(Status)
 
 public:
     enum CullMode
@@ -80,6 +83,13 @@ public:
         FrontFaceCulling = QQuickShaderEffectMaterial::FrontFaceCulling
     };
 
+    enum Status
+    {
+        Compiled,
+        Uncompiled,
+        Error
+    };
+
     QQuickShaderEffect(QQuickItem *parent = 0);
     ~QQuickShaderEffect();
 
@@ -98,7 +108,11 @@ public:
     CullMode cullMode() const { return m_cullMode; }
     void setCullMode(CullMode face);
 
+    QString log() const { return m_log; }
+    Status status() const { return m_status; }
+
     void ensureCompleted();
+    QString parseLog() { return m_parseLog; }
 
 Q_SIGNALS:
     void fragmentShaderChanged();
@@ -106,6 +120,8 @@ Q_SIGNALS:
     void blendingChanged();
     void meshChanged();
     void cullModeChanged();
+    void logChanged();
+    void statusChanged();
 
 protected:
     virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry);
@@ -116,6 +132,7 @@ private Q_SLOTS:
     void changeSource(int index);
     void updateData();
     void updateGeometry();
+    void updateLogAndStatus(const QString &log, int status);
 
 private:
     friend class QQuickCustomMaterialShader;
@@ -133,6 +150,8 @@ private:
     QQuickShaderEffectMesh *m_mesh;
     QQuickGridMesh m_defaultMesh;
     CullMode m_cullMode;
+    QString m_log;
+    Status m_status;
 
     struct SourceData
     {
@@ -141,6 +160,7 @@ private:
         QByteArray name;
     };
     QVector<SourceData> m_sources;
+    QString m_parseLog;
 
     uint m_blending : 1;
     uint m_dirtyData : 1;
index 3154ac7..f0edf8d 100644 (file)
@@ -68,40 +68,47 @@ QQuickGridMesh::QQuickGridMesh(QObject *parent)
     connect(this, SIGNAL(resolutionChanged()), this, SIGNAL(geometryChanged()));
 }
 
-QSGGeometry *QQuickGridMesh::updateGeometry(QSGGeometry *geometry, const QVector<QByteArray> &attributes, const QRectF &dstRect) const
+QSGGeometry *QQuickGridMesh::updateGeometry(QSGGeometry *geometry, const QVector<QByteArray> &attributes, const QRectF &dstRect)
 {
     int vmesh = m_resolution.height();
     int hmesh = m_resolution.width();
     int attrCount = attributes.count();
 
+    int positionIndex = attributes.indexOf(qtPositionAttributeName());
+    int texCoordIndex = attributes.indexOf(qtTexCoordAttributeName());
+
     if (!geometry) {
-        bool error = true;
-        Q_UNUSED(error)
         switch (attrCount) {
         case 0:
-            qWarning("QQuickGridMesh:: No attributes specified.");
-            break;
+            m_log = QLatin1String("Error: No attributes specified.");
+            return 0;
         case 1:
-            if (attributes.at(0) == qtPositionAttributeName()) {
-                error = false;
-                break;
+            if (positionIndex != 0) {
+                m_log = QLatin1String("Error: Missing \'");
+                m_log += QLatin1String(qtPositionAttributeName());
+                m_log += QLatin1String("\' attribute.\n");
+                return 0;
             }
-            qWarning("QQuickGridMesh:: Missing \'%s\' attribute.",
-                     qtPositionAttributeName());
             break;
         case 2:
-            if (attributes.contains(qtPositionAttributeName())
-                && attributes.contains(qtTexCoordAttributeName()))
-            {
-                error = false;
-                break;
+            if (positionIndex == -1 || texCoordIndex == -1) {
+                m_log.clear();
+                if (positionIndex == -1) {
+                    m_log = QLatin1String("Error: Missing \'");
+                    m_log += QLatin1String(qtPositionAttributeName());
+                    m_log += QLatin1String("\' attribute.\n");
+                }
+                if (texCoordIndex == -1) {
+                    m_log += QLatin1String("Error: Missing \'");
+                    m_log += QLatin1String(qtTexCoordAttributeName());
+                    m_log += QLatin1String("\' attribute.\n");
+                }
+                return 0;
             }
-            qWarning("QQuickGridMesh:: Missing \'%s\' or \'%s\' attribute.",
-                     qtPositionAttributeName(), qtTexCoordAttributeName());
             break;
         default:
-            qWarning("QQuickGridMesh:: Too many attributes specified.");
-            break;
+            m_log = QLatin1String("Error: Too many attributes specified.");
+            return 0;
         }
 
         geometry = new QSGGeometry(attrCount == 1
@@ -116,8 +123,6 @@ QSGGeometry *QQuickGridMesh::updateGeometry(QSGGeometry *geometry, const QVector
 
     QSGGeometry::Point2D *vdata = static_cast<QSGGeometry::Point2D *>(geometry->vertexData());
 
-    bool positionFirst = attributes.at(0) == qtPositionAttributeName();
-
     QRectF srcRect(0, 0, 1, 1);
     for (int iy = 0; iy <= vmesh; ++iy) {
         float fy = iy / float(vmesh);
@@ -126,7 +131,7 @@ QSGGeometry *QQuickGridMesh::updateGeometry(QSGGeometry *geometry, const QVector
         for (int ix = 0; ix <= hmesh; ++ix) {
             float fx = ix / float(hmesh);
             for (int ia = 0; ia < attrCount; ++ia) {
-                if (positionFirst == (ia == 0)) {
+                if (ia == positionIndex) {
                     vdata->x = float(dstRect.left()) + fx * float(dstRect.width());
                     vdata->y = y;
                     ++vdata;
index 1671fd5..77a3c6a 100644 (file)
@@ -64,7 +64,9 @@ class Q_QUICK_EXPORT QQuickShaderEffectMesh : public QObject
 public:
     QQuickShaderEffectMesh(QObject *parent = 0);
     // If 'geometry' != 0, 'attributes' is the same as last time the function was called.
-    virtual QSGGeometry *updateGeometry(QSGGeometry *geometry, const QVector<QByteArray> &attributes, const QRectF &rect) const = 0;
+    virtual QSGGeometry *updateGeometry(QSGGeometry *geometry, const QVector<QByteArray> &attributes, const QRectF &rect) = 0;
+    // If updateGeometry() fails, the reason should appear in the log.
+    virtual QString log() const { return QString(); }
 
 Q_SIGNALS:
     // Emitted when the geometry needs to be updated.
@@ -77,7 +79,8 @@ class QQuickGridMesh : public QQuickShaderEffectMesh
     Q_PROPERTY(QSize resolution READ resolution WRITE setResolution NOTIFY resolutionChanged)
 public:
     QQuickGridMesh(QObject *parent = 0);
-    virtual QSGGeometry *updateGeometry(QSGGeometry *geometry, const QVector<QByteArray> &attributes, const QRectF &rect) const;
+    virtual QSGGeometry *updateGeometry(QSGGeometry *geometry, const QVector<QByteArray> &attributes, const QRectF &rect);
+    virtual QString log() const { return m_log; }
 
     void setResolution(const QSize &res);
     QSize resolution() const;
@@ -87,6 +90,7 @@ Q_SIGNALS:
 
 private:
     QSize m_resolution;
+    QString m_log;
 };
 
 inline QColor qt_premultiply_color(const QColor &c)
index 968982b..c0d8fbc 100644 (file)
@@ -42,6 +42,7 @@
 #include <private/qquickshadereffectnode_p.h>
 
 #include "qquickshadereffectmesh_p.h"
+#include "qquickshadereffect_p.h"
 #include <QtQuick/qsgtextureprovider.h>
 #include <QtQuick/private/qsgrenderer_p.h>
 
@@ -58,6 +59,7 @@ public:
 protected:
     friend class QQuickShaderEffectNode;
 
+    virtual void compile();
     virtual void initialize();
     virtual const char *vertexShader() const;
     virtual const char *fragmentShader() const;
@@ -65,6 +67,8 @@ protected:
     const QQuickShaderEffectMaterialKey m_key;
     QVector<const char *> m_attributeNames;
     const QVector<QByteArray> m_attributes;
+    QString m_log;
+    bool m_compiled;
 
     QVector<int> m_uniformLocs;
     int m_opacityLoc;
@@ -75,6 +79,7 @@ protected:
 QQuickCustomMaterialShader::QQuickCustomMaterialShader(const QQuickShaderEffectMaterialKey &key, const QVector<QByteArray> &attributes)
     : m_key(key)
     , m_attributes(attributes)
+    , m_compiled(false)
     , m_textureIndicesSet(false)
 {
     for (int i = 0; i < attributes.count(); ++i)
@@ -92,7 +97,12 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri
 {
     Q_ASSERT(newEffect != 0);
 
-    const QQuickShaderEffectMaterial *material = static_cast<const QQuickShaderEffectMaterial *>(newEffect);
+    QQuickShaderEffectMaterial *material = static_cast<QQuickShaderEffectMaterial *>(newEffect);
+    if (!material->m_emittedLogChanged && material->m_node) {
+        material->m_emittedLogChanged = true;
+        emit material->m_node->logAndStatusChanged(m_log, m_compiled ? QQuickShaderEffect::Compiled
+                                                                     : QQuickShaderEffect::Error);
+    }
 
     if (!m_textureIndicesSet) {
         for (int i = 0; i < material->m_textures.size(); ++i)
@@ -192,6 +202,78 @@ char const *const *QQuickCustomMaterialShader::attributeNames() const
     return m_attributeNames.constData();
 }
 
+void QQuickCustomMaterialShader::compile()
+{
+    Q_ASSERT_X(!program()->isLinked(), "QQuickCustomMaterialShader::compile()", "Compile called multiple times!");
+
+    m_log.clear();
+    m_compiled = true;
+    if (!program()->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShader())) {
+        m_log += QLatin1String("*** Vertex shader ***\n");
+        m_log += program()->log();
+        m_compiled = false;
+    }
+    if (!program()->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShader())) {
+        m_log += QLatin1String("*** Fragment shader ***\n");
+        m_log += program()->log();
+        m_compiled = false;
+    }
+
+    char const *const *attr = attributeNames();
+#ifndef QT_NO_DEBUG
+    int maxVertexAttribs = 0;
+    glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs);
+    int attrCount = 0;
+    while (attrCount < maxVertexAttribs && attr[attrCount])
+        ++attrCount;
+    if (attr[attrCount]) {
+        qWarning("List of attribute names is too long.\n"
+                 "Maximum number of attributes on this hardware is %i.\n"
+                 "Vertex shader:\n%s\n"
+                 "Fragment shader:\n%s\n",
+                 maxVertexAttribs, vertexShader(), fragmentShader());
+    }
+#endif
+
+    if (m_compiled) {
+#ifndef QT_NO_DEBUG
+        for (int i = 0; i < attrCount; ++i) {
+#else
+        for (int i = 0; attr[i]; ++i) {
+#endif
+            if (*attr[i])
+                program()->bindAttributeLocation(attr[i], i);
+        }
+        m_compiled = program()->link();
+        m_log += program()->log();
+    }
+
+    static const char *fallbackVertexShader =
+            "uniform highp mat4 qt_Matrix;"
+            "attribute highp vec4 v;"
+            "void main() { gl_Position = qt_Matrix * v; }";
+    static const char *fallbackFragmentShader =
+            "void main() { gl_FragColor = vec4(1., 0., 1., 1.); }";
+
+    if (!m_compiled) {
+        qWarning("QQuickCustomMaterialShader: Shader compilation failed:");
+        qWarning() << program()->log();
+
+        program()->removeAllShaders();
+        program()->addShaderFromSourceCode(QOpenGLShader::Vertex, fallbackVertexShader);
+        program()->addShaderFromSourceCode(QOpenGLShader::Fragment, fallbackFragmentShader);
+#ifndef QT_NO_DEBUG
+        for (int i = 0; i < attrCount; ++i) {
+#else
+        for (int i = 0; attr[i]; ++i) {
+#endif
+            if (qstrcmp(attr[i], qtPositionAttributeName()) == 0)
+                program()->bindAttributeLocation("v", i);
+        }
+        program()->link();
+    }
+}
+
 void QQuickCustomMaterialShader::initialize()
 {
     m_opacityLoc = program()->uniformLocation("qt_Opacity");
@@ -222,8 +304,10 @@ uint qHash(const QQuickShaderEffectMaterialKey &key)
 
 QHash<QQuickShaderEffectMaterialKey, QSharedPointer<QSGMaterialType> > QQuickShaderEffectMaterial::materialMap;
 
-QQuickShaderEffectMaterial::QQuickShaderEffectMaterial()
+QQuickShaderEffectMaterial::QQuickShaderEffectMaterial(QQuickShaderEffectNode *node)
     : m_cullMode(NoCulling)
+    , m_node(node)
+    , m_emittedLogChanged(false)
 {
     setFlag(Blending, true);
 }
@@ -256,6 +340,7 @@ QQuickShaderEffectMaterial::CullMode QQuickShaderEffectMaterial::cullMode() cons
 void QQuickShaderEffectMaterial::setProgramSource(const QQuickShaderEffectProgram &source)
 {
     m_source = source;
+    m_emittedLogChanged = false;
     m_type = materialMap.value(m_source);
     if (m_type.isNull()) {
         m_type = QSharedPointer<QSGMaterialType>(new QSGMaterialType);
@@ -290,6 +375,7 @@ void QQuickShaderEffectMaterial::updateTextures() const
 
 
 QQuickShaderEffectNode::QQuickShaderEffectNode()
+    : m_material(this)
 {
     QSGNode::setFlag(UsePreprocess, true);
     setMaterial(&m_material);
index 5f90e71..4fd84b9 100644 (file)
@@ -78,6 +78,7 @@ struct QQuickShaderEffectProgram : public QQuickShaderEffectMaterialKey
 
 
 class QQuickCustomMaterialShader;
+class QQuickShaderEffectNode;
 class QQuickShaderEffectMaterial : public QSGMaterial
 {
 public:
@@ -88,7 +89,7 @@ public:
         FrontFaceCulling
     };
 
-    QQuickShaderEffectMaterial();
+    explicit QQuickShaderEffectMaterial(QQuickShaderEffectNode *node = 0);
     virtual QSGMaterialType *type() const;
     virtual QSGMaterialShader *createShader() const;
     virtual int compare(const QSGMaterial *other) const;
@@ -117,6 +118,8 @@ protected:
     QVector<QPair<QByteArray, QVariant> > m_uniformValues;
     QVector<QPair<QByteArray, QSGTextureProvider *> > m_textures;
     CullMode m_cullMode;
+    QQuickShaderEffectNode *m_node;
+    bool m_emittedLogChanged;
 
     static QHash<QQuickShaderEffectMaterialKey, QSharedPointer<QSGMaterialType> > materialMap;
 };
@@ -135,6 +138,9 @@ public:
 
     QQuickShaderEffectMaterial *shaderMaterial() { return &m_material; }
 
+Q_SIGNALS:
+    void logAndStatusChanged(const QString &, int status);
+
 private Q_SLOTS:
     void markDirtyTexture();
 
index 3287b8c..dc5ea45 100644 (file)
@@ -89,52 +89,8 @@ private:
         OpacityPresent = 0x08,
         PropertyPresent = 0x10
     };
-
-    static void installMsgHandler();
-    static void uninstallMsgHandler();
-    static void msgHandler(QtMsgType type, const char *msg);
-    static void expectWarning(const char *msg);
-
-    static QtMsgHandler originalMsgHandler;
-    static QByteArray actualWarnings;
-    static QByteArray expectedWarnings;
 };
 
-QtMsgHandler tst_qquickshadereffect::originalMsgHandler = 0;
-QByteArray tst_qquickshadereffect::actualWarnings;
-QByteArray tst_qquickshadereffect::expectedWarnings;
-
-void tst_qquickshadereffect::installMsgHandler()
-{
-    Q_ASSERT(originalMsgHandler == 0);
-    originalMsgHandler = qInstallMsgHandler(msgHandler);
-    actualWarnings.clear();
-    expectedWarnings.clear();
-}
-
-void tst_qquickshadereffect::uninstallMsgHandler()
-{
-    Q_ASSERT(originalMsgHandler != 0);
-    qInstallMsgHandler(originalMsgHandler);
-    originalMsgHandler = 0;
-    QCOMPARE(QString(actualWarnings), QString(expectedWarnings)); // QString for sensible output.
-}
-
-void tst_qquickshadereffect::msgHandler(QtMsgType type, const char *msg)
-{
-    Q_ASSERT(originalMsgHandler != 0);
-    if (type == QtWarningMsg)
-        actualWarnings.append(msg).append('\n');
-    originalMsgHandler(type, msg);
-}
-
-void tst_qquickshadereffect::expectWarning(const char *msg)
-{
-    Q_ASSERT(originalMsgHandler != 0);
-    expectedWarnings.append(msg).append('\n');
-    QTest::ignoreMessage(QtWarningMsg, msg);
-}
-
 tst_qquickshadereffect::tst_qquickshadereffect()
 {
 }
@@ -295,21 +251,20 @@ void tst_qquickshadereffect::lookThroughShaderCode()
     TestShaderEffect item;
     QVERIFY(!item.isConnected(SIGNAL(dummyChanged()))); // Nothing connected yet.
 
-    installMsgHandler();
+    QString expected;
     if ((presenceFlags & VertexPresent) == 0)
-        expectWarning("QQuickShaderEffect: Missing reference to \'qt_Vertex\'.");
+        expected += "Warning: Missing reference to \'qt_Vertex\'.\n";
     if ((presenceFlags & TexCoordPresent) == 0)
-        expectWarning("QQuickShaderEffect: Missing reference to \'qt_MultiTexCoord0\'.");
+        expected += "Warning: Missing reference to \'qt_MultiTexCoord0\'.\n";
     if ((presenceFlags & MatrixPresent) == 0)
-        expectWarning("QQuickShaderEffect: Missing reference to \'qt_Matrix\'.");
+        expected += "Warning: Missing reference to \'qt_Matrix\'.\n";
     if ((presenceFlags & OpacityPresent) == 0)
-        expectWarning("QQuickShaderEffect: Missing reference to \'qt_Opacity\'.");
+        expected += "Warning: Missing reference to \'qt_Opacity\'.\n";
 
     item.setVertexShader(vertexShader);
     item.setFragmentShader(fragmentShader);
     item.ensureCompleted();
-
-    uninstallMsgHandler();
+    QCOMPARE(item.parseLog(), expected);
 
     // If the uniform was successfully parsed, the notify signal has been connected to an update slot.
     QCOMPARE(item.isConnected(SIGNAL(dummyChanged())), (presenceFlags & PropertyPresent) != 0);