summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLiang Qi <liang.qi@qt.io>2018-01-22 13:21:04 +0100
committerLiang Qi <liang.qi@qt.io>2018-01-22 14:08:36 +0100
commit598acd70a44f787a3e72c586576bfc028d5af02d (patch)
tree30691da4bbdb935dbf592d9689d034060b9d4d74
parent32a511a667bab93d4fca2e71b38d89f8d76aa07c (diff)
parent2b19bde378e084331b1b7ba9aa076270492999bb (diff)
Merge remote-tracking branch 'origin/5.10' into dev
Conflicts: src/render/backend/renderer_p.h src/render/geometry/geometryrenderer.cpp src/render/geometry/qmesh.cpp src/render/geometry/qmesh_p.h tests/auto/render/commons/testrenderer.h tests/auto/render/meshfunctors/tst_meshfunctors.cpp tests/auto/render/qmesh/tst_qmesh.cpp Change-Id: Ia078029e2faf23fe253c5ce385e393c094266e3b
-rw-r--r--dist/changes-5.9.427
-rw-r--r--src/animation/backend/animationutils.cpp7
-rw-r--r--src/animation/backend/animationutils_p.h12
-rw-r--r--src/animation/backend/buildblendtreesjob.cpp35
-rw-r--r--src/animation/backend/clipblendvalue.cpp12
-rw-r--r--src/animation/backend/clipblendvalue_p.h3
-rw-r--r--src/animation/backend/evaluateblendclipanimatorjob.cpp1
-rw-r--r--src/core/nodes/qnode.cpp23
-rw-r--r--src/plugins/sceneparsers/assimp/assimpimporter.cpp1
-rw-r--r--src/quick3d/imports/scene3d/scene3drenderer.cpp6
-rw-r--r--src/quick3d/imports/scene3d/scene3drenderer_p.h1
-rw-r--r--src/render/backend/abstractrenderer_p.h2
-rw-r--r--src/render/backend/renderer.cpp17
-rw-r--r--src/render/backend/renderer_p.h2
-rw-r--r--src/render/frontend/qrenderaspect.cpp4
-rw-r--r--src/render/frontend/qrenderaspect_p.h2
-rw-r--r--src/render/graphicshelpers/graphicscontext.cpp3
-rw-r--r--tests/auto/animation/clipblendvalue/tst_clipblendvalue.cpp19
-rw-r--r--tests/auto/core/nodes/tst_nodes.cpp74
-rw-r--r--tests/auto/render/commons/testrenderer.h2
20 files changed, 218 insertions, 35 deletions
diff --git a/dist/changes-5.9.4 b/dist/changes-5.9.4
new file mode 100644
index 000000000..361215783
--- /dev/null
+++ b/dist/changes-5.9.4
@@ -0,0 +1,27 @@
+Qt 5.9.4 is a bug-fix release. It maintains both forward and backward
+compatibility (source and binary) with Qt 5.9.0.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+http://doc.qt.io/qt-5/index.html
+
+The Qt version 5.9 series is binary compatible with the 5.8.x series.
+Applications compiled for 5.8 will continue to run with 5.9.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+* Qt 5.9.4 Changes *
+****************************************************************************
+
+ - [QTBUG-62235] Fix viewport scaling in Scene3D on hidpi displays
+ - [QTBUG-63537] Fix handling of uniform arrays values in some drivers
+ - [QTBUG-63897][QTBUG-65407] Fix crash when deleting Scene3D items
+
diff --git a/src/animation/backend/animationutils.cpp b/src/animation/backend/animationutils.cpp
index 8652c7df1..44bf31854 100644
--- a/src/animation/backend/animationutils.cpp
+++ b/src/animation/backend/animationutils.cpp
@@ -848,6 +848,13 @@ QVector<float> defaultValueForChannel(Handler *handler,
return result;
}
+void applyComponentDefaultValues(const QVector<ComponentValue> &componentDefaults,
+ ClipResults &formattedClipResults)
+{
+ for (const auto &componentDefault : componentDefaults)
+ formattedClipResults[componentDefault.componentIndex] = componentDefault.value;
+}
+
} // Animation
} // Qt3DAnimation
diff --git a/src/animation/backend/animationutils_p.h b/src/animation/backend/animationutils_p.h
index 6a1b18936..a0ab218d4 100644
--- a/src/animation/backend/animationutils_p.h
+++ b/src/animation/backend/animationutils_p.h
@@ -191,6 +191,13 @@ inline QDebug operator<<(QDebug dbg, const ChannelNameAndType &nameAndType)
}
#endif
+struct ComponentValue
+{
+ int componentIndex;
+ float value;
+};
+QT3D_DECLARE_TYPEINFO_2(Qt3DAnimation, Animation, ComponentValue, Q_PRIMITIVE_TYPE)
+
struct ClipFormat
{
// TODO: Remove the mask and store both the sourceClipIndices and
@@ -200,6 +207,7 @@ struct ClipFormat
QVector<QBitArray> sourceClipMask;
QVector<ComponentIndices> formattedComponentIndices;
QVector<ChannelNameAndType> namesAndTypes;
+ QVector<ComponentValue> defaultComponentValues;
};
#ifndef QT_NO_DEBUG_STREAM
@@ -338,6 +346,10 @@ ClipResults evaluateBlendTree(Handler *handler,
Q_AUTOTEST_EXPORT
QVector<float> defaultValueForChannel(Handler *handler, const ChannelNameAndType &channelDescription);
+Q_AUTOTEST_EXPORT
+void applyComponentDefaultValues(const QVector<ComponentValue> &componentDefaults,
+ ClipResults &formattedClipResults);
+
} // Animation
} // Qt3DAnimation
diff --git a/src/animation/backend/buildblendtreesjob.cpp b/src/animation/backend/buildblendtreesjob.cpp
index a08aa2a34..581b133b5 100644
--- a/src/animation/backend/buildblendtreesjob.cpp
+++ b/src/animation/backend/buildblendtreesjob.cpp
@@ -90,10 +90,17 @@ void BuildBlendTreesJob::run()
QVector<QBitArray> blendTreeChannelMask;
const QVector<Qt3DCore::QNodeId> valueNodeIds
= gatherValueNodesToEvaluate(m_handler, blendClipAnimator->blendTreeRootId());
+
+ // Store the clip value nodes to avoid further lookups below.
+ // TODO: Refactor this next block into a function in animationutils.cpp that takes
+ // a QVector<QClipBlendValue*> as input.
+ QVector<ClipBlendValue *> valueNodes;
+ valueNodes.reserve(valueNodeIds.size());
for (const auto valueNodeId : valueNodeIds) {
ClipBlendValue *valueNode
= static_cast<ClipBlendValue *>(m_handler->clipBlendNodeManager()->lookupNode(valueNodeId));
Q_ASSERT(valueNode);
+ valueNodes.push_back(valueNode);
const Qt3DCore::QNodeId clipId = valueNode->clipId();
AnimationClip *clip = m_handler->animationClipLoaderManager()->lookupResource(clipId);
@@ -132,6 +139,34 @@ void BuildBlendTreesJob::run()
}
}
+ // Now that we know the overall blend tree mask, go back and compare this to
+ // the masks from each of the value nodes. If the overall mask requires a
+ // channel but the value node does not provide it, we need to store default
+ // values to use for that channel so that the blending evaluation works as
+ // expected.
+ for (const auto valueNode : valueNodes) {
+ ClipFormat &f = valueNode->clipFormat(blendClipAnimator->peerId());
+
+ const int channelCount = blendTreeChannelMask.size();
+ for (int i = 0; i < channelCount; ++i) {
+ if (blendTreeChannelMask[i] == f.sourceClipMask[i])
+ continue; // Masks match, nothing to do
+
+ // If we get to here then we need to obtain a default value
+ // for this channel and store it for later application to any
+ // missing components of this channel.
+ const QVector<float> defaultValue = defaultValueForChannel(m_handler,
+ f.namesAndTypes[i]);
+
+ // Find the indices where we later need to inject these default
+ // values and store them in the format.
+ const ComponentIndices &componentIndices = f.formattedComponentIndices[i];
+ Q_ASSERT(componentIndices.size() == defaultValue.size());
+ for (int j = 0; j < defaultValue.size(); ++j)
+ f.defaultComponentValues.push_back({componentIndices[j], defaultValue[j]});
+ }
+ }
+
// Finally, build the mapping data vector for this blended clip animator. This
// gets used during the final stage of evaluation when sending the property changes
// out to the targets of the animation. We do the costly work once up front.
diff --git a/src/animation/backend/clipblendvalue.cpp b/src/animation/backend/clipblendvalue.cpp
index c1de1fea7..5685d5191 100644
--- a/src/animation/backend/clipblendvalue.cpp
+++ b/src/animation/backend/clipblendvalue.cpp
@@ -102,12 +102,16 @@ void ClipBlendValue::setClipFormat(Qt3DCore::QNodeId animatorId, const ClipForma
}
}
-ClipFormat ClipBlendValue::clipFormat(Qt3DCore::QNodeId animatorId)
+ClipFormat &ClipBlendValue::clipFormat(Qt3DCore::QNodeId animatorId)
{
const int animatorIndex = m_animatorIds.indexOf(animatorId);
- if (animatorIndex != -1)
- return m_clipFormats[animatorIndex];
- return ClipFormat();
+ return m_clipFormats[animatorIndex];
+}
+
+const ClipFormat &ClipBlendValue::clipFormat(Qt3DCore::QNodeId animatorId) const
+{
+ const int animatorIndex = m_animatorIds.indexOf(animatorId);
+ return m_clipFormats[animatorIndex];
}
} // namespace Animation
diff --git a/src/animation/backend/clipblendvalue_p.h b/src/animation/backend/clipblendvalue_p.h
index 63dfe6ddc..168989a89 100644
--- a/src/animation/backend/clipblendvalue_p.h
+++ b/src/animation/backend/clipblendvalue_p.h
@@ -79,7 +79,8 @@ public:
double duration() const override;
void setClipFormat(Qt3DCore::QNodeId animatorId, const ClipFormat &formatIndices);
- ClipFormat clipFormat(Qt3DCore::QNodeId animatorId);
+ ClipFormat &clipFormat(Qt3DCore::QNodeId animatorId);
+ const ClipFormat &clipFormat(Qt3DCore::QNodeId animatorId) const;
protected:
ClipResults doBlend(const QVector<ClipResults> &blendData) const override;
diff --git a/src/animation/backend/evaluateblendclipanimatorjob.cpp b/src/animation/backend/evaluateblendclipanimatorjob.cpp
index 5382b9c92..d01cf3bdd 100644
--- a/src/animation/backend/evaluateblendclipanimatorjob.cpp
+++ b/src/animation/backend/evaluateblendclipanimatorjob.cpp
@@ -105,6 +105,7 @@ void EvaluateBlendClipAnimatorJob::run()
// Reformat the clip results into the layout used by this animator/blend tree
const ClipFormat format = valueNode->clipFormat(blendedClipAnimator->peerId());
ClipResults formattedClipResults = formatClipResults(rawClipResults, format.sourceClipIndices);
+ applyComponentDefaultValues(format.defaultComponentValues, formattedClipResults);
valueNode->setClipResults(blendedClipAnimator->peerId(), formattedClipResults);
}
diff --git a/src/core/nodes/qnode.cpp b/src/core/nodes/qnode.cpp
index 58e016cda..dbe3fd102 100644
--- a/src/core/nodes/qnode.cpp
+++ b/src/core/nodes/qnode.cpp
@@ -173,11 +173,21 @@ void QNodePrivate::_q_postConstructorInit()
{
Q_Q(QNode);
+ // If we've already done the work then bail out. This can happen if the
+ // user creates a QNode subclass with an explicit parent, then immediately
+ // sets the new QNode as a property on another node. In this case, the
+ // property setter will call this function directly, but as we can't
+ // un-schedule a deferred invocation, this function will be called again
+ // the next time the event loop spins. So, catch this case and abort.
+ if (m_hasBackendNode)
+ return;
+
// Check that the parent hasn't been unset since this call was enqueued
auto parentNode = q->parentNode();
if (!parentNode)
return;
+
if (m_scene)
m_scene->addObservable(q); // Sets the m_changeArbiter to that of the scene
@@ -369,7 +379,18 @@ void QNodePrivate::propertyChanged(int propertyIndex)
const QVariant data = property.read(q);
if (data.canConvert<QNode*>()) {
- const QNode * const node = data.value<QNode*>();
+ QNode *node = data.value<QNode*>();
+
+ // Ensure the node has issued a node creation change. We can end
+ // up here if a newly created node with a parent is immediately set
+ // as a property on another node. In this case the deferred call to
+ // _q_postConstructorInit() will not have happened yet as the event
+ // loop will still be blocked. So force it here and we catch this
+ // eventuality in the _q_postConstructorInit() function so that we
+ // do not repeat the creation and new child scene change events.
+ if (node)
+ QNodePrivate::get(node)->_q_postConstructorInit();
+
const QNodeId id = node ? node->id() : QNodeId();
notifyPropertyChange(property.name(), QVariant::fromValue(id));
} else {
diff --git a/src/plugins/sceneparsers/assimp/assimpimporter.cpp b/src/plugins/sceneparsers/assimp/assimpimporter.cpp
index 66446ced5..e630ce657 100644
--- a/src/plugins/sceneparsers/assimp/assimpimporter.cpp
+++ b/src/plugins/sceneparsers/assimp/assimpimporter.cpp
@@ -1290,6 +1290,7 @@ void AssimpImporter::copyMaterialTextures(QMaterial *material, aiMaterial *assim
QAbstractTexture *tex = QAbstractNodeFactory::createNode<QTexture2D>("QTexture2D");
QTextureImage *texImage = QAbstractNodeFactory::createNode<QTextureImage>("QTextureImage");
texImage->setSource(QUrl::fromLocalFile(fullPath));
+ texImage->setMirrored(false);
tex->addTextureImage(texImage);
// Set proper wrapping mode
diff --git a/src/quick3d/imports/scene3d/scene3drenderer.cpp b/src/quick3d/imports/scene3d/scene3drenderer.cpp
index 37ae8e48b..1c9fec4d2 100644
--- a/src/quick3d/imports/scene3d/scene3drenderer.cpp
+++ b/src/quick3d/imports/scene3d/scene3drenderer.cpp
@@ -135,6 +135,7 @@ Scene3DRenderer::Scene3DRenderer(Scene3DItem *item, Qt3DCore::QAspectEngine *asp
, m_multisample(false) // this value is not used, will be synced from the Scene3DItem instead
, m_lastMultisample(false)
, m_needsShutdown(true)
+ , m_blocking(false)
{
Q_CHECK_PTR(m_item);
Q_CHECK_PTR(m_item->window());
@@ -154,6 +155,9 @@ Scene3DRenderer::Scene3DRenderer(Scene3DItem *item, Qt3DCore::QAspectEngine *asp
ContextSaver saver;
static_cast<QRenderAspectPrivate*>(QRenderAspectPrivate::get(m_renderAspect))->renderInitialize(saver.context());
scheduleRootEntityChange();
+
+ const bool blockingRendermode = !qgetenv("SCENE3D_BLOCKING_RENDERMODE").isEmpty();
+ m_blocking = blockingRendermode;
}
Scene3DRenderer::~Scene3DRenderer()
@@ -307,7 +311,7 @@ void Scene3DRenderer::render()
m_finalFBO->bind();
// Render Qt3D Scene
- static_cast<QRenderAspectPrivate*>(QRenderAspectPrivate::get(m_renderAspect))->renderSynchronous();
+ static_cast<QRenderAspectPrivate*>(QRenderAspectPrivate::get(m_renderAspect))->renderSynchronous(m_blocking);
// We may have called doneCurrent() so restore the context if the rendering surface was changed
// Note: keep in mind that the ContextSave also restores the surface when destroyed
diff --git a/src/quick3d/imports/scene3d/scene3drenderer_p.h b/src/quick3d/imports/scene3d/scene3drenderer_p.h
index 7a85bc774..eb2b930ef 100644
--- a/src/quick3d/imports/scene3d/scene3drenderer_p.h
+++ b/src/quick3d/imports/scene3d/scene3drenderer_p.h
@@ -109,6 +109,7 @@ private:
bool m_multisample;
bool m_lastMultisample;
bool m_needsShutdown;
+ bool m_blocking;
friend class Scene3DCleaner;
};
diff --git a/src/render/backend/abstractrenderer_p.h b/src/render/backend/abstractrenderer_p.h
index 4d80ec87d..c934dbfae 100644
--- a/src/render/backend/abstractrenderer_p.h
+++ b/src/render/backend/abstractrenderer_p.h
@@ -133,7 +133,7 @@ public:
// Threaded renderer
virtual void render() = 0;
// Synchronous renderer
- virtual void doRender() = 0;
+ virtual void doRender(bool scene3dBlocking = false) = 0;
virtual void cleanGraphicsResources() = 0;
diff --git a/src/render/backend/renderer.cpp b/src/render/backend/renderer.cpp
index 0507cc0b0..535b10b85 100644
--- a/src/render/backend/renderer.cpp
+++ b/src/render/backend/renderer.cpp
@@ -556,7 +556,7 @@ void Renderer::render()
}
}
-void Renderer::doRender()
+void Renderer::doRender(bool scene3dBlocking)
{
Renderer::ViewSubmissionResultData submissionData;
bool hasCleanedQueueAndProceeded = false;
@@ -566,9 +566,22 @@ void Renderer::doRender()
// Lock the mutex to protect access to the renderQueue while we look for its state
QMutexLocker locker(m_renderQueue->mutex());
- const bool queueIsComplete = m_renderQueue->isFrameQueueComplete();
+ bool queueIsComplete = m_renderQueue->isFrameQueueComplete();
const bool queueIsEmpty = m_renderQueue->targetRenderViewCount() == 0;
+ // Scene3D Blocking Mode
+ if (scene3dBlocking && !queueIsComplete && !queueIsEmpty) {
+ int i = 0;
+ // We wait at most 10ms to avoid a case we could never recover from
+ while (!queueIsComplete && i++ < 10) {
+ QThread::msleep(1);
+ qCDebug(Backend) << Q_FUNC_INFO << "Waiting for ready queue (try:" << i << "/ 10)";
+ locker.unlock();
+ queueIsComplete = m_renderQueue->isFrameQueueComplete();
+ locker.relock();
+ }
+ }
+
// When using synchronous rendering (QtQuick)
// We are not sure that the frame queue is actually complete
// Since a call to render may not be synched with the completions
diff --git a/src/render/backend/renderer_p.h b/src/render/backend/renderer_p.h
index 81f76bd0c..c7ca49401 100644
--- a/src/render/backend/renderer_p.h
+++ b/src/render/backend/renderer_p.h
@@ -170,7 +170,7 @@ public:
void releaseGraphicsResources() override;
void render() override;
- void doRender() override;
+ void doRender(bool scene3dBlocking = false) override;
void cleanGraphicsResources() override;
bool isRunning() const override { return m_running.load(); }
diff --git a/src/render/frontend/qrenderaspect.cpp b/src/render/frontend/qrenderaspect.cpp
index ba9fd4451..53adf96a9 100644
--- a/src/render/frontend/qrenderaspect.cpp
+++ b/src/render/frontend/qrenderaspect.cpp
@@ -405,9 +405,9 @@ void QRenderAspectPrivate::renderInitialize(QOpenGLContext *context)
}
/*! \internal */
-void QRenderAspectPrivate::renderSynchronous()
+void QRenderAspectPrivate::renderSynchronous(bool blocking)
{
- m_renderer->doRender();
+ m_renderer->doRender(blocking);
}
/*!
diff --git a/src/render/frontend/qrenderaspect_p.h b/src/render/frontend/qrenderaspect_p.h
index b8c8538ee..26ca091f6 100644
--- a/src/render/frontend/qrenderaspect_p.h
+++ b/src/render/frontend/qrenderaspect_p.h
@@ -90,7 +90,7 @@ public:
void loadSceneParsers();
void loadRenderPlugin(const QString &pluginName);
void renderInitialize(QOpenGLContext *context);
- void renderSynchronous();
+ void renderSynchronous(bool blocking = false);
void renderShutdown();
void registerBackendType(const QMetaObject &, const Qt3DCore::QBackendNodeMapperPtr &functor);
QVector<Qt3DCore::QAspectJobPtr> createGeometryRendererJobs();
diff --git a/src/render/graphicshelpers/graphicscontext.cpp b/src/render/graphicshelpers/graphicscontext.cpp
index d33aca825..9e8e3d610 100644
--- a/src/render/graphicshelpers/graphicscontext.cpp
+++ b/src/render/graphicshelpers/graphicscontext.cpp
@@ -515,7 +515,8 @@ void GraphicsContext::loadShader(Shader *shader, ShaderManager *manager)
shaderProgram = createShaderProgram(shader);
// Store in cache
- m_shaderCache.insert(shader->dna(), shader->peerId(), shaderProgram);
+ if (shaderProgram)
+ m_shaderCache.insert(shader->dna(), shader->peerId(), shaderProgram);
}
// Ensure the Shader node knows about the program interface
diff --git a/tests/auto/animation/clipblendvalue/tst_clipblendvalue.cpp b/tests/auto/animation/clipblendvalue/tst_clipblendvalue.cpp
index cc9d22556..197378094 100644
--- a/tests/auto/animation/clipblendvalue/tst_clipblendvalue.cpp
+++ b/tests/auto/animation/clipblendvalue/tst_clipblendvalue.cpp
@@ -195,25 +195,6 @@ private Q_SLOTS:
<< blendNode << indexes << animatorIds << expectedClipFormat;
}
- // No data
- {
- auto blendNode = new ClipBlendValue;
- QVector<Qt3DCore::QNodeId> animatorIds;
- QVector<ClipFormat> expectedClipFormat;
-
- auto animatorId = Qt3DCore::QNodeId::createId();
- animatorIds.push_back(animatorId);
-
- ClipFormat clipFormat;
- expectedClipFormat.push_back(clipFormat);
-
- // Don't set any data
- QVector<int> indexes = QVector<int>() << 0;
-
- QTest::newRow("no entries")
- << blendNode << indexes << animatorIds << expectedClipFormat;
- }
-
// Multiple entries, ordered
{
auto blendNode = new ClipBlendValue;
diff --git a/tests/auto/core/nodes/tst_nodes.cpp b/tests/auto/core/nodes/tst_nodes.cpp
index 6dcd1bd13..0ec661fed 100644
--- a/tests/auto/core/nodes/tst_nodes.cpp
+++ b/tests/auto/core/nodes/tst_nodes.cpp
@@ -80,6 +80,7 @@ private slots:
void removingChildEntitiesFromNode();
void checkConstructionSetParentMix(); // QTBUG-60612
+ void checkConstructionWithParent();
void appendingComponentToEntity();
void appendingParentlessComponentToEntity();
@@ -169,13 +170,17 @@ void SimplePostman::notifyBackend(const Qt3DCore::QSceneChangePtr &change)
m_spy->sceneChangeEventWithLock(change);
}
+
+
class MyQNode : public Qt3DCore::QNode
{
Q_OBJECT
Q_PROPERTY(QString customProperty READ customProperty WRITE setCustomProperty NOTIFY customPropertyChanged)
+ Q_PROPERTY(MyQNode *nodeProperty READ nodeProperty WRITE setNodeProperty NOTIFY nodePropertyChanged)
public:
explicit MyQNode(Qt3DCore::QNode *parent = 0)
: QNode(parent)
+ , m_nodeProperty(nullptr)
{}
~MyQNode()
@@ -211,11 +216,37 @@ public:
Qt3DCore::QNodePrivate::get(this)->m_hasBackendNode = created;
}
+ MyQNode *nodeProperty() const { return m_nodeProperty; }
+
+public slots:
+ void setNodeProperty(MyQNode *node)
+ {
+ Qt3DCore::QNodePrivate *d = Qt3DCore::QNodePrivate::get(this);
+ if (m_nodeProperty == node)
+ return;
+
+ if (m_nodeProperty)
+ d->unregisterDestructionHelper(m_nodeProperty);
+
+ if (node && !node->parent())
+ node->setParent(this);
+
+ m_nodeProperty = node;
+
+ // Ensures proper bookkeeping
+ if (m_nodeProperty)
+ d->registerDestructionHelper(m_nodeProperty, &MyQNode::setNodeProperty, m_nodeProperty);
+
+ emit nodePropertyChanged(node);
+ }
+
signals:
void customPropertyChanged();
+ void nodePropertyChanged(MyQNode *node);
protected:
QString m_customProperty;
+ MyQNode *m_nodeProperty;
};
class MyQEntity : public Qt3DCore::QEntity
@@ -847,6 +878,49 @@ void tst_Nodes::checkConstructionSetParentMix()
QCOMPARE(lastEvent->addedNodeId(), subTreeRoot->id());
}
+void tst_Nodes::checkConstructionWithParent()
+{
+ // GIVEN
+ ObserverSpy spy;
+ Qt3DCore::QScene scene;
+ QScopedPointer<MyQNode> root(new MyQNode());
+
+ // WHEN
+ root->setArbiterAndScene(&spy, &scene);
+ root->setSimulateBackendCreated(true);
+
+ // THEN
+ QVERIFY(Qt3DCore::QNodePrivate::get(root.data())->scene() != nullptr);
+
+ // WHEN we create a child and then set it as a Node* property
+ auto *node = new MyQNode(root.data());
+ root->setNodeProperty(node);
+
+ // THEN we should get one creation change, one child added change
+ // and one property change event, in that order.
+ QCoreApplication::processEvents();
+ QCOMPARE(root->children().count(), 1);
+ QCOMPARE(spy.events.size(), 3); // 1 creation change, 1 child added change, 1 property change
+
+ // Ensure first event is child node's creation change
+ const auto creationEvent = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QNodeCreatedChangeBase>();
+ QVERIFY(!creationEvent.isNull());
+ QCOMPARE(creationEvent->subjectId(), node->id());
+
+ const auto newChildEvent = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QPropertyNodeAddedChange>();
+ QVERIFY(!newChildEvent.isNull());
+ QCOMPARE(newChildEvent->subjectId(), root->id());
+ QCOMPARE(newChildEvent->propertyName(), "children");
+ QCOMPARE(newChildEvent->addedNodeId(), node->id());
+
+ // Ensure second and last event is property set change
+ const auto propertyEvent = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QPropertyUpdatedChange>();
+ QVERIFY(!propertyEvent.isNull());
+ QCOMPARE(propertyEvent->subjectId(), root->id());
+ QCOMPARE(propertyEvent->propertyName(), "nodeProperty");
+ QCOMPARE(propertyEvent->value().value<Qt3DCore::QNodeId>(), node->id());
+}
+
void tst_Nodes::appendingParentlessComponentToEntity()
{
// GIVEN
diff --git a/tests/auto/render/commons/testrenderer.h b/tests/auto/render/commons/testrenderer.h
index b26a3ffad..327e08d78 100644
--- a/tests/auto/render/commons/testrenderer.h
+++ b/tests/auto/render/commons/testrenderer.h
@@ -52,7 +52,7 @@ public:
void shutdown() override {}
void releaseGraphicsResources() override {}
void render() override {}
- void doRender() override {}
+ void doRender(bool scene3dBlocking = false) override { Q_UNUSED(scene3dBlocking); }
void cleanGraphicsResources() override {}
bool isRunning() const override { return true; }
bool shouldRender() override { return true; }