From 929e72e36cc10556bdf33a50911f2cd651f0b8dc Mon Sep 17 00:00:00 2001 From: Svenn-Arne Dragly Date: Mon, 22 Jan 2018 10:44:55 +0100 Subject: Organize dirty bits in categories To get a better overview of which dirty bits changed at what time, this change organizes them in the following categories: - current: dirty bits at job building - marked: dirty bits marked since last job building - remaining: dirty bits set but not cleared in the previous frame Further, within renderBinJobs, we add a variable "notCleared" to keep track of which bits in "current" should be set in "remaining". Change-Id: I43a42a4fd495a6d9f794721d1f09381718dfa647 Reviewed-by: Paul Lemire --- src/render/backend/renderer.cpp | 52 +++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 23 deletions(-) (limited to 'src/render/backend/renderer.cpp') diff --git a/src/render/backend/renderer.cpp b/src/render/backend/renderer.cpp index 20aeaf2df..c1b5760c7 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) @@ -514,7 +513,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) @@ -1445,25 +1444,29 @@ Renderer::ViewSubmissionResultData Renderer::submitRenderViews(const QVectorrenderPolicy() == QRenderSettings::Always - || m_changeSet != 0 + || m_dirtyBits.marked != 0 + || m_dirtyBits.remaining != 0 || !m_lastFrameCorrect.load()); } @@ -1497,28 +1500,31 @@ QVector 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); } @@ -1534,13 +1540,13 @@ QVector Renderer::renderBinJobs() // 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) + if (dirtyBitsForFrame & 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); } @@ -1548,7 +1554,7 @@ QVector 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; @@ -1584,19 +1590,19 @@ QVector Renderer::renderBinJobs() // Only reset LayersDirty flag once we have really rebuilt the caches if (layersDirty && !layersCacheRebuilt) - changesToUnset.setFlag(AbstractRenderer::LayersDirty, false); + notCleared |= AbstractRenderer::LayersDirty; if (entitiesEnabledDirty && !layersCacheRebuilt) - changesToUnset.setFlag(AbstractRenderer::EntityEnabledDirty, false); + notCleared |= AbstractRenderer::EntityEnabledDirty; // Clear dirty bits // TO DO: When secondary GL thread is integrated, the following line can be removed - changesToUnset.setFlag(AbstractRenderer::ShadersDirty, false); + notCleared |= AbstractRenderer::ShadersDirty; // 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); + notCleared |= Renderer::ComputeDirty; + + m_dirtyBits.remaining = dirtyBitsForFrame & notCleared; return renderBinJobs; } @@ -1722,7 +1728,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(); -- cgit v1.2.3 From a819190eb71ceb6bb9202f0b3497cc117d8331e7 Mon Sep 17 00:00:00 2001 From: Svenn-Arne Dragly Date: Tue, 30 Jan 2018 09:21:38 +0100 Subject: Make ShaderGathererJob depend on FilterCompatibleTechniques The ShaderGathererJob will use Technique::isCompatibleWithRenderer() while looking for dirty shaders. This is set in the FilterCompatibleTechniquesJob, but it might run after (or, even worse, at the same time as) ShaderGathererJob. Task-number: QTBUG-66024 Change-Id: I929e87b9c67068b51f7d64c637b1741d743b1839 Reviewed-by: Paul Lemire --- src/render/backend/renderer.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/render/backend/renderer.cpp') diff --git a/src/render/backend/renderer.cpp b/src/render/backend/renderer.cpp index c1b5760c7..7ed62a98f 100644 --- a/src/render/backend/renderer.cpp +++ b/src/render/backend/renderer.cpp @@ -217,6 +217,8 @@ Renderer::Renderer(QRenderAspect::RenderType type) m_updateLevelOfDetailJob->addDependency(m_updateMeshTriangleListJob); m_pickBoundingVolumeJob->addDependency(m_updateMeshTriangleListJob); + m_shaderGathererJob->addDependency(m_filterCompatibleTechniqueJob); + m_filterCompatibleTechniqueJob->setRenderer(this); m_defaultRenderStateSet = new RenderStateSet; -- cgit v1.2.3 From d02c54e3349e04fd21f22e67bf88c4a1631faf45 Mon Sep 17 00:00:00 2001 From: Svenn-Arne Dragly Date: Mon, 22 Jan 2018 10:42:36 +0100 Subject: Fix OnDemand render policy The OnDemand render policy has no effect because the ShadersDirty and ComputeDirty flags are always set. This commit resets ShadersDirty so that ShaderGathererJob is only run when needed. It also introduces the TechniquesDirty flag so we know when to run FilterCompatibleTechniqueJob and makes sure the job is only run if the context has been initialized and the renderer is running. Task-number: QTBUG-65965 Change-Id: Icbcc03ed2dc6b14c6580cc794267b5a88e5f4ca2 Reviewed-by: Paul Lemire --- src/render/backend/renderer.cpp | 154 +++++++++++++++++++--------------------- 1 file changed, 74 insertions(+), 80 deletions(-) (limited to 'src/render/backend/renderer.cpp') diff --git a/src/render/backend/renderer.cpp b/src/render/backend/renderer.cpp index 7ed62a98f..cfe0cd98a 100644 --- a/src/render/backend/renderer.cpp +++ b/src/render/backend/renderer.cpp @@ -1013,72 +1013,71 @@ void Renderer::lookForDirtyTextures() // Executed in a job void Renderer::lookForDirtyShaders() { - if (isRunning()) { - const QVector activeTechniques = m_nodesManager->techniqueManager()->activeHandles(); - const QVector 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 activeTechniques = m_nodesManager->techniqueManager()->activeHandles(); + const QVector 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(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) { + shaderBuilder->setGraphicsApi(*technique->graphicsApiFilter()); + + for (int i = 0; i <= ShaderBuilder::Compute; i++) { + const auto builderType = static_cast(i); + if (!shaderBuilder->shaderGraph(builderType).isValid()) + continue; + + if (shaderBuilder->isShaderCodeDirty(builderType)) { + shaderBuilder->generateCode(builderType); } - } - if (Q_UNLIKELY(shader->hasPendingNotifications())) - shader->submitPendingNotifications(); - if (shader != nullptr && !shader->isLoaded()) - m_dirtyShaders.push_back(shaderHandle); + 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 (Q_UNLIKELY(shader->hasPendingNotifications())) + shader->submitPendingNotifications(); + if (shader != nullptr && !shader->isLoaded()) + m_dirtyShaders.push_back(shaderHandle); } } } @@ -1536,7 +1535,6 @@ QVector 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 @@ -1545,9 +1543,6 @@ QVector Renderer::renderBinJobs() if (dirtyBitsForFrame & AbstractRenderer::BuffersDirty) renderBinJobs.push_back(m_bufferGathererJob); - if (dirtyBitsForFrame & AbstractRenderer::ShadersDirty) - renderBinJobs.push_back(m_shaderGathererJob); - if (dirtyBitsForFrame & AbstractRenderer::TexturesDirty) { renderBinJobs.push_back(m_syncTextureLoadingJob); renderBinJobs.push_back(m_textureGathererJob); @@ -1558,7 +1553,6 @@ QVector Renderer::renderBinJobs() // on entities const bool layersDirty = dirtyBitsForFrame & AbstractRenderer::LayersDirty; const bool layersCacheNeedsToBeRebuilt = layersDirty || entitiesEnabledDirty; - bool layersCacheRebuilt = false; QMutexLocker lock(m_renderQueue->mutex()); if (m_renderQueue->wasReset()) { // Have we rendered yet? (Scene3D case) @@ -1584,25 +1578,25 @@ QVector Renderer::renderBinJobs() builder.prepareJobs(); renderBinJobs.append(builder.buildJobHierachy()); } - layersCacheRebuilt = true; // Set target number of RenderViews m_renderQueue->setTargetRenderViewCount(fgBranchCount); - } - - // Only reset LayersDirty flag once we have really rebuilt the caches - if (layersDirty && !layersCacheRebuilt) - notCleared |= AbstractRenderer::LayersDirty; - if (entitiesEnabledDirty && !layersCacheRebuilt) + } 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; + } - // Clear dirty bits - // TO DO: When secondary GL thread is integrated, the following line can be removed - notCleared |= AbstractRenderer::ShadersDirty; - - // Clear all dirty flags but Compute so that - // we still render every frame when a compute shader is used in a scene - notCleared |= Renderer::ComputeDirty; + 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; + } m_dirtyBits.remaining = dirtyBitsForFrame & notCleared; -- cgit v1.2.3 From 959db7d038544803fe4cf2601523ea3af224652e Mon Sep 17 00:00:00 2001 From: Svenn-Arne Dragly Date: Mon, 29 Jan 2018 18:25:04 +0100 Subject: Make PickBoundingVolumeJob depend on ExpandBoundingVolumeJob PickBoundingVolumeJob uses QEntity::worldBoundingVolumeWithChildren(), which is set by ExpandBoundingVolumeJob. Change-Id: Ic03360a694254e45c9abfd6863a7b101910bb7fc Reviewed-by: Paul Lemire --- src/render/backend/renderer.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/render/backend/renderer.cpp') diff --git a/src/render/backend/renderer.cpp b/src/render/backend/renderer.cpp index cfe0cd98a..b5b387cc5 100644 --- a/src/render/backend/renderer.cpp +++ b/src/render/backend/renderer.cpp @@ -205,6 +205,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 -- cgit v1.2.3 From 156afc0f9dad1c16ee9a0ed31307121941443491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A4=C3=A4tt=C3=A4=20Antti?= Date: Mon, 5 Feb 2018 16:36:44 +0200 Subject: Fix crash if sharecontext is requested before initailization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Scene2D can sometimes receive the render initialization event before the qt3d renderer has been initialized. This causes crash because the sharecontext hasn't been set yet. Add safeguard against this. Task-number: QT3DS-904 Change-Id: Ib50a60ed89c12ac54c9165266466d9804affe77c Reviewed-by: Laszlo Agocs Reviewed-by: Paul Lemire Reviewed-by: Sean Harmer (cherry picked from commit 13f340c92bdf725d214ab4840fc2e071d12d6e00) Reviewed-by: Antti Määttä --- src/render/backend/renderer.cpp | 75 ++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 34 deletions(-) (limited to 'src/render/backend/renderer.cpp') diff --git a/src/render/backend/renderer.cpp b/src/render/backend/renderer.cpp index b5b387cc5..6217338eb 100644 --- a/src/render/backend/renderer.cpp +++ b/src/render/backend/renderer.cpp @@ -307,7 +307,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) @@ -325,45 +329,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 -- cgit v1.2.3