1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
42 #include <private/qquickshadereffect_p.h>
43 #include <private/qquickshadereffectnode_p.h>
45 #include <QtQuick/qsgmaterial.h>
46 #include "qquickitem_p.h"
48 #include <QtQuick/private/qsgcontext_p.h>
49 #include <QtQuick/qsgtextureprovider.h>
50 #include "qquickcanvas.h"
52 #include "qquickimage_p.h"
53 #include "qquickshadereffectsource_p.h"
55 #include <QtCore/qsignalmapper.h>
56 #include <QtGui/qopenglframebufferobject.h>
60 static const char qt_default_vertex_code[] =
61 "uniform highp mat4 qt_Matrix; \n"
62 "attribute highp vec4 qt_Vertex; \n"
63 "attribute highp vec2 qt_MultiTexCoord0; \n"
64 "varying highp vec2 qt_TexCoord0; \n"
66 " qt_TexCoord0 = qt_MultiTexCoord0; \n"
67 " gl_Position = qt_Matrix * qt_Vertex; \n"
70 static const char qt_default_fragment_code[] =
71 "varying highp vec2 qt_TexCoord0; \n"
72 "uniform sampler2D source; \n"
73 "uniform lowp float qt_Opacity; \n"
75 " gl_FragColor = texture2D(source, qt_TexCoord0) * qt_Opacity; \n"
78 static const char qt_position_attribute_name[] = "qt_Vertex";
79 static const char qt_texcoord_attribute_name[] = "qt_MultiTexCoord0";
81 const char *qtPositionAttributeName()
83 return qt_position_attribute_name;
86 const char *qtTexCoordAttributeName()
88 return qt_texcoord_attribute_name;
93 enum VariableQualifier {
98 inline bool qt_isalpha(char c)
101 return (ch >= 'a' && ch <= 'z') || c == '_';
104 inline bool qt_isalnum(char c)
106 return qt_isalpha(c) || (c >= '0' && c <= '9');
109 inline bool qt_isspace(char c)
111 return c == ' ' || (c >= 0x09 && c <= 0x0d);
114 // Returns -1 if not found, returns index to first character after the name if found.
115 int qt_search_for_variable(const char *s, int length, int index, VariableQualifier &decl,
116 int &typeIndex, int &typeLength,
117 int &nameIndex, int &nameLength)
120 QualifierIdentifier, // Base state
125 Identifier expected = QualifierIdentifier;
126 bool compilerDirectiveExpected = index == 0;
128 while (index < length) {
130 while (qt_isspace(s[index])) {
131 compilerDirectiveExpected |= s[index] == '\n';
135 if (qt_isalpha(s[index])) {
139 while (qt_isalnum(s[index]))
141 int idLength = index - idIndex;
143 const int attrLen = sizeof("attribute") - 1;
144 const int uniLen = sizeof("uniform") - 1;
145 const int loLen = sizeof("lowp") - 1;
146 const int medLen = sizeof("mediump") - 1;
147 const int hiLen = sizeof("highp") - 1;
150 case QualifierIdentifier:
151 if (idLength == attrLen && qstrncmp("attribute", s + idIndex, attrLen) == 0) {
152 decl = AttributeQualifier;
153 expected = PrecisionIdentifier;
154 } else if (idLength == uniLen && qstrncmp("uniform", s + idIndex, uniLen) == 0) {
155 decl = UniformQualifier;
156 expected = PrecisionIdentifier;
159 case PrecisionIdentifier:
160 if ((idLength == loLen && qstrncmp("lowp", s + idIndex, loLen) == 0)
161 || (idLength == medLen && qstrncmp("mediump", s + idIndex, medLen) == 0)
162 || (idLength == hiLen && qstrncmp("highp", s + idIndex, hiLen) == 0))
164 expected = TypeIdentifier;
170 typeLength = idLength;
171 expected = NameIdentifier;
175 nameLength = idLength;
176 return index; // Attribute or uniform declaration found. Return result.
180 } else if (s[index] == '#' && compilerDirectiveExpected) {
181 // Skip compiler directives.
183 while (index < length && (s[index] != '\n' || s[index - 1] == '\\'))
185 } else if (s[index] == '/' && s[index + 1] == '/') {
188 while (index < length && s[index] != '\n')
190 } else if (s[index] == '/' && s[index + 1] == '*') {
193 while (index < length && (s[index] != '*' || s[index + 1] != '/'))
196 index += 2; // Skip star-slash.
198 expected = QualifierIdentifier;
201 compilerDirectiveExpected = false;
209 QQuickShaderEffectCommon::~QQuickShaderEffectCommon()
211 for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType)
212 qDeleteAll(signalMappers[shaderType]);
215 void QQuickShaderEffectCommon::disconnectPropertySignals(QQuickItem *item, Key::ShaderType shaderType)
217 for (int i = 0; i < uniformData[shaderType].size(); ++i) {
218 if (signalMappers[shaderType].at(i) == 0)
220 const UniformData &d = uniformData[shaderType].at(i);
221 QSignalMapper *mapper = signalMappers[shaderType].at(i);
222 QObject::disconnect(item, 0, mapper, SLOT(map()));
223 QObject::disconnect(mapper, SIGNAL(mapped(int)), item, SLOT(propertyChanged(int)));
224 if (d.specialType == UniformData::Sampler) {
225 QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
228 QQuickItemPrivate::get(source)->derefCanvas();
229 QObject::disconnect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*)));
235 void QQuickShaderEffectCommon::connectPropertySignals(QQuickItem *item, Key::ShaderType shaderType)
237 for (int i = 0; i < uniformData[shaderType].size(); ++i) {
238 if (signalMappers[shaderType].at(i) == 0)
240 const UniformData &d = uniformData[shaderType].at(i);
241 int pi = item->metaObject()->indexOfProperty(d.name.constData());
243 QMetaProperty mp = item->metaObject()->property(pi);
244 if (!mp.hasNotifySignal())
245 qWarning("QQuickShaderEffect: property '%s' does not have notification method!", d.name.constData());
246 const QByteArray signalName = '2' + mp.notifySignal().methodSignature();
247 QSignalMapper *mapper = signalMappers[shaderType].at(i);
248 QObject::connect(item, signalName, mapper, SLOT(map()));
249 QObject::connect(mapper, SIGNAL(mapped(int)), item, SLOT(propertyChanged(int)));
251 // If the source is set via a dynamic property, like the layer is, then we need this
252 // check to disable the warning.
253 if (!item->property(d.name.constData()).isValid())
254 qWarning("QQuickShaderEffect: '%s' does not have a matching property!", d.name.constData());
257 if (d.specialType == UniformData::Sampler) {
258 QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
261 QQuickItemPrivate::get(source)->refCanvas(item->canvas());
262 QObject::connect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*)));
268 void QQuickShaderEffectCommon::updateParseLog(bool ignoreAttributes)
271 if (!ignoreAttributes) {
272 if (!attributes.contains(qt_position_attribute_name)) {
273 parseLog += QLatin1String("Warning: Missing reference to \'");
274 parseLog += QLatin1String(qt_position_attribute_name);
275 parseLog += QLatin1String("\'.\n");
277 if (!attributes.contains(qt_texcoord_attribute_name)) {
278 parseLog += QLatin1String("Warning: Missing reference to \'");
279 parseLog += QLatin1String(qt_texcoord_attribute_name);
280 parseLog += QLatin1String("\'.\n");
283 bool respectsMatrix = false;
284 bool respectsOpacity = false;
285 for (int i = 0; i < uniformData[Key::VertexShader].size(); ++i)
286 respectsMatrix |= uniformData[Key::VertexShader].at(i).specialType == UniformData::Matrix;
287 for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
288 for (int i = 0; i < uniformData[shaderType].size(); ++i)
289 respectsOpacity |= uniformData[shaderType].at(i).specialType == UniformData::Opacity;
292 parseLog += QLatin1String("Warning: Vertex shader is missing reference to \'qt_Matrix\'.\n");
293 if (!respectsOpacity)
294 parseLog += QLatin1String("Warning: Shaders are missing reference to \'qt_Opacity\'.\n");
297 void QQuickShaderEffectCommon::lookThroughShaderCode(QQuickItem *item, Key::ShaderType shaderType, const QByteArray &code)
304 const char *s = code.constData();
305 VariableQualifier decl = AttributeQualifier;
306 while ((index = qt_search_for_variable(s, code.size(), index, decl, typeIndex, typeLength,
307 nameIndex, nameLength)) != -1)
309 if (decl == AttributeQualifier) {
310 if (shaderType == Key::VertexShader)
311 attributes.append(QByteArray(s + nameIndex, nameLength));
313 Q_ASSERT(decl == UniformQualifier);
315 const int sampLen = sizeof("sampler2D") - 1;
316 const int opLen = sizeof("qt_Opacity") - 1;
317 const int matLen = sizeof("qt_Matrix") - 1;
320 QSignalMapper *mapper = 0;
321 d.name = QByteArray(s + nameIndex, nameLength);
322 if (nameLength == opLen && qstrncmp("qt_Opacity", s + nameIndex, opLen) == 0) {
323 d.specialType = UniformData::Opacity;
324 } else if (nameLength == matLen && qstrncmp("qt_Matrix", s + nameIndex, matLen) == 0) {
325 d.specialType = UniformData::Matrix;
327 mapper = new QSignalMapper;
328 mapper->setMapping(item, uniformData[shaderType].size() | (shaderType << 16));
329 d.value = item->property(d.name.constData());
330 bool sampler = typeLength == sampLen && qstrncmp("sampler2D", s + typeIndex, sampLen) == 0;
331 d.specialType = sampler ? UniformData::Sampler : UniformData::None;
333 uniformData[shaderType].append(d);
334 signalMappers[shaderType].append(mapper);
339 void QQuickShaderEffectCommon::updateShader(QQuickItem *item, Key::ShaderType shaderType)
341 disconnectPropertySignals(item, shaderType);
342 qDeleteAll(signalMappers[shaderType]);
343 uniformData[shaderType].clear();
344 signalMappers[shaderType].clear();
345 if (shaderType == Key::VertexShader)
348 const QByteArray &code = source.sourceCode[shaderType];
349 if (code.isEmpty()) {
350 // Optimize for default code.
351 if (shaderType == Key::VertexShader) {
352 attributes.append(QByteArray(qt_position_attribute_name));
353 attributes.append(QByteArray(qt_texcoord_attribute_name));
355 d.name = "qt_Matrix";
356 d.specialType = UniformData::Matrix;
357 uniformData[Key::VertexShader].append(d);
358 signalMappers[Key::VertexShader].append(0);
359 } else if (shaderType == Key::FragmentShader) {
361 d.name = "qt_Opacity";
362 d.specialType = UniformData::Opacity;
363 uniformData[Key::FragmentShader].append(d);
364 signalMappers[Key::FragmentShader].append(0);
365 QSignalMapper *mapper = new QSignalMapper;
366 mapper->setMapping(item, 1 | (Key::FragmentShader << 16));
367 const char *sourceName = "source";
369 d.value = item->property(sourceName);
370 d.specialType = UniformData::Sampler;
371 uniformData[Key::FragmentShader].append(d);
372 signalMappers[Key::FragmentShader].append(mapper);
375 lookThroughShaderCode(item, shaderType, code);
378 connectPropertySignals(item, shaderType);
381 void QQuickShaderEffectCommon::updateMaterial(QQuickShaderEffectNode *node,
382 QQuickShaderEffectMaterial *material,
383 bool updateUniforms, bool updateUniformValues,
384 bool updateTextureProviders)
386 if (updateUniforms) {
387 for (int i = 0; i < material->textureProviders.size(); ++i) {
388 QSGTextureProvider *t = material->textureProviders.at(i).second;
390 QObject::disconnect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
391 QObject::disconnect(t, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*)));
394 material->textureProviders.clear();
396 for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
397 for (int i = 0; i < uniformData[shaderType].size(); ++i) {
398 const UniformData &d = uniformData[shaderType].at(i);
399 // First make room in the textureProviders array. Set to proper value further down.
400 if (d.specialType == UniformData::Sampler)
401 material->textureProviders.append(qMakePair(d.name, (QSGTextureProvider *)0));
403 material->uniforms[shaderType] = uniformData[shaderType];
405 updateUniformValues = false;
406 updateTextureProviders = true;
409 if (updateUniformValues) {
410 for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
411 Q_ASSERT(uniformData[shaderType].size() == material->uniforms[shaderType].size());
412 for (int i = 0; i < uniformData[shaderType].size(); ++i)
413 material->uniforms[shaderType][i].value = uniformData[shaderType].at(i).value;
417 if (updateTextureProviders) {
419 for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
420 for (int i = 0; i < uniformData[shaderType].size(); ++i) {
421 const UniformData &d = uniformData[shaderType].at(i);
422 if (d.specialType != UniformData::Sampler)
424 Q_ASSERT(material->textureProviders.at(index).first == d.name);
425 QSGTextureProvider *oldProvider = material->textureProviders.at(index).second;
426 QSGTextureProvider *newProvider = 0;
427 QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
428 if (source && source->isTextureProvider())
429 newProvider = source->textureProvider();
430 if (newProvider != oldProvider) {
432 QObject::disconnect(oldProvider, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
433 QObject::disconnect(oldProvider, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*)));
436 Q_ASSERT_X(newProvider->thread() == QThread::currentThread(),
437 "QQuickShaderEffect::updatePaintNode",
438 "Texture provider must belong to the rendering thread");
439 QObject::connect(newProvider, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
440 QObject::connect(newProvider, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*)));
442 const char *typeName = source ? source->metaObject()->className() : d.value.typeName();
443 qWarning("ShaderEffect: Property '%s' is not assigned a valid texture provider (%s).",
444 d.name.constData(), typeName);
446 material->textureProviders[index].second = newProvider;
451 Q_ASSERT(index == material->textureProviders.size());
455 void QQuickShaderEffectCommon::updateCanvas(QQuickCanvas *canvas)
457 // See comment in QQuickShaderEffectCommon::propertyChanged().
459 for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
460 for (int i = 0; i < uniformData[shaderType].size(); ++i) {
461 const UniformData &d = uniformData[shaderType].at(i);
462 if (d.specialType == UniformData::Sampler) {
463 QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
465 QQuickItemPrivate::get(source)->refCanvas(canvas);
470 for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
471 for (int i = 0; i < uniformData[shaderType].size(); ++i) {
472 const UniformData &d = uniformData[shaderType].at(i);
473 if (d.specialType == UniformData::Sampler) {
474 QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
476 QQuickItemPrivate::get(source)->derefCanvas();
483 void QQuickShaderEffectCommon::sourceDestroyed(QObject *object)
485 for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
486 for (int i = 0; i < uniformData[shaderType].size(); ++i) {
487 UniformData &d = uniformData[shaderType][i];
488 if (d.specialType == UniformData::Sampler && d.value.canConvert<QObject *>()) {
489 if (qvariant_cast<QObject *>(d.value) == object)
490 d.value = QVariant();
497 void QQuickShaderEffectCommon::propertyChanged(QQuickItem *item, int mappedId,
498 bool *textureProviderChanged)
500 Key::ShaderType shaderType = Key::ShaderType(mappedId >> 16);
501 int index = mappedId & 0xffff;
502 UniformData &d = uniformData[shaderType][index];
503 if (d.specialType == UniformData::Sampler) {
504 QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
507 QQuickItemPrivate::get(source)->derefCanvas();
508 QObject::disconnect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*)));
511 d.value = item->property(d.name.constData());
513 source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
515 // 'source' needs a canvas to get a scene graph node. It usually gets one through its
516 // parent, but if the source item is "inline" rather than a reference -- i.e.
517 // "property variant source: Image { }" instead of "property variant source: foo" -- it
518 // will not get a parent. In those cases, 'source' should get the canvas from 'item'.
520 QQuickItemPrivate::get(source)->refCanvas(item->canvas());
521 QObject::connect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*)));
523 if (textureProviderChanged)
524 *textureProviderChanged = true;
526 d.value = item->property(d.name.constData());
527 if (textureProviderChanged)
528 *textureProviderChanged = false;
534 \qmlclass ShaderEffect QQuickShaderEffect
535 \inqmlmodule QtQuick 2
537 \ingroup qtquick-shaders
538 \brief Applies custom shaders to a rectangle
540 The ShaderEffect element applies a custom OpenGL
541 \l{vertexShader}{vertex} and \l{fragmentShader}{fragment} shader to a
542 rectangle. It allows you to write effects such as drop shadow, blur,
543 colorize and page curl directly in QML.
545 There are two types of input to the \l vertexShader:
546 uniform variables and attributes. Some are predefined:
548 \li uniform mat4 qt_Matrix - combined transformation
549 matrix, the product of the matrices from the root item to this
550 ShaderEffect, and an orthogonal projection.
551 \li uniform float qt_Opacity - combined opacity, the product of the
552 opacities from the root item to this ShaderEffect.
553 \li attribute vec4 qt_Vertex - vertex position, the top-left vertex has
554 position (0, 0), the bottom-right (\l{Item::width}{width},
555 \l{Item::height}{height}).
556 \li attribute vec2 qt_MultiTexCoord0 - texture coordinate, the top-left
557 coordinate is (0, 0), the bottom-right (1, 1).
560 In addition, any property that can be mapped to an OpenGL Shading Language
561 (GLSL) type is available as a uniform variable. The following list shows
562 how properties are mapped to GLSL uniform variables:
564 \li bool, int, qreal -> bool, int, float - If the type in the shader is not
565 the same as in QML, the value is converted automatically.
566 \li QColor -> vec4 - When colors are passed to the shader, they are first
567 premultiplied. Thus Qt.rgba(0.2, 0.6, 1.0, 0.5) becomes
568 vec4(0.1, 0.3, 0.5, 0.5) in the shader, for example.
569 \li QRect, QRectF -> vec4 - Qt.rect(x, y, w, h) becomes vec4(x, y, w, h) in
571 \li QPoint, QPointF, QSize, QSizeF -> vec2
572 \li QVector3D -> vec3
573 \li QTransform -> mat4
574 \li \l Image, \l ShaderEffectSource -> sampler2D - Origin is in the top-left
575 corner, and the color values are premultiplied.
578 The output from the \l fragmentShader should be premultiplied. If
579 \l blending is enabled, source-over blending is used. However, additive
580 blending can be achieved by outputting zero in the alpha channel.
583 \li \image declarative-shadereffectitem.png
588 width: 200; height: 100
590 Image { id: img; sourceSize { width: 100; height: 100 } source: "qt-logo.png" }
592 width: 100; height: 100
593 property variant src: img
595 uniform highp mat4 qt_Matrix;
596 attribute highp vec4 qt_Vertex;
597 attribute highp vec2 qt_MultiTexCoord0;
598 varying highp vec2 coord;
600 coord = qt_MultiTexCoord0;
601 gl_Position = qt_Matrix * qt_Vertex;
604 varying highp vec2 coord;
605 uniform sampler2D src;
606 uniform lowp float qt_Opacity;
608 lowp vec4 tex = texture2D(src, coord);
609 gl_FragColor = vec4(vec3(dot(tex.rgb, vec3(0.344, 0.5, 0.156))), tex.a) * qt_Opacity;
617 By default, the ShaderEffect consists of four vertices, one for each
618 corner. For non-linear vertex transformations, like page curl, you can
619 specify a fine grid of vertices by specifying a \l mesh resolution.
621 \note Scene Graph textures have origin in the top-left corner rather than
622 bottom-left which is common in OpenGL.
625 QQuickShaderEffect::QQuickShaderEffect(QQuickItem *parent)
627 , m_meshResolution(1, 1)
629 , m_cullMode(NoCulling)
630 , m_status(Uncompiled)
632 , m_dirtyUniforms(true)
633 , m_dirtyUniformValues(true)
634 , m_dirtyTextureProviders(true)
635 , m_dirtyProgram(true)
636 , m_dirtyParseLog(true)
638 , m_dirtyGeometry(true)
640 setFlag(QQuickItem::ItemHasContents);
643 QQuickShaderEffect::~QQuickShaderEffect()
645 for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType)
646 m_common.disconnectPropertySignals(this, Key::ShaderType(shaderType));
650 \qmlproperty string QtQuick2::ShaderEffect::fragmentShader
652 This property holds the fragment shader's GLSL source code.
653 The default shader passes the texture coordinate along to the fragment
654 shader as "varying highp vec2 qt_TexCoord0".
657 void QQuickShaderEffect::setFragmentShader(const QByteArray &code)
659 if (m_common.source.sourceCode[Key::FragmentShader].constData() == code.constData())
661 m_common.source.sourceCode[Key::FragmentShader] = code;
662 m_dirtyProgram = true;
663 m_dirtyParseLog = true;
665 if (isComponentComplete())
666 m_common.updateShader(this, Key::FragmentShader);
669 if (m_status != Uncompiled) {
670 m_status = Uncompiled;
671 emit statusChanged();
673 emit fragmentShaderChanged();
677 \qmlproperty string QtQuick2::ShaderEffect::vertexShader
679 This property holds the vertex shader's GLSL source code.
680 The default shader expects the texture coordinate to be passed from the
681 vertex shader as "varying highp vec2 qt_TexCoord0", and it samples from a
682 sampler2D named "source".
685 void QQuickShaderEffect::setVertexShader(const QByteArray &code)
687 if (m_common.source.sourceCode[Key::VertexShader].constData() == code.constData())
689 m_common.source.sourceCode[Key::VertexShader] = code;
690 m_dirtyProgram = true;
691 m_dirtyParseLog = true;
693 if (isComponentComplete())
694 m_common.updateShader(this, Key::VertexShader);
697 if (m_status != Uncompiled) {
698 m_status = Uncompiled;
699 emit statusChanged();
701 emit vertexShaderChanged();
705 \qmlproperty bool QtQuick2::ShaderEffect::blending
707 If this property is true, the output from the \l fragmentShader is blended
708 with the background using source-over blend mode. If false, the background
709 is disregarded. Blending decreases the performance, so you should set this
710 property to false when blending is not needed. The default value is true.
713 void QQuickShaderEffect::setBlending(bool enable)
715 if (blending() == enable)
721 emit blendingChanged();
725 \qmlproperty variant QtQuick2::ShaderEffect::mesh
727 This property defines the mesh used to draw the ShaderEffect. It can hold
728 any mesh object deriving from \l QQuickShaderEffectMesh, such as \l GridMesh.
729 If a size value is assigned to this property, the ShaderEffect implicitly
730 uses a \l GridMesh with the value as
731 \l{GridMesh::resolution}{mesh resolution}. By default, this property is
737 QVariant QQuickShaderEffect::mesh() const
739 return m_mesh ? qVariantFromValue(static_cast<QObject *>(m_mesh))
740 : qVariantFromValue(m_meshResolution);
743 void QQuickShaderEffect::setMesh(const QVariant &mesh)
745 QQuickShaderEffectMesh *newMesh = qobject_cast<QQuickShaderEffectMesh *>(qvariant_cast<QObject *>(mesh));
746 if (newMesh && newMesh == m_mesh)
749 disconnect(m_mesh, SIGNAL(geometryChanged()), this, 0);
752 connect(m_mesh, SIGNAL(geometryChanged()), this, SLOT(updateGeometry()));
754 if (mesh.canConvert<QSize>()) {
755 m_meshResolution = mesh.toSize();
757 QList<QByteArray> res = mesh.toByteArray().split('x');
758 bool ok = res.size() == 2;
760 int w = res.at(0).toInt(&ok);
762 int h = res.at(1).toInt(&ok);
764 m_meshResolution = QSize(w, h);
768 qWarning("ShaderEffect: mesh property must be size or object deriving from QQuickShaderEffectMesh.");
770 m_defaultMesh.setResolution(m_meshResolution);
774 m_dirtyParseLog = true;
780 \qmlproperty enumeration QtQuick2::ShaderEffect::cullMode
782 This property defines which sides of the element should be visible.
785 \li ShaderEffect.NoCulling - Both sides are visible
786 \li ShaderEffect.BackFaceCulling - only front side is visible
787 \li ShaderEffect.FrontFaceCulling - only back side is visible
790 The default is NoCulling.
793 void QQuickShaderEffect::setCullMode(CullMode face)
795 if (face == m_cullMode)
799 emit cullModeChanged();
802 QString QQuickShaderEffect::parseLog()
804 if (m_dirtyParseLog) {
805 m_common.updateParseLog(m_mesh != 0);
806 m_dirtyParseLog = false;
808 return m_common.parseLog;
811 bool QQuickShaderEffect::event(QEvent *event)
813 if (event->type() == QEvent::DynamicPropertyChange) {
814 QDynamicPropertyChangeEvent *e = static_cast<QDynamicPropertyChangeEvent *>(event);
815 for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
816 for (int i = 0; i < m_common.uniformData[shaderType].size(); ++i) {
817 if (m_common.uniformData[shaderType].at(i).name == e->propertyName()) {
818 bool textureProviderChanged;
819 m_common.propertyChanged(this, (shaderType << 16) | i, &textureProviderChanged);
820 m_dirtyTextureProviders |= textureProviderChanged;
821 m_dirtyUniformValues = true;
827 return QQuickItem::event(event);
831 \qmlproperty enumeration QtQuick2::ShaderEffect::status
833 This property tells the current status of the OpenGL shader program.
836 \li ShaderEffect.Compiled - the shader program was successfully compiled and linked.
837 \li ShaderEffect.Uncompiled - the shader program has not yet been compiled.
838 \li ShaderEffect.Error - the shader program failed to compile or link.
841 When setting the fragment or vertex shader source code, the status will become Uncompiled.
842 The first time the ShaderEffect is rendered with new shader source code, the shaders are
843 compiled and linked, and the status is updated to Compiled or Error.
849 \qmlproperty string QtQuick2::ShaderEffect::log
851 This property holds a log of warnings and errors from the latest attempt at compiling and
852 linking the OpenGL shader program. It is updated at the same time \l status is set to Compiled
858 void QQuickShaderEffect::updateGeometry()
860 m_dirtyGeometry = true;
864 void QQuickShaderEffect::updateLogAndStatus(const QString &log, int status)
866 m_log = parseLog() + log;
867 m_status = Status(status);
869 emit statusChanged();
872 void QQuickShaderEffect::sourceDestroyed(QObject *object)
874 m_common.sourceDestroyed(object);
878 void QQuickShaderEffect::propertyChanged(int mappedId)
880 bool textureProviderChanged;
881 m_common.propertyChanged(this, mappedId, &textureProviderChanged);
882 m_dirtyTextureProviders |= textureProviderChanged;
883 m_dirtyUniformValues = true;
887 void QQuickShaderEffect::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
889 m_dirtyGeometry = true;
890 QQuickItem::geometryChanged(newGeometry, oldGeometry);
893 QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
895 QQuickShaderEffectNode *node = static_cast<QQuickShaderEffectNode *>(oldNode);
897 // In the case of a bad vertex shader, don't try to create a node...
898 if (m_common.attributes.isEmpty()) {
905 node = new QQuickShaderEffectNode;
906 node->setMaterial(new QQuickShaderEffectMaterial(node));
907 node->setFlag(QSGNode::OwnsMaterial, true);
908 m_dirtyProgram = true;
909 m_dirtyUniforms = true;
910 m_dirtyGeometry = true;
911 connect(node, SIGNAL(logAndStatusChanged(QString,int)), this, SLOT(updateLogAndStatus(QString,int)));
915 node->setGeometry(0);
917 m_dirtyGeometry = true;
920 if (m_dirtyGeometry) {
921 node->setFlag(QSGNode::OwnsGeometry, false);
922 QSGGeometry *geometry = node->geometry();
923 QRectF rect(0, 0, width(), height());
924 QQuickShaderEffectMesh *mesh = m_mesh ? m_mesh : &m_defaultMesh;
926 geometry = mesh->updateGeometry(geometry, m_common.attributes, rect);
928 QString log = mesh->log();
931 m_log += QLatin1String("*** Mesh ***\n");
935 emit statusChanged();
941 node->setGeometry(geometry);
942 node->setFlag(QSGNode::OwnsGeometry, true);
944 m_dirtyGeometry = false;
947 QQuickShaderEffectMaterial *material = static_cast<QQuickShaderEffectMaterial *>(node->material());
950 if (bool(material->flags() & QSGMaterial::Blending) != m_blending) {
951 material->setFlag(QSGMaterial::Blending, m_blending);
952 node->markDirty(QSGNode::DirtyMaterial);
955 if (int(material->cullMode) != int(m_cullMode)) {
956 material->cullMode = QQuickShaderEffectMaterial::CullMode(m_cullMode);
957 node->markDirty(QSGNode::DirtyMaterial);
960 if (m_dirtyProgram) {
961 Key s = m_common.source;
962 if (s.sourceCode[Key::FragmentShader].isEmpty())
963 s.sourceCode[Key::FragmentShader] = qt_default_fragment_code;
964 if (s.sourceCode[Key::VertexShader].isEmpty())
965 s.sourceCode[Key::VertexShader] = qt_default_vertex_code;
966 s.className = metaObject()->className();
968 material->setProgramSource(s);
969 material->attributes = m_common.attributes;
970 node->markDirty(QSGNode::DirtyMaterial);
971 m_dirtyProgram = false;
972 m_dirtyUniforms = true;
975 if (m_dirtyUniforms || m_dirtyUniformValues || m_dirtyTextureProviders) {
976 m_common.updateMaterial(node, material, m_dirtyUniforms, m_dirtyUniformValues,
977 m_dirtyTextureProviders);
978 node->markDirty(QSGNode::DirtyMaterial);
979 m_dirtyUniforms = m_dirtyUniformValues = m_dirtyTextureProviders = false;
985 void QQuickShaderEffect::componentComplete()
987 m_common.updateShader(this, Key::VertexShader);
988 m_common.updateShader(this, Key::FragmentShader);
989 QQuickItem::componentComplete();
992 void QQuickShaderEffect::itemChange(ItemChange change, const ItemChangeData &value)
994 if (change == QQuickItem::ItemSceneChange)
995 m_common.updateCanvas(value.canvas);
996 QQuickItem::itemChange(change, value);