Revision control

++ // Copyright (C) 2016 Paul Lemire <paul.lemire350@gmail.com>
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++
++ #include <QtTest/QTest>
++ #include <Qt3DCore/private/qaspectjobmanager_p.h>
++ #include <Qt3DCore/private/qnodevisitor_p.h>
++ #include <Qt3DCore/private/qnode_p.h>
++
++ #include <renderviewbuilder_p.h>
++ #include <Qt3DRender/private/qrenderaspect_p.h>
++ #include <Qt3DRender/qviewport.h>
++ #include <Qt3DRender/qclearbuffers.h>
++ #include <Qt3DRender/qdispatchcompute.h>
++ #include <Qt3DRender/qfrustumculling.h>
++ #include <Qt3DRender/qmaterial.h>
++ #include <Qt3DRender/qspotlight.h>
++ #include <Qt3DRender/qpointlight.h>
++ #include <Qt3DRender/qenvironmentlight.h>
++ #include <Qt3DRender/qgeometryrenderer.h>
++ #include <Qt3DRender/qcomputecommand.h>
++ #include <Qt3DRender/qlayerfilter.h>
++ #include <Qt3DRender/qrenderpassfilter.h>
++ #include <Qt3DRender/qtechniquefilter.h>
++ #include <Qt3DRender/qcameraselector.h>
++ #include <Qt3DRender/qcamera.h>
++ #include <Qt3DRender/qlayer.h>
++ #include <Qt3DRender/private/qrenderaspect_p.h>
++ #include <Qt3DRender/private/nodemanagers_p.h>
++ #include <Qt3DRender/private/managers_p.h>
++ #include <Qt3DRender/private/filterentitybycomponentjob_p.h>
++ #include <Qt3DRender/private/qrenderaspect_p.h>
++ #include <Qt3DRender/private/renderviewjobutils_p.h>
++
++ QT_BEGIN_NAMESPACE
++
++ namespace Qt3DRender {
++
++ class TestAspect : public QRenderAspect
++ {
++ public:
++ TestAspect(Qt3DCore::QNode *root)
++ : QRenderAspect()
++ , m_jobManager(new Qt3DCore::QAspectJobManager())
++ {
++ Qt3DCore::QAbstractAspectPrivate::get(this)->m_jobManager = m_jobManager.data();
++ QRenderAspect::onRegistered();
++
++ QList<Qt3DCore::QNode *> nodes;
++ Qt3DCore::QNodeVisitor v;
++ v.traverse(root, [&nodes](Qt3DCore::QNode *node) {
++ Qt3DCore::QNodePrivate *d = Qt3DCore::QNodePrivate::get(node);
++ d->m_typeInfo = const_cast<QMetaObject*>(Qt3DCore::QNodePrivate::findStaticMetaObject(node->metaObject()));
++ d->m_hasBackendNode = true;
++ nodes << node;
++ });
++
++ for (const auto node: nodes)
++ d_func()->createBackendNode({
++ node->id(),
++ Qt3DCore::QNodePrivate::get(node)->m_typeInfo,
++ Qt3DCore::NodeTreeChange::Added,
++ node
++ });
++ }
++
++ ~TestAspect();
++
++ Qt3DRender::Render::NodeManagers *nodeManagers() const
++ {
++ return d_func()->m_renderer->nodeManagers();
++ }
++
++ Render::OpenGL::Renderer *renderer() const
++ {
++ return static_cast<Render::OpenGL::Renderer *>(d_func()->m_renderer);
++ }
++
++ Render::MaterialParameterGathererJobPtr materialGathererJob() const
++ {
++ Render::MaterialParameterGathererJobPtr job = Render::MaterialParameterGathererJobPtr::create();
++ job->setNodeManagers(nodeManagers());
++ return job;
++ }
++
++ void onRegistered() override { QRenderAspect::onRegistered(); }
++ void onUnregistered() override { QRenderAspect::onUnregistered(); }
++
++ private:
++ QScopedPointer<Qt3DCore::QAspectJobManager> m_jobManager;
++ };
++
++ TestAspect::~TestAspect()
++ {
++ QRenderAspect::onUnregistered();
++ }
++
++ } // namespace Qt3DRender
++
++ QT_END_NAMESPACE
++
++ namespace {
++
++ Qt3DCore::QEntity *buildSimpleScene(Qt3DRender::QFrameGraphNode *fg)
++ {
++ Qt3DCore::QEntity *root = new Qt3DCore::QEntity();
++
++ Qt3DRender::QRenderSettings* renderSettings = new Qt3DRender::QRenderSettings();
++ renderSettings->setActiveFrameGraph(fg);
++ root->addComponent(renderSettings);
++
++ // Scene
++ {
++ Qt3DCore::QEntity *e = new Qt3DCore::QEntity();
++ Qt3DRender::QMaterial *material = new Qt3DRender::QMaterial();
++ Qt3DRender::QGeometryRenderer *geometryRenderer = new Qt3DRender::QGeometryRenderer();
++ e->addComponent(material);
++ e->addComponent(geometryRenderer);
++ e->setParent(root);
++ }
++ {
++ Qt3DCore::QEntity *e = new Qt3DCore::QEntity();
++ Qt3DRender::QMaterial *material = new Qt3DRender::QMaterial();
++ Qt3DRender::QComputeCommand *computeCommand = new Qt3DRender::QComputeCommand();
++ e->addComponent(material);
++ e->addComponent(computeCommand);
++ e->setParent(root);
++ }
++
++ {
++ Qt3DCore::QEntity *e = new Qt3DCore::QEntity();
++ Qt3DRender::QPointLight *light = new Qt3DRender::QPointLight();
++ e->addComponent(light);
++ e->setParent(root);
++ }
++
++ {
++ Qt3DCore::QEntity *e = new Qt3DCore::QEntity();
++ Qt3DRender::QSpotLight *light = new Qt3DRender::QSpotLight();
++ e->addComponent(light);
++ e->setParent(root);
++ }
++
++ {
++ Qt3DCore::QEntity *e = new Qt3DCore::QEntity();
++ Qt3DRender::QEnvironmentLight *light = new Qt3DRender::QEnvironmentLight();
++ e->addComponent(light);
++ e->setParent(root);
++ }
++
++ return root;
++ }
++
++ Qt3DCore::QEntity *buildEntityFilterTestScene(Qt3DRender::QFrameGraphNode *fg, Qt3DRender::QLayer *layer)
++ {
++ Qt3DCore::QEntity *root = new Qt3DCore::QEntity();
++
++ Qt3DRender::QRenderSettings* renderSettings = new Qt3DRender::QRenderSettings();
++ renderSettings->setActiveFrameGraph(fg);
++ root->addComponent(renderSettings);
++
++ // Scene
++ for (int i = 0; i < 200; ++i) {
++ Qt3DCore::QEntity *e = new Qt3DCore::QEntity();
++ Qt3DRender::QMaterial *material = new Qt3DRender::QMaterial();
++ Qt3DRender::QGeometryRenderer *geometryRenderer = new Qt3DRender::QGeometryRenderer();
++ e->addComponent(material);
++ e->addComponent(geometryRenderer);
++ if (i % 2 == 0)
++ e->addComponent(layer);
++ e->setParent(root);
++ }
++
++ return root;
++ }
++
++ bool containsDependency(const std::vector<QWeakPointer<Qt3DCore::QAspectJob>> &dependencies,
++ const Qt3DCore::QAspectJobPtr &dependency)
++ {
++ return std::find(dependencies.begin(),
++ dependencies.end(),
++ dependency) != dependencies.end();
++ }
++
++ } // anonymous
++
++
++ class tst_RenderViewBuilder : public QObject
++ {
++ Q_OBJECT
++
++ private Q_SLOTS:
++
++ void initTestCase()
++ {
++ qputenv("QT3D_RENDERER", "opengl");
++ }
++
++ void checkInitialState()
++ {
++ // GIVEN
++ Qt3DRender::QViewport *viewport = new Qt3DRender::QViewport();
++ Qt3DRender::QClearBuffers *clearBuffer = new Qt3DRender::QClearBuffers(viewport);
++ Qt3DRender::TestAspect testAspect(buildSimpleScene(viewport));
++
++ // THEN
++ Qt3DRender::Render::FrameGraphNode *leafNode = testAspect.nodeManagers()->frameGraphManager()->lookupNode(clearBuffer->id());
++ QVERIFY(leafNode != nullptr);
++
++ {
++ // WHEN
++ Qt3DRender::Render::OpenGL::RenderViewBuilder renderViewBuilder(leafNode, 0, testAspect.renderer());
++
++ // THEN
++ QCOMPARE(renderViewBuilder.renderViewIndex(), 0);
++ QCOMPARE(renderViewBuilder.renderer(), testAspect.renderer());
++ QCOMPARE(renderViewBuilder.layerCacheNeedsToBeRebuilt(), false);
++ QCOMPARE(renderViewBuilder.materialGathererCacheNeedsToBeRebuilt(), false);
++ QVERIFY(!renderViewBuilder.renderViewJob().isNull());
++ QVERIFY(!renderViewBuilder.frustumCullingJob().isNull());
++ QVERIFY(!renderViewBuilder.syncPreFrustumCullingJob().isNull());
++ QVERIFY(!renderViewBuilder.setClearDrawBufferIndexJob().isNull());
++
++ QVERIFY(renderViewBuilder.filterEntityByLayerJob().isNull());
++ QVERIFY(renderViewBuilder.syncFilterEntityByLayerJob().isNull());
++ QVERIFY(renderViewBuilder.syncRenderViewPreCommandUpdateJob().isNull());
++ QVERIFY(renderViewBuilder.syncRenderViewPostCommandUpdateJob().isNull());
++ QVERIFY(renderViewBuilder.syncRenderViewPostInitializationJob().isNull());
++
++ QCOMPARE(renderViewBuilder.renderViewCommandUpdaterJobs().size(), 0U);
++ QCOMPARE(renderViewBuilder.materialGathererJobs().size(), 0U);
++
++ // WHEN
++ renderViewBuilder.prepareJobs();
++
++ // THEN
++ QVERIFY(!renderViewBuilder.syncRenderViewPreCommandUpdateJob().isNull());
++ QVERIFY(!renderViewBuilder.syncRenderViewPostCommandUpdateJob().isNull());
++ QVERIFY(!renderViewBuilder.syncRenderViewPostInitializationJob().isNull());
++ QVERIFY(renderViewBuilder.filterEntityByLayerJob().isNull());
++ QVERIFY(renderViewBuilder.syncFilterEntityByLayerJob().isNull());
++
++ QCOMPARE(renderViewBuilder.renderViewCommandUpdaterJobs().size(), size_t(Qt3DCore::QAspectJobManager::idealThreadCount()));
++ QCOMPARE(renderViewBuilder.materialGathererJobs().size(), 0U);
++ QCOMPARE(renderViewBuilder.buildJobHierachy().size(), 8U + 1 * Qt3DCore::QAspectJobManager::idealThreadCount());
++ }
++
++ {
++ // WHEN
++ Qt3DRender::Render::OpenGL::RenderViewBuilder renderViewBuilder(leafNode, 0, testAspect.renderer());
++ renderViewBuilder.setOptimalJobCount(2);
++ renderViewBuilder.setLayerCacheNeedsToBeRebuilt(true);
++ renderViewBuilder.prepareJobs();
++
++ // THEN
++ QCOMPARE(renderViewBuilder.layerCacheNeedsToBeRebuilt(), true);
++ QVERIFY(!renderViewBuilder.filterEntityByLayerJob().isNull());
++ QVERIFY(!renderViewBuilder.syncFilterEntityByLayerJob().isNull());
++
++ // mark jobs dirty and recheck
++ QCOMPARE(renderViewBuilder.buildJobHierachy().size(), 10U + renderViewBuilder.optimalJobCount());
++ }
++
++ {
++ // WHEN
++ Qt3DRender::Render::OpenGL::RenderViewBuilder renderViewBuilder(leafNode, 0, testAspect.renderer());
++ renderViewBuilder.setOptimalJobCount(2);
++ renderViewBuilder.setMaterialGathererCacheNeedsToBeRebuilt(true);
++ renderViewBuilder.prepareJobs();
++
++ // THEN
++ QCOMPARE(renderViewBuilder.materialGathererCacheNeedsToBeRebuilt(), true);
++ // We have elementsPerJob = qMax(materialHandles.size() / m_optimalParallelJobCount, 1)
++ // Given we have 2 materials -> 1 element per job -> so we need 2 jobs
++ QCOMPARE(renderViewBuilder.materialGathererJobs().size(), 2U);
++ QVERIFY(!renderViewBuilder.syncMaterialGathererJob().isNull());
++
++ // mark jobs dirty and recheck
++ QCOMPARE(renderViewBuilder.buildJobHierachy().size(), 13U);
++ }
++ }
++
++ void checkCheckJobDependencies()
++ {
++ // GIVEN
++ Qt3DRender::QViewport *viewport = new Qt3DRender::QViewport();
++ Qt3DRender::QClearBuffers *clearBuffer = new Qt3DRender::QClearBuffers(viewport);
++ Qt3DRender::TestAspect testAspect(buildSimpleScene(viewport));
++
++ auto expandBVJob = Qt3DRender::QRenderAspectPrivate::get(&testAspect)->m_expandBoundingVolumeJob;
++ auto wordTransformJob = Qt3DRender::QRenderAspectPrivate::get(&testAspect)->m_worldTransformJob;
++ auto updateTreeEnabledJob = Qt3DRender::QRenderAspectPrivate::get(&testAspect)->m_updateTreeEnabledJob;
++ auto updateEntityLayerJob = Qt3DRender::QRenderAspectPrivate::get(&testAspect)->m_updateEntityLayersJob;
++
++ // THEN
++ Qt3DRender::Render::FrameGraphNode *leafNode = testAspect.nodeManagers()->frameGraphManager()->lookupNode(clearBuffer->id());
++ QVERIFY(leafNode != nullptr);
++
++ {
++ // WHEN
++ Qt3DRender::Render::OpenGL::RenderViewBuilder renderViewBuilder(leafNode, 0, testAspect.renderer());
++ renderViewBuilder.prepareJobs();
++ renderViewBuilder.buildJobHierachy();
++
++ // THEN
++ // Step 1
++ QCOMPARE(renderViewBuilder.renderViewJob()->dependencies().size(), 1U); // Depends upon skinning palette update
++
++ // Step 2
++ QCOMPARE(renderViewBuilder.syncRenderViewPostInitializationJob()->dependencies().size(), 1U);
++ QCOMPARE(renderViewBuilder.syncRenderViewPostInitializationJob()->dependencies().front().toStrongRef().data(),
++ renderViewBuilder.renderViewJob().data());
++
++ // Step 3
++ QVERIFY(renderViewBuilder.filterEntityByLayerJob().isNull());
++ QVERIFY(renderViewBuilder.syncFilterEntityByLayerJob().isNull());
++
++ QCOMPARE(renderViewBuilder.filterProximityJob()->dependencies().size(), 2U);
++ QVERIFY(containsDependency(renderViewBuilder.filterProximityJob()->dependencies(), renderViewBuilder.syncRenderViewPostInitializationJob()));
++ QVERIFY(containsDependency(renderViewBuilder.filterProximityJob()->dependencies(), expandBVJob));
++
++ QCOMPARE(renderViewBuilder.setClearDrawBufferIndexJob()->dependencies().size(), 1U);
++ QCOMPARE(renderViewBuilder.setClearDrawBufferIndexJob()->dependencies().front().toStrongRef().data(),
++ renderViewBuilder.syncRenderViewPostInitializationJob().data());
++
++ QCOMPARE(renderViewBuilder.syncPreFrustumCullingJob()->dependencies().size(), 3U);
++ QVERIFY(containsDependency(renderViewBuilder.syncPreFrustumCullingJob()->dependencies(), renderViewBuilder.syncRenderViewPostInitializationJob()));
++ QVERIFY(containsDependency(renderViewBuilder.syncPreFrustumCullingJob()->dependencies(), wordTransformJob));
++ QVERIFY(containsDependency(renderViewBuilder.syncPreFrustumCullingJob()->dependencies(), testAspect.renderer()->updateShaderDataTransformJob()));
++
++ // Step 4
++ QCOMPARE(renderViewBuilder.frustumCullingJob()->dependencies().size(), 2U);
++ QVERIFY(containsDependency(renderViewBuilder.frustumCullingJob()->dependencies(), renderViewBuilder.syncPreFrustumCullingJob()));
++ QVERIFY(containsDependency(renderViewBuilder.frustumCullingJob()->dependencies(), expandBVJob));
++
++ QCOMPARE(renderViewBuilder.syncRenderViewPreCommandUpdateJob()->dependencies().size(), renderViewBuilder.materialGathererJobs().size() + 7);
++ QVERIFY(containsDependency(renderViewBuilder.syncRenderViewPreCommandUpdateJob()->dependencies(), renderViewBuilder.syncRenderViewPostInitializationJob()));
++ QVERIFY(containsDependency(renderViewBuilder.syncRenderViewPreCommandUpdateJob()->dependencies(), renderViewBuilder.filterProximityJob()));
++ QVERIFY(containsDependency(renderViewBuilder.syncRenderViewPreCommandUpdateJob()->dependencies(), renderViewBuilder.frustumCullingJob()));
++ QVERIFY(containsDependency(renderViewBuilder.syncRenderViewPreCommandUpdateJob()->dependencies(), testAspect.renderer()->introspectShadersJob()));
++ QVERIFY(containsDependency(renderViewBuilder.syncRenderViewPreCommandUpdateJob()->dependencies(), testAspect.renderer()->bufferGathererJob()));
++ QVERIFY(containsDependency(renderViewBuilder.syncRenderViewPreCommandUpdateJob()->dependencies(), testAspect.renderer()->textureGathererJob()));
++ QVERIFY(containsDependency(renderViewBuilder.syncRenderViewPreCommandUpdateJob()->dependencies(), testAspect.renderer()->lightGathererJob()));
++
++ // Step 5
++ for (const auto &renderViewBuilderJob : renderViewBuilder.renderViewCommandUpdaterJobs()) {
++ QCOMPARE(renderViewBuilderJob->dependencies().size(), 1U);
++ QCOMPARE(renderViewBuilderJob->dependencies().front().toStrongRef().data(),
++ renderViewBuilder.syncRenderViewPreCommandUpdateJob().data());
++ }
++
++ // Step 6
++ QCOMPARE(renderViewBuilder.syncRenderViewPostCommandUpdateJob()->dependencies().size(), renderViewBuilder.renderViewCommandUpdaterJobs().size());
++ for (const auto &renderViewBuilderJob : renderViewBuilder.renderViewCommandUpdaterJobs()) {
++ QVERIFY(containsDependency(renderViewBuilder.syncRenderViewPostCommandUpdateJob()->dependencies(), renderViewBuilderJob));
++ }
++ }
++ {
++ // WHEN
++ Qt3DRender::Render::OpenGL::RenderViewBuilder renderViewBuilder(leafNode, 0, testAspect.renderer());
++ renderViewBuilder.setLayerCacheNeedsToBeRebuilt(true);
++ renderViewBuilder.setMaterialGathererCacheNeedsToBeRebuilt(true);
++ renderViewBuilder.prepareJobs();
++ renderViewBuilder.buildJobHierachy();
++
++ // THEN
++ // Step 1
++ QCOMPARE(renderViewBuilder.renderViewJob()->dependencies().size(), 1U); // Depends upon skinning palette update
++
++ // Step 2
++ QCOMPARE(renderViewBuilder.syncRenderViewPostInitializationJob()->dependencies().size(), 1U);
++ QCOMPARE(renderViewBuilder.syncRenderViewPostInitializationJob()->dependencies().front().toStrongRef().data(),
++ renderViewBuilder.renderViewJob().data());
++
++ // Step 3
++ QCOMPARE(renderViewBuilder.filterEntityByLayerJob()->dependencies().size(), 3U);
++ QVERIFY(containsDependency(renderViewBuilder.filterEntityByLayerJob()->dependencies(), updateEntityLayerJob));
++ QVERIFY(containsDependency(renderViewBuilder.filterEntityByLayerJob()->dependencies(), renderViewBuilder.syncRenderViewPostInitializationJob()));
++ QVERIFY(containsDependency(renderViewBuilder.filterEntityByLayerJob()->dependencies(), updateTreeEnabledJob));
++
++ QCOMPARE(renderViewBuilder.syncFilterEntityByLayerJob()->dependencies().size(), 1U);
++ QVERIFY(containsDependency(renderViewBuilder.syncFilterEntityByLayerJob()->dependencies(), renderViewBuilder.filterEntityByLayerJob()));
++
++ QCOMPARE(renderViewBuilder.filterProximityJob()->dependencies().size(), 2U);
++ QVERIFY(containsDependency(renderViewBuilder.filterProximityJob()->dependencies(), renderViewBuilder.syncRenderViewPostInitializationJob()));
++ QVERIFY(containsDependency(renderViewBuilder.filterProximityJob()->dependencies(), expandBVJob));
++
++ QCOMPARE(renderViewBuilder.setClearDrawBufferIndexJob()->dependencies().size(), 1U);
++ QCOMPARE(renderViewBuilder.setClearDrawBufferIndexJob()->dependencies().front().toStrongRef().data(),
++ renderViewBuilder.syncRenderViewPostInitializationJob().data());
++
++ QCOMPARE(renderViewBuilder.syncPreFrustumCullingJob()->dependencies().size(), 3U);
++ QVERIFY(containsDependency(renderViewBuilder.syncPreFrustumCullingJob()->dependencies(), renderViewBuilder.syncRenderViewPostInitializationJob()));
++ QVERIFY(containsDependency(renderViewBuilder.syncPreFrustumCullingJob()->dependencies(), wordTransformJob));
++ QVERIFY(containsDependency(renderViewBuilder.syncPreFrustumCullingJob()->dependencies(), testAspect.renderer()->updateShaderDataTransformJob()));
++
++ for (const auto &materialGatherer : renderViewBuilder.materialGathererJobs()) {
++ QCOMPARE(materialGatherer->dependencies().size(), 3U);
++ QVERIFY(containsDependency(materialGatherer->dependencies(), renderViewBuilder.syncRenderViewPostInitializationJob()));
++ QVERIFY(containsDependency(materialGatherer->dependencies(), testAspect.renderer()->introspectShadersJob()));
++ QVERIFY(containsDependency(materialGatherer->dependencies(), testAspect.renderer()->filterCompatibleTechniqueJob()));
++ }
++
++ // Step 4
++ QCOMPARE(renderViewBuilder.frustumCullingJob()->dependencies().size(), 2U);
++ QVERIFY(containsDependency(renderViewBuilder.frustumCullingJob()->dependencies(), renderViewBuilder.syncPreFrustumCullingJob()));
++ QVERIFY(containsDependency(renderViewBuilder.frustumCullingJob()->dependencies(), expandBVJob));
++
++ QVERIFY(containsDependency(renderViewBuilder.syncRenderViewPreCommandUpdateJob()->dependencies(), renderViewBuilder.syncRenderViewPostInitializationJob()));
++ QVERIFY(containsDependency(renderViewBuilder.syncRenderViewPreCommandUpdateJob()->dependencies(), renderViewBuilder.syncFilterEntityByLayerJob()));
++ QVERIFY(containsDependency(renderViewBuilder.syncRenderViewPreCommandUpdateJob()->dependencies(), renderViewBuilder.frustumCullingJob()));
++ QVERIFY(containsDependency(renderViewBuilder.syncRenderViewPreCommandUpdateJob()->dependencies(), renderViewBuilder.filterProximityJob()));
++ QVERIFY(containsDependency(renderViewBuilder.syncRenderViewPreCommandUpdateJob()->dependencies(), testAspect.renderer()->introspectShadersJob()));
++ QVERIFY(containsDependency(renderViewBuilder.syncRenderViewPreCommandUpdateJob()->dependencies(), testAspect.renderer()->bufferGathererJob()));
++ QVERIFY(containsDependency(renderViewBuilder.syncRenderViewPreCommandUpdateJob()->dependencies(), testAspect.renderer()->textureGathererJob()));
++
++ // Step 5
++ for (const auto &renderViewBuilderJob : renderViewBuilder.renderViewCommandUpdaterJobs()) {
++ QCOMPARE(renderViewBuilderJob->dependencies().size(), 1U);
++ QCOMPARE(renderViewBuilderJob->dependencies().front().toStrongRef().data(),
++ renderViewBuilder.syncRenderViewPreCommandUpdateJob().data());
++ }
++
++ // Step 6
++ QCOMPARE(renderViewBuilder.syncRenderViewPostCommandUpdateJob()->dependencies().size(), renderViewBuilder.renderViewCommandUpdaterJobs().size());
++ for (const auto &renderViewBuilderJob : renderViewBuilder.renderViewCommandUpdaterJobs()) {
++ QVERIFY(containsDependency(renderViewBuilder.syncRenderViewPostCommandUpdateJob()->dependencies(), renderViewBuilderJob));
++ }
++ }
++ }
++
++ void checkRenderViewJobExecution()
++ {
++ // GIVEN
++ Qt3DRender::QViewport *viewport = new Qt3DRender::QViewport();
++ Qt3DRender::QClearBuffers *clearBuffer = new Qt3DRender::QClearBuffers(viewport);
++ Qt3DRender::TestAspect testAspect(buildSimpleScene(viewport));
++
++ // THEN
++ Qt3DRender::Render::FrameGraphNode *leafNode = testAspect.nodeManagers()->frameGraphManager()->lookupNode(clearBuffer->id());
++ QVERIFY(leafNode != nullptr);
++
++ // WHEN
++ Qt3DRender::Render::OpenGL::RenderViewBuilder renderViewBuilder(leafNode, 0, testAspect.renderer());
++ renderViewBuilder.prepareJobs();
++ renderViewBuilder.buildJobHierachy();
++ renderViewBuilder.renderViewJob()->run();
++
++ // THEN
++ QVERIFY(renderViewBuilder.renderViewJob()->renderView() != nullptr);
++ }
++
++ void checkLightGatherExecution()
++ {
++ // GIVEN
++ Qt3DRender::QViewport *viewport = new Qt3DRender::QViewport();
++ Qt3DRender::QClearBuffers *clearBuffer = new Qt3DRender::QClearBuffers(viewport);
++ Qt3DRender::TestAspect testAspect(buildSimpleScene(viewport));
++ Qt3DRender::Render::OpenGL::Renderer *renderer = testAspect.renderer();
++
++ // THEN
++ Qt3DRender::Render::FrameGraphNode *leafNode = testAspect.nodeManagers()->frameGraphManager()->lookupNode(clearBuffer->id());
++ QVERIFY(leafNode != nullptr);
++
++ // WHEN
++ renderer->lightGathererJob()->run();
++
++ // THEN
++ Qt3DRender::Render::RendererCache<Qt3DRender::Render::OpenGL::RenderCommand> *cache = renderer->cache();
++ QCOMPARE(cache->gatheredLights.size(), 2U);
++ QVERIFY(cache->environmentLight != nullptr);
++ }
++
++ void checkRenderableEntitiesFilteringExecution()
++ {
++ // GIVEN
++ Qt3DRender::QViewport *viewport = new Qt3DRender::QViewport();
++ Qt3DRender::QClearBuffers *clearBuffer = new Qt3DRender::QClearBuffers(viewport);
++ Qt3DRender::TestAspect testAspect(buildSimpleScene(viewport));
++ Qt3DRender::Render::OpenGL::Renderer *renderer = testAspect.renderer();
++
++ // THEN
++ Qt3DRender::Render::FrameGraphNode *leafNode = testAspect.nodeManagers()->frameGraphManager()->lookupNode(clearBuffer->id());
++ QVERIFY(leafNode != nullptr);
++
++ // WHEN
++ renderer->renderableEntityFilterJob()->run();
++
++ // THEN
++ Qt3DRender::Render::RendererCache<Qt3DRender::Render::OpenGL::RenderCommand> *cache = renderer->cache();
++ QCOMPARE(cache->renderableEntities.size(), 1U);
++ }
++
++ void checkComputableEntitiesFilteringExecution()
++ {
++ // GIVEN
++ Qt3DRender::QViewport *viewport = new Qt3DRender::QViewport();
++ Qt3DRender::QClearBuffers *clearBuffer = new Qt3DRender::QClearBuffers(viewport);
++ Qt3DRender::TestAspect testAspect(buildSimpleScene(viewport));
++ Qt3DRender::Render::OpenGL::Renderer *renderer = testAspect.renderer();
++
++ // THEN
++ Qt3DRender::Render::FrameGraphNode *leafNode = testAspect.nodeManagers()->frameGraphManager()->lookupNode(clearBuffer->id());
++ QVERIFY(leafNode != nullptr);
++
++ // WHEN
++ renderer->computableEntityFilterJob()->run();
++
++ // THEN
++ Qt3DRender::Render::RendererCache<Qt3DRender::Render::OpenGL::RenderCommand> *cache = renderer->cache();
++ QCOMPARE(cache->computeEntities.size(), 1U);
++ }
++
++ void checkSyncRenderViewInitializationExecution()
++ {
++ // GIVEN
++ Qt3DRender::QViewport *viewport = new Qt3DRender::QViewport();
++ Qt3DRender::QClearBuffers *clearBuffer = new Qt3DRender::QClearBuffers(viewport);
++ Qt3DRender::QLayerFilter *layerFilter = new Qt3DRender::QLayerFilter(clearBuffer);
++ Qt3DRender::QFrustumCulling *frustumCulling = new Qt3DRender::QFrustumCulling(layerFilter);
++ Qt3DRender::QTechniqueFilter *techniqueFilter = new Qt3DRender::QTechniqueFilter(frustumCulling);
++ Qt3DRender::QRenderPassFilter *renderPassFilter = new Qt3DRender::QRenderPassFilter(techniqueFilter);
++ Qt3DRender::QLayer *layer = new Qt3DRender::QLayer();
++ layerFilter->addLayer(layer);
++
++ Qt3DRender::TestAspect testAspect(buildSimpleScene(viewport));
++
++ // THEN
++ Qt3DRender::Render::FrameGraphNode *leafNode = testAspect.nodeManagers()->frameGraphManager()->lookupNode(renderPassFilter->id());
++ QVERIFY(leafNode != nullptr);
++
++ {
++ // WHEN
++ Qt3DRender::Render::OpenGL::RenderViewBuilder renderViewBuilder(leafNode, 0, testAspect.renderer());
++ renderViewBuilder.prepareJobs();
++ renderViewBuilder.buildJobHierachy();
++
++ // THEN
++ QCOMPARE(renderViewBuilder.frustumCullingJob()->isActive(), false);
++ for (const auto &materialGatherer : renderViewBuilder.materialGathererJobs()) {
++ QVERIFY(materialGatherer->techniqueFilter() == nullptr);
++ QVERIFY(materialGatherer->renderPassFilter() == nullptr);
++ }
++
++ // WHEN
++ renderViewBuilder.renderViewJob()->run();
++ renderViewBuilder.syncRenderViewPostInitializationJob()->run();
++
++ // THEN
++ QCOMPARE(renderViewBuilder.frustumCullingJob()->isActive(), true);
++ for (const auto &materialGatherer : renderViewBuilder.materialGathererJobs()) {
++ QVERIFY(materialGatherer->techniqueFilter() != nullptr);
++ QVERIFY(materialGatherer->renderPassFilter() != nullptr);
++ }
++ }
++ {
++ // WHEN
++ Qt3DRender::Render::OpenGL::RenderViewBuilder renderViewBuilder(leafNode, 0, testAspect.renderer());
++ renderViewBuilder.setLayerCacheNeedsToBeRebuilt(true);
++ renderViewBuilder.prepareJobs();
++ renderViewBuilder.buildJobHierachy();
++
++ // THEN
++ QCOMPARE(renderViewBuilder.frustumCullingJob()->isActive(), false);
++ QCOMPARE(renderViewBuilder.filterEntityByLayerJob()->hasLayerFilter(), false);
++ QCOMPARE(renderViewBuilder.filterEntityByLayerJob()->layerFilters().size(), 0);
++ for (const auto &materialGatherer : renderViewBuilder.materialGathererJobs()) {
++ QVERIFY(materialGatherer->techniqueFilter() == nullptr);
++ QVERIFY(materialGatherer->renderPassFilter() == nullptr);
++ }
++
++ // WHEN
++ renderViewBuilder.renderViewJob()->run();
++ renderViewBuilder.syncRenderViewPostInitializationJob()->run();
++
++ // THEN
++ QCOMPARE(renderViewBuilder.frustumCullingJob()->isActive(), true);
++ QCOMPARE(renderViewBuilder.filterEntityByLayerJob()->hasLayerFilter(), true);
++ QCOMPARE(renderViewBuilder.filterEntityByLayerJob()->layerFilters().size(), 1);
++ for (const auto &materialGatherer : renderViewBuilder.materialGathererJobs()) {
++ QVERIFY(materialGatherer->techniqueFilter() != nullptr);
++ QVERIFY(materialGatherer->renderPassFilter() != nullptr);
++ }
++ }
++ }
++
++ void checkSyncFrustumCullingExecution()
++ {
++ // GIVEN
++ Qt3DRender::QViewport *viewport = new Qt3DRender::QViewport();
++ Qt3DRender::QClearBuffers *clearBuffer = new Qt3DRender::QClearBuffers(viewport);
++ Qt3DRender::QFrustumCulling *frustumCulling = new Qt3DRender::QFrustumCulling(clearBuffer);
++ Qt3DRender::QCameraSelector *cameraSelector = new Qt3DRender::QCameraSelector(frustumCulling);
++ Qt3DRender::QCamera *camera = new Qt3DRender::QCamera();
++ cameraSelector->setCamera(camera);
++
++ Qt3DRender::TestAspect testAspect(buildSimpleScene(viewport));
++
++ // THEN
++ Qt3DRender::Render::FrameGraphNode *leafNode = testAspect.nodeManagers()->frameGraphManager()->lookupNode(cameraSelector->id());
++ QVERIFY(leafNode != nullptr);
++
++ // WHEN
++ Qt3DRender::Render::OpenGL::RenderViewBuilder renderViewBuilder(leafNode, 0, testAspect.renderer());
++ renderViewBuilder.prepareJobs();
++ renderViewBuilder.buildJobHierachy();
++
++ // THEN
++ QCOMPARE(renderViewBuilder.frustumCullingJob()->viewProjection(), Matrix4x4());
++
++ // WHEN
++ renderViewBuilder.renderViewJob()->run();
++ renderViewBuilder.syncPreFrustumCullingJob()->run();
++
++ // THEN
++ QCOMPARE(convertToQMatrix4x4(renderViewBuilder.frustumCullingJob()->viewProjection()), camera->projectionMatrix() * camera->viewMatrix());
++ }
++
++ void checkRemoveEntitiesNotInSubset()
++ {
++ // GIVEN
++ Qt3DRender::QViewport *viewport = new Qt3DRender::QViewport();
++ Qt3DRender::QClearBuffers *clearBuffer = new Qt3DRender::QClearBuffers(viewport);
++ Qt3DRender::QLayerFilter *layerFilter = new Qt3DRender::QLayerFilter(clearBuffer);
++ Qt3DRender::QLayer *layer = new Qt3DRender::QLayer();
++ layerFilter->addLayer(layer);
++ Qt3DRender::TestAspect testAspect(buildEntityFilterTestScene(viewport, layer));
++ Qt3DRender::Render::OpenGL::Renderer *renderer = testAspect.renderer();
++
++ // THEN
++ Qt3DRender::Render::FrameGraphNode *leafNode = testAspect.nodeManagers()->frameGraphManager()->lookupNode(layerFilter->id());
++ QVERIFY(leafNode != nullptr);
++
++ // WHEN
++ renderer->markDirty(Qt3DRender::Render::AbstractRenderer::AllDirty, nullptr);
++
++ Qt3DRender::Render::OpenGL::RenderViewBuilder renderViewBuilder(leafNode, 0, testAspect.renderer());
++
++ renderViewBuilder.setLayerCacheNeedsToBeRebuilt(true);
++ renderViewBuilder.prepareJobs();
++ renderViewBuilder.buildJobHierachy();
++
++ renderer->renderableEntityFilterJob()->run();
++
++ renderViewBuilder.renderViewJob()->run();
++ renderViewBuilder.syncRenderViewPostInitializationJob()->run();
++ renderViewBuilder.filterEntityByLayerJob()->run();
++
++ Qt3DRender::Render::RendererCache<Qt3DRender::Render::OpenGL::RenderCommand> *cache = renderer->cache();
++ std::vector<Qt3DRender::Render::Entity *> renderableEntity = cache->renderableEntities;
++ std::vector<Qt3DRender::Render::Entity *> filteredEntity = renderViewBuilder.filterEntityByLayerJob()->filteredEntities();
++
++ // THEN
++ QCOMPARE(renderableEntity.size(), 200U);
++ QCOMPARE(filteredEntity.size(), 100U);
++
++ std::sort(renderableEntity.begin(), renderableEntity.end());
++
++ // WHEN
++ renderableEntity = Qt3DRender::Render::entitiesInSubset(renderableEntity, filteredEntity);
++
++ // THEN
++ QCOMPARE(renderableEntity.size(), 100U);
++ for (const auto entity : renderableEntity) {
++ QVERIFY(std::find(filteredEntity.begin(),
++ filteredEntity.end(),
++ entity) != filteredEntity.end());
++ }
++ }
++
++ };
++
++ QTEST_MAIN(tst_RenderViewBuilder)
++
++ #include "tst_renderviewbuilder.moc"