aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/quick/items/qquickshadereffect.cpp54
-rw-r--r--src/quick/items/qquickshadereffect_p_p.h3
2 files changed, 51 insertions, 6 deletions
diff --git a/src/quick/items/qquickshadereffect.cpp b/src/quick/items/qquickshadereffect.cpp
index 032a801f6e..9ff2fcc655 100644
--- a/src/quick/items/qquickshadereffect.cpp
+++ b/src/quick/items/qquickshadereffect.cpp
@@ -1180,8 +1180,15 @@ void QQuickShaderEffectPrivate::disconnectSignals(Shader shaderType)
for (const auto &vd : std::as_const(m_shaders[shaderType].varData)) {
if (vd.specialType == QSGShaderEffectNode::VariableData::Source) {
QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(vd.value));
- if (source && q->window())
- QQuickItemPrivate::get(source)->derefWindow();
+ if (source) {
+ if (q->window())
+ QQuickItemPrivate::get(source)->derefWindow();
+ auto it = m_destroyedConnections.constFind(source);
+ if (it != m_destroyedConnections.constEnd()) {
+ QObject::disconnect(*it);
+ m_destroyedConnections.erase(it);
+ }
+ }
}
}
}
@@ -1404,7 +1411,15 @@ void QQuickShaderEffectPrivate::updateShaderVars(Shader shaderType)
if (source) {
if (q->window())
QQuickItemPrivate::get(source)->refWindow(q->window());
- QObject::connect(source, &QObject::destroyed, q, [this](QObject *obj) { sourceDestroyed(obj); });
+
+ // Cannot just pass q as the 'context' for the connect(). The
+ // order of destruction is...complicated. Having an inline
+ // source (e.g. source: ShaderEffectSource { ... } in QML would
+ // emit destroyed() after the connection was already gone. To
+ // work that around, store the Connection and manually
+ // disconnect instead.
+ if (!m_destroyedConnections.contains(source))
+ m_destroyedConnections.insert(source, QObject::connect(source, &QObject::destroyed, [this](QObject *obj) { sourceDestroyed(obj); }));
}
}
}
@@ -1423,6 +1438,20 @@ std::optional<int> QQuickShaderEffectPrivate::findMappedShaderVariableId(const Q
return {};
}
+bool QQuickShaderEffectPrivate::sourceIsUnique(QQuickItem *source, Shader typeToSkip, int indexToSkip) const
+{
+ for (int shaderType = 0; shaderType < NShader; ++shaderType) {
+ for (int idx = 0; idx < m_shaders[shaderType].varData.count(); ++idx) {
+ if (shaderType != typeToSkip || idx != indexToSkip) {
+ const auto &vd(m_shaders[shaderType].varData[idx]);
+ if (vd.specialType == QSGShaderEffectNode::VariableData::Source && qvariant_cast<QObject *>(vd.value) == source)
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
void QQuickShaderEffectPrivate::propertyChanged(int mappedId)
{
Q_Q(QQuickShaderEffect);
@@ -1436,8 +1465,20 @@ void QQuickShaderEffectPrivate::propertyChanged(int mappedId)
if (vd.specialType == QSGShaderEffectNode::VariableData::Source) {
QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(oldValue));
- if (source && q->window())
- QQuickItemPrivate::get(source)->derefWindow();
+ if (source) {
+ if (q->window())
+ QQuickItemPrivate::get(source)->derefWindow();
+ // If the same source has been attached to two separate
+ // textures/samplers, then changing one of them would trigger both
+ // to be disconnected. So check first.
+ if (sourceIsUnique(source, type, idx)) {
+ auto it = m_destroyedConnections.constFind(source);
+ if (it != m_destroyedConnections.constEnd()) {
+ QObject::disconnect(*it);
+ m_destroyedConnections.erase(it);
+ }
+ }
+ }
source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(vd.value));
if (source) {
@@ -1447,7 +1488,8 @@ void QQuickShaderEffectPrivate::propertyChanged(int mappedId)
// will not get a parent. In those cases, 'source' should get the window from 'item'.
if (q->window())
QQuickItemPrivate::get(source)->refWindow(q->window());
- QObject::connect(source, &QObject::destroyed, q, [this](QObject *obj) { sourceDestroyed(obj); });
+ if (!m_destroyedConnections.contains(source))
+ m_destroyedConnections.insert(source, QObject::connect(source, &QObject::destroyed, [this](QObject *obj) { sourceDestroyed(obj); }));
}
m_dirty |= QSGShaderEffectNode::DirtyShaderTexture;
diff --git a/src/quick/items/qquickshadereffect_p_p.h b/src/quick/items/qquickshadereffect_p_p.h
index 085b7cdc02..2310b6d366 100644
--- a/src/quick/items/qquickshadereffect_p_p.h
+++ b/src/quick/items/qquickshadereffect_p_p.h
@@ -92,6 +92,7 @@ private:
void disconnectSignals(Shader shaderType);
void clearMappers(Shader shaderType);
std::optional<int> findMappedShaderVariableId(const QByteArray &name) const;
+ bool sourceIsUnique(QQuickItem *source, Shader typeToSkip, int indexToSkip) const;
bool inDestructor = false;
const QMetaObject *m_itemMetaObject = nullptr;
@@ -115,6 +116,8 @@ private:
QSGGuiThreadShaderEffectManager::ShaderInfo *m_inProgress[NShader];
QVector<QtPrivate::EffectSlotMapper *> m_mappers[NShader];
+
+ QHash<QQuickItem *, QMetaObject::Connection> m_destroyedConnections;
};
QT_END_NAMESPACE