diff options
author | Paul Lemire <paul.lemire@kdab.com> | 2016-05-20 17:11:37 +0200 |
---|---|---|
committer | Sean Harmer <sean.harmer@kdab.com> | 2016-05-23 16:31:55 +0000 |
commit | 073930f2ef030b3019c323999d910185f4639d9b (patch) | |
tree | 0c2bba252f7a92d4f3841fbe42eeca27e2a8a838 | |
parent | 15dace7c02bc5acdf02f94c8be08fec1a792383c (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>
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); |