From e79eedcb1eaf56280035550f4c6bb2395a51fd16 Mon Sep 17 00:00:00 2001 From: Kim Motoyoshi Kalland Date: Wed, 11 Jan 2012 17:34:58 +0100 Subject: Added log and status properties to ShaderEffect. Task-number: QTBUG-23531 Change-Id: I136f6d9642ff9d4074fe8dae1f5714a05349107a Reviewed-by: Gunnar Sletta --- src/quick/items/qquickshadereffect.cpp | 82 ++++++++++++++++++++++++--- src/quick/items/qquickshadereffect_p.h | 20 +++++++ src/quick/items/qquickshadereffectmesh.cpp | 49 ++++++++-------- src/quick/items/qquickshadereffectmesh_p.h | 8 ++- src/quick/items/qquickshadereffectnode.cpp | 90 +++++++++++++++++++++++++++++- src/quick/items/qquickshadereffectnode_p.h | 8 ++- 6 files changed, 221 insertions(+), 36 deletions(-) (limited to 'src/quick/items') diff --git a/src/quick/items/qquickshadereffect.cpp b/src/quick/items/qquickshadereffect.cpp index be01338c1b..7e52988572 100644 --- a/src/quick/items/qquickshadereffect.cpp +++ b/src/quick/items/qquickshadereffect.cpp @@ -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; } diff --git a/src/quick/items/qquickshadereffect_p.h b/src/quick/items/qquickshadereffect_p.h index 7536b42a73..4c575d985d 100644 --- a/src/quick/items/qquickshadereffect_p.h +++ b/src/quick/items/qquickshadereffect_p.h @@ -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 m_sources; + QString m_parseLog; uint m_blending : 1; uint m_dirtyData : 1; diff --git a/src/quick/items/qquickshadereffectmesh.cpp b/src/quick/items/qquickshadereffectmesh.cpp index 3154ac7cfd..f0edf8d356 100644 --- a/src/quick/items/qquickshadereffectmesh.cpp +++ b/src/quick/items/qquickshadereffectmesh.cpp @@ -68,40 +68,47 @@ QQuickGridMesh::QQuickGridMesh(QObject *parent) connect(this, SIGNAL(resolutionChanged()), this, SIGNAL(geometryChanged())); } -QSGGeometry *QQuickGridMesh::updateGeometry(QSGGeometry *geometry, const QVector &attributes, const QRectF &dstRect) const +QSGGeometry *QQuickGridMesh::updateGeometry(QSGGeometry *geometry, const QVector &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(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; diff --git a/src/quick/items/qquickshadereffectmesh_p.h b/src/quick/items/qquickshadereffectmesh_p.h index 1671fd53f9..77a3c6abf6 100644 --- a/src/quick/items/qquickshadereffectmesh_p.h +++ b/src/quick/items/qquickshadereffectmesh_p.h @@ -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 &attributes, const QRectF &rect) const = 0; + virtual QSGGeometry *updateGeometry(QSGGeometry *geometry, const QVector &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 &attributes, const QRectF &rect) const; + virtual QSGGeometry *updateGeometry(QSGGeometry *geometry, const QVector &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) diff --git a/src/quick/items/qquickshadereffectnode.cpp b/src/quick/items/qquickshadereffectnode.cpp index 968982bb88..c0d8fbcd30 100644 --- a/src/quick/items/qquickshadereffectnode.cpp +++ b/src/quick/items/qquickshadereffectnode.cpp @@ -42,6 +42,7 @@ #include #include "qquickshadereffectmesh_p.h" +#include "qquickshadereffect_p.h" #include #include @@ -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 m_attributeNames; const QVector m_attributes; + QString m_log; + bool m_compiled; QVector m_uniformLocs; int m_opacityLoc; @@ -75,6 +79,7 @@ protected: QQuickCustomMaterialShader::QQuickCustomMaterialShader(const QQuickShaderEffectMaterialKey &key, const QVector &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(newEffect); + QQuickShaderEffectMaterial *material = static_cast(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 > 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(new QSGMaterialType); @@ -290,6 +375,7 @@ void QQuickShaderEffectMaterial::updateTextures() const QQuickShaderEffectNode::QQuickShaderEffectNode() + : m_material(this) { QSGNode::setFlag(UsePreprocess, true); setMaterial(&m_material); diff --git a/src/quick/items/qquickshadereffectnode_p.h b/src/quick/items/qquickshadereffectnode_p.h index 5f90e71cab..4fd84b969a 100644 --- a/src/quick/items/qquickshadereffectnode_p.h +++ b/src/quick/items/qquickshadereffectnode_p.h @@ -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 > m_uniformValues; QVector > m_textures; CullMode m_cullMode; + QQuickShaderEffectNode *m_node; + bool m_emittedLogChanged; static QHash > materialMap; }; @@ -135,6 +138,9 @@ public: QQuickShaderEffectMaterial *shaderMaterial() { return &m_material; } +Q_SIGNALS: + void logAndStatusChanged(const QString &, int status); + private Q_SLOTS: void markDirtyTexture(); -- cgit v1.2.3