diff options
author | Kim Motoyoshi Kalland <kim.kalland@nokia.com> | 2012-03-21 14:21:51 +0100 |
---|---|---|
committer | Qt by Nokia <qt-info@nokia.com> | 2012-04-04 03:29:45 +0200 |
commit | c71c8c4619594a276d69378bbd77da5117afd566 (patch) | |
tree | 9b8ac6fb0db75abb747de6ec2a74e754770f4820 /src/quick/particles/qquickcustomparticle.cpp | |
parent | 2c741bc8af46aa26ec9a61bc996bb0de2e409943 (diff) |
Fixed crash when using 'var' property in ShaderEffect.
Also made CustomParticle and ShaderEffect share some code.
Task-number: QTBUG-23924
Change-Id: I975f71f031b379cf5e29302a9c931e4ead5437ea
Reviewed-by: Alan Alpert <alan.alpert@nokia.com>
Diffstat (limited to 'src/quick/particles/qquickcustomparticle.cpp')
-rw-r--r-- | src/quick/particles/qquickcustomparticle.cpp | 387 |
1 files changed, 129 insertions, 258 deletions
diff --git a/src/quick/particles/qquickcustomparticle.cpp b/src/quick/particles/qquickcustomparticle.cpp index 6f10307851..7d27e48c6b 100644 --- a/src/quick/particles/qquickcustomparticle.cpp +++ b/src/quick/particles/qquickcustomparticle.cpp @@ -130,29 +130,27 @@ struct PlainVertices { QQuickCustomParticle::QQuickCustomParticle(QQuickItem* parent) : QQuickParticlePainter(parent) - , m_dirtyData(true) - , m_material(0) - , m_rootNode(0) + , m_dirtyUniforms(true) + , m_dirtyUniformValues(true) + , m_dirtyTextureProviders(true) + , m_dirtyProgram(true) { setFlag(QQuickItem::ItemHasContents); } -class QQuickShaderEffectMaterialObject : public QObject, public QQuickShaderEffectMaterial { }; - void QQuickCustomParticle::sceneGraphInvalidated() { m_nodes.clear(); - m_rootNode = 0; } QQuickCustomParticle::~QQuickCustomParticle() { - if (m_material) - m_material->deleteLater(); } void QQuickCustomParticle::componentComplete() { + m_common.updateShader(this, Key::FragmentShader); + updateVertexShader(); reset(); QQuickParticlePainter::componentComplete(); } @@ -170,10 +168,12 @@ void QQuickCustomParticle::componentComplete() void QQuickCustomParticle::setFragmentShader(const QByteArray &code) { - if (m_source.fragmentCode.constData() == code.constData()) + if (m_common.source.sourceCode[Key::FragmentShader].constData() == code.constData()) return; - m_source.fragmentCode = code; + m_common.source.sourceCode[Key::FragmentShader] = code; + m_dirtyProgram = true; if (isComponentComplete()) { + m_common.updateShader(this, Key::FragmentShader); reset(); } emit fragmentShaderChanged(); @@ -222,236 +222,108 @@ void QQuickCustomParticle::setFragmentShader(const QByteArray &code) void QQuickCustomParticle::setVertexShader(const QByteArray &code) { - if (m_source.vertexCode.constData() == code.constData()) + if (m_common.source.sourceCode[Key::VertexShader].constData() == code.constData()) return; - m_source.vertexCode = code; + m_common.source.sourceCode[Key::VertexShader] = code; + + m_dirtyProgram = true; if (isComponentComplete()) { + updateVertexShader(); reset(); } emit vertexShaderChanged(); } -void QQuickCustomParticle::reset() +void QQuickCustomParticle::updateVertexShader() { - disconnectPropertySignals(); - - m_source.attributeNames.clear(); - m_source.uniformNames.clear(); - m_source.respectsOpacity = false; - m_source.respectsMatrix = false; - m_source.className = metaObject()->className(); - - for (int i = 0; i < m_sources.size(); ++i) { - const SourceData &source = m_sources.at(i); - delete source.mapper; - if (source.item && source.item->parentItem() == this) - source.item->setParentItem(0); - } - m_sources.clear(); - - QQuickParticlePainter::reset(); - m_pleaseReset = true; - update(); + m_common.disconnectPropertySignals(this, Key::VertexShader); + qDeleteAll(m_common.signalMappers[Key::VertexShader]); + m_common.uniformData[Key::VertexShader].clear(); + m_common.signalMappers[Key::VertexShader].clear(); + m_common.attributes.clear(); + m_common.attributes.append("qt_ParticlePos"); + m_common.attributes.append("qt_ParticleTex"); + m_common.attributes.append("qt_ParticleData"); + m_common.attributes.append("qt_ParticleVec"); + m_common.attributes.append("qt_ParticleR"); + + UniformData d; + d.name = "qt_Matrix"; + d.specialType = UniformData::Matrix; + m_common.uniformData[Key::VertexShader].append(d); + m_common.signalMappers[Key::VertexShader].append(0); + + d.name = "qt_Timestamp"; + d.specialType = UniformData::None; + m_common.uniformData[Key::VertexShader].append(d); + m_common.signalMappers[Key::VertexShader].append(0); + + const QByteArray &code = m_common.source.sourceCode[Key::VertexShader]; + if (!code.isEmpty()) + m_common.lookThroughShaderCode(this, Key::VertexShader, code); + + m_common.connectPropertySignals(this, Key::VertexShader); } - -void QQuickCustomParticle::changeSource(int index) -{ - Q_ASSERT(index >= 0 && index < m_sources.size()); - QVariant v = property(m_sources.at(index).name.constData()); - setSource(v, index); -} - -void QQuickCustomParticle::updateData() +void QQuickCustomParticle::reset() { - m_dirtyData = true; + QQuickParticlePainter::reset(); update(); } -void QQuickCustomParticle::setSource(const QVariant &var, int index) -{ - Q_ASSERT(index >= 0 && index < m_sources.size()); - - SourceData &source = m_sources[index]; - - source.item = 0; - if (var.isNull()) { - return; - } else if (!qVariantCanConvert<QObject *>(var)) { - qWarning("Could not assign source of type '%s' to property '%s'.", var.typeName(), source.name.constData()); - return; - } - - QObject *obj = qVariantValue<QObject *>(var); - source.item = qobject_cast<QQuickItem *>(obj); - if (!source.item || !source.item->isTextureProvider()) { - qWarning("ShaderEffect: source uniform [%s] is not assigned a valid texture provider: %s [%s]", - source.name.constData(), qPrintable(obj->objectName()), obj->metaObject()->className()); - return; - } - - // TODO: Copy better solution in QQuickShaderEffect when they find it. - // 'source.item' needs a canvas to get a scenegraph node. - // The easiest way to make sure it gets a canvas is to - // make it a part of the same item tree as 'this'. - if (source.item && source.item->parentItem() == 0) { - source.item->setParentItem(this); - source.item->setVisible(false); - } -} - -void QQuickCustomParticle::disconnectPropertySignals() -{ - disconnect(this, 0, this, SLOT(updateData())); - for (int i = 0; i < m_sources.size(); ++i) { - SourceData &source = m_sources[i]; - disconnect(this, 0, source.mapper, 0); - disconnect(source.mapper, 0, this, 0); - } -} - -void QQuickCustomParticle::connectPropertySignals() -{ - QSet<QByteArray>::const_iterator it; - for (it = m_source.uniformNames.begin(); it != m_source.uniformNames.end(); ++it) { - int pi = metaObject()->indexOfProperty(it->constData()); - if (pi >= 0) { - QMetaProperty mp = metaObject()->property(pi); - if (!mp.hasNotifySignal()) - qWarning("QQuickCustomParticle: property '%s' does not have notification method!", it->constData()); - QByteArray signalName("2"); - signalName.append(mp.notifySignal().signature()); - connect(this, signalName, this, SLOT(updateData())); - } else { - qWarning("QQuickCustomParticle: '%s' does not have a matching property!", it->constData()); - } - } - for (int i = 0; i < m_sources.size(); ++i) { - SourceData &source = m_sources[i]; - int pi = metaObject()->indexOfProperty(source.name.constData()); - if (pi >= 0) { - QMetaProperty mp = metaObject()->property(pi); - QByteArray signalName("2"); - signalName.append(mp.notifySignal().signature()); - connect(this, signalName, source.mapper, SLOT(map())); - source.mapper->setMapping(this, i); - connect(source.mapper, SIGNAL(mapped(int)), this, SLOT(changeSource(int))); - } else { - qWarning("QQuickCustomParticle: '%s' does not have a matching source!", source.name.constData()); - } - } -} - -void QQuickCustomParticle::updateProperties() -{ - QByteArray vertexCode = m_source.vertexCode; - QByteArray fragmentCode = m_source.fragmentCode; - if (vertexCode.isEmpty()) - vertexCode = qt_particles_default_vertex_code; - if (fragmentCode.isEmpty()) - fragmentCode = qt_particles_default_fragment_code; - vertexCode = qt_particles_template_vertex_code + vertexCode; - - m_source.attributeNames.clear(); - m_source.attributeNames << "qt_ParticlePos" - << "qt_ParticleTex" - << "qt_ParticleData" - << "qt_ParticleVec" - << "qt_ParticleR"; - - lookThroughShaderCode(vertexCode); - lookThroughShaderCode(fragmentCode); - - if (!m_source.respectsMatrix) - qWarning("QQuickCustomParticle: Missing reference to \'qt_Matrix\'."); - if (!m_source.respectsOpacity) - qWarning("QQuickCustomParticle: Missing reference to \'qt_Opacity\'."); - - for (int i = 0; i < m_sources.size(); ++i) { - QVariant v = property(m_sources.at(i).name); - setSource(v, i); - } - - connectPropertySignals(); -} - -void QQuickCustomParticle::lookThroughShaderCode(const QByteArray &code) -{ - // Regexp for matching attributes and uniforms. - // In human readable form: attribute|uniform [lowp|mediump|highp] <type> <name> - static QRegExp re(QLatin1String("\\b(attribute|uniform)\\b\\s*\\b(?:lowp|mediump|highp)?\\b\\s*\\b(\\w+)\\b\\s*\\b(\\w+)")); - Q_ASSERT(re.isValid()); - - int pos = -1; - - QString wideCode = QString::fromLatin1(code.constData(), code.size()); - - while ((pos = re.indexIn(wideCode, pos + 1)) != -1) { - QByteArray decl = re.cap(1).toLatin1(); // uniform or attribute - QByteArray type = re.cap(2).toLatin1(); // type - QByteArray name = re.cap(3).toLatin1(); // variable name - - if (decl == "attribute") { - if (!m_source.attributeNames.contains(name)) - qWarning() << "Custom Particle: Unknown attribute " << name; - } else { - Q_ASSERT(decl == "uniform");//TODO: Shouldn't assert - - if (name == "qt_Matrix") { - m_source.respectsMatrix = true; - } else if (name == "qt_Opacity") { - m_source.respectsOpacity = true; - } else if (name == "qt_Timestamp") { - //Not strictly necessary - } else { - m_source.uniformNames.insert(name); - if (type == "sampler2D") { - SourceData d; - d.mapper = new QSignalMapper; - d.name = name; - d.item = 0; - m_sources.append(d); - } - } - } - } -} - QSGNode *QQuickCustomParticle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) { - Q_UNUSED(oldNode); + QQuickShaderEffectNode *rootNode = static_cast<QQuickShaderEffectNode *>(oldNode); if (m_pleaseReset){ - - //delete m_material;//Shader effect item doesn't regen material? - - delete m_rootNode;//Automatically deletes children - m_rootNode = 0; + delete rootNode;//Automatically deletes children + rootNode = 0; m_nodes.clear(); m_pleaseReset = false; - m_dirtyData = false; + m_dirtyProgram = true; } if (m_system && m_system->isRunning() && !m_system->isPaused()){ - prepareNextFrame(); - if (m_rootNode) { + rootNode = prepareNextFrame(rootNode); + if (rootNode) update(); - foreach (QSGGeometryNode* node, m_nodes) - node->markDirty(QSGNode::DirtyGeometry);//done in buildData? - } } - return m_rootNode; + return rootNode; } -void QQuickCustomParticle::prepareNextFrame(){ - if (!m_rootNode) - m_rootNode = buildCustomNodes(); - if (!m_rootNode) - return; +QQuickShaderEffectNode *QQuickCustomParticle::prepareNextFrame(QQuickShaderEffectNode *rootNode) +{ + if (!rootNode) + rootNode = buildCustomNodes(); + + if (!rootNode) + return 0; + + if (m_dirtyProgram) { + QQuickShaderEffectMaterial *material = static_cast<QQuickShaderEffectMaterial *>(rootNode->material()); + Q_ASSERT(material); + + Key s = m_common.source; + if (s.sourceCode[Key::FragmentShader].isEmpty()) + s.sourceCode[Key::FragmentShader] = qt_particles_default_fragment_code; + if (s.sourceCode[Key::VertexShader].isEmpty()) + s.sourceCode[Key::VertexShader] = qt_particles_default_vertex_code; + s.sourceCode[Key::VertexShader] = qt_particles_template_vertex_code + s.sourceCode[Key::VertexShader]; + s.className = metaObject()->className(); + + material->setProgramSource(s); + material->attributes = m_common.attributes; + foreach (QQuickShaderEffectNode* node, m_nodes) + node->markDirty(QSGNode::DirtyMaterial); + + m_dirtyProgram = false; + m_dirtyUniforms = true; + } m_lastTime = m_system->systemSync(this) / 1000.; - if (m_dirtyData || true)//Currently this is how we update timestamp... potentially over expensive. - buildData(); + if (true) //Currently this is how we update timestamp... potentially over expensive. + buildData(rootNode); + return rootNode; } QQuickShaderEffectNode* QQuickCustomParticle::buildCustomNodes() @@ -468,20 +340,13 @@ QQuickShaderEffectNode* QQuickCustomParticle::buildCustomNodes() return 0; } - updateProperties(); - - QQuickShaderEffectProgram s = m_source; - if (s.fragmentCode.isEmpty()) - s.fragmentCode = qt_particles_default_fragment_code; - if (s.vertexCode.isEmpty()) - s.vertexCode = qt_particles_default_vertex_code; + if (m_groups.isEmpty()) + return 0; - if (!m_material) { - m_material = new QQuickShaderEffectMaterialObject; - } + QQuickShaderEffectNode *rootNode = 0; + QQuickShaderEffectMaterial *material = new QQuickShaderEffectMaterial; + m_dirtyProgram = true; - s.vertexCode = qt_particles_template_vertex_code + s.vertexCode; - m_material->setProgramSource(s); foreach (const QString &str, m_groups){ int gIdx = m_system->groupIds[str]; int count = m_system->groupData[gIdx]->size(); @@ -489,8 +354,7 @@ QQuickShaderEffectNode* QQuickCustomParticle::buildCustomNodes() QQuickShaderEffectNode* node = new QQuickShaderEffectNode(); m_nodes.insert(gIdx, node); - node->setMaterial(m_material); - node->markDirty(QSGNode::DirtyMaterial); + node->setMaterial(material); //Create Particle Geometry int vCount = count * 4; @@ -498,6 +362,7 @@ QQuickShaderEffectNode* QQuickCustomParticle::buildCustomNodes() QSGGeometry *g = new QSGGeometry(PlainParticle_AttributeSet, vCount, iCount); g->setDrawingMode(GL_TRIANGLES); node->setGeometry(g); + node->setFlag(QSGNode::OwnsGeometry, true); PlainVertex *vertices = (PlainVertex *) g->vertexData(); for (int p=0; p < count; ++p) { commit(gIdx, p); @@ -526,48 +391,46 @@ QQuickShaderEffectNode* QQuickCustomParticle::buildCustomNodes() indices += 6; } } - foreach (QQuickShaderEffectNode* node, m_nodes){ - if (node == *(m_nodes.begin())) - continue; - (*(m_nodes.begin()))->appendChildNode(node); - } - return *(m_nodes.begin()); + QHash<int, QQuickShaderEffectNode*>::const_iterator it = m_nodes.begin(); + rootNode = it.value(); + rootNode->setFlag(QSGNode::OwnsMaterial, true); + for (++it; it != m_nodes.end(); ++it) + rootNode->appendChildNode(it.value()); + + return rootNode; } +void QQuickCustomParticle::sourceDestroyed(QObject *object) +{ + m_common.sourceDestroyed(object); +} -void QQuickCustomParticle::buildData() +void QQuickCustomParticle::propertyChanged(int mappedId) { - if (!m_rootNode) + bool textureProviderChanged; + m_common.propertyChanged(this, mappedId, &textureProviderChanged); + m_dirtyTextureProviders |= textureProviderChanged; + m_dirtyUniformValues = true; + update(); +} + + +void QQuickCustomParticle::buildData(QQuickShaderEffectNode *rootNode) +{ + if (!rootNode) return; - const QByteArray timestampName("qt_Timestamp"); - QVector<QPair<QByteArray, QVariant> > values; - QVector<QPair<QByteArray, QSGTextureProvider *> > textures; - const QVector<QPair<QByteArray, QSGTextureProvider *> > &oldTextures = m_material->textureProviders(); - for (int i = 0; i < oldTextures.size(); ++i) { - QSGTextureProvider *t = oldTextures.at(i).second; - if (t) - foreach (QQuickShaderEffectNode* node, m_nodes) - disconnect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture())); - } - for (int i = 0; i < m_sources.size(); ++i) { - const SourceData &source = m_sources.at(i); - QSGTextureProvider *t = source.item->textureProvider(); - textures.append(qMakePair(source.name, t)); - if (t) - foreach (QQuickShaderEffectNode* node, m_nodes) - connect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()), Qt::DirectConnection); - } - for (QSet<QByteArray>::const_iterator it = m_source.uniformNames.begin(); - it != m_source.uniformNames.end(); ++it) { - values.append(qMakePair(*it, property(*it))); + 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 == "qt_Timestamp") + m_common.uniformData[shaderType][i].value = qVariantFromValue(m_lastTime); + } } - values.append(qMakePair(timestampName, QVariant(m_lastTime))); - m_material->setUniforms(values); - m_material->setTextureProviders(textures); - m_dirtyData = false; + m_common.updateMaterial(rootNode, static_cast<QQuickShaderEffectMaterial *>(rootNode->material()), + m_dirtyUniforms, true, m_dirtyTextureProviders); foreach (QQuickShaderEffectNode* node, m_nodes) node->markDirty(QSGNode::DirtyMaterial); + m_dirtyUniforms = m_dirtyUniformValues = m_dirtyTextureProviders = false; } void QQuickCustomParticle::initialize(int gIdx, int pIdx) @@ -599,4 +462,12 @@ void QQuickCustomParticle::commit(int gIdx, int pIdx) } } +void QQuickCustomParticle::itemChange(ItemChange change, const ItemChangeData &value) +{ + if (change == QQuickItem::ItemSceneChange) + m_common.updateCanvas(value.canvas); + QQuickParticlePainter::itemChange(change, value); +} + + QT_END_NAMESPACE |