summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Lemire <paul.lemire@kdab.com>2016-05-20 17:11:37 +0200
committerSean Harmer <sean.harmer@kdab.com>2016-05-23 16:31:55 +0000
commit073930f2ef030b3019c323999d910185f4639d9b (patch)
tree0c2bba252f7a92d4f3841fbe42eeca27e2a8a838
parent15dace7c02bc5acdf02f94c8be08fec1a792383c (diff)
Shared node bookkeeping
Any time a property references a QNode there is a risk that the node gets destroyed and then the property is left pointing to a dangling pointer. To handle such cases, setters of such properties are able to use a helper that internally connect QObject::destroyed signal to a setter removal method. Change-Id: I42428c851d0e3d2d88ab0cf6a5b75605334ec648 Task-number: QTBUG-53456 Reviewed-by: Sean Harmer <sean.harmer@kdab.com>
-rw-r--r--src/core/nodes/qentity.cpp15
-rw-r--r--src/core/nodes/qnode.cpp5
-rw-r--r--src/core/nodes/qnode.h1
-rw-r--r--src/core/nodes/qnode_p.h29
-rw-r--r--src/input/frontend/qabstractaxisinput.cpp11
-rw-r--r--src/input/frontend/qaction.cpp6
-rw-r--r--src/input/frontend/qactioninput.cpp8
-rw-r--r--src/input/frontend/qaxis.cpp6
-rw-r--r--src/input/frontend/qinputchord.cpp6
-rw-r--r--src/input/frontend/qinputsequence.cpp6
-rw-r--r--src/input/frontend/qkeyboardhandler.cpp8
-rw-r--r--src/input/frontend/qlogicaldevice.cpp12
-rw-r--r--src/render/framegraph/qcameraselector.cpp10
-rw-r--r--src/render/framegraph/qlayerfilter.cpp5
-rw-r--r--src/render/framegraph/qrenderpassfilter.cpp10
-rw-r--r--src/render/framegraph/qrenderstateset.cpp5
-rw-r--r--src/render/framegraph/qrendertargetselector.cpp10
-rw-r--r--src/render/framegraph/qtechniquefilter.cpp10
-rw-r--r--src/render/frontend/qrendersettings.cpp9
-rw-r--r--src/render/frontend/qrendertarget.cpp5
-rw-r--r--src/render/frontend/qrendertargetoutput.cpp11
-rw-r--r--src/render/geometry/qattribute.cpp8
-rw-r--r--src/render/geometry/qgeometry.cpp5
-rw-r--r--src/render/geometry/qgeometryrenderer.cpp9
-rw-r--r--src/render/materialsystem/qeffect.cpp10
-rw-r--r--src/render/materialsystem/qmaterial.cpp11
-rw-r--r--src/render/materialsystem/qrenderpass.cpp33
-rw-r--r--src/render/materialsystem/qtechnique.cpp15
-rw-r--r--src/render/texture/qabstracttexture.cpp4
-rw-r--r--tests/auto/render/qlayerfilter/tst_qlayerfilter.cpp18
-rw-r--r--tests/auto/render/qmaterial/tst_qmaterial.cpp6
31 files changed, 279 insertions, 28 deletions
diff --git a/src/core/nodes/qentity.cpp b/src/core/nodes/qentity.cpp
index 86cb050a6..67238ca7e 100644
--- a/src/core/nodes/qentity.cpp
+++ b/src/core/nodes/qentity.cpp
@@ -140,12 +140,18 @@ void QEntity::addComponent(QComponent *comp)
if (d->m_components.count(comp) != 0)
return ;
- d->m_components.append(comp);
- // We only set the Entity as the Component's parent when it has no parent
- // This will be the case mostly on C++ but rarely in QML
+ // We need to add it as a child of the current node if it has been declared inline
+ // Or not previously added as a child of the current node so that
+ // 1) The backend gets notified about it's creation
+ // 2) When the current node is destroyed, it gets destroyed as well
if (!comp->parent())
comp->setParent(this);
+ d->m_components.append(comp);
+
+ // Ensures proper bookkeeping
+ d->registerDestructionHelper(comp, &QEntity::removeComponent, d->m_components);
+
if (d->m_changeArbiter) {
const auto componentAddedChange = QComponentAddedChangePtr::create(this, comp);
d->notifyObservers(componentAddedChange);
@@ -170,6 +176,9 @@ void QEntity::removeComponent(QComponent *comp)
}
d->m_components.removeOne(comp);
+
+ // Remove bookkeeping connection
+ d->unregisterDestructionHelper(comp);
}
/*!
diff --git a/src/core/nodes/qnode.cpp b/src/core/nodes/qnode.cpp
index 6f19e7217..4cdd9ca7b 100644
--- a/src/core/nodes/qnode.cpp
+++ b/src/core/nodes/qnode.cpp
@@ -79,6 +79,10 @@ QNodePrivate::QNodePrivate()
QNodePrivate::~QNodePrivate()
{
+ // Disconnect each connection that was stored
+ for (auto it = m_destructionConnections.begin(), end = m_destructionConnections.end(); it != end; ++it)
+ QObject::disconnect(it.value());
+ m_destructionConnections.clear();
}
void QNodePrivate::init(QNode *parent)
@@ -626,6 +630,7 @@ QNode::~QNode()
{
// Notify the backend that the parent lost this node as a child and
// that this node is being destroyed.
+ Q_EMIT nodeDestroyed();
Q_D(QNode);
d->notifyDestructionChangesAndRemoveFromScene();
}
diff --git a/src/core/nodes/qnode.h b/src/core/nodes/qnode.h
index 0baf1bb8a..5107b3aa8 100644
--- a/src/core/nodes/qnode.h
+++ b/src/core/nodes/qnode.h
@@ -90,6 +90,7 @@ public Q_SLOTS:
Q_SIGNALS:
void parentChanged(QObject *parent);
void enabledChanged(bool enabled);
+ void nodeDestroyed();
protected:
explicit QNode(QNodePrivate &dd, QNode *parent = nullptr);
diff --git a/src/core/nodes/qnode_p.h b/src/core/nodes/qnode_p.h
index e290ffe32..8f48dd728 100644
--- a/src/core/nodes/qnode_p.h
+++ b/src/core/nodes/qnode_p.h
@@ -57,6 +57,7 @@
#include <Qt3DCore/private/qchangearbiter_p.h>
#include <Qt3DCore/private/qt3dcore_global_p.h>
#include "propertychangehandler_p.h"
+#include <functional>
QT_BEGIN_NAMESPACE
@@ -100,6 +101,33 @@ public:
static QNodePrivate *get(QNode *q);
static void nodePtrDeleter(QNode *q);
+ template<typename Caller, typename NodeType>
+ using DestructionFunction = void (Caller::*)(NodeType *);
+
+ template<typename Caller, typename NodeType, typename PropertyType>
+ void registerDestructionHelper(NodeType *, DestructionFunction<Caller, NodeType>, PropertyType);
+
+ template<typename Caller, typename NodeType>
+ void registerDestructionHelper(NodeType *node, DestructionFunction<Caller, NodeType> func, NodeType *&)
+ {
+ // If the node is destoyed, we make sure not to keep a dangling pointer to it
+ auto f = std::bind(func, static_cast<Caller *>(q_func()), nullptr);
+ m_destructionConnections.insert(node, QObject::connect(node, &QNode::nodeDestroyed, f));
+ }
+
+ template<typename Caller, typename NodeType>
+ void registerDestructionHelper(NodeType *node, DestructionFunction<Caller, NodeType> func, QVector<NodeType*> &)
+ {
+ // If the node is destoyed, we make sure not to keep a dangling pointer to it
+ auto f = std::bind(func, static_cast<Caller *>(q_func()), node);
+ m_destructionConnections.insert(node, QObject::connect(node, &QNode::nodeDestroyed, f));
+ }
+
+ void unregisterDestructionHelper(QNode *node)
+ {
+ QObject::disconnect(m_destructionConnections.take(node));
+ }
+
private:
void notifyCreationChange();
void notifyDestructionChangesAndRemoveFromScene();
@@ -118,6 +146,7 @@ private:
friend class PropertyChangeHandler<QNodePrivate>;
bool m_propertyChangesSetup;
PropertyChangeHandler<QNodePrivate> m_signals;
+ QHash<QNode *, QMetaObject::Connection> m_destructionConnections;
};
} // namespace Qt3DCore
diff --git a/src/input/frontend/qabstractaxisinput.cpp b/src/input/frontend/qabstractaxisinput.cpp
index 77966eb1d..ff7c69c12 100644
--- a/src/input/frontend/qabstractaxisinput.cpp
+++ b/src/input/frontend/qabstractaxisinput.cpp
@@ -73,10 +73,21 @@ void QAbstractAxisInput::setSourceDevice(QAbstractPhysicalDevice *sourceDevice)
Q_D(QAbstractAxisInput);
if (d->m_sourceDevice != sourceDevice) {
+ if (d->m_sourceDevice)
+ d->unregisterDestructionHelper(d->m_sourceDevice);
+
+ // We need to add it as a child of the current node if it has been declared inline
+ // Or not previously added as a child of the current node so that
+ // 1) The backend gets notified about it's creation
+ // 2) When the current node is destroyed, it gets destroyed as well
if (sourceDevice && !sourceDevice->parent())
sourceDevice->setParent(this);
d->m_sourceDevice = sourceDevice;
+ // Ensures proper bookkeeping
+ if (d->m_sourceDevice)
+ d->registerDestructionHelper(sourceDevice, &QAbstractAxisInput::setSourceDevice, d->m_sourceDevice);
+
emit sourceDeviceChanged(sourceDevice);
}
}
diff --git a/src/input/frontend/qaction.cpp b/src/input/frontend/qaction.cpp
index 11a8d3ab6..2d98cc9e8 100644
--- a/src/input/frontend/qaction.cpp
+++ b/src/input/frontend/qaction.cpp
@@ -125,6 +125,9 @@ void QAction::addInput(QAbstractActionInput *input)
if (!input->parent())
input->setParent(this);
+ // Ensures proper bookkeeping
+ d->registerDestructionHelper(input, &QAction::removeInput, d->m_inputs);
+
if (d->m_changeArbiter != nullptr) {
const auto change = Qt3DCore::QPropertyNodeAddedChangePtr::create(id(), input);
change->setPropertyName("input");
@@ -148,6 +151,9 @@ void QAction::removeInput(QAbstractActionInput *input)
}
d->m_inputs.removeOne(input);
+
+ // Remove bookkeeping connection
+ d->unregisterDestructionHelper(input);
}
}
diff --git a/src/input/frontend/qactioninput.cpp b/src/input/frontend/qactioninput.cpp
index d3441d284..0be75ec07 100644
--- a/src/input/frontend/qactioninput.cpp
+++ b/src/input/frontend/qactioninput.cpp
@@ -137,12 +137,20 @@ void QActionInput::setSourceDevice(QAbstractPhysicalDevice *sourceDevice)
Q_D(QActionInput);
if (d->m_sourceDevice != sourceDevice) {
+ if (d->m_sourceDevice)
+ d->unregisterDestructionHelper(d->m_sourceDevice);
+
// Check and set parent if needed
// to force creation in the backend
if (sourceDevice && !sourceDevice->parent())
sourceDevice->setParent(this);
d->m_sourceDevice = sourceDevice;
+
+ // Ensures proper bookkeeping
+ if (d->m_sourceDevice)
+ d->registerDestructionHelper(sourceDevice, &QActionInput::setSourceDevice, d->m_sourceDevice);
+
emit sourceDeviceChanged(sourceDevice);
}
}
diff --git a/src/input/frontend/qaxis.cpp b/src/input/frontend/qaxis.cpp
index a12b277e9..3b84e0205 100644
--- a/src/input/frontend/qaxis.cpp
+++ b/src/input/frontend/qaxis.cpp
@@ -83,6 +83,9 @@ void QAxis::addInput(QAbstractAxisInput *input)
if (!input->parent())
input->setParent(this);
+ // Ensures proper bookkeeping
+ d->registerDestructionHelper(input, &QAxis::removeInput, d->m_inputs);
+
if (d->m_changeArbiter != nullptr) {
const auto change = Qt3DCore::QPropertyNodeAddedChangePtr::create(id(), input);
change->setPropertyName("input");
@@ -103,6 +106,9 @@ void QAxis::removeInput(QAbstractAxisInput *input)
}
d->m_inputs.removeOne(input);
+
+ // Remove bookkeeping connection
+ d->unregisterDestructionHelper(input);
}
}
diff --git a/src/input/frontend/qinputchord.cpp b/src/input/frontend/qinputchord.cpp
index a85efa0d2..8260e66da 100644
--- a/src/input/frontend/qinputchord.cpp
+++ b/src/input/frontend/qinputchord.cpp
@@ -152,6 +152,9 @@ void QInputChord::addChord(QAbstractActionInput *input)
if (!d->m_chords.contains(input)) {
d->m_chords.push_back(input);
+ // Ensures proper bookkeeping
+ d->registerDestructionHelper(input, &QInputChord::removeChord, d->m_chords);
+
if (!input->parent())
input->setParent(this);
@@ -180,6 +183,9 @@ void QInputChord::removeChord(QAbstractActionInput *input)
}
d->m_chords.removeOne(input);
+
+ // Remove bookkeeping connection
+ d->unregisterDestructionHelper(input);
}
}
diff --git a/src/input/frontend/qinputsequence.cpp b/src/input/frontend/qinputsequence.cpp
index 1786a9584..27f440ec7 100644
--- a/src/input/frontend/qinputsequence.cpp
+++ b/src/input/frontend/qinputsequence.cpp
@@ -205,6 +205,9 @@ void QInputSequence::addSequence(QAbstractActionInput *input)
if (!d->m_sequences.contains(input)) {
d->m_sequences.push_back(input);
+ // Ensures proper bookkeeping
+ d->registerDestructionHelper(input, &QInputSequence::removeSequence, d->m_sequences);
+
if (!input->parent())
input->setParent(this);
@@ -232,6 +235,9 @@ void QInputSequence::removeSequence(QAbstractActionInput *input)
}
d->m_sequences.removeOne(input);
+
+ // Remove bookkeeping connection
+ d->unregisterDestructionHelper(input);
}
}
diff --git a/src/input/frontend/qkeyboardhandler.cpp b/src/input/frontend/qkeyboardhandler.cpp
index a55e8ce9e..59128ad4d 100644
--- a/src/input/frontend/qkeyboardhandler.cpp
+++ b/src/input/frontend/qkeyboardhandler.cpp
@@ -196,10 +196,18 @@ void QKeyboardHandler::setSourceDevice(QKeyboardDevice *keyboardDevice)
Q_D(QKeyboardHandler);
if (d->m_keyboardDevice != keyboardDevice) {
+ if (d->m_keyboardDevice)
+ d->unregisterDestructionHelper(d->m_keyboardDevice);
+
if (keyboardDevice && !keyboardDevice->parent())
keyboardDevice->setParent(this);
d->m_keyboardDevice = keyboardDevice;
+
+ // Ensures proper bookkeeping
+ if (d->m_keyboardDevice)
+ d->registerDestructionHelper(keyboardDevice, &QKeyboardHandler::setSourceDevice, d->m_keyboardDevice);
+
emit sourceDeviceChanged(keyboardDevice);
}
}
diff --git a/src/input/frontend/qlogicaldevice.cpp b/src/input/frontend/qlogicaldevice.cpp
index f84b45001..90c2e4b05 100644
--- a/src/input/frontend/qlogicaldevice.cpp
+++ b/src/input/frontend/qlogicaldevice.cpp
@@ -171,6 +171,9 @@ void QLogicalDevice::addAction(QAction *action)
if (!action->parent())
action->setParent(this);
+ // Ensures proper bookkeeping
+ d->registerDestructionHelper(action, &QLogicalDevice::removeAction, d->m_actions);
+
if (d->m_changeArbiter != nullptr) {
const auto change = Qt3DCore::QPropertyNodeAddedChangePtr::create(id(), action);
change->setPropertyName("action");
@@ -194,6 +197,9 @@ void QLogicalDevice::removeAction(QAction *action)
}
d->m_actions.removeOne(action);
+
+ // Remove bookkeeping connection
+ d->unregisterDestructionHelper(action);
}
}
@@ -225,6 +231,9 @@ void QLogicalDevice::addAxis(QAxis *axis)
if (!axis->parent())
axis->setParent(this);
+ // Ensures proper bookkeeping
+ d->registerDestructionHelper(axis, &QLogicalDevice::removeAxis, d->m_axes);
+
if (d->m_changeArbiter != nullptr) {
const auto change = Qt3DCore::QPropertyNodeAddedChangePtr::create(id(), axis);
change->setPropertyName("axis");
@@ -247,6 +256,9 @@ void QLogicalDevice::removeAxis(QAxis *axis)
}
d->m_axes.removeOne(axis);
+
+ // Remove bookkeeping connection
+ d->unregisterDestructionHelper(axis);
}
}
diff --git a/src/render/framegraph/qcameraselector.cpp b/src/render/framegraph/qcameraselector.cpp
index 7e26fb7a1..7e76141f9 100644
--- a/src/render/framegraph/qcameraselector.cpp
+++ b/src/render/framegraph/qcameraselector.cpp
@@ -81,7 +81,9 @@ void QCameraSelector::setCamera(Qt3DCore::QEntity *camera)
{
Q_D(QCameraSelector);
if (d->m_camera != camera) {
- d->m_camera = camera;
+
+ if (d->m_camera)
+ d->unregisterDestructionHelper(d->m_camera);
// We need to add it as a child of the current node if it has been declared inline
// Or not previously added as a child of the current node so that
@@ -89,6 +91,12 @@ void QCameraSelector::setCamera(Qt3DCore::QEntity *camera)
// 2) When the current node is destroyed, it gets destroyed as well
if (camera && !camera->parent())
camera->setParent(this);
+ d->m_camera = camera;
+
+ // Ensures proper bookkeeping
+ if (d->m_camera)
+ d->registerDestructionHelper(d->m_camera, &QCameraSelector::setCamera, d->m_camera);
+
emit cameraChanged(camera);
}
}
diff --git a/src/render/framegraph/qlayerfilter.cpp b/src/render/framegraph/qlayerfilter.cpp
index c78142224..7900d5fba 100644
--- a/src/render/framegraph/qlayerfilter.cpp
+++ b/src/render/framegraph/qlayerfilter.cpp
@@ -105,6 +105,9 @@ void QLayerFilter::addLayer(QLayer *layer)
if (!d->m_layers.contains(layer)) {
d->m_layers.append(layer);
+ // Ensures proper bookkeeping
+ d->registerDestructionHelper(layer, &QLayerFilter::removeLayer, d->m_layers);
+
// We need to add it as a child of the current node if it has been declared inline
// Or not previously added as a child of the current node so that
// 1) The backend gets notified about it's creation
@@ -130,6 +133,8 @@ void QLayerFilter::removeLayer(QLayer *layer)
d->notifyObservers(change);
}
d->m_layers.removeOne(layer);
+ // Remove bookkeeping connection
+ d->unregisterDestructionHelper(layer);
}
QVector<QLayer *> QLayerFilter::layers() const
diff --git a/src/render/framegraph/qrenderpassfilter.cpp b/src/render/framegraph/qrenderpassfilter.cpp
index 202a10a35..21f90a998 100644
--- a/src/render/framegraph/qrenderpassfilter.cpp
+++ b/src/render/framegraph/qrenderpassfilter.cpp
@@ -81,6 +81,9 @@ void QRenderPassFilter::addMatch(QFilterKey *filterKey)
if (!d->m_matchList.contains(filterKey)) {
d->m_matchList.append(filterKey);
+ // Ensures proper bookkeeping
+ d->registerDestructionHelper(filterKey, &QRenderPassFilter::removeMatch, d->m_matchList);
+
// We need to add it as a child of the current node if it has been declared inline
// Or not previously added as a child of the current node so that
// 1) The backend gets notified about it's creation
@@ -107,6 +110,8 @@ void QRenderPassFilter::removeMatch(QFilterKey *filterKey)
d->notifyObservers(change);
}
d->m_matchList.removeOne(filterKey);
+ // Remove bookkeeping connection
+ d->unregisterDestructionHelper(filterKey);
}
void QRenderPassFilter::addParameter(QParameter *parameter)
@@ -116,6 +121,9 @@ void QRenderPassFilter::addParameter(QParameter *parameter)
if (!d->m_parameters.contains(parameter)) {
d->m_parameters.append(parameter);
+ // Ensures proper bookkeeping
+ d->registerDestructionHelper(parameter, &QRenderPassFilter::removeParameter, d->m_parameters);
+
// We need to add it as a child of the current node if it has been declared inline
// Or not previously added as a child of the current node so that
// 1) The backend gets notified about it's creation
@@ -142,6 +150,8 @@ void QRenderPassFilter::removeParameter(QParameter *parameter)
d->notifyObservers(change);
}
d->m_parameters.removeOne(parameter);
+ // Remove bookkeeping connection
+ d->unregisterDestructionHelper(parameter);
}
QVector<QParameter *> QRenderPassFilter::parameters() const
diff --git a/src/render/framegraph/qrenderstateset.cpp b/src/render/framegraph/qrenderstateset.cpp
index 5d0db339a..845fb5d91 100644
--- a/src/render/framegraph/qrenderstateset.cpp
+++ b/src/render/framegraph/qrenderstateset.cpp
@@ -99,6 +99,9 @@ void QRenderStateSet::addRenderState(QRenderState *state)
if (!d->m_renderStates.contains(state)) {
d->m_renderStates.append(state);
+ // Ensures proper bookkeeping
+ d->registerDestructionHelper(state, &QRenderStateSet::removeRenderState, d->m_renderStates);
+
if (!state->parent())
state->setParent(this);
@@ -124,6 +127,8 @@ void QRenderStateSet::removeRenderState(QRenderState *state)
d->notifyObservers(change);
}
d->m_renderStates.removeOne(state);
+ // Remove bookkeeping connection
+ d->unregisterDestructionHelper(state);
}
/*!
diff --git a/src/render/framegraph/qrendertargetselector.cpp b/src/render/framegraph/qrendertargetselector.cpp
index f90e977a8..ca5743a57 100644
--- a/src/render/framegraph/qrendertargetselector.cpp
+++ b/src/render/framegraph/qrendertargetselector.cpp
@@ -69,11 +69,19 @@ void QRenderTargetSelector::setTarget(QRenderTarget *target)
{
Q_D(QRenderTargetSelector);
if (d->m_target != target) {
- d->m_target = target;
+
+ if (d->m_target)
+ d->unregisterDestructionHelper(d->m_target);
// For inline declaration cases
if (target != nullptr && !target->parent())
target->setParent(this);
+
+ d->m_target = target;
+
+ if (d->m_target)
+ d->registerDestructionHelper(d->m_target, &QRenderTargetSelector::setTarget, d->m_target);
+
emit targetChanged(target);
}
}
diff --git a/src/render/framegraph/qtechniquefilter.cpp b/src/render/framegraph/qtechniquefilter.cpp
index 49ac03041..07707866c 100644
--- a/src/render/framegraph/qtechniquefilter.cpp
+++ b/src/render/framegraph/qtechniquefilter.cpp
@@ -85,6 +85,9 @@ void QTechniqueFilter::addMatch(QFilterKey *filterKey)
if (!d->m_matchList.contains(filterKey)) {
d->m_matchList.append(filterKey);
+ // Ensures proper bookkeeping
+ d->registerDestructionHelper(filterKey, &QTechniqueFilter::addMatch, d->m_matchList);
+
// We need to add it as a child of the current node if it has been declared inline
// Or not previously added as a child of the current node so that
// 1) The backend gets notified about it's creation
@@ -110,6 +113,8 @@ void QTechniqueFilter::removeMatch(QFilterKey *filterKey)
d->notifyObservers(change);
}
d->m_matchList.removeOne(filterKey);
+ // Remove bookkeeping connection
+ d->unregisterDestructionHelper(filterKey);
}
void QTechniqueFilter::addParameter(QParameter *parameter)
@@ -119,6 +124,9 @@ void QTechniqueFilter::addParameter(QParameter *parameter)
if (!d->m_parameters.contains(parameter)) {
d->m_parameters.append(parameter);
+ // Ensures proper bookkeeping
+ d->registerDestructionHelper(parameter, &QTechniqueFilter::removeParameter, d->m_parameters);
+
// We need to add it as a child of the current node if it has been declared inline
// Or not previously added as a child of the current node so that
// 1) The backend gets notified about it's creation
@@ -144,6 +152,8 @@ void QTechniqueFilter::removeParameter(QParameter *parameter)
d->notifyObservers(change);
}
d->m_parameters.removeOne(parameter);
+ // Remove bookkeeping connection
+ d->unregisterDestructionHelper(parameter);
}
QVector<QParameter *> QTechniqueFilter::parameters() const
diff --git a/src/render/frontend/qrendersettings.cpp b/src/render/frontend/qrendersettings.cpp
index a6e4a99eb..b0a409275 100644
--- a/src/render/frontend/qrendersettings.cpp
+++ b/src/render/frontend/qrendersettings.cpp
@@ -110,9 +110,18 @@ void QRenderSettings::setActiveFrameGraph(QFrameGraphNode *activeFrameGraph)
if (d->m_activeFrameGraph == activeFrameGraph)
return;
+ if (d->m_activeFrameGraph)
+ d->unregisterDestructionHelper(d->m_activeFrameGraph);
+
if (activeFrameGraph != nullptr && !activeFrameGraph->parent())
activeFrameGraph->setParent(this);
+
d->m_activeFrameGraph = activeFrameGraph;
+
+ // Ensures proper bookkeeping
+ if (d->m_activeFrameGraph)
+ d->registerDestructionHelper(d->m_activeFrameGraph, &QRenderSettings::setActiveFrameGraph, d->m_activeFrameGraph);
+
emit activeFrameGraphChanged(activeFrameGraph);
}
diff --git a/src/render/frontend/qrendertarget.cpp b/src/render/frontend/qrendertarget.cpp
index 0157a16f0..349997bdb 100644
--- a/src/render/frontend/qrendertarget.cpp
+++ b/src/render/frontend/qrendertarget.cpp
@@ -77,6 +77,9 @@ void QRenderTarget::addOutput(QRenderTargetOutput *output)
if (output && !d->m_outputs.contains(output)) {
d->m_outputs.append(output);
+ // Ensures proper bookkeeping
+ d->registerDestructionHelper(output, &QRenderTarget::removeOutput, d->m_outputs);
+
if (!output->parent())
output->setParent(this);
@@ -98,6 +101,8 @@ void QRenderTarget::removeOutput(QRenderTargetOutput *output)
d->notifyObservers(change);
}
d->m_outputs.removeOne(output);
+ // Remove bookkeeping connection
+ d->unregisterDestructionHelper(output);
}
QVector<QRenderTargetOutput *> QRenderTarget::outputs() const
diff --git a/src/render/frontend/qrendertargetoutput.cpp b/src/render/frontend/qrendertargetoutput.cpp
index 75bf1ab91..0b08b5b04 100644
--- a/src/render/frontend/qrendertargetoutput.cpp
+++ b/src/render/frontend/qrendertargetoutput.cpp
@@ -91,11 +91,20 @@ void QRenderTargetOutput::setTexture(QAbstractTexture *texture)
{
Q_D(QRenderTargetOutput);
if (texture != d->m_texture) {
- d->m_texture = texture;
+
+ if (d->m_texture)
+ d->unregisterDestructionHelper(d->m_texture);
// Handle inline declaration
if (!texture->parent())
texture->setParent(this);
+
+ d->m_texture = texture;
+
+ // Ensures proper bookkeeping
+ if (d->m_texture)
+ d->registerDestructionHelper(d->m_texture, &QRenderTargetOutput::setTexture, d->m_texture);
+
emit textureChanged(texture);
}
}
diff --git a/src/render/geometry/qattribute.cpp b/src/render/geometry/qattribute.cpp
index 1e9348567..671179f20 100644
--- a/src/render/geometry/qattribute.cpp
+++ b/src/render/geometry/qattribute.cpp
@@ -258,6 +258,9 @@ void QAttribute::setBuffer(QBuffer *buffer)
if (d->m_buffer == buffer)
return;
+ if (d->m_buffer)
+ d->unregisterDestructionHelper(d->m_buffer);
+
// We need to add it as a child of the current node if it has been declared inline
// Or not previously added as a child of the current node so that
// 1) The backend gets notified about it's creation
@@ -266,6 +269,11 @@ void QAttribute::setBuffer(QBuffer *buffer)
buffer->setParent(this);
d->m_buffer = buffer;
+
+ // Ensures proper bookkeeping
+ if (d->m_buffer)
+ d->registerDestructionHelper(d->m_buffer, &QAttribute::setBuffer, d->m_buffer);
+
emit bufferChanged(buffer);
}
diff --git a/src/render/geometry/qgeometry.cpp b/src/render/geometry/qgeometry.cpp
index 57222a364..9b6b9a8e7 100644
--- a/src/render/geometry/qgeometry.cpp
+++ b/src/render/geometry/qgeometry.cpp
@@ -124,6 +124,9 @@ void QGeometry::addAttribute(QAttribute *attribute)
if (!d->m_attributes.contains(attribute)) {
d->m_attributes.append(attribute);
+ // Ensures proper bookkeeping
+ d->registerDestructionHelper(attribute, &QGeometry::removeAttribute, d->m_attributes);
+
// We need to add it as a child of the current node if it has been declared inline
// Or not previously added as a child of the current node so that
// 1) The backend gets notified about it's creation
@@ -152,6 +155,8 @@ void QGeometry::removeAttribute(QAttribute *attribute)
d->notifyObservers(change);
}
d->m_attributes.removeOne(attribute);
+ // Remove bookkeeping connection
+ d->unregisterDestructionHelper(attribute);
}
void QGeometry::setBoundingVolumePositionAttribute(QAttribute *boundingVolumePositionAttribute)
diff --git a/src/render/geometry/qgeometryrenderer.cpp b/src/render/geometry/qgeometryrenderer.cpp
index a714c3155..db59f04a6 100644
--- a/src/render/geometry/qgeometryrenderer.cpp
+++ b/src/render/geometry/qgeometryrenderer.cpp
@@ -398,10 +398,19 @@ void QGeometryRenderer::setGeometry(QGeometry *geometry)
d->notifyObservers(change);
}
+ if (d->m_geometry)
+ d->unregisterDestructionHelper(d->m_geometry);
+
if (geometry && !geometry->parent())
geometry->setParent(this);
d->m_geometry = geometry;
+
+
+ // Ensures proper bookkeeping
+ if (d->m_geometry)
+ d->registerDestructionHelper(d->m_geometry, &QGeometryRenderer::setGeometry, d->m_geometry);
+
const bool blocked = blockNotifications(true);
emit geometryChanged(geometry);
blockNotifications(blocked);
diff --git a/src/render/materialsystem/qeffect.cpp b/src/render/materialsystem/qeffect.cpp
index b82875dd1..4d02b6d79 100644
--- a/src/render/materialsystem/qeffect.cpp
+++ b/src/render/materialsystem/qeffect.cpp
@@ -79,6 +79,9 @@ void QEffect::addParameter(QParameter *parameter)
if (parameter && !d->m_parameters.contains(parameter)) {
d->m_parameters.append(parameter);
+ // Ensures proper bookkeeping
+ d->registerDestructionHelper(parameter, &QEffect::removeParameter, d->m_parameters);
+
// We need to add it as a child of the current node if it has been declared inline
// Or not previously added as a child of the current node so that
// 1) The backend gets notified about it's creation
@@ -104,6 +107,8 @@ void QEffect::removeParameter(QParameter *parameter)
d->notifyObservers(change);
}
d->m_parameters.removeOne(parameter);
+ // Remove bookkeeping connection
+ d->unregisterDestructionHelper(parameter);
}
QVector<QParameter *> QEffect::parameters() const
@@ -124,6 +129,9 @@ void QEffect::addTechnique(QTechnique *t)
if (t && !d->m_techniques.contains(t)) {
d->m_techniques.append(t);
+ // Ensures proper bookkeeping
+ d->registerDestructionHelper(t, &QEffect::removeTechnique, d->m_techniques);
+
// We need to add it as a child of the current node if it has been declared inline
// Or not previously added as a child of the current node so that
// 1) The backend gets notified about it's creation
@@ -153,6 +161,8 @@ void QEffect::removeTechnique(QTechnique *t)
d->notifyObservers(change);
}
d->m_techniques.removeOne(t);
+ // Remove bookkeeping connection
+ d->unregisterDestructionHelper(t);
}
/*!
diff --git a/src/render/materialsystem/qmaterial.cpp b/src/render/materialsystem/qmaterial.cpp
index 644c11b4d..fa4955017 100644
--- a/src/render/materialsystem/qmaterial.cpp
+++ b/src/render/materialsystem/qmaterial.cpp
@@ -112,6 +112,9 @@ void QMaterial::setEffect(QEffect *effect)
Q_D(QMaterial);
if (effect != d->m_effect) {
+ if (d->m_effect)
+ d->unregisterDestructionHelper(d->m_effect);
+
// We need to add it as a child of the current node if it has been declared inline
// Or not previously added as a child of the current node so that
// 1) The backend gets notified about it's creation
@@ -119,6 +122,11 @@ void QMaterial::setEffect(QEffect *effect)
if (effect && !effect->parent())
effect->setParent(this);
d->m_effect = effect;
+
+ // Ensures proper bookkeeping
+ if (d->m_effect)
+ d->registerDestructionHelper(d->m_effect, &QMaterial::setEffect, d->m_effect);
+
emit effectChanged(effect);
}
}
@@ -139,6 +147,9 @@ void QMaterial::addParameter(QParameter *parameter)
if (!d->m_parameters.contains(parameter)) {
d->m_parameters.append(parameter);
+ // Ensures proper bookkeeping
+ d->registerDestructionHelper(parameter, &QMaterial::removeParameter, d->m_parameters);
+
// We need to add it as a child of the current node if it has been declared inline
// Or not previously added as a child of the current node so that
// 1) The backend gets notified about it's creation
diff --git a/src/render/materialsystem/qrenderpass.cpp b/src/render/materialsystem/qrenderpass.cpp
index 69098f9ec..9ec7a452b 100644
--- a/src/render/materialsystem/qrenderpass.cpp
+++ b/src/render/materialsystem/qrenderpass.cpp
@@ -92,21 +92,23 @@ void QRenderPass::setShaderProgram(QShaderProgram *shaderProgram)
d->notifyObservers(change);
}
- d->m_shader = shaderProgram;
- emit shaderProgramChanged(shaderProgram);
+ if (d->m_shader)
+ d->unregisterDestructionHelper(d->m_shader);
// We need to add it as a child of the current node if it has been declared inline
// Or not previously added as a child of the current node so that
// 1) The backend gets notified about it's creation
// 2) When the current node is destroyed, it gets destroyed as well
- if (!shaderProgram->parent())
+ if (shaderProgram && !shaderProgram->parent())
shaderProgram->setParent(this);
- if (d->m_shader && d->m_changeArbiter != nullptr) {
- const auto change = QPropertyNodeAddedChangePtr::create(id(), d->m_shader);
- change->setPropertyName("shaderProgram");
- d->notifyObservers(change);
- }
+ d->m_shader = shaderProgram;
+
+ // Ensures proper bookkeeping
+ if (d->m_shader)
+ d->registerDestructionHelper(d->m_shader, &QRenderPass::setShaderProgram, d->m_shader);
+
+ emit shaderProgramChanged(shaderProgram);
}
}
@@ -123,6 +125,9 @@ void QRenderPass::addFilterKey(QFilterKey *filterKey)
if (!d->m_filterKeyList.contains(filterKey)) {
d->m_filterKeyList.append(filterKey);
+ // Ensures proper bookkeeping
+ d->registerDestructionHelper(filterKey, &QRenderPass::removeFilterKey, d->m_filterKeyList);
+
// We need to add it as a child of the current node if it has been declared inline
// Or not previously added as a child of the current node so that
// 1) The backend gets notified about it's creation
@@ -148,6 +153,8 @@ void QRenderPass::removeFilterKey(QFilterKey *filterKey)
d->notifyObservers(change);
}
d->m_filterKeyList.removeOne(filterKey);
+ // Remove bookkeeping connection
+ d->unregisterDestructionHelper(filterKey);
}
QVector<QFilterKey *> QRenderPass::filterKeys() const
@@ -171,6 +178,9 @@ void QRenderPass::addRenderState(QRenderState *state)
if (!d->m_renderStates.contains(state)) {
d->m_renderStates.append(state);
+ // Ensures proper bookkeeping
+ d->registerDestructionHelper(state, &QRenderPass::removeRenderState, d->m_renderStates);
+
if (!state->parent())
state->setParent(this);
@@ -195,6 +205,8 @@ void QRenderPass::removeRenderState(QRenderState *state)
d->notifyObservers(change);
}
d->m_renderStates.removeOne(state);
+ // Remove bookkeeping connection
+ d->unregisterDestructionHelper(state);
}
/*!
@@ -214,6 +226,9 @@ void QRenderPass::addParameter(QParameter *parameter)
if (!d->m_parameters.contains(parameter)) {
d->m_parameters.append(parameter);
+ // Ensures proper bookkeeping
+ d->registerDestructionHelper(parameter, &QRenderPass::removeParameter, d->m_parameters);
+
// We need to add it as a child of the current node if it has been declared inline
// Or not previously added as a child of the current node so that
// 1) The backend gets notified about it's creation
@@ -239,6 +254,8 @@ void QRenderPass::removeParameter(QParameter *parameter)
d->notifyObservers(change);
}
d->m_parameters.removeOne(parameter);
+ // Remove bookkeeping connection
+ d->unregisterDestructionHelper(parameter);
}
diff --git a/src/render/materialsystem/qtechnique.cpp b/src/render/materialsystem/qtechnique.cpp
index f2d146202..c0cc175f9 100644
--- a/src/render/materialsystem/qtechnique.cpp
+++ b/src/render/materialsystem/qtechnique.cpp
@@ -97,6 +97,9 @@ void QTechnique::addFilterKey(QFilterKey *filterKey)
if (!d->m_filterKeys.contains(filterKey)) {
d->m_filterKeys.append(filterKey);
+ // Ensures proper bookkeeping
+ d->registerDestructionHelper(filterKey, &QTechnique::removeFilterKey, d->m_filterKeys);
+
// We need to add it as a child of the current node if it has been declared inline
// Or not previously added as a child of the current node so that
// 1) The backend gets notified about it's creation
@@ -122,6 +125,8 @@ void QTechnique::removeFilterKey(QFilterKey *filterKey)
d->notifyObservers(change);
}
d->m_filterKeys.removeOne(filterKey);
+ // Remove bookkeeping connection
+ d->unregisterDestructionHelper(filterKey);
}
QVector<QFilterKey *> QTechnique::filterKeys() const
@@ -137,6 +142,9 @@ void QTechnique::addParameter(QParameter *parameter)
if (!d->m_parameters.contains(parameter)) {
d->m_parameters.append(parameter);
+ // Ensures proper bookkeeping
+ d->registerDestructionHelper(parameter, &QTechnique::removeParameter, d->m_parameters);
+
// We need to add it as a child of the current node if it has been declared inline
// Or not previously added as a child of the current node so that
// 1) The backend gets notified about it's creation
@@ -162,6 +170,8 @@ void QTechnique::removeParameter(QParameter *parameter)
d->notifyObservers(change);
}
d->m_parameters.removeOne(parameter);
+ // Remove bookkeeping connection
+ d->unregisterDestructionHelper(parameter);
}
/*!
@@ -176,6 +186,9 @@ void QTechnique::addRenderPass(QRenderPass *pass)
if (!d->m_renderPasses.contains(pass)) {
d->m_renderPasses.append(pass);
+ // Ensures proper bookkeeping
+ d->registerDestructionHelper(pass, &QTechnique::removeRenderPass, d->m_renderPasses);
+
// We need to add it as a child of the current node if it has been declared inline
// Or not previously added as a child of the current node so that
// 1) The backend gets notified about it's creation
@@ -206,6 +219,8 @@ void QTechnique::removeRenderPass(QRenderPass *pass)
d->notifyObservers(change);
}
d->m_renderPasses.removeOne(pass);
+ // Remove bookkeeping connection
+ d->unregisterDestructionHelper(pass);
}
/*!
diff --git a/src/render/texture/qabstracttexture.cpp b/src/render/texture/qabstracttexture.cpp
index 36ee6851f..b62424eb9 100644
--- a/src/render/texture/qabstracttexture.cpp
+++ b/src/render/texture/qabstracttexture.cpp
@@ -273,6 +273,8 @@ void QAbstractTexture::addTextureImage(QAbstractTextureImage *textureImage)
if (!d->m_textureImages.contains(textureImage)) {
d->m_textureImages.append(textureImage);
+ // Ensures proper bookkeeping
+ d->registerDestructionHelper(textureImage, &QAbstractTexture::removeTextureImage, d->m_textureImages);
if (textureImage->parent() && textureImage->parent() != this)
qWarning() << "A QAbstractTextureImage was shared, expect a crash, undefined behavior at best";
@@ -304,6 +306,8 @@ void QAbstractTexture::removeTextureImage(QAbstractTextureImage *textureImage)
d->notifyObservers(change);
}
d->m_textureImages.removeOne(textureImage);
+ // Remove bookkeeping connection
+ d->unregisterDestructionHelper(textureImage);
}
/*!
diff --git a/tests/auto/render/qlayerfilter/tst_qlayerfilter.cpp b/tests/auto/render/qlayerfilter/tst_qlayerfilter.cpp
index 9c14af50e..6ae2394a1 100644
--- a/tests/auto/render/qlayerfilter/tst_qlayerfilter.cpp
+++ b/tests/auto/render/qlayerfilter/tst_qlayerfilter.cpp
@@ -45,14 +45,10 @@ class tst_QLayerFilter: public QObject
public:
tst_QLayerFilter()
: QObject()
- , layersNode(new Qt3DCore::QNode)
{
qRegisterMetaType<Qt3DCore::QNode*>();
}
-private:
- QScopedPointer<Qt3DCore::QNode> layersNode;
-
private Q_SLOTS:
void checkCloning_data()
@@ -65,15 +61,15 @@ private Q_SLOTS:
QTest::newRow("defaultConstructed") << defaultConstructed << QVector<Qt3DRender::QLayer*>();
Qt3DRender::QLayerFilter *singleLayer = new Qt3DRender::QLayerFilter();
- auto layer = QVector<Qt3DRender::QLayer*>() << new Qt3DRender::QLayer(layersNode.data());
+ auto layer = QVector<Qt3DRender::QLayer*>() << new Qt3DRender::QLayer();
QCoreApplication::processEvents();
singleLayer->addLayer(layer.first());
QTest::newRow("single layer") << singleLayer << layer;
Qt3DRender::QLayerFilter *multiLayers = new Qt3DRender::QLayerFilter();
- auto layers = QVector<Qt3DRender::QLayer*>() << new Qt3DRender::QLayer(layersNode.data())
- << new Qt3DRender::QLayer(layersNode.data())
- << new Qt3DRender::QLayer(layersNode.data());
+ auto layers = QVector<Qt3DRender::QLayer*>() << new Qt3DRender::QLayer()
+ << new Qt3DRender::QLayer()
+ << new Qt3DRender::QLayer();
QCoreApplication::processEvents();
for (Qt3DRender::QLayer *layer : qAsConst(layers))
multiLayers->addLayer(layer);
@@ -94,7 +90,7 @@ private Q_SLOTS:
QVector<Qt3DCore::QNodeCreatedChangeBasePtr> creationChanges = creationChangeGenerator.creationChanges();
// THEN
- QCOMPARE(creationChanges.size(), 1);
+ QCOMPARE(creationChanges.size(), layers.size() + 1);
const Qt3DCore::QNodeCreatedChangePtr<Qt3DRender::QLayerFilterData> creationChangeData =
qSharedPointerCast<Qt3DCore::QNodeCreatedChange<Qt3DRender::QLayerFilterData>>(creationChanges.first());
@@ -117,7 +113,7 @@ private Q_SLOTS:
arbiter.setArbiterOnNode(layerFilter.data());
// WHEN
- auto layer = new Qt3DRender::QLayer(layersNode.data());
+ auto layer = new Qt3DRender::QLayer(layerFilter.data());
QCoreApplication::processEvents();
layerFilter->addLayer(layer);
QCoreApplication::processEvents();
@@ -133,7 +129,7 @@ private Q_SLOTS:
arbiter.events.clear();
// WHEN
- layer = new Qt3DRender::QLayer(layersNode.data());
+ layer = new Qt3DRender::QLayer(layerFilter.data());
QCoreApplication::processEvents();
layerFilter->addLayer(layer);
QCoreApplication::processEvents();
diff --git a/tests/auto/render/qmaterial/tst_qmaterial.cpp b/tests/auto/render/qmaterial/tst_qmaterial.cpp
index d658b1b3e..2dfe6637c 100644
--- a/tests/auto/render/qmaterial/tst_qmaterial.cpp
+++ b/tests/auto/render/qmaterial/tst_qmaterial.cpp
@@ -58,9 +58,9 @@ public:
explicit TestMaterial(Qt3DCore::QNode *parent = 0)
: Qt3DRender::QMaterial(parent)
, m_effect(new Qt3DRender::QEffect(this))
- , m_technique(new Qt3DRender::QTechnique(this))
- , m_renderPass(new Qt3DRender::QRenderPass(this))
- , m_shaderProgram(new Qt3DRender::QShaderProgram(this))
+ , m_technique(new Qt3DRender::QTechnique(m_effect))
+ , m_renderPass(new Qt3DRender::QRenderPass(m_technique))
+ , m_shaderProgram(new Qt3DRender::QShaderProgram(m_renderPass))
{
m_renderPass->setShaderProgram(m_shaderProgram);
m_technique->addRenderPass(m_renderPass);