summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLiang Qi <liang.qi@qt.io>2018-02-14 11:55:27 +0000
committerThe Qt Project <gerrit-noreply@qt-project.org>2018-02-14 11:56:26 +0000
commita400bbe50deb98f5d924cd4f6562e1b868e73248 (patch)
treea636e2224d3677aca2406bac5d835100610500e3
parentd360e16fdcab2197924e2505695c412bcaadbbe0 (diff)
parent1382b0cfb336cc04924d61b46f0b69dadb1c3d39 (diff)
Merge "Merge remote-tracking branch 'origin/5.10.1' into 5.11" into refs/staging/5.11v5.11.0-alpha1
-rw-r--r--dist/changes-5.10.132
-rw-r--r--src/animation/backend/animationutils_p.h3
-rw-r--r--src/core/changes/qpropertynodeaddedchange.cpp10
-rw-r--r--src/core/nodes/qnode.cpp17
-rw-r--r--src/core/nodes/qnode_p.h4
-rw-r--r--src/plugins/sceneparsers/assimp/assimpimporter.cpp1
-rw-r--r--src/quick3d/quick3dscene2d/items/scene2d.cpp13
-rw-r--r--src/render/backend/abstractrenderer_p.h3
-rw-r--r--src/render/backend/renderer.cpp279
-rw-r--r--src/render/backend/renderer_p.h13
-rw-r--r--src/render/jobs/filtercompatibletechniquejob.cpp13
-rw-r--r--src/render/jobs/loadscenejob.cpp103
-rw-r--r--src/render/jobs/loadscenejob_p.h8
-rw-r--r--src/render/materialsystem/technique.cpp26
-rw-r--r--src/render/renderlogging.cpp1
-rw-r--r--src/render/renderlogging_p.h1
-rw-r--r--tests/auto/core/nodes/tst_nodes.cpp83
-rw-r--r--tests/auto/render/filtercompatibletechniquejob/tst_filtercompatibletechniquejob.cpp29
-rw-r--r--tests/auto/render/renderer/tst_renderer.cpp28
-rw-r--r--tests/auto/render/technique/tst_technique.cpp16
20 files changed, 428 insertions, 255 deletions
diff --git a/dist/changes-5.10.1 b/dist/changes-5.10.1
new file mode 100644
index 000000000..62ea7ddd2
--- /dev/null
+++ b/dist/changes-5.10.1
@@ -0,0 +1,32 @@
+Qt 5.10.1 is a bug-fix release. It maintains both forward and backward
+compatibility (source and binary) with Qt 5.10.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.10 series is binary compatible with the 5.9.x series.
+Applications compiled for 5.9 will continue to run with 5.10.
+
+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.
+
+This release contains all fixes included in the Qt 5.9.4 release.
+
+****************************************************************************
+* Qt 5.10.1 Changes *
+****************************************************************************
+
+UNSPECIFIED
+-----------
+
+ - [QTBUG-65123] (Q)BlitFramebuffer has been corrected to treat the source
+ and destination rectangles to be in the usual Qt coordinate system with
+ Y
+ at top.
diff --git a/src/animation/backend/animationutils_p.h b/src/animation/backend/animationutils_p.h
index 01816bda2..42402c5ec 100644
--- a/src/animation/backend/animationutils_p.h
+++ b/src/animation/backend/animationutils_p.h
@@ -145,6 +145,7 @@ struct ChannelNameAndType
, jointIndex(-1)
, mappingId()
, jointTransformComponent(NoTransformComponent)
+ , pad(0)
{}
ChannelNameAndType(const QString &_name,
@@ -157,6 +158,7 @@ struct ChannelNameAndType
, jointIndex(_jointIndex)
, mappingId(_mappingId)
, jointTransformComponent(NoTransformComponent)
+ , pad(0)
{}
ChannelNameAndType(const QString &_name,
@@ -168,6 +170,7 @@ struct ChannelNameAndType
, jointIndex(invalidIndex)
, mappingId()
, jointTransformComponent(_jointTransformComponent)
+ , pad(0)
{}
bool operator==(const ChannelNameAndType &rhs) const
diff --git a/src/core/changes/qpropertynodeaddedchange.cpp b/src/core/changes/qpropertynodeaddedchange.cpp
index 390a170b6..9f8e50872 100644
--- a/src/core/changes/qpropertynodeaddedchange.cpp
+++ b/src/core/changes/qpropertynodeaddedchange.cpp
@@ -76,6 +76,16 @@ QPropertyNodeAddedChange::QPropertyNodeAddedChange(QNodeId subjectId, QNode *nod
{
Q_D(QPropertyNodeAddedChange);
d->m_addedNodeIdTypePair = QNodeIdTypePair(node->id(), QNodePrivate::findStaticMetaObject(node->metaObject()));
+
+ // 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();
}
/*! \internal */
diff --git a/src/core/nodes/qnode.cpp b/src/core/nodes/qnode.cpp
index dbe3fd102..e5a3aca71 100644
--- a/src/core/nodes/qnode.cpp
+++ b/src/core/nodes/qnode.cpp
@@ -74,6 +74,7 @@ QNodePrivate::QNodePrivate()
, m_blockNotifications(false)
, m_hasBackendNode(false)
, m_enabled(true)
+ , m_notifiedParent(false)
, m_defaultPropertyTrackMode(QNode::TrackFinalValues)
, m_propertyChangesSetup(false)
, m_signals(this)
@@ -210,13 +211,19 @@ void QNodePrivate::_q_addChild(QNode *childNode)
Q_ASSERT(childNode);
Q_ASSERT_X(childNode->parent() == q_func(), Q_FUNC_INFO, "not a child of this node");
+ // Have we already notified the parent about its new child? If so, bail out
+ // early so that we do not send more than one new child event to the backend
+ QNodePrivate *childD = QNodePrivate::get(childNode);
+ if (childD->m_notifiedParent == true)
+ return;
+
// Store our id as the parentId in the child so that even if the child gets
// removed from the scene as part of the destruction of the parent, when the
// parent's children are deleted in the QObject dtor, we still have access to
// the parentId. If we didn't store this, we wouldn't have access at that time
// because the parent would then only be a QObject, the QNode part would have
// been destroyed already.
- QNodePrivate::get(childNode)->m_parentId = m_id;
+ childD->m_parentId = m_id;
if (!m_scene)
return;
@@ -224,6 +231,11 @@ void QNodePrivate::_q_addChild(QNode *childNode)
// We need to send a QPropertyNodeAddedChange to the backend
// to notify the backend that we have a new child
if (m_changeArbiter != nullptr) {
+ // Flag that we have notified the parent. We do this immediately before
+ // creating the change because that recurses back into this function and
+ // we need to catch that to avoid sending more than one new child event
+ // to the backend.
+ childD->m_notifiedParent = true;
const auto change = QPropertyNodeAddedChangePtr::create(m_id, childNode);
change->setPropertyName("children");
notifyObservers(change);
@@ -299,6 +311,9 @@ void QNodePrivate::_q_setParentHelper(QNode *parent)
notifyDestructionChangesAndRemoveFromScene();
}
+ // Flag that we need to notify any new parent
+ m_notifiedParent = false;
+
// Basically QObject::setParent but for QObjectPrivate
QObjectPrivate::setParent_helper(parent);
QNode *newParentNode = q->parentNode();
diff --git a/src/core/nodes/qnode_p.h b/src/core/nodes/qnode_p.h
index a122993fe..c203b342f 100644
--- a/src/core/nodes/qnode_p.h
+++ b/src/core/nodes/qnode_p.h
@@ -100,6 +100,7 @@ public:
bool m_blockNotifications;
bool m_hasBackendNode;
bool m_enabled;
+ bool m_notifiedParent;
QNode::PropertyTrackingMode m_defaultPropertyTrackMode;
QHash<QString, QNode::PropertyTrackingMode> m_trackedPropertiesOverrides;
@@ -137,10 +138,11 @@ public:
static const QMetaObject *findStaticMetaObject(const QMetaObject *metaObject);
+ void _q_postConstructorInit();
+
private:
void notifyCreationChange();
void notifyDestructionChangesAndRemoveFromScene();
- void _q_postConstructorInit();
void _q_addChild(QNode *childNode);
void _q_removeChild(QNode *childNode);
void _q_setParentHelper(QNode *parent);
diff --git a/src/plugins/sceneparsers/assimp/assimpimporter.cpp b/src/plugins/sceneparsers/assimp/assimpimporter.cpp
index e630ce657..219a75ce4 100644
--- a/src/plugins/sceneparsers/assimp/assimpimporter.cpp
+++ b/src/plugins/sceneparsers/assimp/assimpimporter.cpp
@@ -616,6 +616,7 @@ void AssimpImporter::readSceneFile(const QString &path)
*/
void AssimpImporter::readSceneData(const QByteArray& data, const QString &basePath)
{
+ Q_UNUSED(basePath);
cleanup();
m_scene = new SceneImporter();
diff --git a/src/quick3d/quick3dscene2d/items/scene2d.cpp b/src/quick3d/quick3dscene2d/items/scene2d.cpp
index 4abc7cf42..1147abf68 100644
--- a/src/quick3d/quick3dscene2d/items/scene2d.cpp
+++ b/src/quick3d/quick3dscene2d/items/scene2d.cpp
@@ -123,7 +123,7 @@ Scene2D::Scene2D()
, m_mouseEnabled(true)
, m_renderPolicy(Qt3DRender::Quick::QScene2D::Continuous)
{
- renderThreadClientCount->fetchAndAddAcquire(1);
+
}
Scene2D::~Scene2D()
@@ -146,6 +146,8 @@ void Scene2D::initializeSharedObject()
return;
}
+ renderThreadClientCount->fetchAndAddAcquire(1);
+
renderThread->setObjectName(QStringLiteral("Scene2D::renderThread"));
m_renderThread = renderThread;
m_sharedObject->m_renderThread = m_renderThread;
@@ -413,10 +415,11 @@ void Scene2D::cleanup()
m_sharedObject->wake();
m_sharedObject = nullptr;
}
-
- renderThreadClientCount->fetchAndSubAcquire(1);
- if (renderThreadClientCount->load() == 0)
- renderThread->quit();
+ if (m_renderThread) {
+ renderThreadClientCount->fetchAndSubAcquire(1);
+ if (renderThreadClientCount->load() == 0)
+ renderThread->quit();
+ }
}
diff --git a/src/render/backend/abstractrenderer_p.h b/src/render/backend/abstractrenderer_p.h
index a9d6f19a5..54f37ea21 100644
--- a/src/render/backend/abstractrenderer_p.h
+++ b/src/render/backend/abstractrenderer_p.h
@@ -108,6 +108,7 @@ public:
SkeletonDataDirty = 1 << 10,
JointDirty = 1 << 11,
LayersDirty = 1 << 12,
+ TechniquesDirty = 1 << 13,
AllDirty = 0xffffff
};
Q_DECLARE_FLAGS(BackendNodeDirtySet, BackendNodeDirtyFlag)
@@ -141,7 +142,9 @@ public:
virtual void markDirty(BackendNodeDirtySet changes, BackendNode *node) = 0;
virtual BackendNodeDirtySet dirtyBits() = 0;
+#if defined(QT_BUILD_INTERNAL)
virtual void clearDirtyBits(BackendNodeDirtySet changes) = 0;
+#endif
virtual bool shouldRender() = 0;
virtual void skipNextFrame() = 0;
diff --git a/src/render/backend/renderer.cpp b/src/render/backend/renderer.cpp
index 70daa9904..bb0585e6f 100644
--- a/src/render/backend/renderer.cpp
+++ b/src/render/backend/renderer.cpp
@@ -162,7 +162,6 @@ Renderer::Renderer(QRenderAspect::RenderType type)
, m_waitForInitializationToBeCompleted(0)
, m_pickEventFilter(new PickEventFilter())
, m_exposed(0)
- , m_changeSet(0)
, m_lastFrameCorrect(0)
, m_glContext(nullptr)
, m_shareContext(nullptr)
@@ -207,6 +206,7 @@ Renderer::Renderer(QRenderAspect::RenderType type)
m_updateWorldBoundingVolumeJob->addDependency(m_calculateBoundingVolumeJob);
m_expandBoundingVolumeJob->addDependency(m_updateWorldBoundingVolumeJob);
m_updateShaderDataTransformJob->addDependency(m_worldTransformJob);
+ m_pickBoundingVolumeJob->addDependency(m_expandBoundingVolumeJob);
// Dirty texture gathering depends on m_syncTextureLoadingJob
// m_syncTextureLoadingJob will depend on the texture loading jobs
@@ -220,6 +220,8 @@ Renderer::Renderer(QRenderAspect::RenderType type)
m_pickBoundingVolumeJob->addDependency(m_updateMeshTriangleListJob);
m_rayCastingJob->addDependency(m_updateMeshTriangleListJob);
+ m_shaderGathererJob->addDependency(m_filterCompatibleTechniqueJob);
+
m_filterCompatibleTechniqueJob->setRenderer(this);
m_defaultRenderStateSet = new RenderStateSet;
@@ -308,7 +310,11 @@ NodeManagers *Renderer::nodeManagers() const
*/
QOpenGLContext *Renderer::shareContext() const
{
- return m_shareContext ? m_shareContext : m_graphicsContext->openGLContext()->shareContext();
+ QMutexLocker lock(&m_shareContextMutex);
+ return m_shareContext ? m_shareContext
+ : (m_graphicsContext->openGLContext()
+ ? m_graphicsContext->openGLContext()->shareContext()
+ : nullptr);
}
void Renderer::setOpenGLContext(QOpenGLContext *context)
@@ -326,45 +332,48 @@ void Renderer::initialize()
QOpenGLContext* ctx = m_glContext;
- // If we are using our own context (not provided by QtQuick),
- // we need to create it
- if (!m_glContext) {
- ctx = new QOpenGLContext;
- ctx->setShareContext(qt_gl_global_share_context());
-
- // TO DO: Shouldn't we use the highest context available and trust
- // QOpenGLContext to fall back on the best lowest supported ?
- const QByteArray debugLoggingMode = qgetenv("QT3DRENDER_DEBUG_LOGGING");
+ {
+ QMutexLocker lock(&m_shareContextMutex);
+ // If we are using our own context (not provided by QtQuick),
+ // we need to create it
+ if (!m_glContext) {
+ ctx = new QOpenGLContext;
+ ctx->setShareContext(qt_gl_global_share_context());
+
+ // TO DO: Shouldn't we use the highest context available and trust
+ // QOpenGLContext to fall back on the best lowest supported ?
+ const QByteArray debugLoggingMode = qgetenv("QT3DRENDER_DEBUG_LOGGING");
+
+ if (!debugLoggingMode.isEmpty()) {
+ QSurfaceFormat sf = ctx->format();
+ sf.setOption(QSurfaceFormat::DebugContext);
+ ctx->setFormat(sf);
+ }
- if (!debugLoggingMode.isEmpty()) {
- QSurfaceFormat sf = ctx->format();
- sf.setOption(QSurfaceFormat::DebugContext);
- ctx->setFormat(sf);
+ // Create OpenGL context
+ if (ctx->create())
+ qCDebug(Backend) << "OpenGL context created with actual format" << ctx->format();
+ else
+ qCWarning(Backend) << Q_FUNC_INFO << "OpenGL context creation failed";
+ m_ownedContext = true;
+ } else {
+ // Context is not owned by us, so we need to know if it gets destroyed
+ m_contextConnection = QObject::connect(m_glContext, &QOpenGLContext::aboutToBeDestroyed,
+ [this] { releaseGraphicsResources(); });
}
- // Create OpenGL context
- if (ctx->create())
- qCDebug(Backend) << "OpenGL context created with actual format" << ctx->format();
- else
- qCWarning(Backend) << Q_FUNC_INFO << "OpenGL context creation failed";
- m_ownedContext = true;
- } else {
- // Context is not owned by us, so we need to know if it gets destroyed
- m_contextConnection = QObject::connect(m_glContext, &QOpenGLContext::aboutToBeDestroyed,
- [this] { releaseGraphicsResources(); });
- }
+ if (!ctx->shareContext()) {
+ m_shareContext = new QOpenGLContext;
+ m_shareContext->setFormat(ctx->format());
+ m_shareContext->setShareContext(ctx);
+ m_shareContext->create();
+ }
- if (!ctx->shareContext()) {
- m_shareContext = new QOpenGLContext;
- m_shareContext->setFormat(ctx->format());
- m_shareContext->setShareContext(ctx);
- m_shareContext->create();
+ // Note: we don't have a surface at this point
+ // The context will be made current later on (at render time)
+ m_graphicsContext->setOpenGLContext(ctx);
}
- // Note: we don't have a surface at this point
- // The context will be made current later on (at render time)
- m_graphicsContext->setOpenGLContext(ctx);
-
// Store the format used by the context and queue up creating an
// offscreen surface in the main thread so that it is available
// for use when we want to shutdown the renderer. We need to create
@@ -518,7 +527,7 @@ void Renderer::setSceneRoot(QBackendNodeFactory *factory, Entity *sgRoot)
m_updateTreeEnabledJob->setRoot(m_renderSceneRoot);
// Set all flags to dirty
- m_changeSet |= AbstractRenderer::AllDirty;
+ m_dirtyBits.marked |= AbstractRenderer::AllDirty;
}
void Renderer::registerEventFilter(QEventFilterService *service)
@@ -1016,72 +1025,71 @@ void Renderer::lookForDirtyTextures()
// Executed in a job
void Renderer::lookForDirtyShaders()
{
- if (isRunning()) {
- const QVector<HTechnique> activeTechniques = m_nodesManager->techniqueManager()->activeHandles();
- const QVector<HShaderBuilder> activeBuilders = m_nodesManager->shaderBuilderManager()->activeHandles();
- for (const HTechnique &techniqueHandle : activeTechniques) {
- Technique *technique = m_nodesManager->techniqueManager()->data(techniqueHandle);
- // If api of the renderer matches the one from the technique
- if (technique->isCompatibleWithRenderer()) {
- const auto passIds = technique->renderPasses();
- for (const QNodeId passId : passIds) {
- RenderPass *renderPass = m_nodesManager->renderPassManager()->lookupResource(passId);
- HShader shaderHandle = m_nodesManager->shaderManager()->lookupHandle(renderPass->shaderProgram());
- Shader *shader = m_nodesManager->shaderManager()->data(shaderHandle);
-
- ShaderBuilder *shaderBuilder = nullptr;
- for (const HShaderBuilder &builderHandle : activeBuilders) {
- ShaderBuilder *builder = m_nodesManager->shaderBuilderManager()->data(builderHandle);
- if (builder->shaderProgramId() == shader->peerId()) {
- shaderBuilder = builder;
- break;
- }
+ Q_ASSERT(isRunning());
+ const QVector<HTechnique> activeTechniques = m_nodesManager->techniqueManager()->activeHandles();
+ const QVector<HShaderBuilder> activeBuilders = m_nodesManager->shaderBuilderManager()->activeHandles();
+ for (const HTechnique &techniqueHandle : activeTechniques) {
+ Technique *technique = m_nodesManager->techniqueManager()->data(techniqueHandle);
+ // If api of the renderer matches the one from the technique
+ if (technique->isCompatibleWithRenderer()) {
+ const auto passIds = technique->renderPasses();
+ for (const QNodeId passId : passIds) {
+ RenderPass *renderPass = m_nodesManager->renderPassManager()->lookupResource(passId);
+ HShader shaderHandle = m_nodesManager->shaderManager()->lookupHandle(renderPass->shaderProgram());
+ Shader *shader = m_nodesManager->shaderManager()->data(shaderHandle);
+
+ ShaderBuilder *shaderBuilder = nullptr;
+ for (const HShaderBuilder &builderHandle : activeBuilders) {
+ ShaderBuilder *builder = m_nodesManager->shaderBuilderManager()->data(builderHandle);
+ if (builder->shaderProgramId() == shader->peerId()) {
+ shaderBuilder = builder;
+ break;
}
+ }
+
+ if (shaderBuilder) {
+ shaderBuilder->setGraphicsApi(*technique->graphicsApiFilter());
+
+ for (int i = 0; i <= ShaderBuilder::Compute; i++) {
+ const auto builderType = static_cast<ShaderBuilder::ShaderType>(i);
+ if (!shaderBuilder->shaderGraph(builderType).isValid())
+ continue;
- if (shaderBuilder) {
- shaderBuilder->setGraphicsApi(*technique->graphicsApiFilter());
-
- for (int i = 0; i <= ShaderBuilder::Compute; i++) {
- const auto builderType = static_cast<ShaderBuilder::ShaderType>(i);
- if (!shaderBuilder->shaderGraph(builderType).isValid())
- continue;
-
- if (shaderBuilder->isShaderCodeDirty(builderType)) {
- shaderBuilder->generateCode(builderType);
- }
-
- QShaderProgram::ShaderType shaderType = QShaderProgram::Vertex;
- switch (builderType) {
- case ShaderBuilder::Vertex:
- shaderType = QShaderProgram::Vertex;
- break;
- case ShaderBuilder::TessellationControl:
- shaderType = QShaderProgram::TessellationControl;
- break;
- case ShaderBuilder::TessellationEvaluation:
- shaderType = QShaderProgram::TessellationEvaluation;
- break;
- case ShaderBuilder::Geometry:
- shaderType = QShaderProgram::Geometry;
- break;
- case ShaderBuilder::Fragment:
- shaderType = QShaderProgram::Fragment;
- break;
- case ShaderBuilder::Compute:
- shaderType = QShaderProgram::Compute;
- break;
- }
-
- const auto code = shaderBuilder->shaderCode(builderType);
- shader->setShaderCode(shaderType, code);
+ if (shaderBuilder->isShaderCodeDirty(builderType)) {
+ shaderBuilder->generateCode(builderType);
+ }
+
+ QShaderProgram::ShaderType shaderType = QShaderProgram::Vertex;
+ switch (builderType) {
+ case ShaderBuilder::Vertex:
+ shaderType = QShaderProgram::Vertex;
+ break;
+ case ShaderBuilder::TessellationControl:
+ shaderType = QShaderProgram::TessellationControl;
+ break;
+ case ShaderBuilder::TessellationEvaluation:
+ shaderType = QShaderProgram::TessellationEvaluation;
+ break;
+ case ShaderBuilder::Geometry:
+ shaderType = QShaderProgram::Geometry;
+ break;
+ case ShaderBuilder::Fragment:
+ shaderType = QShaderProgram::Fragment;
+ break;
+ case ShaderBuilder::Compute:
+ shaderType = QShaderProgram::Compute;
+ break;
}
- }
- if (Q_UNLIKELY(shader->hasPendingNotifications()))
- shader->submitPendingNotifications();
- if (shader != nullptr && !shader->isLoaded())
- m_dirtyShaders.push_back(shaderHandle);
+ const auto code = shaderBuilder->shaderCode(builderType);
+ shader->setShaderCode(shaderType, code);
+ }
}
+
+ if (Q_UNLIKELY(shader->hasPendingNotifications()))
+ shader->submitPendingNotifications();
+ if (shader != nullptr && !shader->isLoaded())
+ m_dirtyShaders.push_back(shaderHandle);
}
}
}
@@ -1449,25 +1457,29 @@ Renderer::ViewSubmissionResultData Renderer::submitRenderViews(const QVector<Ren
void Renderer::markDirty(BackendNodeDirtySet changes, BackendNode *node)
{
Q_UNUSED(node);
- m_changeSet |= changes;
+ m_dirtyBits.marked |= changes;
}
Renderer::BackendNodeDirtySet Renderer::dirtyBits()
{
- return m_changeSet;
+ return m_dirtyBits.marked;
}
+#if defined(QT_BUILD_INTERNAL)
void Renderer::clearDirtyBits(BackendNodeDirtySet changes)
{
- m_changeSet &= ~changes;
+ m_dirtyBits.remaining &= ~changes;
+ m_dirtyBits.marked &= ~changes;
}
+#endif
bool Renderer::shouldRender()
{
// Only render if something changed during the last frame, or the last frame
// was not rendered successfully (or render-on-demand is disabled)
return (m_settings->renderPolicy() == QRenderSettings::Always
- || m_changeSet != 0
+ || m_dirtyBits.marked != 0
+ || m_dirtyBits.remaining != 0
|| !m_lastFrameCorrect.load());
}
@@ -1501,28 +1513,31 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs()
m_updateLevelOfDetailJob->setFrameGraphRoot(frameGraphRoot());
- BackendNodeDirtySet changesToUnset = dirtyBits();
+ const BackendNodeDirtySet dirtyBitsForFrame = m_dirtyBits.marked | m_dirtyBits.remaining;
+ m_dirtyBits.marked = 0;
+ m_dirtyBits.remaining = 0;
+ BackendNodeDirtySet notCleared = 0;
// Add jobs
- const bool entitiesEnabledDirty = changesToUnset & AbstractRenderer::EntityEnabledDirty;
+ const bool entitiesEnabledDirty = dirtyBitsForFrame & AbstractRenderer::EntityEnabledDirty;
if (entitiesEnabledDirty) {
renderBinJobs.push_back(m_updateTreeEnabledJob);
m_calculateBoundingVolumeJob->addDependency(m_updateTreeEnabledJob);
}
- if (changesToUnset & AbstractRenderer::TransformDirty) {
+ if (dirtyBitsForFrame & AbstractRenderer::TransformDirty) {
renderBinJobs.push_back(m_worldTransformJob);
renderBinJobs.push_back(m_updateWorldBoundingVolumeJob);
renderBinJobs.push_back(m_updateShaderDataTransformJob);
}
- if (changesToUnset & AbstractRenderer::GeometryDirty) {
+ if (dirtyBitsForFrame & AbstractRenderer::GeometryDirty) {
renderBinJobs.push_back(m_calculateBoundingVolumeJob);
renderBinJobs.push_back(m_updateMeshTriangleListJob);
}
- if (changesToUnset & AbstractRenderer::GeometryDirty ||
- changesToUnset & AbstractRenderer::TransformDirty) {
+ if (dirtyBitsForFrame & AbstractRenderer::GeometryDirty ||
+ dirtyBitsForFrame & AbstractRenderer::TransformDirty) {
renderBinJobs.push_back(m_expandBoundingVolumeJob);
}
@@ -1532,19 +1547,15 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs()
renderBinJobs.push_back(m_cleanupJob);
renderBinJobs.push_back(m_sendRenderCaptureJob);
renderBinJobs.push_back(m_sendBufferCaptureJob);
- renderBinJobs.push_back(m_filterCompatibleTechniqueJob);
renderBinJobs.append(bufferJobs);
// Jobs to prepare GL Resource upload
renderBinJobs.push_back(m_vaoGathererJob);
- if (changesToUnset & AbstractRenderer::BuffersDirty)
+ if (dirtyBitsForFrame & AbstractRenderer::BuffersDirty)
renderBinJobs.push_back(m_bufferGathererJob);
- if (changesToUnset & AbstractRenderer::ShadersDirty)
- renderBinJobs.push_back(m_shaderGathererJob);
-
- if (changesToUnset & AbstractRenderer::TexturesDirty) {
+ if (dirtyBitsForFrame & AbstractRenderer::TexturesDirty) {
renderBinJobs.push_back(m_syncTextureLoadingJob);
renderBinJobs.push_back(m_textureGathererJob);
}
@@ -1552,12 +1563,10 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs()
// Layer cache is dependent on layers, layer filters and the enabled flag
// on entities
- const bool layersDirty = changesToUnset & AbstractRenderer::LayersDirty;
+ const bool layersDirty = dirtyBitsForFrame & AbstractRenderer::LayersDirty;
const bool layersCacheNeedsToBeRebuilt = layersDirty || entitiesEnabledDirty;
- bool layersCacheRebuilt = false;
- const bool materialDirty = changesToUnset & AbstractRenderer::MaterialDirty;
- bool materialGathererCacheRebuilt = false;
+ const bool materialDirty = dirtyBitsForFrame & AbstractRenderer::MaterialDirty;
QMutexLocker lock(m_renderQueue->mutex());
if (m_renderQueue->wasReset()) { // Have we rendered yet? (Scene3D case)
@@ -1584,30 +1593,28 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs()
builder.prepareJobs();
renderBinJobs.append(builder.buildJobHierachy());
}
- layersCacheRebuilt = true;
- materialGathererCacheRebuilt = true;
// Set target number of RenderViews
m_renderQueue->setTargetRenderViewCount(fgBranchCount);
+ } else {
+ // FilterLayerEntityJob is part of the RenderViewBuilder jobs and must be run later
+ // if none of those jobs are started this frame
+ notCleared |= AbstractRenderer::EntityEnabledDirty;
+ notCleared |= AbstractRenderer::LayersDirty;
}
- // Only reset dirty flags once we have really rebuilt the caches
- if (layersDirty && !layersCacheRebuilt)
- changesToUnset.setFlag(AbstractRenderer::LayersDirty, false);
- if (entitiesEnabledDirty && !layersCacheRebuilt)
- changesToUnset.setFlag(AbstractRenderer::EntityEnabledDirty, false);
- if (materialDirty && !materialGathererCacheRebuilt)
- changesToUnset.setFlag(AbstractRenderer::MaterialDirty, false);
-
- // Clear dirty bits
- // TO DO: When secondary GL thread is integrated, the following line can be removed
- changesToUnset.setFlag(AbstractRenderer::ShadersDirty, false);
+ if (isRunning() && m_graphicsContext->isInitialized()) {
+ if (dirtyBitsForFrame & AbstractRenderer::TechniquesDirty )
+ renderBinJobs.push_back(m_filterCompatibleTechniqueJob);
+ if (dirtyBitsForFrame & AbstractRenderer::ShadersDirty)
+ renderBinJobs.push_back(m_shaderGathererJob);
+ } else {
+ notCleared |= AbstractRenderer::TechniquesDirty;
+ notCleared |= AbstractRenderer::ShadersDirty;
+ notCleared |= AbstractRenderer::MaterialDirty;
+ }
- // Clear all dirty flags but Compute so that
- // we still render every frame when a compute shader is used in a scene
- if (changesToUnset.testFlag(Renderer::ComputeDirty))
- changesToUnset.setFlag(Renderer::ComputeDirty, false);
- clearDirtyBits(changesToUnset);
+ m_dirtyBits.remaining = dirtyBitsForFrame & notCleared;
return renderBinJobs;
}
@@ -1745,7 +1752,7 @@ void Renderer::performCompute(const RenderView *, RenderCommand *command)
command->m_workGroups[2]);
}
// HACK: Reset the compute flag to dirty
- m_changeSet |= AbstractRenderer::ComputeDirty;
+ m_dirtyBits.marked |= AbstractRenderer::ComputeDirty;
#if defined(QT3D_RENDER_ASPECT_OPENGL_DEBUG)
int err = m_graphicsContext->openGLContext()->functions()->glGetError();
diff --git a/src/render/backend/renderer_p.h b/src/render/backend/renderer_p.h
index f996ebfe1..521d17e80 100644
--- a/src/render/backend/renderer_p.h
+++ b/src/render/backend/renderer_p.h
@@ -183,8 +183,10 @@ public:
void markDirty(BackendNodeDirtySet changes, BackendNode *node) override;
BackendNodeDirtySet dirtyBits() override;
- void clearDirtyBits(BackendNodeDirtySet changes) override;
+#if defined(QT_BUILD_INTERNAL)
+ void clearDirtyBits(BackendNodeDirtySet changes) override;
+#endif
bool shouldRender() override;
void skipNextFrame() override;
@@ -309,10 +311,17 @@ private:
QVector<Attribute *> m_dirtyAttributes;
QVector<Geometry *> m_dirtyGeometry;
QAtomicInt m_exposed;
- BackendNodeDirtySet m_changeSet;
+
+ struct DirtyBits {
+ BackendNodeDirtySet marked = 0; // marked dirty since last job build
+ BackendNodeDirtySet remaining = 0; // remaining dirty after jobs have finished
+ };
+ DirtyBits m_dirtyBits;
+
QAtomicInt m_lastFrameCorrect;
QOpenGLContext *m_glContext;
QOpenGLContext *m_shareContext;
+ mutable QMutex m_shareContextMutex;
PickBoundingVolumeJobPtr m_pickBoundingVolumeJob;
RayCastingJobPtr m_rayCastingJob;
diff --git a/src/render/jobs/filtercompatibletechniquejob.cpp b/src/render/jobs/filtercompatibletechniquejob.cpp
index 362e4088f..080ccd306 100644
--- a/src/render/jobs/filtercompatibletechniquejob.cpp
+++ b/src/render/jobs/filtercompatibletechniquejob.cpp
@@ -79,14 +79,13 @@ Renderer *FilterCompatibleTechniqueJob::renderer() const
void FilterCompatibleTechniqueJob::run()
{
Q_ASSERT(m_manager != nullptr && m_renderer != nullptr);
+ Q_ASSERT(m_renderer->isRunning() && m_renderer->graphicsContext()->isInitialized());
- if (m_renderer->isRunning() && m_renderer->graphicsContext()->isInitialized()) {
- const QVector<Qt3DCore::QNodeId> dirtyTechniqueIds = m_manager->takeDirtyTechniques();
- for (const Qt3DCore::QNodeId techniqueId : dirtyTechniqueIds) {
- Technique *technique = m_manager->lookupResource(techniqueId);
- if (Q_LIKELY(technique != nullptr))
- technique->setCompatibleWithRenderer((*m_renderer->contextInfo() == *technique->graphicsApiFilter()));
- }
+ const QVector<Qt3DCore::QNodeId> dirtyTechniqueIds = m_manager->takeDirtyTechniques();
+ for (const Qt3DCore::QNodeId techniqueId : dirtyTechniqueIds) {
+ Technique *technique = m_manager->lookupResource(techniqueId);
+ if (Q_LIKELY(technique != nullptr))
+ technique->setCompatibleWithRenderer((*m_renderer->contextInfo() == *technique->graphicsApiFilter()));
}
}
diff --git a/src/render/jobs/loadscenejob.cpp b/src/render/jobs/loadscenejob.cpp
index 5be733a4d..f767fe720 100644
--- a/src/render/jobs/loadscenejob.cpp
+++ b/src/render/jobs/loadscenejob.cpp
@@ -46,7 +46,7 @@
#include <Qt3DRender/private/qsceneimporter_p.h>
#include <Qt3DRender/private/qurlhelper_p.h>
#include <Qt3DRender/qsceneloader.h>
-
+#include <Qt3DRender/private/renderlogging_p.h>
#include <QFileInfo>
#include <QMimeDatabase>
@@ -93,7 +93,6 @@ void LoadSceneJob::run()
{
// Iterate scene IO handlers until we find one that can handle this file type
Qt3DCore::QEntity *sceneSubTree = nullptr;
-
Scene *scene = m_managers->sceneManager()->lookupResource(m_sceneComponent);
Q_ASSERT(scene);
@@ -107,52 +106,37 @@ void LoadSceneJob::run()
if (m_data.isEmpty()) {
const QString path = QUrlHelper::urlToLocalFileOrQrc(m_source);
- QFileInfo finfo(path);
+ const QFileInfo finfo(path);
+ qCDebug(SceneLoaders) << Q_FUNC_INFO << "Attempting to load" << finfo.filePath();
if (finfo.exists()) {
- QStringList extensions(finfo.suffix());
-
- for (QSceneImporter *sceneImporter : qAsConst(m_sceneImporters)) {
- if (!sceneImporter->areFileTypesSupported(extensions))
- continue;
-
- // If the file type is supported -> enter Loading status
- scene->setStatus(QSceneLoader::Loading);
-
- // File type is supported, try to load it
- sceneImporter->setSource(m_source);
- sceneSubTree = sceneImporter->scene();
- if (sceneSubTree != nullptr) {
- // Successfully built a subtree
- finalStatus = QSceneLoader::Ready;
- break;
- }
- }
+ const QStringList extensions(finfo.suffix());
+ sceneSubTree = tryLoadScene(scene,
+ finalStatus,
+ extensions,
+ [this] (QSceneImporter *importer) {
+ importer->setSource(m_source);
+ });
+ } else {
+ qCWarning(SceneLoaders) << Q_FUNC_INFO << finfo.filePath() << "doesn't exist";
}
} else {
QStringList extensions;
QMimeDatabase db;
- QMimeType mtype = db.mimeTypeForData(m_data);
- if (mtype.isValid()) {
+ const QMimeType mtype = db.mimeTypeForData(m_data);
+
+ if (mtype.isValid())
extensions = mtype.suffixes();
- }
+ else
+ qCWarning(SceneLoaders) << Q_FUNC_INFO << "Invalid mime type" << mtype;
- QString basePath = m_source.adjusted(QUrl::RemoveFilename).toString();
- for (QSceneImporter *sceneImporter : qAsConst(m_sceneImporters)) {
- if (!sceneImporter->areFileTypesSupported(extensions))
- continue;
-
- // If the file type is supported -> enter Loading status
- scene->setStatus(QSceneLoader::Loading);
-
- // File type is supported, try to load it
- sceneImporter->setData(m_data, basePath);
- sceneSubTree = sceneImporter->scene();
- if (sceneSubTree != nullptr) {
- // Successfully built a subtree
- finalStatus = QSceneLoader::Ready;
- break;
- }
- }
+ const QString basePath = m_source.adjusted(QUrl::RemoveFilename).toString();
+
+ sceneSubTree = tryLoadScene(scene,
+ finalStatus,
+ extensions,
+ [this, basePath] (QSceneImporter *importer) {
+ importer->setData(m_data, basePath);
+ });
}
}
@@ -167,6 +151,43 @@ void LoadSceneJob::run()
scene->setStatus(finalStatus);
}
+Qt3DCore::QEntity *LoadSceneJob::tryLoadScene(Scene *scene,
+ QSceneLoader::Status &finalStatus,
+ const QStringList &extensions,
+ const std::function<void (QSceneImporter *)> &importerSetupFunc)
+{
+ Qt3DCore::QEntity *sceneSubTree = nullptr;
+ bool foundSuitableLoggerPlugin = false;
+
+ for (QSceneImporter *sceneImporter : qAsConst(m_sceneImporters)) {
+ if (!sceneImporter->areFileTypesSupported(extensions))
+ continue;
+
+ foundSuitableLoggerPlugin = true;
+
+ // If the file type is supported -> enter Loading status
+ scene->setStatus(QSceneLoader::Loading);
+
+ // Set source file or data on importer
+ importerSetupFunc(sceneImporter);
+
+ // File type is supported, try to load it
+ sceneSubTree = sceneImporter->scene();
+ if (sceneSubTree != nullptr) {
+ // Successfully built a subtree
+ finalStatus = QSceneLoader::Ready;
+ break;
+ }
+
+ qCWarning(SceneLoaders) << Q_FUNC_INFO << "Failed to import" << m_source << "with errors" << sceneImporter->errors();
+ }
+
+ if (!foundSuitableLoggerPlugin)
+ qCWarning(SceneLoaders) << Q_FUNC_INFO << "Found not suitable importer plugin for" << m_source;
+
+ return sceneSubTree;
+}
+
} // namespace Render
} // namespace Qt3DRender
diff --git a/src/render/jobs/loadscenejob_p.h b/src/render/jobs/loadscenejob_p.h
index b33637985..0c77dc6e8 100644
--- a/src/render/jobs/loadscenejob_p.h
+++ b/src/render/jobs/loadscenejob_p.h
@@ -53,8 +53,10 @@
#include <Qt3DCore/qaspectjob.h>
#include <Qt3DCore/qnodeid.h>
+#include <Qt3DRender/qsceneloader.h>
#include <QSharedPointer>
#include <QUrl>
+#include <functional>
QT_BEGIN_NAMESPACE
@@ -64,6 +66,7 @@ class QSceneImporter;
namespace Render {
+class Scene;
class NodeManagers;
class Q_AUTOTEST_EXPORT LoadSceneJob : public Qt3DCore::QAspectJob
@@ -87,6 +90,11 @@ private:
Qt3DCore::QNodeId m_sceneComponent;
NodeManagers *m_managers;
QList<QSceneImporter *> m_sceneImporters;
+
+ Qt3DCore::QEntity *tryLoadScene(Scene *scene,
+ QSceneLoader::Status &finalStatus,
+ const QStringList &extensions,
+ const std::function<void (QSceneImporter *)> &importerSetupFunc);
};
typedef QSharedPointer<LoadSceneJob> LoadSceneJobPtr;
diff --git a/src/render/materialsystem/technique.cpp b/src/render/materialsystem/technique.cpp
index 4fd1555e1..5438fa9c8 100644
--- a/src/render/materialsystem/technique.cpp
+++ b/src/render/materialsystem/technique.cpp
@@ -102,43 +102,53 @@ void Technique::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
switch (e->type()) {
case PropertyUpdated: {
const auto change = qSharedPointerCast<QPropertyUpdatedChange>(e);
- if (change->propertyName() == QByteArrayLiteral("graphicsApiFilterData")) {
+ if (change->propertyName() == QByteArrayLiteral("enabled")) {
+ markDirty(AbstractRenderer::TechniquesDirty);
+ } else if (change->propertyName() == QByteArrayLiteral("graphicsApiFilterData")) {
GraphicsApiFilterData filterData = change->value().value<GraphicsApiFilterData>();
m_graphicsApiFilterData = filterData;
// Notify the manager that our graphicsApiFilterData has changed
// and that we therefore need to be check for compatibility again
m_isCompatibleWithRenderer = false;
m_nodeManager->techniqueManager()->addDirtyTechnique(peerId());
+ markDirty(AbstractRenderer::TechniquesDirty);
}
break;
}
case PropertyValueAdded: {
const auto change = qSharedPointerCast<QPropertyNodeAddedChange>(e);
- if (change->propertyName() == QByteArrayLiteral("pass"))
+ if (change->propertyName() == QByteArrayLiteral("pass")) {
appendRenderPass(change->addedNodeId());
- else if (change->propertyName() == QByteArrayLiteral("parameter"))
+ markDirty(AbstractRenderer::TechniquesDirty);
+ } else if (change->propertyName() == QByteArrayLiteral("parameter")) {
m_parameterPack.appendParameter(change->addedNodeId());
- else if (change->propertyName() == QByteArrayLiteral("filterKeys"))
+ markDirty(AbstractRenderer::TechniquesDirty);
+ } else if (change->propertyName() == QByteArrayLiteral("filterKeys")) {
appendFilterKey(change->addedNodeId());
+ markDirty(AbstractRenderer::TechniquesDirty);
+ }
break;
}
case PropertyValueRemoved: {
const auto change = qSharedPointerCast<QPropertyNodeRemovedChange>(e);
- if (change->propertyName() == QByteArrayLiteral("pass"))
+ if (change->propertyName() == QByteArrayLiteral("pass")) {
removeRenderPass(change->removedNodeId());
- else if (change->propertyName() == QByteArrayLiteral("parameter"))
+ markDirty(AbstractRenderer::TechniquesDirty);
+ } else if (change->propertyName() == QByteArrayLiteral("parameter")) {
m_parameterPack.removeParameter(change->removedNodeId());
- else if (change->propertyName() == QByteArrayLiteral("filterKeys"))
+ markDirty(AbstractRenderer::TechniquesDirty);
+ } else if (change->propertyName() == QByteArrayLiteral("filterKeys")) {
removeFilterKey(change->removedNodeId());
+ markDirty(AbstractRenderer::TechniquesDirty);
+ }
break;
}
default:
break;
}
- markDirty(AbstractRenderer::AllDirty);
BackendNode::sceneChangeEvent(e);
}
diff --git a/src/render/renderlogging.cpp b/src/render/renderlogging.cpp
index b9d423163..2eb1835e6 100644
--- a/src/render/renderlogging.cpp
+++ b/src/render/renderlogging.cpp
@@ -49,6 +49,7 @@ Q_LOGGING_CATEGORY(Backend, "Qt3D.Renderer.Backend", QtWarningMsg)
Q_LOGGING_CATEGORY(Frontend, "Qt3D.Renderer.Frontend", QtWarningMsg)
Q_LOGGING_CATEGORY(Io, "Qt3D.Renderer.IO", QtWarningMsg)
Q_LOGGING_CATEGORY(Jobs, "Qt3D.Renderer.Jobs", QtWarningMsg)
+Q_LOGGING_CATEGORY(SceneLoaders, "Qt3D.Renderer.SceneLoaders", QtWarningMsg)
Q_LOGGING_CATEGORY(Framegraph, "Qt3D.Renderer.Framegraph", QtWarningMsg)
Q_LOGGING_CATEGORY(RenderNodes, "Qt3D.Renderer.RenderNodes", QtWarningMsg)
Q_LOGGING_CATEGORY(Rendering, "Qt3D.Renderer.Rendering", QtWarningMsg)
diff --git a/src/render/renderlogging_p.h b/src/render/renderlogging_p.h
index dfa761e46..00ae572f4 100644
--- a/src/render/renderlogging_p.h
+++ b/src/render/renderlogging_p.h
@@ -63,6 +63,7 @@ Q_DECLARE_LOGGING_CATEGORY(Backend)
Q_DECLARE_LOGGING_CATEGORY(Frontend)
Q_DECLARE_LOGGING_CATEGORY(Io)
Q_DECLARE_LOGGING_CATEGORY(Jobs)
+Q_DECLARE_LOGGING_CATEGORY(SceneLoaders)
Q_DECLARE_LOGGING_CATEGORY(Framegraph)
Q_DECLARE_LOGGING_CATEGORY(RenderNodes)
Q_DECLARE_LOGGING_CATEGORY(Rendering)
diff --git a/tests/auto/core/nodes/tst_nodes.cpp b/tests/auto/core/nodes/tst_nodes.cpp
index 0ec661fed..2e73e6c34 100644
--- a/tests/auto/core/nodes/tst_nodes.cpp
+++ b/tests/auto/core/nodes/tst_nodes.cpp
@@ -81,6 +81,7 @@ private slots:
void checkConstructionSetParentMix(); // QTBUG-60612
void checkConstructionWithParent();
+ void checkConstructionAsListElement();
void appendingComponentToEntity();
void appendingParentlessComponentToEntity();
@@ -240,6 +241,43 @@ public slots:
emit nodePropertyChanged(node);
}
+ void addAttribute(MyQNode *attribute)
+ {
+ Qt3DCore::QNodePrivate *d = Qt3DCore::QNodePrivate::get(this);
+ if (!m_attributes.contains(attribute)) {
+ m_attributes.append(attribute);
+
+ // Ensures proper bookkeeping
+ d->registerDestructionHelper(attribute, &MyQNode::removeAttribute, 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
+ // 2) When the current node is destroyed, it gets destroyed as well
+ if (!attribute->parent())
+ attribute->setParent(this);
+
+ if (d->m_changeArbiter != nullptr) {
+ const auto change = Qt3DCore::QPropertyNodeAddedChangePtr::create(id(), attribute);
+ change->setPropertyName("attribute");
+ d->notifyObservers(change);
+ }
+ }
+ }
+
+ void removeAttribute(MyQNode *attribute)
+ {
+ Qt3DCore::QNodePrivate *d = Qt3DCore::QNodePrivate::get(this);
+ if (d->m_changeArbiter != nullptr) {
+ const auto change = Qt3DCore::QPropertyNodeRemovedChangePtr::create(id(), attribute);
+ change->setPropertyName("attribute");
+ d->notifyObservers(change);
+ }
+ m_attributes.removeOne(attribute);
+ // Remove bookkeeping connection
+ d->unregisterDestructionHelper(attribute);
+ }
+
signals:
void customPropertyChanged();
void nodePropertyChanged(MyQNode *node);
@@ -247,6 +285,7 @@ signals:
protected:
QString m_customProperty;
MyQNode *m_nodeProperty;
+ QVector<MyQNode *> m_attributes;
};
class MyQEntity : public Qt3DCore::QEntity
@@ -921,6 +960,50 @@ void tst_Nodes::checkConstructionWithParent()
QCOMPARE(propertyEvent->value().value<Qt3DCore::QNodeId>(), node->id());
}
+void tst_Nodes::checkConstructionAsListElement()
+{
+ // 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->addAttribute(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::QPropertyNodeAddedChange>();
+ QVERIFY(!propertyEvent.isNull());
+ QCOMPARE(propertyEvent->subjectId(), root->id());
+ QCOMPARE(propertyEvent->propertyName(), "attribute");
+ QCOMPARE(newChildEvent->addedNodeId(), node->id());
+}
+
void tst_Nodes::appendingParentlessComponentToEntity()
{
// GIVEN
diff --git a/tests/auto/render/filtercompatibletechniquejob/tst_filtercompatibletechniquejob.cpp b/tests/auto/render/filtercompatibletechniquejob/tst_filtercompatibletechniquejob.cpp
index 83e816861..4d4a08a34 100644
--- a/tests/auto/render/filtercompatibletechniquejob/tst_filtercompatibletechniquejob.cpp
+++ b/tests/auto/render/filtercompatibletechniquejob/tst_filtercompatibletechniquejob.cpp
@@ -192,35 +192,6 @@ private Q_SLOTS:
QCOMPARE(backendFilterCompatibleTechniqueJob.renderer(), &renderer);
}
- void checkRunRendererNotRunning()
- {
- // GIVEN
- Qt3DRender::Render::FilterCompatibleTechniqueJob backendFilterCompatibleTechniqueJob;
- Qt3DRender::TestAspect testAspect(buildTestScene());
-
- // WHEN
- Qt3DRender::Render::NodeManagers *nodeManagers = testAspect.nodeManagers();
- QVERIFY(nodeManagers);
- Qt3DRender::Render::TechniqueManager *techniqueManager = nodeManagers->techniqueManager();
- QVERIFY(techniqueManager);
- backendFilterCompatibleTechniqueJob.setManager(techniqueManager);
- backendFilterCompatibleTechniqueJob.setRenderer(testAspect.renderer());
- testAspect.initializeRenderer();
- testAspect.renderer()->shutdown();
-
- // THEN
- QCOMPARE(testAspect.renderer()->isRunning(), false);
- QVector<Qt3DRender::Render::HTechnique> handles = testAspect.nodeManagers()->techniqueManager()->activeHandles();
- QCOMPARE(handles.size(), 3);
-
- // WHEN
- backendFilterCompatibleTechniqueJob.run();
-
- // THEN -> untouched since not running
- const QVector<Qt3DCore::QNodeId> dirtyTechniquesId = testAspect.nodeManagers()->techniqueManager()->takeDirtyTechniques();
- QCOMPARE(dirtyTechniquesId.size(), 3);
- }
-
void checkRunRendererRunning()
{
// GIVEN
diff --git a/tests/auto/render/renderer/tst_renderer.cpp b/tests/auto/render/renderer/tst_renderer.cpp
index a2ea993e8..cfccf8b78 100644
--- a/tests/auto/render/renderer/tst_renderer.cpp
+++ b/tests/auto/render/renderer/tst_renderer.cpp
@@ -60,6 +60,9 @@ private Q_SLOTS:
renderer.setSettings(&settings);
renderer.initialize();
+ // NOTE: FilterCompatibleTechniqueJob and ShaderGathererJob cannot run because the context
+ // is not initialized in this test
+
const int singleRenderViewJobCount = 11 + 1 * Qt3DRender::Render::RenderViewBuilder::optimalJobCount();
// RenderViewBuilder renderViewJob,
// renderableEntityFilterJob,
@@ -84,7 +87,6 @@ private Q_SLOTS:
1 + // cleanupJob
1 + // sendRenderCaptureJob
1 + // sendBufferCaptureJob
- 1 + // filterCompatibleTechniquesJob
1 + // VAOGatherer
1 + // updateSkinningPaletteJob
singleRenderViewJobCount); // Only valid for the first call to renderBinJobs(), since subsequent calls won't have the renderqueue reset
@@ -100,7 +102,6 @@ private Q_SLOTS:
1 + // cleanupJob
1 + // sendRenderCaptureJob
1 + // sendBufferCaptureJob
- 1 + // filterCompatibleTechniquesJob
1 + // VAOGatherer
1 + // updateSkinningPaletteJob
1); // EntityEnabledDirty
@@ -117,7 +118,6 @@ private Q_SLOTS:
1 + // cleanupJob
1 + // sendRenderCaptureJob
1 + // sendBufferCaptureJob
- 1 + // filterCompatibleTechniquesJob
1 + // VAOGatherer
1 + // WorldTransformJob
1 + // UpdateWorldBoundingVolume
@@ -137,7 +137,6 @@ private Q_SLOTS:
1 + // cleanupJob
1 + // sendRenderCaptureJob
1 + // sendBufferCaptureJob
- 1 + // filterCompatibleTechniquesJob
1 + // VAOGatherer
1 + // CalculateBoundingVolumeJob
1 + // UpdateMeshTriangleListJob
@@ -156,7 +155,6 @@ private Q_SLOTS:
1 + // cleanupJob
1 + // sendRenderCaptureJob
1 + // sendBufferCaptureJob
- 1 + // filterCompatibleTechniquesJob
1 + // VAOGatherer
1 + // updateSkinningPaletteJob
1); // BufferGathererJob
@@ -164,23 +162,6 @@ private Q_SLOTS:
renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
// WHEN
- renderer.markDirty(Qt3DRender::Render::AbstractRenderer::ShadersDirty, nullptr);
- jobs = renderer.renderBinJobs();
-
- // THEN (level
- QCOMPARE(jobs.size(),
- 1 + // updateLevelOfDetailJob
- 1 + // cleanupJob
- 1 + // sendRenderCaptureJob
- 1 + // sendBufferCaptureJob
- 1 + // filterCompatibleTechniquesJob
- 1 + // VAOGatherer
- 1 + // updateSkinningPaletteJob
- 1); // ShaderGathererJob
-
- renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
-
- // WHEN
renderer.markDirty(Qt3DRender::Render::AbstractRenderer::TexturesDirty, nullptr);
jobs = renderer.renderBinJobs();
@@ -190,7 +171,6 @@ private Q_SLOTS:
1 + // cleanupJob
1 + // sendRenderCaptureJob
1 + // sendBufferCaptureJob
- 1 + // filterCompatibleTechniquesJob
1 + // VAOGatherer
1 + // TexturesGathererJob
1 + // updateSkinningPaletteJob
@@ -208,7 +188,6 @@ private Q_SLOTS:
1 + // cleanupJob
1 + // sendRenderCaptureJob
1 + // sendBufferCaptureJob
- 1 + // filterCompatibleTechniquesJob
1 + // VAOGatherer
1 + // EntityEnabledDirty
1 + // WorldTransformJob
@@ -218,7 +197,6 @@ private Q_SLOTS:
1 + // CalculateBoundingVolumeJob
1 + // UpdateMeshTriangleListJob
1 + // BufferGathererJob
- 1 + // ShaderGathererJob
1 + // TexturesGathererJob
1 + // updateSkinningPaletteJob
1); // SyncTexturesGathererJob
diff --git a/tests/auto/render/technique/tst_technique.cpp b/tests/auto/render/technique/tst_technique.cpp
index a712434d1..7dfd5b85c 100644
--- a/tests/auto/render/technique/tst_technique.cpp
+++ b/tests/auto/render/technique/tst_technique.cpp
@@ -193,6 +193,8 @@ private Q_SLOTS:
// THEN
QCOMPARE(backendTechnique.isEnabled(), newValue);
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::TechniquesDirty);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
}
{
// WHEN
@@ -217,6 +219,8 @@ private Q_SLOTS:
QCOMPARE(dirtyTechniques.size(), 1);
QCOMPARE(dirtyTechniques.first(), backendTechnique.peerId());
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::TechniquesDirty);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
}
{
Qt3DRender::QParameter parameter;
@@ -230,6 +234,8 @@ private Q_SLOTS:
// THEN
QCOMPARE(backendTechnique.parameters().size(), 1);
QCOMPARE(backendTechnique.parameters().first(), parameter.id());
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::TechniquesDirty);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
}
{
// WHEN
@@ -239,6 +245,8 @@ private Q_SLOTS:
// THEN
QCOMPARE(backendTechnique.parameters().size(), 0);
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::TechniquesDirty);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
}
}
{
@@ -253,6 +261,8 @@ private Q_SLOTS:
// THEN
QCOMPARE(backendTechnique.filterKeys().size(), 1);
QCOMPARE(backendTechnique.filterKeys().first(), filterKey.id());
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::TechniquesDirty);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
}
{
// WHEN
@@ -262,6 +272,8 @@ private Q_SLOTS:
// THEN
QCOMPARE(backendTechnique.filterKeys().size(), 0);
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::TechniquesDirty);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
}
}
{
@@ -276,6 +288,8 @@ private Q_SLOTS:
// THEN
QCOMPARE(backendTechnique.renderPasses().size(), 1);
QCOMPARE(backendTechnique.renderPasses().first(), pass.id());
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::TechniquesDirty);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
}
{
// WHEN
@@ -285,6 +299,8 @@ private Q_SLOTS:
// THEN
QCOMPARE(backendTechnique.renderPasses().size(), 0);
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::TechniquesDirty);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
}
}
}