b81045518bf60c933f13f35b169579c50716bfe7
[profile/ivi/qtdeclarative.git] / src / quick / items / qquickshadereffectnode.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <private/qquickshadereffectnode_p.h>
43
44 #include "qquickshadereffectmesh_p.h"
45 #include "qquickshadereffect_p.h"
46 #include <QtQuick/qsgtextureprovider.h>
47 #include <QtQuick/private/qsgrenderer_p.h>
48
49 QT_BEGIN_NAMESPACE
50
51 class QQuickCustomMaterialShader : public QSGMaterialShader
52 {
53 public:
54     QQuickCustomMaterialShader(const QQuickShaderEffectMaterialKey &key, const QVector<QByteArray> &attributes);
55     virtual void deactivate();
56     virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect);
57     virtual char const *const *attributeNames() const;
58
59 protected:
60     friend class QQuickShaderEffectNode;
61
62     virtual void compile();
63     virtual const char *vertexShader() const;
64     virtual const char *fragmentShader() const;
65
66     const QQuickShaderEffectMaterialKey m_key;
67     QVector<const char *> m_attributeNames;
68     const QVector<QByteArray> m_attributes;
69     QString m_log;
70     bool m_compiled;
71
72     QVector<int> m_uniformLocs[QQuickShaderEffectMaterialKey::ShaderTypeCount];
73     uint m_initialized : 1;
74 };
75
76 QQuickCustomMaterialShader::QQuickCustomMaterialShader(const QQuickShaderEffectMaterialKey &key, const QVector<QByteArray> &attributes)
77     : m_key(key)
78     , m_attributes(attributes)
79     , m_compiled(false)
80     , m_initialized(false)
81 {
82     for (int i = 0; i < attributes.count(); ++i)
83         m_attributeNames.append(attributes.at(i).constData());
84     m_attributeNames.append(0);
85 }
86
87 void QQuickCustomMaterialShader::deactivate()
88 {
89     QSGMaterialShader::deactivate();
90     glDisable(GL_CULL_FACE);
91 }
92
93 void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
94 {
95     typedef QQuickShaderEffectMaterial::UniformData UniformData;
96
97     Q_ASSERT(newEffect != 0);
98
99     QQuickShaderEffectMaterial *material = static_cast<QQuickShaderEffectMaterial *>(newEffect);
100     if (!material->m_emittedLogChanged && material->m_node) {
101         material->m_emittedLogChanged = true;
102         emit material->m_node->logAndStatusChanged(m_log, m_compiled ? QQuickShaderEffect::Compiled
103                                                                      : QQuickShaderEffect::Error);
104     }
105
106     int textureProviderIndex = 0;
107     if (!m_initialized) {
108         for (int shaderType = 0; shaderType < QQuickShaderEffectMaterialKey::ShaderTypeCount; ++shaderType) {
109             Q_ASSERT(m_uniformLocs[shaderType].isEmpty());
110             m_uniformLocs[shaderType].reserve(material->uniforms[shaderType].size());
111             for (int i = 0; i < material->uniforms[shaderType].size(); ++i) {
112                 const UniformData &d = material->uniforms[shaderType].at(i);
113                 QByteArray name = d.name;
114                 if (d.specialType == UniformData::Sampler) {
115                     program()->setUniformValue(d.name.constData(), textureProviderIndex++);
116                     // We don't need to store the sampler uniform locations, since their values
117                     // only need to be set once. Look for the "qt_SubRect_" uniforms instead.
118                     // These locations are used when binding the textures later.
119                     name = "qt_SubRect_" + name;
120                 }
121                 m_uniformLocs[shaderType].append(program()->uniformLocation(name.constData()));
122             }
123         }
124         m_initialized = true;
125         textureProviderIndex = 0;
126     }
127
128     QOpenGLFunctions *functions = state.context()->functions();
129     for (int shaderType = 0; shaderType < QQuickShaderEffectMaterialKey::ShaderTypeCount; ++shaderType) {
130         for (int i = 0; i < material->uniforms[shaderType].size(); ++i) {
131             const UniformData &d = material->uniforms[shaderType].at(i);
132             int loc = m_uniformLocs[shaderType].at(i);
133             if (d.specialType == UniformData::Sampler) {
134                 int idx = textureProviderIndex++;
135                 functions->glActiveTexture(GL_TEXTURE0 + idx);
136                 if (QSGTextureProvider *provider = material->textureProviders.at(idx)) {
137                     if (QSGTexture *texture = provider->texture()) {
138                         if (loc >= 0) {
139                             QRectF r = texture->normalizedTextureSubRect();
140                             program()->setUniformValue(loc, r.x(), r.y(), r.width(), r.height());
141                         } else if (texture->isAtlasTexture()) {
142                             texture = texture->removedFromAtlas();
143                         }
144                         texture->bind();
145                         continue;
146                     }
147                 }
148                 qWarning("ShaderEffect: source or provider missing when binding textures");
149                 glBindTexture(GL_TEXTURE_2D, 0);
150             } else if (d.specialType == UniformData::Opacity) {
151                 program()->setUniformValue(loc, state.opacity());
152             } else if (d.specialType == UniformData::Matrix) {
153                 if (state.isMatrixDirty())
154                     program()->setUniformValue(loc, state.combinedMatrix());
155             } else if (d.specialType == UniformData::None) {
156                 switch (d.value.type()) {
157                 case QMetaType::QColor:
158                     program()->setUniformValue(loc, qt_premultiply_color(qvariant_cast<QColor>(d.value)));
159                     break;
160                 case QMetaType::Float:
161                     program()->setUniformValue(loc, qvariant_cast<float>(d.value));
162                     break;
163                 case QMetaType::Double:
164                     program()->setUniformValue(loc, (float) qvariant_cast<double>(d.value));
165                     break;
166                 case QMetaType::QTransform:
167                     program()->setUniformValue(loc, qvariant_cast<QTransform>(d.value));
168                     break;
169                 case QMetaType::Int:
170                     program()->setUniformValue(loc, d.value.toInt());
171                     break;
172                 case QMetaType::Bool:
173                     program()->setUniformValue(loc, GLint(d.value.toBool()));
174                     break;
175                 case QMetaType::QSize:
176                 case QMetaType::QSizeF:
177                     program()->setUniformValue(loc, d.value.toSizeF());
178                     break;
179                 case QMetaType::QPoint:
180                 case QMetaType::QPointF:
181                     program()->setUniformValue(loc, d.value.toPointF());
182                     break;
183                 case QMetaType::QRect:
184                 case QMetaType::QRectF:
185                     {
186                         QRectF r = d.value.toRectF();
187                         program()->setUniformValue(loc, r.x(), r.y(), r.width(), r.height());
188                     }
189                     break;
190                 case QMetaType::QVector3D:
191                     program()->setUniformValue(loc, qvariant_cast<QVector3D>(d.value));
192                     break;
193                 case QMetaType::QVector4D:
194                     program()->setUniformValue(loc, qvariant_cast<QVector4D>(d.value));
195                     break;
196                 default:
197                     break;
198                 }
199             }
200         }
201     }
202     functions->glActiveTexture(GL_TEXTURE0);
203
204     const QQuickShaderEffectMaterial *oldMaterial = static_cast<const QQuickShaderEffectMaterial *>(oldEffect);
205     if (oldEffect == 0 || material->cullMode != oldMaterial->cullMode) {
206         switch (material->cullMode) {
207         case QQuickShaderEffectMaterial::FrontFaceCulling:
208             glEnable(GL_CULL_FACE);
209             glCullFace(GL_FRONT);
210             break;
211         case QQuickShaderEffectMaterial::BackFaceCulling:
212             glEnable(GL_CULL_FACE);
213             glCullFace(GL_BACK);
214             break;
215         default:
216             glDisable(GL_CULL_FACE);
217             break;
218         }
219     }
220 }
221
222 char const *const *QQuickCustomMaterialShader::attributeNames() const
223 {
224     return m_attributeNames.constData();
225 }
226
227 void QQuickCustomMaterialShader::compile()
228 {
229     Q_ASSERT_X(!program()->isLinked(), "QQuickCustomMaterialShader::compile()", "Compile called multiple times!");
230
231     m_log.clear();
232     m_compiled = true;
233     if (!program()->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShader())) {
234         m_log += QLatin1String("*** Vertex shader ***\n");
235         m_log += program()->log();
236         m_compiled = false;
237     }
238     if (!program()->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShader())) {
239         m_log += QLatin1String("*** Fragment shader ***\n");
240         m_log += program()->log();
241         m_compiled = false;
242     }
243
244     char const *const *attr = attributeNames();
245 #ifndef QT_NO_DEBUG
246     int maxVertexAttribs = 0;
247     glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs);
248     int attrCount = 0;
249     while (attrCount < maxVertexAttribs && attr[attrCount])
250         ++attrCount;
251     if (attr[attrCount]) {
252         qWarning("List of attribute names is too long.\n"
253                  "Maximum number of attributes on this hardware is %i.\n"
254                  "Vertex shader:\n%s\n"
255                  "Fragment shader:\n%s\n",
256                  maxVertexAttribs, vertexShader(), fragmentShader());
257     }
258 #endif
259
260     if (m_compiled) {
261 #ifndef QT_NO_DEBUG
262         for (int i = 0; i < attrCount; ++i) {
263 #else
264         for (int i = 0; attr[i]; ++i) {
265 #endif
266             if (*attr[i])
267                 program()->bindAttributeLocation(attr[i], i);
268         }
269         m_compiled = program()->link();
270         m_log += program()->log();
271     }
272
273     static const char *fallbackVertexShader =
274             "uniform highp mat4 qt_Matrix;"
275             "attribute highp vec4 v;"
276             "void main() { gl_Position = qt_Matrix * v; }";
277     static const char *fallbackFragmentShader =
278             "void main() { gl_FragColor = vec4(1., 0., 1., 1.); }";
279
280     if (!m_compiled) {
281         qWarning("QQuickCustomMaterialShader: Shader compilation failed:");
282         qWarning() << program()->log();
283
284         program()->removeAllShaders();
285         program()->addShaderFromSourceCode(QOpenGLShader::Vertex, fallbackVertexShader);
286         program()->addShaderFromSourceCode(QOpenGLShader::Fragment, fallbackFragmentShader);
287 #ifndef QT_NO_DEBUG
288         for (int i = 0; i < attrCount; ++i) {
289 #else
290         for (int i = 0; attr[i]; ++i) {
291 #endif
292             if (qstrcmp(attr[i], qtPositionAttributeName()) == 0)
293                 program()->bindAttributeLocation("v", i);
294         }
295         program()->link();
296     }
297 }
298
299 const char *QQuickCustomMaterialShader::vertexShader() const
300 {
301     return m_key.sourceCode[QQuickShaderEffectMaterialKey::VertexShader].constData();
302 }
303
304 const char *QQuickCustomMaterialShader::fragmentShader() const
305 {
306     return m_key.sourceCode[QQuickShaderEffectMaterialKey::FragmentShader].constData();
307 }
308
309
310 bool QQuickShaderEffectMaterialKey::operator == (const QQuickShaderEffectMaterialKey &other) const
311 {
312     if (className != other.className)
313         return false;
314     for (int shaderType = 0; shaderType < ShaderTypeCount; ++shaderType) {
315         if (sourceCode[shaderType] != other.sourceCode[shaderType])
316             return false;
317     }
318     return true;
319 }
320
321 uint qHash(const QQuickShaderEffectMaterialKey &key)
322 {
323     uint hash = qHash((void *)key.className);
324     typedef QQuickShaderEffectMaterialKey Key;
325     for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType)
326         hash = hash * 31337 + qHash(key.sourceCode[shaderType]);
327     return hash;
328 }
329
330
331 QHash<QQuickShaderEffectMaterialKey, QSharedPointer<QSGMaterialType> > QQuickShaderEffectMaterial::materialMap;
332
333 QQuickShaderEffectMaterial::QQuickShaderEffectMaterial(QQuickShaderEffectNode *node)
334     : cullMode(NoCulling)
335     , m_node(node)
336     , m_emittedLogChanged(false)
337 {
338     setFlag(Blending | RequiresFullMatrix, true);
339 }
340
341 QSGMaterialType *QQuickShaderEffectMaterial::type() const
342 {
343     return m_type.data();
344 }
345
346 QSGMaterialShader *QQuickShaderEffectMaterial::createShader() const
347 {
348     return new QQuickCustomMaterialShader(m_source, attributes);
349 }
350
351 int QQuickShaderEffectMaterial::compare(const QSGMaterial *other) const
352 {
353     return this - static_cast<const QQuickShaderEffectMaterial *>(other);
354 }
355
356 void QQuickShaderEffectMaterial::setProgramSource(const QQuickShaderEffectMaterialKey &source)
357 {
358     m_source = source;
359     m_emittedLogChanged = false;
360     m_type = materialMap.value(m_source);
361     if (m_type.isNull()) {
362         m_type = QSharedPointer<QSGMaterialType>(new QSGMaterialType);
363         materialMap.insert(m_source, m_type);
364     }
365 }
366
367 void QQuickShaderEffectMaterial::updateTextures() const
368 {
369     for (int i = 0; i < textureProviders.size(); ++i) {
370         if (QSGTextureProvider *provider = textureProviders.at(i)) {
371             if (QSGDynamicTexture *texture = qobject_cast<QSGDynamicTexture *>(provider->texture()))
372                 texture->updateTexture();
373         }
374     }
375 }
376
377 void QQuickShaderEffectMaterial::invalidateTextureProvider(QSGTextureProvider *provider)
378 {
379     for (int i = 0; i < textureProviders.size(); ++i) {
380         if (provider == textureProviders.at(i))
381             textureProviders[i] = 0;
382     }
383 }
384
385
386 QQuickShaderEffectNode::QQuickShaderEffectNode()
387 {
388     QSGNode::setFlag(UsePreprocess, true);
389
390 #ifdef QML_RUNTIME_TESTING
391     description = QLatin1String("shadereffect");
392 #endif
393 }
394
395 QQuickShaderEffectNode::~QQuickShaderEffectNode()
396 {
397 }
398
399 void QQuickShaderEffectNode::markDirtyTexture()
400 {
401     markDirty(DirtyMaterial);
402 }
403
404 void QQuickShaderEffectNode::textureProviderDestroyed(QObject *object)
405 {
406     Q_ASSERT(material());
407     static_cast<QQuickShaderEffectMaterial *>(material())->invalidateTextureProvider(static_cast<QSGTextureProvider *>(object));
408 }
409
410 void QQuickShaderEffectNode::preprocess()
411 {
412     Q_ASSERT(material());
413     static_cast<QQuickShaderEffectMaterial *>(material())->updateTextures();
414 }
415
416 QT_END_NAMESPACE