~ubuntu-branches/ubuntu/saucy/qtdeclarative-opensource-src/saucy

« back to all changes in this revision

Viewing changes to src/quick/items/qquickshadereffect.cpp

  • Committer: Package Import Robot
  • Author(s): Timo Jyrinki
  • Date: 2013-02-05 14:17:19 UTC
  • Revision ID: package-import@ubuntu.com-20130205141719-qqeyml8wslpyez52
Tags: upstream-5.0.1
ImportĀ upstreamĀ versionĀ 5.0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
 
4
** Contact: http://www.qt-project.org/legal
 
5
**
 
6
** This file is part of the QtQml module of the Qt Toolkit.
 
7
**
 
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.
 
16
**
 
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.
 
24
**
 
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.
 
28
**
 
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.
 
36
**
 
37
**
 
38
** $QT_END_LICENSE$
 
39
**
 
40
****************************************************************************/
 
41
 
 
42
#include <private/qquickshadereffect_p.h>
 
43
#include <private/qquickshadereffectnode_p.h>
 
44
 
 
45
#include <QtQuick/qsgmaterial.h>
 
46
#include "qquickitem_p.h"
 
47
 
 
48
#include <QtQuick/private/qsgcontext_p.h>
 
49
#include <QtQuick/qsgtextureprovider.h>
 
50
#include "qquickwindow.h"
 
51
 
 
52
#include "qquickimage_p.h"
 
53
#include "qquickshadereffectsource_p.h"
 
54
 
 
55
#include <QtCore/qsignalmapper.h>
 
56
#include <QtGui/qopenglframebufferobject.h>
 
57
 
 
58
QT_BEGIN_NAMESPACE
 
59
 
 
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"
 
65
    "void main() {                                                  \n"
 
66
    "    qt_TexCoord0 = qt_MultiTexCoord0;                          \n"
 
67
    "    gl_Position = qt_Matrix * qt_Vertex;                       \n"
 
68
    "}";
 
69
 
 
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"
 
74
    "void main() {                                                      \n"
 
75
    "    gl_FragColor = texture2D(source, qt_TexCoord0) * qt_Opacity;   \n"
 
76
    "}";
 
77
 
 
78
static const char qt_position_attribute_name[] = "qt_Vertex";
 
79
static const char qt_texcoord_attribute_name[] = "qt_MultiTexCoord0";
 
80
 
 
81
const char *qtPositionAttributeName()
 
82
{
 
83
    return qt_position_attribute_name;
 
84
}
 
85
 
 
86
const char *qtTexCoordAttributeName()
 
87
{
 
88
    return qt_texcoord_attribute_name;
 
89
}
 
90
 
 
91
namespace {
 
92
 
 
93
    enum VariableQualifier {
 
94
        AttributeQualifier,
 
95
        UniformQualifier
 
96
    };
 
97
 
 
98
    inline bool qt_isalpha(char c)
 
99
    {
 
100
        char ch = c | 0x20;
 
101
        return (ch >= 'a' && ch <= 'z') || c == '_';
 
102
    }
 
103
 
 
104
    inline bool qt_isalnum(char c)
 
105
    {
 
106
        return qt_isalpha(c) || (c >= '0' && c <= '9');
 
107
    }
 
108
 
 
109
    inline bool qt_isspace(char c)
 
110
    {
 
111
        return c == ' ' || (c >= 0x09 && c <= 0x0d);
 
112
    }
 
113
 
 
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)
 
