/**************************************************************************** ** ** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB). ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt3D 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 "renderviewbuilder_p.h" #include QT_BEGIN_NAMESPACE namespace Qt3DRender { namespace Render { // In some cases having less jobs is better (especially on fast cpus where // splitting just adds more overhead). Ideally, we should try to set the value // depending on the platform/CPU/nbr of cores const int RenderViewBuilder::m_optimalParallelJobCount = QThread::idealThreadCount(); namespace { int findIdealNumberOfWorkers(int elementCount, int packetSize = 100) { if (elementCount == 0 || packetSize == 0) return 0; return std::min(std::max(elementCount / packetSize, 1), RenderViewBuilder::optimalJobCount()); } class SyncPreCommandBuilding { public: explicit SyncPreCommandBuilding(RenderViewInitializerJobPtr renderViewInitializerJob, const QVector &renderViewCommandBuilderJobs, Renderer *renderer, FrameGraphNode *leafNode) : m_renderViewInitializer(renderViewInitializerJob) , m_renderViewCommandBuilderJobs(renderViewCommandBuilderJobs) , m_renderer(renderer) , m_leafNode(leafNode) { } void operator()() { // Split commands to build among jobs QMutexLocker lock(m_renderer->cache()->mutex()); // Rebuild RenderCommands for all entities in RV (ignoring filtering) RendererCache *cache = m_renderer->cache(); const RendererCache::LeafNodeData &dataCacheForLeaf = cache->leafNodeCache[m_leafNode]; RenderView *rv = m_renderViewInitializer->renderView(); const auto entities = !rv->isCompute() ? cache->renderableEntities : cache->computeEntities; rv->setMaterialParameterTable(dataCacheForLeaf.materialParameterGatherer); lock.unlock(); // Split among the ideal number of command builders const int idealPacketSize = std::min(std::max(100, entities.size() / RenderViewBuilder::optimalJobCount()), entities.size()); // Try to split work into an ideal number of workers const int m = findIdealNumberOfWorkers(entities.size(), idealPacketSize); for (int i = 0; i < m; ++i) { const RenderViewCommandBuilderJobPtr renderViewCommandBuilder = m_renderViewCommandBuilderJobs.at(i); const int count = (i == m - 1) ? entities.size() - (i * idealPacketSize) : idealPacketSize; renderViewCommandBuilder->setEntities(entities, i * idealPacketSize, count); } } private: RenderViewInitializerJobPtr m_renderViewInitializer; QVector m_renderViewCommandBuilderJobs; Renderer *m_renderer; FrameGraphNode *m_leafNode; }; class SyncRenderViewPostCommandUpdate { public: explicit SyncRenderViewPostCommandUpdate(const RenderViewInitializerJobPtr &renderViewJob, const QVector &renderViewCommandUpdateJobs, Renderer *renderer) : m_renderViewJob(renderViewJob) , m_renderViewCommandUpdaterJobs(renderViewCommandUpdateJobs) , m_renderer(renderer) {} void operator()() { // Append all the commands and sort them RenderView *rv = m_renderViewJob->renderView(); const EntityRenderCommandDataPtr commandData = m_renderViewCommandUpdaterJobs.first()->renderables(); if (commandData) { const QVector commands = std::move(commandData->commands); rv->setCommands(commands); // TO DO: Find way to store commands once or at least only when required // Sort the commands rv->sort(); } // Enqueue our fully populated RenderView with the RenderThread m_renderer->enqueueRenderView(rv, m_renderViewJob->submitOrderIndex()); } private: RenderViewInitializerJobPtr m_renderViewJob; QVector m_renderViewCommandUpdaterJobs; Renderer *m_renderer; }; class SyncPreFrustumCulling { public: explicit SyncPreFrustumCulling(const RenderViewInitializerJobPtr &renderViewJob, const FrustumCullingJobPtr &frustumCulling) : m_renderViewJob(renderViewJob) , m_frustumCullingJob(frustumCulling) {} void operator()() { RenderView *rv = m_renderViewJob->renderView(); // Update matrices now that all transforms have been updated rv->updateMatrices(); // Frustum culling m_frustumCullingJob->setViewProjection(rv->viewProjectionMatrix()); } private: RenderViewInitializerJobPtr m_renderViewJob; FrustumCullingJobPtr m_frustumCullingJob; }; class SyncRenderViewPostInitialization { public: explicit SyncRenderViewPostInitialization(const RenderViewInitializerJobPtr &renderViewJob, const FrustumCullingJobPtr &frustumCullingJob, const FilterLayerEntityJobPtr &filterEntityByLayerJob, const FilterProximityDistanceJobPtr &filterProximityJob, const QVector &materialGathererJobs, const QVector &renderViewCommandUpdaterJobs, const QVector &renderViewCommandBuilderJobs) : m_renderViewJob(renderViewJob) , m_frustumCullingJob(frustumCullingJob) , m_filterEntityByLayerJob(filterEntityByLayerJob) , m_filterProximityJob(filterProximityJob) , m_materialGathererJobs(materialGathererJobs) , m_renderViewCommandUpdaterJobs(renderViewCommandUpdaterJobs) , m_renderViewCommandBuilderJobs(renderViewCommandBuilderJobs) {} void operator()() { RenderView *rv = m_renderViewJob->renderView(); // Layer filtering if (!m_filterEntityByLayerJob.isNull()) m_filterEntityByLayerJob->setLayerFilters(rv->layerFilters()); // Proximity filtering m_filterProximityJob->setProximityFilterIds(rv->proximityFilterIds()); // Material Parameter building for (const auto &materialGatherer : qAsConst(m_materialGathererJobs)) { materialGatherer->setRenderPassFilter(const_cast(rv->renderPassFilter())); materialGatherer->setTechniqueFilter(const_cast(rv->techniqueFilter())); } // Command builders and updates for (const auto &renderViewCommandUpdater : qAsConst(m_renderViewCommandUpdaterJobs)) renderViewCommandUpdater->setRenderView(rv); for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewCommandBuilderJobs)) renderViewCommandBuilder->setRenderView(rv); // Set whether frustum culling is enabled or not m_frustumCullingJob->setActive(rv->frustumCulling()); } private: RenderViewInitializerJobPtr m_renderViewJob; FrustumCullingJobPtr m_frustumCullingJob; FilterLayerEntityJobPtr m_filterEntityByLayerJob; FilterProximityDistanceJobPtr m_filterProximityJob; QVector m_materialGathererJobs; QVector m_renderViewCommandUpdaterJobs; QVector m_renderViewCommandBuilderJobs; }; class SyncRenderViewPreCommandUpdate { public: explicit SyncRenderViewPreCommandUpdate(const RenderViewInitializerJobPtr &renderViewJob, const FrustumCullingJobPtr &frustumCullingJob, const FilterProximityDistanceJobPtr &filterProximityJob, const QVector &materialGathererJobs, const QVector &renderViewCommandUpdaterJobs, const QVector &renderViewCommandBuilderJobs, Renderer *renderer, FrameGraphNode *leafNode, bool fullCommandRebuild) : m_renderViewJob(renderViewJob) , m_frustumCullingJob(frustumCullingJob) , m_filterProximityJob(filterProximityJob) , m_materialGathererJobs(materialGathererJobs) , m_renderViewCommandUpdaterJobs(renderViewCommandUpdaterJobs) , m_renderViewCommandBuilderJobs(renderViewCommandBuilderJobs) , m_renderer(renderer) , m_leafNode(leafNode) , m_fullRebuild(fullCommandRebuild) {} void operator()() { // Set the result of previous job computations // for final RenderCommand building RenderView *rv = m_renderViewJob->renderView(); if (!rv->noDraw()) { ///////// CACHE LOCKED //////////// // Retrieve Data from Cache RendererCache *cache = m_renderer->cache(); QMutexLocker lock(cache->mutex()); Q_ASSERT(cache->leafNodeCache.contains(m_leafNode)); const bool isDraw = !rv->isCompute(); const RendererCache::LeafNodeData &dataCacheForLeaf = cache->leafNodeCache[m_leafNode]; // Rebuild RenderCommands if required // This should happen fairly infrequently (FrameGraph Change, Geometry/Material change) // and allow to skip that step most of the time if (m_fullRebuild) { EntityRenderCommandData commandData; // Reduction { int totalCommandCount = 0; for (const RenderViewCommandBuilderJobPtr &renderViewCommandBuilder : qAsConst(m_renderViewCommandBuilderJobs)) totalCommandCount += renderViewCommandBuilder->commandData().size(); commandData.reserve(totalCommandCount); for (const RenderViewCommandBuilderJobPtr &renderViewCommandBuilder : qAsConst(m_renderViewCommandBuilderJobs)) commandData += std::move(renderViewCommandBuilder->commandData()); } // Store new cache RendererCache::LeafNodeData &writableCacheForLeaf = cache->leafNodeCache[m_leafNode]; writableCacheForLeaf.renderCommandData = std::move(commandData); } const EntityRenderCommandData commandData = dataCacheForLeaf.renderCommandData; const QVector filteredEntities = dataCacheForLeaf.filterEntitiesByLayer; QVector renderableEntities = isDraw ? cache->renderableEntities : cache->computeEntities; QVector lightSources = cache->gatheredLights; rv->setMaterialParameterTable(dataCacheForLeaf.materialParameterGatherer); rv->setEnvironmentLight(cache->environmentLight); lock.unlock(); ///////// END OF CACHE LOCKED //////////// // Filter out entities that weren't selected by the layer filters // Remove all entities from the compute and renderable vectors that aren't in the filtered layer vector renderableEntities = RenderViewBuilder::entitiesInSubset(renderableEntities, filteredEntities); // Set the light sources, with layer filters applied. for (int i = 0; i < lightSources.count(); ++i) { if (!filteredEntities.contains(lightSources[i].entity)) lightSources.removeAt(i--); } rv->setLightSources(lightSources); if (isDraw) { // Filter out frustum culled entity for drawable entities if (rv->frustumCulling()) renderableEntities = RenderViewBuilder::entitiesInSubset(renderableEntities, m_frustumCullingJob->visibleEntities()); // Filter out entities which didn't satisfy proximity filtering if (!rv->proximityFilterIds().empty()) renderableEntities = RenderViewBuilder::entitiesInSubset(renderableEntities, m_filterProximityJob->filteredEntities()); } // Early return in case we have nothing to filter if (renderableEntities.size() == 0) return; // Filter out Render commands for which the Entity wasn't selected because // of frustum, proximity or layer filtering EntityRenderCommandDataPtr filteredCommandData = EntityRenderCommandDataPtr::create(); filteredCommandData->reserve(renderableEntities.size()); // Because dataCacheForLeaf.renderableEntities or computeEntities are sorted // What we get out of EntityRenderCommandData is also sorted by Entity auto eIt = renderableEntities.cbegin(); const auto eEnd = renderableEntities.cend(); int cIt = 0; const int cEnd = commandData.size(); while (eIt != eEnd) { const Entity *targetEntity = *eIt; // Advance until we have commands whose Entity has a lower address // than the selected filtered entity while (cIt != cEnd && commandData.entities.at(cIt) < targetEntity) ++cIt; // Push pointers to command data for all commands that match the // entity while (cIt != cEnd && commandData.entities.at(cIt) == targetEntity) { filteredCommandData->push_back(commandData.entities.at(cIt), commandData.commands.at(cIt), commandData.passesData.at(cIt)); ++cIt; } ++eIt; } // Split among the number of command builders // The idealPacketSize is at least 100 entities per worker const int idealPacketSize = std::min(std::max(100, filteredCommandData->size() / RenderViewBuilder::optimalJobCount()), filteredCommandData->size()); const int m = findIdealNumberOfWorkers(filteredCommandData->size(), idealPacketSize); for (int i = 0; i < m; ++i) { const RenderViewCommandUpdaterJobPtr renderViewCommandBuilder = m_renderViewCommandUpdaterJobs.at(i); const int count = (i == m - 1) ? filteredCommandData->size() - (i * idealPacketSize) : idealPacketSize; renderViewCommandBuilder->setRenderables(filteredCommandData, i * idealPacketSize, count); } } } private: RenderViewInitializerJobPtr m_renderViewJob; FrustumCullingJobPtr m_frustumCullingJob; FilterProximityDistanceJobPtr m_filterProximityJob; QVector m_materialGathererJobs; QVector m_renderViewCommandUpdaterJobs; QVector m_renderViewCommandBuilderJobs; Renderer *m_renderer; FrameGraphNode *m_leafNode; bool m_fullRebuild; }; class SetClearDrawBufferIndex { public: explicit SetClearDrawBufferIndex(const RenderViewInitializerJobPtr &renderViewJob) : m_renderViewJob(renderViewJob) {} void operator()() { RenderView *rv = m_renderViewJob->renderView(); QVector &clearBuffersInfo = rv->specificClearColorBufferInfo(); const AttachmentPack &attachmentPack = rv->attachmentPack(); for (ClearBufferInfo &clearBufferInfo : clearBuffersInfo) clearBufferInfo.drawBufferIndex = attachmentPack.getDrawBufferIndex(clearBufferInfo.attchmentPoint); } private: RenderViewInitializerJobPtr m_renderViewJob; }; class SyncFilterEntityByLayer { public: explicit SyncFilterEntityByLayer(const FilterLayerEntityJobPtr &filterEntityByLayerJob, Renderer *renderer, FrameGraphNode *leafNode) : m_filterEntityByLayerJob(filterEntityByLayerJob) , m_renderer(renderer) , m_leafNode(leafNode) { } void operator()() { QMutexLocker lock(m_renderer->cache()->mutex()); // Save the filtered by layer subset into the cache const QVector filteredEntities = m_filterEntityByLayerJob->filteredEntities(); RendererCache::LeafNodeData &dataCacheForLeaf = m_renderer->cache()->leafNodeCache[m_leafNode]; dataCacheForLeaf.filterEntitiesByLayer = filteredEntities; } private: FilterLayerEntityJobPtr m_filterEntityByLayerJob; Renderer *m_renderer; FrameGraphNode *m_leafNode; }; class SyncMaterialParameterGatherer { public: explicit SyncMaterialParameterGatherer(const QVector &materialParameterGathererJobs, Renderer *renderer, FrameGraphNode *leafNode) : m_materialParameterGathererJobs(materialParameterGathererJobs) , m_renderer(renderer) , m_leafNode(leafNode) { } void operator()() { QMutexLocker lock(m_renderer->cache()->mutex()); RendererCache::LeafNodeData &dataCacheForLeaf = m_renderer->cache()->leafNodeCache[m_leafNode]; dataCacheForLeaf.materialParameterGatherer.clear(); for (const auto &materialGatherer : qAsConst(m_materialParameterGathererJobs)) dataCacheForLeaf.materialParameterGatherer.unite(materialGatherer->materialToPassAndParameter()); } private: QVector m_materialParameterGathererJobs; Renderer *m_renderer; FrameGraphNode *m_leafNode; }; } // anonymous RenderViewBuilder::RenderViewBuilder(Render::FrameGraphNode *leafNode, int renderViewIndex, Renderer *renderer) : m_leafNode(leafNode) , m_renderViewIndex(renderViewIndex) , m_renderer(renderer) , m_layerCacheNeedsToBeRebuilt(false) , m_materialGathererCacheNeedsToBeRebuilt(false) , m_renderCommandCacheNeedsToBeRebuilt(false) , m_renderViewJob(RenderViewInitializerJobPtr::create()) , m_filterEntityByLayerJob() , m_frustumCullingJob(new Render::FrustumCullingJob()) , m_syncPreFrustumCullingJob(SynchronizerJobPtr::create(SyncPreFrustumCulling(m_renderViewJob, m_frustumCullingJob), JobTypes::SyncFrustumCulling)) , m_setClearDrawBufferIndexJob(SynchronizerJobPtr::create(SetClearDrawBufferIndex(m_renderViewJob), JobTypes::ClearBufferDrawIndex)) , m_syncFilterEntityByLayerJob() , m_filterProximityJob(Render::FilterProximityDistanceJobPtr::create()) { } RenderViewInitializerJobPtr RenderViewBuilder::renderViewJob() const { return m_renderViewJob; } FilterLayerEntityJobPtr RenderViewBuilder::filterEntityByLayerJob() const { return m_filterEntityByLayerJob; } FrustumCullingJobPtr RenderViewBuilder::frustumCullingJob() const { return m_frustumCullingJob; } QVector RenderViewBuilder::renderViewCommandUpdaterJobs() const { return m_renderViewCommandUpdaterJobs; } QVector RenderViewBuilder::renderViewCommandBuilderJobs() const { return m_renderViewCommandBuilderJobs; } QVector RenderViewBuilder::materialGathererJobs() const { return m_materialGathererJobs; } SynchronizerJobPtr RenderViewBuilder::syncRenderViewPostInitializationJob() const { return m_syncRenderViewPostInitializationJob; } SynchronizerJobPtr RenderViewBuilder::syncPreFrustumCullingJob() const { return m_syncPreFrustumCullingJob; } SynchronizerJobPtr RenderViewBuilder::syncRenderViewPreCommandBuildingJob() const { return m_syncRenderViewPreCommandBuildingJob; } SynchronizerJobPtr RenderViewBuilder::syncRenderViewPreCommandUpdateJob() const { return m_syncRenderViewPreCommandUpdateJob; } SynchronizerJobPtr RenderViewBuilder::syncRenderViewPostCommandUpdateJob() const { return m_syncRenderViewPostCommandUpdateJob; } SynchronizerJobPtr RenderViewBuilder::setClearDrawBufferIndexJob() const { return m_setClearDrawBufferIndexJob; } SynchronizerJobPtr RenderViewBuilder::syncFilterEntityByLayerJob() const { return m_syncFilterEntityByLayerJob; } SynchronizerJobPtr RenderViewBuilder::syncMaterialGathererJob() const { return m_syncMaterialGathererJob; } FilterProximityDistanceJobPtr RenderViewBuilder::filterProximityJob() const { return m_filterProximityJob; } void RenderViewBuilder::prepareJobs() { // Init what we can here m_filterProximityJob->setManager(m_renderer->nodeManagers()); m_frustumCullingJob->setRoot(m_renderer->sceneRoot()); if (m_renderCommandCacheNeedsToBeRebuilt) { m_renderViewCommandBuilderJobs.reserve(RenderViewBuilder::m_optimalParallelJobCount); for (auto i = 0; i < RenderViewBuilder::m_optimalParallelJobCount; ++i) { auto renderViewCommandBuilder = Render::RenderViewCommandBuilderJobPtr::create(); m_renderViewCommandBuilderJobs.push_back(renderViewCommandBuilder); } m_syncRenderViewPreCommandBuildingJob = SynchronizerJobPtr::create(SyncPreCommandBuilding(m_renderViewJob, m_renderViewCommandBuilderJobs, m_renderer, m_leafNode), JobTypes::SyncRenderViewPreCommandBuilding); } m_renderViewJob->setRenderer(m_renderer); m_renderViewJob->setFrameGraphLeafNode(m_leafNode); m_renderViewJob->setSubmitOrderIndex(m_renderViewIndex); // RenderCommand building is the most consuming task -> split it // Estimate the number of jobs to create based on the number of entities m_renderViewCommandUpdaterJobs.reserve(RenderViewBuilder::m_optimalParallelJobCount); for (auto i = 0; i < RenderViewBuilder::m_optimalParallelJobCount; ++i) { auto renderViewCommandUpdater = Render::RenderViewCommandUpdaterJobPtr::create(); renderViewCommandUpdater->setRenderer(m_renderer); m_renderViewCommandUpdaterJobs.push_back(renderViewCommandUpdater); } if (m_materialGathererCacheNeedsToBeRebuilt) { // Since Material gathering is an heavy task, we split it const QVector materialHandles = m_renderer->nodeManagers()->materialManager()->activeHandles(); const int elementsPerJob = materialHandles.size() / RenderViewBuilder::m_optimalParallelJobCount; const int lastRemaingElements = materialHandles.size() % RenderViewBuilder::m_optimalParallelJobCount; m_materialGathererJobs.reserve(RenderViewBuilder::m_optimalParallelJobCount); for (auto i = 0; i < RenderViewBuilder::m_optimalParallelJobCount; ++i) { auto materialGatherer = Render::MaterialParameterGathererJobPtr::create(); materialGatherer->setNodeManagers(m_renderer->nodeManagers()); if (i == RenderViewBuilder::m_optimalParallelJobCount - 1) materialGatherer->setHandles(materialHandles.mid(i * elementsPerJob, elementsPerJob + lastRemaingElements)); else materialGatherer->setHandles(materialHandles.mid(i * elementsPerJob, elementsPerJob)); m_materialGathererJobs.push_back(materialGatherer); } m_syncMaterialGathererJob = SynchronizerJobPtr::create(SyncMaterialParameterGatherer(m_materialGathererJobs, m_renderer, m_leafNode), JobTypes::SyncMaterialGatherer); } if (m_layerCacheNeedsToBeRebuilt) { m_filterEntityByLayerJob = Render::FilterLayerEntityJobPtr::create(); m_filterEntityByLayerJob->setManager(m_renderer->nodeManagers()); m_syncFilterEntityByLayerJob = SynchronizerJobPtr::create(SyncFilterEntityByLayer(m_filterEntityByLayerJob, m_renderer, m_leafNode), JobTypes::SyncFilterEntityByLayer); } m_syncRenderViewPreCommandUpdateJob = SynchronizerJobPtr::create(SyncRenderViewPreCommandUpdate(m_renderViewJob, m_frustumCullingJob, m_filterProximityJob, m_materialGathererJobs, m_renderViewCommandUpdaterJobs, m_renderViewCommandBuilderJobs, m_renderer, m_leafNode, m_renderCommandCacheNeedsToBeRebuilt), JobTypes::SyncRenderViewPreCommandUpdate); m_syncRenderViewPostCommandUpdateJob = SynchronizerJobPtr::create(SyncRenderViewPostCommandUpdate(m_renderViewJob, m_renderViewCommandUpdaterJobs, m_renderer), JobTypes::SyncRenderViewPostCommandUpdate); m_syncRenderViewPostInitializationJob = SynchronizerJobPtr::create(SyncRenderViewPostInitialization(m_renderViewJob, m_frustumCullingJob, m_filterEntityByLayerJob, m_filterProximityJob, m_materialGathererJobs, m_renderViewCommandUpdaterJobs, m_renderViewCommandBuilderJobs), JobTypes::SyncRenderViewInitialization); } QVector RenderViewBuilder::buildJobHierachy() const { QVector jobs; jobs.reserve(m_materialGathererJobs.size() + m_renderViewCommandUpdaterJobs.size() + 11); // Set dependencies // Finish the skinning palette job before processing renderviews // TODO: Maybe only update skinning palettes for non-culled entities m_renderViewJob->addDependency(m_renderer->updateSkinningPaletteJob()); m_syncPreFrustumCullingJob->addDependency(m_renderer->updateWorldTransformJob()); m_syncPreFrustumCullingJob->addDependency(m_renderer->updateShaderDataTransformJob()); m_syncPreFrustumCullingJob->addDependency(m_syncRenderViewPostInitializationJob); m_frustumCullingJob->addDependency(m_renderer->expandBoundingVolumeJob()); m_frustumCullingJob->addDependency(m_syncPreFrustumCullingJob); m_setClearDrawBufferIndexJob->addDependency(m_syncRenderViewPostInitializationJob); m_syncRenderViewPostInitializationJob->addDependency(m_renderViewJob); m_filterProximityJob->addDependency(m_renderer->expandBoundingVolumeJob()); m_filterProximityJob->addDependency(m_syncRenderViewPostInitializationJob); m_syncRenderViewPreCommandUpdateJob->addDependency(m_syncRenderViewPostInitializationJob); m_syncRenderViewPreCommandUpdateJob->addDependency(m_filterProximityJob); m_syncRenderViewPreCommandUpdateJob->addDependency(m_frustumCullingJob); // Ensure the RenderThread won't be able to process dirtyResources // before they have been completely gathered m_syncRenderViewPreCommandUpdateJob->addDependency(m_renderer->introspectShadersJob()); m_syncRenderViewPreCommandUpdateJob->addDependency(m_renderer->bufferGathererJob()); m_syncRenderViewPreCommandUpdateJob->addDependency(m_renderer->textureGathererJob()); m_syncRenderViewPreCommandUpdateJob->addDependency(m_renderer->cacheLightJob()); for (const auto &renderViewCommandUpdater : qAsConst(m_renderViewCommandUpdaterJobs)) { renderViewCommandUpdater->addDependency(m_syncRenderViewPreCommandUpdateJob); m_syncRenderViewPostCommandUpdateJob->addDependency(renderViewCommandUpdater); } m_renderer->frameCleanupJob()->addDependency(m_syncRenderViewPostCommandUpdateJob); m_renderer->frameCleanupJob()->addDependency(m_setClearDrawBufferIndexJob); // Add jobs jobs.push_back(m_renderViewJob); // Step 1 jobs.push_back(m_syncRenderViewPostInitializationJob); // Step 2 if (m_renderCommandCacheNeedsToBeRebuilt) { // Step 3 m_syncRenderViewPreCommandBuildingJob->addDependency(m_renderer->cacheComputableEntitiesJob()); m_syncRenderViewPreCommandBuildingJob->addDependency(m_renderer->cacheRenderableEntitiesJob()); m_syncRenderViewPreCommandBuildingJob->addDependency(m_syncRenderViewPostInitializationJob); if (m_materialGathererCacheNeedsToBeRebuilt) m_syncRenderViewPreCommandBuildingJob->addDependency(m_syncMaterialGathererJob); jobs.push_back(m_syncRenderViewPreCommandBuildingJob); for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewCommandBuilderJobs)) { renderViewCommandBuilder->addDependency(m_syncRenderViewPreCommandBuildingJob); m_syncRenderViewPreCommandUpdateJob->addDependency(renderViewCommandBuilder); jobs.push_back(renderViewCommandBuilder); } } if (m_layerCacheNeedsToBeRebuilt) { m_filterEntityByLayerJob->addDependency(m_renderer->updateEntityLayersJob()); m_filterEntityByLayerJob->addDependency(m_syncRenderViewPostInitializationJob); m_filterEntityByLayerJob->addDependency(m_renderer->updateTreeEnabledJob()); m_syncFilterEntityByLayerJob->addDependency(m_filterEntityByLayerJob); m_syncRenderViewPreCommandUpdateJob->addDependency(m_syncFilterEntityByLayerJob); jobs.push_back(m_filterEntityByLayerJob); // Step 3 jobs.push_back(m_syncFilterEntityByLayerJob); // Step 4 } jobs.push_back(m_syncPreFrustumCullingJob); // Step 3 jobs.push_back(m_filterProximityJob); // Step 3 jobs.push_back(m_setClearDrawBufferIndexJob); // Step 3 if (m_materialGathererCacheNeedsToBeRebuilt) { for (const auto &materialGatherer : qAsConst(m_materialGathererJobs)) { materialGatherer->addDependency(m_syncRenderViewPostInitializationJob); materialGatherer->addDependency(m_renderer->introspectShadersJob()); materialGatherer->addDependency(m_renderer->filterCompatibleTechniqueJob()); jobs.push_back(materialGatherer); // Step3 m_syncMaterialGathererJob->addDependency(materialGatherer); } m_syncRenderViewPreCommandUpdateJob->addDependency(m_syncMaterialGathererJob); jobs.push_back(m_syncMaterialGathererJob); // Step 3 } jobs.push_back(m_frustumCullingJob); // Step 4 jobs.push_back(m_syncRenderViewPreCommandUpdateJob); // Step 5 // Build RenderCommands or Update RenderCommand Uniforms for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewCommandUpdaterJobs)) // Step 6 jobs.push_back(renderViewCommandBuilder); jobs.push_back(m_syncRenderViewPostCommandUpdateJob); // Step 7 return jobs; } Renderer *RenderViewBuilder::renderer() const { return m_renderer; } int RenderViewBuilder::renderViewIndex() const { return m_renderViewIndex; } void RenderViewBuilder::setLayerCacheNeedsToBeRebuilt(bool needsToBeRebuilt) { m_layerCacheNeedsToBeRebuilt = needsToBeRebuilt; } bool RenderViewBuilder::layerCacheNeedsToBeRebuilt() const { return m_layerCacheNeedsToBeRebuilt; } void RenderViewBuilder::setMaterialGathererCacheNeedsToBeRebuilt(bool needsToBeRebuilt) { m_materialGathererCacheNeedsToBeRebuilt = needsToBeRebuilt; } bool RenderViewBuilder::materialGathererCacheNeedsToBeRebuilt() const { return m_materialGathererCacheNeedsToBeRebuilt; } void RenderViewBuilder::setRenderCommandCacheNeedsToBeRebuilt(bool needsToBeRebuilt) { m_renderCommandCacheNeedsToBeRebuilt = needsToBeRebuilt; } bool RenderViewBuilder::renderCommandCacheNeedsToBeRebuilt() const { return m_renderCommandCacheNeedsToBeRebuilt; } int RenderViewBuilder::optimalJobCount() { return RenderViewBuilder::m_optimalParallelJobCount; } QVector RenderViewBuilder::entitiesInSubset(const QVector &entities, const QVector &subset) { QVector intersection; intersection.reserve(qMin(entities.size(), subset.size())); std::set_intersection(entities.begin(), entities.end(), subset.begin(), subset.end(), std::back_inserter(intersection)); return intersection; } } // Render } // Qt3DRender QT_END_NAMESPACE