QmlDesigner: fix shutdown crash via Q_GLOBAL_STATIC singleton

- Ensures proper lazy initialization and destruction order
- Prevents null accesses during shutdown
  (triggered by missing null-checks in qtddeclarative QQuickShaderEffect)

Change-Id: Ibcce41441b7472e483a3f36c27cf8acfbf45b0c8
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
Tim Jenßen
2025-04-10 21:46:43 +02:00
committed by Tim Jenssen
parent b81f674f31
commit 7c11cd1a1e
9 changed files with 31 additions and 19 deletions

View File

@@ -4,6 +4,7 @@
// This file should match the BlurHelper.qml in qtquickdesigner repository, except for shader paths // This file should match the BlurHelper.qml in qtquickdesigner repository, except for shader paths
import QtQuick import QtQuick
import EffectComposerPropertyData
Item { Item {
id: rootItem id: rootItem
@@ -20,8 +21,8 @@ Item {
visible: false visible: false
layer.enabled: true layer.enabled: true
layer.smooth: true layer.smooth: true
vertexShader: g_propertyData?.blur_vs_path ?? "" vertexShader: GlobalPropertyData?.blur_vs_path ?? ""
fragmentShader: g_propertyData?.blur_fs_path ?? "" fragmentShader: GlobalPropertyData?.blur_fs_path ?? ""
} }
QtObject { QtObject {

View File

@@ -7,6 +7,7 @@ import HelperWidgets as HelperWidgets
import StudioControls as StudioControls import StudioControls as StudioControls
import StudioTheme as StudioTheme import StudioTheme as StudioTheme
import EffectComposerBackend import EffectComposerBackend
import EffectComposerPropertyData
Column { Column {
id: root id: root
@@ -298,8 +299,8 @@ Column {
BlurHelper { BlurHelper {
id: blurHelper id: blurHelper
source: source source: source
property int blurMax: g_propertyData?.blur_helper_max_level ?? 64 property int blurMax: GlobalPropertyData?.blur_helper_max_level ?? 64
property real blurMultiplier: g_propertyData?.blurMultiplier ?? 0 property real blurMultiplier: GlobalPropertyData?.blurMultiplier ?? 0
} }
Item { Item {

View File

@@ -210,7 +210,7 @@ void CompositionNode::parse(const QString &effectName, const QString &qenPath, c
for (const QJsonValueConstRef &prop : jsonProps) { for (const QJsonValueConstRef &prop : jsonProps) {
const auto uniform = new Uniform(effectName, prop.toObject(), qenPath); const auto uniform = new Uniform(effectName, prop.toObject(), qenPath);
m_uniformsModel.addUniform(uniform); m_uniformsModel.addUniform(uniform);
g_propertyData.insert(uniform->name(), uniform->value()); g_propertyData()->insert(uniform->name(), uniform->value());
if (uniform->type() == Uniform::Type::Define) { if (uniform->type() == Uniform::Type::Define) {
// Changing defines requires rebaking the shaders // Changing defines requires rebaking the shaders
connect(uniform, &Uniform::uniformValueChanged, this, &CompositionNode::rebakeRequested); connect(uniform, &Uniform::uniformValueChanged, this, &CompositionNode::rebakeRequested);
@@ -335,7 +335,7 @@ void CompositionNode::openCodeEditor()
void CompositionNode::addUniform(const QVariantMap &data) void CompositionNode::addUniform(const QVariantMap &data)
{ {
const auto uniform = new Uniform({}, QJsonObject::fromVariantMap(data), {}); const auto uniform = new Uniform({}, QJsonObject::fromVariantMap(data), {});
g_propertyData.insert(uniform->name(), uniform->value()); g_propertyData()->insert(uniform->name(), uniform->value());
m_uniformsModel.addUniform(uniform); m_uniformsModel.addUniform(uniform);
updateAreUniformsInUse(true); updateAreUniformsInUse(true);
} }
@@ -346,7 +346,7 @@ void CompositionNode::updateUniform(int index, const QVariantMap &data)
const auto uniform = new Uniform({}, QJsonObject::fromVariantMap(data), {}); const auto uniform = new Uniform({}, QJsonObject::fromVariantMap(data), {});
g_propertyData.insert(uniform->name(), uniform->value()); g_propertyData()->insert(uniform->name(), uniform->value());
m_uniformsModel.updateUniform(index, uniform); m_uniformsModel.updateUniform(index, uniform);
updateAreUniformsInUse(true); updateAreUniformsInUse(true);
} }

View File

@@ -2547,8 +2547,8 @@ QString EffectComposerModel::getQmlComponentString(bool localFiles)
s += l3 + "id: blurHelper\n"; s += l3 + "id: blurHelper\n";
s += l3 + "source: rootItem.source\n"; s += l3 + "source: rootItem.source\n";
int blurMax = 32; int blurMax = 32;
if (g_propertyData.contains("BLUR_HELPER_MAX_LEVEL")) if (g_propertyData()->contains("BLUR_HELPER_MAX_LEVEL"))
blurMax = g_propertyData["BLUR_HELPER_MAX_LEVEL"].toInt(); blurMax = g_propertyData()->value("BLUR_HELPER_MAX_LEVEL").toInt();
s += l3 + QString("property int blurMax: %1\n").arg(blurMax); s += l3 + QString("property int blurMax: %1\n").arg(blurMax);
s += l3 + "property real blurMultiplier: rootItem.blurMultiplier\n"; s += l3 + "property real blurMultiplier: rootItem.blurMultiplier\n";
s += l2 + "}\n"; s += l2 + "}\n";

View File

@@ -68,10 +68,10 @@ bool EffectComposerUniformsModel::setData(const QModelIndex &index, const QVaria
updatedValue = QUrl::fromLocalFile(path).toString(); updatedValue = QUrl::fromLocalFile(path).toString();
uniform->setValue(updatedValue); uniform->setValue(updatedValue);
g_propertyData.insert(uniform->name(), updatedValue); g_propertyData()->insert(uniform->name(), updatedValue);
} else { } else {
uniform->setValue(value); uniform->setValue(value);
g_propertyData.insert(uniform->name(), value); g_propertyData()->insert(uniform->name(), value);
} }
emit dataChanged(index, index, {role}); emit dataChanged(index, index, {role});

View File

@@ -100,11 +100,12 @@ EffectComposerWidget::EffectComposerWidget(EffectComposerView *view)
QmlDesigner::QmlDesignerPlugin::trackWidgetFocusTime(this, QmlDesigner::Constants::EVENT_EFFECTCOMPOSER_TIME); QmlDesigner::QmlDesignerPlugin::trackWidgetFocusTime(this, QmlDesigner::Constants::EVENT_EFFECTCOMPOSER_TIME);
m_quickWidget->rootContext()->setContextProperty("g_propertyData", &g_propertyData); qmlRegisterSingletonInstance<QQmlPropertyMap>(
"EffectComposerPropertyData", 1, 0, "GlobalPropertyData", g_propertyData());
QString blurPath = "file:" + EffectUtils::nodesSourcesPath() + "/common/"; QString blurPath = "file:" + EffectUtils::nodesSourcesPath() + "/common/";
g_propertyData.insert(QString("blur_vs_path"), QString(blurPath + "bluritems.vert.qsb")); g_propertyData()->insert("blur_vs_path", QString(blurPath + "bluritems.vert.qsb"));
g_propertyData.insert(QString("blur_fs_path"), QString(blurPath + "bluritems.frag.qsb")); g_propertyData()->insert("blur_fs_path", QString(blurPath + "bluritems.frag.qsb"));
auto map = m_quickWidget->registerPropertyMap("EffectComposerBackend"); auto map = m_quickWidget->registerPropertyMap("EffectComposerBackend");
map->setProperties({{"effectComposerNodesModel", QVariant::fromValue(effectComposerNodesModel().data())}, map->setProperties({{"effectComposerNodesModel", QVariant::fromValue(effectComposerNodesModel().data())},

View File

@@ -3,8 +3,14 @@
#include "propertyhandler.h" #include "propertyhandler.h"
#include <QQmlPropertyMap>
namespace EffectComposer { namespace EffectComposer {
QQmlPropertyMap g_propertyData; Q_GLOBAL_STATIC(QQmlPropertyMap, globalEffectComposerPropertyData)
QQmlPropertyMap *g_propertyData()
{
return globalEffectComposerPropertyData();
}
} }

View File

@@ -3,13 +3,16 @@
#pragma once #pragma once
#include <QQmlPropertyMap> #include <QtCore/qglobal.h>
QT_BEGIN_NAMESPACE
class QQmlPropertyMap;
QT_END_NAMESPACE
namespace EffectComposer { namespace EffectComposer {
// This will be used for binding dynamic properties // This will be used for binding dynamic properties
// changes between C++ and QML. // changes between C++ and QML.
extern QQmlPropertyMap g_propertyData; QQmlPropertyMap *g_propertyData();
} }

View File

@@ -47,7 +47,7 @@ Uniform::Uniform(const QString &effectName, const QJsonObject &propObj, const QS
m_enableMipmap = getBoolValue(propObj.value("enableMipmap"), false); m_enableMipmap = getBoolValue(propObj.value("enableMipmap"), false);
// Update the mipmap property // Update the mipmap property
QString mipmapProperty = mipmapPropertyName(m_name); QString mipmapProperty = mipmapPropertyName(m_name);
g_propertyData[mipmapProperty] = m_enableMipmap; g_propertyData()->insert(mipmapProperty, m_enableMipmap);
} }
QString controlType = propObj.value("controlType").toString(); QString controlType = propObj.value("controlType").toString();