118
    {
 
119
        enum Identifier {
 
120
            QualifierIdentifier, // Base state
 
121
            PrecisionIdentifier,
 
122
            TypeIdentifier,
 
123
            NameIdentifier
 
124
        };
 
125
        Identifier expected = QualifierIdentifier;
 
126
        bool compilerDirectiveExpected = index == 0;
 
127
 
 
128
        while (index < length) {
 
129
            // Skip whitespace.
 
130
            while (qt_isspace(s[index])) {
 
131
                compilerDirectiveExpected |= s[index] == '\n';
 
132
                ++index;
 
133
            }
 
134
 
 
135
            if (qt_isalpha(s[index])) {
 
136
                // Read identifier.
 
137
                int idIndex = index;
 
138
                ++index;
 
139
                while (qt_isalnum(s[index]))
 
140
                    ++index;
 
141
                int idLength = index - idIndex;
 
142
 
 
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;
 
148
 
 
149
                switch (expected) {
 
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;
 
157
                    }
 
158
                    break;
 
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))
 
163
                    {
 
164
                        expected = TypeIdentifier;
 
165
                        break;
 
166
                    }
 
167
                    // Fall through.
 
168
                case TypeIdentifier:
 
169
                    typeIndex = idIndex;
 
170
                    typeLength = idLength;
 
171
                    expected = NameIdentifier;
 
172
                    break;
 
173
                case NameIdentifier:
 
174
                    nameIndex = idIndex;
 
175
                    nameLength = idLength;
 
176
                    return index; // Attribute or uniform declaration found. Return result.
 
177
                default:
 
178
                    break;
 
179
                }
 
180
            } else if (s[index] == '#' && compilerDirectiveExpected) {
 
181
                // Skip compiler directives.
 
182
                ++index;
 
183
                while (index < length && (s[index] != '\n' || s[index - 1] == '\\'))
 
184
                    ++index;
 
185
            } else if (s[index] == '/' && s[index + 1] == '/') {
 
186
                // Skip comments.
 
187
                index += 2;
 
188
                while (index < length && s[index] != '\n')
 
189
                    ++index;
 
190
            } else if (s[index] == '/' && s[index + 1] == '*') {
 
191
                // Skip comments.
 
192
                index += 2;
 
193
                while (index < length && (s[index] != '*' || s[index + 1] != '/'))
 
194
                    ++index;
 
195
                if (index < length)
 
196
                    index += 2; // Skip star-slash.
 
197
            } else {
 
198
                expected = QualifierIdentifier;
 
199
                ++index;
 
200
            }
 
201
            compilerDirectiveExpected = false;
 
202
        }
 
203
        return -1;
 
204
    }
 
205
}
 
206
 
 
207
 
 
208
 
 
209
QQuickShaderEffectCommon::~QQuickShaderEffectCommon()
 
210
{
 
211
    for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType)
 
212
        qDeleteAll(signalMappers[shaderType]);
 
213
}
 
214
 
 
215
void QQuickShaderEffectCommon::disconnectPropertySignals(QQuickItem *item, Key::ShaderType shaderType)
 
216
{
 
217
    for (int i = 0; i < uniformData[shaderType].size(); ++i) {
 
218
        if (signalMappers[shaderType].at(i) == 0)
 
219
            continue;
 
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));
 
226
            if (source) {
 
227
                if (item->window())
 
228
                    QQuickItemPrivate::get(source)->derefWindow();
 
229
                QObject::disconnect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*)));
 
230
            }
 
231
        }
 
232
    }
 
233
}
 
234
 
 
235
void QQuickShaderEffectCommon::connectPropertySignals(QQuickItem *item, Key::ShaderType shaderType)
 
236
{
 
237
    for (int i = 0; i < uniformData[shaderType].size(); ++i) {
 
238
        if (signalMappers[shaderType].at(i) == 0)
 
239
            continue;
 
240
        const UniformData &d = uniformData[shaderType].at(i);
 
241
        int pi = item->metaObject()->indexOfProperty(d.name.constData());
 
242
        if (pi >= 0) {
 
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)));
 
250
        } else {
 
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());
 
255
        }
 
256
 
 
257
        if (d.specialType == UniformData::Sampler) {
 
258
            QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
 
259
            if (source) {
 
260
                if (item->window())
 
261
                    QQuickItemPrivate::get(source)->refWindow(item->window());
 
262
                QObject::connect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*)));
 
263
            }
 
264
        }
 
265
    }
 
266
}
 
267
 
 
268
void QQuickShaderEffectCommon::updateParseLog(bool ignoreAttributes)
 
