Move QSGShaderEffectMaterial into the QSGShaderEffectNode. Now owned by Render Thread
[profile/ivi/qtdeclarative.git] / src / declarative / items / qsgshadereffect.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <private/qsgshadereffect_p.h>
43 #include <private/qsgshadereffectnode_p.h>
44
45 #include "qsgmaterial.h"
46 #include "qsgitem_p.h"
47
48 #include <private/qsgcontext_p.h>
49 #include <private/qsgtextureprovider_p.h>
50 #include "qsgcanvas.h"
51
52 #include <QtCore/qsignalmapper.h>
53 #include <QtOpenGL/qglframebufferobject.h>
54
55 QT_BEGIN_NAMESPACE
56
57 static const char qt_default_vertex_code[] =
58     "uniform highp mat4 qt_Matrix;                                  \n"
59     "attribute highp vec4 qt_Vertex;                                \n"
60     "attribute highp vec2 qt_MultiTexCoord0;                        \n"
61     "varying highp vec2 qt_TexCoord0;                               \n"
62     "void main() {                                                  \n"
63     "    qt_TexCoord0 = qt_MultiTexCoord0;                          \n"
64     "    gl_Position = qt_Matrix * qt_Vertex;                       \n"
65     "}";
66
67 static const char qt_default_fragment_code[] =
68     "varying highp vec2 qt_TexCoord0;                                   \n"
69     "uniform sampler2D source;                                          \n"
70     "uniform lowp float qt_Opacity;                                     \n"
71     "void main() {                                                      \n"
72     "    gl_FragColor = texture2D(source, qt_TexCoord0) * qt_Opacity;   \n"
73     "}";
74
75 static const char qt_position_attribute_name[] = "qt_Vertex";
76 static const char qt_texcoord_attribute_name[] = "qt_MultiTexCoord0";
77
78 const char *qtPositionAttributeName()
79 {
80     return qt_position_attribute_name;
81 }
82
83 const char *qtTexCoordAttributeName()
84 {
85     return qt_texcoord_attribute_name;
86 }
87
88 // TODO: Remove after grace period.
89 QSGShaderEffectItem::QSGShaderEffectItem(QSGItem *parent)
90     : QSGShaderEffect(parent)
91 {
92     qWarning("ShaderEffectItem has been deprecated. Use ShaderEffect instead.");
93 }
94
95
96 /*!
97     \qmlclass ShaderEffect QSGShaderEffect
98     \since 5.0
99     \ingroup qml-basic-visual-elements
100     \brief The ShaderEffect element applies custom shaders to a rectangle.
101     \inherits Item
102
103     The ShaderEffect element applies a custom OpenGL
104     \l{vertexShader}{vertex} and \l{fragmentShader}{fragment} shader to a
105     rectangle. It allows you to write effects such as drop shadow, blur,
106     colorize and page curl directly in QML.
107
108     There are two types of input to the \l vertexShader:
109     uniform variables and attributes. Some are predefined:
110     \list
111     \o uniform mat4 qt_Matrix - combined transformation
112        matrix, the product of the matrices from the root item to this
113        ShaderEffect, and an orthogonal projection.
114     \o uniform float qt_Opacity - combined opacity, the product of the
115        opacities from the root item to this ShaderEffect.
116     \o attribute vec4 qt_Vertex - vertex position, the top-left vertex has
117        position (0, 0), the bottom-right (\l{Item::width}{width},
118        \l{Item::height}{height}).
119     \o attribute vec2 qt_MultiTexCoord0 - texture coordinate, the top-left
120        coordinate is (0, 0), the bottom-right (1, 1).
121     \endlist
122
123     In addition, any property that can be mapped to an OpenGL Shading Language
124     (GLSL) type is available as a uniform variable. The following list shows
125     how properties are mapped to GLSL uniform variables:
126     \list
127     \o bool, int, qreal -> bool, int, float - If the type in the shader is not
128        the same as in QML, the value is converted automatically.
129     \o QColor -> vec4 - When colors are passed to the shader, they are first
130        premultiplied. Thus Qt.rgba(0.2, 0.6, 1.0, 0.5) becomes
131        vec4(0.1, 0.3, 0.5, 0.5) in the shader, for example.
132     \o QRect, QRectF -> vec4 - Qt.rect(x, y, w, h) becomes vec4(x, y, w, h) in
133        the shader.
134     \o QPoint, QPointF, QSize, QSizeF -> vec2
135     \o QVector3D -> vec3
136     \o QTransform -> mat4
137     \o \l Image, \l ShaderEffectSource -> sampler2D - Origin is in the top-left
138        corner, and the color values are premultiplied.
139     \endlist
140
141     The output from the \l fragmentShader should be premultiplied. If
142     \l blending is enabled, source-over blending is used. However, additive
143     blending can be achieved by outputting zero in the alpha channel.
144
145     \row
146     \o \image declarative-shadereffectitem.png
147     \o \qml
148         import QtQuick 2.0
149
150         Rectangle {
151             width: 200; height: 100
152             Row {
153                 Image { id: img; sourceSize { width: 100; height: 100 } source: "qt-logo.png" }
154                 ShaderEffect {
155                     width: 100; height: 100
156                     property variant src: img
157                     vertexShader: "
158                         uniform highp mat4 qt_Matrix;
159                         attribute highp vec4 qt_Vertex;
160                         attribute highp vec2 qt_MultiTexCoord0;
161                         varying highp vec2 coord;
162                         void main() {
163                             coord = qt_MultiTexCoord0;
164                             gl_Position = qt_Matrix * qt_Vertex;
165                         }"
166                     fragmentShader: "
167                         varying highp vec2 coord;
168                         uniform sampler2D src;
169                         uniform lowp float qt_Opacity;
170                         void main() {
171                             lowp vec4 tex = texture2D(src, coord);
172                             gl_FragColor = vec4(vec3(dot(tex.rgb, vec3(0.344, 0.5, 0.156))), tex.a) * qt_Opacity;
173                         }"
174                 }
175             }
176         }
177         \endqml
178     \endrow
179
180     By default, the ShaderEffect consists of four vertices, one for each
181     corner. For non-linear vertex transformations, like page curl, you can
182     specify a fine grid of vertices by specifying a \l mesh resolution.
183
184     \note Scene Graph textures have origin in the top-left corner rather than
185     bottom-left which is common in OpenGL.
186 */
187
188 QSGShaderEffect::QSGShaderEffect(QSGItem *parent)
189     : QSGItem(parent)
190     , m_meshResolution(1, 1)
191     , m_deprecatedMesh(0)
192     , m_cullMode(NoCulling)
193     , m_blending(true)
194     , m_dirtyData(true)
195     , m_programDirty(true)
196     , m_dirtyMesh(true)
197     , m_dirtyGeometry(true)
198 {
199     setFlag(QSGItem::ItemHasContents);
200 }
201
202 QSGShaderEffect::~QSGShaderEffect()
203 {
204     reset();
205 }
206
207 void QSGShaderEffect::componentComplete()
208 {
209     updateProperties();
210     QSGItem::componentComplete();
211 }
212
213 /*!
214     \qmlproperty string ShaderEffect::fragmentShader
215
216     This property holds the fragment shader's GLSL source code.
217     The default shader passes the texture coordinate along to the fragment
218     shader as "varying highp vec2 qt_TexCoord0".
219 */
220
221 void QSGShaderEffect::setFragmentShader(const QByteArray &code)
222 {
223     if (m_source.fragmentCode.constData() == code.constData())
224         return;
225     m_source.fragmentCode = code;
226     if (isComponentComplete()) {
227         reset();
228         updateProperties();
229     }
230     emit fragmentShaderChanged();
231 }
232
233 /*!
234     \qmlproperty string ShaderEffect::vertexShader
235
236     This property holds the vertex shader's GLSL source code.
237     The default shader expects the texture coordinate to be passed from the
238     vertex shader as "varying highp vec2 qt_TexCoord0", and it samples from a
239     sampler2D named "source".
240 */
241
242 void QSGShaderEffect::setVertexShader(const QByteArray &code)
243 {
244     if (m_source.vertexCode.constData() == code.constData())
245         return;
246     m_source.vertexCode = code;
247     if (isComponentComplete()) {
248         reset();
249         updateProperties();
250     }
251     emit vertexShaderChanged();
252 }
253
254 /*!
255     \qmlproperty bool ShaderEffect::blending
256
257     If this property is true, the output from the \l fragmentShader is blended
258     with the background using source-over blend mode. If false, the background
259     is disregarded. Blending decreases the performance, so you should set this
260     property to false when blending is not needed. The default value is true.
261 */
262
263 void QSGShaderEffect::setBlending(bool enable)
264 {
265     if (blending() == enable)
266         return;
267
268     m_blending = enable;
269     update();
270
271     emit blendingChanged();
272 }
273
274 /*!
275     \qmlproperty size ShaderEffect::mesh
276
277     This property holds the mesh resolution. The default resolution is 1x1
278     which is the minimum and corresponds to a mesh with four vertices.
279     For non-linear vertex transformations, you probably want to set the
280     resolution higher.
281
282     \row
283     \o \image declarative-gridmesh.png
284     \o \qml
285         import QtQuick 2.0
286
287         ShaderEffect {
288             width: 200
289             height: 200
290             mesh: Qt.size(20, 20)
291             property variant source: Image {
292                 source: "qt-logo.png"
293                 sourceSize { width: 200; height: 200 }
294                 smooth: true
295             }
296             vertexShader: "
297                 uniform highp mat4 qt_Matrix;
298                 attribute highp vec4 qt_Vertex;
299                 attribute highp vec2 qt_MultiTexCoord0;
300                 varying highp vec2 qt_TexCoord0;
301                 uniform highp float width;
302                 void main() {
303                     highp vec4 pos = qt_Vertex;
304                     highp float d = .5 * smoothstep(0., 1., qt_MultiTexCoord0.y);
305                     pos.x = width * mix(d, 1.0 - d, qt_MultiTexCoord0.x);
306                     gl_Position = qt_Matrix * pos;
307                     qt_TexCoord0 = qt_MultiTexCoord0;
308                 }"
309         }
310         \endqml
311     \endrow
312 */
313
314 QVariant QSGShaderEffect::mesh() const
315 {
316     return m_deprecatedMesh ? qVariantFromValue(static_cast<QObject *>(m_deprecatedMesh))
317                             : qVariantFromValue(m_meshResolution);
318 }
319
320 void QSGShaderEffect::setMesh(const QVariant &mesh)
321 {
322     // TODO: Replace QVariant with QSize after grace period.
323     QSGShaderEffectMesh *newMesh = qobject_cast<QSGShaderEffectMesh *>(qVariantValue<QObject *>(mesh));
324     if (newMesh && newMesh == m_deprecatedMesh)
325         return;
326     if (m_deprecatedMesh)
327         disconnect(m_deprecatedMesh, SIGNAL(geometryChanged()), this, 0);
328     m_deprecatedMesh = newMesh;
329     if (m_deprecatedMesh) {
330         qWarning("ShaderEffect: Setting the mesh to something other than a size is deprecated.");
331         connect(m_deprecatedMesh, SIGNAL(geometryChanged()), this, SLOT(updateGeometry()));
332     } else {
333         if (qVariantCanConvert<QSize>(mesh)) {
334             m_meshResolution = mesh.toSize();
335         } else {
336             QList<QByteArray> res = mesh.toByteArray().split('x');
337             bool ok = res.size() == 2;
338             if (ok) {
339                 int w = res.at(0).toInt(&ok);
340                 if (ok) {
341                     int h = res.at(1).toInt(&ok);
342                     if (ok)
343                         m_meshResolution = QSize(w, h);
344                 }
345             }
346             if (!ok)
347                 qWarning("ShaderEffect: mesh resolution must be a size.");
348         }
349         m_defaultMesh.setResolution(m_meshResolution);
350     }
351
352     m_dirtyMesh = true;
353     update();
354     emit meshChanged();
355 }
356
357 /*!
358     \qmlproperty enumeration ShaderEffect::cullMode
359
360     This property defines which sides of the element should be visible.
361
362     \list
363     \o ShaderEffect.NoCulling - Both sides are visible
364     \o ShaderEffect.BackFaceCulling - only front side is visible
365     \o ShaderEffect.FrontFaceCulling - only back side is visible
366     \endlist
367
368     The default is NoCulling.
369 */
370
371 void QSGShaderEffect::setCullMode(CullMode face)
372 {
373     if (face == m_cullMode)
374         return;
375     m_cullMode = face;
376     update();
377     emit cullModeChanged();
378 }
379
380 void QSGShaderEffect::changeSource(int index)
381 {
382     Q_ASSERT(index >= 0 && index < m_sources.size());
383     QVariant v = property(m_sources.at(index).name.constData());
384     setSource(v, index);
385 }
386
387 void QSGShaderEffect::updateData()
388 {
389     m_dirtyData = true;
390     update();
391 }
392
393 void QSGShaderEffect::updateGeometry()
394 {
395     m_dirtyGeometry = true;
396     update();
397 }
398
399 void QSGShaderEffect::setSource(const QVariant &var, int index)
400 {
401     Q_ASSERT(index >= 0 && index < m_sources.size());
402
403     SourceData &source = m_sources[index];
404
405     source.item = 0;
406     if (var.isNull()) {
407         return;
408     } else if (!qVariantCanConvert<QObject *>(var)) {
409         qWarning("Could not assign source of type '%s' to property '%s'.", var.typeName(), source.name.constData());
410         return;
411     }
412
413     QObject *obj = qVariantValue<QObject *>(var);
414
415     QSGTextureProvider *int3rface = QSGTextureProvider::from(obj);
416     if (!int3rface) {
417         qWarning("Could not assign property '%s', did not implement QSGTextureProvider.", source.name.constData());
418     }
419
420     source.item = qobject_cast<QSGItem *>(obj);
421
422     // TODO: Find better solution.
423     // 'source.item' needs a canvas to get a scenegraph node.
424     // The easiest way to make sure it gets a canvas is to
425     // make it a part of the same item tree as 'this'.
426     if (source.item && source.item->parentItem() == 0) {
427         source.item->setParentItem(this);
428         source.item->setVisible(false);
429     }
430 }
431
432 void QSGShaderEffect::disconnectPropertySignals()
433 {
434     disconnect(this, 0, this, SLOT(updateData()));
435     for (int i = 0; i < m_sources.size(); ++i) {
436         SourceData &source = m_sources[i];
437         disconnect(this, 0, source.mapper, 0);
438         disconnect(source.mapper, 0, this, 0);
439     }
440 }
441
442 void QSGShaderEffect::connectPropertySignals()
443 {
444     QSet<QByteArray>::const_iterator it;
445     for (it = m_source.uniformNames.begin(); it != m_source.uniformNames.end(); ++it) {
446         int pi = metaObject()->indexOfProperty(it->constData());
447         if (pi >= 0) {
448             QMetaProperty mp = metaObject()->property(pi);
449             if (!mp.hasNotifySignal())
450                 qWarning("QSGShaderEffect: property '%s' does not have notification method!", it->constData());
451             QByteArray signalName("2");
452             signalName.append(mp.notifySignal().signature());
453             connect(this, signalName, this, SLOT(updateData()));
454         } else {
455             qWarning("QSGShaderEffect: '%s' does not have a matching property!", it->constData());
456         }
457     }
458     for (int i = 0; i < m_sources.size(); ++i) {
459         SourceData &source = m_sources[i];
460         int pi = metaObject()->indexOfProperty(source.name.constData());
461         if (pi >= 0) {
462             QMetaProperty mp = metaObject()->property(pi);
463             QByteArray signalName("2");
464             signalName.append(mp.notifySignal().signature());
465             connect(this, signalName, source.mapper, SLOT(map()));
466             source.mapper->setMapping(this, i);
467             connect(source.mapper, SIGNAL(mapped(int)), this, SLOT(changeSource(int)));
468         } else {
469             qWarning("QSGShaderEffect: '%s' does not have a matching source!", source.name.constData());
470         }
471     }
472 }
473
474 void QSGShaderEffect::reset()
475 {
476     disconnectPropertySignals();
477
478     m_source.attributeNames.clear();
479     m_source.uniformNames.clear();
480     m_source.respectsOpacity = false;
481     m_source.respectsMatrix = false;
482     m_source.className = metaObject()->className();
483
484     for (int i = 0; i < m_sources.size(); ++i) {
485         const SourceData &source = m_sources.at(i);
486         delete source.mapper;
487         if (source.item && source.item->parentItem() == this)
488             source.item->setParentItem(0);
489     }
490     m_sources.clear();
491
492     m_programDirty = true;
493     m_dirtyMesh = true;
494 }
495
496 void QSGShaderEffect::updateProperties()
497 {
498     QByteArray vertexCode = m_source.vertexCode;
499     QByteArray fragmentCode = m_source.fragmentCode;
500     if (vertexCode.isEmpty())
501         vertexCode = qt_default_vertex_code;
502     if (fragmentCode.isEmpty())
503         fragmentCode = qt_default_fragment_code;
504
505     lookThroughShaderCode(vertexCode);
506     lookThroughShaderCode(fragmentCode);
507
508     // TODO: Remove !m_deprecatedMesh check after grace period.
509     if (!m_deprecatedMesh && !m_source.attributeNames.contains(qt_position_attribute_name))
510         qWarning("QSGShaderEffect: Missing reference to \'%s\'.", qt_position_attribute_name);
511     if (!m_deprecatedMesh && !m_source.attributeNames.contains(qt_texcoord_attribute_name))
512         qWarning("QSGShaderEffect: Missing reference to \'%s\'.", qt_texcoord_attribute_name);
513     if (!m_source.respectsMatrix)
514         qWarning("QSGShaderEffect: Missing reference to \'qt_Matrix\'.");
515     if (!m_source.respectsOpacity)
516         qWarning("QSGShaderEffect: Missing reference to \'qt_Opacity\'.");
517
518     for (int i = 0; i < m_sources.size(); ++i) {
519         QVariant v = property(m_sources.at(i).name);
520         setSource(v, i);
521     }
522
523     connectPropertySignals();
524 }
525
526 void QSGShaderEffect::lookThroughShaderCode(const QByteArray &code)
527 {
528     // Regexp for matching attributes and uniforms.
529     // In human readable form: attribute|uniform [lowp|mediump|highp] <type> <name>
530     static QRegExp re(QLatin1String("\\b(attribute|uniform)\\b\\s*\\b(?:lowp|mediump|highp)?\\b\\s*\\b(\\w+)\\b\\s*\\b(\\w+)"));
531     Q_ASSERT(re.isValid());
532
533     int pos = -1;
534
535     QString wideCode = QString::fromLatin1(code.constData(), code.size());
536
537     while ((pos = re.indexIn(wideCode, pos + 1)) != -1) {
538         QByteArray decl = re.cap(1).toLatin1(); // uniform or attribute
539         QByteArray type = re.cap(2).toLatin1(); // type
540         QByteArray name = re.cap(3).toLatin1(); // variable name
541
542         if (decl == "attribute") {
543             m_source.attributeNames.append(name);
544         } else {
545             Q_ASSERT(decl == "uniform");
546
547             if (name == "qt_Matrix") {
548                 m_source.respectsMatrix = true;
549             } else if (name == "qt_ModelViewProjectionMatrix") {
550                 // TODO: Remove after grace period.
551                 qWarning("ShaderEffect: qt_ModelViewProjectionMatrix is deprecated. Use qt_Matrix instead.");
552                 m_source.respectsMatrix = true;
553             } else if (name == "qt_Opacity") {
554                 m_source.respectsOpacity = true;
555             } else {
556                 m_source.uniformNames.insert(name);
557                 if (type == "sampler2D") {
558                     SourceData d;
559                     d.mapper = new QSignalMapper;
560                     d.name = name;
561                     d.item = 0;
562                     m_sources.append(d);
563                 }
564             }
565         }
566     }
567 }
568
569 void QSGShaderEffect::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
570 {
571     m_dirtyGeometry = true;
572     QSGItem::geometryChanged(newGeometry, oldGeometry);
573 }
574
575 QSGNode *QSGShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
576 {
577     QSGShaderEffectNode *node = static_cast<QSGShaderEffectNode *>(oldNode);
578
579     if (!node) {
580         node = new QSGShaderEffectNode;
581         m_programDirty = true;
582         m_dirtyData = true;
583         m_dirtyGeometry = true;
584     }
585
586     QSGShaderEffectMaterial *material = node->shaderMaterial();
587
588     if (m_dirtyMesh) {
589         node->setGeometry(0);
590         m_dirtyMesh = false;
591         m_dirtyGeometry = true;
592     }
593
594     if (m_dirtyGeometry) {
595         node->setFlag(QSGNode::OwnsGeometry, false);
596         QSGGeometry *geometry = node->geometry();
597         QRectF rect(0, 0, width(), height());
598         QSGShaderEffectMesh *mesh = m_deprecatedMesh ? m_deprecatedMesh : &m_defaultMesh;
599
600         geometry = mesh->updateGeometry(geometry, m_source.attributeNames, rect);
601         if (!geometry) {
602             delete node;
603             return 0;
604         }
605
606         node->setGeometry(geometry);
607         node->setFlag(QSGNode::OwnsGeometry, true);
608
609         m_dirtyGeometry = false;
610     }
611
612     if (m_programDirty) {
613         QSGShaderEffectProgram s = m_source;
614         if (s.fragmentCode.isEmpty())
615             s.fragmentCode = qt_default_fragment_code;
616         if (s.vertexCode.isEmpty())
617             s.vertexCode = qt_default_vertex_code;
618         s.className = metaObject()->className();
619
620         material->setProgramSource(s);
621         node->markDirty(QSGNode::DirtyMaterial);
622         m_programDirty = false;
623     }
624
625     // Update blending
626     if (bool(material->flags() & QSGMaterial::Blending) != m_blending) {
627         material->setFlag(QSGMaterial::Blending, m_blending);
628         node->markDirty(QSGNode::DirtyMaterial);
629     }
630
631     if (int(material->cullMode()) != int(m_cullMode)) {
632         material->setCullMode(QSGShaderEffectMaterial::CullMode(m_cullMode));
633         node->markDirty(QSGNode::DirtyMaterial);
634     }
635
636     if (m_dirtyData) {
637         QVector<QPair<QByteArray, QVariant> > values;
638         QVector<QPair<QByteArray, QPointer<QSGItem> > > textures;
639         const QVector<QPair<QByteArray, QPointer<QSGItem> > > &oldTextures = material->textureProviders();
640
641         for (QSet<QByteArray>::const_iterator it = m_source.uniformNames.begin(); 
642              it != m_source.uniformNames.end(); ++it) {
643             values.append(qMakePair(*it, property(*it)));
644         }
645         for (int i = 0; i < oldTextures.size(); ++i) {
646             QSGTextureProvider *oldSource = QSGTextureProvider::from(oldTextures.at(i).second);
647             if (oldSource && oldSource->textureChangedSignal())
648                 disconnect(oldTextures.at(i).second, oldSource->textureChangedSignal(), node, SLOT(markDirtyTexture()));
649         }
650         for (int i = 0; i < m_sources.size(); ++i) {
651             const SourceData &source = m_sources.at(i);
652             textures.append(qMakePair(source.name, source.item));
653             QSGTextureProvider *t = QSGTextureProvider::from(source.item);
654             if (t && t->textureChangedSignal())
655                 connect(source.item, t->textureChangedSignal(), node, SLOT(markDirtyTexture()), Qt::DirectConnection);
656         }
657         material->setUniforms(values);
658         material->setTextureProviders(textures);
659         node->markDirty(QSGNode::DirtyMaterial);
660         m_dirtyData = false;
661     }
662
663     return node;
664 }
665
666 QT_END_NAMESPACE