summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Lemire <paul.lemire@kdab.com>2018-02-20 15:21:13 +0100
committerPaul Lemire <paul.lemire@kdab.com>2018-02-28 10:44:49 +0000
commit3dff7c204da1d834629544b54b9f6efaafe165e2 (patch)
treef8529c267fd3a8ffa2f7e293682bdea219fe2897
parenta43cf3726679704cc8c4c5a405c60c6f1c4fcfce (diff)
Revert "Keep rendering in sync with aspect jobs by adding barriers"
This reverts commit 46319648436814afb5a77755dde6681e304befaf. We want to be able to render one set of RenderViews for frame n while concurrently building RenderViews for frame n + 1. The reverted commit removed that behavior which reduced the CPU time available to prepare a frame. This would cause on some scenes a failure to meet the ~10ms budget we have to prepare a frame. This is therefore a regression. The root cause behind this regression is that a job cannot be executed until all the jobs which have been previously launched have completed. The proposed solution would be to instead add an OpenGL command thread that can be used to load graphics resources required for the RenderViews directly when required. This would in turn allow to cache RenderViews and keep the concurrent behavior of RenderView submission and creation. With that goal in mind, the following patches will be rebased and updated https://codereview.qt-project.org/#/c/189309/ https://codereview.qt-project.org/#/c/189310/ Change-Id: I4879047c45986a0e615e3aef7b7352f82a04a9da Reviewed-by: Sean Harmer <sean.harmer@kdab.com>
-rw-r--r--src/quick3d/imports/scene3d/scene3drenderer.cpp8
-rw-r--r--src/quick3d/imports/scene3d/scene3drenderer_p.h1
-rw-r--r--src/render/backend/abstractrenderer_p.h10
-rw-r--r--src/render/backend/render-backend.pri6
-rw-r--r--src/render/backend/renderbarrierjob.cpp111
-rw-r--r--src/render/backend/renderbarrierjob_p.h91
-rw-r--r--src/render/backend/renderer.cpp582
-rw-r--r--src/render/backend/renderer_p.h51
-rw-r--r--src/render/backend/renderqueue.cpp1
-rw-r--r--src/render/backend/renderview.cpp1
-rw-r--r--src/render/backend/renderviewbuilder.cpp23
-rw-r--r--src/render/frontend/qrenderaspect.cpp69
-rw-r--r--src/render/frontend/qrenderaspect_p.h4
-rw-r--r--src/render/geometry/geometryrenderer.cpp2
-rw-r--r--src/render/graphicshelpers/graphicscontext.cpp15
-rw-r--r--src/render/graphicshelpers/graphicscontext_p.h2
-rw-r--r--src/render/jobs/job_common_p.h7
-rw-r--r--src/render/jobs/loadtexturedatajob.cpp30
-rw-r--r--src/render/jobs/loadtexturedatajob_p.h6
-rw-r--r--src/render/services/vsyncframeadvanceservice.cpp2
-rw-r--r--tests/auto/render/commons/testrenderer.h10
-rw-r--r--tests/auto/render/filtercompatibletechniquejob/tst_filtercompatibletechniquejob.cpp3
-rw-r--r--tests/auto/render/geometryrenderer/tst_geometryrenderer.cpp3
-rw-r--r--tests/auto/render/render.pro3
-rw-r--r--tests/auto/render/renderbarrierjob/renderbarrierjob.pro9
-rw-r--r--tests/auto/render/renderbarrierjob/tst_renderbarrierjob.cpp118
-rw-r--r--tests/auto/render/renderer/tst_renderer.cpp159
-rw-r--r--tests/auto/render/renderviewbuilder/tst_renderviewbuilder.cpp21
28 files changed, 437 insertions, 911 deletions
diff --git a/src/quick3d/imports/scene3d/scene3drenderer.cpp b/src/quick3d/imports/scene3d/scene3drenderer.cpp
index b2e541d35..1c9fec4d2 100644
--- a/src/quick3d/imports/scene3d/scene3drenderer.cpp
+++ b/src/quick3d/imports/scene3d/scene3drenderer.cpp
@@ -135,6 +135,7 @@ Scene3DRenderer::Scene3DRenderer(Scene3DItem *item, Qt3DCore::QAspectEngine *asp
, m_multisample(false) // this value is not used, will be synced from the Scene3DItem instead
, m_lastMultisample(false)
, m_needsShutdown(true)
+ , m_blocking(false)
{
Q_CHECK_PTR(m_item);
Q_CHECK_PTR(m_item->window());
@@ -154,6 +155,9 @@ Scene3DRenderer::Scene3DRenderer(Scene3DItem *item, Qt3DCore::QAspectEngine *asp
ContextSaver saver;
static_cast<QRenderAspectPrivate*>(QRenderAspectPrivate::get(m_renderAspect))->renderInitialize(saver.context());
scheduleRootEntityChange();
+
+ const bool blockingRendermode = !qgetenv("SCENE3D_BLOCKING_RENDERMODE").isEmpty();
+ m_blocking = blockingRendermode;
}
Scene3DRenderer::~Scene3DRenderer()
@@ -203,8 +207,6 @@ void Scene3DRenderer::shutdown()
// would return early
m_item = nullptr;
- static_cast<QRenderAspectPrivate*>(QRenderAspectPrivate::get(m_renderAspect))->abortRenderJobs();
-
// Exit the simulation loop so no more jobs are asked for. Once this
// returns it is safe to shutdown the renderer.
if (m_aspectEngine) {
@@ -309,7 +311,7 @@ void Scene3DRenderer::render()
m_finalFBO->bind();
// Render Qt3D Scene
- static_cast<QRenderAspectPrivate*>(QRenderAspectPrivate::get(m_renderAspect))->tryRenderSynchronous();
+ static_cast<QRenderAspectPrivate*>(QRenderAspectPrivate::get(m_renderAspect))->renderSynchronous(m_blocking);
// We may have called doneCurrent() so restore the context if the rendering surface was changed
// Note: keep in mind that the ContextSave also restores the surface when destroyed
diff --git a/src/quick3d/imports/scene3d/scene3drenderer_p.h b/src/quick3d/imports/scene3d/scene3drenderer_p.h
index 7a85bc774..eb2b930ef 100644
--- a/src/quick3d/imports/scene3d/scene3drenderer_p.h
+++ b/src/quick3d/imports/scene3d/scene3drenderer_p.h
@@ -109,6 +109,7 @@ private:
bool m_multisample;
bool m_lastMultisample;
bool m_needsShutdown;
+ bool m_blocking;
friend class Scene3DCleaner;
};
diff --git a/src/render/backend/abstractrenderer_p.h b/src/render/backend/abstractrenderer_p.h
index bf05bdec8..54f37ea21 100644
--- a/src/render/backend/abstractrenderer_p.h
+++ b/src/render/backend/abstractrenderer_p.h
@@ -134,7 +134,7 @@ public:
// Threaded renderer
virtual void render() = 0;
// Synchronous renderer
- virtual void doRender() = 0;
+ virtual void doRender(bool scene3dBlocking = false) = 0;
virtual void cleanGraphicsResources() = 0;
@@ -151,7 +151,7 @@ public:
virtual QVector<Qt3DCore::QAspectJobPtr> renderBinJobs() = 0;
virtual Qt3DCore::QAspectJobPtr pickBoundingVolumeJob() = 0;
virtual Qt3DCore::QAspectJobPtr rayCastingJob() = 0;
- virtual Qt3DCore::QAspectJobPtr syncSkeletonLoadingJob() = 0;
+ virtual Qt3DCore::QAspectJobPtr syncTextureLoadingJob() = 0;
virtual Qt3DCore::QAspectJobPtr expandBoundingVolumeJob() = 0;
virtual void setSceneRoot(Qt3DCore::QBackendNodeFactory *factory, Entity *root) = 0;
@@ -170,12 +170,6 @@ public:
virtual void setOffscreenSurfaceHelper(OffscreenSurfaceHelper *helper) = 0;
virtual QSurfaceFormat format() = 0;
virtual QOpenGLContext *shareContext() const = 0;
-
- virtual void lockSurfaceAndRender() = 0;
- virtual bool releaseRendererAndRequestPromiseToRender() = 0;
- virtual bool waitForRenderJobs() = 0;
- virtual bool tryWaitForRenderJobs(int timeout) = 0;
- virtual void abortRenderJobs() = 0;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractRenderer::BackendNodeDirtySet)
diff --git a/src/render/backend/render-backend.pri b/src/render/backend/render-backend.pri
index a3df90f8c..f7f30b5c9 100644
--- a/src/render/backend/render-backend.pri
+++ b/src/render/backend/render-backend.pri
@@ -45,8 +45,7 @@ HEADERS += \
$$PWD/visitorutils_p.h \
$$PWD/segmentsvisitor_p.h \
$$PWD/pointsvisitor_p.h \
- $$PWD/renderercache_p.h \
- $$PWD/renderbarrierjob_p.h
+ $$PWD/renderercache_p.h
SOURCES += \
$$PWD/renderthread.cpp \
@@ -83,8 +82,7 @@ SOURCES += \
$$PWD/resourceaccessor.cpp \
$$PWD/segmentsvisitor.cpp \
$$PWD/commandthread.cpp \
- $$PWD/pointsvisitor.cpp \
- $$PWD/renderbarrierjob.cpp
+ $$PWD/pointsvisitor.cpp
include($$QT3D_BUILD_ROOT/src/core/qt3dcore-config.pri)
QT_FOR_CONFIG += 3dcore-private
diff --git a/src/render/backend/renderbarrierjob.cpp b/src/render/backend/renderbarrierjob.cpp
deleted file mode 100644
index c60c9f368..000000000
--- a/src/render/backend/renderbarrierjob.cpp
+++ /dev/null
@@ -1,111 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "renderbarrierjob_p.h"
-
-#include <Qt3DRender/private/renderlogging_p.h>
-
-#include <QtCore/QThread>
-
-QT_BEGIN_NAMESPACE
-
-namespace Qt3DRender {
-namespace Render {
-
-QDebug operator<<(QDebug debug, JobTypes::JobType type)
-{
- switch (type) {
- case JobTypes::ReadRenderQueueSizeBarrier:
- debug << "ReadRenderQueueSize";
- break;
- case JobTypes::BeginDrawingBarrier:
- debug << "BeginDrawing";
- break;
- case JobTypes::UpdateGLResourcesBarrier:
- debug << "UpdateGLResources";
- break;
- case JobTypes::PrepareCommandSubmissionBarrier:
- debug << "PrepareCommandSubmission";
- break;
- case JobTypes::EndDrawingBarrier:
- debug << "EndDrawing";
- break;
- default:
- break;
- }
- return debug;
-}
-
-RenderBarrierJob::RenderBarrierJob(JobTypes::JobType type)
- : QAspectJob()
- , m_type(type)
- , m_begin(0)
- , m_end(0)
-{
- SET_JOB_RUN_STAT_TYPE(this, type, 0);
-}
-
-void RenderBarrierJob::waitForDependencies()
-{
- qCDebug(Jobs) << Q_FUNC_INFO << m_type << "waiting for job on" << QThread::currentThread();
- m_begin.acquire();
- qCDebug(Jobs) << Q_FUNC_INFO << m_type << "done waiting for job on" << QThread::currentThread();
- Q_ASSERT(m_begin.available() == 0);
-}
-
-void RenderBarrierJob::allowToProceed()
-{
- qCDebug(Jobs) << Q_FUNC_INFO << m_type << "releasing job on" << QThread::currentThread();
- m_end.release();
-}
-
-void RenderBarrierJob::run()
-{
- qCDebug(Jobs) << Q_FUNC_INFO << m_type << "job releasing render thread on" << QThread::currentThread();
- m_begin.release();
- qCDebug(Jobs) << Q_FUNC_INFO << m_type << "job waiting for render thread on" << QThread::currentThread();
- m_end.acquire();
- qCDebug(Jobs) << Q_FUNC_INFO << m_type << "job done on" << QThread::currentThread();
- Q_ASSERT(m_end.available() == 0);
-}
-
-} // namespace Render
-} // namespace Qt3DRender
-
-QT_END_NAMESPACE
diff --git a/src/render/backend/renderbarrierjob_p.h b/src/render/backend/renderbarrierjob_p.h
deleted file mode 100644
index a39b2aa83..000000000
--- a/src/render/backend/renderbarrierjob_p.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QT3DRENDER_RENDER_RENDERBARRIERJOB_P_H
-#define QT3DRENDER_RENDER_RENDERBARRIERJOB_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists for the convenience
-// of other Qt classes. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <Qt3DCore/QAspectJob>
-
-#include <Qt3DRender/private/qt3drender_global_p.h>
-#include <Qt3DRender/private/job_common_p.h>
-
-#include <QtCore/qsemaphore.h>
-
-#include <functional>
-
-QT_BEGIN_NAMESPACE
-
-namespace Qt3DRender {
-namespace Render {
-
-class QT3DRENDERSHARED_PRIVATE_EXPORT RenderBarrierJob : public Qt3DCore::QAspectJob
-{
-public:
- RenderBarrierJob(JobTypes::JobType type);
- // Called from render thread
- void waitForDependencies();
- // Called from render thread
- void allowToProceed();
-
- void run() final;
-private:
- JobTypes::JobType m_type;
- QSemaphore m_begin;
- QSemaphore m_end;
-};
-
-using RenderBarrierJobPtr = QSharedPointer<RenderBarrierJob>;
-
-} // namespace Render
-} // namespace Qt3DRender
-
-QT_END_NAMESPACE
-
-#endif // QT3DRENDER_RENDER_RENDERBARRIERJOB_P_H
diff --git a/src/render/backend/renderer.cpp b/src/render/backend/renderer.cpp
index d89813f57..2dda73679 100644
--- a/src/render/backend/renderer.cpp
+++ b/src/render/backend/renderer.cpp
@@ -89,14 +89,11 @@
#include <Qt3DRender/private/buffercapture_p.h>
#include <Qt3DRender/private/offscreensurfacehelper_p.h>
#include <Qt3DRender/private/renderviewbuilder_p.h>
-#include <Qt3DRender/private/loadtexturedatajob_p.h>
-#include <Qt3DRender/private/renderbarrierjob_p.h>
#include <Qt3DRender/qcameralens.h>
#include <Qt3DCore/private/qeventfilterservice_p.h>
#include <Qt3DCore/private/qabstractaspectjobmanager_p.h>
#include <Qt3DCore/private/qnodecreatedchangegenerator_p.h>
-#include <private/rendersurfaceselector_p.h>
#if QT_CONFIG(qt3d_profile_jobs)
#include <Qt3DCore/private/aspectcommanddebugger_p.h>
@@ -188,16 +185,10 @@ Renderer::Renderer(QRenderAspect::RenderType type)
, m_bufferGathererJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { lookForDirtyBuffers(); }, JobTypes::DirtyBufferGathering))
, m_vaoGathererJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { lookForAbandonedVaos(); }, JobTypes::DirtyVaoGathering))
, m_textureGathererJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { lookForDirtyTextures(); }, JobTypes::DirtyTextureGathering))
- , m_loadTextureJob(Render::LoadTextureDataJobPtr::create())
, m_shaderGathererJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { lookForDirtyShaders(); }, JobTypes::DirtyShaderGathering))
- , m_syncSkeletonLoadingJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([] {}, JobTypes::SyncSkeletonLoading))
+ , m_syncTextureLoadingJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([] {}, JobTypes::SyncTextureLoading))
, m_ownedContext(false)
, m_offscreenHelper(nullptr)
- , m_readRenderQueueSizeBarrierJob(RenderBarrierJobPtr::create(JobTypes::ReadRenderQueueSizeBarrier))
- , m_beginDrawingBarrierJob(RenderBarrierJobPtr::create(JobTypes::BeginDrawingBarrier))
- , m_updateGLResourcesBarrierJob(RenderBarrierJobPtr::create(JobTypes::UpdateGLResourcesBarrier))
- , m_prepareCommandSubmissionBarrierJob(RenderBarrierJobPtr::create(JobTypes::PrepareCommandSubmissionBarrier))
- , m_endDrawingBarrierJob(RenderBarrierJobPtr::create(JobTypes::EndDrawingBarrier))
#if QT_CONFIG(qt3d_profile_jobs)
, m_commandExecuter(new Qt3DRender::Debug::CommandExecuter(this))
#endif
@@ -208,12 +199,6 @@ Renderer::Renderer(QRenderAspect::RenderType type)
if (m_renderThread)
m_renderThread->waitForStart();
- // Render thread barrier interdependencies
- m_beginDrawingBarrierJob->addDependency(m_readRenderQueueSizeBarrierJob);
- m_updateGLResourcesBarrierJob->addDependency(m_beginDrawingBarrierJob);
- m_prepareCommandSubmissionBarrierJob->addDependency(m_updateGLResourcesBarrierJob);
- m_endDrawingBarrierJob->addDependency(m_prepareCommandSubmissionBarrierJob);
-
// Create jobs to update transforms and bounding volumes
// We can only update bounding volumes once all world transforms are known
m_updateWorldBoundingVolumeJob->addDependency(m_worldTransformJob);
@@ -223,34 +208,18 @@ Renderer::Renderer(QRenderAspect::RenderType type)
m_pickBoundingVolumeJob->addDependency(m_expandBoundingVolumeJob);
// m_calculateBoundingVolumeJob's dependency on m_updateTreeEnabledJob is set in renderBinJobs
+ // Dirty texture gathering depends on m_syncTextureLoadingJob
+ // m_syncTextureLoadingJob will depend on the texture loading jobs
+ m_textureGathererJob->addDependency(m_syncTextureLoadingJob);
+
// Ensures all skeletons are loaded before we try to update them
- m_updateSkinningPaletteJob->addDependency(m_syncSkeletonLoadingJob);
+ m_updateSkinningPaletteJob->addDependency(m_syncTextureLoadingJob);
// All world stuff depends on the RenderEntity's localBoundingVolume
m_updateLevelOfDetailJob->addDependency(m_updateMeshTriangleListJob);
m_pickBoundingVolumeJob->addDependency(m_updateMeshTriangleListJob);
m_rayCastingJob->addDependency(m_updateMeshTriangleListJob);
- // Independent job dependencies
- m_beginDrawingBarrierJob->addDependency(m_vaoGathererJob);
-
- // Make sure we do not try to filter techniques before we have proper context
- m_filterCompatibleTechniqueJob->addDependency(m_beginDrawingBarrierJob);
-
- // Make sure we have gathered dirty shaders, textures and buffers before calling updateGLResources
- m_updateGLResourcesBarrierJob->addDependency(m_bufferGathererJob);
- m_updateGLResourcesBarrierJob->addDependency(m_shaderGathererJob);
- m_updateGLResourcesBarrierJob->addDependency(m_textureGathererJob);
-
- // Make sure we load textures after gathering
- m_loadTextureJob->addDependency(m_updateGLResourcesBarrierJob);
- m_loadTextureJob->addDependency(m_textureGathererJob);
-
- // Make sure we prepare command submission after textures are loaded
- m_prepareCommandSubmissionBarrierJob->addDependency(m_loadTextureJob);
-
- // Make sure we have found compatible techniques before gathering shaders
- m_updateGLResourcesBarrierJob->addDependency(m_filterCompatibleTechniqueJob);
m_shaderGathererJob->addDependency(m_filterCompatibleTechniqueJob);
m_filterCompatibleTechniqueJob->setRenderer(this);
@@ -319,7 +288,6 @@ void Renderer::setNodeManagers(NodeManagers *managers)
m_updateSkinningPaletteJob->setManagers(m_nodesManager);
m_updateMeshTriangleListJob->setManagers(m_nodesManager);
m_filterCompatibleTechniqueJob->setManager(m_nodesManager->techniqueManager());
- m_loadTextureJob->setNodeManagers(m_nodesManager);
}
void Renderer::setServices(QServiceLocator *services)
@@ -438,17 +406,12 @@ void Renderer::shutdown()
m_renderQueue->reset();
if (!m_renderThread) {
- // Make sure the aspect thread is not left behind
- m_willRenderPromise.store(0);
- m_rendererReadySemaphore.release();
releaseGraphicsResources();
} else {
// Wake up the render thread in case it is waiting for some renderviews
// to be ready. The isReadyToSubmit() function checks for a shutdown
// having been requested.
- m_willRenderPromise.store(0);
- m_rendererReadySemaphore.release(1);
- m_renderJobsReadySemaphore.release(1);
+ m_submitRenderViewsSemaphore.release(1);
m_renderThread->wait();
}
}
@@ -564,7 +527,7 @@ void Renderer::setSceneRoot(QBackendNodeFactory *factory, Entity *sgRoot)
m_updateTreeEnabledJob->setRoot(m_renderSceneRoot);
// Set all flags to dirty
- m_dirtyBits |= AbstractRenderer::AllDirty;
+ m_dirtyBits.marked |= AbstractRenderer::AllDirty;
}
void Renderer::registerEventFilter(QEventFilterService *service)
@@ -583,123 +546,6 @@ RenderSettings *Renderer::settings() const
return m_settings;
}
-bool Renderer::createSurfaceLockAndMakeCurrent()
-{
- FrameGraphVisitor visitor(nodeManagers()->frameGraphManager());
- const QVector<FrameGraphNode *> fgLeaves = visitor.traverse(frameGraphRoot());
- for (FrameGraphNode *leafNode : fgLeaves) {
- FrameGraphNode *node = leafNode;
- while (node) {
- if (!node->isEnabled() || node->nodeType() != FrameGraphNode::Surface) {
- node = node->parent();
- continue;
- }
- const auto *surfaceSelector = static_cast<const RenderSurfaceSelector *>(node);
- auto locker = QSharedPointer<SurfaceLocker>::create(surfaceSelector->surface());
- if (!locker->isSurfaceValid())
- return false;
-
- m_surfaceLockers.append(locker);
- node = node->parent();
-
- if (!m_graphicsContext->makeCurrent(surfaceSelector->surface()))
- return false;
-
- // TODO: The same surface may appear twice here, so we return early to avoid a deadlock.
- // We may want to fix this at some point and make sure we properly lock multiple
- // surfaces.
- return true;
- }
- }
- // No surface found
- return false;
-}
-
-// Called by render() or Scene3D on render thread
-void Renderer::lockSurfaceAndRender()
-{
- // Make sure we can lock the surface before rendering. This cannot be done in a job,
- // because we would otherwise risk rendering into a surface that could get destroyed.
- // We could lock/unlock while rendering, but that requires a fair amount of extra
- // logic to be able to recover gracefully from a partially completed render.
- if (!createSurfaceLockAndMakeCurrent()) {
- m_surfaceLockers.clear();
- abortRenderJobs();
- // We cannot render because we could not lock the surface or the surface is a nullptr.
- // However, the surface might change in the next frontend/backend sync.
- // We therefore need to make sure the aspect thread does not get stuck waiting for
- // the next frame.
- m_vsyncFrameAdvanceService->proceedToNextFrame();
- return;
- }
- // Let the aspect thread know that we promise to render
- m_willRenderPromise.store(1);
- // Release the aspect thread
- m_rendererReadySemaphore.release();
- doRender();
- // Release the surface lockers
- m_surfaceLockers.clear();
-}
-
-/*!
- * \internal
- * Called by RenderAspect on aspect thread, returns false if
- * the render thread has not promised to render the next frame.
- *
- * If this function returns false, the aspect thread
- * bails out and does not issue the renderBinJobs.
- *
- * This keeps us from ending up in a deadlock where the barriers are
- * waiting for doRender to release them.
-*/
-bool Renderer::releaseRendererAndRequestPromiseToRender()
-{
- // Release the render thread, which could be waiting in waitForRenderJobs
- // or is about to call tryWaitForRenderJobs
- m_renderJobsReadySemaphore.release();
- // Wait for the render thread to allow us to continue
- m_rendererReadySemaphore.acquire();
- // Check if the render thread has promised to call doRender
- return m_willRenderPromise.testAndSetOrdered(1, 0);
-}
-
-/*!
- * \internal
- * Called by render(), returns false if we are no longer running
-*/
-bool Renderer::waitForRenderJobs()
-{
- // Wait for the aspect thread to arrive at releaseRendererAndRequestPromiseToRender,
- // which is right before it will create the renderBinJobs
- m_renderJobsReadySemaphore.acquire();
- // Check if we have not shut down in the meantime and rather want to bail out
- return m_running.load();
-}
-
-/*!
- * \internal
- * Called by Scene3D, returns true if we could acquire
-*/
-bool Renderer::tryWaitForRenderJobs(int timeout)
-{
- // See if the aspect thread has arrived at releaseRendererAndRequestPromiseToRender.
- // If not, Scene3D needs to continue to avoid locking up the render thread.
- return m_renderJobsReadySemaphore.tryAcquire(1, timeout) && m_running.load();
-}
-
-/*!
- * \internal
- * Called by Scene3D, in shutdown
-*/
-void Renderer::abortRenderJobs()
-{
- // Make sure the aspect does not start render jobs
- m_willRenderPromise.store(0);
- // Release the aspect so it gets out of the job creation
- m_rendererReadySemaphore.release();
-}
-
-
void Renderer::render()
{
// Traversing the framegraph tree from root to lead node
@@ -716,102 +562,168 @@ void Renderer::render()
// One scene description
// One framegraph description
- while (waitForRenderJobs()) {
- lockSurfaceAndRender();
+ while (m_running.load() > 0) {
+ doRender();
// TO DO: Restore windows exposed detection
// Probably needs to happens some place else though
}
}
-/*!
- * \internal
- * The doRender function is called either by Renderer::render or
- * QRenderAspect::tryRenderSynchronous (Scene3D).
- * Jobs that it depends on are guaranteed to have been spawned by the aspect thread.
- * It will run once for each set of renderBinJobs.
- * Throughout the function call, the jobs are kept in sync using barriers.
- * This guarantees the state of the GL context for the jobs.
- * It is therefore important that any job that needs this function to be in at a specific point
- * in the executio depends on the right barrier.
-*/
-void Renderer::doRender()
+void Renderer::doRender(bool scene3dBlocking)
{
- m_readRenderQueueSizeBarrierJob->waitForDependencies();
- const bool queueIsComplete = m_renderQueue->isFrameQueueComplete();
+ Renderer::ViewSubmissionResultData submissionData;
+ bool hasCleanedQueueAndProceeded = false;
+ bool preprocessingComplete = false;
+ bool beganDrawing = false;
+ const bool canSubmit = isReadyToSubmit();
+
+ // Lock the mutex to protect access to the renderQueue while we look for its state
+ QMutexLocker locker(m_renderQueue->mutex());
+ bool queueIsComplete = m_renderQueue->isFrameQueueComplete();
const bool queueIsEmpty = m_renderQueue->targetRenderViewCount() == 0;
- Q_ASSERT(queueIsComplete && !queueIsEmpty);
- m_readRenderQueueSizeBarrierJob->allowToProceed();
- const QVector<Render::RenderView *> renderViews = m_renderQueue->nextFrameQueue();
+ // Scene3D Blocking Mode
+ if (scene3dBlocking && !queueIsComplete && !queueIsEmpty) {
+ int i = 0;
+ // We wait at most 10ms to avoid a case we could never recover from
+ while (!queueIsComplete && i++ < 10) {
+ QThread::msleep(1);
+ qCDebug(Backend) << Q_FUNC_INFO << "Waiting for ready queue (try:" << i << "/ 10)";
+ locker.unlock();
+ queueIsComplete = m_renderQueue->isFrameQueueComplete();
+ locker.relock();
+ }
+ }
+
+ // When using synchronous rendering (QtQuick)
+ // We are not sure that the frame queue is actually complete
+ // Since a call to render may not be synched with the completions
+ // of the RenderViewJobs
+ // In such a case we return early, waiting for a next call with
+ // the frame queue complete at this point
+
+ // RenderQueue is complete (but that means it may be of size 0)
+ if (canSubmit && (queueIsComplete && !queueIsEmpty)) {
+ const QVector<Render::RenderView *> renderViews = m_renderQueue->nextFrameQueue();
+
#if QT_CONFIG(qt3d_profile_jobs)
- // Save start of frame
- JobRunStats submissionStatsPart1;
- JobRunStats submissionStatsPart2;
- submissionStatsPart1.jobId.typeAndInstance[0] = JobTypes::FrameSubmissionPart1;
- submissionStatsPart1.jobId.typeAndInstance[1] = 0;
- submissionStatsPart1.threadId = reinterpret_cast<quint64>(QThread::currentThreadId());
- submissionStatsPart1.startTime = QThreadPooler::m_jobsStatTimer.nsecsElapsed();
- submissionStatsPart2.jobId.typeAndInstance[0] = JobTypes::FrameSubmissionPart2;
- submissionStatsPart2.jobId.typeAndInstance[1] = 0;
- submissionStatsPart2.threadId = reinterpret_cast<quint64>(QThread::currentThreadId());
+ // Save start of frame
+ JobRunStats submissionStatsPart1;
+ JobRunStats submissionStatsPart2;
+ submissionStatsPart1.jobId.typeAndInstance[0] = JobTypes::FrameSubmissionPart1;
+ submissionStatsPart1.jobId.typeAndInstance[1] = 0;
+ submissionStatsPart1.threadId = reinterpret_cast<quint64>(QThread::currentThreadId());
+ submissionStatsPart1.startTime = QThreadPooler::m_jobsStatTimer.nsecsElapsed();
+ submissionStatsPart2.jobId.typeAndInstance[0] = JobTypes::FrameSubmissionPart2;
+ submissionStatsPart2.jobId.typeAndInstance[1] = 0;
+ submissionStatsPart2.threadId = reinterpret_cast<quint64>(QThread::currentThreadId());
#endif
- // Reset state for each draw if we don't have complete control of the context
- if (!m_ownedContext)
- m_graphicsContext->setCurrentStateSet(nullptr);
- // Initialize context
- m_beginDrawingBarrierJob->waitForDependencies();
- m_graphicsContext->beginDrawing();
- m_beginDrawingBarrierJob->allowToProceed();
- // Execute commands for buffer uploads, texture updates, shader loading first
- m_updateGLResourcesBarrierJob->waitForDependencies();
- updateGLResources();
- m_updateGLResourcesBarrierJob->allowToProceed();
- // Update VAO and copy data into commands to allow concurrent submission
- m_prepareCommandSubmissionBarrierJob->waitForDependencies();
- prepareCommandsSubmission(renderViews);
- m_prepareCommandSubmissionBarrierJob->allowToProceed();
-
- // Proceed to next frame and start preparing frame n + 1
- m_renderQueue->reset();
- m_vsyncFrameAdvanceService->proceedToNextFrame();
+ if (canRender()) {
+ { // Scoped to destroy surfaceLock
+ QSurface *surface = nullptr;
+ for (const Render::RenderView *rv: renderViews) {
+ surface = rv->surface();
+ if (surface)
+ break;
+ }
+
+ SurfaceLocker surfaceLock(surface);
+ const bool surfaceIsValid = (surface && surfaceLock.isSurfaceValid());
+ if (surfaceIsValid) {
+ // Reset state for each draw if we don't have complete control of the context
+ if (!m_ownedContext)
+ m_graphicsContext->setCurrentStateSet(nullptr);
+ beganDrawing = m_graphicsContext->beginDrawing(surface);
+ if (beganDrawing) {
+ // 1) Execute commands for buffer uploads, texture updates, shader loading first
+ updateGLResources();
+ // 2) Update VAO and copy data into commands to allow concurrent submission
+ prepareCommandsSubmission(renderViews);
+ preprocessingComplete = true;
+ }
+ }
+ }
+ // 2) Proceed to next frame and start preparing frame n + 1
+ m_renderQueue->reset();
+ locker.unlock(); // Done protecting RenderQueue
+ m_vsyncFrameAdvanceService->proceedToNextFrame();
+ hasCleanedQueueAndProceeded = true;
#if QT_CONFIG(qt3d_profile_jobs)
- submissionStatsPart2.startTime = QThreadPooler::m_jobsStatTimer.nsecsElapsed();
- submissionStatsPart1.endTime = submissionStatsPart2.startTime;
+ if (preprocessingComplete) {
+ submissionStatsPart2.startTime = QThreadPooler::m_jobsStatTimer.nsecsElapsed();
+ submissionStatsPart1.endTime = submissionStatsPart2.startTime;
+ }
#endif
- // Submit the render commands for frame n (making sure we never reference something that could be changing)
- // Render using current device state and renderer configuration
- Renderer::ViewSubmissionResultData submissionData = submitRenderViews(renderViews);
- // Perform any required cleanup of the Graphics resources (Buffers deleted, Shader deleted...)
- cleanGraphicsResources();
+ // Only try to submit the RenderViews if the preprocessing was successful
+ // This part of the submission is happening in parallel to the RV building for the next frame
+ if (preprocessingComplete) {
+ // 3) Submit the render commands for frame n (making sure we never reference something that could be changing)
+ // Render using current device state and renderer configuration
+ submissionData = submitRenderViews(renderViews);
+
+ // Perform any required cleanup of the Graphics resources (Buffers deleted, Shader deleted...)
+ cleanGraphicsResources();
+ }
+ }
#if QT_CONFIG(qt3d_profile_jobs)
- // Execute the pending shell commands
- m_commandExecuter->performAsynchronousCommandExecution(renderViews);
+ // Execute the pending shell commands
+ m_commandExecuter->performAsynchronousCommandExecution(renderViews);
#endif
- // Delete all the RenderViews which will clear the allocators
- // that were used for their allocation
- qDeleteAll(renderViews);
+ // Delete all the RenderViews which will clear the allocators
+ // that were used for their allocation
+ qDeleteAll(renderViews);
#if QT_CONFIG(qt3d_profile_jobs)
- // Save submission elapsed time
- submissionStatsPart2.endTime = QThreadPooler::m_jobsStatTimer.nsecsElapsed();
- // Note this is safe since proceedToNextFrame is the one going to trigger
- // the write to the file, and this is performed after this step
- Qt3DCore::QThreadPooler::addSubmissionLogStatsEntry(submissionStatsPart1);
- Qt3DCore::QThreadPooler::addSubmissionLogStatsEntry(submissionStatsPart2);
- Profiling::GLTimeRecorder::writeResults();
+ if (preprocessingComplete) {
+ // Save submission elapsed time
+ submissionStatsPart2.endTime = QThreadPooler::m_jobsStatTimer.nsecsElapsed();
+ // Note this is safe since proceedToNextFrame is the one going to trigger
+ // the write to the file, and this is performed after this step
+ Qt3DCore::QThreadPooler::addSubmissionLogStatsEntry(submissionStatsPart1);
+ Qt3DCore::QThreadPooler::addSubmissionLogStatsEntry(submissionStatsPart2);
+ Profiling::GLTimeRecorder::writeResults();
+ }
#endif
+ }
+
+ // Only reset renderQueue and proceed to next frame if the submission
+ // succeeded or if we are using a render thread and that is wasn't performed
+ // already
+
+ // If hasCleanedQueueAndProceeded isn't true this implies that something went wrong
+ // with the rendering and/or the renderqueue is incomplete from some reason
+ // (in the case of scene3d the render jobs may be taking too long ....)
+ // or alternatively it could be complete but empty (RenderQueue of size 0)
+ if (!hasCleanedQueueAndProceeded &&
+ (m_renderThread || queueIsComplete || queueIsEmpty)) {
+ // RenderQueue was full but something bad happened when
+ // trying to render it and therefore proceedToNextFrame was not called
+ // Note: in this case the renderQueue mutex is still locked
+
+ // Reset the m_renderQueue so that we won't try to render
+ // with a queue used by a previous frame with corrupted content
+ // if the current queue was correctly submitted
+ m_renderQueue->reset();
+
+ // We allow the RenderTickClock service to proceed to the next frame
+ // In turn this will allow the aspect manager to request a new set of jobs
+ // to be performed for each aspect
+ m_vsyncFrameAdvanceService->proceedToNextFrame();
+ }
// Perform the last swapBuffers calls after the proceedToNextFrame
// as this allows us to gain a bit of time for the preparation of the
// next frame
// Finish up with last surface used in the list of RenderViews
- m_endDrawingBarrierJob->waitForDependencies();
- m_graphicsContext->endDrawing(submissionData.lastBoundFBOId == m_graphicsContext->defaultFBO());
- m_endDrawingBarrierJob->allowToProceed();
+ if (beganDrawing) {
+ SurfaceLocker surfaceLock(submissionData.surface);
+ // Finish up with last surface used in the list of RenderViews
+ m_graphicsContext->endDrawing(submissionData.lastBoundFBOId == m_graphicsContext->defaultFBO() && surfaceLock.isSurfaceValid());
+ }
}
// Called by RenderViewJobs
@@ -826,8 +738,48 @@ void Renderer::enqueueRenderView(Render::RenderView *renderView, int submitOrder
// could be invalid since depending on the order of execution
// the counter could be complete but the renderview not yet added to the
// buffer depending on whichever order the cpu decides to process this
- m_renderQueue->queueRenderView(renderView, submitOrder);
+ const bool isQueueComplete = m_renderQueue->queueRenderView(renderView, submitOrder);
locker.unlock(); // We're done protecting the queue at this point
+ if (isQueueComplete) {
+ if (m_renderThread && m_running.load())
+ Q_ASSERT(m_submitRenderViewsSemaphore.available() == 0);
+ m_submitRenderViewsSemaphore.release(1);
+ }
+}
+
+bool Renderer::canRender() const
+{
+ // Make sure that we've not been told to terminate
+ if (m_renderThread && !m_running.load()) {
+ qCDebug(Rendering) << "RenderThread termination requested whilst waiting";
+ return false;
+ }
+
+ // TO DO: Check if all surfaces have been destroyed...
+ // It may be better if the last window to be closed trigger a call to shutdown
+ // Rather than having checks for the surface everywhere
+
+ return true;
+}
+
+bool Renderer::isReadyToSubmit()
+{
+ // If we are using a render thread, make sure that
+ // we've been told to render before rendering
+ if (m_renderThread) { // Prevent ouf of order execution
+ m_submitRenderViewsSemaphore.acquire(1);
+
+ // Check if shutdown has been requested
+ if (m_running.load() == 0)
+ return false;
+
+ // When using Thread rendering, the semaphore should only
+ // be released when the frame queue is complete and there's
+ // something to render
+ // The case of shutdown should have been handled just before
+ Q_ASSERT(m_renderQueue->isFrameQueueComplete());
+ }
+ return true;
}
// Main thread
@@ -938,7 +890,6 @@ void Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderView
const QVector<Qt3DCore::QNodeId> attributeIds = rGeometry->attributes();
for (Qt3DCore::QNodeId attributeId : attributeIds) {
Attribute *attribute = m_nodesManager->attributeManager()->lookupResource(attributeId);
- Q_ASSERT(attribute);
switch (attribute->attributeType()) {
case QAttribute::IndexAttribute:
indexAttribute = attribute;
@@ -971,7 +922,6 @@ void Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderView
if (command->m_drawIndirect) {
command->m_indirectAttributeByteOffset = indirectAttribute->byteOffset();
command->m_indirectDrawBuffer = m_nodesManager->bufferManager()->lookupHandle(indirectAttribute->bufferId());
- Q_ASSERT(!command->m_indirectDrawBuffer.isNull());
} else {
// Use the count specified by the GeometryRender
// If not specify use the indexAttribute count if present
@@ -1087,7 +1037,6 @@ void Renderer::lookForDirtyShaders()
RenderPass *renderPass = m_nodesManager->renderPassManager()->lookupResource(passId);
HShader shaderHandle = m_nodesManager->shaderManager()->lookupHandle(renderPass->shaderProgram());
Shader *shader = m_nodesManager->shaderManager()->data(shaderHandle);
- Q_ASSERT(renderPass && shader);
ShaderBuilder *shaderBuilder = nullptr;
for (const HShaderBuilder &builderHandle : activeBuilders) {
@@ -1154,7 +1103,6 @@ void Renderer::updateGLResources()
const QVector<HBuffer> dirtyBufferHandles = std::move(m_dirtyBuffers);
for (const HBuffer &handle: dirtyBufferHandles) {
Buffer *buffer = m_nodesManager->bufferManager()->data(handle);
- Q_ASSERT(buffer);
// Forces creation if it doesn't exit
// Also note the binding point doesn't really matter here, we just upload data
if (!m_graphicsContext->hasGLBufferForBuffer(buffer))
@@ -1171,7 +1119,6 @@ void Renderer::updateGLResources()
ShaderManager *shaderManager = m_nodesManager->shaderManager();
for (const HShader &handle: dirtyShaderHandles) {
Shader *shader = shaderManager->data(handle);
- Q_ASSERT(shader);
// Compile shader
m_graphicsContext->loadShader(shader, shaderManager);
}
@@ -1182,7 +1129,6 @@ void Renderer::updateGLResources()
const QVector<HTexture> activeTextureHandles = std::move(m_dirtyTextures);
for (const HTexture &handle: activeTextureHandles) {
Texture *texture = m_nodesManager->textureManager()->data(handle);
- Q_ASSERT(texture);
// Upload/Update texture
updateTexture(texture);
}
@@ -1290,7 +1236,6 @@ void Renderer::updateTexture(Texture *texture)
// Render Thread
void Renderer::cleanupTexture(const Texture *texture)
{
- Q_ASSERT(texture);
GLTextureManager *glTextureManager = m_nodesManager->glTextureManager();
GLTexture *glTexture = glTextureManager->lookupResource(texture->peerId());
@@ -1343,6 +1288,7 @@ Renderer::ViewSubmissionResultData Renderer::submitRenderViews(const QVector<Ren
// If not, we have to free up the context from the previous surface
// and make the context current on the new surface
surface = renderView->surface();
+ SurfaceLocker surfaceLock(surface);
// TO DO: Make sure that the surface we are rendering too has not been unset
@@ -1350,7 +1296,7 @@ Renderer::ViewSubmissionResultData Renderer::submitRenderViews(const QVector<Ren
// TODO: Investigate if it's worth providing a fallback offscreen surface
// to use when surface is null. Or if we should instead expose an
// offscreensurface to Qt3D.
- if (!surface) {
+ if (!surface || !surfaceLock.isSurfaceValid()) {
m_lastFrameCorrect.store(0);
continue;
}
@@ -1367,14 +1313,12 @@ Renderer::ViewSubmissionResultData Renderer::submitRenderViews(const QVector<Ren
if (surfaceHasChanged) {
// If we can't make the context current on the surface, skip to the
// next RenderView. We won't get the full frame but we may get something
- if (!m_graphicsContext->makeCurrent(surface)) {
+ if (!m_graphicsContext->beginDrawing(surface)) {
qWarning() << "Failed to make OpenGL context current on surface";
m_lastFrameCorrect.store(0);
continue;
}
- m_graphicsContext->beginDrawing();
-
previousSurface = surface;
lastBoundFBOId = m_graphicsContext->boundFrameBufferObject();
}
@@ -1513,35 +1457,39 @@ Renderer::ViewSubmissionResultData Renderer::submitRenderViews(const QVector<Ren
void Renderer::markDirty(BackendNodeDirtySet changes, BackendNode *node)
{
Q_UNUSED(node);
- m_dirtyBits|= changes;
+ m_dirtyBits.marked |= changes;
}
Renderer::BackendNodeDirtySet Renderer::dirtyBits()
{
- return m_dirtyBits;
+ return m_dirtyBits.marked;
}
#if defined(QT_BUILD_INTERNAL)
void Renderer::clearDirtyBits(BackendNodeDirtySet changes)
{
- m_dirtyBits &= ~changes;
+ m_dirtyBits.remaining &= ~changes;
+ m_dirtyBits.marked &= ~changes;
}
#endif
bool Renderer::shouldRender()
{
- if (!m_settings)
- return false;
// 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_dirtyBits != 0
+ || m_dirtyBits.marked != 0
+ || m_dirtyBits.remaining != 0
|| !m_lastFrameCorrect.load());
}
void Renderer::skipNextFrame()
{
- m_vsyncFrameAdvanceService->proceedToNextFrame();
+ Q_ASSERT(m_settings->renderPolicy() != QRenderSettings::Always);
+
+ // make submitRenderViews() actually run
+ m_renderQueue->setNoRender();
+ m_submitRenderViewsSemaphore.release(1);
}
// Waits to be told to create jobs for the next frame
@@ -1552,41 +1500,25 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs()
{
QVector<QAspectJobPtr> renderBinJobs;
+ // Create the jobs to build the frame
+ const QVector<QAspectJobPtr> bufferJobs = createRenderBufferJobs();
+
// Remove previous dependencies
m_calculateBoundingVolumeJob->removeDependency(QWeakPointer<QAspectJob>());
m_cleanupJob->removeDependency(QWeakPointer<QAspectJob>());
- // Render thread barrier jobs
- renderBinJobs.append(m_readRenderQueueSizeBarrierJob);
- renderBinJobs.append(m_beginDrawingBarrierJob);
- renderBinJobs.append(m_updateGLResourcesBarrierJob);
- renderBinJobs.append(m_prepareCommandSubmissionBarrierJob);
- renderBinJobs.append(m_endDrawingBarrierJob);
-
- // Jobs independent of dirty bits
- renderBinJobs.push_back(m_updateSkinningPaletteJob);
- renderBinJobs.push_back(m_updateLevelOfDetailJob);
- renderBinJobs.push_back(m_cleanupJob);
- renderBinJobs.push_back(m_sendRenderCaptureJob);
- renderBinJobs.push_back(m_sendBufferCaptureJob);
- renderBinJobs.push_back(m_vaoGathererJob);
+ // Set dependencies
+ for (const QAspectJobPtr &bufferJob : bufferJobs)
+ m_calculateBoundingVolumeJob->addDependency(bufferJob);
- // Independent job properties
- m_updateSkinningPaletteJob->setDirtyJoints(m_nodesManager->jointManager()->dirtyJoints());
m_updateLevelOfDetailJob->setFrameGraphRoot(frameGraphRoot());
- // Buffer jobs, depends on dirty buffers
- const QVector<QAspectJobPtr> bufferJobs = createRenderBufferJobs();
- for (const QAspectJobPtr &bufferJob : bufferJobs) {
- m_calculateBoundingVolumeJob->addDependency(bufferJob);
- m_beginDrawingBarrierJob->addDependency(bufferJob);
- }
- renderBinJobs.append(bufferJobs);
-
- // Jobs dependent on dirty bits
- const BackendNodeDirtySet dirtyBitsForFrame = m_dirtyBits;
- m_dirtyBits = 0;
+ 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 = dirtyBitsForFrame & AbstractRenderer::EntityEnabledDirty;
if (entitiesEnabledDirty) {
renderBinJobs.push_back(m_updateTreeEnabledJob);
@@ -1611,22 +1543,25 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs()
renderBinJobs.push_back(m_expandBoundingVolumeJob);
}
+ m_updateSkinningPaletteJob->setDirtyJoints(m_nodesManager->jointManager()->dirtyJoints());
+ renderBinJobs.push_back(m_updateSkinningPaletteJob);
+ renderBinJobs.push_back(m_updateLevelOfDetailJob);
+ renderBinJobs.push_back(m_cleanupJob);
+ renderBinJobs.push_back(m_sendRenderCaptureJob);
+ renderBinJobs.push_back(m_sendBufferCaptureJob);
+ renderBinJobs.append(bufferJobs);
+
+ // Jobs to prepare GL Resource upload
+ renderBinJobs.push_back(m_vaoGathererJob);
+
if (dirtyBitsForFrame & AbstractRenderer::BuffersDirty)
renderBinJobs.push_back(m_bufferGathererJob);
if (dirtyBitsForFrame & AbstractRenderer::TexturesDirty) {
- renderBinJobs.push_back(m_loadTextureJob);
+ renderBinJobs.push_back(m_syncTextureLoadingJob);
renderBinJobs.push_back(m_textureGathererJob);
}
- if (dirtyBitsForFrame & AbstractRenderer::SkeletonDataDirty)
- renderBinJobs.push_back(m_syncSkeletonLoadingJob);
-
- if (dirtyBitsForFrame & AbstractRenderer::TechniquesDirty )
- renderBinJobs.push_back(m_filterCompatibleTechniqueJob);
-
- if (dirtyBitsForFrame & AbstractRenderer::ShadersDirty)
- renderBinJobs.push_back(m_shaderGathererJob);
// Layer cache is dependent on layers, layer filters and the enabled flag
// on entities
@@ -1634,32 +1569,52 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs()
const bool layersCacheNeedsToBeRebuilt = layersDirty || entitiesEnabledDirty;
const bool materialDirty = dirtyBitsForFrame & AbstractRenderer::MaterialDirty;
- // Traverse the current framegraph. For each leaf node create a
- // RenderView and set its configuration then create a job to
- // populate the RenderView with a set of RenderCommands that get
- // their details from the RenderNodes that are visible to the
- // Camera selected by the framegraph configuration
- FrameGraphVisitor visitor(m_nodesManager->frameGraphManager());
- const QVector<FrameGraphNode *> fgLeaves = visitor.traverse(frameGraphRoot());
-
- // Remove leaf nodes that no longer exist from cache
- const QList<FrameGraphNode *> keys = m_cache.leafNodeCache.keys();
- for (FrameGraphNode *leafNode : keys) {
- if (!fgLeaves.contains(leafNode))
- m_cache.leafNodeCache.remove(leafNode);
+ QMutexLocker lock(m_renderQueue->mutex());
+ if (m_renderQueue->wasReset()) { // Have we rendered yet? (Scene3D case)
+ // Traverse the current framegraph. For each leaf node create a
+ // RenderView and set its configuration then create a job to
+ // populate the RenderView with a set of RenderCommands that get
+ // their details from the RenderNodes that are visible to the
+ // Camera selected by the framegraph configuration
+ FrameGraphVisitor visitor(m_nodesManager->frameGraphManager());
+ const QVector<FrameGraphNode *> fgLeaves = visitor.traverse(frameGraphRoot());
+
+ // Remove leaf nodes that no longer exist from cache
+ const QList<FrameGraphNode *> keys = m_cache.leafNodeCache.keys();
+ for (FrameGraphNode *leafNode : keys) {
+ if (!fgLeaves.contains(leafNode))
+ m_cache.leafNodeCache.remove(leafNode);
+ }
+
+ const int fgBranchCount = fgLeaves.size();
+ for (int i = 0; i < fgBranchCount; ++i) {
+ RenderViewBuilder builder(fgLeaves.at(i), i, this);
+ builder.setLayerCacheNeedsToBeRebuilt(layersCacheNeedsToBeRebuilt);
+ builder.setMaterialGathererCacheNeedsToBeRebuilt(materialDirty);
+ builder.prepareJobs();
+ renderBinJobs.append(builder.buildJobHierachy());
+ }
+
+ // 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;
}
- const int fgBranchCount = fgLeaves.size();
- for (int i = 0; i < fgBranchCount; ++i) {
- RenderViewBuilder builder(fgLeaves.at(i), i, this);
- builder.setLayerCacheNeedsToBeRebuilt(layersCacheNeedsToBeRebuilt);
- builder.setMaterialGathererCacheNeedsToBeRebuilt(materialDirty);
- builder.prepareJobs();
- renderBinJobs.append(builder.buildJobHierachy());
+ 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;
}
- // Set target number of RenderViews
- m_renderQueue->setTargetRenderViewCount(fgBranchCount);
+ m_dirtyBits.remaining = dirtyBitsForFrame & notCleared;
return renderBinJobs;
}
@@ -1690,9 +1645,9 @@ QAspectJobPtr Renderer::rayCastingJob()
return m_rayCastingJob;
}
-QAspectJobPtr Renderer::syncSkeletonLoadingJob()
+QAspectJobPtr Renderer::syncTextureLoadingJob()
{
- return m_syncSkeletonLoadingJob;
+ return m_syncTextureLoadingJob;
}
QAspectJobPtr Renderer::expandBoundingVolumeJob()
@@ -1797,7 +1752,7 @@ void Renderer::performCompute(const RenderView *, RenderCommand *command)
command->m_workGroups[2]);
}
// HACK: Reset the compute flag to dirty
- m_dirtyBits |= AbstractRenderer::ComputeDirty;
+ m_dirtyBits.marked |= AbstractRenderer::ComputeDirty;
#if defined(QT3D_RENDER_ASPECT_OPENGL_DEBUG)
int err = m_graphicsContext->openGLContext()->functions()->glGetError();
@@ -1933,13 +1888,11 @@ bool Renderer::updateVAOWithAttributes(Geometry *geometry,
for (QNodeId attributeId : attributeIds) {
// TO DO: Improvement we could store handles and use the non locking policy on the attributeManager
Attribute *attribute = m_nodesManager->attributeManager()->lookupResource(attributeId);
- Q_ASSERT(attribute);
if (attribute == nullptr)
return false;
Buffer *buffer = m_nodesManager->bufferManager()->lookupResource(attribute->bufferId());
- Q_ASSERT(buffer);
// Buffer update was already performed at this point
// Just make sure the attribute reference a valid buffer
@@ -1992,7 +1945,6 @@ bool Renderer::requiresVAOAttributeUpdate(Geometry *geometry,
for (QNodeId attributeId : attributeIds) {
// TO DO: Improvement we could store handles and use the non locking policy on the attributeManager
Attribute *attribute = m_nodesManager->attributeManager()->lookupResource(attributeId);
- Q_ASSERT(attribute);
if (attribute == nullptr)
continue;
diff --git a/src/render/backend/renderer_p.h b/src/render/backend/renderer_p.h
index fbb29dc7c..f72794e16 100644
--- a/src/render/backend/renderer_p.h
+++ b/src/render/backend/renderer_p.h
@@ -144,10 +144,6 @@ class NodeManagers;
class UpdateLevelOfDetailJob;
typedef QSharedPointer<UpdateLevelOfDetailJob> UpdateLevelOfDetailJobPtr;
-class LoadTextureDataJob;
-using LoadTextureDataJobPtr = QSharedPointer<LoadTextureDataJob>;
-class RenderBarrierJob;
-using RenderBarrierJobPtr = QSharedPointer<RenderBarrierJob>;
using SynchronizerJobPtr = GenericLambdaJobPtr<std::function<void()>>;
@@ -175,7 +171,7 @@ public:
void releaseGraphicsResources() override;
void render() override;
- void doRender() override;
+ void doRender(bool scene3dBlocking = false) override;
void cleanGraphicsResources() override;
bool isRunning() const override { return m_running.load(); }
@@ -191,30 +187,17 @@ public:
#if defined(QT_BUILD_INTERNAL)
void clearDirtyBits(BackendNodeDirtySet changes) override;
#endif
-
- void lockSurfaceAndRender() override;
- bool releaseRendererAndRequestPromiseToRender() override;
- bool waitForRenderJobs() override;
- bool tryWaitForRenderJobs(int timeout) override;
- void abortRenderJobs() override;
-
bool shouldRender() override;
void skipNextFrame() override;
QVector<Qt3DCore::QAspectJobPtr> renderBinJobs() override;
Qt3DCore::QAspectJobPtr pickBoundingVolumeJob() override;
Qt3DCore::QAspectJobPtr rayCastingJob() override;
- Qt3DCore::QAspectJobPtr syncSkeletonLoadingJob() override;
+ Qt3DCore::QAspectJobPtr syncTextureLoadingJob() override;
Qt3DCore::QAspectJobPtr expandBoundingVolumeJob() override;
QVector<Qt3DCore::QAspectJobPtr> createRenderBufferJobs() const;
- inline RenderBarrierJobPtr readRenderQueueSizeBarrierJob() const { return m_readRenderQueueSizeBarrierJob; }
- inline RenderBarrierJobPtr beginDrawingBarrierJob() const { return m_beginDrawingBarrierJob; }
- inline RenderBarrierJobPtr updateGLResourcesBarrierJob() const { return m_updateGLResourcesBarrierJob; }
- inline RenderBarrierJobPtr prepareCommandSubmissionBarrierJob() const { return m_prepareCommandSubmissionBarrierJob; }
- inline RenderBarrierJobPtr endDrawingBarrierJob() const { return m_endDrawingBarrierJob; }
-
inline FrameCleanupJobPtr frameCleanupJob() const { return m_cleanupJob; }
inline UpdateShaderDataTransformJobPtr updateShaderDataTransformJob() const { return m_updateShaderDataTransformJob; }
inline CalculateBoundingVolumeJobPtr calculateBoundingVolumeJob() const { return m_calculateBoundingVolumeJob; }
@@ -224,9 +207,8 @@ public:
inline UpdateLevelOfDetailJobPtr updateLevelOfDetailJob() const { return m_updateLevelOfDetailJob; }
inline UpdateMeshTriangleListJobPtr updateMeshTriangleListJob() const { return m_updateMeshTriangleListJob; }
inline FilterCompatibleTechniqueJobPtr filterCompatibleTechniqueJob() const { return m_filterCompatibleTechniqueJob; }
- inline SynchronizerJobPtr textureLoadSyncJob() const { return m_syncSkeletonLoadingJob; }
+ inline SynchronizerJobPtr textureLoadSyncJob() const { return m_syncTextureLoadingJob; }
inline UpdateSkinningPaletteJobPtr updateSkinningPaletteJob() const { return m_updateSkinningPaletteJob; }
- inline LoadTextureDataJobPtr loadTextureJob() const {return m_loadTextureJob; }
Qt3DCore::QAbstractFrameAdvanceService *frameAdvanceService() const override;
@@ -269,6 +251,7 @@ public:
const QVector<Qt3DCore::QNodeId> takePendingRenderCaptureSendRequests();
void enqueueRenderView(RenderView *renderView, int submitOrder);
+ bool isReadyToSubmit();
QVariant executeCommand(const QStringList &args) override;
void setOffscreenSurfaceHelper(OffscreenSurfaceHelper *helper) override;
@@ -295,7 +278,7 @@ public:
private:
#endif
- bool hasBeenAskedToTerminate() const;
+ bool canRender() const;
Qt3DCore::QServiceLocator *m_services;
NodeManagers *m_nodesManager;
@@ -317,6 +300,7 @@ private:
QScopedPointer<RenderThread> m_renderThread;
QScopedPointer<VSyncFrameAdvanceService> m_vsyncFrameAdvanceService;
+ QSemaphore m_submitRenderViewsSemaphore;
QSemaphore m_waitForInitializationToBeCompleted;
QAtomicInt m_running;
@@ -327,7 +311,11 @@ private:
QVector<Geometry *> m_dirtyGeometry;
QAtomicInt m_exposed;
- BackendNodeDirtySet m_dirtyBits;
+ 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;
@@ -365,18 +353,15 @@ private:
GenericLambdaJobPtr<std::function<void ()>> m_bufferGathererJob;
GenericLambdaJobPtr<std::function<void ()>> m_vaoGathererJob;
GenericLambdaJobPtr<std::function<void ()>> m_textureGathererJob;
- LoadTextureDataJobPtr m_loadTextureJob;
-
GenericLambdaJobPtr<std::function<void ()>> m_shaderGathererJob;
- SynchronizerJobPtr m_syncSkeletonLoadingJob;
+ SynchronizerJobPtr m_syncTextureLoadingJob;
void lookForAbandonedVaos();
void lookForDirtyBuffers();
void lookForDownloadableBuffers();
void lookForDirtyTextures();
void lookForDirtyShaders();
- bool createSurfaceLockAndMakeCurrent();
QMutex m_abandonedVaosMutex;
QVector<HVao> m_abandonedVaos;
@@ -398,18 +383,6 @@ private:
QMetaObject::Connection m_contextConnection;
RendererCache m_cache;
-
- RenderBarrierJobPtr m_readRenderQueueSizeBarrierJob;
- RenderBarrierJobPtr m_beginDrawingBarrierJob;
- RenderBarrierJobPtr m_updateGLResourcesBarrierJob;
- RenderBarrierJobPtr m_prepareCommandSubmissionBarrierJob;
- RenderBarrierJobPtr m_endDrawingBarrierJob;
-
- QAtomicInt m_willRenderPromise;
- QSemaphore m_rendererReadySemaphore;
- QSemaphore m_renderJobsReadySemaphore;
-
- QVector<QSharedPointer<SurfaceLocker>> m_surfaceLockers;
};
} // namespace Render
diff --git a/src/render/backend/renderqueue.cpp b/src/render/backend/renderqueue.cpp
index db75558a4..2fa1cb7d2 100644
--- a/src/render/backend/renderqueue.cpp
+++ b/src/render/backend/renderqueue.cpp
@@ -90,6 +90,7 @@ bool RenderQueue::queueRenderView(RenderView *renderView, uint submissionOrderIn
Q_ASSERT(!m_noRender);
m_currentWorkQueue[submissionOrderIndex] = renderView;
++m_currentRenderViewCount;
+ Q_ASSERT(m_currentRenderViewCount <= m_targetRenderViewCount);
return isFrameQueueComplete();
}
diff --git a/src/render/backend/renderview.cpp b/src/render/backend/renderview.cpp
index 84de728d4..747a3da41 100644
--- a/src/render/backend/renderview.cpp
+++ b/src/render/backend/renderview.cpp
@@ -648,7 +648,6 @@ QVector<RenderCommand *> RenderView::buildDrawRenderCommands(const QVector<Entit
const QVector<Qt3DCore::QNodeId> attributeIds = geometry->attributes();
for (Qt3DCore::QNodeId attributeId : attributeIds) {
Attribute *attribute = m_manager->attributeManager()->lookupResource(attributeId);
- Q_ASSERT(attribute != nullptr);
if (attribute->attributeType() == QAttribute::IndexAttribute)
indexAttribute = attribute;
else if (command->m_attributes.contains(attribute->nameId()))
diff --git a/src/render/backend/renderviewbuilder.cpp b/src/render/backend/renderviewbuilder.cpp
index 495b666f5..f34b76c4c 100644
--- a/src/render/backend/renderviewbuilder.cpp
+++ b/src/render/backend/renderviewbuilder.cpp
@@ -39,8 +39,6 @@
#include "renderviewbuilder_p.h"
-#include <Qt3DRender/private/renderbarrierjob_p.h>
-
#include <QThread>
QT_BEGIN_NAMESPACE
@@ -83,6 +81,9 @@ public:
// Sort the commands
rv->sort();
+
+ // Enqueue our fully populated RenderView with the RenderThread
+ m_renderer->enqueueRenderView(rv, m_renderViewJob->submitOrderIndex());
}
private:
@@ -124,15 +125,13 @@ public:
const FilterLayerEntityJobPtr &filterEntityByLayerJob,
const FilterProximityDistanceJobPtr &filterProximityJob,
const QVector<MaterialParameterGathererJobPtr> &materialGathererJobs,
- const QVector<RenderViewBuilderJobPtr> &renderViewBuilderJobs,
- Renderer *renderer)
+ const QVector<RenderViewBuilderJobPtr> &renderViewBuilderJobs)
: m_renderViewJob(renderViewJob)
, m_frustumCullingJob(frustumCullingJob)
, m_filterEntityByLayerJob(filterEntityByLayerJob)
, m_filterProximityJob(filterProximityJob)
, m_materialGathererJobs(materialGathererJobs)
, m_renderViewBuilderJobs(renderViewBuilderJobs)
- , m_renderer(renderer)
{}
void operator()()
@@ -158,9 +157,6 @@ public:
// Set whether frustum culling is enabled or not
m_frustumCullingJob->setActive(rv->frustumCulling());
-
- // Enqueue RenderView with the renderer
- m_renderer->enqueueRenderView(rv, m_renderViewJob->submitOrderIndex());
}
private:
@@ -170,7 +166,6 @@ private:
FilterProximityDistanceJobPtr m_filterProximityJob;
QVector<MaterialParameterGathererJobPtr> m_materialGathererJobs;
QVector<RenderViewBuilderJobPtr> m_renderViewBuilderJobs;
- Renderer *m_renderer;
};
class SyncRenderCommandBuilding
@@ -524,8 +519,7 @@ void RenderViewBuilder::prepareJobs()
m_filterEntityByLayerJob,
m_filterProximityJob,
m_materialGathererJobs,
- m_renderViewBuilderJobs,
- m_renderer),
+ m_renderViewBuilderJobs),
JobTypes::SyncRenderViewInitialization);
}
@@ -551,13 +545,11 @@ QVector<Qt3DCore::QAspectJobPtr> RenderViewBuilder::buildJobHierachy() const
m_setClearDrawBufferIndexJob->addDependency(m_syncRenderViewInitializationJob);
- m_renderer->readRenderQueueSizeBarrierJob()->addDependency(m_syncRenderViewInitializationJob);
m_syncRenderViewInitializationJob->addDependency(m_renderViewJob);
m_filterProximityJob->addDependency(m_renderer->expandBoundingVolumeJob());
m_filterProximityJob->addDependency(m_syncRenderViewInitializationJob);
- m_syncRenderCommandBuildingJob->addDependency(m_renderer->updateGLResourcesBarrierJob());
m_syncRenderCommandBuildingJob->addDependency(m_syncRenderViewInitializationJob);
m_syncRenderCommandBuildingJob->addDependency(m_renderableEntityFilterJob);
m_syncRenderCommandBuildingJob->addDependency(m_computableEntityFilterJob);
@@ -566,15 +558,10 @@ QVector<Qt3DCore::QAspectJobPtr> RenderViewBuilder::buildJobHierachy() const
m_syncRenderCommandBuildingJob->addDependency(m_frustumCullingJob);
for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) {
- renderViewCommandBuilder->addDependency(m_renderer->updateSkinningPaletteJob());
- renderViewCommandBuilder->addDependency(m_renderer->updateWorldBoundingVolumeJob());
- renderViewCommandBuilder->addDependency(m_renderer->updateShaderDataTransformJob());
renderViewCommandBuilder->addDependency(m_syncRenderCommandBuildingJob);
-
m_syncRenderViewCommandBuildersJob->addDependency(renderViewCommandBuilder);
}
- m_renderer->prepareCommandSubmissionBarrierJob()->addDependency(m_syncRenderViewCommandBuildersJob);
m_renderer->frameCleanupJob()->addDependency(m_syncRenderViewCommandBuildersJob);
m_renderer->frameCleanupJob()->addDependency(m_setClearDrawBufferIndexJob);
diff --git a/src/render/frontend/qrenderaspect.cpp b/src/render/frontend/qrenderaspect.cpp
index 5e1fea288..e5cce4c7b 100644
--- a/src/render/frontend/qrenderaspect.cpp
+++ b/src/render/frontend/qrenderaspect.cpp
@@ -153,8 +153,6 @@
#include <private/qrenderpluginfactory_p.h>
#include <private/qrenderplugin_p.h>
-#include <private/framegraphvisitor_p.h>
-#include <private/platformsurfacefilter_p.h>
#include <Qt3DCore/qentity.h>
#include <Qt3DCore/qtransform.h>
@@ -190,12 +188,7 @@ QRenderAspectPrivate::QRenderAspectPrivate(QRenderAspect::RenderType type)
, m_initialized(false)
, m_renderType(type)
, m_offscreenHelper(nullptr)
- , m_blockingRendermode(false)
{
- // The blocking mode can be enabled to make sure we render even while
- // waiting for geometries to load. This is necessary if we want
- // to call doRender for every QtQuick frame when using Scene3D frame.
- m_blockingRendermode = !qgetenv("SCENE3D_BLOCKING_RENDERMODE").isEmpty();
m_instances.append(this);
loadSceneParsers();
}
@@ -381,11 +374,6 @@ void QRenderAspectPrivate::registerBackendType(const QMetaObject &obj,
q->registerBackendType(obj, functor);
}
-void QRenderAspectPrivate::abortRenderJobs()
-{
- m_renderer->abortRenderJobs();
-}
-
/*!
* The constructor creates a new QRenderAspect::QRenderAspect instance with the
* specified \a parent.
@@ -424,17 +412,9 @@ void QRenderAspectPrivate::renderInitialize(QOpenGLContext *context)
}
/*! \internal */
-void QRenderAspectPrivate::tryRenderSynchronous()
+void QRenderAspectPrivate::renderSynchronous(bool blocking)
{
- // If the render aspect is slow for some reason and does not build jobs
- // immediately, we might want to wait for it to make sure we render
- // one Qt3D frame for each QtQuick frame. If blocking mode is enabled,
- // we will wait a short time.
- // TODO By allowing the aspect thread to skip a couple of frames that
- // are not rendered without being in sync with the QtQuick scene graph,
- // we can make this into a blocking call and get rid of the timeout.
- if (m_renderer->tryWaitForRenderJobs(m_blockingRendermode ? 20 : 0))
- m_renderer->lockSurfaceAndRender();
+ m_renderer->doRender(blocking);
}
/*!
@@ -470,16 +450,36 @@ QVector<Qt3DCore::QAspectJobPtr> QRenderAspect::jobsToExecute(qint64 time)
// asked for jobs to execute (this function). If that is the case, the RenderSettings will
// be null and we should not generate any jobs.
if (d->m_renderer->isRunning() && d->m_renderer->settings()) {
+
Render::NodeManagers *manager = d->m_renderer->nodeManagers();
- QAspectJobPtr assetLoadingSync = d->m_renderer->syncSkeletonLoadingJob();
- assetLoadingSync->removeDependency(QWeakPointer<QAspectJob>());
- // Launch skeleton loader jobs
+ QAspectJobPtr textureLoadingSync = d->m_renderer->syncTextureLoadingJob();
+ textureLoadingSync->removeDependency(QWeakPointer<QAspectJob>());
+
+ // Launch texture generator jobs
+ const QVector<QTextureImageDataGeneratorPtr> pendingImgGen = manager->textureImageDataManager()->pendingGenerators();
+ for (const QTextureImageDataGeneratorPtr &imgGen : pendingImgGen) {
+ auto loadTextureJob = Render::LoadTextureDataJobPtr::create(imgGen);
+ textureLoadingSync->addDependency(loadTextureJob);
+ loadTextureJob->setNodeManagers(manager);
+ jobs.append(loadTextureJob);
+ }
+ const QVector<QTextureGeneratorPtr> pendingTexGen = manager->textureDataManager()->pendingGenerators();
+ for (const QTextureGeneratorPtr &texGen : pendingTexGen) {
+ auto loadTextureJob = Render::LoadTextureDataJobPtr::create(texGen);
+ textureLoadingSync->addDependency(loadTextureJob);
+ loadTextureJob->setNodeManagers(manager);
+ jobs.append(loadTextureJob);
+ }
+
+ // Launch skeleton loader jobs. We join on the syncTextureLoadingJob for now
+ // which should likely be renamed to something more generic or we introduce
+ // another synchronizing job for skeleton loading
const QVector<Render::HSkeleton> skeletonsToLoad =
manager->skeletonManager()->dirtySkeletons(Render::SkeletonManager::SkeletonDataDirty);
for (const auto &skeletonHandle : skeletonsToLoad) {
auto loadSkeletonJob = Render::LoadSkeletonJobPtr::create(skeletonHandle);
loadSkeletonJob->setNodeManagers(manager);
- assetLoadingSync->addDependency(loadSkeletonJob);
+ textureLoadingSync->addDependency(loadSkeletonJob);
jobs.append(loadSkeletonJob);
}
@@ -496,6 +496,7 @@ QVector<Qt3DCore::QAspectJobPtr> QRenderAspect::jobsToExecute(qint64 time)
const QVector<QAspectJobPtr> geometryJobs = d->createGeometryRendererJobs();
jobs.append(geometryJobs);
+
// Add all jobs to queue
// Note: the getter is also responsible for returning a job ready to run
jobs.append(d->m_renderer->pickBoundingVolumeJob());
@@ -510,16 +511,12 @@ QVector<Qt3DCore::QAspectJobPtr> QRenderAspect::jobsToExecute(qint64 time)
return jobs;
}
- // Let the rendering thread know that we are ready to spawn jobs if it
- // can promise us that it will in fact call doRender.
- if (d->m_renderer->releaseRendererAndRequestPromiseToRender()) {
- // Traverse the current framegraph and create jobs to populate
- // RenderBins with RenderCommands
- // All jobs needed to create the frame and their dependencies are set by
- // renderBinJobs()
- const QVector<QAspectJobPtr> renderBinJobs = d->m_renderer->renderBinJobs();
- jobs.append(renderBinJobs);
- }
+ // Traverse the current framegraph and create jobs to populate
+ // RenderBins with RenderCommands
+ // All jobs needed to create the frame and their dependencies are set by
+ // renderBinJobs()
+ const QVector<QAspectJobPtr> renderBinJobs = d->m_renderer->renderBinJobs();
+ jobs.append(renderBinJobs);
}
return jobs;
}
diff --git a/src/render/frontend/qrenderaspect_p.h b/src/render/frontend/qrenderaspect_p.h
index d18ce8b4f..26ca091f6 100644
--- a/src/render/frontend/qrenderaspect_p.h
+++ b/src/render/frontend/qrenderaspect_p.h
@@ -90,10 +90,9 @@ public:
void loadSceneParsers();
void loadRenderPlugin(const QString &pluginName);
void renderInitialize(QOpenGLContext *context);
- void tryRenderSynchronous();
+ void renderSynchronous(bool blocking = false);
void renderShutdown();
void registerBackendType(const QMetaObject &, const Qt3DCore::QBackendNodeMapperPtr &functor);
- void abortRenderJobs();
QVector<Qt3DCore::QAspectJobPtr> createGeometryRendererJobs();
Render::NodeManagers *m_nodeManagers;
@@ -105,7 +104,6 @@ public:
QVector<Render::QRenderPlugin *> m_renderPlugins;
QRenderAspect::RenderType m_renderType;
Render::OffscreenSurfaceHelper *m_offscreenHelper;
- bool m_blockingRendermode;
static QMutex m_pluginLock;
static QVector<QString> m_pluginConfig;
diff --git a/src/render/geometry/geometryrenderer.cpp b/src/render/geometry/geometryrenderer.cpp
index a73589507..ea059c6ee 100644
--- a/src/render/geometry/geometryrenderer.cpp
+++ b/src/render/geometry/geometryrenderer.cpp
@@ -42,8 +42,6 @@
#include <Qt3DRender/private/qboundingvolume_p.h>
#include <Qt3DRender/private/qgeometryrenderer_p.h>
#include <Qt3DRender/private/qmesh_p.h>
-#include <Qt3DRender/private/managers_p.h>
-#include <Qt3DRender/private/nodemanagers_p.h>
#include <Qt3DCore/qpropertyupdatedchange.h>
#include <Qt3DCore/qpropertynodeaddedchange.h>
#include <Qt3DCore/qpropertynoderemovedchange.h>
diff --git a/src/render/graphicshelpers/graphicscontext.cpp b/src/render/graphicshelpers/graphicscontext.cpp
index a8fdcefa9..d742865c4 100644
--- a/src/render/graphicshelpers/graphicscontext.cpp
+++ b/src/render/graphicshelpers/graphicscontext.cpp
@@ -232,10 +232,13 @@ void GraphicsContext::resolveRenderTargetFormat()
#undef RGBA_BITS
}
-void GraphicsContext::beginDrawing()
+bool GraphicsContext::beginDrawing(QSurface *surface)
{
+ Q_ASSERT(surface);
Q_ASSERT(m_gl);
+ m_surface = surface;
+
// TO DO: Find a way to make to pause work if the window is not exposed
// if (m_surface && m_surface->surfaceClass() == QSurface::Window) {
// qDebug() << Q_FUNC_INFO << 1;
@@ -244,6 +247,12 @@ void GraphicsContext::beginDrawing()
// qDebug() << Q_FUNC_INFO << 2;
// }
+ // Makes the surface current on the OpenGLContext
+ // and sets the right glHelper
+ m_ownCurrent = !(m_gl->surface() == m_surface);
+ if (m_ownCurrent && !makeCurrent(m_surface))
+ return false;
+
// TODO: cache surface format somewhere rather than doing this every time render surface changes
resolveRenderTargetFormat();
@@ -284,6 +293,8 @@ void GraphicsContext::beginDrawing()
const int shaderPurgePeriod = 600;
if (callCount % shaderPurgePeriod == 0)
m_shaderCache.purge();
+
+ return true;
}
void GraphicsContext::clearBackBuffer(QClearBuffers::BufferTypeFlags buffers)
@@ -442,7 +453,6 @@ bool GraphicsContext::makeCurrent(QSurface *surface)
m_glHelper = resolveHighestOpenGLFunctions();
m_glHelpers.insert(surface, m_glHelper);
}
- m_surface = surface;
return true;
}
@@ -534,6 +544,7 @@ void GraphicsContext::loadShader(Shader *shader, ShaderManager *manager)
shader->setGraphicsContext(this);
shader->setLoaded(true);
+ shader->markDirty(AbstractRenderer::AllDirty);
}
}
diff --git a/src/render/graphicshelpers/graphicscontext_p.h b/src/render/graphicshelpers/graphicscontext_p.h
index bca841f53..24b08e45e 100644
--- a/src/render/graphicshelpers/graphicscontext_p.h
+++ b/src/render/graphicshelpers/graphicscontext_p.h
@@ -113,7 +113,7 @@ public:
int id() const; // unique, small integer ID of this context
- void beginDrawing();
+ bool beginDrawing(QSurface *surface);
void clearBackBuffer(QClearBuffers::BufferTypeFlags buffers);
void endDrawing(bool swapBuffers);
diff --git a/src/render/jobs/job_common_p.h b/src/render/jobs/job_common_p.h
index 7fd88a697..648f07a9b 100644
--- a/src/render/jobs/job_common_p.h
+++ b/src/render/jobs/job_common_p.h
@@ -100,17 +100,12 @@ namespace JobTypes {
UpdateMeshTriangleList,
FilterCompatibleTechniques,
UpdateLevelOfDetail,
- SyncSkeletonLoading,
+ SyncTextureLoading,
LoadSkeleton,
UpdateSkinningPalette,
ProximityFiltering,
SyncFilterEntityByLayer,
SyncMaterialGatherer,
- ReadRenderQueueSizeBarrier,
- BeginDrawingBarrier,
- UpdateGLResourcesBarrier,
- PrepareCommandSubmissionBarrier,
- EndDrawingBarrier
};
} // JobTypes
diff --git a/src/render/jobs/loadtexturedatajob.cpp b/src/render/jobs/loadtexturedatajob.cpp
index 790e41998..55232d74f 100644
--- a/src/render/jobs/loadtexturedatajob.cpp
+++ b/src/render/jobs/loadtexturedatajob.cpp
@@ -49,7 +49,16 @@ QT_BEGIN_NAMESPACE
namespace Qt3DRender {
namespace Render {
-LoadTextureDataJob::LoadTextureDataJob()
+LoadTextureDataJob::LoadTextureDataJob(const QTextureGeneratorPtr &texGen)
+ : m_texGen(texGen)
+ , m_imgDataGen(nullptr)
+{
+ SET_JOB_RUN_STAT_TYPE(this, JobTypes::LoadTextureData, 0);
+}
+
+LoadTextureDataJob::LoadTextureDataJob(const QTextureImageDataGeneratorPtr &imgDataGen)
+ : m_texGen(nullptr)
+ , m_imgDataGen(imgDataGen)
{
SET_JOB_RUN_STAT_TYPE(this, JobTypes::LoadTextureData, 0);
}
@@ -60,20 +69,13 @@ LoadTextureDataJob::~LoadTextureDataJob()
void LoadTextureDataJob::run()
{
- // NOTE: This must run after Renderer::updateGLResources(),
- // because that is where pendingGenerators is populated.
- // We are therefore not able to create one job for each texture
- // before we add the ability for running jobs (like this) to
- // spawn new jobs.
- const QVector<QTextureImageDataGeneratorPtr> pendingImgGen = m_manager->textureImageDataManager()->pendingGenerators();
- for (const QTextureImageDataGeneratorPtr &imgGen : pendingImgGen) {
- QTextureImageDataPtr imgData = (*imgGen)();
- m_manager->textureImageDataManager()->assignData(imgGen, imgData);
+ if (m_texGen) {
+ QTextureDataPtr texData = (*m_texGen)();
+ m_manager->textureDataManager()->assignData(m_texGen, texData);
}
- const QVector<QTextureGeneratorPtr> pendingTexGen = m_manager->textureDataManager()->pendingGenerators();
- for (const QTextureGeneratorPtr &texGen : pendingTexGen) {
- QTextureDataPtr texData = (*texGen)();
- m_manager->textureDataManager()->assignData(texGen, texData);
+ if (m_imgDataGen) {
+ QTextureImageDataPtr imgData = (*m_imgDataGen)();
+ m_manager->textureImageDataManager()->assignData(m_imgDataGen, imgData);
}
}
diff --git a/src/render/jobs/loadtexturedatajob_p.h b/src/render/jobs/loadtexturedatajob_p.h
index 7272bbc1d..607068712 100644
--- a/src/render/jobs/loadtexturedatajob_p.h
+++ b/src/render/jobs/loadtexturedatajob_p.h
@@ -67,7 +67,8 @@ class NodeManagers;
class LoadTextureDataJob : public Qt3DCore::QAspectJob
{
public:
- LoadTextureDataJob();
+ LoadTextureDataJob(const QTextureGeneratorPtr &texGen);
+ LoadTextureDataJob(const QTextureImageDataGeneratorPtr &imgDataGen);
~LoadTextureDataJob();
inline void setNodeManagers(NodeManagers *manager) { m_manager = manager; }
@@ -76,6 +77,9 @@ protected:
void run() final;
private:
+ QTextureGeneratorPtr m_texGen;
+ QTextureImageDataGeneratorPtr m_imgDataGen;
+
NodeManagers *m_manager;
};
diff --git a/src/render/services/vsyncframeadvanceservice.cpp b/src/render/services/vsyncframeadvanceservice.cpp
index c8437e20d..8749a54ab 100644
--- a/src/render/services/vsyncframeadvanceservice.cpp
+++ b/src/render/services/vsyncframeadvanceservice.cpp
@@ -89,7 +89,7 @@ qint64 VSyncFrameAdvanceService::waitForNextFrame()
if (d->m_drivenByRenderThread)
d->m_semaphore.acquire(1);
else
- d->m_semaphore.acquire(d->m_semaphore.available());
+ d->m_semaphore.acquire(d->m_semaphore.available() + 1);
const quint64 currentTime = d->m_elapsed.nsecsElapsed();
qCDebug(VSyncAdvanceService) << "Elapsed nsecs since last call " << currentTime - d->m_elapsedTimeSincePreviousFrame;
diff --git a/tests/auto/render/commons/testrenderer.h b/tests/auto/render/commons/testrenderer.h
index 9624f129a..b3f602dc2 100644
--- a/tests/auto/render/commons/testrenderer.h
+++ b/tests/auto/render/commons/testrenderer.h
@@ -52,7 +52,7 @@ public:
void shutdown() override {}
void releaseGraphicsResources() override {}
void render() override {}
- void doRender() override {}
+ void doRender(bool scene3dBlocking = false) override { Q_UNUSED(scene3dBlocking); }
void cleanGraphicsResources() override {}
bool isRunning() const override { return true; }
bool shouldRender() override { return true; }
@@ -60,7 +60,7 @@ public:
QVector<Qt3DCore::QAspectJobPtr> renderBinJobs() override { return QVector<Qt3DCore::QAspectJobPtr>(); }
Qt3DCore::QAspectJobPtr pickBoundingVolumeJob() override { return Qt3DCore::QAspectJobPtr(); }
Qt3DCore::QAspectJobPtr rayCastingJob() override { return Qt3DCore::QAspectJobPtr(); }
- Qt3DCore::QAspectJobPtr syncSkeletonLoadingJob() override { return Qt3DCore::QAspectJobPtr(); }
+ Qt3DCore::QAspectJobPtr syncTextureLoadingJob() override { return Qt3DCore::QAspectJobPtr(); }
Qt3DCore::QAspectJobPtr expandBoundingVolumeJob() override { return Qt3DCore::QAspectJobPtr(); }
void setSceneRoot(Qt3DCore::QBackendNodeFactory *factory, Qt3DRender::Render::Entity *root) override { Q_UNUSED(factory); Q_UNUSED(root); }
Qt3DRender::Render::Entity *sceneRoot() const override { return nullptr; }
@@ -81,12 +81,6 @@ public:
void setOffscreenSurfaceHelper(Qt3DRender::Render::OffscreenSurfaceHelper *helper) override;
QSurfaceFormat format() override;
- void lockSurfaceAndRender() override {}
- bool releaseRendererAndRequestPromiseToRender() override { return true; }
- bool waitForRenderJobs() override { return true; }
- bool tryWaitForRenderJobs(int timeout) override { Q_UNUSED(timeout); return true; }
- void abortRenderJobs() override {}
-
protected:
Qt3DRender::Render::AbstractRenderer::BackendNodeDirtySet m_changes;
Qt3DRender::Render::NodeManagers *m_managers;
diff --git a/tests/auto/render/filtercompatibletechniquejob/tst_filtercompatibletechniquejob.cpp b/tests/auto/render/filtercompatibletechniquejob/tst_filtercompatibletechniquejob.cpp
index 803d57fa9..4d4a08a34 100644
--- a/tests/auto/render/filtercompatibletechniquejob/tst_filtercompatibletechniquejob.cpp
+++ b/tests/auto/render/filtercompatibletechniquejob/tst_filtercompatibletechniquejob.cpp
@@ -93,8 +93,7 @@ public:
{
renderer()->setOpenGLContext(&m_glContext);
d_func()->m_renderer->initialize();
- renderer()->graphicsContext()->makeCurrent(m_window.data());
- renderer()->graphicsContext()->beginDrawing();
+ renderer()->graphicsContext()->beginDrawing(m_window.data());
}
Render::Renderer *renderer() const
diff --git a/tests/auto/render/geometryrenderer/tst_geometryrenderer.cpp b/tests/auto/render/geometryrenderer/tst_geometryrenderer.cpp
index e61fdf6e5..d07ec9914 100644
--- a/tests/auto/render/geometryrenderer/tst_geometryrenderer.cpp
+++ b/tests/auto/render/geometryrenderer/tst_geometryrenderer.cpp
@@ -30,7 +30,6 @@
#include <qbackendnodetester.h>
#include <Qt3DRender/private/geometryrenderer_p.h>
#include <Qt3DRender/private/geometryrenderermanager_p.h>
-#include <Qt3DRender/private/nodemanagers_p.h>
#include <Qt3DRender/qgeometry.h>
#include <Qt3DRender/qgeometryfactory.h>
#include <Qt3DCore/qpropertyupdatedchange.h>
@@ -187,8 +186,6 @@ private Q_SLOTS:
// GIVEN
Qt3DRender::Render::GeometryRenderer renderGeometryRenderer;
TestRenderer renderer;
- Qt3DRender::Render::NodeManagers nodeManagers;
-
renderGeometryRenderer.setRenderer(&renderer);
QVERIFY(!renderGeometryRenderer.isDirty());
diff --git a/tests/auto/render/render.pro b/tests/auto/render/render.pro
index 4045b577a..6cb9aefcf 100644
--- a/tests/auto/render/render.pro
+++ b/tests/auto/render/render.pro
@@ -110,8 +110,7 @@ qtConfig(private_tests) {
raycaster \
qscreenraycaster \
raycastingjob \
- qcamera \
- renderbarrierjob
+ qcamera
QT_FOR_CONFIG = 3dcore-private
# TO DO: These could be restored to be executed in all cases
diff --git a/tests/auto/render/renderbarrierjob/renderbarrierjob.pro b/tests/auto/render/renderbarrierjob/renderbarrierjob.pro
deleted file mode 100644
index 5a988144f..000000000
--- a/tests/auto/render/renderbarrierjob/renderbarrierjob.pro
+++ /dev/null
@@ -1,9 +0,0 @@
-TEMPLATE = app
-
-TARGET = tst_renderbarrierjob
-
-QT += 3dcore 3dcore-private 3drender 3drender-private testlib
-
-CONFIG += testcase
-
-SOURCES += tst_renderbarrierjob.cpp
diff --git a/tests/auto/render/renderbarrierjob/tst_renderbarrierjob.cpp b/tests/auto/render/renderbarrierjob/tst_renderbarrierjob.cpp
deleted file mode 100644
index e3b49edd4..000000000
--- a/tests/auto/render/renderbarrierjob/tst_renderbarrierjob.cpp
+++ /dev/null
@@ -1,118 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt3D module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtTest/QtTest>
-#include <QMutex>
-#include <QWaitCondition>
-#include <QThread>
-#include <Qt3DRender/private/renderbarrierjob_p.h>
-#include <Qt3DRender/private/job_common_p.h>
-
-class TestRenderThread : public QThread
-{
-public:
- TestRenderThread(Qt3DRender::Render::RenderBarrierJobPtr barrier)
- : m_barrier(barrier)
- {}
-
- void run() override
- {
- m_started.store(1);
- m_writeSemaphore.release();
- m_readSemaphore.acquire();
-
- m_barrier->allowToProceed();
- m_released.store(1);
- m_writeSemaphore.release();
- m_readSemaphore.acquire();
-
- m_barrier->waitForDependencies();
- m_completed.store(1);
- }
-
- Qt3DRender::Render::RenderBarrierJobPtr m_barrier;
- QSemaphore m_writeSemaphore;
- QSemaphore m_readSemaphore;
-
- QAtomicInt m_started;
- QAtomicInt m_released;
- QAtomicInt m_completed;
-};
-
-class tst_RenderBarrierJob : public QObject
-{
- Q_OBJECT
-public :
- tst_RenderBarrierJob() {}
- ~tst_RenderBarrierJob() {}
-
-private Q_SLOTS:
- void shouldControlRenderThread()
- {
- // GIVEN a barrier (of any type)
- auto barrier = Qt3DRender::Render::RenderBarrierJobPtr::create(Qt3DRender::Render::JobTypes::BeginDrawingBarrier);
- TestRenderThread testThread(barrier);
-
- // THEN
- QVERIFY(testThread.m_started.load() == 0);
- QVERIFY(testThread.m_released.load() == 0);
- QVERIFY(testThread.m_completed.load() == 0);
-
- // WHEN
- testThread.start();
- testThread.m_writeSemaphore.acquire();
-
- // THEN thread should have started
- QVERIFY(testThread.m_started.load() == 1);
- QVERIFY(testThread.m_released.load() == 0);
- QVERIFY(testThread.m_completed.load() == 0);
- testThread.m_readSemaphore.release();
-
- // WHEN
- testThread.m_writeSemaphore.acquire();
-
- // THEN job should be released
- QVERIFY(testThread.m_started.load() == 1);
- QVERIFY(testThread.m_released.load() == 1);
- QVERIFY(testThread.m_completed.load() == 0);
- testThread.m_readSemaphore.release();
-
- // WHEN job runs and we wait for thread
- barrier->run();
- testThread.wait();
-
- // THEN thread should be finished
- QVERIFY(testThread.m_started.load() == 1);
- QVERIFY(testThread.m_released.load() == 1);
- QVERIFY(testThread.m_completed.load() == 1);
- }
-};
-
-QTEST_MAIN(tst_RenderBarrierJob)
-
-#include "tst_renderbarrierjob.moc"
diff --git a/tests/auto/render/renderer/tst_renderer.cpp b/tests/auto/render/renderer/tst_renderer.cpp
index b941a2ac4..27708c1b9 100644
--- a/tests/auto/render/renderer/tst_renderer.cpp
+++ b/tests/auto/render/renderer/tst_renderer.cpp
@@ -34,9 +34,6 @@
#include <Qt3DRender/private/viewportnode_p.h>
#include <Qt3DRender/private/renderview_p.h>
#include <Qt3DRender/private/renderviewbuilder_p.h>
-#include <Qt3DRender/private/offscreensurfacehelper_p.h>
-#include <Qt3DRender/private/loadtexturedatajob_p.h>
-#include <Qt3DRender/private/renderbarrierjob_p.h>
class tst_Renderer : public QObject
{
@@ -66,62 +63,46 @@ private Q_SLOTS:
// NOTE: FilterCompatibleTechniqueJob and ShaderGathererJob cannot run because the context
// is not initialized in this test
- const int renderBarrierJobCount = 5;
- // Barriers: readRenderQueueSize
- // beginDrawing
- // updateGLResources
- // prepareCommandSubmission
- // endDrawing
-
- const int renderViewJobCount = 13 + 1 * Qt3DRender::Render::RenderViewBuilder::optimalJobCount();
+ const int singleRenderViewJobCount = 11 + 1 * Qt3DRender::Render::RenderViewBuilder::optimalJobCount();
// RenderViewBuilder renderViewJob,
// renderableEntityFilterJob,
// lightGatherJob,
// computableEntityFilterJob,
// syncRenderViewInitializationJob,
- // filterEntityByLayerJob,
- // syncFilterEntityByLayerJob,
// syncFrustumCullingJob,
// filterProximityJob,
// setClearDrawBufferIndexJob,
// frustumCullingJob,
// syncRenderCommandBuldingJob,
// syncRenderViewCommandBuilderJob
- // n * RenderViewCommandBuildJobs
-
- const int flagIndependentRendererJobCount = 6;
- // Flag independent: updateLevelOfDetailJob
- // cleanupJob
- // sendRenderCaptureJob
- // sendBufferCaptureJob
- // VAOGatherer
- // updateSkinningPaletteJob
-
- const int flagIndependetRenderViewJobCount = renderViewJobCount - 2;
- // Cached: filterEntityByLayerJob,
- // syncFilterEntityByLayerJob,
+ // n * (RenderViewCommandBuildJobs)
-
- // WHEN (nothing dirty, no buffers)
+ // WHEN (nothing dirty, no buffers, no layers to be rebuilt, no materials to be rebuilt)
QVector<Qt3DCore::QAspectJobPtr> jobs = renderer.renderBinJobs();
- // THEN
+ // THEN (level
QCOMPARE(jobs.size(),
- flagIndependetRenderViewJobCount +
- renderBarrierJobCount +
- flagIndependentRendererJobCount);
+ 1 + // updateLevelOfDetailJob
+ 1 + // cleanupJob
+ 1 + // sendRenderCaptureJob
+ 1 + // sendBufferCaptureJob
+ 1 + // VAOGatherer
+ 1 + // updateSkinningPaletteJob
+ singleRenderViewJobCount); // Only valid for the first call to renderBinJobs(), since subsequent calls won't have the renderqueue reset
+
// WHEN
renderer.markDirty(Qt3DRender::Render::AbstractRenderer::EntityEnabledDirty, nullptr);
jobs = renderer.renderBinJobs();
- // THEN
+ // THEN (level
QCOMPARE(jobs.size(),
- flagIndependetRenderViewJobCount +
- renderBarrierJobCount +
- flagIndependentRendererJobCount +
- 1 + // filterEntityByLayerJob
- 1 + // syncFilterEntityByLayerJob
+ 1 + // updateLevelOfDetailJob
+ 1 + // cleanupJob
+ 1 + // sendRenderCaptureJob
+ 1 + // sendBufferCaptureJob
+ 1 + // VAOGatherer
+ 1 + // updateSkinningPaletteJob
1); // EntityEnabledDirty
renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
@@ -130,14 +111,17 @@ private Q_SLOTS:
renderer.markDirty(Qt3DRender::Render::AbstractRenderer::TransformDirty, nullptr);
jobs = renderer.renderBinJobs();
- // THEN
+ // THEN (level
QCOMPARE(jobs.size(),
- flagIndependetRenderViewJobCount +
- renderBarrierJobCount +
- flagIndependentRendererJobCount +
+ 1 + // updateLevelOfDetailJob
+ 1 + // cleanupJob
+ 1 + // sendRenderCaptureJob
+ 1 + // sendBufferCaptureJob
+ 1 + // VAOGatherer
1 + // WorldTransformJob
1 + // UpdateWorldBoundingVolume
1 + // UpdateShaderDataTransform
+ 1 + // updateSkinningPaletteJob
1); // ExpandBoundingVolumeJob
renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
@@ -146,13 +130,16 @@ private Q_SLOTS:
renderer.markDirty(Qt3DRender::Render::AbstractRenderer::GeometryDirty, nullptr);
jobs = renderer.renderBinJobs();
- // THEN
+ // THEN (level
QCOMPARE(jobs.size(),
- flagIndependetRenderViewJobCount +
- renderBarrierJobCount +
- flagIndependentRendererJobCount +
+ 1 + // updateLevelOfDetailJob
+ 1 + // cleanupJob
+ 1 + // sendRenderCaptureJob
+ 1 + // sendBufferCaptureJob
+ 1 + // VAOGatherer
1 + // CalculateBoundingVolumeJob
1 + // UpdateMeshTriangleListJob
+ 1 + // updateSkinningPaletteJob
1); // ExpandBoundingVolumeJob
renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
@@ -161,11 +148,14 @@ private Q_SLOTS:
renderer.markDirty(Qt3DRender::Render::AbstractRenderer::BuffersDirty, nullptr);
jobs = renderer.renderBinJobs();
- // THEN
+ // THEN (level
QCOMPARE(jobs.size(),
- flagIndependetRenderViewJobCount +
- renderBarrierJobCount +
- flagIndependentRendererJobCount +
+ 1 + // updateLevelOfDetailJob
+ 1 + // cleanupJob
+ 1 + // sendRenderCaptureJob
+ 1 + // sendBufferCaptureJob
+ 1 + // VAOGatherer
+ 1 + // updateSkinningPaletteJob
1); // BufferGathererJob
renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
@@ -174,26 +164,16 @@ private Q_SLOTS:
renderer.markDirty(Qt3DRender::Render::AbstractRenderer::TexturesDirty, nullptr);
jobs = renderer.renderBinJobs();
- // THEN
+ // THEN (level
QCOMPARE(jobs.size(),
- flagIndependetRenderViewJobCount +
- renderBarrierJobCount +
- flagIndependentRendererJobCount +
- 1 + // LoadTextureDataJob
- 1); // TexturesGathererJob
-
- renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
-
- // WHEN
- renderer.markDirty(Qt3DRender::Render::AbstractRenderer::SkeletonDataDirty, nullptr);
- jobs = renderer.renderBinJobs();
-
- // THEN
- QCOMPARE(jobs.size(),
- flagIndependetRenderViewJobCount +
- renderBarrierJobCount +
- flagIndependentRendererJobCount +
- 1); // SyncSkeletonLoadingJob
+ 1 + // updateLevelOfDetailJob
+ 1 + // cleanupJob
+ 1 + // sendRenderCaptureJob
+ 1 + // sendBufferCaptureJob
+ 1 + // VAOGatherer
+ 1 + // TexturesGathererJob
+ 1 + // updateSkinningPaletteJob
+ 1); // SyncTexturesGathererJob
renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
@@ -201,47 +181,30 @@ private Q_SLOTS:
renderer.markDirty(Qt3DRender::Render::AbstractRenderer::AllDirty, nullptr);
jobs = renderer.renderBinJobs();
- // THEN
+ // THEN (Renderer is not initialized so FilterCompatibleTechniqueJob
+ // and ShaderGathererJob are not added here)
QCOMPARE(jobs.size(),
- renderViewJobCount +
- renderBarrierJobCount +
- flagIndependentRendererJobCount +
- 1 + // UpdateTreeEnablee
+ 1 + // EntityEnabledDirty
1 + // WorldTransformJob
1 + // UpdateWorldBoundingVolume
1 + // UpdateShaderDataTransform
+ 1 + // ExpandBoundingVolumeJob
1 + // CalculateBoundingVolumeJob
1 + // UpdateMeshTriangleListJob
- 1 + // ExpandBoundingVolumeJob
+ 1 + // updateSkinningPaletteJob
+ 1 + // updateLevelOfDetailJob
+ 1 + // cleanupJob
+ 1 + // sendRenderCaptureJob
+ 1 + // sendBufferCaptureJob
+ 1 + // VAOGatherer
1 + // BufferGathererJob
- 1 + // LoadTextureDataJob
1 + // TexturesGathererJob
- 1 + // SyncSkeletonLoadingJob
- 1 + // FilterCompatibleTechniqueJob
- 1 + // ShaderGathererJob
- Qt3DRender::Render::RenderViewBuilder::optimalJobCount() + // MaterialGathererJobs
- 1); // syncMaterialGathererJob
+ 1 // SyncTextureLoadingJob
+ );
renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
- }
-
- void checkRenderBarrierJobDependencies()
- {
- // GIVEN
- Qt3DRender::Render::Renderer renderer(Qt3DRender::QRenderAspect::Synchronous);
-
- // THEN
- // internal dependencies
- QVERIFY(renderer.beginDrawingBarrierJob()->dependencies().contains(renderer.readRenderQueueSizeBarrierJob()));
- QVERIFY(renderer.updateGLResourcesBarrierJob()->dependencies().contains(renderer.beginDrawingBarrierJob()));
- QVERIFY(renderer.prepareCommandSubmissionBarrierJob()->dependencies().contains(renderer.updateGLResourcesBarrierJob()));
- QVERIFY(renderer.endDrawingBarrierJob()->dependencies().contains(renderer.prepareCommandSubmissionBarrierJob()));
- QVERIFY(renderer.filterCompatibleTechniqueJob()->dependencies().contains(renderer.beginDrawingBarrierJob()));
- QVERIFY(renderer.loadTextureJob()->dependencies().contains(renderer.updateGLResourcesBarrierJob()));
- QVERIFY(renderer.prepareCommandSubmissionBarrierJob()->dependencies().contains(renderer.loadTextureJob()));
- QVERIFY(renderer.updateGLResourcesBarrierJob()->dependencies().contains(renderer.filterCompatibleTechniqueJob()));
}
};
diff --git a/tests/auto/render/renderviewbuilder/tst_renderviewbuilder.cpp b/tests/auto/render/renderviewbuilder/tst_renderviewbuilder.cpp
index 1bdf20170..be1f1fb5d 100644
--- a/tests/auto/render/renderviewbuilder/tst_renderviewbuilder.cpp
+++ b/tests/auto/render/renderviewbuilder/tst_renderviewbuilder.cpp
@@ -49,7 +49,6 @@
#include <Qt3DRender/private/qrenderaspect_p.h>
#include <Qt3DRender/private/nodemanagers_p.h>
#include <Qt3DRender/private/managers_p.h>
-#include <Qt3DRender/private/renderbarrierjob_p.h>
QT_BEGIN_NAMESPACE
@@ -307,25 +306,21 @@ private Q_SLOTS:
QVERIFY(renderViewBuilder.frustumCullingJob()->dependencies().contains(renderViewBuilder.syncFrustumCullingJob()));
QVERIFY(renderViewBuilder.frustumCullingJob()->dependencies().contains(testAspect.renderer()->expandBoundingVolumeJob()));
- QCOMPARE(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().size(), renderViewBuilder.materialGathererJobs().size() + 7);
+ QCOMPARE(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().size(), renderViewBuilder.materialGathererJobs().size() + 6);
QVERIFY(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().contains(renderViewBuilder.syncRenderViewInitializationJob()));
QVERIFY(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().contains(renderViewBuilder.renderableEntityFilterJob()));
QVERIFY(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().contains(renderViewBuilder.computableEntityFilterJob()));
QVERIFY(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().contains(renderViewBuilder.filterProximityJob()));
QVERIFY(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().contains(renderViewBuilder.lightGathererJob()));
QVERIFY(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().contains(renderViewBuilder.frustumCullingJob()));
- QVERIFY(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().contains(testAspect.renderer()->updateGLResourcesBarrierJob()));
for (const auto materialGatherer : renderViewBuilder.materialGathererJobs()) {
QVERIFY(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().contains(materialGatherer));
}
// Step 5
for (const auto renderViewBuilderJob : renderViewBuilder.renderViewBuilderJobs()) {
- QCOMPARE(renderViewBuilderJob->dependencies().size(), 4);
- QVERIFY(renderViewBuilderJob->dependencies().contains(renderViewBuilder.syncRenderCommandBuildingJob()));
- QVERIFY(renderViewBuilderJob->dependencies().contains(testAspect.renderer()->updateSkinningPaletteJob()));
- QVERIFY(renderViewBuilderJob->dependencies().contains(testAspect.renderer()->updateWorldBoundingVolumeJob()));
- QVERIFY(renderViewBuilderJob->dependencies().contains(testAspect.renderer()->updateShaderDataTransformJob()));
+ QCOMPARE(renderViewBuilderJob->dependencies().size(), 1);
+ QCOMPARE(renderViewBuilderJob->dependencies().first().data(), renderViewBuilder.syncRenderCommandBuildingJob().data());
}
// Step 6
@@ -383,7 +378,7 @@ private Q_SLOTS:
QVERIFY(renderViewBuilder.frustumCullingJob()->dependencies().contains(renderViewBuilder.syncFrustumCullingJob()));
QVERIFY(renderViewBuilder.frustumCullingJob()->dependencies().contains(testAspect.renderer()->expandBoundingVolumeJob()));
- QCOMPARE(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().size(), renderViewBuilder.materialGathererJobs().size() + 8);
+ QCOMPARE(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().size(), renderViewBuilder.materialGathererJobs().size() + 7);
QVERIFY(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().contains(renderViewBuilder.syncRenderViewInitializationJob()));
QVERIFY(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().contains(renderViewBuilder.renderableEntityFilterJob()));
QVERIFY(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().contains(renderViewBuilder.computableEntityFilterJob()));
@@ -391,18 +386,14 @@ private Q_SLOTS:
QVERIFY(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().contains(renderViewBuilder.lightGathererJob()));
QVERIFY(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().contains(renderViewBuilder.frustumCullingJob()));
QVERIFY(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().contains(renderViewBuilder.filterProximityJob()));
- QVERIFY(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().contains(testAspect.renderer()->updateGLResourcesBarrierJob()));
for (const auto materialGatherer : renderViewBuilder.materialGathererJobs()) {
QVERIFY(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().contains(materialGatherer));
}
// Step 5
for (const auto renderViewBuilderJob : renderViewBuilder.renderViewBuilderJobs()) {
- QCOMPARE(renderViewBuilderJob->dependencies().size(), 4);
- QVERIFY(renderViewBuilderJob->dependencies().contains(renderViewBuilder.syncRenderCommandBuildingJob()));
- QVERIFY(renderViewBuilderJob->dependencies().contains(testAspect.renderer()->updateSkinningPaletteJob()));
- QVERIFY(renderViewBuilderJob->dependencies().contains(testAspect.renderer()->updateWorldBoundingVolumeJob()));
- QVERIFY(renderViewBuilderJob->dependencies().contains(testAspect.renderer()->updateShaderDataTransformJob()));
+ QCOMPARE(renderViewBuilderJob->dependencies().size(), 1);
+ QCOMPARE(renderViewBuilderJob->dependencies().first().data(), renderViewBuilder.syncRenderCommandBuildingJob().data());
}
// Step 6