269
{
 
270
    parseLog.clear();
 
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");
 
276
        }
 
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");
 
281
        }
 
282
    }
 
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;
 
290
    }
 
291
    if (!respectsMatrix)
 
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");
 
295
}
 
296
 
 
297
void QQuickShaderEffectCommon::lookThroughShaderCode(QQuickItem *item, Key::ShaderType shaderType, const QByteArray &code)
 
298
{
 
299
    int index = 0;
 
300
    int typeIndex = -1;
 
301
    int typeLength = 0;
 
302
    int nameIndex = -1;
 
303
    int nameLength = 0;
 
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)
 
308
    {
 
309
        if (decl == AttributeQualifier) {
 
310
            if (shaderType == Key::VertexShader)
 
311
                attributes.append(QByteArray(s + nameIndex, nameLength));
 
312
        } else {
 
313
            Q_ASSERT(decl == UniformQualifier);
 
314
 
 
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;
 
319
 
 
320
            UniformData d;
 
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;
 
329
            } else {
 
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;
 
335
            }
 
336
            uniformData[shaderType].append(d);
 
337
            signalMappers[shaderType].append(mapper);
 
338
        }
 
339
    }
 
340
}
 
341
 
 
342
void QQuickShaderEffectCommon::updateShader(QQuickItem *item, Key::ShaderType shaderType)
 
343
{
 
344
    disconnectPropertySignals(item, shaderType);
 
345
    qDeleteAll(signalMappers[shaderType]);
 
346
    uniformData[shaderType].clear();
 
347
    signalMappers[shaderType].clear();
 
348
    if (shaderType == Key::VertexShader)
 
349
        attributes.clear();
 
350
 
 
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));
 
357
            UniformData d;
 
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) {
 
363
            UniformData d;
 
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";
 
371
            d.name = sourceName;
 
372
            d.value = item->property(sourceName);
 
373
            d.specialType = UniformData::Sampler;
 
374
            uniformData[Key::FragmentShader].append(d);
 
375
            signalMappers[Key::FragmentShader].append(mapper);
 
376
        }
 
377
    } else {
 
378
        lookThroughShaderCode(item, shaderType, code);
 
379
    }
 
380
 
 
381
    connectPropertySignals(item, shaderType);
 
382
}
 
383
 
 
384
void QQuickShaderEffectCommon::updateMaterial(QQuickShaderEffectNode *node,
 
385
                                              QQuickShaderEffectMaterial *material,
 
386
                                              bool updateUniforms, bool updateUniformValues,
 
387
                                              bool updateTextureProviders)
 
388
{
 
389
    if (updateUniforms) {
 
390
        for (int i = 0; i < material->textureProviders.size(); ++i) {
 
391
            QSGTextureProvider *t = material->textureProviders.at(i);
 
392
            if (t) {
 
393
                QObject::disconnect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
 
394
                QObject::disconnect(t, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*)));
 
395
            }
 
396
        }
 
397
 
 
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;
 
404
            }
 
405
            material->uniforms[shaderType] = uniformData[shaderType];
 
406
        }
 
407
        material->textureProviders.fill(0, textureProviderCount);
 
408
        updateUniformValues = false;
 
409
        updateTextureProviders = true;
 
410
    }
 
411
 
 
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;
 
417
        }
 
418
    }
 
419
 
 
420
    if (updateTextureProviders) {
 
421
        int index = 0;
 
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)
 
426
                    continue;
 
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) {
 
433
                    if (oldProvider) {
 
434
                        QObject::disconnect(oldProvider, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
 
435
                        QObject::disconnect(oldProvider, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*)));
 
436
                    }
 
437
                    if (newProvider) {
 
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*)));
 
443
                    } else {
 
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);
 
447
                    }
 
448
                    material->textureProviders[index] = newProvider;
 
449
                }
 
450
                ++index;
 
451
            }
 
452
        }
 
453
        Q_ASSERT(index == material->textureProviders.size());
 
454
    }
 
455
}
 
