Revision control

Copy as Markdown

// Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "renderviewbuilder_p.h"
#include <Qt3DRender/private/qrenderaspect_p.h>
#include <Qt3DRender/private/rendersyncjobs_p.h>
#include <Qt3DCore/private/qaspectjobmanager_p.h>
QT_BEGIN_NAMESPACE
namespace Qt3DRender {
namespace Render {
namespace OpenGL {
namespace {
class SetClearDrawBufferIndex
{
public:
explicit SetClearDrawBufferIndex(const RenderViewInitializerJobPtr &renderViewJob)
: m_renderViewJob(renderViewJob)
{}
void operator()()
{
RenderView *rv = m_renderViewJob->renderView();
std::vector<ClearBufferInfo> &clearBuffersInfo = rv->specificClearColorBufferInfo();
const AttachmentPack &attachmentPack = rv->attachmentPack();
for (ClearBufferInfo &clearBufferInfo : clearBuffersInfo)
clearBufferInfo.drawBufferIndex = attachmentPack.getDrawBufferIndex(clearBufferInfo.attchmentPoint);
}
private:
RenderViewInitializerJobPtr m_renderViewJob;
};
} // anonymous
RenderViewBuilder::RenderViewBuilder(Render::FrameGraphNode *leafNode, int renderViewIndex, Renderer *renderer)
: m_leafNode(leafNode)
, m_renderViewIndex(renderViewIndex)
, m_renderer(renderer)
, m_renderViewJob(RenderViewInitializerJobPtr::create())
, m_filterEntityByLayerJob()
, m_frustumCullingJob(new Render::FrustumCullingJob())
, m_syncPreFrustumCullingJob(CreateSynchronizerJobPtr(SyncPreFrustumCulling(m_renderViewJob, m_frustumCullingJob), JobTypes::SyncFrustumCulling, renderViewIndex))
, m_setClearDrawBufferIndexJob(CreateSynchronizerJobPtr(SetClearDrawBufferIndex(m_renderViewJob), JobTypes::ClearBufferDrawIndex, renderViewIndex))
, m_syncFilterEntityByLayerJob()
, m_filterProximityJob(Render::FilterProximityDistanceJobPtr::create())
{
// 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
m_optimalParallelJobCount = Qt3DCore::QAspectJobManager::idealThreadCount();
}
RenderViewInitializerJobPtr RenderViewBuilder::renderViewJob() const
{
return m_renderViewJob;
}
FilterLayerEntityJobPtr RenderViewBuilder::filterEntityByLayerJob() const
{
return m_filterEntityByLayerJob;
}
FrustumCullingJobPtr RenderViewBuilder::frustumCullingJob() const
{
return m_frustumCullingJob;
}
const std::vector<RenderViewCommandUpdaterJobPtr> &RenderViewBuilder::renderViewCommandUpdaterJobs() const
{
return m_renderViewCommandUpdaterJobs;
}
const std::vector<RenderViewCommandBuilderJobPtr> &RenderViewBuilder::renderViewCommandBuilderJobs() const
{
return m_renderViewCommandBuilderJobs;
}
const std::vector<MaterialParameterGathererJobPtr> &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());
const bool commandsNeedRebuild = m_rebuildFlags.testFlag(RebuildFlag::FullCommandRebuild);
if (commandsNeedRebuild) {
m_renderViewCommandBuilderJobs.reserve(m_optimalParallelJobCount);
for (auto i = 0; i < m_optimalParallelJobCount; ++i) {
auto renderViewCommandBuilder = Render::OpenGL::RenderViewCommandBuilderJobPtr::create();
m_renderViewCommandBuilderJobs.push_back(renderViewCommandBuilder);
}
m_syncRenderViewPreCommandBuildingJob = CreateSynchronizerJobPtr(SyncPreCommandBuilding(m_renderViewJob,
m_renderViewCommandBuilderJobs,
m_renderer,
m_leafNode),
JobTypes::SyncRenderViewPreCommandBuilding,
m_renderViewIndex);
}
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(m_optimalParallelJobCount);
for (auto i = 0; i < m_optimalParallelJobCount; ++i) {
auto renderViewCommandUpdater = RenderViewCommandUpdaterJobPtr::create();
m_renderViewCommandUpdaterJobs.push_back(renderViewCommandUpdater);
}
const bool materialCacheNeedsRebuild = m_rebuildFlags.testFlag(RebuildFlag::MaterialCacheRebuild);
if (materialCacheNeedsRebuild) {
// Since Material gathering is an heavy task, we split it
const std::vector<HMaterial> &materialHandles = m_renderer->nodeManagers()->materialManager()->activeHandles();
const size_t handlesCount = materialHandles.size();
if (handlesCount) {
m_materialGathererJobs.reserve(m_optimalParallelJobCount);
const size_t elementsPerJob = std::max(handlesCount / m_optimalParallelJobCount, size_t(1));
size_t elementCount = 0;
while (elementCount < handlesCount) {
auto materialGatherer = MaterialParameterGathererJobPtr::create();
materialGatherer->setNodeManagers(m_renderer->nodeManagers());
// TO DO: Candidate for std::span if C++20
materialGatherer->setHandles({materialHandles.begin() + elementCount,
materialHandles.begin() + std::min(elementCount + elementsPerJob, handlesCount)});
m_materialGathererJobs.push_back(materialGatherer);
elementCount += elementsPerJob;
}
}
m_syncMaterialGathererJob = CreateSynchronizerJobPtr(SyncMaterialParameterGatherer(m_materialGathererJobs,
m_renderer,
m_leafNode),
JobTypes::SyncMaterialGatherer,
m_renderViewIndex);
}
const bool layerCacheNeedsRebuild = m_rebuildFlags.testFlag(RebuildFlag::LayerCacheRebuild);
if (layerCacheNeedsRebuild) {
m_filterEntityByLayerJob = Render::FilterLayerEntityJobPtr::create();
m_filterEntityByLayerJob->setManager(m_renderer->nodeManagers());
m_syncFilterEntityByLayerJob = CreateSynchronizerJobPtr(SyncFilterEntityByLayer(m_filterEntityByLayerJob,
m_renderer,
m_leafNode),
JobTypes::SyncFilterEntityByLayer,
m_renderViewIndex);
}
m_syncRenderViewPreCommandUpdateJob = CreateSynchronizerJobPtr(SyncRenderViewPreCommandUpdate(m_renderViewJob,
m_frustumCullingJob,
m_filterProximityJob,
m_materialGathererJobs,
m_renderViewCommandUpdaterJobs,
m_renderViewCommandBuilderJobs,
m_renderer,
m_leafNode,
m_rebuildFlags),
JobTypes::SyncRenderViewPreCommandUpdate,
m_renderViewIndex);
m_syncRenderViewPostCommandUpdateJob = CreateSynchronizerJobPtr(SyncRenderViewPostCommandUpdate(m_renderViewJob,
m_renderViewCommandUpdaterJobs,
m_renderer),
JobTypes::SyncRenderViewPostCommandUpdate,
m_renderViewIndex);
m_syncRenderViewPostInitializationJob = CreateSynchronizerJobPtr(SyncRenderViewPostInitialization(m_renderViewJob,
m_frustumCullingJob,
m_filterEntityByLayerJob,
m_filterProximityJob,
m_materialGathererJobs,
m_renderViewCommandUpdaterJobs,
m_renderViewCommandBuilderJobs),
JobTypes::SyncRenderViewInitialization,
m_renderViewIndex);
}
std::vector<Qt3DCore::QAspectJobPtr> RenderViewBuilder::buildJobHierachy() const
{
std::vector<Qt3DCore::QAspectJobPtr> jobs;
auto daspect = QRenderAspectPrivate::get(m_renderer->aspect());
auto expandBVJob = daspect->m_expandBoundingVolumeJob;
auto wordTransformJob = daspect->m_worldTransformJob;
auto updateTreeEnabledJob = daspect->m_updateTreeEnabledJob;
auto updateSkinningPaletteJob = daspect->m_updateSkinningPaletteJob;
auto updateEntityLayersJob = daspect->m_updateEntityLayersJob;
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(updateSkinningPaletteJob);
m_syncPreFrustumCullingJob->addDependency(wordTransformJob);
m_syncPreFrustumCullingJob->addDependency(m_renderer->updateShaderDataTransformJob());
m_syncPreFrustumCullingJob->addDependency(m_syncRenderViewPostInitializationJob);
m_frustumCullingJob->addDependency(expandBVJob);
m_frustumCullingJob->addDependency(m_syncPreFrustumCullingJob);
m_setClearDrawBufferIndexJob->addDependency(m_syncRenderViewPostInitializationJob);
m_syncRenderViewPostInitializationJob->addDependency(m_renderViewJob);
m_filterProximityJob->addDependency(expandBVJob);
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->lightGathererJob());
for (const auto &renderViewCommandUpdater : 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
const bool commandsNeedRebuild = m_rebuildFlags.testFlag(RebuildFlag::FullCommandRebuild);
const bool materialCacheNeedsRebuild = m_rebuildFlags.testFlag(RebuildFlag::MaterialCacheRebuild);
const bool layerCacheNeedsRebuild = m_rebuildFlags.testFlag(RebuildFlag::LayerCacheRebuild);
if (commandsNeedRebuild) { // Step 3
m_syncRenderViewPreCommandBuildingJob->addDependency(m_renderer->computableEntityFilterJob());
m_syncRenderViewPreCommandBuildingJob->addDependency(m_renderer->renderableEntityFilterJob());
m_syncRenderViewPreCommandBuildingJob->addDependency(m_syncRenderViewPostInitializationJob);
if (materialCacheNeedsRebuild)
m_syncRenderViewPreCommandBuildingJob->addDependency(m_syncMaterialGathererJob);
jobs.push_back(m_syncRenderViewPreCommandBuildingJob);
for (const auto &renderViewCommandBuilder : m_renderViewCommandBuilderJobs) {
renderViewCommandBuilder->addDependency(m_syncRenderViewPreCommandBuildingJob);
m_syncRenderViewPreCommandUpdateJob->addDependency(renderViewCommandBuilder);
jobs.push_back(renderViewCommandBuilder);
}
}
if (layerCacheNeedsRebuild) {
m_filterEntityByLayerJob->addDependency(updateEntityLayersJob);
m_filterEntityByLayerJob->addDependency(m_syncRenderViewPostInitializationJob);
m_filterEntityByLayerJob->addDependency(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 (materialCacheNeedsRebuild) {
for (const auto &materialGatherer : 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 : 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_rebuildFlags.setFlag(RebuildFlag::LayerCacheRebuild, needsToBeRebuilt);
}
bool RenderViewBuilder::layerCacheNeedsToBeRebuilt() const
{
return m_rebuildFlags.testFlag(RebuildFlag::LayerCacheRebuild);
}
void RenderViewBuilder::setMaterialGathererCacheNeedsToBeRebuilt(bool needsToBeRebuilt)
{
m_rebuildFlags.setFlag(RebuildFlag::MaterialCacheRebuild, needsToBeRebuilt);
}
bool RenderViewBuilder::materialGathererCacheNeedsToBeRebuilt() const
{
return m_rebuildFlags.testFlag(RebuildFlag::MaterialCacheRebuild);
}
void RenderViewBuilder::setRenderCommandCacheNeedsToBeRebuilt(bool needsToBeRebuilt)
{
m_rebuildFlags.setFlag(RebuildFlag::FullCommandRebuild, needsToBeRebuilt);
}
bool RenderViewBuilder::renderCommandCacheNeedsToBeRebuilt() const
{
return m_rebuildFlags.testFlag(RebuildFlag::FullCommandRebuild);
}
void RenderViewBuilder::setLightCacheNeedsToBeRebuilt(bool needsToBeRebuilt)
{
m_rebuildFlags.setFlag(RebuildFlag::LightCacheRebuild, needsToBeRebuilt);
}
bool RenderViewBuilder::lightCacheNeedsToBeRebuilt() const
{
return m_rebuildFlags.testFlag(RebuildFlag::LightCacheRebuild);
}
int RenderViewBuilder::optimalJobCount() const
{
return m_optimalParallelJobCount;
}
void RenderViewBuilder::setOptimalJobCount(int v)
{
m_optimalParallelJobCount = v;
}
} // OpenGL
} // Render
} // Qt3DRender
QT_END_NAMESPACE