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 QtDeclarative 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);
416 QQuickItem *item = qobject_cast<QQuickItem *>(obj);
417 if (!item || !item->isTextureProvider()) {
418 qWarning("ShaderEffect: source uniform [%s] is not assigned a valid texture provider: %s [%s]",
419 source.name.constData(), qPrintable(obj->objectName()), obj->metaObject()->className());
423 source.sourceObject = item;
426 QQuickItemPrivate *d = QQuickItemPrivate::get(item);
427 // 'item' needs a canvas to get a scene graph node. It usually gets one through its
428 // parent, but if the source item is "inline" rather than a reference -- i.e.
429 // "property variant source: Image { }" instead of "property variant source: foo" -- it
430 // will not get a parent. In those cases, 'item' should get the canvas from 'this'.
431 if (!d->parentItem && canvas() && !d->canvas) {
432 QQuickItemPrivate::InitializationState initState;
434 d->initCanvas(&initState, canvas());
439 void QQuickShaderEffect::disconnectPropertySignals()
441 disconnect(this, 0, this, SLOT(updateData()));
442 for (int i = 0; i < m_sources.size(); ++i) {
443 SourceData &source = m_sources[i];
444 disconnect(this, 0, source.mapper, 0);
445 disconnect(source.mapper, 0, this, 0);
449 void QQuickShaderEffect::connectPropertySignals()
451 QSet<QByteArray>::const_iterator it;
452 for (it = m_source.uniformNames.begin(); it != m_source.uniformNames.end(); ++it) {
453 int pi = metaObject()->indexOfProperty(it->constData());
455 QMetaProperty mp = metaObject()->property(pi);
456 if (!mp.hasNotifySignal())
457 qWarning("QQuickShaderEffect: property '%s' does not have notification method!", it->constData());
458 QByteArray signalName("2");
459 signalName.append(mp.notifySignal().signature());
460 connect(this, signalName, this, SLOT(updateData()));
462 // If the source is set via a dynamic property, like the layer is, then we need this check
463 // to disable the warning.
464 if (property(it->constData()).isValid())
466 qWarning("QQuickShaderEffect: '%s' does not have a matching property!", it->constData());
469 for (int i = 0; i < m_sources.size(); ++i) {
470 SourceData &source = m_sources[i];
471 int pi = metaObject()->indexOfProperty(source.name.constData());
473 QMetaProperty mp = metaObject()->property(pi);
474 QByteArray signalName("2");
475 signalName.append(mp.notifySignal().signature());
476 connect(this, signalName, source.mapper, SLOT(map()));
477 source.mapper->setMapping(this, i);
478 connect(source.mapper, SIGNAL(mapped(int)), this, SLOT(changeSource(int)));
480 // If the source is set via a dynamic property, like the layer is, then we need this check
481 // to disable the warning.
482 if (property(source.name.constData()).isValid())
484 qWarning("QQuickShaderEffect: '%s' does not have a matching source!", source.name.constData());
490 void QQuickShaderEffect::ensureCompleted()
500 void QQuickShaderEffect::reset()
502 disconnectPropertySignals();
504 m_source.attributeNames.clear();
505 m_source.uniformNames.clear();
506 m_source.respectsOpacity = false;
507 m_source.respectsMatrix = false;
508 m_source.className = metaObject()->className();
510 for (int i = 0; i < m_sources.size(); ++i) {
511 const SourceData &source = m_sources.at(i);
512 delete source.mapper;
517 m_programDirty = true;
521 void QQuickShaderEffect::updateProperties()
523 if (m_source.vertexCode.isEmpty()) {
524 m_source.attributeNames.append(QByteArray(qt_position_attribute_name));
525 m_source.attributeNames.append(QByteArray(qt_texcoord_attribute_name));
526 m_source.respectsMatrix = true;
528 lookThroughShaderCode(m_source.vertexCode);
530 if (m_source.fragmentCode.isEmpty()) {
531 m_source.respectsOpacity = true;
532 QByteArray name("source");
533 m_source.uniformNames.insert(name);
535 d.mapper = new QSignalMapper;
540 lookThroughShaderCode(m_source.fragmentCode);
543 if (!m_mesh && !m_source.attributeNames.contains(qt_position_attribute_name)) {
544 m_parseLog += QLatin1String("Warning: Missing reference to \'");
545 m_parseLog += QLatin1String(qt_position_attribute_name);
546 m_parseLog += QLatin1String("\'.\n");
548 if (!m_mesh && !m_source.attributeNames.contains(qt_texcoord_attribute_name)) {
549 m_parseLog += QLatin1String("Warning: Missing reference to \'");
550 m_parseLog += QLatin1String(qt_texcoord_attribute_name);
551 m_parseLog += QLatin1String("\'.\n");
553 if (!m_source.respectsMatrix) {
554 m_parseLog += QLatin1String("Warning: Missing reference to \'qt_Matrix\'.\n");
556 if (!m_source.respectsOpacity) {
557 m_parseLog += QLatin1String("Warning: Missing reference to \'qt_Opacity\'.\n");
560 for (int i = 0; i < m_sources.size(); ++i) {
561 QVariant v = property(m_sources.at(i).name);
565 connectPropertySignals();
570 enum VariableQualifier {
575 inline bool qt_isalpha(char c)
578 return (ch >= 'a' && ch <= 'z') || c == '_';
581 inline bool qt_isalnum(char c)
583 return qt_isalpha(c) || (c >= '0' && c <= '9');
586 inline bool qt_isspace(char c)
588 return c == ' ' || (c >= 0x09 && c <= 0x0d);
591 // Returns -1 if not found, returns index to first character after the name if found.
592 int qt_search_for_variable(const char *s, int length, int index, VariableQualifier &decl,
593 int &typeIndex, int &typeLength,
594 int &nameIndex, int &nameLength)
597 QualifierIdentifier, // Base state
602 Identifier expected = QualifierIdentifier;
603 bool compilerDirectiveExpected = index == 0;
605 while (index < length) {
607 while (qt_isspace(s[index])) {
608 compilerDirectiveExpected |= s[index] == '\n';
612 if (qt_isalpha(s[index])) {
616 while (qt_isalnum(s[index]))
618 int idLength = index - idIndex;
620 const int attrLen = sizeof("attribute") - 1;
621 const int uniLen = sizeof("uniform") - 1;
622 const int loLen = sizeof("lowp") - 1;
623 const int medLen = sizeof("mediump") - 1;
624 const int hiLen = sizeof("highp") - 1;
627 case QualifierIdentifier:
628 if (idLength == attrLen && qstrncmp("attribute", s + idIndex, attrLen) == 0) {
629 decl = AttributeQualifier;
630 expected = PrecisionIdentifier;
631 } else if (idLength == uniLen && qstrncmp("uniform", s + idIndex, uniLen) == 0) {
632 decl = UniformQualifier;
633 expected = PrecisionIdentifier;
636 case PrecisionIdentifier:
637 if ((idLength == loLen && qstrncmp("lowp", s + idIndex, loLen) == 0)
638 || (idLength == medLen && qstrncmp("mediump", s + idIndex, medLen) == 0)
639 || (idLength == hiLen && qstrncmp("highp", s + idIndex, hiLen) == 0))
641 expected = TypeIdentifier;
647 typeLength = idLength;
648 expected = NameIdentifier;
652 nameLength = idLength;
653 return index; // Attribute or uniform declaration found. Return result.
657 } else if (s[index] == '#' && compilerDirectiveExpected) {
658 // Skip compiler directives.
660 while (index < length && (s[index] != '\n' || s[index - 1] == '\\'))
662 } else if (s[index] == '/' && s[index + 1] == '/') {
665 while (index < length && s[index] != '\n')
667 } else if (s[index] == '/' && s[index + 1] == '*') {
670 while (index < length && (s[index] != '*' || s[index + 1] != '/'))
673 index += 2; // Skip star-slash.
675 expected = QualifierIdentifier;
678 compilerDirectiveExpected = false;
684 void QQuickShaderEffect::lookThroughShaderCode(const QByteArray &code)
687 int typeIndex, typeLength, nameIndex, nameLength;
688 const char *s = code.constData();
689 VariableQualifier decl;
690 while ((index = qt_search_for_variable(s, code.size(), index, decl, typeIndex, typeLength,
691 nameIndex, nameLength)) != -1)
693 if (decl == AttributeQualifier) {
694 m_source.attributeNames.append(QByteArray(s + nameIndex, nameLength));
696 Q_ASSERT(decl == UniformQualifier);
698 const int matLen = sizeof("qt_Matrix") - 1;
699 const int opLen = sizeof("qt_Opacity") - 1;
700 const int sampLen = sizeof("sampler2D") - 1;
702 if (nameLength == matLen && qstrncmp("qt_Matrix", s + nameIndex, matLen) == 0) {
703 m_source.respectsMatrix = true;
704 } else if (nameLength == opLen && qstrncmp("qt_Opacity", s + nameIndex, opLen) == 0) {
705 m_source.respectsOpacity = true;
707 QByteArray name(s + nameIndex, nameLength);
708 m_source.uniformNames.insert(name);
709 if (typeLength == sampLen && qstrncmp("sampler2D", s + typeIndex, sampLen) == 0) {
711 d.mapper = new QSignalMapper;
721 void QQuickShaderEffect::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
723 m_dirtyGeometry = true;
724 QQuickItem::geometryChanged(newGeometry, oldGeometry);
727 QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
729 QQuickShaderEffectNode *node = static_cast<QQuickShaderEffectNode *>(oldNode);
733 // In the case of a bad vertex shader, don't try to create a node...
734 if (m_source.attributeNames.isEmpty()) {
741 node = new QQuickShaderEffectNode;
742 m_programDirty = true;
744 m_dirtyGeometry = true;
745 connect(node, SIGNAL(logAndStatusChanged(QString,int)), this, SLOT(updateLogAndStatus(QString,int)));
748 QQuickShaderEffectMaterial *material = node->shaderMaterial();
751 node->setGeometry(0);
753 m_dirtyGeometry = true;
756 if (m_dirtyGeometry) {
757 node->setFlag(QSGNode::OwnsGeometry, false);
758 QSGGeometry *geometry = node->geometry();
759 QRectF rect(0, 0, width(), height());
760 QQuickShaderEffectMesh *mesh = m_mesh ? m_mesh : &m_defaultMesh;
762 geometry = mesh->updateGeometry(geometry, m_source.attributeNames, rect);
764 QString log = mesh->log();
767 m_log += QLatin1String("*** Mesh ***\n");
771 emit statusChanged();
777 node->setGeometry(geometry);
778 node->setFlag(QSGNode::OwnsGeometry, true);
780 m_dirtyGeometry = false;
783 if (m_programDirty) {
784 QQuickShaderEffectProgram s = m_source;
785 if (s.fragmentCode.isEmpty())
786 s.fragmentCode = qt_default_fragment_code;
787 if (s.vertexCode.isEmpty())
788 s.vertexCode = qt_default_vertex_code;
789 s.className = metaObject()->className();
791 material->setProgramSource(s);
792 node->markDirty(QSGNode::DirtyMaterial);
793 m_programDirty = false;
797 if (bool(material->flags() & QSGMaterial::Blending) != m_blending) {
798 material->setFlag(QSGMaterial::Blending, m_blending);
799 node->markDirty(QSGNode::DirtyMaterial);
802 if (int(material->cullMode()) != int(m_cullMode)) {
803 material->setCullMode(QQuickShaderEffectMaterial::CullMode(m_cullMode));
804 node->markDirty(QSGNode::DirtyMaterial);
808 QVector<QPair<QByteArray, QVariant> > values;
809 QVector<QPair<QByteArray, QSGTextureProvider *> > textures;
810 const QVector<QPair<QByteArray, QSGTextureProvider *> > &oldTextures = material->textureProviders();
812 for (QSet<QByteArray>::const_iterator it = m_source.uniformNames.begin();
813 it != m_source.uniformNames.end(); ++it) {
814 values.append(qMakePair(*it, property(*it)));
816 for (int i = 0; i < oldTextures.size(); ++i) {
817 QSGTextureProvider *t = oldTextures.at(i).second;
819 disconnect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
821 for (int i = 0; i < m_sources.size(); ++i) {
822 const SourceData &source = m_sources.at(i);
823 QSGTextureProvider *t = source.sourceObject ? source.sourceObject->textureProvider() : 0;
824 textures.append(qMakePair(source.name, t));
826 connect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()), Qt::DirectConnection);
828 material->setUniforms(values);
829 material->setTextureProviders(textures);
830 node->markDirty(QSGNode::DirtyMaterial);
837 void QQuickShaderEffect::itemChange(ItemChange change, const ItemChangeData &value)
839 if (change == QQuickItem::ItemSceneChange) {
840 // See comment in QQuickShaderEffect::setSource().
841 for (int i = 0; i < m_sources.size(); ++i) {
842 QQuickItemPrivate *d = QQuickItemPrivate::get(m_sources.at(i).sourceObject);
843 if (!d->parentItem && value.canvas != d->canvas) {
844 QQuickItemPrivate::InitializationState initState;
846 d->initCanvas(&initState, value.canvas);
850 QQuickItem::itemChange(change, value);