456
 
 
457
void QQuickShaderEffectCommon::updateWindow(QQuickWindow *window)
 
458
{
 
459
    // See comment in QQuickShaderEffectCommon::propertyChanged().
 
460
    if (window) {
 
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));
 
466
                    if (source)
 
467
                        QQuickItemPrivate::get(source)->refWindow(window);
 
468
                }
 
469
            }
 
470
        }
 
471
    } else {
 
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));
 
477
                    if (source)
 
478
                        QQuickItemPrivate::get(source)->derefWindow();
 
479
                }
 
480
            }
 
481
        }
 
482
    }
 
483
}
 
484
 
 
485
void QQuickShaderEffectCommon::sourceDestroyed(QObject *object)
 
486
{
 
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();
 
493
            }
 
494
        }
 
495
    }
 
496
}
 
497
 
 
498
 
 
499
void QQuickShaderEffectCommon::propertyChanged(QQuickItem *item, int mappedId,
 
500
                                               bool *textureProviderChanged)
 
501
{
 
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));
 
507
        if (source) {
 
508
            if (item->window())
 
509
                QQuickItemPrivate::get(source)->derefWindow();
 
510
            QObject::disconnect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*)));
 
511
        }
 
512
 
 
513
        d.value = item->property(d.name.constData());
 
514
 
 
515
        source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
 
516
        if (source) {
 
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'.
 
521
            if (item->window())
 
522
                QQuickItemPrivate::get(source)->refWindow(item->window());
 
523
            QObject::connect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*)));
 
524
        }
 
525
        if (textureProviderChanged)
 
526
            *textureProviderChanged = true;
 
527
    } else {
 
528
        d.value = item->property(d.name.constData());
 
529
        if (textureProviderChanged)
 
530
            *textureProviderChanged = false;
 
531
    }
 
532
}
 
533
 
 
534
 
 
535
/*!
 
536
    \qmltype ShaderEffect
 
537
    \instantiates QQuickShaderEffect
 
538
    \inqmlmodule QtQuick 2
 
539
    \inherits Item
 
540
    \ingroup qtquick-effects
 
541
    \brief Applies custom shaders to a rectangle
 
542
 
 
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.
 
547
 
 
548
    There are two types of input to the \l vertexShader:
 
549
    uniform variables and attributes. Some are predefined:
 
550
    \list
 
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).
 
561
    \endlist
 
562
 
 
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:
 
566
    \list
 
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
 
573
       the shader.
 
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.
 
579
    \endlist
 
580
 
 
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".
 
594
 
 
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.
 
598
 
 
599
    \table
 
600
    \row
 
601
    \li \image declarative-shadereffectitem.png
 
602
    \li \qml
 
603
        import QtQuick 2.0
 
604
 
 
605
        Rectangle {
 
606
            width: 200; height: 100
 
607
            Row {
 
608
                Image { id: img; sourceSize { width: 100; height: 100 } source: "qt-logo.png" }
 
609
                ShaderEffect {
 
610
                    width: 100; height: 100
 
611
                    property variant src: img
 
612
                    vertexShader: "
 
613
                        uniform highp mat4 qt_Matrix;
 
614
                        attribute highp vec4 qt_Vertex;
 
615
                        attribute highp vec2 qt_MultiTexCoord0;
 
616
                        varying highp vec2 coord;
 
617
                        void main() {
 
618
                            coord = qt_MultiTexCoord0;
 
619
                            gl_Position = qt_Matrix * qt_Vertex;
 
620
                        }"
 
621
                    fragmentShader: "
 
622
                        varying highp vec2 coord;
 
623
                        uniform sampler2D src;
 
624
                        uniform lowp float qt_Opacity;
 
625
                        void main() {
 
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;
 
628
                        }"
 
629
                }
 
630
            }
 
631
        }
 
632
        \endqml
 
633
    \endtable
 
634
 
 
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.
 
638
 
 
639
    \note Scene Graph textures have origin in the top-left corner rather than
 
640
    bottom-left which is common in OpenGL.
 
641
*/
 
