1
/****************************************************************************
3
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4
** Contact: http://www.qt-project.org/legal
6
** This file is part of the QtQml module of the Qt Toolkit.
8
** $QT_BEGIN_LICENSE:LGPL$
9
** Commercial License Usage
10
** Licensees holding valid commercial Qt licenses may use this file in
11
** accordance with the commercial license agreement provided with the
12
** Software or, alternatively, in accordance with the terms contained in
13
** a written agreement between you and Digia. For licensing terms and
14
** conditions see http://qt.digia.com/licensing. For further information
15
** use the contact form at http://qt.digia.com/contact-us.
17
** GNU Lesser General Public License Usage
18
** Alternatively, this file may be used under the terms of the GNU Lesser
19
** General Public License version 2.1 as published by the Free Software
20
** Foundation and appearing in the file LICENSE.LGPL included in the
21
** packaging of this file. Please review the following information to
22
** ensure the GNU Lesser General Public License version 2.1 requirements
23
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25
** In addition, as a special exception, Digia gives you certain additional
26
** rights. These rights are described in the Digia Qt LGPL Exception
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29
** GNU General Public License Usage
30
** Alternatively, this file may be used under the terms of the GNU
31
** General Public License version 3.0 as published by the Free Software
32
** Foundation and appearing in the file LICENSE.GPL included in the
33
** packaging of this file. Please review the following information to
34
** ensure the GNU General Public License version 3.0 requirements will be
35
** met: http://www.gnu.org/copyleft/gpl.html.
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 "qquickwindow.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)->derefWindow();
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)->refWindow(item->window());
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::updateWindow(QQuickWindow *window)
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)->refWindow(window);
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)->derefWindow();
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)->derefWindow();
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 window 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 window from 'item'.
522
QQuickItemPrivate::get(source)->refWindow(item->window());
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
\qmltype ShaderEffect
537
\instantiates QQuickShaderEffect
538
\inqmlmodule QtQuick 2
540
\ingroup qtquick-effects
541
\brief Applies custom shaders to a rectangle
543
The ShaderEffect type applies a custom OpenGL
544
\l{vertexShader}{vertex} and \l{fragmentShader}{fragment} shader to a
545
rectangle. It allows you to write effects such as drop shadow, blur,
546
colorize and page curl directly in QML.
548
There are two types of input to the \l vertexShader:
549
uniform variables and attributes. Some are predefined:
551
\li uniform mat4 qt_Matrix - combined transformation
552
matrix, the product of the matrices from the root item to this
553
ShaderEffect, and an orthogonal projection.
554
\li uniform float qt_Opacity - combined opacity, the product of the
555
opacities from the root item to this ShaderEffect.
556
\li attribute vec4 qt_Vertex - vertex position, the top-left vertex has
557
position (0, 0), the bottom-right (\l{Item::width}{width},
558
\l{Item::height}{height}).
559
\li attribute vec2 qt_MultiTexCoord0 - texture coordinate, the top-left
560
coordinate is (0, 0), the bottom-right (1, 1).
563
In addition, any property that can be mapped to an OpenGL Shading Language
564
(GLSL) type is available as a uniform variable. The following list shows
565
how properties are mapped to GLSL uniform variables:
567
\li bool, int, qreal -> bool, int, float - If the type in the shader is not
568
the same as in QML, the value is converted automatically.
569
\li QColor -> vec4 - When colors are passed to the shader, they are first
570
premultiplied. Thus Qt.rgba(0.2, 0.6, 1.0, 0.5) becomes
571
vec4(0.1, 0.3, 0.5, 0.5) in the shader, for example.
572
\li QRect, QRectF -> vec4 - Qt.rect(x, y, w, h) becomes vec4(x, y, w, h) in
574
\li QPoint, QPointF, QSize, QSizeF -> vec2
575
\li QVector3D -> vec3
576
\li QTransform -> mat4
577
\li \l Image, \l ShaderEffectSource -> sampler2D - Origin is in the top-left
578
corner, and the color values are premultiplied.
581
The QML scene graph back-end may choose to allocate textures in texture
582
atlases. If a texture allocated in an atlas is passed to a ShaderEffect,
583
it is by default copied from the texture atlas into a stand-alone texture
584
so that the texture coordinates span from 0 to 1, and you get the expected
585
wrap modes. However, this will increase the memory usage. To avoid the
586
texture copy, you can for each "uniform sampler2D <name>" declare a
587
"uniform vec4 qt_SubRect_<name>" which will be assigned the texture's
588
normalized source rectangle. For stand-alone textures, the source rectangle
589
is [0, 1]x[0, 1]. For textures in an atlas, the source rectangle corresponds
590
to the part of the texture atlas where the texture is stored.
591
The correct way to calculate the texture coordinate for a texture called
592
"source" within a texture atlas is
593
"qt_SubRect_source.xy + qt_SubRect_source.zw * qt_MultiTexCoord0".
595
The output from the \l fragmentShader should be premultiplied. If
596
\l blending is enabled, source-over blending is used. However, additive
597
blending can be achieved by outputting zero in the alpha channel.
601
\li \image declarative-shadereffectitem.png
606
width: 200; height: 100
608
Image { id: img; sourceSize { width: 100; height: 100 } source: "qt-logo.png" }
610
width: 100; height: 100
611
property variant src: img
613
uniform highp mat4 qt_Matrix;
614
attribute highp vec4 qt_Vertex;
615
attribute highp vec2 qt_MultiTexCoord0;
616
varying highp vec2 coord;
618
coord = qt_MultiTexCoord0;
619
gl_Position = qt_Matrix * qt_Vertex;
622
varying highp vec2 coord;
623
uniform sampler2D src;
624
uniform lowp float qt_Opacity;
626
lowp vec4 tex = texture2D(src, coord);
627
gl_FragColor = vec4(vec3(dot(tex.rgb, vec3(0.344, 0.5, 0.156))), tex.a) * qt_Opacity;
635
By default, the ShaderEffect consists of four vertices, one for each
636
corner. For non-linear vertex transformations, like page curl, you can
637
specify a fine grid of vertices by specifying a \l mesh resolution.
639
\note Scene Graph textures have origin in the top-left corner rather than
640
bottom-left which is common in OpenGL.
643
QQuickShaderEffect::QQuickShaderEffect(QQuickItem *parent)
645
, m_meshResolution(1, 1)
647
, m_cullMode(NoCulling)
648
, m_status(Uncompiled)
650
, m_dirtyUniforms(true)
651
, m_dirtyUniformValues(true)
652
, m_dirtyTextureProviders(true)
653
, m_dirtyProgram(true)
654
, m_dirtyParseLog(true)
656
, m_dirtyGeometry(true)
658
setFlag(QQuickItem::ItemHasContents);
661
QQuickShaderEffect::~QQuickShaderEffect()
663
for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType)
664
m_common.disconnectPropertySignals(this, Key::ShaderType(shaderType));
668
\qmlproperty string QtQuick2::ShaderEffect::fragmentShader
670
This property holds the fragment shader's GLSL source code.
671
The default shader passes the texture coordinate along to the fragment
672
shader as "varying highp vec2 qt_TexCoord0".
675
void QQuickShaderEffect::setFragmentShader(const QByteArray &code)
677
if (m_common.source.sourceCode[Key::FragmentShader].constData() == code.constData())
679
m_common.source.sourceCode[Key::FragmentShader] = code;
680
m_dirtyProgram = true;
681
m_dirtyParseLog = true;
683
if (isComponentComplete())
684
m_common.updateShader(this, Key::FragmentShader);
687
if (m_status != Uncompiled) {
688
m_status = Uncompiled;
689
emit statusChanged();
691
emit fragmentShaderChanged();
695
\qmlproperty string QtQuick2::ShaderEffect::vertexShader
697
This property holds the vertex shader's GLSL source code.
698
The default shader expects the texture coordinate to be passed from the
699
vertex shader as "varying highp vec2 qt_TexCoord0", and it samples from a
700
sampler2D named "source".
703
void QQuickShaderEffect::setVertexShader(const QByteArray &code)
705
if (m_common.source.sourceCode[Key::VertexShader].constData() == code.constData())
707
m_common.source.sourceCode[Key::VertexShader] = code;
708
m_dirtyProgram = true;
709
m_dirtyParseLog = true;
711
if (isComponentComplete())
712
m_common.updateShader(this, Key::VertexShader);
715
if (m_status != Uncompiled) {
716
m_status = Uncompiled;
717
emit statusChanged();
719
emit vertexShaderChanged();
723
\qmlproperty bool QtQuick2::ShaderEffect::blending
725
If this property is true, the output from the \l fragmentShader is blended
726
with the background using source-over blend mode. If false, the background
727
is disregarded. Blending decreases the performance, so you should set this
728
property to false when blending is not needed. The default value is true.
731
void QQuickShaderEffect::setBlending(bool enable)
733
if (blending() == enable)
739
emit blendingChanged();
743
\qmlproperty variant QtQuick2::ShaderEffect::mesh
745
This property defines the mesh used to draw the ShaderEffect. It can hold
746
any \l GridMesh object.
747
If a size value is assigned to this property, the ShaderEffect implicitly
748
uses a \l GridMesh with the value as
749
\l{GridMesh::resolution}{mesh resolution}. By default, this property is
755
QVariant QQuickShaderEffect::mesh() const
757
return m_mesh ? qVariantFromValue(static_cast<QObject *>(m_mesh))
758
: qVariantFromValue(m_meshResolution);
761
void QQuickShaderEffect::setMesh(const QVariant &mesh)
763
QQuickShaderEffectMesh *newMesh = qobject_cast<QQuickShaderEffectMesh *>(qvariant_cast<QObject *>(mesh));
764
if (newMesh && newMesh == m_mesh)
767
disconnect(m_mesh, SIGNAL(geometryChanged()), this, 0);
770
connect(m_mesh, SIGNAL(geometryChanged()), this, SLOT(updateGeometry()));
772
if (mesh.canConvert<QSize>()) {
773
m_meshResolution = mesh.toSize();
775
QList<QByteArray> res = mesh.toByteArray().split('x');
776
bool ok = res.size() == 2;
778
int w = res.at(0).toInt(&ok);
780
int h = res.at(1).toInt(&ok);
782
m_meshResolution = QSize(w, h);
786
qWarning("ShaderEffect: mesh property must be size or object deriving from QQuickShaderEffectMesh.");
788
m_defaultMesh.setResolution(m_meshResolution);
792
m_dirtyParseLog = true;
798
\qmlproperty enumeration QtQuick2::ShaderEffect::cullMode
800
This property defines which sides of the item should be visible.
803
\li ShaderEffect.NoCulling - Both sides are visible
804
\li ShaderEffect.BackFaceCulling - only front side is visible
805
\li ShaderEffect.FrontFaceCulling - only back side is visible
808
The default is NoCulling.
811
void QQuickShaderEffect::setCullMode(CullMode face)
813
if (face == m_cullMode)
817
emit cullModeChanged();
820
QString QQuickShaderEffect::parseLog()
822
if (m_dirtyParseLog) {
823
m_common.updateParseLog(m_mesh != 0);
824
m_dirtyParseLog = false;
826
return m_common.parseLog;
829
bool QQuickShaderEffect::event(QEvent *event)
831
if (event->type() == QEvent::DynamicPropertyChange) {
832
QDynamicPropertyChangeEvent *e = static_cast<QDynamicPropertyChangeEvent *>(event);
833
for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
834
for (int i = 0; i < m_common.uniformData[shaderType].size(); ++i) {
835
if (m_common.uniformData[shaderType].at(i).name == e->propertyName()) {
836
bool textureProviderChanged;
837
m_common.propertyChanged(this, (shaderType << 16) | i, &textureProviderChanged);
838
m_dirtyTextureProviders |= textureProviderChanged;
839
m_dirtyUniformValues = true;
845
return QQuickItem::event(event);
849
\qmlproperty enumeration QtQuick2::ShaderEffect::status
851
This property tells the current status of the OpenGL shader program.
854
\li ShaderEffect.Compiled - the shader program was successfully compiled and linked.
855
\li ShaderEffect.Uncompiled - the shader program has not yet been compiled.
856
\li ShaderEffect.Error - the shader program failed to compile or link.
859
When setting the fragment or vertex shader source code, the status will become Uncompiled.
860
The first time the ShaderEffect is rendered with new shader source code, the shaders are
861
compiled and linked, and the status is updated to Compiled or Error.
867
\qmlproperty string QtQuick2::ShaderEffect::log
869
This property holds a log of warnings and errors from the latest attempt at compiling and
870
linking the OpenGL shader program. It is updated at the same time \l status is set to Compiled
876
void QQuickShaderEffect::updateGeometry()
878
m_dirtyGeometry = true;
882
void QQuickShaderEffect::updateLogAndStatus(const QString &log, int status)
884
m_log = parseLog() + log;
885
m_status = Status(status);
887
emit statusChanged();
890
void QQuickShaderEffect::sourceDestroyed(QObject *object)
892
m_common.sourceDestroyed(object);
896
void QQuickShaderEffect::propertyChanged(int mappedId)
898
bool textureProviderChanged;
899
m_common.propertyChanged(this, mappedId, &textureProviderChanged);
900
m_dirtyTextureProviders |= textureProviderChanged;
901
m_dirtyUniformValues = true;
905
void QQuickShaderEffect::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
907
m_dirtyGeometry = true;
908
QQuickItem::geometryChanged(newGeometry, oldGeometry);
911
QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
913
QQuickShaderEffectNode *node = static_cast<QQuickShaderEffectNode *>(oldNode);
915
// In the case of a bad vertex shader, don't try to create a node...
916
if (m_common.attributes.isEmpty()) {
923
node = new QQuickShaderEffectNode;
924
node->setMaterial(new QQuickShaderEffectMaterial(node));
925
node->setFlag(QSGNode::OwnsMaterial, true);
926
m_dirtyProgram = true;
927
m_dirtyUniforms = true;
928
m_dirtyGeometry = true;
929
connect(node, SIGNAL(logAndStatusChanged(QString,int)), this, SLOT(updateLogAndStatus(QString,int)));
933
node->setGeometry(0);
935
m_dirtyGeometry = true;
938
if (m_dirtyGeometry) {
939
node->setFlag(QSGNode::OwnsGeometry, false);
940
QSGGeometry *geometry = node->geometry();
941
QRectF rect(0, 0, width(), height());
942
QQuickShaderEffectMesh *mesh = m_mesh ? m_mesh : &m_defaultMesh;
944
geometry = mesh->updateGeometry(geometry, m_common.attributes, rect);
946
QString log = mesh->log();
949
m_log += QLatin1String("*** Mesh ***\n");
953
emit statusChanged();
959
node->setGeometry(geometry);
960
node->setFlag(QSGNode::OwnsGeometry, true);
962
m_dirtyGeometry = false;
965
QQuickShaderEffectMaterial *material = static_cast<QQuickShaderEffectMaterial *>(node->material());
968
if (bool(material->flags() & QSGMaterial::Blending) != m_blending) {
969
material->setFlag(QSGMaterial::Blending, m_blending);
970
node->markDirty(QSGNode::DirtyMaterial);
973
if (int(material->cullMode) != int(m_cullMode)) {
974
material->cullMode = QQuickShaderEffectMaterial::CullMode(m_cullMode);
975
node->markDirty(QSGNode::DirtyMaterial);
978
if (m_dirtyProgram) {
979
Key s = m_common.source;
980
if (s.sourceCode[Key::FragmentShader].isEmpty())
981
s.sourceCode[Key::FragmentShader] = qt_default_fragment_code;
982
if (s.sourceCode[Key::VertexShader].isEmpty())
983
s.sourceCode[Key::VertexShader] = qt_default_vertex_code;
984
s.className = metaObject()->className();
986
material->setProgramSource(s);
987
material->attributes = m_common.attributes;
988
node->markDirty(QSGNode::DirtyMaterial);
989
m_dirtyProgram = false;
990
m_dirtyUniforms = true;
993
if (m_dirtyUniforms || m_dirtyUniformValues || m_dirtyTextureProviders) {
994
m_common.updateMaterial(node, material, m_dirtyUniforms, m_dirtyUniformValues,
995
m_dirtyTextureProviders);
996
node->markDirty(QSGNode::DirtyMaterial);
997
m_dirtyUniforms = m_dirtyUniformValues = m_dirtyTextureProviders = false;
1003
void QQuickShaderEffect::componentComplete()
1005
m_common.updateShader(this, Key::VertexShader);
1006
m_common.updateShader(this, Key::FragmentShader);
1007
QQuickItem::componentComplete();
1010
void QQuickShaderEffect::itemChange(ItemChange change, const ItemChangeData &value)
1012
if (change == QQuickItem::ItemSceneChange)
1013
m_common.updateWindow(value.window);
1014
QQuickItem::itemChange(change, value);