Remove "All rights reserved" line from license headers.
[profile/ivi/qtdeclarative.git] / src / quick / items / qquickshadereffectnode.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
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 void initialize();
64     virtual const char *vertexShader() const;
65     virtual const char *fragmentShader() const;
66
67     const QQuickShaderEffectMaterialKey m_key;
68     QVector<const char *> m_attributeNames;
69     const QVector<QByteArray> m_attributes;
70     QString m_log;
71     bool m_compiled;
72
73     QVector<int> m_uniformLocs;
74     int m_opacityLoc;
75     int m_matrixLoc;
76     uint m_textureIndicesSet;
77 };
78
79 QQuickCustomMaterialShader::QQuickCustomMaterialShader(const QQuickShaderEffectMaterialKey &key, const QVector<QByteArray> &attributes)
80     : m_key(key)
81     , m_attributes(attributes)
82     , m_compiled(false)
83     , m_textureIndicesSet(false)
84 {
85     for (int i = 0; i < attributes.count(); ++i)
86         m_attributeNames.append(attributes.at(i).constData());
87     m_attributeNames.append(0);
88 }
89
90 void QQuickCustomMaterialShader::deactivate()
91 {
92     QSGMaterialShader::deactivate();
93     glDisable(GL_CULL_FACE);
94 }
95
96 void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
97 {
98     Q_ASSERT(newEffect != 0);
99
100     QQuickShaderEffectMaterial *material = static_cast<QQuickShaderEffectMaterial *>(newEffect);
101     if (!material->m_emittedLogChanged && material->m_node) {
102         material->m_emittedLogChanged = true;
103         emit material->m_node->logAndStatusChanged(m_log, m_compiled ? QQuickShaderEffect::Compiled
104                                                                      : QQuickShaderEffect::Error);
105     }
106
107     if (!m_textureIndicesSet) {
108         for (int i = 0; i < material->m_textures.size(); ++i)
109             program()->setUniformValue(material->m_textures.at(i).first.constData(), i);
110         m_textureIndicesSet = true;
111     }
112
113     if (m_uniformLocs.size() != material->m_uniformValues.size()) {
114         m_uniformLocs.reserve(material->m_uniformValues.size());
115         for (int i = 0; i < material->m_uniformValues.size(); ++i) {
116             const QByteArray &name = material->m_uniformValues.at(i).first;
117             m_uniformLocs.append(program()->uniformLocation(name.constData()));
118         }
119     }
120
121     QOpenGLFunctions *functions = state.context()->functions();
122     for (int i = material->m_textures.size() - 1; i >= 0; --i) {
123         functions->glActiveTexture(GL_TEXTURE0 + i);
124         if (QSGTextureProvider *provider = material->m_textures.at(i).second) {
125             if (QSGTexture *texture = provider->texture()) {
126                 texture->bind();
127                 continue;
128             }
129         }
130         qWarning("ShaderEffect: source or provider missing when binding textures");
131         glBindTexture(GL_TEXTURE_2D, 0);
132     }
133
134     if (material->m_source.respectsOpacity)
135         program()->setUniformValue(m_opacityLoc, state.opacity());
136
137     for (int i = 0; i < material->m_uniformValues.count(); ++i) {
138         const QVariant &v = material->m_uniformValues.at(i).second;
139
140         switch (v.type()) {
141         case QVariant::Color:
142             program()->setUniformValue(m_uniformLocs.at(i), qt_premultiply_color(qvariant_cast<QColor>(v)));
143             break;
144         case QVariant::Double:
145             program()->setUniformValue(m_uniformLocs.at(i), (float) qvariant_cast<double>(v));
146             break;
147         case QVariant::Transform:
148             program()->setUniformValue(m_uniformLocs.at(i), qvariant_cast<QTransform>(v));
149             break;
150         case QVariant::Int:
151             program()->setUniformValue(m_uniformLocs.at(i), v.toInt());
152             break;
153         case QVariant::Bool:
154             program()->setUniformValue(m_uniformLocs.at(i), GLint(v.toBool()));
155             break;
156         case QVariant::Size:
157         case QVariant::SizeF:
158             program()->setUniformValue(m_uniformLocs.at(i), v.toSizeF());
159             break;
160         case QVariant::Point:
161         case QVariant::PointF:
162             program()->setUniformValue(m_uniformLocs.at(i), v.toPointF());
163             break;
164         case QVariant::Rect:
165         case QVariant::RectF:
166             {
167                 QRectF r = v.toRectF();
168                 program()->setUniformValue(m_uniformLocs.at(i), r.x(), r.y(), r.width(), r.height());
169             }
170             break;
171         case QVariant::Vector3D:
172             program()->setUniformValue(m_uniformLocs.at(i), qvariant_cast<QVector3D>(v));
173             break;
174         default:
175             break;
176         }
177     }
178
179     const QQuickShaderEffectMaterial *oldMaterial = static_cast<const QQuickShaderEffectMaterial *>(oldEffect);
180     if (oldEffect == 0 || material->cullMode() != oldMaterial->cullMode()) {
181         switch (material->cullMode()) {
182         case QQuickShaderEffectMaterial::FrontFaceCulling:
183             glEnable(GL_CULL_FACE);
184             glCullFace(GL_FRONT);
185             break;
186         case QQuickShaderEffectMaterial::BackFaceCulling:
187             glEnable(GL_CULL_FACE);
188             glCullFace(GL_BACK);
189             break;
190         default:
191             glDisable(GL_CULL_FACE);
192             break;
193         }
194     }
195
196     if ((state.isMatrixDirty()) && material->m_source.respectsMatrix)
197         program()->setUniformValue(m_matrixLoc, state.combinedMatrix());
198 }
199
200 char const *const *QQuickCustomMaterialShader::attributeNames() const
201 {
202     return m_attributeNames.constData();
203 }
204
205 void QQuickCustomMaterialShader::compile()
206 {
207     Q_ASSERT_X(!program()->isLinked(), "QQuickCustomMaterialShader::compile()", "Compile called multiple times!");
208
209     m_log.clear();
210     m_compiled = true;
211     if (!program()->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShader())) {
212         m_log += QLatin1String("*** Vertex shader ***\n");
213         m_log += program()->log();
214         m_compiled = false;
215     }
216     if (!program()->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShader())) {
217         m_log += QLatin1String("*** Fragment shader ***\n");
218         m_log += program()->log();
219         m_compiled = false;
220     }
221
222     char const *const *attr = attributeNames();
223 #ifndef QT_NO_DEBUG
224     int maxVertexAttribs = 0;
225     glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs);
226     int attrCount = 0;
227     while (attrCount < maxVertexAttribs && attr[attrCount])
228         ++attrCount;
229     if (attr[attrCount]) {
230         qWarning("List of attribute names is too long.\n"
231                  "Maximum number of attributes on this hardware is %i.\n"
232                  "Vertex shader:\n%s\n"
233                  "Fragment shader:\n%s\n",
234                  maxVertexAttribs, vertexShader(), fragmentShader());
235     }
236 #endif
237
238     if (m_compiled) {
239 #ifndef QT_NO_DEBUG
240         for (int i = 0; i < attrCount; ++i) {
241 #else
242         for (int i = 0; attr[i]; ++i) {
243 #endif
244             if (*attr[i])
245                 program()->bindAttributeLocation(attr[i], i);
246         }
247         m_compiled = program()->link();
248         m_log += program()->log();
249     }
250
251     static const char *fallbackVertexShader =
252             "uniform highp mat4 qt_Matrix;"
253             "attribute highp vec4 v;"
254             "void main() { gl_Position = qt_Matrix * v; }";
255     static const char *fallbackFragmentShader =
256             "void main() { gl_FragColor = vec4(1., 0., 1., 1.); }";
257
258     if (!m_compiled) {
259         qWarning("QQuickCustomMaterialShader: Shader compilation failed:");
260         qWarning() << program()->log();
261
262         program()->removeAllShaders();
263         program()->addShaderFromSourceCode(QOpenGLShader::Vertex, fallbackVertexShader);
264         program()->addShaderFromSourceCode(QOpenGLShader::Fragment, fallbackFragmentShader);
265 #ifndef QT_NO_DEBUG
266         for (int i = 0; i < attrCount; ++i) {
267 #else
268         for (int i = 0; attr[i]; ++i) {
269 #endif
270             if (qstrcmp(attr[i], qtPositionAttributeName()) == 0)
271                 program()->bindAttributeLocation("v", i);
272         }
273         program()->link();
274     }
275 }
276
277 void QQuickCustomMaterialShader::initialize()
278 {
279     m_opacityLoc = program()->uniformLocation("qt_Opacity");
280     m_matrixLoc = program()->uniformLocation("qt_Matrix");
281 }
282
283 const char *QQuickCustomMaterialShader::vertexShader() const
284 {
285     return m_key.vertexCode.constData();
286 }
287
288 const char *QQuickCustomMaterialShader::fragmentShader() const
289 {
290     return m_key.fragmentCode.constData();
291 }
292
293
294 bool QQuickShaderEffectMaterialKey::operator == (const QQuickShaderEffectMaterialKey &other) const
295 {
296     return vertexCode == other.vertexCode && fragmentCode == other.fragmentCode && className == other.className;
297 }
298
299 uint qHash(const QQuickShaderEffectMaterialKey &key)
300 {
301     return qHash(qMakePair(qMakePair(key.vertexCode, key.fragmentCode), key.className));
302 }
303
304
305 QHash<QQuickShaderEffectMaterialKey, QSharedPointer<QSGMaterialType> > QQuickShaderEffectMaterial::materialMap;
306
307 QQuickShaderEffectMaterial::QQuickShaderEffectMaterial(QQuickShaderEffectNode *node)
308     : m_cullMode(NoCulling)
309     , m_node(node)
310     , m_emittedLogChanged(false)
311 {
312     setFlag(Blending | RequiresFullMatrix, true);
313 }
314
315 QSGMaterialType *QQuickShaderEffectMaterial::type() const
316 {
317     return m_type.data();
318 }
319
320 QSGMaterialShader *QQuickShaderEffectMaterial::createShader() const
321 {
322     return new QQuickCustomMaterialShader(m_source, m_source.attributeNames);
323 }
324
325 int QQuickShaderEffectMaterial::compare(const QSGMaterial *other) const
326 {
327     return this - static_cast<const QQuickShaderEffectMaterial *>(other);
328 }
329
330 void QQuickShaderEffectMaterial::setCullMode(QQuickShaderEffectMaterial::CullMode face)
331 {
332     m_cullMode = face;
333 }
334
335 QQuickShaderEffectMaterial::CullMode QQuickShaderEffectMaterial::cullMode() const
336 {
337     return m_cullMode;
338 }
339
340 void QQuickShaderEffectMaterial::setProgramSource(const QQuickShaderEffectProgram &source)
341 {
342     m_source = source;
343     m_emittedLogChanged = false;
344     m_type = materialMap.value(m_source);
345     if (m_type.isNull()) {
346         m_type = QSharedPointer<QSGMaterialType>(new QSGMaterialType);
347         materialMap.insert(m_source, m_type);
348     }
349 }
350
351 void QQuickShaderEffectMaterial::setUniforms(const QVector<QPair<QByteArray, QVariant> > &uniformValues)
352 {
353     m_uniformValues = uniformValues;
354 }
355
356 void QQuickShaderEffectMaterial::setTextureProviders(const QVector<QPair<QByteArray, QSGTextureProvider *> > &textures)
357 {
358     m_textures = textures;
359 }
360
361 const QVector<QPair<QByteArray, QSGTextureProvider *> > &QQuickShaderEffectMaterial::textureProviders() const
362 {
363     return m_textures;
364 }
365
366 void QQuickShaderEffectMaterial::updateTextures() const
367 {
368     for (int i = 0; i < m_textures.size(); ++i) {
369         if (QSGTextureProvider *provider = m_textures.at(i).second) {
370             if (QSGDynamicTexture *texture = qobject_cast<QSGDynamicTexture *>(provider->texture()))
371                 texture->updateTexture();
372         }
373     }
374 }
375
376
377 QQuickShaderEffectNode::QQuickShaderEffectNode()
378     : m_material(this)
379 {
380     QSGNode::setFlag(UsePreprocess, true);
381     setMaterial(&m_material);
382
383 #ifdef QML_RUNTIME_TESTING
384     description = QLatin1String("shadereffect");
385 #endif
386 }
387
388 QQuickShaderEffectNode::~QQuickShaderEffectNode()
389 {
390 }
391
392 void QQuickShaderEffectNode::markDirtyTexture()
393 {
394     markDirty(DirtyMaterial);
395 }
396
397 void QQuickShaderEffectNode::preprocess()
398 {
399     Q_ASSERT(material());
400     static_cast<QQuickShaderEffectMaterial *>(material())->updateTextures();
401 }
402
403 QT_END_NAMESPACE