642
 
 
643
QQuickShaderEffect::QQuickShaderEffect(QQuickItem *parent)
 
644
    : QQuickItem(parent)
 
645
    , m_meshResolution(1, 1)
 
646
    , m_mesh(0)
 
647
    , m_cullMode(NoCulling)
 
648
    , m_status(Uncompiled)
 
649
    , m_blending(true)
 
650
    , m_dirtyUniforms(true)
 
651
    , m_dirtyUniformValues(true)
 
652
    , m_dirtyTextureProviders(true)
 
653
    , m_dirtyProgram(true)
 
654
    , m_dirtyParseLog(true)
 
655
    , m_dirtyMesh(true)
 
656
    , m_dirtyGeometry(true)
 
657
{
 
658
    setFlag(QQuickItem::ItemHasContents);
 
659
}
 
660
 
 
661
QQuickShaderEffect::~QQuickShaderEffect()
 
662
{
 
663
    for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType)
 
664
        m_common.disconnectPropertySignals(this, Key::ShaderType(shaderType));
 
665
}
 
666
 
 
667
/*!
 
668
    \qmlproperty string QtQuick2::ShaderEffect::fragmentShader
 
669
 
 
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".
 
673
*/
 
674
 
 
675
void QQuickShaderEffect::setFragmentShader(const QByteArray &code)
 
676
{
 
677
    if (m_common.source.sourceCode[Key::FragmentShader].constData() == code.constData())
 
678
        return;
 
679
    m_common.source.sourceCode[Key::FragmentShader] = code;
 
680
    m_dirtyProgram = true;
 
681
    m_dirtyParseLog = true;
 
682
 
 
683
    if (isComponentComplete())
 
684
        m_common.updateShader(this, Key::FragmentShader);
 
685
 
 
686
    update();
 
687
    if (m_status != Uncompiled) {
 
688
        m_status = Uncompiled;
 
689
        emit statusChanged();
 
690
    }
 
691
    emit fragmentShaderChanged();
 
692
}
 
693
 
 
694
/*!
 
695
    \qmlproperty string QtQuick2::ShaderEffect::vertexShader
 
696
 
 
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".
 
701
*/
 
702
 
 
703
void QQuickShaderEffect::setVertexShader(const QByteArray &code)
 
704
{
 
705
    if (m_common.source.sourceCode[Key::VertexShader].constData() == code.constData())
 
706
        return;
 
707
    m_common.source.sourceCode[Key::VertexShader] = code;
 
708
    m_dirtyProgram = true;
 
709
    m_dirtyParseLog = true;
 
710
 
 
711
    if (isComponentComplete())
 
712
        m_common.updateShader(this, Key::VertexShader);
 
713
 
 
714
    update();
 
715
    if (m_status != Uncompiled) {
 
716
        m_status = Uncompiled;
 
717
        emit statusChanged();
 
718
    }
 
719
    emit vertexShaderChanged();
 
720
}
 
721
 
 
722
/*!
 
723
    \qmlproperty bool QtQuick2::ShaderEffect::blending
 
724
 
 
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.
 
729
*/
 
730
 
 
731
void QQuickShaderEffect::setBlending(bool enable)
 
732
{
 
733
    if (blending() == enable)
 
734
        return;
 
735
 
 
736
    m_blending = enable;
 
737
    update();
 
738
 
 
739
    emit blendingChanged();
 
740
}
 
741
 
 
742
/*!
 
743
    \qmlproperty variant QtQuick2::ShaderEffect::mesh
 
744
 
 
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
 
750
    the size 1x1.
 
751
 
 
752
    \sa GridMesh
 
753
*/
 
754
 
 
755
QVariant QQuickShaderEffect::mesh() const
 
756
{
 
757
    return m_mesh ? qVariantFromValue(static_cast<QObject *>(m_mesh))
 
758
                  : qVariantFromValue(m_meshResolution);
 
759
}
 
760
 
 
761
void QQuickShaderEffect::setMesh(const QVariant &mesh)
 
