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;
318 const int srLen = sizeof("qt_SubRect_") - 1;
321 QSignalMapper *mapper = 0;
322 d.name = QByteArray(s + nameIndex, nameLength);
323 if (nameLength == opLen && qstrncmp("qt_Opacity", s + nameIndex, opLen) == 0) {
324 d.specialType = UniformData::Opacity;
325 } else if (nameLength == matLen && qstrncmp("qt_Matrix", s + nameIndex, matLen) == 0) {
326 d.specialType = UniformData::Matrix;
327 } else if (nameLength > srLen && qstrncmp("qt_SubRect_", s + nameIndex, srLen) == 0) {
328 d.specialType = UniformData::SubRect;
330 mapper = new QSignalMapper;
331 mapper->setMapping(item, uniformData[shaderType].size() | (shaderType << 16));
332 d.value = item->property(d.name.constData());
333 bool sampler = typeLength == sampLen && qstrncmp("sampler2D", s + typeIndex, sampLen) == 0;
334 d.specialType = sampler ? UniformData::Sampler : UniformData::None;
336 uniformData[shaderType].append(d);
337 signalMappers[shaderType].append(mapper);
342 void QQuickShaderEffectCommon::updateShader(QQuickItem *item, Key::ShaderType shaderType)
344 disconnectPropertySignals(item, shaderType);
345 qDeleteAll(signalMappers[shaderType]);
346 uniformData[shaderType].clear();
347 signalMappers[shaderType].clear();
348 if (shaderType == Key::VertexShader)
351 const QByteArray &code = source.sourceCode[shaderType];
352 if (code.isEmpty()) {
353 // Optimize for default code.
354 if (shaderType == Key::VertexShader) {
355 attributes.append(QByteArray(qt_position_attribute_name));
356 attributes.append(QByteArray(qt_texcoord_attribute_name));
358 d.name = "qt_Matrix";
359 d.specialType = UniformData::Matrix;
360 uniformData[Key::VertexShader].append(d);
361 signalMappers[Key::VertexShader].append(0);
362 } else if (shaderType == Key::FragmentShader) {
364 d.name = "qt_Opacity";
365 d.specialType = UniformData::Opacity;
366 uniformData[Key::FragmentShader].append(d);
367 signalMappers[Key::FragmentShader].append(0);
368 QSignalMapper *mapper = new QSignalMapper;
369 mapper->setMapping(item, 1 | (Key::FragmentShader << 16));
370 const char *sourceName = "source";
372 d.value = item->property(sourceName);
373 d.specialType = UniformData::Sampler;
374 uniformData[Key::FragmentShader].append(d);
375 signalMappers[Key::FragmentShader].append(mapper);
378 lookThroughShaderCode(item, shaderType, code);
381 connectPropertySignals(item, shaderType);
384 void QQuickShaderEffectCommon::updateMaterial(QQuickShaderEffectNode *node,
385 QQuickShaderEffectMaterial *material,
386 bool updateUniforms, bool updateUniformValues,
387 bool updateTextureProviders)
389 if (updateUniforms) {
390 for (int i = 0; i < material->textureProviders.size(); ++i) {
391 QSGTextureProvider *t = material->textureProviders.at(i);
393 QObject::disconnect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
394 QObject::disconnect(t, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*)));
398 // First make room in the textureProviders array. Set to proper value further down.
399 int textureProviderCount = 0;
400 for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
401 for (int i = 0; i < uniformData[shaderType].size(); ++i) {
402 if (uniformData[shaderType].at(i).specialType == UniformData::Sampler)
403 ++textureProviderCount;
405 material->uniforms[shaderType] = uniformData[shaderType];
407 material->textureProviders.fill(0, textureProviderCount);
408 updateUniformValues = false;
409 updateTextureProviders = true;
412 if (updateUniformValues) {
413 for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
414 Q_ASSERT(uniformData[shaderType].size() == material->uniforms[shaderType].size());
415 for (int i = 0; i < uniformData[shaderType].size(); ++i)
416 material->uniforms[shaderType][i].value = uniformData[shaderType].at(i).value;
420 if (updateTextureProviders) {
422 for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
423 for (int i = 0; i < uniformData[shaderType].size(); ++i) {
424 const UniformData &d = uniformData[shaderType].at(i);
425 if (d.specialType != UniformData::Sampler)
427 QSGTextureProvider *oldProvider = material->textureProviders.at(index);
428 QSGTextureProvider *newProvider = 0;
429 QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
430 if (source && source->isTextureProvider())
431 newProvider = source->textureProvider();
432 if (newProvider != oldProvider) {
434 QObject::disconnect(oldProvider, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
435 QObject::disconnect(oldProvider, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*)));
438 Q_ASSERT_X(newProvider->thread() == QThread::currentThread(),
439 "QQuickShaderEffect::updatePaintNode",
440 "Texture provider must belong to the rendering thread");
441 QObject::connect(newProvider, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
442 QObject::connect(newProvider, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*)));
444 const char *typeName = source ? source->metaObject()->className() : d.value.typeName();
445 qWarning("ShaderEffect: Property '%s' is not assigned a valid texture provider (%s).",
446 d.name.constData(), typeName);
448 material->textureProviders[index] = newProvider;
453 Q_ASSERT(index == material->textureProviders.size());
457 void QQuickShaderEffectCommon::updateCanvas(QQuickCanvas *canvas)
459 // See comment in QQuickShaderEffectCommon::propertyChanged().
461 for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
462 for (int i = 0; i < uniformData[shaderType].size(); ++i) {
463 const UniformData &d = uniformData[shaderType].at(i);
464 if (d.specialType == UniformData::Sampler) {
465 QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
467 QQuickItemPrivate::get(source)->refCanvas(canvas);
472 for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
473 for (int i = 0; i < uniformData[shaderType].size(); ++i) {
474 const UniformData &d = uniformData[shaderType].at(i);
475 if (d.specialType == UniformData::Sampler) {
476 QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
478 QQuickItemPrivate::get(source)->derefCanvas();
485 void QQuickShaderEffectCommon::sourceDestroyed(QObject *object)
487 for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
488 for (int i = 0; i < uniformData[shaderType].size(); ++i) {
489 UniformData &d = uniformData[shaderType][i];
490 if (d.specialType == UniformData::Sampler && d.value.canConvert<QObject *>()) {
491 if (qvariant_cast<QObject *>(d.value) == object)
492 d.value = QVariant();
499 void QQuickShaderEffectCommon::propertyChanged(QQuickItem *item, int mappedId,
500 bool *textureProviderChanged)
502 Key::ShaderType shaderType = Key::ShaderType(mappedId >> 16);
503 int index = mappedId & 0xffff;
504 UniformData &d = uniformData[shaderType][index];
505 if (d.specialType == UniformData::Sampler) {
506 QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
509 QQuickItemPrivate::get(source)->derefCanvas();
510 QObject::disconnect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*)));
513 d.value = item->property(d.name.constData());
515 source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
517 // 'source' needs a canvas to get a scene graph node. It usually gets one through its
518 // parent, but if the source item is "inline" rather than a reference -- i.e.
519 // "property variant source: Image { }" instead of "property variant source: foo" -- it
520 // will not get a parent. In those cases, 'source' should get the canvas from 'item'.
522 QQuickItemPrivate::get(source)->refCanvas(item->canvas());
523 QObject::connect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*)));
525 if (textureProviderChanged)
526 *textureProviderChanged = true;
528 d.value = item->property(d.name.constData());
529 if (textureProviderChanged)
530 *textureProviderChanged = false;
536 \qmlclass ShaderEffect QQuickShaderEffect
537 \inqmlmodule QtQuick 2
539 \ingroup qtquick-effects
540 \brief Applies custom shaders to a rectangle
542 The ShaderEffect type applies a custom OpenGL
543 \l{vertexShader}{vertex} and \l{fragmentShader}{fragment} shader to a
544 rectangle. It allows you to write effects such as drop shadow, blur,
545 colorize and page curl directly in QML.
547 There are two types of input to the \l vertexShader:
548 uniform variables and attributes. Some are predefined:
550 \li uniform mat4 qt_Matrix - combined transformation
551 matrix, the product of the matrices from the root item to this
552 ShaderEffect, and an orthogonal projection.
553 \li uniform float qt_Opacity - combined opacity, the product of the
554 opacities from the root item to this ShaderEffect.
555 \li attribute vec4 qt_Vertex - vertex position, the top-left vertex has
556 position (0, 0), the bottom-right (\l{Item::width}{width},
557 \l{Item::height}{height}).
558 \li attribute vec2 qt_MultiTexCoord0 - texture coordinate, the top-left
559 coordinate is (0, 0), the bottom-right (1, 1).
562 In addition, any property that can be mapped to an OpenGL Shading Language
563 (GLSL) type is available as a uniform variable. The following list shows
564 how properties are mapped to GLSL uniform variables:
566 \li bool, int, qreal -> bool, int, float - If the type in the shader is not
567 the same as in QML, the value is converted automatically.
568 \li QColor -> vec4 - When colors are passed to the shader, they are first
569 premultiplied. Thus Qt.rgba(0.2, 0.6, 1.0, 0.5) becomes
570 vec4(0.1, 0.3, 0.5, 0.5) in the shader, for example.
571 \li QRect, QRectF -> vec4 - Qt.rect(x, y, w, h) becomes vec4(x, y, w, h) in
573 \li QPoint, QPointF, QSize, QSizeF -> vec2
574 \li QVector3D -> vec3
575 \li QTransform -> mat4
576 \li \l Image, \l ShaderEffectSource -> sampler2D - Origin is in the top-left
577 corner, and the color values are premultiplied.
580 The QML scene graph back-end may choose to allocate textures in texture
581 atlases. If a texture allocated in an atlas is passed to a ShaderEffect,
582 it is by default copied from the texture atlas into a stand-alone texture
583 so that the texture coordinates span from 0 to 1, and you get the expected
584 wrap modes. However, this will increase the memory usage. To avoid the
585 texture copy, you can for each "uniform sampler2D <name>" declare a
586 "uniform vec4 qt_SubRect_<name>" which will be assigned the texture's
587 normalized source rectangle. For stand-alone textures, the source rectangle
588 is [0, 1]x[0, 1]. For textures in an atlas, the source rectangle corresponds
589 to the part of the texture atlas where the texture is stored.
590 The correct way to calculate the texture coordinate for a texture called
591 "source" within a texture atlas is
592 "qt_SubRect_source.xy + qt_SubRect_source.zw * qt_MultiTexCoord0".
594 The output from the \l fragmentShader should be premultiplied. If
595 \l blending is enabled, source-over blending is used. However, additive
596 blending can be achieved by outputting zero in the alpha channel.
600 \li \image declarative-shadereffectitem.png
605 width: 200; height: 100
607 Image { id: img; sourceSize { width: 100; height: 100 } source: "qt-logo.png" }
609 width: 100; height: 100
610 property variant src: img
612 uniform highp mat4 qt_Matrix;
613 attribute highp vec4 qt_Vertex;
614 attribute highp vec2 qt_MultiTexCoord0;
615 varying highp vec2 coord;
617 coord = qt_MultiTexCoord0;
618 gl_Position = qt_Matrix * qt_Vertex;
621 varying highp vec2 coord;
622 uniform sampler2D src;
623 uniform lowp float qt_Opacity;
625 lowp vec4 tex = texture2D(src, coord);
626 gl_FragColor = vec4(vec3(dot(tex.rgb, vec3(0.344, 0.5, 0.156))), tex.a) * qt_Opacity;
634 By default, the ShaderEffect consists of four vertices, one for each
635 corner. For non-linear vertex transformations, like page curl, you can
636 specify a fine grid of vertices by specifying a \l mesh resolution.
638 \note Scene Graph textures have origin in the top-left corner rather than
639 bottom-left which is common in OpenGL.
642 QQuickShaderEffect::QQuickShaderEffect(QQuickItem *parent)
644 , m_meshResolution(1, 1)
646 , m_cullMode(NoCulling)
647 , m_status(Uncompiled)
649 , m_dirtyUniforms(true)
650 , m_dirtyUniformValues(true)
651 , m_dirtyTextureProviders(true)
652 , m_dirtyProgram(true)
653 , m_dirtyParseLog(true)
655 , m_dirtyGeometry(true)
657 setFlag(QQuickItem::ItemHasContents);
660 QQuickShaderEffect::~QQuickShaderEffect()
662 for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType)
663 m_common.disconnectPropertySignals(this, Key::ShaderType(shaderType));
667 \qmlproperty string QtQuick2::ShaderEffect::fragmentShader
669 This property holds the fragment shader's GLSL source code.
670 The default shader passes the texture coordinate along to the fragment
671 shader as "varying highp vec2 qt_TexCoord0".
674 void QQuickShaderEffect::setFragmentShader(const QByteArray &code)
676 if (m_common.source.sourceCode[Key::FragmentShader].constData() == code.constData())
678 m_common.source.sourceCode[Key::FragmentShader] = code;
679 m_dirtyProgram = true;
680 m_dirtyParseLog = true;
682 if (isComponentComplete())
683 m_common.updateShader(this, Key::FragmentShader);
686 if (m_status != Uncompiled) {
687 m_status = Uncompiled;
688 emit statusChanged();
690 emit fragmentShaderChanged();
694 \qmlproperty string QtQuick2::ShaderEffect::vertexShader
696 This property holds the vertex shader's GLSL source code.
697 The default shader expects the texture coordinate to be passed from the
698 vertex shader as "varying highp vec2 qt_TexCoord0", and it samples from a
699 sampler2D named "source".
702 void QQuickShaderEffect::setVertexShader(const QByteArray &code)
704 if (m_common.source.sourceCode[Key::VertexShader].constData() == code.constData())
706 m_common.source.sourceCode[Key::VertexShader] = code;
707 m_dirtyProgram = true;
708 m_dirtyParseLog = true;
710 if (isComponentComplete())
711 m_common.updateShader(this, Key::VertexShader);
714 if (m_status != Uncompiled) {
715 m_status = Uncompiled;
716 emit statusChanged();
718 emit vertexShaderChanged();
722 \qmlproperty bool QtQuick2::ShaderEffect::blending
724 If this property is true, the output from the \l fragmentShader is blended
725 with the background using source-over blend mode. If false, the background
726 is disregarded. Blending decreases the performance, so you should set this
727 property to false when blending is not needed. The default value is true.
730 void QQuickShaderEffect::setBlending(bool enable)
732 if (blending() == enable)
738 emit blendingChanged();
742 \qmlproperty variant QtQuick2::ShaderEffect::mesh
744 This property defines the mesh used to draw the ShaderEffect. It can hold
745 any mesh object deriving from \l QQuickShaderEffectMesh, such as \l GridMesh.
746 If a size value is assigned to this property, the ShaderEffect implicitly
747 uses a \l GridMesh with the value as
748 \l{GridMesh::resolution}{mesh resolution}. By default, this property is
754 QVariant QQuickShaderEffect::mesh() const
756 return m_mesh ? qVariantFromValue(static_cast<QObject *>(m_mesh))
757 : qVariantFromValue(m_meshResolution);
760 void QQuickShaderEffect::setMesh(const QVariant &mesh)
762 QQuickShaderEffectMesh *newMesh = qobject_cast<QQuickShaderEffectMesh *>(qvariant_cast<QObject *>(mesh));
763 if (newMesh && newMesh == m_mesh)
766 disconnect(m_mesh, SIGNAL(geometryChanged()), this, 0);
769 connect(m_mesh, SIGNAL(geometryChanged()), this, SLOT(updateGeometry()));
771 if (mesh.canConvert<QSize>()) {
772 m_meshResolution = mesh.toSize();
774 QList<QByteArray> res = mesh.toByteArray().split('x');
775 bool ok = res.size() == 2;
777 int w = res.at(0).toInt(&ok);
779 int h = res.at(1).toInt(&ok);
781 m_meshResolution = QSize(w, h);
785 qWarning("ShaderEffect: mesh property must be size or object deriving from QQuickShaderEffectMesh.");
787 m_defaultMesh.setResolution(m_meshResolution);
791 m_dirtyParseLog = true;
797 \qmlproperty enumeration QtQuick2::ShaderEffect::cullMode
799 This property defines which sides of the item should be visible.
802 \li ShaderEffect.NoCulling - Both sides are visible
803 \li ShaderEffect.BackFaceCulling - only front side is visible
804 \li ShaderEffect.FrontFaceCulling - only back side is visible
807 The default is NoCulling.
810 void QQuickShaderEffect::setCullMode(CullMode face)
812 if (face == m_cullMode)
816 emit cullModeChanged();
819 QString QQuickShaderEffect::parseLog()
821 if (m_dirtyParseLog) {
822 m_common.updateParseLog(m_mesh != 0);
823 m_dirtyParseLog = false;
825 return m_common.parseLog;
828 bool QQuickShaderEffect::event(QEvent *event)
830 if (event->type() == QEvent::DynamicPropertyChange) {
831 QDynamicPropertyChangeEvent *e = static_cast<QDynamicPropertyChangeEvent *>(event);
832 for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
833 for (int i = 0; i < m_common.uniformData[shaderType].size(); ++i) {
834 if (m_common.uniformData[shaderType].at(i).name == e->propertyName()) {
835 bool textureProviderChanged;
836 m_common.propertyChanged(this, (shaderType << 16) | i, &textureProviderChanged);
837 m_dirtyTextureProviders |= textureProviderChanged;
838 m_dirtyUniformValues = true;
844 return QQuickItem::event(event);
848 \qmlproperty enumeration QtQuick2::ShaderEffect::status
850 This property tells the current status of the OpenGL shader program.
853 \li ShaderEffect.Compiled - the shader program was successfully compiled and linked.
854 \li ShaderEffect.Uncompiled - the shader program has not yet been compiled.
855 \li ShaderEffect.Error - the shader program failed to compile or link.
858 When setting the fragment or vertex shader source code, the status will become Uncompiled.
859 The first time the ShaderEffect is rendered with new shader source code, the shaders are
860 compiled and linked, and the status is updated to Compiled or Error.
866 \qmlproperty string QtQuick2::ShaderEffect::log
868 This property holds a log of warnings and errors from the latest attempt at compiling and
869 linking the OpenGL shader program. It is updated at the same time \l status is set to Compiled
875 void QQuickShaderEffect::updateGeometry()
877 m_dirtyGeometry = true;
881 void QQuickShaderEffect::updateLogAndStatus(const QString &log, int status)
883 m_log = parseLog() + log;
884 m_status = Status(status);
886 emit statusChanged();
889 void QQuickShaderEffect::sourceDestroyed(QObject *object)
891 m_common.sourceDestroyed(object);
895 void QQuickShaderEffect::propertyChanged(int mappedId)
897 bool textureProviderChanged;
898 m_common.propertyChanged(this, mappedId, &textureProviderChanged);
899 m_dirtyTextureProviders |= textureProviderChanged;
900 m_dirtyUniformValues = true;
904 void QQuickShaderEffect::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
906 m_dirtyGeometry = true;
907 QQuickItem::geometryChanged(newGeometry, oldGeometry);
910 QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
912 QQuickShaderEffectNode *node = static_cast<QQuickShaderEffectNode *>(oldNode);
914 // In the case of a bad vertex shader, don't try to create a node...
915 if (m_common.attributes.isEmpty()) {
922 node = new QQuickShaderEffectNode;
923 node->setMaterial(new QQuickShaderEffectMaterial(node));
924 node->setFlag(QSGNode::OwnsMaterial, true);
925 m_dirtyProgram = true;
926 m_dirtyUniforms = true;
927 m_dirtyGeometry = true;
928 connect(node, SIGNAL(logAndStatusChanged(QString,int)), this, SLOT(updateLogAndStatus(QString,int)));
932 node->setGeometry(0);
934 m_dirtyGeometry = true;
937 if (m_dirtyGeometry) {
938 node->setFlag(QSGNode::OwnsGeometry, false);
939 QSGGeometry *geometry = node->geometry();
940 QRectF rect(0, 0, width(), height());
941 QQuickShaderEffectMesh *mesh = m_mesh ? m_mesh : &m_defaultMesh;
943 geometry = mesh->updateGeometry(geometry, m_common.attributes, rect);
945 QString log = mesh->log();
948 m_log += QLatin1String("*** Mesh ***\n");
952 emit statusChanged();
958 node->setGeometry(geometry);
959 node->setFlag(QSGNode::OwnsGeometry, true);
961 m_dirtyGeometry = false;
964 QQuickShaderEffectMaterial *material = static_cast<QQuickShaderEffectMaterial *>(node->material());
967 if (bool(material->flags() & QSGMaterial::Blending) != m_blending) {
968 material->setFlag(QSGMaterial::Blending, m_blending);
969 node->markDirty(QSGNode::DirtyMaterial);
972 if (int(material->cullMode) != int(m_cullMode)) {
973 material->cullMode = QQuickShaderEffectMaterial::CullMode(m_cullMode);
974 node->markDirty(QSGNode::DirtyMaterial);
977 if (m_dirtyProgram) {
978 Key s = m_common.source;
979 if (s.sourceCode[Key::FragmentShader].isEmpty())
980 s.sourceCode[Key::FragmentShader] = qt_default_fragment_code;
981 if (s.sourceCode[Key::VertexShader].isEmpty())
982 s.sourceCode[Key::VertexShader] = qt_default_vertex_code;
983 s.className = metaObject()->className();
985 material->setProgramSource(s);
986 material->attributes = m_common.attributes;
987 node->markDirty(QSGNode::DirtyMaterial);
988 m_dirtyProgram = false;
989 m_dirtyUniforms = true;
992 if (m_dirtyUniforms || m_dirtyUniformValues || m_dirtyTextureProviders) {
993 m_common.updateMaterial(node, material, m_dirtyUniforms, m_dirtyUniformValues,
994 m_dirtyTextureProviders);
995 node->markDirty(QSGNode::DirtyMaterial);
996 m_dirtyUniforms = m_dirtyUniformValues = m_dirtyTextureProviders = false;
1002 void QQuickShaderEffect::componentComplete()
1004 m_common.updateShader(this, Key::VertexShader);
1005 m_common.updateShader(this, Key::FragmentShader);
1006 QQuickItem::componentComplete();
1009 void QQuickShaderEffect::itemChange(ItemChange change, const ItemChangeData &value)
1011 if (change == QQuickItem::ItemSceneChange)
1012 m_common.updateCanvas(value.canvas);
1013 QQuickItem::itemChange(change, value);