diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/animation/backend/animationutils_p.h | 3 | ||||
-rw-r--r-- | src/core/changes/qpropertynodeaddedchange.cpp | 10 | ||||
-rw-r--r-- | src/core/nodes/qnode.cpp | 17 | ||||
-rw-r--r-- | src/core/nodes/qnode_p.h | 4 | ||||
-rw-r--r-- | src/plugins/sceneparsers/assimp/assimpimporter.cpp | 1 | ||||
-rw-r--r-- | src/quick3d/quick3dscene2d/items/scene2d.cpp | 13 | ||||
-rw-r--r-- | src/render/backend/abstractrenderer_p.h | 3 | ||||
-rw-r--r-- | src/render/backend/renderer.cpp | 279 | ||||
-rw-r--r-- | src/render/backend/renderer_p.h | 13 | ||||
-rw-r--r-- | src/render/jobs/filtercompatibletechniquejob.cpp | 13 | ||||
-rw-r--r-- | src/render/jobs/loadscenejob.cpp | 103 | ||||
-rw-r--r-- | src/render/jobs/loadscenejob_p.h | 8 | ||||
-rw-r--r-- | src/render/materialsystem/technique.cpp | 26 | ||||
-rw-r--r-- | src/render/renderlogging.cpp | 1 | ||||
-rw-r--r-- | src/render/renderlogging_p.h | 1 |
15 files changed, 294 insertions, 201 deletions
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) |