762
{
 
763
    QQuickShaderEffectMesh *newMesh = qobject_cast<QQuickShaderEffectMesh *>(qvariant_cast<QObject *>(mesh));
 
764
    if (newMesh && newMesh == m_mesh)
 
765
        return;
 
766
    if (m_mesh)
 
767
        disconnect(m_mesh, SIGNAL(geometryChanged()), this, 0);
 
768
    m_mesh = newMesh;
 
769
    if (m_mesh) {
 
770
        connect(m_mesh, SIGNAL(geometryChanged()), this, SLOT(updateGeometry()));
 
771
    } else {
 
772
        if (mesh.canConvert<QSize>()) {
 
773
            m_meshResolution = mesh.toSize();
 
774
        } else {
 
775
            QList<QByteArray> res = mesh.toByteArray().split('x');
 
776
            bool ok = res.size() == 2;
 
777
            if (ok) {
 
778
                int w = res.at(0).toInt(&ok);
 
779
                if (ok) {
 
780
                    int h = res.at(1).toInt(&ok);
 
781
                    if (ok)
 
782
                        m_meshResolution = QSize(w, h);
 
783
                }
 
784
            }
 
785
            if (!ok)
 
786
                qWarning("ShaderEffect: mesh property must be size or object deriving from QQuickShaderEffectMesh.");
 
787
        }
 
788
        m_defaultMesh.setResolution(m_meshResolution);
 
789
    }
 
790
 
 
791
    m_dirtyMesh = true;
 
792
    m_dirtyParseLog = true;
 
793
    update();
 
794
    emit meshChanged();
 
795
}
 
796
 
 
797
/*!
 
798
    \qmlproperty enumeration QtQuick2::ShaderEffect::cullMode
 
799
 
 
800
    This property defines which sides of the item should be visible.
 
801
 
 
802
    \list
 
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
 
806
    \endlist
 
807
 
 
808
    The default is NoCulling.
 
809
*/
 
810
 
 
811
void QQuickShaderEffect::setCullMode(CullMode face)
 
812
{
 
813
    if (face == m_cullMode)
 
814
        return;
 
815
    m_cullMode = face;
 
816
    update();
 
817
    emit cullModeChanged();
 
818
}
 
819
 
 
820
QString QQuickShaderEffect::parseLog()
 
821
{
 
822
    if (m_dirtyParseLog) {
 
823
        m_common.updateParseLog(m_mesh != 0);
 
824
        m_dirtyParseLog = false;
 
825
    }
 
826
    return m_common.parseLog;
 
827
}
 
828
 
 
829
bool QQuickShaderEffect::event(QEvent *event)
 
830
{
 
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;
 
840
                    update();
 
841
                }
 
842
            }
 
843
        }
 
844
    }
 
845
    return QQuickItem::event(event);
 
846
}
 
847
 
 
848
/*!
 
849
    \qmlproperty enumeration QtQuick2::ShaderEffect::status
 
850
 
 
851
    This property tells the current status of the OpenGL shader program.
 
852
 
 
853
    \list
 
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.
 
857
    \endlist
 
858
 
 
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.
 
862
 
 
863
    \sa log
 
864
*/
 
865
 
 
866
/*!
 
867
    \qmlproperty string QtQuick2::ShaderEffect::log
 
868
 
 
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
 
871
    or Error.
 
872
 
 
873
    \sa status
 
874
*/
 
875
 
 
876
void QQuickShaderEffect::updateGeometry()
 
877
{
 
878
    m_dirtyGeometry = true;
 
879
    update();
 
880
}
 
881
 
 
882
void QQuickShaderEffect::updateLogAndStatus(const QString &log, int status)
 
883
{
 
884
    m_log = parseLog() + log;
 
885
    m_status = Status(status);
 
886
    emit logChanged();
 
887
    emit statusChanged();
 
888
}
 
889
 
 
890
void QQuickShaderEffect::sourceDestroyed(QObject *object)
 
