aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items
diff options
context:
space:
mode:
authorKim Motoyoshi Kalland <kim.kalland@nokia.com>2012-01-11 17:34:58 +0100
committerQt by Nokia <qt-info@nokia.com>2012-01-16 15:07:33 +0100
commite79eedcb1eaf56280035550f4c6bb2395a51fd16 (patch)
tree45e6d0c6aa05ddd59762159b7dc27bd877f57134 /src/quick/items
parent7911627f718d0e4876c42adbb5f3e02cf3c9f4eb (diff)
Added log and status properties to ShaderEffect.
Task-number: QTBUG-23531 Change-Id: I136f6d9642ff9d4074fe8dae1f5714a05349107a Reviewed-by: Gunnar Sletta <gunnar.sletta@nokia.com>
Diffstat (limited to 'src/quick/items')
-rw-r--r--src/quick/items/qquickshadereffect.cpp82
-rw-r--r--src/quick/items/qquickshadereffect_p.h20
-rw-r--r--src/quick/items/qquickshadereffectmesh.cpp49
-rw-r--r--src/quick/items/qquickshadereffectmesh_p.h8
-rw-r--r--src/quick/items/qquickshadereffectnode.cpp90
-rw-r--r--src/quick/items/qquickshadereffectnode_p.h8
6 files changed, 221 insertions, 36 deletions
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<SourceData> 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<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;
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<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)
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 <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);
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<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();