1 /****************************************************************************
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
42 #include <private/qsgshadereffectitem_p.h>
43 #include <private/qsgshadereffectnode_p.h>
45 #include "qsgmaterial.h"
46 #include "qsgitem_p.h"
48 #include <private/qsgcontext_p.h>
49 #include <private/qsgtextureprovider_p.h>
50 #include "qsgcanvas.h"
52 #include <QtCore/qsignalmapper.h>
53 #include <QtOpenGL/qglframebufferobject.h>
57 static const char qt_default_vertex_code[] =
58 "uniform highp mat4 qt_ModelViewProjectionMatrix; \n"
59 "attribute highp vec4 qt_Vertex; \n"
60 "attribute highp vec2 qt_MultiTexCoord0; \n"
61 "varying highp vec2 qt_TexCoord0; \n"
63 " qt_TexCoord0 = qt_MultiTexCoord0; \n"
64 " gl_Position = qt_ModelViewProjectionMatrix * qt_Vertex; \n"
67 static const char qt_default_fragment_code[] =
68 "varying highp vec2 qt_TexCoord0; \n"
69 "uniform sampler2D source; \n"
70 "uniform lowp float qt_Opacity; \n"
72 " gl_FragColor = texture2D(source, qt_TexCoord0) * qt_Opacity; \n"
75 static const char qt_position_attribute_name[] = "qt_Vertex";
76 static const char qt_texcoord_attribute_name[] = "qt_MultiTexCoord0";
78 const char *qtPositionAttributeName()
80 return qt_position_attribute_name;
83 const char *qtTexCoordAttributeName()
85 return qt_texcoord_attribute_name;
88 QSGShaderEffectItem::QSGShaderEffectItem(QSGItem *parent)
91 , m_cullMode(NoCulling)
94 , m_programDirty(true)
96 , m_dirtyGeometry(true)
98 setFlag(QSGItem::ItemHasContents);
101 QSGShaderEffectItem::~QSGShaderEffectItem()
106 void QSGShaderEffectItem::componentComplete()
109 QSGItem::componentComplete();
112 void QSGShaderEffectItem::setFragmentShader(const QByteArray &code)
114 if (m_source.fragmentCode.constData() == code.constData())
116 m_source.fragmentCode = code;
117 if (isComponentComplete()) {
121 emit fragmentShaderChanged();
124 void QSGShaderEffectItem::setVertexShader(const QByteArray &code)
126 if (m_source.vertexCode.constData() == code.constData())
128 m_source.vertexCode = code;
129 if (isComponentComplete()) {
133 emit vertexShaderChanged();
136 void QSGShaderEffectItem::setBlending(bool enable)
138 if (blending() == enable)
144 emit blendingChanged();
147 void QSGShaderEffectItem::setMesh(QSGShaderEffectMesh *mesh)
152 disconnect(m_mesh, SIGNAL(geometryChanged()), this, 0);
155 connect(m_mesh, SIGNAL(geometryChanged()), this, SLOT(updateGeometry()));
161 void QSGShaderEffectItem::setCullMode(CullMode face)
163 if (face == m_cullMode)
167 emit cullModeChanged();
170 void QSGShaderEffectItem::changeSource(int index)
172 Q_ASSERT(index >= 0 && index < m_sources.size());
173 QVariant v = property(m_sources.at(index).name.constData());
177 void QSGShaderEffectItem::updateData()
183 void QSGShaderEffectItem::updateGeometry()
185 m_dirtyGeometry = true;
189 void QSGShaderEffectItem::setSource(const QVariant &var, int index)
191 Q_ASSERT(index >= 0 && index < m_sources.size());
193 SourceData &source = m_sources[index];
198 } else if (!qVariantCanConvert<QObject *>(var)) {
199 qWarning("Could not assign source of type '%s' to property '%s'.", var.typeName(), source.name.constData());
203 QObject *obj = qVariantValue<QObject *>(var);
205 QSGTextureProvider *int3rface = QSGTextureProvider::from(obj);
207 qWarning("Could not assign property '%s', did not implement QSGTextureProvider.", source.name.constData());
210 source.item = qobject_cast<QSGItem *>(obj);
212 // TODO: Find better solution.
213 // 'source.item' needs a canvas to get a scenegraph node.
214 // The easiest way to make sure it gets a canvas is to
215 // make it a part of the same item tree as 'this'.
216 if (source.item && source.item->parentItem() == 0) {
217 source.item->setParentItem(this);
218 source.item->setVisible(false);
222 void QSGShaderEffectItem::disconnectPropertySignals()
224 disconnect(this, 0, this, SLOT(updateData()));
225 for (int i = 0; i < m_sources.size(); ++i) {
226 SourceData &source = m_sources[i];
227 disconnect(this, 0, source.mapper, 0);
228 disconnect(source.mapper, 0, this, 0);
232 void QSGShaderEffectItem::connectPropertySignals()
234 QSet<QByteArray>::const_iterator it;
235 for (it = m_source.uniformNames.begin(); it != m_source.uniformNames.end(); ++it) {
236 int pi = metaObject()->indexOfProperty(it->constData());
238 QMetaProperty mp = metaObject()->property(pi);
239 if (!mp.hasNotifySignal())
240 qWarning("QSGShaderEffectItem: property '%s' does not have notification method!", it->constData());
241 QByteArray signalName("2");
242 signalName.append(mp.notifySignal().signature());
243 connect(this, signalName, this, SLOT(updateData()));
245 qWarning("QSGShaderEffectItem: '%s' does not have a matching property!", it->constData());
248 for (int i = 0; i < m_sources.size(); ++i) {
249 SourceData &source = m_sources[i];
250 int pi = metaObject()->indexOfProperty(source.name.constData());
252 QMetaProperty mp = metaObject()->property(pi);
253 QByteArray signalName("2");
254 signalName.append(mp.notifySignal().signature());
255 connect(this, signalName, source.mapper, SLOT(map()));
256 source.mapper->setMapping(this, i);
257 connect(source.mapper, SIGNAL(mapped(int)), this, SLOT(changeSource(int)));
259 qWarning("QSGShaderEffectItem: '%s' does not have a matching source!", source.name.constData());
264 void QSGShaderEffectItem::reset()
266 disconnectPropertySignals();
268 m_source.attributeNames.clear();
269 m_source.uniformNames.clear();
270 m_source.respectsOpacity = false;
271 m_source.respectsMatrix = false;
272 m_source.className = metaObject()->className();
274 for (int i = 0; i < m_sources.size(); ++i) {
275 const SourceData &source = m_sources.at(i);
276 delete source.mapper;
277 if (source.item && source.item->parentItem() == this)
278 source.item->setParentItem(0);
282 m_programDirty = true;
286 void QSGShaderEffectItem::updateProperties()
288 QByteArray vertexCode = m_source.vertexCode;
289 QByteArray fragmentCode = m_source.fragmentCode;
290 if (vertexCode.isEmpty())
291 vertexCode = qt_default_vertex_code;
292 if (fragmentCode.isEmpty())
293 fragmentCode = qt_default_fragment_code;
295 lookThroughShaderCode(vertexCode);
296 lookThroughShaderCode(fragmentCode);
298 if (!m_mesh && !m_source.attributeNames.contains(qt_position_attribute_name))
299 qWarning("QSGShaderEffectItem: Missing reference to \'%s\'.", qt_position_attribute_name);
300 if (!m_mesh && !m_source.attributeNames.contains(qt_texcoord_attribute_name))
301 qWarning("QSGShaderEffectItem: Missing reference to \'%s\'.", qt_texcoord_attribute_name);
302 if (!m_source.respectsMatrix)
303 qWarning("QSGShaderEffectItem: Missing reference to \'qt_ModelViewProjectionMatrix\'.");
304 if (!m_source.respectsOpacity)
305 qWarning("QSGShaderEffectItem: Missing reference to \'qt_Opacity\'.");
307 for (int i = 0; i < m_sources.size(); ++i) {
308 QVariant v = property(m_sources.at(i).name);
312 connectPropertySignals();
315 void QSGShaderEffectItem::lookThroughShaderCode(const QByteArray &code)
317 // Regexp for matching attributes and uniforms.
318 // In human readable form: attribute|uniform [lowp|mediump|highp] <type> <name>
319 static QRegExp re(QLatin1String("\\b(attribute|uniform)\\b\\s*\\b(?:lowp|mediump|highp)?\\b\\s*\\b(\\w+)\\b\\s*\\b(\\w+)"));
320 Q_ASSERT(re.isValid());
324 QString wideCode = QString::fromLatin1(code.constData(), code.size());
326 while ((pos = re.indexIn(wideCode, pos + 1)) != -1) {
327 QByteArray decl = re.cap(1).toLatin1(); // uniform or attribute
328 QByteArray type = re.cap(2).toLatin1(); // type
329 QByteArray name = re.cap(3).toLatin1(); // variable name
331 if (decl == "attribute") {
332 m_source.attributeNames.append(name);
334 Q_ASSERT(decl == "uniform");
336 if (name == "qt_ModelViewProjectionMatrix") {
337 m_source.respectsMatrix = true;
338 } else if (name == "qt_Opacity") {
339 m_source.respectsOpacity = true;
341 m_source.uniformNames.insert(name);
342 if (type == "sampler2D") {
344 d.mapper = new QSignalMapper;
354 void QSGShaderEffectItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
356 m_dirtyGeometry = true;
357 QSGItem::geometryChanged(newGeometry, oldGeometry);
360 QSGNode *QSGShaderEffectItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
362 QSGShaderEffectNode *node = static_cast<QSGShaderEffectNode *>(oldNode);
365 node = new QSGShaderEffectNode;
366 node->setMaterial(&m_material);
367 m_programDirty = true;
369 m_dirtyGeometry = true;
373 node->setGeometry(0);
375 m_dirtyGeometry = true;
378 if (m_dirtyGeometry) {
379 node->setFlag(QSGNode::OwnsGeometry, false);
380 QSGGeometry *geometry = node->geometry();
381 QRectF rect(0, 0, width(), height());
382 QSGShaderEffectMesh *mesh = m_mesh ? m_mesh : &m_defaultMesh;
384 geometry = mesh->updateGeometry(geometry, m_source.attributeNames, rect);
390 node->setGeometry(geometry);
391 node->setFlag(QSGNode::OwnsGeometry, true);
393 m_dirtyGeometry = false;
396 if (m_programDirty) {
397 QSGShaderEffectProgram s = m_source;
398 if (s.fragmentCode.isEmpty())
399 s.fragmentCode = qt_default_fragment_code;
400 if (s.vertexCode.isEmpty())
401 s.vertexCode = qt_default_vertex_code;
403 m_material.setProgramSource(s);
404 node->markDirty(QSGNode::DirtyMaterial);
405 m_programDirty = false;
409 if (bool(m_material.flags() & QSGMaterial::Blending) != m_blending) {
410 m_material.setFlag(QSGMaterial::Blending, m_blending);
411 node->markDirty(QSGNode::DirtyMaterial);
414 if (int(m_material.cullMode()) != int(m_cullMode)) {
415 m_material.setCullMode(QSGShaderEffectMaterial::CullMode(m_cullMode));
416 node->markDirty(QSGNode::DirtyMaterial);
420 QVector<QPair<QByteArray, QVariant> > values;
421 QVector<QPair<QByteArray, QPointer<QSGItem> > > textures;
422 const QVector<QPair<QByteArray, QPointer<QSGItem> > > &oldTextures = m_material.textureProviders();
424 for (QSet<QByteArray>::const_iterator it = m_source.uniformNames.begin();
425 it != m_source.uniformNames.end(); ++it) {
426 values.append(qMakePair(*it, property(*it)));
428 for (int i = 0; i < oldTextures.size(); ++i) {
429 QSGTextureProvider *oldSource = QSGTextureProvider::from(oldTextures.at(i).second);
430 if (oldSource && oldSource->textureChangedSignal())
431 disconnect(oldTextures.at(i).second, oldSource->textureChangedSignal(), node, SLOT(markDirtyTexture()));
433 for (int i = 0; i < m_sources.size(); ++i) {
434 const SourceData &source = m_sources.at(i);
435 textures.append(qMakePair(source.name, source.item));
436 QSGTextureProvider *t = QSGTextureProvider::from(source.item);
437 if (t && t->textureChangedSignal())
438 connect(source.item, t->textureChangedSignal(), node, SLOT(markDirtyTexture()));
440 m_material.setUniforms(values);
441 m_material.setTextureProviders(textures);
442 node->markDirty(QSGNode::DirtyMaterial);