891
{
 
892
    m_common.sourceDestroyed(object);
 
893
}
 
894
 
 
895
 
 
896
void QQuickShaderEffect::propertyChanged(int mappedId)
 
897
{
 
898
    bool textureProviderChanged;
 
899
    m_common.propertyChanged(this, mappedId, &textureProviderChanged);
 
900
    m_dirtyTextureProviders |= textureProviderChanged;
 
901
    m_dirtyUniformValues = true;
 
902
    update();
 
903
}
 
904
 
 
905
void QQuickShaderEffect::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
 
906
{
 
907
    m_dirtyGeometry = true;
 
908
    QQuickItem::geometryChanged(newGeometry, oldGeometry);
 
909
}
 
910
 
 
911
QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
 
912
{
 
913
    QQuickShaderEffectNode *node = static_cast<QQuickShaderEffectNode *>(oldNode);
 
914
 
 
915
    // In the case of a bad vertex shader, don't try to create a node...
 
916
    if (m_common.attributes.isEmpty()) {
 
917
        if (node)
 
918
            delete node;
 
919
        return 0;
 
920
    }
 
921
 
 
922
    if (!node) {
 
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)));
 
930
    }
 
931
 
 
932
    if (m_dirtyMesh) {
 
933
        node->setGeometry(0);
 
934
        m_dirtyMesh = false;
 
935
        m_dirtyGeometry = true;
 
936
    }
 
937
 
 
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;
 
943
 
 
944
        geometry = mesh->updateGeometry(geometry, m_common.attributes, rect);
 
945
        if (!geometry) {
 
946
            QString log = mesh->log();
 
947
            if (!log.isNull()) {
 
948
                m_log = parseLog();
 
949
                m_log += QLatin1String("*** Mesh ***\n");
 
950
                m_log += log;
 
951
                m_status = Error;
 
952
                emit logChanged();
 
953
                emit statusChanged();
 
954
            }
 
955
            delete node;
 
956
            return 0;
 
957
        }
 
958
 
 
959
        node->setGeometry(geometry);
 
960
        node->setFlag(QSGNode::OwnsGeometry, true);
 
961
 
 
962
        m_dirtyGeometry = false;
 
963
    }
 
964
 
 
965
    QQuickShaderEffectMaterial *material = static_cast<QQuickShaderEffectMaterial *>(node->material());
 
966
 
 
967
    // Update blending
 
968
    if (bool(material->flags() & QSGMaterial::Blending) != m_blending) {
 
969
        material->setFlag(QSGMaterial::Blending, m_blending);
 
970
        node->markDirty(QSGNode::DirtyMaterial);
 
971
    }
 
972
 
 
973
    if (int(material->cullMode) != int(m_cullMode)) {
 
974
        material->cullMode = QQuickShaderEffectMaterial::CullMode(m_cullMode);
 
975
        node->markDirty(QSGNode::DirtyMaterial);
 
976
    }
 
977
 
 
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();
 
985
 
 
986
        material->setProgramSource(s);
 
987
        material->attributes = m_common.attributes;
 
988
        node->markDirty(QSGNode::DirtyMaterial);
 
989
        m_dirtyProgram = false;
 
990
        m_dirtyUniforms = true;
 
991
    }
 
992
 
 
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;
 
998
    }
 
999
 
 
1000
    return node;
 
1001
}
 
1002
 
 
1003
void QQuickShaderEffect::componentComplete()
 
1004
{
 
1005
    m_common.updateShader(this, Key::VertexShader);
 
1006
    m_common.updateShader(this, Key::FragmentShader);
 
1007
    QQuickItem::componentComplete();
 
1008
}
 
1009
 
 
1010
void QQuickShaderEffect::itemChange(ItemChange change, const ItemChangeData &value)
 
1011
{
 
1012
    if (change == QQuickItem::ItemSceneChange)
 
1013
        m_common.updateWindow(value.window);
 
1014
    QQuickItem::itemChange(change, value);
 
1015
}
 
1016
 
 
1017
QT_END_NAMESPACE