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;
92 \qmlclass ShaderEffect QQuickShaderEffect
93 \inqmlmodule QtQuick 2
94 \ingroup qml-basic-visual-elements
95 \brief The ShaderEffect element applies custom shaders to a rectangle.
98 The ShaderEffect element applies a custom OpenGL
99 \l{vertexShader}{vertex} and \l{fragmentShader}{fragment} shader to a
100 rectangle. It allows you to write effects such as drop shadow, blur,
101 colorize and page curl directly in QML.
103 There are two types of input to the \l vertexShader:
104 uniform variables and attributes. Some are predefined:
106 \o uniform mat4 qt_Matrix - combined transformation
107 matrix, the product of the matrices from the root item to this
108 ShaderEffect, and an orthogonal projection.
109 \o uniform float qt_Opacity - combined opacity, the product of the
110 opacities from the root item to this ShaderEffect.
111 \o attribute vec4 qt_Vertex - vertex position, the top-left vertex has
112 position (0, 0), the bottom-right (\l{Item::width}{width},
113 \l{Item::height}{height}).
114 \o attribute vec2 qt_MultiTexCoord0 - texture coordinate, the top-left
115 coordinate is (0, 0), the bottom-right (1, 1).
118 In addition, any property that can be mapped to an OpenGL Shading Language
119 (GLSL) type is available as a uniform variable. The following list shows
120 how properties are mapped to GLSL uniform variables:
122 \o bool, int, qreal -> bool, int, float - If the type in the shader is not
123 the same as in QML, the value is converted automatically.
124 \o QColor -> vec4 - When colors are passed to the shader, they are first
125 premultiplied. Thus Qt.rgba(0.2, 0.6, 1.0, 0.5) becomes
126 vec4(0.1, 0.3, 0.5, 0.5) in the shader, for example.
127 \o QRect, QRectF -> vec4 - Qt.rect(x, y, w, h) becomes vec4(x, y, w, h) in
129 \o QPoint, QPointF, QSize, QSizeF -> vec2
131 \o QTransform -> mat4
132 \o \l Image, \l ShaderEffectSource -> sampler2D - Origin is in the top-left
133 corner, and the color values are premultiplied.
136 The output from the \l fragmentShader should be premultiplied. If
137 \l blending is enabled, source-over blending is used. However, additive
138 blending can be achieved by outputting zero in the alpha channel.
141 \o \image declarative-shadereffectitem.png
146 width: 200; height: 100
148 Image { id: img; sourceSize { width: 100; height: 100 } source: "qt-logo.png" }
150 width: 100; height: 100
151 property variant src: img
153 uniform highp mat4 qt_Matrix;
154 attribute highp vec4 qt_Vertex;
155 attribute highp vec2 qt_MultiTexCoord0;
156 varying highp vec2 coord;
158 coord = qt_MultiTexCoord0;
159 gl_Position = qt_Matrix * qt_Vertex;
162 varying highp vec2 coord;
163 uniform sampler2D src;
164 uniform lowp float qt_Opacity;
166 lowp vec4 tex = texture2D(src, coord);
167 gl_FragColor = vec4(vec3(dot(tex.rgb, vec3(0.344, 0.5, 0.156))), tex.a) * qt_Opacity;
175 By default, the ShaderEffect consists of four vertices, one for each
176 corner. For non-linear vertex transformations, like page curl, you can
177 specify a fine grid of vertices by specifying a \l mesh resolution.
179 \note Scene Graph textures have origin in the top-left corner rather than
180 bottom-left which is common in OpenGL.
183 QQuickShaderEffect::QQuickShaderEffect(QQuickItem *parent)
185 , m_meshResolution(1, 1)
187 , m_cullMode(NoCulling)
188 , m_status(Uncompiled)
191 , m_programDirty(true)
193 , m_dirtyGeometry(true)
196 setFlag(QQuickItem::ItemHasContents);
199 QQuickShaderEffect::~QQuickShaderEffect()
205 \qmlproperty string QtQuick2::ShaderEffect::fragmentShader
207 This property holds the fragment shader's GLSL source code.
208 The default shader passes the texture coordinate along to the fragment
209 shader as "varying highp vec2 qt_TexCoord0".
212 void QQuickShaderEffect::setFragmentShader(const QByteArray &code)
214 if (m_source.fragmentCode.constData() == code.constData())
216 m_source.fragmentCode = code;
219 if (m_status != Uncompiled) {
220 m_status = Uncompiled;
221 emit statusChanged();
223 emit fragmentShaderChanged();
227 \qmlproperty string QtQuick2::ShaderEffect::vertexShader
229 This property holds the vertex shader's GLSL source code.
230 The default shader expects the texture coordinate to be passed from the
231 vertex shader as "varying highp vec2 qt_TexCoord0", and it samples from a
232 sampler2D named "source".
235 void QQuickShaderEffect::setVertexShader(const QByteArray &code)
237 if (m_source.vertexCode.constData() == code.constData())
239 m_source.vertexCode = code;
242 if (m_status != Uncompiled) {
243 m_status = Uncompiled;
244 emit statusChanged();
246 emit vertexShaderChanged();
250 \qmlproperty bool QtQuick2::ShaderEffect::blending
252 If this property is true, the output from the \l fragmentShader is blended
253 with the background using source-over blend mode. If false, the background
254 is disregarded. Blending decreases the performance, so you should set this
255 property to false when blending is not needed. The default value is true.
258 void QQuickShaderEffect::setBlending(bool enable)
260 if (blending() == enable)
266 emit blendingChanged();
270 \qmlproperty variant QtQuick2::ShaderEffect::mesh
272 This property defines the mesh used to draw the ShaderEffect. It can hold
273 any mesh object deriving from \l QQuickShaderEffectMesh, such as \l GridMesh.
274 If a size value is assigned to this property, the ShaderEffect implicitly
275 uses a \l GridMesh with the value as
276 \l{GridMesh::resolution}{mesh resolution}. By default, this property is
282 QVariant QQuickShaderEffect::mesh() const
284 return m_mesh ? qVariantFromValue(static_cast<QObject *>(m_mesh))
285 : qVariantFromValue(m_meshResolution);
288 void QQuickShaderEffect::setMesh(const QVariant &mesh)
290 QQuickShaderEffectMesh *newMesh = qobject_cast<QQuickShaderEffectMesh *>(qVariantValue<QObject *>(mesh));
291 if (newMesh && newMesh == m_mesh)
294 disconnect(m_mesh, SIGNAL(geometryChanged()), this, 0);
297 connect(m_mesh, SIGNAL(geometryChanged()), this, SLOT(updateGeometry()));
299 if (qVariantCanConvert<QSize>(mesh)) {
300 m_meshResolution = mesh.toSize();
302 QList<QByteArray> res = mesh.toByteArray().split('x');
303 bool ok = res.size() == 2;
305 int w = res.at(0).toInt(&ok);
307 int h = res.at(1).toInt(&ok);
309 m_meshResolution = QSize(w, h);
313 qWarning("ShaderEffect: mesh property must be size or object deriving from QQuickShaderEffectMesh.");
315 m_defaultMesh.setResolution(m_meshResolution);
324 \qmlproperty enumeration QtQuick2::ShaderEffect::cullMode
326 This property defines which sides of the element should be visible.
329 \o ShaderEffect.NoCulling - Both sides are visible
330 \o ShaderEffect.BackFaceCulling - only front side is visible
331 \o ShaderEffect.FrontFaceCulling - only back side is visible
334 The default is NoCulling.
337 void QQuickShaderEffect::setCullMode(CullMode face)
339 if (face == m_cullMode)
343 emit cullModeChanged();
347 \qmlproperty enumeration QtQuick2::ShaderEffect::status
349 This property tells the current status of the OpenGL shader program.
352 \o ShaderEffect.Compiled - the shader program was successfully compiled and linked.
353 \o ShaderEffect.Uncompiled - the shader program has not yet been compiled.
354 \o ShaderEffect.Error - the shader program failed to compile or link.
357 When setting the fragment or vertex shader source code, the status will become Uncompiled.
358 The first time the ShaderEffect is rendered with new shader source code, the shaders are
359 compiled and linked, and the status is updated to Compiled or Error.
365 \qmlproperty string QtQuick2::ShaderEffect::log
367 This property holds a log of warnings and errors from the latest attempt at compiling and
368 linking the OpenGL shader program. It is updated at the same time \l status is set to Compiled
374 void QQuickShaderEffect::changeSource(int index)
376 Q_ASSERT(index >= 0 && index < m_sources.size());
377 QVariant v = property(m_sources.at(index).name.constData());
381 void QQuickShaderEffect::updateData()
387 void QQuickShaderEffect::updateGeometry()
389 m_dirtyGeometry = true;
393 void QQuickShaderEffect::updateLogAndStatus(const QString &log, int status)
395 m_log = m_parseLog + log;
396 m_status = Status(status);
398 emit statusChanged();
401 void QQuickShaderEffect::setSource(const QVariant &var, int index)
403 Q_ASSERT(index >= 0 && index < m_sources.size());
405 SourceData &source = m_sources[index];
407 source.sourceObject = 0;
410 } else if (!qVariantCanConvert<QObject *>(var)) {
411 qWarning("Could not assign source of type '%s' to property '%s'.", var.typeName(), source.name.constData());
415 QObject *obj = qVariantValue<QObject *>(var);
418 QQuickItem *item = qobject_cast<QQuickItem *>(obj);
419 if (!item || !item->isTextureProvider()) {
420 qWarning("ShaderEffect: source uniform [%s] is not assigned a valid texture provider: %s [%s]",
421 source.name.constData(), qPrintable(obj->objectName()), obj->metaObject()->className());
425 source.sourceObject = item;
428 QQuickItemPrivate *d = QQuickItemPrivate::get(item);
429 // 'item' needs a canvas to get a scene graph node. It usually gets one through its
430 // parent, but if the source item is "inline" rather than a reference -- i.e.
431 // "property variant source: Image { }" instead of "property variant source: foo" -- it
432 // will not get a parent. In those cases, 'item' should get the canvas from 'this'.
433 if (!d->parentItem && canvas() && !d->canvas) {
434 QQuickItemPrivate::InitializationState initState;
436 d->initCanvas(&initState, canvas());
441 void QQuickShaderEffect::disconnectPropertySignals()
443 disconnect(this, 0, this, SLOT(updateData()));
444 for (int i = 0; i < m_sources.size(); ++i) {
445 SourceData &source = m_sources[i];
446 disconnect(this, 0, source.mapper, 0);
447 disconnect(source.mapper, 0, this, 0);
451 void QQuickShaderEffect::connectPropertySignals()
453 QSet<QByteArray>::const_iterator it;
454 for (it = m_source.uniformNames.begin(); it != m_source.uniformNames.end(); ++it) {
455 int pi = metaObject()->indexOfProperty(it->constData());
457 QMetaProperty mp = metaObject()->property(pi);
458 if (!mp.hasNotifySignal())
459 qWarning("QQuickShaderEffect: property '%s' does not have notification method!", it->constData());
460 QByteArray signalName("2");
461 signalName.append(mp.notifySignal().signature());
462 connect(this, signalName, this, SLOT(updateData()));
464 // If the source is set via a dynamic property, like the layer is, then we need this check
465 // to disable the warning.
466 if (property(it->constData()).isValid())
468 qWarning("QQuickShaderEffect: '%s' does not have a matching property!", it->constData());
471 for (int i = 0; i < m_sources.size(); ++i) {
472 SourceData &source = m_sources[i];
473 int pi = metaObject()->indexOfProperty(source.name.constData());
475 QMetaProperty mp = metaObject()->property(pi);
476 QByteArray signalName("2");
477 signalName.append(mp.notifySignal().signature());
478 connect(this, signalName, source.mapper, SLOT(map()));
479 source.mapper->setMapping(this, i);
480 connect(source.mapper, SIGNAL(mapped(int)), this, SLOT(changeSource(int)));
482 // If the source is set via a dynamic property, like the layer is, then we need this check
483 // to disable the warning.
484 if (property(source.name.constData()).isValid())
486 qWarning("QQuickShaderEffect: '%s' does not have a matching source!", source.name.constData());
492 void QQuickShaderEffect::ensureCompleted()
502 void QQuickShaderEffect::reset()
504 disconnectPropertySignals();
506 m_source.attributeNames.clear();
507 m_source.uniformNames.clear();
508 m_source.respectsOpacity = false;
509 m_source.respectsMatrix = false;
510 m_source.className = metaObject()->className();
512 for (int i = 0; i < m_sources.size(); ++i) {
513 const SourceData &source = m_sources.at(i);
514 delete source.mapper;
519 m_programDirty = true;
523 void QQuickShaderEffect::updateProperties()
525 if (m_source.vertexCode.isEmpty()) {
526 m_source.attributeNames.append(QByteArray(qt_position_attribute_name));
527 m_source.attributeNames.append(QByteArray(qt_texcoord_attribute_name));
528 m_source.respectsMatrix = true;
530 lookThroughShaderCode(m_source.vertexCode);
532 if (m_source.fragmentCode.isEmpty()) {
533 m_source.respectsOpacity = true;
534 QByteArray name("source");
535 m_source.uniformNames.insert(name);
537 d.mapper = new QSignalMapper;
542 lookThroughShaderCode(m_source.fragmentCode);
545 if (!m_mesh && !m_source.attributeNames.contains(qt_position_attribute_name)) {
546 m_parseLog += QLatin1String("Warning: Missing reference to \'");
547 m_parseLog += QLatin1String(qt_position_attribute_name);
548 m_parseLog += QLatin1String("\'.\n");
550 if (!m_mesh && !m_source.attributeNames.contains(qt_texcoord_attribute_name)) {
551 m_parseLog += QLatin1String("Warning: Missing reference to \'");
552 m_parseLog += QLatin1String(qt_texcoord_attribute_name);
553 m_parseLog += QLatin1String("\'.\n");
555 if (!m_source.respectsMatrix) {
556 m_parseLog += QLatin1String("Warning: Missing reference to \'qt_Matrix\'.\n");
558 if (!m_source.respectsOpacity) {
559 m_parseLog += QLatin1String("Warning: Missing reference to \'qt_Opacity\'.\n");
562 for (int i = 0; i < m_sources.size(); ++i) {
563 QVariant v = property(m_sources.at(i).name);
567 connectPropertySignals();
572 enum VariableQualifier {
577 inline bool qt_isalpha(char c)
580 return (ch >= 'a' && ch <= 'z') || c == '_';
583 inline bool qt_isalnum(char c)
585 return qt_isalpha(c) || (c >= '0' && c <= '9');
588 inline bool qt_isspace(char c)
590 return c == ' ' || (c >= 0x09 && c <= 0x0d);
593 // Returns -1 if not found, returns index to first character after the name if found.
594 int qt_search_for_variable(const char *s, int length, int index, VariableQualifier &decl,
595 int &typeIndex, int &typeLength,
596 int &nameIndex, int &nameLength)
599 QualifierIdentifier, // Base state
604 Identifier expected = QualifierIdentifier;
605 bool compilerDirectiveExpected = index == 0;
607 while (index < length) {
609 while (qt_isspace(s[index])) {
610 compilerDirectiveExpected |= s[index] == '\n';
614 if (qt_isalpha(s[index])) {
618 while (qt_isalnum(s[index]))
620 int idLength = index - idIndex;
622 const int attrLen = sizeof("attribute") - 1;
623 const int uniLen = sizeof("uniform") - 1;
624 const int loLen = sizeof("lowp") - 1;
625 const int medLen = sizeof("mediump") - 1;
626 const int hiLen = sizeof("highp") - 1;
629 case QualifierIdentifier:
630 if (idLength == attrLen && qstrncmp("attribute", s + idIndex, attrLen) == 0) {
631 decl = AttributeQualifier;
632 expected = PrecisionIdentifier;
633 } else if (idLength == uniLen && qstrncmp("uniform", s + idIndex, uniLen) == 0) {
634 decl = UniformQualifier;
635 expected = PrecisionIdentifier;
638 case PrecisionIdentifier:
639 if ((idLength == loLen && qstrncmp("lowp", s + idIndex, loLen) == 0)
640 || (idLength == medLen && qstrncmp("mediump", s + idIndex, medLen) == 0)
641 || (idLength == hiLen && qstrncmp("highp", s + idIndex, hiLen) == 0))
643 expected = TypeIdentifier;
649 typeLength = idLength;
650 expected = NameIdentifier;
654 nameLength = idLength;
655 return index; // Attribute or uniform declaration found. Return result.
659 } else if (s[index] == '#' && compilerDirectiveExpected) {
660 // Skip compiler directives.
662 while (index < length && (s[index] != '\n' || s[index - 1] == '\\'))
664 } else if (s[index] == '/' && s[index + 1] == '/') {
667 while (index < length && s[index] != '\n')
669 } else if (s[index] == '/' && s[index + 1] == '*') {
672 while (index < length && (s[index] != '*' || s[index + 1] != '/'))
675 index += 2; // Skip star-slash.
677 expected = QualifierIdentifier;
680 compilerDirectiveExpected = false;
686 void QQuickShaderEffect::lookThroughShaderCode(const QByteArray &code)
689 int typeIndex, typeLength, nameIndex, nameLength;
690 const char *s = code.constData();
691 VariableQualifier decl;
692 while ((index = qt_search_for_variable(s, code.size(), index, decl, typeIndex, typeLength,
693 nameIndex, nameLength)) != -1)
695 if (decl == AttributeQualifier) {
696 m_source.attributeNames.append(QByteArray(s + nameIndex, nameLength));
698 Q_ASSERT(decl == UniformQualifier);
700 const int matLen = sizeof("qt_Matrix") - 1;
701 const int opLen = sizeof("qt_Opacity") - 1;
702 const int sampLen = sizeof("sampler2D") - 1;
704 if (nameLength == matLen && qstrncmp("qt_Matrix", s + nameIndex, matLen) == 0) {
705 m_source.respectsMatrix = true;
706 } else if (nameLength == opLen && qstrncmp("qt_Opacity", s + nameIndex, opLen) == 0) {
707 m_source.respectsOpacity = true;
709 QByteArray name(s + nameIndex, nameLength);
710 m_source.uniformNames.insert(name);
711 if (typeLength == sampLen && qstrncmp("sampler2D", s + typeIndex, sampLen) == 0) {
713 d.mapper = new QSignalMapper;
723 void QQuickShaderEffect::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
725 m_dirtyGeometry = true;
726 QQuickItem::geometryChanged(newGeometry, oldGeometry);
729 QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
731 QQuickShaderEffectNode *node = static_cast<QQuickShaderEffectNode *>(oldNode);
735 // In the case of a bad vertex shader, don't try to create a node...
736 if (m_source.attributeNames.isEmpty()) {
743 node = new QQuickShaderEffectNode;
744 m_programDirty = true;
746 m_dirtyGeometry = true;
747 connect(node, SIGNAL(logAndStatusChanged(QString,int)), this, SLOT(updateLogAndStatus(QString,int)));
750 QQuickShaderEffectMaterial *material = node->shaderMaterial();
753 node->setGeometry(0);
755 m_dirtyGeometry = true;
758 if (m_dirtyGeometry) {
759 node->setFlag(QSGNode::OwnsGeometry, false);
760 QSGGeometry *geometry = node->geometry();
761 QRectF rect(0, 0, width(), height());
762 QQuickShaderEffectMesh *mesh = m_mesh ? m_mesh : &m_defaultMesh;
764 geometry = mesh->updateGeometry(geometry, m_source.attributeNames, rect);
766 QString log = mesh->log();
769 m_log += QLatin1String("*** Mesh ***\n");
773 emit statusChanged();
779 node->setGeometry(geometry);
780 node->setFlag(QSGNode::OwnsGeometry, true);
782 m_dirtyGeometry = false;
785 if (m_programDirty) {
786 QQuickShaderEffectProgram s = m_source;
787 if (s.fragmentCode.isEmpty())
788 s.fragmentCode = qt_default_fragment_code;
789 if (s.vertexCode.isEmpty())
790 s.vertexCode = qt_default_vertex_code;
791 s.className = metaObject()->className();
793 material->setProgramSource(s);
794 node->markDirty(QSGNode::DirtyMaterial);
795 m_programDirty = false;
799 if (bool(material->flags() & QSGMaterial::Blending) != m_blending) {
800 material->setFlag(QSGMaterial::Blending, m_blending);
801 node->markDirty(QSGNode::DirtyMaterial);
804 if (int(material->cullMode()) != int(m_cullMode)) {
805 material->setCullMode(QQuickShaderEffectMaterial::CullMode(m_cullMode));
806 node->markDirty(QSGNode::DirtyMaterial);
810 QVector<QPair<QByteArray, QVariant> > values;
811 QVector<QPair<QByteArray, QSGTextureProvider *> > textures;
812 const QVector<QPair<QByteArray, QSGTextureProvider *> > &oldTextures = material->textureProviders();
814 for (QSet<QByteArray>::const_iterator it = m_source.uniformNames.begin();
815 it != m_source.uniformNames.end(); ++it) {
816 values.append(qMakePair(*it, property(*it)));
818 for (int i = 0; i < oldTextures.size(); ++i) {
819 QSGTextureProvider *t = oldTextures.at(i).second;
821 disconnect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
823 for (int i = 0; i < m_sources.size(); ++i) {
824 const SourceData &source = m_sources.at(i);
825 QSGTextureProvider *t = source.sourceObject ? source.sourceObject->textureProvider() : 0;
826 textures.append(qMakePair(source.name, t));
828 connect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()), Qt::DirectConnection);
830 material->setUniforms(values);
831 material->setTextureProviders(textures);
832 node->markDirty(QSGNode::DirtyMaterial);
839 void QQuickShaderEffect::itemChange(ItemChange change, const ItemChangeData &value)
841 if (change == QQuickItem::ItemSceneChange) {
842 // See comment in QQuickShaderEffect::setSource().
843 for (int i = 0; i < m_sources.size(); ++i) {
844 QQuickItemPrivate *d = QQuickItemPrivate::get(m_sources.at(i).sourceObject);
845 if (!d->parentItem && value.canvas != d->canvas) {
846 QQuickItemPrivate::InitializationState initState;
848 d->initCanvas(&initState, value.canvas);
852 QQuickItem::itemChange(change, value);