diff options
21 files changed, 1252 insertions, 1009 deletions
diff --git a/src/particles/qquickcustomparticle.cpp b/src/particles/qquickcustomparticle.cpp index 32b2fd847e..ea8a25f5e6 100644 --- a/src/particles/qquickcustomparticle.cpp +++ b/src/particles/qquickcustomparticle.cpp @@ -237,7 +237,7 @@ void QQuickCustomParticle::reset() QSGNode *QQuickCustomParticle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) { - QQuickShaderEffectNode *rootNode = static_cast<QQuickShaderEffectNode *>(oldNode); + QQuickOpenGLShaderEffectNode *rootNode = static_cast<QQuickOpenGLShaderEffectNode *>(oldNode); if (m_pleaseReset){ delete rootNode;//Automatically deletes children rootNode = 0; @@ -258,7 +258,7 @@ QSGNode *QQuickCustomParticle::updatePaintNode(QSGNode *oldNode, UpdatePaintNode return rootNode; } -QQuickShaderEffectNode *QQuickCustomParticle::prepareNextFrame(QQuickShaderEffectNode *rootNode) +QQuickOpenGLShaderEffectNode *QQuickCustomParticle::prepareNextFrame(QQuickOpenGLShaderEffectNode *rootNode) { if (!rootNode) rootNode = buildCustomNodes(); @@ -269,7 +269,7 @@ QQuickShaderEffectNode *QQuickCustomParticle::prepareNextFrame(QQuickShaderEffec if (m_dirtyProgram) { const bool isES = QOpenGLContext::currentContext()->isOpenGLES(); - QQuickShaderEffectMaterial *material = static_cast<QQuickShaderEffectMaterial *>(rootNode->material()); + QQuickOpenGLShaderEffectMaterial *material = static_cast<QQuickOpenGLShaderEffectMaterial *>(rootNode->material()); Q_ASSERT(material); Key s = m_common.source; @@ -294,7 +294,7 @@ QQuickShaderEffectNode *QQuickCustomParticle::prepareNextFrame(QQuickShaderEffec material->setProgramSource(s); material->attributes = m_common.attributes; - foreach (QQuickShaderEffectNode* node, m_nodes) + foreach (QQuickOpenGLShaderEffectNode* node, m_nodes) node->markDirty(QSGNode::DirtyMaterial); m_dirtyProgram = false; @@ -307,9 +307,9 @@ QQuickShaderEffectNode *QQuickCustomParticle::prepareNextFrame(QQuickShaderEffec return rootNode; } -QQuickShaderEffectNode* QQuickCustomParticle::buildCustomNodes() +QQuickOpenGLShaderEffectNode* QQuickCustomParticle::buildCustomNodes() { - typedef QHash<int, QQuickShaderEffectNode*>::const_iterator NodeHashConstIt; + typedef QHash<int, QQuickOpenGLShaderEffectNode*>::const_iterator NodeHashConstIt; if (!QOpenGLContext::currentContext()) return 0; @@ -327,14 +327,14 @@ QQuickShaderEffectNode* QQuickCustomParticle::buildCustomNodes() if (groups().isEmpty()) return 0; - QQuickShaderEffectNode *rootNode = 0; - QQuickShaderEffectMaterial *material = new QQuickShaderEffectMaterial; + QQuickOpenGLShaderEffectNode *rootNode = 0; + QQuickOpenGLShaderEffectMaterial *material = new QQuickOpenGLShaderEffectMaterial; m_dirtyProgram = true; for (auto groupId : groupIds()) { int count = m_system->groupData[groupId]->size(); - QQuickShaderEffectNode* node = new QQuickShaderEffectNode(); + QQuickOpenGLShaderEffectNode* node = new QQuickOpenGLShaderEffectNode(); m_nodes.insert(groupId, node); node->setMaterial(material); @@ -400,7 +400,7 @@ void QQuickCustomParticle::propertyChanged(int mappedId) } -void QQuickCustomParticle::buildData(QQuickShaderEffectNode *rootNode) +void QQuickCustomParticle::buildData(QQuickOpenGLShaderEffectNode *rootNode) { if (!rootNode) return; @@ -410,9 +410,9 @@ void QQuickCustomParticle::buildData(QQuickShaderEffectNode *rootNode) m_common.uniformData[shaderType][i].value = qVariantFromValue(m_lastTime); } } - m_common.updateMaterial(rootNode, static_cast<QQuickShaderEffectMaterial *>(rootNode->material()), + m_common.updateMaterial(rootNode, static_cast<QQuickOpenGLShaderEffectMaterial *>(rootNode->material()), m_dirtyUniforms, true, m_dirtyTextureProviders); - foreach (QQuickShaderEffectNode* node, m_nodes) + foreach (QQuickOpenGLShaderEffectNode* node, m_nodes) node->markDirty(QSGNode::DirtyMaterial); m_dirtyUniforms = m_dirtyUniformValues = m_dirtyTextureProviders = false; } diff --git a/src/particles/qquickcustomparticle_p.h b/src/particles/qquickcustomparticle_p.h index 25a3a1197c..d9690aa96a 100644 --- a/src/particles/qquickcustomparticle_p.h +++ b/src/particles/qquickcustomparticle_p.h @@ -51,8 +51,8 @@ // We mean it. // #include "qquickparticlepainter_p.h" -#include <private/qquickshadereffectnode_p.h> -#include <private/qquickshadereffect_p.h> +#include <private/qquickopenglshadereffectnode_p.h> +#include <private/qquickopenglshadereffect_p.h> #include <QSignalMapper> QT_BEGIN_NAMESPACE @@ -88,11 +88,11 @@ protected: virtual void commit(int gIdx, int pIdx); QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); - QQuickShaderEffectNode *prepareNextFrame(QQuickShaderEffectNode *rootNode); + QQuickOpenGLShaderEffectNode *prepareNextFrame(QQuickOpenGLShaderEffectNode *rootNode); void reset(); void resize(int oldCount, int newCount); virtual void componentComplete(); - QQuickShaderEffectNode *buildCustomNodes(); + QQuickOpenGLShaderEffectNode *buildCustomNodes(); void sceneGraphInvalidated(); void itemChange(ItemChange change, const ItemChangeData &value); @@ -102,15 +102,15 @@ private Q_SLOTS: void propertyChanged(int mappedId); private: - typedef QQuickShaderEffectMaterialKey Key; - typedef QQuickShaderEffectMaterial::UniformData UniformData; + typedef QQuickOpenGLShaderEffectMaterialKey Key; + typedef QQuickOpenGLShaderEffectMaterial::UniformData UniformData; - void buildData(QQuickShaderEffectNode *rootNode); + void buildData(QQuickOpenGLShaderEffectNode *rootNode); void updateVertexShader(); - QQuickShaderEffectCommon m_common; + QQuickOpenGLShaderEffectCommon m_common; - QHash<int, QQuickShaderEffectNode*> m_nodes; + QHash<int, QQuickOpenGLShaderEffectNode*> m_nodes; qreal m_lastTime; uint m_dirtyUniforms : 1; diff --git a/src/quick/items/context2d/qquickcontext2dtexture.cpp b/src/quick/items/context2d/qquickcontext2dtexture.cpp index 1bf5c333be..ddecf7c3d4 100644 --- a/src/quick/items/context2d/qquickcontext2dtexture.cpp +++ b/src/quick/items/context2d/qquickcontext2dtexture.cpp @@ -47,6 +47,7 @@ #ifndef QT_NO_OPENGL #include <QOpenGLFramebufferObject> #include <QOpenGLFramebufferObjectFormat> +#include <QOpenGLFunctions> #endif #include <QtCore/QThread> #include <QtGui/QGuiApplication> diff --git a/src/quick/items/items.pri b/src/quick/items/items.pri index ee58fef1c4..baa59cb7fd 100644 --- a/src/quick/items/items.pri +++ b/src/quick/items/items.pri @@ -74,6 +74,7 @@ HEADERS += \ $$PWD/qquickwindowmodule_p.h \ $$PWD/qquickshadereffectsource_p.h \ $$PWD/qquickshadereffectmesh_p.h \ + $$PWD/qquickshadereffect_p.h \ $$PWD/qquickrendercontrol.h \ $$PWD/qquickrendercontrol_p.h @@ -126,14 +127,15 @@ SOURCES += \ $$PWD/qquickwindowattached.cpp \ $$PWD/qquickshadereffectsource.cpp \ $$PWD/qquickshadereffectmesh.cpp \ + $$PWD/qquickshadereffect.cpp \ $$PWD/qquickrendercontrol.cpp # Items that depend on OpenGL Renderer contains(QT_CONFIG, opengl(es1|es2)?) { SOURCES += \ $$PWD/qquickopenglinfo.cpp \ - $$PWD/qquickshadereffect.cpp \ - $$PWD/qquickshadereffectnode.cpp \ + $$PWD/qquickopenglshadereffect.cpp \ + $$PWD/qquickopenglshadereffectnode.cpp \ $$PWD/qquickframebufferobject.cpp \ $$PWD/qquickspriteengine.cpp \ $$PWD/qquicksprite.cpp \ @@ -147,8 +149,8 @@ contains(QT_CONFIG, opengl(es1|es2)?) { $$PWD/qquicksprite_p.h \ $$PWD/qquickspritesequence_p.h \ $$PWD/qquickanimatedsprite_p.h \ - $$PWD/qquickshadereffect_p.h \ - $$PWD/qquickshadereffectnode_p.h \ + $$PWD/qquickopenglshadereffect_p.h \ + $$PWD/qquickopenglshadereffectnode_p.h \ $$PWD/qquickframebufferobject.h \ $$PWD/qquickitemgrabresult.h diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index 4404006a88..46b08b11fb 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -77,9 +77,6 @@ #include <QtCore/qelapsedtimer.h> #include <QtQuick/private/qquickshadereffectsource_p.h> -#ifndef QT_NO_OPENGL -# include <QtQuick/private/qquickshadereffect_p.h> -#endif QT_BEGIN_NAMESPACE diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index 763133c39b..575228cffa 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -75,7 +75,7 @@ #include <QtQuick/private/qquickcontext2d_p.h> #ifndef QT_NO_OPENGL # include "qquickitemgrabresult.h" -# include <private/qquickshadereffect_p.h> +# include <private/qquickopenglshadereffect_p.h> # include "qquicksprite_p.h" # include "qquickspritesequence_p.h" # include "qquickanimatedsprite_p.h" @@ -215,7 +215,7 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) qmlRegisterUncreatableType<QQuickShaderEffectMesh>("QtQuick", 2, 0, "ShaderEffectMesh", QQuickShaderEffectMesh::tr("Cannot create instance of abstract class ShaderEffectMesh.")); qmlRegisterType<QQuickGridMesh>("QtQuick", 2, 0, "GridMesh"); #ifndef QT_NO_OPENGL - qmlRegisterType<QQuickShaderEffect>("QtQuick", 2, 0, "ShaderEffect"); + qmlRegisterType<QQuickOpenGLShaderEffect>("QtQuick", 2, 0, "ShaderEffect"); #endif qmlRegisterUncreatableType<QQuickPaintedItem>("QtQuick", 2, 0, "PaintedItem", QQuickPaintedItem::tr("Cannot create instance of abstract class PaintedItem")); @@ -268,7 +268,7 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) qmlRegisterType<QQuickListView, 2>(uri, 2, 4, "ListView"); qmlRegisterType<QQuickMouseArea, 1>(uri, 2, 4, "MouseArea"); #ifndef QT_NO_OPENGL - qmlRegisterType<QQuickShaderEffect, 1>(uri, 2, 4, "ShaderEffect"); + qmlRegisterType<QQuickOpenGLShaderEffect, 1>(uri, 2, 4, "ShaderEffect"); qmlRegisterUncreatableType<QQuickOpenGLInfo>(uri, 2, 4,"OpenGLInfo", QQuickOpenGLInfo::tr("OpenGLInfo is only available via attached properties")); #endif qmlRegisterType<QQuickPinchArea, 1>(uri, 2, 5,"PinchArea"); diff --git a/src/quick/items/qquickopenglshadereffect.cpp b/src/quick/items/qquickopenglshadereffect.cpp new file mode 100644 index 0000000000..8919afd066 --- /dev/null +++ b/src/quick/items/qquickopenglshadereffect.cpp @@ -0,0 +1,865 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qquickopenglshadereffect_p.h> + +#include <QtQuick/qsgmaterial.h> +#include <QtQuick/private/qsgshadersourcebuilder_p.h> +#include "qquickitem_p.h" + +#include <QtQuick/private/qsgcontext_p.h> +#include <QtQuick/qsgtextureprovider.h> +#include "qquickwindow.h" + +#include "qquickimage_p.h" +#include "qquickshadereffectsource_p.h" +#include "qquickshadereffectmesh_p.h" + +#include <QtCore/qsignalmapper.h> + +QT_BEGIN_NAMESPACE + +namespace { + + enum VariableQualifier { + AttributeQualifier, + UniformQualifier + }; + + inline bool qt_isalpha(char c) + { + char ch = c | 0x20; + return (ch >= 'a' && ch <= 'z') || c == '_'; + } + + inline bool qt_isalnum(char c) + { + return qt_isalpha(c) || (c >= '0' && c <= '9'); + } + + inline bool qt_isspace(char c) + { + return c == ' ' || (c >= 0x09 && c <= 0x0d); + } + + // Returns -1 if not found, returns index to first character after the name if found. + int qt_search_for_variable(const char *s, int length, int index, VariableQualifier &decl, + int &typeIndex, int &typeLength, + int &nameIndex, int &nameLength, + QQuickOpenGLShaderEffectCommon::Key::ShaderType shaderType) + { + enum Identifier { + QualifierIdentifier, // Base state + PrecisionIdentifier, + TypeIdentifier, + NameIdentifier + }; + Identifier expected = QualifierIdentifier; + bool compilerDirectiveExpected = index == 0; + + while (index < length) { + // Skip whitespace. + while (qt_isspace(s[index])) { + compilerDirectiveExpected |= s[index] == '\n'; + ++index; + } + + if (qt_isalpha(s[index])) { + // Read identifier. + int idIndex = index; + ++index; + while (qt_isalnum(s[index])) + ++index; + int idLength = index - idIndex; + + const int attrLen = sizeof("attribute") - 1; + const int inLen = sizeof("in") - 1; + const int uniLen = sizeof("uniform") - 1; + const int loLen = sizeof("lowp") - 1; + const int medLen = sizeof("mediump") - 1; + const int hiLen = sizeof("highp") - 1; + + switch (expected) { + case QualifierIdentifier: + if (idLength == attrLen && qstrncmp("attribute", s + idIndex, attrLen) == 0) { + decl = AttributeQualifier; + expected = PrecisionIdentifier; + } else if (shaderType == QQuickOpenGLShaderEffectCommon::Key::VertexShader + && idLength == inLen && qstrncmp("in", s + idIndex, inLen) == 0) { + decl = AttributeQualifier; + expected = PrecisionIdentifier; + } else if (idLength == uniLen && qstrncmp("uniform", s + idIndex, uniLen) == 0) { + decl = UniformQualifier; + expected = PrecisionIdentifier; + } + break; + case PrecisionIdentifier: + if ((idLength == loLen && qstrncmp("lowp", s + idIndex, loLen) == 0) + || (idLength == medLen && qstrncmp("mediump", s + idIndex, medLen) == 0) + || (idLength == hiLen && qstrncmp("highp", s + idIndex, hiLen) == 0)) + { + expected = TypeIdentifier; + break; + } + // Fall through. + case TypeIdentifier: + typeIndex = idIndex; + typeLength = idLength; + expected = NameIdentifier; + break; + case NameIdentifier: + nameIndex = idIndex; + nameLength = idLength; + return index; // Attribute or uniform declaration found. Return result. + default: + break; + } + } else if (s[index] == '#' && compilerDirectiveExpected) { + // Skip compiler directives. + ++index; + while (index < length && (s[index] != '\n' || s[index - 1] == '\\')) + ++index; + } else if (s[index] == '/' && s[index + 1] == '/') { + // Skip comments. + index += 2; + while (index < length && s[index] != '\n') + ++index; + } else if (s[index] == '/' && s[index + 1] == '*') { + // Skip comments. + index += 2; + while (index < length && (s[index] != '*' || s[index + 1] != '/')) + ++index; + if (index < length) + index += 2; // Skip star-slash. + } else { + expected = QualifierIdentifier; + ++index; + } + compilerDirectiveExpected = false; + } + return -1; + } +} + + + +QQuickOpenGLShaderEffectCommon::~QQuickOpenGLShaderEffectCommon() +{ + for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) + qDeleteAll(signalMappers[shaderType]); +} + +void QQuickOpenGLShaderEffectCommon::disconnectPropertySignals(QQuickItem *item, Key::ShaderType shaderType) +{ + for (int i = 0; i < uniformData[shaderType].size(); ++i) { + if (signalMappers[shaderType].at(i) == 0) + continue; + const UniformData &d = uniformData[shaderType].at(i); + QSignalMapper *mapper = signalMappers[shaderType].at(i); + QObject::disconnect(item, 0, mapper, SLOT(map())); + QObject::disconnect(mapper, SIGNAL(mapped(int)), item, SLOT(propertyChanged(int))); + if (d.specialType == UniformData::Sampler) { + QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value)); + if (source) { + if (item->window()) + QQuickItemPrivate::get(source)->derefWindow(); + QObject::disconnect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*))); + } + } + } +} + +void QQuickOpenGLShaderEffectCommon::connectPropertySignals(QQuickItem *item, Key::ShaderType shaderType) +{ + for (int i = 0; i < uniformData[shaderType].size(); ++i) { + if (signalMappers[shaderType].at(i) == 0) + continue; + const UniformData &d = uniformData[shaderType].at(i); + int pi = item->metaObject()->indexOfProperty(d.name.constData()); + if (pi >= 0) { + QMetaProperty mp = item->metaObject()->property(pi); + if (!mp.hasNotifySignal()) + qWarning("QQuickOpenGLShaderEffect: property '%s' does not have notification method!", d.name.constData()); + const QByteArray signalName = '2' + mp.notifySignal().methodSignature(); + QSignalMapper *mapper = signalMappers[shaderType].at(i); + QObject::connect(item, signalName, mapper, SLOT(map())); + QObject::connect(mapper, SIGNAL(mapped(int)), item, SLOT(propertyChanged(int))); + } else { + // If the source is set via a dynamic property, like the layer is, then we need this + // check to disable the warning. + if (!item->property(d.name.constData()).isValid()) + qWarning("QQuickOpenGLShaderEffect: '%s' does not have a matching property!", d.name.constData()); + } + + if (d.specialType == UniformData::Sampler) { + QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value)); + if (source) { + if (item->window()) + QQuickItemPrivate::get(source)->refWindow(item->window()); + QObject::connect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*))); + } + } + } +} + +void QQuickOpenGLShaderEffectCommon::updateParseLog(bool ignoreAttributes) +{ + parseLog.clear(); + if (!ignoreAttributes) { + if (!attributes.contains(qtPositionAttributeName())) { + parseLog += QLatin1String("Warning: Missing reference to \'"); + parseLog += QLatin1String(qtPositionAttributeName()); + parseLog += QLatin1String("\'.\n"); + } + if (!attributes.contains(qtTexCoordAttributeName())) { + parseLog += QLatin1String("Warning: Missing reference to \'"); + parseLog += QLatin1String(qtTexCoordAttributeName()); + parseLog += QLatin1String("\'.\n"); + } + } + bool respectsMatrix = false; + bool respectsOpacity = false; + for (int i = 0; i < uniformData[Key::VertexShader].size(); ++i) + respectsMatrix |= uniformData[Key::VertexShader].at(i).specialType == UniformData::Matrix; + for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { + for (int i = 0; i < uniformData[shaderType].size(); ++i) + respectsOpacity |= uniformData[shaderType].at(i).specialType == UniformData::Opacity; + } + if (!respectsMatrix) + parseLog += QLatin1String("Warning: Vertex shader is missing reference to \'qt_Matrix\'.\n"); + if (!respectsOpacity) + parseLog += QLatin1String("Warning: Shaders are missing reference to \'qt_Opacity\'.\n"); +} + +void QQuickOpenGLShaderEffectCommon::lookThroughShaderCode(QQuickItem *item, Key::ShaderType shaderType, const QByteArray &code) +{ + int index = 0; + int typeIndex = -1; + int typeLength = 0; + int nameIndex = -1; + int nameLength = 0; + const char *s = code.constData(); + VariableQualifier decl = AttributeQualifier; + while ((index = qt_search_for_variable(s, code.size(), index, decl, typeIndex, typeLength, + nameIndex, nameLength, shaderType)) != -1) + { + if (decl == AttributeQualifier) { + if (shaderType == Key::VertexShader) + attributes.append(QByteArray(s + nameIndex, nameLength)); + } else { + Q_ASSERT(decl == UniformQualifier); + + const int sampLen = sizeof("sampler2D") - 1; + const int opLen = sizeof("qt_Opacity") - 1; + const int matLen = sizeof("qt_Matrix") - 1; + const int srLen = sizeof("qt_SubRect_") - 1; + + UniformData d; + QSignalMapper *mapper = 0; + d.name = QByteArray(s + nameIndex, nameLength); + if (nameLength == opLen && qstrncmp("qt_Opacity", s + nameIndex, opLen) == 0) { + d.specialType = UniformData::Opacity; + } else if (nameLength == matLen && qstrncmp("qt_Matrix", s + nameIndex, matLen) == 0) { + d.specialType = UniformData::Matrix; + } else if (nameLength > srLen && qstrncmp("qt_SubRect_", s + nameIndex, srLen) == 0) { + d.specialType = UniformData::SubRect; + } else { + mapper = new QSignalMapper; + mapper->setMapping(item, uniformData[shaderType].size() | (shaderType << 16)); + d.value = item->property(d.name.constData()); + bool sampler = typeLength == sampLen && qstrncmp("sampler2D", s + typeIndex, sampLen) == 0; + d.specialType = sampler ? UniformData::Sampler : UniformData::None; + } + uniformData[shaderType].append(d); + signalMappers[shaderType].append(mapper); + } + } +} + +void QQuickOpenGLShaderEffectCommon::updateShader(QQuickItem *item, Key::ShaderType shaderType) +{ + disconnectPropertySignals(item, shaderType); + qDeleteAll(signalMappers[shaderType]); + uniformData[shaderType].clear(); + signalMappers[shaderType].clear(); + if (shaderType == Key::VertexShader) + attributes.clear(); + + const QByteArray &code = source.sourceCode[shaderType]; + if (code.isEmpty()) { + // Optimize for default code. + if (shaderType == Key::VertexShader) { + attributes.append(QByteArray(qtPositionAttributeName())); + attributes.append(QByteArray(qtTexCoordAttributeName())); + UniformData d; + d.name = "qt_Matrix"; + d.specialType = UniformData::Matrix; + uniformData[Key::VertexShader].append(d); + signalMappers[Key::VertexShader].append(0); + } else if (shaderType == Key::FragmentShader) { + UniformData d; + d.name = "qt_Opacity"; + d.specialType = UniformData::Opacity; + uniformData[Key::FragmentShader].append(d); + signalMappers[Key::FragmentShader].append(0); + QSignalMapper *mapper = new QSignalMapper; + mapper->setMapping(item, 1 | (Key::FragmentShader << 16)); + const char *sourceName = "source"; + d.name = sourceName; + d.value = item->property(sourceName); + d.specialType = UniformData::Sampler; + uniformData[Key::FragmentShader].append(d); + signalMappers[Key::FragmentShader].append(mapper); + } + } else { + lookThroughShaderCode(item, shaderType, code); + } + + connectPropertySignals(item, shaderType); +} + +void QQuickOpenGLShaderEffectCommon::updateMaterial(QQuickOpenGLShaderEffectNode *node, + QQuickOpenGLShaderEffectMaterial *material, + bool updateUniforms, bool updateUniformValues, + bool updateTextureProviders) +{ + if (updateUniforms) { + for (int i = 0; i < material->textureProviders.size(); ++i) { + QSGTextureProvider *t = material->textureProviders.at(i); + if (t) { + QObject::disconnect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture())); + QObject::disconnect(t, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*))); + } + } + + // First make room in the textureProviders array. Set to proper value further down. + int textureProviderCount = 0; + for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { + for (int i = 0; i < uniformData[shaderType].size(); ++i) { + if (uniformData[shaderType].at(i).specialType == UniformData::Sampler) + ++textureProviderCount; + } + material->uniforms[shaderType] = uniformData[shaderType]; + } + material->textureProviders.fill(0, textureProviderCount); + updateUniformValues = false; + updateTextureProviders = true; + } + + if (updateUniformValues) { + for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { + Q_ASSERT(uniformData[shaderType].size() == material->uniforms[shaderType].size()); + for (int i = 0; i < uniformData[shaderType].size(); ++i) + material->uniforms[shaderType][i].value = uniformData[shaderType].at(i).value; + } + } + + if (updateTextureProviders) { + int index = 0; + for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { + for (int i = 0; i < uniformData[shaderType].size(); ++i) { + const UniformData &d = uniformData[shaderType].at(i); + if (d.specialType != UniformData::Sampler) + continue; + QSGTextureProvider *oldProvider = material->textureProviders.at(index); + QSGTextureProvider *newProvider = 0; + QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value)); + if (source && source->isTextureProvider()) + newProvider = source->textureProvider(); + if (newProvider != oldProvider) { + if (oldProvider) { + QObject::disconnect(oldProvider, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture())); + QObject::disconnect(oldProvider, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*))); + } + if (newProvider) { + Q_ASSERT_X(newProvider->thread() == QThread::currentThread(), + "QQuickOpenGLShaderEffect::updatePaintNode", + "Texture provider must belong to the rendering thread"); + QObject::connect(newProvider, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture())); + QObject::connect(newProvider, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*))); + } else { + const char *typeName = source ? source->metaObject()->className() : d.value.typeName(); + qWarning("ShaderEffect: Property '%s' is not assigned a valid texture provider (%s).", + d.name.constData(), typeName); + } + material->textureProviders[index] = newProvider; + } + ++index; + } + } + Q_ASSERT(index == material->textureProviders.size()); + } +} + +void QQuickOpenGLShaderEffectCommon::updateWindow(QQuickWindow *window) +{ + // See comment in QQuickOpenGLShaderEffectCommon::propertyChanged(). + if (window) { + for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { + for (int i = 0; i < uniformData[shaderType].size(); ++i) { + const UniformData &d = uniformData[shaderType].at(i); + if (d.specialType == UniformData::Sampler) { + QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value)); + if (source) + QQuickItemPrivate::get(source)->refWindow(window); + } + } + } + } else { + for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { + for (int i = 0; i < uniformData[shaderType].size(); ++i) { + const UniformData &d = uniformData[shaderType].at(i); + if (d.specialType == UniformData::Sampler) { + QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value)); + if (source) + QQuickItemPrivate::get(source)->derefWindow(); + } + } + } + } +} + +void QQuickOpenGLShaderEffectCommon::sourceDestroyed(QObject *object) +{ + for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { + for (int i = 0; i < uniformData[shaderType].size(); ++i) { + UniformData &d = uniformData[shaderType][i]; + if (d.specialType == UniformData::Sampler && d.value.canConvert<QObject *>()) { + if (qvariant_cast<QObject *>(d.value) == object) + d.value = QVariant(); + } + } + } +} + +static bool qquick_uniqueInUniformData(QQuickItem *source, const QVector<QQuickOpenGLShaderEffectMaterial::UniformData> *uniformData, int typeToSkip, int indexToSkip) +{ + for (int s=0; s<QQuickOpenGLShaderEffectMaterialKey::ShaderTypeCount; ++s) { + for (int i=0; i<uniformData[s].size(); ++i) { + if (s == typeToSkip && i == indexToSkip) + continue; + const QQuickOpenGLShaderEffectMaterial::UniformData &d = uniformData[s][i]; + if (d.specialType == QQuickOpenGLShaderEffectMaterial::UniformData::Sampler && qvariant_cast<QObject *>(d.value) == source) + return false; + } + } + return true; +} + +void QQuickOpenGLShaderEffectCommon::propertyChanged(QQuickItem *item, int mappedId, + bool *textureProviderChanged) +{ + Key::ShaderType shaderType = Key::ShaderType(mappedId >> 16); + int index = mappedId & 0xffff; + UniformData &d = uniformData[shaderType][index]; + if (d.specialType == UniformData::Sampler) { + QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value)); + if (source) { + if (item->window()) + QQuickItemPrivate::get(source)->derefWindow(); + + // QObject::disconnect() will disconnect all matching connections. If the same + // source has been attached to two separate samplers, then changing one of them + // would trigger both to be disconnected. Without the connection we'll end up + // with a dangling pointer in the uniformData. + if (qquick_uniqueInUniformData(source, uniformData, shaderType, index)) + QObject::disconnect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*))); + } + + d.value = item->property(d.name.constData()); + + source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value)); + if (source) { + // 'source' needs a window to get a scene graph node. It usually gets one through its + // parent, but if the source item is "inline" rather than a reference -- i.e. + // "property variant source: Image { }" instead of "property variant source: foo" -- it + // will not get a parent. In those cases, 'source' should get the window from 'item'. + if (item->window()) + QQuickItemPrivate::get(source)->refWindow(item->window()); + QObject::connect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*))); + } + if (textureProviderChanged) + *textureProviderChanged = true; + } else { + d.value = item->property(d.name.constData()); + if (textureProviderChanged) + *textureProviderChanged = false; + } +} + +QQuickOpenGLShaderEffect::QQuickOpenGLShaderEffect(QQuickItem *parent) + : QQuickItem(parent) + , m_meshResolution(1, 1) + , m_mesh(0) + , m_cullMode(NoCulling) + , m_status(Uncompiled) + , m_blending(true) + , m_dirtyUniforms(true) + , m_dirtyUniformValues(true) + , m_dirtyTextureProviders(true) + , m_dirtyProgram(true) + , m_dirtyParseLog(true) + , m_dirtyMesh(true) + , m_dirtyGeometry(true) + , m_customVertexShader(false) + , m_supportsAtlasTextures(false) +{ + setFlag(QQuickItem::ItemHasContents); +} + +QQuickOpenGLShaderEffect::~QQuickOpenGLShaderEffect() +{ + for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) + m_common.disconnectPropertySignals(this, Key::ShaderType(shaderType)); +} + +void QQuickOpenGLShaderEffect::setFragmentShader(const QByteArray &code) +{ + if (m_common.source.sourceCode[Key::FragmentShader].constData() == code.constData()) + return; + m_common.source.sourceCode[Key::FragmentShader] = code; + m_dirtyProgram = true; + m_dirtyParseLog = true; + + if (isComponentComplete()) + m_common.updateShader(this, Key::FragmentShader); + + update(); + if (m_status != Uncompiled) { + m_status = Uncompiled; + emit statusChanged(); + } + emit fragmentShaderChanged(); +} + +void QQuickOpenGLShaderEffect::setVertexShader(const QByteArray &code) +{ + if (m_common.source.sourceCode[Key::VertexShader].constData() == code.constData()) + return; + m_common.source.sourceCode[Key::VertexShader] = code; + m_dirtyProgram = true; + m_dirtyParseLog = true; + m_customVertexShader = true; + + if (isComponentComplete()) + m_common.updateShader(this, Key::VertexShader); + + update(); + if (m_status != Uncompiled) { + m_status = Uncompiled; + emit statusChanged(); + } + emit vertexShaderChanged(); +} + +void QQuickOpenGLShaderEffect::setBlending(bool enable) +{ + if (blending() == enable) + return; + + m_blending = enable; + update(); + + emit blendingChanged(); +} + +QVariant QQuickOpenGLShaderEffect::mesh() const +{ + return m_mesh ? qVariantFromValue(static_cast<QObject *>(m_mesh)) + : qVariantFromValue(m_meshResolution); +} + +void QQuickOpenGLShaderEffect::setMesh(const QVariant &mesh) +{ + QQuickShaderEffectMesh *newMesh = qobject_cast<QQuickShaderEffectMesh *>(qvariant_cast<QObject *>(mesh)); + if (newMesh && newMesh == m_mesh) + return; + if (m_mesh) + disconnect(m_mesh, SIGNAL(geometryChanged()), this, 0); + m_mesh = newMesh; + if (m_mesh) { + connect(m_mesh, SIGNAL(geometryChanged()), this, SLOT(updateGeometry())); + } else { + if (mesh.canConvert<QSize>()) { + m_meshResolution = mesh.toSize(); + } else { + QList<QByteArray> res = mesh.toByteArray().split('x'); + bool ok = res.size() == 2; + if (ok) { + int w = res.at(0).toInt(&ok); + if (ok) { + int h = res.at(1).toInt(&ok); + if (ok) + m_meshResolution = QSize(w, h); + } + } + if (!ok) + qWarning("ShaderEffect: mesh property must be size or object deriving from QQuickOpenGLShaderEffectMesh."); + } + m_defaultMesh.setResolution(m_meshResolution); + } + + m_dirtyMesh = true; + m_dirtyParseLog = true; + update(); + emit meshChanged(); +} + +void QQuickOpenGLShaderEffect::setCullMode(CullMode face) +{ + if (face == m_cullMode) + return; + m_cullMode = face; + update(); + emit cullModeChanged(); +} + +void QQuickOpenGLShaderEffect::setSupportsAtlasTextures(bool supports) +{ + if (supports == m_supportsAtlasTextures) + return; + m_supportsAtlasTextures = supports; + updateGeometry(); + emit supportsAtlasTexturesChanged(); +} + +QString QQuickOpenGLShaderEffect::parseLog() +{ + if (m_dirtyParseLog) { + m_common.updateParseLog(m_mesh != 0); + m_dirtyParseLog = false; + } + return m_common.parseLog; +} + +bool QQuickOpenGLShaderEffect::event(QEvent *event) +{ + if (event->type() == QEvent::DynamicPropertyChange) { + QDynamicPropertyChangeEvent *e = static_cast<QDynamicPropertyChangeEvent *>(event); + for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { + for (int i = 0; i < m_common.uniformData[shaderType].size(); ++i) { + if (m_common.uniformData[shaderType].at(i).name == e->propertyName()) { + bool textureProviderChanged; + m_common.propertyChanged(this, (shaderType << 16) | i, &textureProviderChanged); + m_dirtyTextureProviders |= textureProviderChanged; + m_dirtyUniformValues = true; + update(); + } + } + } + } + return QQuickItem::event(event); +} + +void QQuickOpenGLShaderEffect::updateGeometry() +{ + m_dirtyGeometry = true; + update(); +} + +void QQuickOpenGLShaderEffect::updateGeometryIfAtlased() +{ + if (m_supportsAtlasTextures) + updateGeometry(); +} + +void QQuickOpenGLShaderEffect::updateLogAndStatus(const QString &log, int status) +{ + m_log = parseLog() + log; + m_status = Status(status); + emit logChanged(); + emit statusChanged(); +} + +void QQuickOpenGLShaderEffect::sourceDestroyed(QObject *object) +{ + m_common.sourceDestroyed(object); +} + + +void QQuickOpenGLShaderEffect::propertyChanged(int mappedId) +{ + bool textureProviderChanged; + m_common.propertyChanged(this, mappedId, &textureProviderChanged); + m_dirtyTextureProviders |= textureProviderChanged; + m_dirtyUniformValues = true; + update(); +} + +void QQuickOpenGLShaderEffect::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + m_dirtyGeometry = true; + QQuickItem::geometryChanged(newGeometry, oldGeometry); +} + +QSGNode *QQuickOpenGLShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) +{ + QQuickOpenGLShaderEffectNode *node = static_cast<QQuickOpenGLShaderEffectNode *>(oldNode); + + // In the case of zero-size or a bad vertex shader, don't try to create a node... + if (m_common.attributes.isEmpty() || width() <= 0 || height() <= 0) { + if (node) + delete node; + return 0; + } + + if (!node) { + node = new QQuickOpenGLShaderEffectNode; + node->setMaterial(new QQuickOpenGLShaderEffectMaterial(node)); + node->setFlag(QSGNode::OwnsMaterial, true); + m_dirtyProgram = true; + m_dirtyUniforms = true; + m_dirtyGeometry = true; + connect(node, SIGNAL(logAndStatusChanged(QString,int)), this, SLOT(updateLogAndStatus(QString,int))); + connect(node, &QQuickOpenGLShaderEffectNode::dirtyTexture, + this, &QQuickOpenGLShaderEffect::updateGeometryIfAtlased); + } + + QQuickOpenGLShaderEffectMaterial *material = static_cast<QQuickOpenGLShaderEffectMaterial *>(node->material()); + + // Update blending + if (bool(material->flags() & QSGMaterial::Blending) != m_blending) { + material->setFlag(QSGMaterial::Blending, m_blending); + node->markDirty(QSGNode::DirtyMaterial); + } + + if (int(material->cullMode) != int(m_cullMode)) { + material->cullMode = QQuickShaderEffect::CullMode(m_cullMode); + node->markDirty(QSGNode::DirtyMaterial); + } + + if (m_dirtyProgram) { + Key s = m_common.source; + QSGShaderSourceBuilder builder; + if (s.sourceCode[Key::FragmentShader].isEmpty()) { + builder.appendSourceFile(QStringLiteral(":/qt-project.org/items/shaders/shadereffect.frag")); + s.sourceCode[Key::FragmentShader] = builder.source(); + builder.clear(); + } + if (s.sourceCode[Key::VertexShader].isEmpty()) { + builder.appendSourceFile(QStringLiteral(":/qt-project.org/items/shaders/shadereffect.vert")); + s.sourceCode[Key::VertexShader] = builder.source(); + } + s.className = metaObject()->className(); + + material->setProgramSource(s); + material->attributes = m_common.attributes; + node->markDirty(QSGNode::DirtyMaterial); + m_dirtyProgram = false; + m_dirtyUniforms = true; + } + + if (m_dirtyUniforms || m_dirtyUniformValues || m_dirtyTextureProviders) { + m_common.updateMaterial(node, material, m_dirtyUniforms, m_dirtyUniformValues, + m_dirtyTextureProviders); + node->markDirty(QSGNode::DirtyMaterial); + m_dirtyUniforms = m_dirtyUniformValues = m_dirtyTextureProviders = false; + } + + QRectF srcRect(0, 0, 1, 1); + bool geometryUsesTextureSubRect = false; + if (m_supportsAtlasTextures && material->textureProviders.size() == 1) { + QSGTextureProvider *provider = material->textureProviders.at(0); + if (provider->texture()) { + srcRect = provider->texture()->normalizedTextureSubRect(); + geometryUsesTextureSubRect = true; + } + } + + if (bool(material->flags() & QSGMaterial::RequiresFullMatrix) != m_customVertexShader) { + material->setFlag(QSGMaterial::RequiresFullMatrix, m_customVertexShader); + node->markDirty(QSGNode::DirtyMaterial); + } + + if (material->geometryUsesTextureSubRect != geometryUsesTextureSubRect) { + material->geometryUsesTextureSubRect = geometryUsesTextureSubRect; + node->markDirty(QSGNode::DirtyMaterial); + } + + if (m_dirtyMesh) { + node->setGeometry(0); + m_dirtyMesh = false; + m_dirtyGeometry = true; + } + + if (m_dirtyGeometry) { + node->setFlag(QSGNode::OwnsGeometry, false); + QSGGeometry *geometry = node->geometry(); + QRectF rect(0, 0, width(), height()); + QQuickShaderEffectMesh *mesh = m_mesh ? m_mesh : &m_defaultMesh; + + geometry = mesh->updateGeometry(geometry, m_common.attributes, srcRect, rect); + if (!geometry) { + QString log = mesh->log(); + if (!log.isNull()) { + m_log = parseLog(); + m_log += QLatin1String("*** Mesh ***\n"); + m_log += log; + m_status = Error; + emit logChanged(); + emit statusChanged(); + } + delete node; + return 0; + } + + node->setGeometry(geometry); + node->setFlag(QSGNode::OwnsGeometry, true); + + m_dirtyGeometry = false; + } + + return node; +} + +void QQuickOpenGLShaderEffect::componentComplete() +{ + m_common.updateShader(this, Key::VertexShader); + m_common.updateShader(this, Key::FragmentShader); + QQuickItem::componentComplete(); +} + +void QQuickOpenGLShaderEffect::itemChange(ItemChange change, const ItemChangeData &value) +{ + if (change == QQuickItem::ItemSceneChange) + m_common.updateWindow(value.window); + QQuickItem::itemChange(change, value); +} + +QT_END_NAMESPACE diff --git a/src/quick/items/qquickopenglshadereffect_p.h b/src/quick/items/qquickopenglshadereffect_p.h new file mode 100644 index 0000000000..1b72367e9f --- /dev/null +++ b/src/quick/items/qquickopenglshadereffect_p.h @@ -0,0 +1,209 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKOPENGLSHADEREFFECT_P_H +#define QQUICKOPENGLSHADEREFFECT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuick/qquickitem.h> + +#include <QtQuick/qsgmaterial.h> +#include <private/qtquickglobal_p.h> +#include <private/qsgadaptationlayer_p.h> +#include <private/qquickopenglshadereffectnode_p.h> +#include "qquickshadereffect_p.h" +#include "qquickshadereffectmesh_p.h" + +#include <QtCore/qpointer.h> + +QT_BEGIN_NAMESPACE + +class QSGContext; +class QSignalMapper; +class QQuickOpenGLCustomMaterialShader; + +// Common class for QQuickOpenGLShaderEffect and QQuickCustomParticle. +struct Q_QUICK_PRIVATE_EXPORT QQuickOpenGLShaderEffectCommon +{ + typedef QQuickOpenGLShaderEffectMaterialKey Key; + typedef QQuickOpenGLShaderEffectMaterial::UniformData UniformData; + + ~QQuickOpenGLShaderEffectCommon(); + void disconnectPropertySignals(QQuickItem *item, Key::ShaderType shaderType); + void connectPropertySignals(QQuickItem *item, Key::ShaderType shaderType); + void updateParseLog(bool ignoreAttributes); + void lookThroughShaderCode(QQuickItem *item, Key::ShaderType shaderType, const QByteArray &code); + void updateShader(QQuickItem *item, Key::ShaderType shaderType); + void updateMaterial(QQuickOpenGLShaderEffectNode *node, QQuickOpenGLShaderEffectMaterial *material, + bool updateUniforms, bool updateUniformValues, bool updateTextureProviders); + void updateWindow(QQuickWindow *window); + + // Called by slots in QQuickOpenGLShaderEffect: + void sourceDestroyed(QObject *object); + void propertyChanged(QQuickItem *item, int mappedId, bool *textureProviderChanged); + + Key source; + QVector<QByteArray> attributes; + QVector<UniformData> uniformData[Key::ShaderTypeCount]; + QVector<QSignalMapper *> signalMappers[Key::ShaderTypeCount]; + QString parseLog; +}; + + +class Q_QUICK_PRIVATE_EXPORT QQuickOpenGLShaderEffect : public QQuickItem +{ + Q_OBJECT + Q_PROPERTY(QByteArray fragmentShader READ fragmentShader WRITE setFragmentShader NOTIFY fragmentShaderChanged) + Q_PROPERTY(QByteArray vertexShader READ vertexShader WRITE setVertexShader NOTIFY vertexShaderChanged) + Q_PROPERTY(bool blending READ blending WRITE setBlending NOTIFY blendingChanged) + Q_PROPERTY(QVariant mesh READ mesh WRITE setMesh NOTIFY meshChanged) + Q_PROPERTY(CullMode cullMode READ cullMode WRITE setCullMode NOTIFY cullModeChanged) + Q_PROPERTY(QString log READ log NOTIFY logChanged) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + Q_PROPERTY(bool supportsAtlasTextures READ supportsAtlasTextures WRITE setSupportsAtlasTextures NOTIFY supportsAtlasTexturesChanged REVISION 1) + +public: + enum CullMode + { + NoCulling = QQuickShaderEffect::NoCulling, + BackFaceCulling = QQuickShaderEffect::BackFaceCulling, + FrontFaceCulling = QQuickShaderEffect::FrontFaceCulling + }; + Q_ENUM(CullMode) + + enum Status + { + Compiled, + Uncompiled, + Error + }; + Q_ENUM(Status) + + QQuickOpenGLShaderEffect(QQuickItem *parent = 0); + ~QQuickOpenGLShaderEffect(); + + QByteArray fragmentShader() const { return m_common.source.sourceCode[Key::FragmentShader]; } + void setFragmentShader(const QByteArray &code); + + QByteArray vertexShader() const { return m_common.source.sourceCode[Key::VertexShader]; } + void setVertexShader(const QByteArray &code); + + bool blending() const { return m_blending; } + void setBlending(bool enable); + + QVariant mesh() const; + void setMesh(const QVariant &mesh); + + CullMode cullMode() const { return m_cullMode; } + void setCullMode(CullMode face); + + QString log() const { return m_log; } + Status status() const { return m_status; } + + bool supportsAtlasTextures() const { return m_supportsAtlasTextures; } + void setSupportsAtlasTextures(bool supports); + + QString parseLog(); + + bool event(QEvent *) Q_DECL_OVERRIDE; + +Q_SIGNALS: + void fragmentShaderChanged(); + void vertexShaderChanged(); + void blendingChanged(); + void meshChanged(); + void cullModeChanged(); + void logChanged(); + void statusChanged(); + void supportsAtlasTexturesChanged(); + +protected: + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE; + QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) Q_DECL_OVERRIDE; + void componentComplete() Q_DECL_OVERRIDE; + void itemChange(ItemChange change, const ItemChangeData &value) Q_DECL_OVERRIDE; + +private Q_SLOTS: + void updateGeometry(); + void updateGeometryIfAtlased(); + void updateLogAndStatus(const QString &log, int status); + void sourceDestroyed(QObject *object); + void propertyChanged(int mappedId); + +private: + friend class QQuickCustomMaterialShader; + friend class QQuickOpenGLShaderEffectNode; + + typedef QQuickOpenGLShaderEffectMaterialKey Key; + typedef QQuickOpenGLShaderEffectMaterial::UniformData UniformData; + + QSize m_meshResolution; + QQuickShaderEffectMesh *m_mesh; + QQuickGridMesh m_defaultMesh; + CullMode m_cullMode; + QString m_log; + Status m_status; + + QQuickOpenGLShaderEffectCommon m_common; + + uint m_blending : 1; + uint m_dirtyUniforms : 1; + uint m_dirtyUniformValues : 1; + uint m_dirtyTextureProviders : 1; + uint m_dirtyProgram : 1; + uint m_dirtyParseLog : 1; + uint m_dirtyMesh : 1; + uint m_dirtyGeometry : 1; + uint m_customVertexShader : 1; + uint m_supportsAtlasTextures : 1; +}; + +QT_END_NAMESPACE + +#endif // QQUICKOPENGLSHADEREFFECT_P_H diff --git a/src/quick/items/qquickshadereffectnode.cpp b/src/quick/items/qquickopenglshadereffectnode.cpp index a06fe26a9c..4535aec332 100644 --- a/src/quick/items/qquickshadereffectnode.cpp +++ b/src/quick/items/qquickopenglshadereffectnode.cpp @@ -37,9 +37,9 @@ ** ****************************************************************************/ -#include <private/qquickshadereffectnode_p.h> +#include <private/qquickopenglshadereffectnode_p.h> -#include "qquickshadereffect_p.h" +#include "qquickopenglshadereffect_p.h" #include <QtQuick/qsgtextureprovider.h> #include <QtQuick/private/qsgrenderer_p.h> #include <QtQuick/private/qsgshadersourcebuilder_p.h> @@ -61,29 +61,29 @@ static bool hasAtlasTexture(const QVector<QSGTextureProvider *> &textureProvider class QQuickCustomMaterialShader : public QSGMaterialShader { public: - QQuickCustomMaterialShader(const QQuickShaderEffectMaterialKey &key, const QVector<QByteArray> &attributes); + QQuickCustomMaterialShader(const QQuickOpenGLShaderEffectMaterialKey &key, const QVector<QByteArray> &attributes); void deactivate() Q_DECL_OVERRIDE; void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) Q_DECL_OVERRIDE; char const *const *attributeNames() const Q_DECL_OVERRIDE; protected: - friend class QQuickShaderEffectNode; + friend class QQuickOpenGLShaderEffectNode; void compile() Q_DECL_OVERRIDE; const char *vertexShader() const Q_DECL_OVERRIDE; const char *fragmentShader() const Q_DECL_OVERRIDE; - const QQuickShaderEffectMaterialKey m_key; + const QQuickOpenGLShaderEffectMaterialKey m_key; QVector<QByteArray> m_attributes; QVector<const char *> m_attributeNames; QString m_log; bool m_compiled; - QVector<int> m_uniformLocs[QQuickShaderEffectMaterialKey::ShaderTypeCount]; + QVector<int> m_uniformLocs[QQuickOpenGLShaderEffectMaterialKey::ShaderTypeCount]; uint m_initialized : 1; }; -QQuickCustomMaterialShader::QQuickCustomMaterialShader(const QQuickShaderEffectMaterialKey &key, const QVector<QByteArray> &attributes) +QQuickCustomMaterialShader::QQuickCustomMaterialShader(const QQuickOpenGLShaderEffectMaterialKey &key, const QVector<QByteArray> &attributes) : m_key(key) , m_attributes(attributes) , m_compiled(false) @@ -104,20 +104,20 @@ void QQuickCustomMaterialShader::deactivate() void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) { - typedef QQuickShaderEffectMaterial::UniformData UniformData; + typedef QQuickOpenGLShaderEffectMaterial::UniformData UniformData; Q_ASSERT(newEffect != 0); - QQuickShaderEffectMaterial *material = static_cast<QQuickShaderEffectMaterial *>(newEffect); + QQuickOpenGLShaderEffectMaterial *material = static_cast<QQuickOpenGLShaderEffectMaterial *>(newEffect); if (!material->m_emittedLogChanged && material->m_node) { material->m_emittedLogChanged = true; - emit material->m_node->logAndStatusChanged(m_log, m_compiled ? QQuickShaderEffect::Compiled - : QQuickShaderEffect::Error); + emit material->m_node->logAndStatusChanged(m_log, m_compiled ? QQuickOpenGLShaderEffect::Compiled + : QQuickOpenGLShaderEffect::Error); } int textureProviderIndex = 0; if (!m_initialized) { - for (int shaderType = 0; shaderType < QQuickShaderEffectMaterialKey::ShaderTypeCount; ++shaderType) { + for (int shaderType = 0; shaderType < QQuickOpenGLShaderEffectMaterialKey::ShaderTypeCount; ++shaderType) { Q_ASSERT(m_uniformLocs[shaderType].isEmpty()); m_uniformLocs[shaderType].reserve(material->uniforms[shaderType].size()); for (int i = 0; i < material->uniforms[shaderType].size(); ++i) { @@ -138,7 +138,7 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri } QOpenGLFunctions *functions = state.context()->functions(); - for (int shaderType = 0; shaderType < QQuickShaderEffectMaterialKey::ShaderTypeCount; ++shaderType) { + for (int shaderType = 0; shaderType < QQuickOpenGLShaderEffectMaterialKey::ShaderTypeCount; ++shaderType) { for (int i = 0; i < material->uniforms[shaderType].size(); ++i) { const UniformData &d = material->uniforms[shaderType].at(i); int loc = m_uniformLocs[shaderType].at(i); @@ -230,14 +230,14 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri } functions->glActiveTexture(GL_TEXTURE0); - const QQuickShaderEffectMaterial *oldMaterial = static_cast<const QQuickShaderEffectMaterial *>(oldEffect); + const QQuickOpenGLShaderEffectMaterial *oldMaterial = static_cast<const QQuickOpenGLShaderEffectMaterial *>(oldEffect); if (oldEffect == 0 || material->cullMode != oldMaterial->cullMode) { switch (material->cullMode) { - case QQuickShaderEffectMaterial::FrontFaceCulling: + case QQuickShaderEffect::FrontFaceCulling: functions->glEnable(GL_CULL_FACE); functions->glCullFace(GL_FRONT); break; - case QQuickShaderEffectMaterial::BackFaceCulling: + case QQuickShaderEffect::BackFaceCulling: functions->glEnable(GL_CULL_FACE); functions->glCullFace(GL_BACK); break; @@ -322,16 +322,16 @@ void QQuickCustomMaterialShader::compile() const char *QQuickCustomMaterialShader::vertexShader() const { - return m_key.sourceCode[QQuickShaderEffectMaterialKey::VertexShader].constData(); + return m_key.sourceCode[QQuickOpenGLShaderEffectMaterialKey::VertexShader].constData(); } const char *QQuickCustomMaterialShader::fragmentShader() const { - return m_key.sourceCode[QQuickShaderEffectMaterialKey::FragmentShader].constData(); + return m_key.sourceCode[QQuickOpenGLShaderEffectMaterialKey::FragmentShader].constData(); } -bool QQuickShaderEffectMaterialKey::operator == (const QQuickShaderEffectMaterialKey &other) const +bool QQuickOpenGLShaderEffectMaterialKey::operator == (const QQuickOpenGLShaderEffectMaterialKey &other) const { if (className != other.className) return false; @@ -342,39 +342,39 @@ bool QQuickShaderEffectMaterialKey::operator == (const QQuickShaderEffectMateria return true; } -bool QQuickShaderEffectMaterialKey::operator != (const QQuickShaderEffectMaterialKey &other) const +bool QQuickOpenGLShaderEffectMaterialKey::operator != (const QQuickOpenGLShaderEffectMaterialKey &other) const { return !(*this == other); } -uint qHash(const QQuickShaderEffectMaterialKey &key) +uint qHash(const QQuickOpenGLShaderEffectMaterialKey &key) { uint hash = qHash((const void *)key.className); - typedef QQuickShaderEffectMaterialKey Key; + typedef QQuickOpenGLShaderEffectMaterialKey Key; for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) hash = hash * 31337 + qHash(key.sourceCode[shaderType]); return hash; } -class QQuickShaderEffectMaterialCache : public QObject +class QQuickOpenGLShaderEffectMaterialCache : public QObject { Q_OBJECT public: - static QQuickShaderEffectMaterialCache *get(bool create = true) { + static QQuickOpenGLShaderEffectMaterialCache *get(bool create = true) { QOpenGLContext *ctx = QOpenGLContext::currentContext(); - QQuickShaderEffectMaterialCache *me = ctx->findChild<QQuickShaderEffectMaterialCache *>(QStringLiteral("__qt_ShaderEffectCache"), Qt::FindDirectChildrenOnly); + QQuickOpenGLShaderEffectMaterialCache *me = ctx->findChild<QQuickOpenGLShaderEffectMaterialCache *>(QStringLiteral("__qt_ShaderEffectCache"), Qt::FindDirectChildrenOnly); if (!me && create) { - me = new QQuickShaderEffectMaterialCache(); + me = new QQuickOpenGLShaderEffectMaterialCache(); me->setObjectName(QStringLiteral("__qt_ShaderEffectCache")); me->setParent(ctx); } return me; } - QHash<QQuickShaderEffectMaterialKey, QSGMaterialType *> cache; + QHash<QQuickOpenGLShaderEffectMaterialKey, QSGMaterialType *> cache; }; -QQuickShaderEffectMaterial::QQuickShaderEffectMaterial(QQuickShaderEffectNode *node) - : cullMode(NoCulling) +QQuickOpenGLShaderEffectMaterial::QQuickOpenGLShaderEffectMaterial(QQuickOpenGLShaderEffectNode *node) + : cullMode(QQuickShaderEffect::NoCulling) , geometryUsesTextureSubRect(false) , m_node(node) , m_emittedLogChanged(false) @@ -382,17 +382,17 @@ QQuickShaderEffectMaterial::QQuickShaderEffectMaterial(QQuickShaderEffectNode *n setFlag(Blending | RequiresFullMatrix, true); } -QSGMaterialType *QQuickShaderEffectMaterial::type() const +QSGMaterialType *QQuickOpenGLShaderEffectMaterial::type() const { return m_type; } -QSGMaterialShader *QQuickShaderEffectMaterial::createShader() const +QSGMaterialShader *QQuickOpenGLShaderEffectMaterial::createShader() const { return new QQuickCustomMaterialShader(m_source, attributes); } -bool QQuickShaderEffectMaterial::UniformData::operator == (const UniformData &other) const +bool QQuickOpenGLShaderEffectMaterial::UniformData::operator == (const UniformData &other) const { if (specialType != other.specialType) return false; @@ -409,14 +409,14 @@ bool QQuickShaderEffectMaterial::UniformData::operator == (const UniformData &ot } } -int QQuickShaderEffectMaterial::compare(const QSGMaterial *o) const +int QQuickOpenGLShaderEffectMaterial::compare(const QSGMaterial *o) const { - const QQuickShaderEffectMaterial *other = static_cast<const QQuickShaderEffectMaterial *>(o); + const QQuickOpenGLShaderEffectMaterial *other = static_cast<const QQuickOpenGLShaderEffectMaterial *>(o); if ((hasAtlasTexture(textureProviders) && !geometryUsesTextureSubRect) || (hasAtlasTexture(other->textureProviders) && !other->geometryUsesTextureSubRect)) return 1; if (cullMode != other->cullMode) return 1; - for (int shaderType = 0; shaderType < QQuickShaderEffectMaterialKey::ShaderTypeCount; ++shaderType) { + for (int shaderType = 0; shaderType < QQuickOpenGLShaderEffectMaterialKey::ShaderTypeCount; ++shaderType) { if (uniforms[shaderType] != other->uniforms[shaderType]) return 1; } @@ -440,12 +440,12 @@ int QQuickShaderEffectMaterial::compare(const QSGMaterial *o) const return 0; } -void QQuickShaderEffectMaterial::setProgramSource(const QQuickShaderEffectMaterialKey &source) +void QQuickOpenGLShaderEffectMaterial::setProgramSource(const QQuickOpenGLShaderEffectMaterialKey &source) { m_source = source; m_emittedLogChanged = false; - QQuickShaderEffectMaterialCache *cache = QQuickShaderEffectMaterialCache::get(); + QQuickOpenGLShaderEffectMaterialCache *cache = QQuickOpenGLShaderEffectMaterialCache::get(); m_type = cache->cache.value(m_source); if (!m_type) { m_type = new QSGMaterialType(); @@ -453,16 +453,16 @@ void QQuickShaderEffectMaterial::setProgramSource(const QQuickShaderEffectMateri } } -void QQuickShaderEffectMaterial::cleanupMaterialCache() +void QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache() { - QQuickShaderEffectMaterialCache *cache = QQuickShaderEffectMaterialCache::get(false); + QQuickOpenGLShaderEffectMaterialCache *cache = QQuickOpenGLShaderEffectMaterialCache::get(false); if (cache) { qDeleteAll(cache->cache.values()); delete cache; } } -void QQuickShaderEffectMaterial::updateTextures() const +void QQuickOpenGLShaderEffectMaterial::updateTextures() const { for (int i = 0; i < textureProviders.size(); ++i) { if (QSGTextureProvider *provider = textureProviders.at(i)) { @@ -472,7 +472,7 @@ void QQuickShaderEffectMaterial::updateTextures() const } } -void QQuickShaderEffectMaterial::invalidateTextureProvider(QSGTextureProvider *provider) +void QQuickOpenGLShaderEffectMaterial::invalidateTextureProvider(QSGTextureProvider *provider) { for (int i = 0; i < textureProviders.size(); ++i) { if (provider == textureProviders.at(i)) @@ -481,7 +481,7 @@ void QQuickShaderEffectMaterial::invalidateTextureProvider(QSGTextureProvider *p } -QQuickShaderEffectNode::QQuickShaderEffectNode() +QQuickOpenGLShaderEffectNode::QQuickOpenGLShaderEffectNode() { QSGNode::setFlag(UsePreprocess, true); @@ -490,28 +490,28 @@ QQuickShaderEffectNode::QQuickShaderEffectNode() #endif } -QQuickShaderEffectNode::~QQuickShaderEffectNode() +QQuickOpenGLShaderEffectNode::~QQuickOpenGLShaderEffectNode() { } -void QQuickShaderEffectNode::markDirtyTexture() +void QQuickOpenGLShaderEffectNode::markDirtyTexture() { markDirty(DirtyMaterial); Q_EMIT dirtyTexture(); } -void QQuickShaderEffectNode::textureProviderDestroyed(QObject *object) +void QQuickOpenGLShaderEffectNode::textureProviderDestroyed(QObject *object) { Q_ASSERT(material()); - static_cast<QQuickShaderEffectMaterial *>(material())->invalidateTextureProvider(static_cast<QSGTextureProvider *>(object)); + static_cast<QQuickOpenGLShaderEffectMaterial *>(material())->invalidateTextureProvider(static_cast<QSGTextureProvider *>(object)); } -void QQuickShaderEffectNode::preprocess() +void QQuickOpenGLShaderEffectNode::preprocess() { Q_ASSERT(material()); - static_cast<QQuickShaderEffectMaterial *>(material())->updateTextures(); + static_cast<QQuickOpenGLShaderEffectMaterial *>(material())->updateTextures(); } -#include "qquickshadereffectnode.moc" +#include "qquickopenglshadereffectnode.moc" QT_END_NAMESPACE diff --git a/src/quick/items/qquickshadereffectnode_p.h b/src/quick/items/qquickopenglshadereffectnode_p.h index cc016d13a7..1adfa4dd29 100644 --- a/src/quick/items/qquickshadereffectnode_p.h +++ b/src/quick/items/qquickopenglshadereffectnode_p.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef QQUICKSHADEREFFECTNODE_P_H -#define QQUICKSHADEREFFECTNODE_P_H +#ifndef QQUICKOPENGLSHADEREFFECTNODE_P_H +#define QQUICKOPENGLSHADEREFFECTNODE_P_H // // W A R N I N G @@ -56,13 +56,14 @@ #include <QtQuick/qsgtextureprovider.h> #include <QtQuick/qquickitem.h> #include <private/qtquickglobal_p.h> +#include <private/qquickshadereffect_p.h> #include <QtCore/qsharedpointer.h> #include <QtCore/qpointer.h> QT_BEGIN_NAMESPACE -struct QQuickShaderEffectMaterialKey { +struct QQuickOpenGLShaderEffectMaterialKey { enum ShaderType { VertexShader, @@ -73,15 +74,15 @@ struct QQuickShaderEffectMaterialKey { QByteArray sourceCode[ShaderTypeCount]; const char *className; - bool operator == (const QQuickShaderEffectMaterialKey &other) const; - bool operator != (const QQuickShaderEffectMaterialKey &other) const; + bool operator == (const QQuickOpenGLShaderEffectMaterialKey &other) const; + bool operator != (const QQuickOpenGLShaderEffectMaterialKey &other) const; }; -uint qHash(const QQuickShaderEffectMaterialKey &key); +uint qHash(const QQuickOpenGLShaderEffectMaterialKey &key); class QQuickCustomMaterialShader; -class QQuickShaderEffectNode; -class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffectMaterial : public QSGMaterial +class QQuickOpenGLShaderEffectNode; +class Q_QUICK_PRIVATE_EXPORT QQuickOpenGLShaderEffectMaterial : public QSGMaterial { public: struct UniformData @@ -95,25 +96,18 @@ public: bool operator == (const UniformData &other) const; }; - enum CullMode - { - NoCulling, - BackFaceCulling, - FrontFaceCulling - }; - - explicit QQuickShaderEffectMaterial(QQuickShaderEffectNode *node = 0); + explicit QQuickOpenGLShaderEffectMaterial(QQuickOpenGLShaderEffectNode *node = 0); QSGMaterialType *type() const Q_DECL_OVERRIDE; QSGMaterialShader *createShader() const Q_DECL_OVERRIDE; int compare(const QSGMaterial *other) const Q_DECL_OVERRIDE; QVector<QByteArray> attributes; - QVector<UniformData> uniforms[QQuickShaderEffectMaterialKey::ShaderTypeCount]; + QVector<UniformData> uniforms[QQuickOpenGLShaderEffectMaterialKey::ShaderTypeCount]; QVector<QSGTextureProvider *> textureProviders; - CullMode cullMode; + QQuickShaderEffect::CullMode cullMode; bool geometryUsesTextureSubRect; - void setProgramSource(const QQuickShaderEffectMaterialKey &source); + void setProgramSource(const QQuickOpenGLShaderEffectMaterialKey &source); void updateTextures() const; void invalidateTextureProvider(QSGTextureProvider *provider); @@ -128,21 +122,21 @@ protected: // type. The type is cleaned up in cleanupMaterialCache() which is called // when the GL context is shut down. QSGMaterialType *m_type; - QQuickShaderEffectMaterialKey m_source; + QQuickOpenGLShaderEffectMaterialKey m_source; - QQuickShaderEffectNode *m_node; + QQuickOpenGLShaderEffectNode *m_node; bool m_emittedLogChanged; }; class QSGShaderEffectMesh; -class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffectNode : public QObject, public QSGGeometryNode +class Q_QUICK_PRIVATE_EXPORT QQuickOpenGLShaderEffectNode : public QObject, public QSGGeometryNode { Q_OBJECT public: - QQuickShaderEffectNode(); - virtual ~QQuickShaderEffectNode(); + QQuickOpenGLShaderEffectNode(); + virtual ~QQuickOpenGLShaderEffectNode(); void preprocess() Q_DECL_OVERRIDE; @@ -157,4 +151,4 @@ private Q_SLOTS: QT_END_NAMESPACE -#endif // QQUICKSHADEREFFECTNODE_P_H +#endif // QQUICKOPENGLSHADEREFFECTNODE_P_H diff --git a/src/quick/items/qquickrendercontrol.cpp b/src/quick/items/qquickrendercontrol.cpp index 8ac002ed2c..ec9a104812 100644 --- a/src/quick/items/qquickrendercontrol.cpp +++ b/src/quick/items/qquickrendercontrol.cpp @@ -47,6 +47,7 @@ #ifndef QT_NO_OPENGL # include <QtGui/QOpenGLContext> # include <QtQuick/private/qsgdefaultrendercontext_p.h> +# include <QtQuick/private/qquickopenglshadereffectnode_p.h> #endif #include <QtGui/private/qguiapplication_p.h> #include <qpa/qplatformintegration.h> @@ -57,7 +58,6 @@ #include <QtQuick/private/qquickwindow_p.h> #include <QtCore/private/qobject_p.h> -#include <private/qquickshadereffectnode_p.h> QT_BEGIN_NAMESPACE #ifndef QT_NO_OPENGL @@ -186,7 +186,9 @@ void QQuickRenderControlPrivate::windowDestroyed() delete QQuickWindowPrivate::get(window)->animationController; QQuickWindowPrivate::get(window)->animationController = 0; - QQuickShaderEffectMaterial::cleanupMaterialCache(); +#ifndef QT_NO_OPENGL + QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache(); +#endif window = 0; } diff --git a/src/quick/items/qquickshadereffect.cpp b/src/quick/items/qquickshadereffect.cpp index a2c2ebb380..e604e80d96 100644 --- a/src/quick/items/qquickshadereffect.cpp +++ b/src/quick/items/qquickshadereffect.cpp @@ -38,493 +38,9 @@ ****************************************************************************/ #include <private/qquickshadereffect_p.h> -#include <private/qquickshadereffectnode_p.h> - -#include <QtQuick/qsgmaterial.h> -#include <QtQuick/private/qsgshadersourcebuilder_p.h> -#include "qquickitem_p.h" - -#include <QtQuick/private/qsgcontext_p.h> -#include <QtQuick/qsgtextureprovider.h> -#include "qquickwindow.h" - -#include "qquickimage_p.h" -#include "qquickshadereffectsource_p.h" -#include "qquickshadereffectmesh_p.h" - -#include <QtCore/qsignalmapper.h> QT_BEGIN_NAMESPACE -namespace { - - enum VariableQualifier { - AttributeQualifier, - UniformQualifier - }; - - inline bool qt_isalpha(char c) - { - char ch = c | 0x20; - return (ch >= 'a' && ch <= 'z') || c == '_'; - } - - inline bool qt_isalnum(char c) - { - return qt_isalpha(c) || (c >= '0' && c <= '9'); - } - - inline bool qt_isspace(char c) - { - return c == ' ' || (c >= 0x09 && c <= 0x0d); - } - - // Returns -1 if not found, returns index to first character after the name if found. - int qt_search_for_variable(const char *s, int length, int index, VariableQualifier &decl, - int &typeIndex, int &typeLength, - int &nameIndex, int &nameLength, - QQuickShaderEffectCommon::Key::ShaderType shaderType) - { - enum Identifier { - QualifierIdentifier, // Base state - PrecisionIdentifier, - TypeIdentifier, - NameIdentifier - }; - Identifier expected = QualifierIdentifier; - bool compilerDirectiveExpected = index == 0; - - while (index < length) { - // Skip whitespace. - while (qt_isspace(s[index])) { - compilerDirectiveExpected |= s[index] == '\n'; - ++index; - } - - if (qt_isalpha(s[index])) { - // Read identifier. - int idIndex = index; - ++index; - while (qt_isalnum(s[index])) - ++index; - int idLength = index - idIndex; - - const int attrLen = sizeof("attribute") - 1; - const int inLen = sizeof("in") - 1; - const int uniLen = sizeof("uniform") - 1; - const int loLen = sizeof("lowp") - 1; - const int medLen = sizeof("mediump") - 1; - const int hiLen = sizeof("highp") - 1; - - switch (expected) { - case QualifierIdentifier: - if (idLength == attrLen && qstrncmp("attribute", s + idIndex, attrLen) == 0) { - decl = AttributeQualifier; - expected = PrecisionIdentifier; - } else if (shaderType == QQuickShaderEffectCommon::Key::VertexShader - && idLength == inLen && qstrncmp("in", s + idIndex, inLen) == 0) { - decl = AttributeQualifier; - expected = PrecisionIdentifier; - } else if (idLength == uniLen && qstrncmp("uniform", s + idIndex, uniLen) == 0) { - decl = UniformQualifier; - expected = PrecisionIdentifier; - } - break; - case PrecisionIdentifier: - if ((idLength == loLen && qstrncmp("lowp", s + idIndex, loLen) == 0) - || (idLength == medLen && qstrncmp("mediump", s + idIndex, medLen) == 0) - || (idLength == hiLen && qstrncmp("highp", s + idIndex, hiLen) == 0)) - { - expected = TypeIdentifier; - break; - } - // Fall through. - case TypeIdentifier: - typeIndex = idIndex; - typeLength = idLength; - expected = NameIdentifier; - break; - case NameIdentifier: - nameIndex = idIndex; - nameLength = idLength; - return index; // Attribute or uniform declaration found. Return result. - default: - break; - } - } else if (s[index] == '#' && compilerDirectiveExpected) { - // Skip compiler directives. - ++index; - while (index < length && (s[index] != '\n' || s[index - 1] == '\\')) - ++index; - } else if (s[index] == '/' && s[index + 1] == '/') { - // Skip comments. - index += 2; - while (index < length && s[index] != '\n') - ++index; - } else if (s[index] == '/' && s[index + 1] == '*') { - // Skip comments. - index += 2; - while (index < length && (s[index] != '*' || s[index + 1] != '/')) - ++index; - if (index < length) - index += 2; // Skip star-slash. - } else { - expected = QualifierIdentifier; - ++index; - } - compilerDirectiveExpected = false; - } - return -1; - } -} - - - -QQuickShaderEffectCommon::~QQuickShaderEffectCommon() -{ - for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) - qDeleteAll(signalMappers[shaderType]); -} - -void QQuickShaderEffectCommon::disconnectPropertySignals(QQuickItem *item, Key::ShaderType shaderType) -{ - for (int i = 0; i < uniformData[shaderType].size(); ++i) { - if (signalMappers[shaderType].at(i) == 0) - continue; - const UniformData &d = uniformData[shaderType].at(i); - QSignalMapper *mapper = signalMappers[shaderType].at(i); - QObject::disconnect(item, 0, mapper, SLOT(map())); - QObject::disconnect(mapper, SIGNAL(mapped(int)), item, SLOT(propertyChanged(int))); - if (d.specialType == UniformData::Sampler) { - QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value)); - if (source) { - if (item->window()) - QQuickItemPrivate::get(source)->derefWindow(); - QObject::disconnect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*))); - } - } - } -} - -void QQuickShaderEffectCommon::connectPropertySignals(QQuickItem *item, Key::ShaderType shaderType) -{ - for (int i = 0; i < uniformData[shaderType].size(); ++i) { - if (signalMappers[shaderType].at(i) == 0) - continue; - const UniformData &d = uniformData[shaderType].at(i); - int pi = item->metaObject()->indexOfProperty(d.name.constData()); - if (pi >= 0) { - QMetaProperty mp = item->metaObject()->property(pi); - if (!mp.hasNotifySignal()) - qWarning("QQuickShaderEffect: property '%s' does not have notification method!", d.name.constData()); - const QByteArray signalName = '2' + mp.notifySignal().methodSignature(); - QSignalMapper *mapper = signalMappers[shaderType].at(i); - QObject::connect(item, signalName, mapper, SLOT(map())); - QObject::connect(mapper, SIGNAL(mapped(int)), item, SLOT(propertyChanged(int))); - } else { - // If the source is set via a dynamic property, like the layer is, then we need this - // check to disable the warning. - if (!item->property(d.name.constData()).isValid()) - qWarning("QQuickShaderEffect: '%s' does not have a matching property!", d.name.constData()); - } - - if (d.specialType == UniformData::Sampler) { - QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value)); - if (source) { - if (item->window()) - QQuickItemPrivate::get(source)->refWindow(item->window()); - QObject::connect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*))); - } - } - } -} - -void QQuickShaderEffectCommon::updateParseLog(bool ignoreAttributes) -{ - parseLog.clear(); - if (!ignoreAttributes) { - if (!attributes.contains(qtPositionAttributeName())) { - parseLog += QLatin1String("Warning: Missing reference to \'"); - parseLog += QLatin1String(qtPositionAttributeName()); - parseLog += QLatin1String("\'.\n"); - } - if (!attributes.contains(qtTexCoordAttributeName())) { - parseLog += QLatin1String("Warning: Missing reference to \'"); - parseLog += QLatin1String(qtTexCoordAttributeName()); - parseLog += QLatin1String("\'.\n"); - } - } - bool respectsMatrix = false; - bool respectsOpacity = false; - for (int i = 0; i < uniformData[Key::VertexShader].size(); ++i) - respectsMatrix |= uniformData[Key::VertexShader].at(i).specialType == UniformData::Matrix; - for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { - for (int i = 0; i < uniformData[shaderType].size(); ++i) - respectsOpacity |= uniformData[shaderType].at(i).specialType == UniformData::Opacity; - } - if (!respectsMatrix) - parseLog += QLatin1String("Warning: Vertex shader is missing reference to \'qt_Matrix\'.\n"); - if (!respectsOpacity) - parseLog += QLatin1String("Warning: Shaders are missing reference to \'qt_Opacity\'.\n"); -} - -void QQuickShaderEffectCommon::lookThroughShaderCode(QQuickItem *item, Key::ShaderType shaderType, const QByteArray &code) -{ - int index = 0; - int typeIndex = -1; - int typeLength = 0; - int nameIndex = -1; - int nameLength = 0; - const char *s = code.constData(); - VariableQualifier decl = AttributeQualifier; - while ((index = qt_search_for_variable(s, code.size(), index, decl, typeIndex, typeLength, - nameIndex, nameLength, shaderType)) != -1) - { - if (decl == AttributeQualifier) { - if (shaderType == Key::VertexShader) - attributes.append(QByteArray(s + nameIndex, nameLength)); - } else { - Q_ASSERT(decl == UniformQualifier); - - const int sampLen = sizeof("sampler2D") - 1; - const int opLen = sizeof("qt_Opacity") - 1; - const int matLen = sizeof("qt_Matrix") - 1; - const int srLen = sizeof("qt_SubRect_") - 1; - - UniformData d; - QSignalMapper *mapper = 0; - d.name = QByteArray(s + nameIndex, nameLength); - if (nameLength == opLen && qstrncmp("qt_Opacity", s + nameIndex, opLen) == 0) { - d.specialType = UniformData::Opacity; - } else if (nameLength == matLen && qstrncmp("qt_Matrix", s + nameIndex, matLen) == 0) { - d.specialType = UniformData::Matrix; - } else if (nameLength > srLen && qstrncmp("qt_SubRect_", s + nameIndex, srLen) == 0) { - d.specialType = UniformData::SubRect; - } else { - mapper = new QSignalMapper; - mapper->setMapping(item, uniformData[shaderType].size() | (shaderType << 16)); - d.value = item->property(d.name.constData()); - bool sampler = typeLength == sampLen && qstrncmp("sampler2D", s + typeIndex, sampLen) == 0; - d.specialType = sampler ? UniformData::Sampler : UniformData::None; - } - uniformData[shaderType].append(d); - signalMappers[shaderType].append(mapper); - } - } -} - -void QQuickShaderEffectCommon::updateShader(QQuickItem *item, Key::ShaderType shaderType) -{ - disconnectPropertySignals(item, shaderType); - qDeleteAll(signalMappers[shaderType]); - uniformData[shaderType].clear(); - signalMappers[shaderType].clear(); - if (shaderType == Key::VertexShader) - attributes.clear(); - - const QByteArray &code = source.sourceCode[shaderType]; - if (code.isEmpty()) { - // Optimize for default code. - if (shaderType == Key::VertexShader) { - attributes.append(QByteArray(qtPositionAttributeName())); - attributes.append(QByteArray(qtTexCoordAttributeName())); - UniformData d; - d.name = "qt_Matrix"; - d.specialType = UniformData::Matrix; - uniformData[Key::VertexShader].append(d); - signalMappers[Key::VertexShader].append(0); - } else if (shaderType == Key::FragmentShader) { - UniformData d; - d.name = "qt_Opacity"; - d.specialType = UniformData::Opacity; - uniformData[Key::FragmentShader].append(d); - signalMappers[Key::FragmentShader].append(0); - QSignalMapper *mapper = new QSignalMapper; - mapper->setMapping(item, 1 | (Key::FragmentShader << 16)); - const char *sourceName = "source"; - d.name = sourceName; - d.value = item->property(sourceName); - d.specialType = UniformData::Sampler; - uniformData[Key::FragmentShader].append(d); - signalMappers[Key::FragmentShader].append(mapper); - } - } else { - lookThroughShaderCode(item, shaderType, code); - } - - connectPropertySignals(item, shaderType); -} - -void QQuickShaderEffectCommon::updateMaterial(QQuickShaderEffectNode *node, - QQuickShaderEffectMaterial *material, - bool updateUniforms, bool updateUniformValues, - bool updateTextureProviders) -{ - if (updateUniforms) { - for (int i = 0; i < material->textureProviders.size(); ++i) { - QSGTextureProvider *t = material->textureProviders.at(i); - if (t) { - QObject::disconnect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture())); - QObject::disconnect(t, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*))); - } - } - - // First make room in the textureProviders array. Set to proper value further down. - int textureProviderCount = 0; - for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { - for (int i = 0; i < uniformData[shaderType].size(); ++i) { - if (uniformData[shaderType].at(i).specialType == UniformData::Sampler) - ++textureProviderCount; - } - material->uniforms[shaderType] = uniformData[shaderType]; - } - material->textureProviders.fill(0, textureProviderCount); - updateUniformValues = false; - updateTextureProviders = true; - } - - if (updateUniformValues) { - for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { - Q_ASSERT(uniformData[shaderType].size() == material->uniforms[shaderType].size()); - for (int i = 0; i < uniformData[shaderType].size(); ++i) - material->uniforms[shaderType][i].value = uniformData[shaderType].at(i).value; - } - } - - if (updateTextureProviders) { - int index = 0; - for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { - for (int i = 0; i < uniformData[shaderType].size(); ++i) { - const UniformData &d = uniformData[shaderType].at(i); - if (d.specialType != UniformData::Sampler) - continue; - QSGTextureProvider *oldProvider = material->textureProviders.at(index); - QSGTextureProvider *newProvider = 0; - QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value)); - if (source && source->isTextureProvider()) - newProvider = source->textureProvider(); - if (newProvider != oldProvider) { - if (oldProvider) { - QObject::disconnect(oldProvider, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture())); - QObject::disconnect(oldProvider, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*))); - } - if (newProvider) { - Q_ASSERT_X(newProvider->thread() == QThread::currentThread(), - "QQuickShaderEffect::updatePaintNode", - "Texture provider must belong to the rendering thread"); - QObject::connect(newProvider, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture())); - QObject::connect(newProvider, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*))); - } else { - const char *typeName = source ? source->metaObject()->className() : d.value.typeName(); - qWarning("ShaderEffect: Property '%s' is not assigned a valid texture provider (%s).", - d.name.constData(), typeName); - } - material->textureProviders[index] = newProvider; - } - ++index; - } - } - Q_ASSERT(index == material->textureProviders.size()); - } -} - -void QQuickShaderEffectCommon::updateWindow(QQuickWindow *window) -{ - // See comment in QQuickShaderEffectCommon::propertyChanged(). - if (window) { - for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { - for (int i = 0; i < uniformData[shaderType].size(); ++i) { - const UniformData &d = uniformData[shaderType].at(i); - if (d.specialType == UniformData::Sampler) { - QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value)); - if (source) - QQuickItemPrivate::get(source)->refWindow(window); - } - } - } - } else { - for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { - for (int i = 0; i < uniformData[shaderType].size(); ++i) { - const UniformData &d = uniformData[shaderType].at(i); - if (d.specialType == UniformData::Sampler) { - QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value)); - if (source) - QQuickItemPrivate::get(source)->derefWindow(); - } - } - } - } -} - -void QQuickShaderEffectCommon::sourceDestroyed(QObject *object) -{ - for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { - for (int i = 0; i < uniformData[shaderType].size(); ++i) { - UniformData &d = uniformData[shaderType][i]; - if (d.specialType == UniformData::Sampler && d.value.canConvert<QObject *>()) { - if (qvariant_cast<QObject *>(d.value) == object) - d.value = QVariant(); - } - } - } -} - -static bool qquick_uniqueInUniformData(QQuickItem *source, const QVector<QQuickShaderEffectMaterial::UniformData> *uniformData, int typeToSkip, int indexToSkip) -{ - for (int s=0; s<QQuickShaderEffectMaterialKey::ShaderTypeCount; ++s) { - for (int i=0; i<uniformData[s].size(); ++i) { - if (s == typeToSkip && i == indexToSkip) - continue; - const QQuickShaderEffectMaterial::UniformData &d = uniformData[s][i]; - if (d.specialType == QQuickShaderEffectMaterial::UniformData::Sampler && qvariant_cast<QObject *>(d.value) == source) - return false; - } - } - return true; -} - -void QQuickShaderEffectCommon::propertyChanged(QQuickItem *item, int mappedId, - bool *textureProviderChanged) -{ - Key::ShaderType shaderType = Key::ShaderType(mappedId >> 16); - int index = mappedId & 0xffff; - UniformData &d = uniformData[shaderType][index]; - if (d.specialType == UniformData::Sampler) { - QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value)); - if (source) { - if (item->window()) - QQuickItemPrivate::get(source)->derefWindow(); - - // QObject::disconnect() will disconnect all matching connections. If the same - // source has been attached to two separate samplers, then changing one of them - // would trigger both to be disconnected. Without the connection we'll end up - // with a dangling pointer in the uniformData. - if (qquick_uniqueInUniformData(source, uniformData, shaderType, index)) - QObject::disconnect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*))); - } - - d.value = item->property(d.name.constData()); - - source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value)); - if (source) { - // 'source' needs a window to get a scene graph node. It usually gets one through its - // parent, but if the source item is "inline" rather than a reference -- i.e. - // "property variant source: Image { }" instead of "property variant source: foo" -- it - // will not get a parent. In those cases, 'source' should get the window from 'item'. - if (item->window()) - QQuickItemPrivate::get(source)->refWindow(item->window()); - QObject::connect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*))); - } - if (textureProviderChanged) - *textureProviderChanged = true; - } else { - d.value = item->property(d.name.constData()); - if (textureProviderChanged) - *textureProviderChanged = false; - } -} - - /*! \qmltype ShaderEffect \instantiates QQuickShaderEffect @@ -675,30 +191,10 @@ void QQuickShaderEffectCommon::propertyChanged(QQuickItem *item, int mappedId, QQuickShaderEffect::QQuickShaderEffect(QQuickItem *parent) : QQuickItem(parent) - , m_meshResolution(1, 1) - , m_mesh(0) - , m_cullMode(NoCulling) - , m_status(Uncompiled) - , m_blending(true) - , m_dirtyUniforms(true) - , m_dirtyUniformValues(true) - , m_dirtyTextureProviders(true) - , m_dirtyProgram(true) - , m_dirtyParseLog(true) - , m_dirtyMesh(true) - , m_dirtyGeometry(true) - , m_customVertexShader(false) - , m_supportsAtlasTextures(false) { setFlag(QQuickItem::ItemHasContents); } -QQuickShaderEffect::~QQuickShaderEffect() -{ - for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) - m_common.disconnectPropertySignals(this, Key::ShaderType(shaderType)); -} - /*! \qmlproperty string QtQuick::ShaderEffect::fragmentShader @@ -708,23 +204,14 @@ QQuickShaderEffect::~QQuickShaderEffect() sampler2D named "source". */ -void QQuickShaderEffect::setFragmentShader(const QByteArray &code) +QByteArray QQuickShaderEffect::fragmentShader() const { - if (m_common.source.sourceCode[Key::FragmentShader].constData() == code.constData()) - return; - m_common.source.sourceCode[Key::FragmentShader] = code; - m_dirtyProgram = true; - m_dirtyParseLog = true; - - if (isComponentComplete()) - m_common.updateShader(this, Key::FragmentShader); + return QByteArray(); +} - update(); - if (m_status != Uncompiled) { - m_status = Uncompiled; - emit statusChanged(); - } - emit fragmentShaderChanged(); +void QQuickShaderEffect::setFragmentShader(const QByteArray &code) +{ + Q_UNUSED(code); } /*! @@ -735,24 +222,14 @@ void QQuickShaderEffect::setFragmentShader(const QByteArray &code) shader as "varying highp vec2 qt_TexCoord0". */ -void QQuickShaderEffect::setVertexShader(const QByteArray &code) +QByteArray QQuickShaderEffect::vertexShader() const { - if (m_common.source.sourceCode[Key::VertexShader].constData() == code.constData()) - return; - m_common.source.sourceCode[Key::VertexShader] = code; - m_dirtyProgram = true; - m_dirtyParseLog = true; - m_customVertexShader = true; - - if (isComponentComplete()) - m_common.updateShader(this, Key::VertexShader); + return QByteArray(); +} - update(); - if (m_status != Uncompiled) { - m_status = Uncompiled; - emit statusChanged(); - } - emit vertexShaderChanged(); +void QQuickShaderEffect::setVertexShader(const QByteArray &code) +{ + Q_UNUSED(code); } /*! @@ -764,15 +241,14 @@ void QQuickShaderEffect::setVertexShader(const QByteArray &code) property to false when blending is not needed. The default value is true. */ -void QQuickShaderEffect::setBlending(bool enable) +bool QQuickShaderEffect::blending() const { - if (blending() == enable) - return; - - m_blending = enable; - update(); + return true; +} - emit blendingChanged(); +void QQuickShaderEffect::setBlending(bool enable) +{ + Q_UNUSED(enable); } /*! @@ -790,44 +266,12 @@ void QQuickShaderEffect::setBlending(bool enable) QVariant QQuickShaderEffect::mesh() const { - return m_mesh ? qVariantFromValue(static_cast<QObject *>(m_mesh)) - : qVariantFromValue(m_meshResolution); + return QVariant(); } void QQuickShaderEffect::setMesh(const QVariant &mesh) { - QQuickShaderEffectMesh *newMesh = qobject_cast<QQuickShaderEffectMesh *>(qvariant_cast<QObject *>(mesh)); - if (newMesh && newMesh == m_mesh) - return; - if (m_mesh) - disconnect(m_mesh, SIGNAL(geometryChanged()), this, 0); - m_mesh = newMesh; - if (m_mesh) { - connect(m_mesh, SIGNAL(geometryChanged()), this, SLOT(updateGeometry())); - } else { - if (mesh.canConvert<QSize>()) { - m_meshResolution = mesh.toSize(); - } else { - QList<QByteArray> res = mesh.toByteArray().split('x'); - bool ok = res.size() == 2; - if (ok) { - int w = res.at(0).toInt(&ok); - if (ok) { - int h = res.at(1).toInt(&ok); - if (ok) - m_meshResolution = QSize(w, h); - } - } - if (!ok) - qWarning("ShaderEffect: mesh property must be size or object deriving from QQuickShaderEffectMesh."); - } - m_defaultMesh.setResolution(m_meshResolution); - } - - m_dirtyMesh = true; - m_dirtyParseLog = true; - update(); - emit meshChanged(); + Q_UNUSED(mesh); } /*! @@ -844,13 +288,14 @@ void QQuickShaderEffect::setMesh(const QVariant &mesh) The default is NoCulling. */ +QQuickShaderEffect::CullMode QQuickShaderEffect::cullMode() const +{ + return NoCulling; +} + void QQuickShaderEffect::setCullMode(CullMode face) { - if (face == m_cullMode) - return; - m_cullMode = face; - update(); - emit cullModeChanged(); + Q_UNUSED(face); } /*! @@ -874,41 +319,14 @@ void QQuickShaderEffect::setCullMode(CullMode face) \since QtQuick 2.4 */ -void QQuickShaderEffect::setSupportsAtlasTextures(bool supports) -{ - if (supports == m_supportsAtlasTextures) - return; - m_supportsAtlasTextures = supports; - updateGeometry(); - emit supportsAtlasTexturesChanged(); -} - -QString QQuickShaderEffect::parseLog() +bool QQuickShaderEffect::supportsAtlasTextures() const { - if (m_dirtyParseLog) { - m_common.updateParseLog(m_mesh != 0); - m_dirtyParseLog = false; - } - return m_common.parseLog; + return false; } -bool QQuickShaderEffect::event(QEvent *event) +void QQuickShaderEffect::setSupportsAtlasTextures(bool supports) { - if (event->type() == QEvent::DynamicPropertyChange) { - QDynamicPropertyChangeEvent *e = static_cast<QDynamicPropertyChangeEvent *>(event); - for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { - for (int i = 0; i < m_common.uniformData[shaderType].size(); ++i) { - if (m_common.uniformData[shaderType].at(i).name == e->propertyName()) { - bool textureProviderChanged; - m_common.propertyChanged(this, (shaderType << 16) | i, &textureProviderChanged); - m_dirtyTextureProviders |= textureProviderChanged; - m_dirtyUniformValues = true; - update(); - } - } - } - } - return QQuickItem::event(event); + Q_UNUSED(supports); } /*! @@ -939,179 +357,14 @@ bool QQuickShaderEffect::event(QEvent *event) \sa status */ -void QQuickShaderEffect::updateGeometry() -{ - m_dirtyGeometry = true; - update(); -} - -void QQuickShaderEffect::updateGeometryIfAtlased() -{ - if (m_supportsAtlasTextures) - updateGeometry(); -} - -void QQuickShaderEffect::updateLogAndStatus(const QString &log, int status) -{ - m_log = parseLog() + log; - m_status = Status(status); - emit logChanged(); - emit statusChanged(); -} - -void QQuickShaderEffect::sourceDestroyed(QObject *object) -{ - m_common.sourceDestroyed(object); -} - - -void QQuickShaderEffect::propertyChanged(int mappedId) -{ - bool textureProviderChanged; - m_common.propertyChanged(this, mappedId, &textureProviderChanged); - m_dirtyTextureProviders |= textureProviderChanged; - m_dirtyUniformValues = true; - update(); -} - -void QQuickShaderEffect::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) -{ - m_dirtyGeometry = true; - QQuickItem::geometryChanged(newGeometry, oldGeometry); -} - -QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) -{ - QQuickShaderEffectNode *node = static_cast<QQuickShaderEffectNode *>(oldNode); - - // In the case of zero-size or a bad vertex shader, don't try to create a node... - if (m_common.attributes.isEmpty() || width() <= 0 || height() <= 0) { - if (node) - delete node; - return 0; - } - - if (!node) { - node = new QQuickShaderEffectNode; - node->setMaterial(new QQuickShaderEffectMaterial(node)); - node->setFlag(QSGNode::OwnsMaterial, true); - m_dirtyProgram = true; - m_dirtyUniforms = true; - m_dirtyGeometry = true; - connect(node, SIGNAL(logAndStatusChanged(QString,int)), this, SLOT(updateLogAndStatus(QString,int))); - connect(node, &QQuickShaderEffectNode::dirtyTexture, - this, &QQuickShaderEffect::updateGeometryIfAtlased); - } - - QQuickShaderEffectMaterial *material = static_cast<QQuickShaderEffectMaterial *>(node->material()); - - // Update blending - if (bool(material->flags() & QSGMaterial::Blending) != m_blending) { - material->setFlag(QSGMaterial::Blending, m_blending); - node->markDirty(QSGNode::DirtyMaterial); - } - - if (int(material->cullMode) != int(m_cullMode)) { - material->cullMode = QQuickShaderEffectMaterial::CullMode(m_cullMode); - node->markDirty(QSGNode::DirtyMaterial); - } - - if (m_dirtyProgram) { - Key s = m_common.source; - QSGShaderSourceBuilder builder; - if (s.sourceCode[Key::FragmentShader].isEmpty()) { - builder.appendSourceFile(QStringLiteral(":/qt-project.org/items/shaders/shadereffect.frag")); - s.sourceCode[Key::FragmentShader] = builder.source(); - builder.clear(); - } - if (s.sourceCode[Key::VertexShader].isEmpty()) { - builder.appendSourceFile(QStringLiteral(":/qt-project.org/items/shaders/shadereffect.vert")); - s.sourceCode[Key::VertexShader] = builder.source(); - } - s.className = metaObject()->className(); - - material->setProgramSource(s); - material->attributes = m_common.attributes; - node->markDirty(QSGNode::DirtyMaterial); - m_dirtyProgram = false; - m_dirtyUniforms = true; - } - - if (m_dirtyUniforms || m_dirtyUniformValues || m_dirtyTextureProviders) { - m_common.updateMaterial(node, material, m_dirtyUniforms, m_dirtyUniformValues, - m_dirtyTextureProviders); - node->markDirty(QSGNode::DirtyMaterial); - m_dirtyUniforms = m_dirtyUniformValues = m_dirtyTextureProviders = false; - } - - QRectF srcRect(0, 0, 1, 1); - bool geometryUsesTextureSubRect = false; - if (m_supportsAtlasTextures && material->textureProviders.size() == 1) { - QSGTextureProvider *provider = material->textureProviders.at(0); - if (provider->texture()) { - srcRect = provider->texture()->normalizedTextureSubRect(); - geometryUsesTextureSubRect = true; - } - } - - if (bool(material->flags() & QSGMaterial::RequiresFullMatrix) != m_customVertexShader) { - material->setFlag(QSGMaterial::RequiresFullMatrix, m_customVertexShader); - node->markDirty(QSGNode::DirtyMaterial); - } - - if (material->geometryUsesTextureSubRect != geometryUsesTextureSubRect) { - material->geometryUsesTextureSubRect = geometryUsesTextureSubRect; - node->markDirty(QSGNode::DirtyMaterial); - } - - if (m_dirtyMesh) { - node->setGeometry(0); - m_dirtyMesh = false; - m_dirtyGeometry = true; - } - - if (m_dirtyGeometry) { - node->setFlag(QSGNode::OwnsGeometry, false); - QSGGeometry *geometry = node->geometry(); - QRectF rect(0, 0, width(), height()); - QQuickShaderEffectMesh *mesh = m_mesh ? m_mesh : &m_defaultMesh; - - geometry = mesh->updateGeometry(geometry, m_common.attributes, srcRect, rect); - if (!geometry) { - QString log = mesh->log(); - if (!log.isNull()) { - m_log = parseLog(); - m_log += QLatin1String("*** Mesh ***\n"); - m_log += log; - m_status = Error; - emit logChanged(); - emit statusChanged(); - } - delete node; - return 0; - } - - node->setGeometry(geometry); - node->setFlag(QSGNode::OwnsGeometry, true); - - m_dirtyGeometry = false; - } - - return node; -} - -void QQuickShaderEffect::componentComplete() +QString QQuickShaderEffect::log() const { - m_common.updateShader(this, Key::VertexShader); - m_common.updateShader(this, Key::FragmentShader); - QQuickItem::componentComplete(); + return QString(); } -void QQuickShaderEffect::itemChange(ItemChange change, const ItemChangeData &value) +QQuickShaderEffect::Status QQuickShaderEffect::status() const { - if (change == QQuickItem::ItemSceneChange) - m_common.updateWindow(value.window); - QQuickItem::itemChange(change, value); + return Uncompiled; } QT_END_NAMESPACE diff --git a/src/quick/items/qquickshadereffect_p.h b/src/quick/items/qquickshadereffect_p.h index c43e4a558e..9368ecf89c 100644 --- a/src/quick/items/qquickshadereffect_p.h +++ b/src/quick/items/qquickshadereffect_p.h @@ -52,49 +52,10 @@ // #include <QtQuick/qquickitem.h> - -#include <QtQuick/qsgmaterial.h> #include <private/qtquickglobal_p.h> -#include <private/qsgadaptationlayer_p.h> -#include <private/qquickshadereffectnode_p.h> -#include "qquickshadereffectmesh_p.h" - -#include <QtCore/qpointer.h> QT_BEGIN_NAMESPACE -class QSGContext; -class QSignalMapper; -class QQuickCustomMaterialShader; - -// Common class for QQuickShaderEffect and QQuickCustomParticle. -struct Q_QUICK_PRIVATE_EXPORT QQuickShaderEffectCommon -{ - typedef QQuickShaderEffectMaterialKey Key; - typedef QQuickShaderEffectMaterial::UniformData UniformData; - - ~QQuickShaderEffectCommon(); - void disconnectPropertySignals(QQuickItem *item, Key::ShaderType shaderType); - void connectPropertySignals(QQuickItem *item, Key::ShaderType shaderType); - void updateParseLog(bool ignoreAttributes); - void lookThroughShaderCode(QQuickItem *item, Key::ShaderType shaderType, const QByteArray &code); - void updateShader(QQuickItem *item, Key::ShaderType shaderType); - void updateMaterial(QQuickShaderEffectNode *node, QQuickShaderEffectMaterial *material, - bool updateUniforms, bool updateUniformValues, bool updateTextureProviders); - void updateWindow(QQuickWindow *window); - - // Called by slots in QQuickShaderEffect: - void sourceDestroyed(QObject *object); - void propertyChanged(QQuickItem *item, int mappedId, bool *textureProviderChanged); - - Key source; - QVector<QByteArray> attributes; - QVector<UniformData> uniformData[Key::ShaderTypeCount]; - QVector<QSignalMapper *> signalMappers[Key::ShaderTypeCount]; - QString parseLog; -}; - - class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffect : public QQuickItem { Q_OBJECT @@ -108,16 +69,14 @@ class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffect : public QQuickItem Q_PROPERTY(bool supportsAtlasTextures READ supportsAtlasTextures WRITE setSupportsAtlasTextures NOTIFY supportsAtlasTexturesChanged REVISION 1) public: - enum CullMode - { - NoCulling = QQuickShaderEffectMaterial::NoCulling, - BackFaceCulling = QQuickShaderEffectMaterial::BackFaceCulling, - FrontFaceCulling = QQuickShaderEffectMaterial::FrontFaceCulling + enum CullMode { + NoCulling, + BackFaceCulling, + FrontFaceCulling }; Q_ENUM(CullMode) - enum Status - { + enum Status { Compiled, Uncompiled, Error @@ -125,32 +84,27 @@ public: Q_ENUM(Status) QQuickShaderEffect(QQuickItem *parent = 0); - ~QQuickShaderEffect(); - QByteArray fragmentShader() const { return m_common.source.sourceCode[Key::FragmentShader]; } + QByteArray fragmentShader() const; void setFragmentShader(const QByteArray &code); - QByteArray vertexShader() const { return m_common.source.sourceCode[Key::VertexShader]; } + QByteArray vertexShader() const; void setVertexShader(const QByteArray &code); - bool blending() const { return m_blending; } + bool blending() const; void setBlending(bool enable); QVariant mesh() const; void setMesh(const QVariant &mesh); - CullMode cullMode() const { return m_cullMode; } + CullMode cullMode() const; void setCullMode(CullMode face); - QString log() const { return m_log; } - Status status() const { return m_status; } - - bool supportsAtlasTextures() const { return m_supportsAtlasTextures; } + bool supportsAtlasTextures() const; void setSupportsAtlasTextures(bool supports); - QString parseLog(); - - bool event(QEvent *) Q_DECL_OVERRIDE; + QString log() const; + Status status() const; Q_SIGNALS: void fragmentShaderChanged(); @@ -161,46 +115,6 @@ Q_SIGNALS: void logChanged(); void statusChanged(); void supportsAtlasTexturesChanged(); - -protected: - void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE; - QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) Q_DECL_OVERRIDE; - void componentComplete() Q_DECL_OVERRIDE; - void itemChange(ItemChange change, const ItemChangeData &value) Q_DECL_OVERRIDE; - -private Q_SLOTS: - void updateGeometry(); - void updateGeometryIfAtlased(); - void updateLogAndStatus(const QString &log, int status); - void sourceDestroyed(QObject *object); - void propertyChanged(int mappedId); - -private: - friend class QQuickCustomMaterialShader; - friend class QQuickShaderEffectNode; - - typedef QQuickShaderEffectMaterialKey Key; - typedef QQuickShaderEffectMaterial::UniformData UniformData; - - QSize m_meshResolution; - QQuickShaderEffectMesh *m_mesh; - QQuickGridMesh m_defaultMesh; - CullMode m_cullMode; - QString m_log; - Status m_status; - - QQuickShaderEffectCommon m_common; - - uint m_blending : 1; - uint m_dirtyUniforms : 1; - uint m_dirtyUniformValues : 1; - uint m_dirtyTextureProviders : 1; - uint m_dirtyProgram : 1; - uint m_dirtyParseLog : 1; - uint m_dirtyMesh : 1; - uint m_dirtyGeometry : 1; - uint m_customVertexShader : 1; - uint m_supportsAtlasTextures : 1; }; QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderloop.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderloop.cpp index a4181c7248..121d010bbb 100644 --- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderloop.cpp +++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderloop.cpp @@ -45,7 +45,6 @@ #include <private/qquickwindow_p.h> #include <private/qquickprofiler_p.h> #include <private/qquickanimatorcontroller_p.h> -#include <private/qquickshadereffectnode_p.h> #include <private/qquickprofiler_p.h> #include <private/qqmldebugserviceinterfaces_p.h> #include <private/qqmldebugconnector_p.h> @@ -330,7 +329,6 @@ bool QSGD3D12RenderThread::event(QEvent *e) qDebug("RT - WM_TryRelease - invalidating rc"); if (wme->window) { QQuickWindowPrivate *wd = QQuickWindowPrivate::get(wme->window); - QQuickShaderEffectMaterial::cleanupMaterialCache(); if (wme->destroying) wd->cleanupNodesOnShutdown(); rc->invalidate(); diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp index 3d019eab72..cb8f1399a4 100644 --- a/src/quick/scenegraph/qsgrenderloop.cpp +++ b/src/quick/scenegraph/qsgrenderloop.cpp @@ -61,7 +61,7 @@ #ifndef QT_NO_OPENGL # include <QtGui/QOpenGLContext> # include <private/qsgdefaultrendercontext_p.h> -# include <private/qquickshadereffectnode_p.h> +# include <private/qquickopenglshadereffectnode_p.h> #endif #ifdef Q_OS_WIN @@ -326,7 +326,9 @@ void QSGGuiThreadRenderLoop::windowDestroyed(QQuickWindow *window) if (Q_UNLIKELY(!current)) qCDebug(QSG_LOG_RENDERLOOP) << "cleanup without an OpenGL context"; - QQuickShaderEffectMaterial::cleanupMaterialCache(); +#ifndef QT_NO_OPENGL + QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache(); +#endif d->cleanupNodesOnShutdown(); if (m_windows.size() == 0) { diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp index 608afcff10..1feba32ab2 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp +++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp @@ -63,7 +63,7 @@ #include <private/qqmldebugserviceinterfaces_p.h> #include <private/qqmldebugconnector_p.h> -#include <private/qquickshadereffectnode_p.h> +#include <private/qquickopenglshadereffectnode_p.h> #include <private/qsgdefaultrendercontext_p.h> /* @@ -487,7 +487,7 @@ void QSGRenderThread::invalidateOpenGL(QQuickWindow *window, bool inDestructor, QQuickWindowPrivate *dd = QQuickWindowPrivate::get(window); - QQuickShaderEffectMaterial::cleanupMaterialCache(); + QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache(); // The canvas nodes must be cleaned up regardless if we are in the destructor.. if (wipeSG) { diff --git a/src/quick/scenegraph/qsgwindowsrenderloop.cpp b/src/quick/scenegraph/qsgwindowsrenderloop.cpp index 84423a8cfc..5e025e9761 100644 --- a/src/quick/scenegraph/qsgwindowsrenderloop.cpp +++ b/src/quick/scenegraph/qsgwindowsrenderloop.cpp @@ -53,9 +53,12 @@ #include <QtQuick/QQuickWindow> #include <private/qquickprofiler_p.h> -#include <private/qquickshadereffectnode_p.h> #include <private/qquickanimatorcontroller_p.h> +#ifndef QT_NO_OPENGL +#include <private/qquickopenglshadereffectnode_p.h> +#endif + QT_BEGIN_NAMESPACE extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha); @@ -242,7 +245,9 @@ void QSGWindowsRenderLoop::windowDestroyed(QQuickWindow *window) if (Q_UNLIKELY(!current)) qCDebug(QSG_LOG_RENDERLOOP) << "cleanup without an OpenGL context"; - QQuickShaderEffectMaterial::cleanupMaterialCache(); +#ifndef QT_NO_OPENGL + QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache(); +#endif d->cleanupNodesOnShutdown(); if (m_windows.size() == 0) { diff --git a/src/quick/util/qquickanimatorjob.cpp b/src/quick/util/qquickanimatorjob.cpp index f0ecb8150a..a0c787dae5 100644 --- a/src/quick/util/qquickanimatorjob.cpp +++ b/src/quick/util/qquickanimatorjob.cpp @@ -44,7 +44,8 @@ #include <private/qquickwindow_p.h> #include <private/qquickitem_p.h> #ifndef QT_NO_OPENGL -# include <private/qquickshadereffectnode_p.h> +# include <private/qquickopenglshadereffectnode_p.h> +# include <private/qquickopenglshadereffect_p.h> #endif #include <private/qanimationgroupjob_p.h> @@ -553,7 +554,7 @@ QQuickUniformAnimatorJob::QQuickUniformAnimatorJob() void QQuickUniformAnimatorJob::setTarget(QQuickItem *target) { - if (qobject_cast<QQuickShaderEffect *>(target) != 0) + if (qobject_cast<QQuickOpenGLShaderEffect *>(target) != 0) m_target = target; } @@ -566,14 +567,14 @@ void QQuickUniformAnimatorJob::nodeWasDestroyed() void QQuickUniformAnimatorJob::afterNodeSync() { - m_node = static_cast<QQuickShaderEffectNode *>(QQuickItemPrivate::get(m_target)->paintNode); + m_node = static_cast<QQuickOpenGLShaderEffectNode *>(QQuickItemPrivate::get(m_target)->paintNode); if (m_node && m_uniformIndex == -1 && m_uniformType == -1) { - QQuickShaderEffectMaterial *material = - static_cast<QQuickShaderEffectMaterial *>(m_node->material()); + QQuickOpenGLShaderEffectMaterial *material = + static_cast<QQuickOpenGLShaderEffectMaterial *>(m_node->material()); bool found = false; - for (int t=0; !found && t<QQuickShaderEffectMaterialKey::ShaderTypeCount; ++t) { - const QVector<QQuickShaderEffectMaterial::UniformData> &uniforms = material->uniforms[t]; + for (int t=0; !found && t<QQuickOpenGLShaderEffectMaterialKey::ShaderTypeCount; ++t) { + const QVector<QQuickOpenGLShaderEffectMaterial::UniformData> &uniforms = material->uniforms[t]; for (int i=0; i<uniforms.size(); ++i) { if (uniforms.at(i).name == m_uniform) { m_uniformIndex = i; @@ -597,8 +598,8 @@ void QQuickUniformAnimatorJob::updateCurrentTime(int time) m_value = m_from + (m_to - m_from) * progress(time); - QQuickShaderEffectMaterial *material = - static_cast<QQuickShaderEffectMaterial *>(m_node->material()); + QQuickOpenGLShaderEffectMaterial *material = + static_cast<QQuickOpenGLShaderEffectMaterial *>(m_node->material()); material->uniforms[m_uniformType][m_uniformIndex].value = m_value; // As we're not touching the nodes, we need to explicitly mark it dirty. // Otherwise, the renderer will abort repainting if this was the only diff --git a/src/quick/util/qquickanimatorjob_p.h b/src/quick/util/qquickanimatorjob_p.h index 118487f937..64e849d322 100644 --- a/src/quick/util/qquickanimatorjob_p.h +++ b/src/quick/util/qquickanimatorjob_p.h @@ -68,7 +68,7 @@ class QQuickAbstractAnimation; class QQuickAnimatorController; class QQuickAnimatorProxyJobPrivate; -class QQuickShaderEffectNode; +class QQuickOpenGLShaderEffectNode; class QSGOpacityNode; @@ -296,7 +296,7 @@ public: private: QByteArray m_uniform; - QQuickShaderEffectNode *m_node; + QQuickOpenGLShaderEffectNode *m_node; int m_uniformIndex : 8; int m_uniformType : 8; diff --git a/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp b/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp index d0e718fb91..36c25ae78e 100644 --- a/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp +++ b/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp @@ -30,13 +30,12 @@ #include <QList> #include <QByteArray> -#include <private/qquickshadereffect_p.h> - +#include <private/qquickopenglshadereffect_p.h> +#include <QMatrix4x4> #include <QtQuick/QQuickView> #include "../../shared/util.h" - -class TestShaderEffect : public QQuickShaderEffect +class TestShaderEffect : public QQuickOpenGLShaderEffect { Q_OBJECT Q_PROPERTY(QVariant source READ dummyRead NOTIFY dummyChanged) diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index ff8c80e3ae..3e661db7b1 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -44,6 +44,7 @@ #include <private/qquickwindow_p.h> #include <private/qguiapplication_p.h> #include <QRunnable> +#include <QOpenGLFunctions> struct TouchEventData { QEvent::Type type; |