Cache material parameter gatherer results

Change-Id: I0660f876d7d967d552f7982c3e33e7c971c9abb4
Reviewed-by: Laszlo Agocs <laszlo.agocs@qt.io>
This commit is contained in:
Svenn-Arne Dragly 2017-09-20 12:45:01 +02:00
parent dc16f08f2c
commit f660c65709
10 changed files with 121 additions and 39 deletions

View File

@ -1539,6 +1539,9 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs()
const bool layersCacheNeedsToBeRebuilt = layersDirty || entitiesEnabledDirty;
bool layersCacheRebuilt = false;
const bool materialDirty = changesToUnset & AbstractRenderer::MaterialDirty;
bool materialGathererCacheRebuilt = false;
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
@ -1560,20 +1563,24 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs()
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());
}
layersCacheRebuilt = true;
materialGathererCacheRebuilt = true;
// Set target number of RenderViews
m_renderQueue->setTargetRenderViewCount(fgBranchCount);
}
// Only reset LayersDirty flag once we have really rebuilt the caches
// Only reset dirty flags once we have really rebuilt the caches
if (layersDirty && !layersCacheRebuilt)
changesToUnset.setFlag(AbstractRenderer::LayersDirty, false);
if (entitiesEnabledDirty && !layersCacheRebuilt)
changesToUnset.setFlag(AbstractRenderer::EntityEnabledDirty, false);
if (materialDirty && !materialGathererCacheRebuilt)
changesToUnset.setFlag(AbstractRenderer::MaterialDirty, false);
// Clear dirty bits
// TO DO: When secondary GL thread is integrated, the following line can be removed

View File

@ -66,7 +66,8 @@ struct RendererCache
{
struct LeafNodeData
{
QVector<Entity *> filterEntitiesByLayer;
QVector<Entity *> filterEntitiesByLayer;
MaterialParameterGathererData materialParameterGatherer;
};
QHash<FrameGraphNode *, LeafNodeData> leafNodeCache;

View File

@ -191,7 +191,7 @@ public:
inline bool frustumCulling() const Q_DECL_NOTHROW { return m_frustumCulling; }
void setFrustumCulling(bool frustumCulling) Q_DECL_NOTHROW { m_frustumCulling = frustumCulling; }
inline void setMaterialParameterTable(const QHash<Qt3DCore::QNodeId, QVector<RenderPassParameterData>> &parameters) Q_DECL_NOTHROW { m_parameters = parameters; }
inline void setMaterialParameterTable(const MaterialParameterGathererData &parameters) Q_DECL_NOTHROW { m_parameters = parameters; }
// TODO: Get rid of this overly complex memory management by splitting out the
// InnerData as a RenderViewConfig struct. This can be created by setRenderViewConfigFromFrameGraphLeafNode
@ -326,7 +326,7 @@ private:
mutable QVector<LightSource> m_lightSources;
EnvironmentLight *m_environmentLight;
QHash<Qt3DCore::QNodeId, QVector<RenderPassParameterData>> m_parameters;
MaterialParameterGathererData m_parameters;
enum StandardUniform
{

View File

@ -246,13 +246,10 @@ public:
++i;
}
m_renderViewBuilderJobs.at(i)->setRenderables(renderableEntities.mid(i * packetSize, packetSize + renderableEntities.size() % (m + 1)));
// Reduction
QHash<Qt3DCore::QNodeId, QVector<RenderPassParameterData>> params;
for (const auto &materialGatherer : qAsConst(m_materialGathererJobs))
params.unite(materialGatherer->materialToPassAndParameter());
// Set all required data on the RenderView for final processing
rv->setMaterialParameterTable(std::move(params));
{
QMutexLocker rendererCacheLock(m_renderer->cache()->mutex());
rv->setMaterialParameterTable(m_renderer->cache()->leafNodeCache.value(m_leafNode).materialParameterGatherer);
}
}
}
@ -317,6 +314,34 @@ private:
FrameGraphNode *m_leafNode;
};
class SyncMaterialParameterGatherer
{
public:
explicit SyncMaterialParameterGatherer(const QVector<MaterialParameterGathererJobPtr> &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<MaterialParameterGathererJobPtr> m_materialParameterGathererJobs;
Renderer *m_renderer;
FrameGraphNode *m_leafNode;
};
} // anonymous
RenderViewBuilder::RenderViewBuilder(Render::FrameGraphNode *leafNode, int renderViewIndex, Renderer *renderer)
@ -324,6 +349,7 @@ RenderViewBuilder::RenderViewBuilder(Render::FrameGraphNode *leafNode, int rende
, m_renderViewIndex(renderViewIndex)
, m_renderer(renderer)
, m_layerCacheNeedsToBeRebuilt(false)
, m_materialGathererCacheNeedsToBeRebuilt(false)
, m_renderViewJob(RenderViewInitializerJobPtr::create())
, m_filterEntityByLayerJob()
, m_lightGathererJob(Render::LightGathererPtr::create())
@ -407,6 +433,11 @@ SynchronizerJobPtr RenderViewBuilder::syncFilterEntityByLayerJob() const
return m_syncFilterEntityByLayerJob;
}
SynchronizerJobPtr RenderViewBuilder::syncMaterialGathererJob() const
{
return m_syncMaterialGathererJob;
}
FilterProximityDistanceJobPtr RenderViewBuilder::filterProximityJob() const
{
return m_filterProximityJob;
@ -435,20 +466,26 @@ void RenderViewBuilder::prepareJobs()
m_renderViewBuilderJobs.push_back(renderViewCommandBuilder);
}
// Since Material gathering is an heavy task, we split it
const QVector<HMaterial> 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());
materialGatherer->setRenderer(m_renderer);
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);
if (m_materialGathererCacheNeedsToBeRebuilt) {
// Since Material gathering is an heavy task, we split it
const QVector<HMaterial> 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());
materialGatherer->setRenderer(m_renderer);
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) {
@ -514,11 +551,6 @@ QVector<Qt3DCore::QAspectJobPtr> RenderViewBuilder::buildJobHierachy() const
m_filterProximityJob->addDependency(m_syncRenderViewInitializationJob);
m_syncRenderCommandBuildingJob->addDependency(m_syncRenderViewInitializationJob);
for (const auto &materialGatherer : qAsConst(m_materialGathererJobs)) {
materialGatherer->addDependency(m_syncRenderViewInitializationJob);
materialGatherer->addDependency(m_renderer->filterCompatibleTechniqueJob());
m_syncRenderCommandBuildingJob->addDependency(materialGatherer);
}
m_syncRenderCommandBuildingJob->addDependency(m_renderableEntityFilterJob);
m_syncRenderCommandBuildingJob->addDependency(m_computableEntityFilterJob);
m_syncRenderCommandBuildingJob->addDependency(m_filterProximityJob);
@ -557,8 +589,17 @@ QVector<Qt3DCore::QAspectJobPtr> RenderViewBuilder::buildJobHierachy() const
jobs.push_back(m_filterProximityJob); // Step 3
jobs.push_back(m_setClearDrawBufferIndexJob); // Step 3
for (const auto &materialGatherer : qAsConst(m_materialGathererJobs)) // Step3
jobs.push_back(materialGatherer);
if (m_materialGathererCacheNeedsToBeRebuilt) {
for (const auto &materialGatherer : qAsConst(m_materialGathererJobs)) {
materialGatherer->addDependency(m_syncRenderViewInitializationJob);
materialGatherer->addDependency(m_renderer->filterCompatibleTechniqueJob());
jobs.push_back(materialGatherer); // Step3
m_syncMaterialGathererJob->addDependency(materialGatherer);
}
m_syncRenderCommandBuildingJob->addDependency(m_syncMaterialGathererJob);
jobs.push_back(m_syncMaterialGathererJob); // Step 3
}
jobs.push_back(m_frustumCullingJob); // Step 4
jobs.push_back(m_syncRenderCommandBuildingJob); // Step 5
@ -591,6 +632,16 @@ bool RenderViewBuilder::layerCacheNeedsToBeRebuilt() const
return m_layerCacheNeedsToBeRebuilt;
}
void RenderViewBuilder::setMaterialGathererCacheNeedsToBeRebuilt(bool needsToBeRebuilt)
{
m_materialGathererCacheNeedsToBeRebuilt = needsToBeRebuilt;
}
bool RenderViewBuilder::materialGathererCacheNeedsToBeRebuilt() const
{
return m_materialGathererCacheNeedsToBeRebuilt;
}
int RenderViewBuilder::optimalJobCount()
{
return RenderViewBuilder::m_optimalParallelJobCount;

View File

@ -96,6 +96,7 @@ public:
SynchronizerJobPtr setClearDrawBufferIndexJob() const;
SynchronizerJobPtr syncFilterEntityByLayerJob() const;
FilterProximityDistanceJobPtr filterProximityJob() const;
SynchronizerJobPtr syncMaterialGathererJob() const;
void prepareJobs();
QVector<Qt3DCore::QAspectJobPtr> buildJobHierachy() const;
@ -105,6 +106,8 @@ public:
void setLayerCacheNeedsToBeRebuilt(bool needsToBeRebuilt);
bool layerCacheNeedsToBeRebuilt() const;
void setMaterialGathererCacheNeedsToBeRebuilt(bool needsToBeRebuilt);
bool materialGathererCacheNeedsToBeRebuilt() const;
static int optimalJobCount();
static void removeEntitiesNotInSubset(QVector<Entity *> &entities, QVector<Entity *> subset);
@ -114,6 +117,7 @@ private:
const int m_renderViewIndex;
Renderer *m_renderer;
bool m_layerCacheNeedsToBeRebuilt;
bool m_materialGathererCacheNeedsToBeRebuilt;
RenderViewInitializerJobPtr m_renderViewJob;
FilterLayerEntityJobPtr m_filterEntityByLayerJob;
@ -130,6 +134,7 @@ private:
SynchronizerJobPtr m_syncRenderViewCommandBuildersJob;
SynchronizerJobPtr m_setClearDrawBufferIndexJob;
SynchronizerJobPtr m_syncFilterEntityByLayerJob;
SynchronizerJobPtr m_syncMaterialGathererJob;
FilterProximityDistanceJobPtr m_filterProximityJob;
static const int m_optimalParallelJobCount;

View File

@ -103,7 +103,8 @@ namespace JobTypes {
LoadSkeleton,
UpdateSkinningPalette,
ProximityFiltering,
SyncFilterEntityByLayer
SyncFilterEntityByLayer,
SyncMaterialGatherer
};
} // JobTypes

View File

@ -79,7 +79,7 @@ public:
inline void setTechniqueFilter(TechniqueFilter *techniqueFilter) Q_DECL_NOTHROW { m_techniqueFilter = techniqueFilter; }
inline void setRenderPassFilter(RenderPassFilter *renderPassFilter) Q_DECL_NOTHROW { m_renderPassFilter = renderPassFilter; }
inline void setRenderer(Renderer *renderer) Q_DECL_NOTHROW { m_renderer = renderer; }
inline QHash<Qt3DCore::QNodeId, QVector<RenderPassParameterData>> &materialToPassAndParameter() Q_DECL_NOTHROW { return m_parameters; }
inline const MaterialParameterGathererData &materialToPassAndParameter() Q_DECL_NOTHROW { return m_parameters; }
inline void setHandles(const QVector<HMaterial> &handles) Q_DECL_NOTHROW { m_handles = handles; }
inline TechniqueFilter *techniqueFilter() const Q_DECL_NOTHROW { return m_techniqueFilter; }
@ -94,7 +94,7 @@ private:
Renderer *m_renderer;
// Material id to array of RenderPasse with parameters
QHash<Qt3DCore::QNodeId, QVector<RenderPassParameterData>> m_parameters;
MaterialParameterGathererData m_parameters;
QVector<HMaterial> m_handles;
};

View File

@ -127,6 +127,7 @@ struct RenderPassParameterData
};
QT3D_DECLARE_TYPEINFO_2(Qt3DRender, Render, RenderPassParameterData, Q_MOVABLE_TYPE)
using MaterialParameterGathererData = QHash<Qt3DCore::QNodeId, QVector<RenderPassParameterData>>;
Q_AUTOTEST_EXPORT void parametersFromMaterialEffectTechnique(ParameterInfoList *infoList,
ParameterManager *manager,

View File

@ -60,7 +60,7 @@ private Q_SLOTS:
renderer.setSettings(&settings);
renderer.initialize();
const int singleRenderViewJobCount = 11 + 2 * Qt3DRender::Render::RenderViewBuilder::optimalJobCount();
const int singleRenderViewJobCount = 11 + 1 * Qt3DRender::Render::RenderViewBuilder::optimalJobCount();
// RenderViewBuilder renderViewJob,
// renderableEntityFilterJob,
// lightGatherJob,

View File

@ -187,6 +187,7 @@ private Q_SLOTS:
QCOMPARE(renderViewBuilder.renderViewIndex(), 0);
QCOMPARE(renderViewBuilder.renderer(), testAspect.renderer());
QCOMPARE(renderViewBuilder.layerCacheNeedsToBeRebuilt(), false);
QCOMPARE(renderViewBuilder.materialGathererCacheNeedsToBeRebuilt(), false);
QVERIFY(!renderViewBuilder.renderViewJob().isNull());
QVERIFY(!renderViewBuilder.lightGathererJob().isNull());
QVERIFY(!renderViewBuilder.renderableEntityFilterJob().isNull());
@ -215,8 +216,8 @@ private Q_SLOTS:
QVERIFY(renderViewBuilder.syncFilterEntityByLayerJob().isNull());
QCOMPARE(renderViewBuilder.renderViewBuilderJobs().size(), Qt3DRender::Render::RenderViewBuilder::optimalJobCount());
QCOMPARE(renderViewBuilder.materialGathererJobs().size(), Qt3DRender::Render::RenderViewBuilder::optimalJobCount());
QCOMPARE(renderViewBuilder.buildJobHierachy().size(), 11 + 2 * Qt3DRender::Render::RenderViewBuilder::optimalJobCount());
QCOMPARE(renderViewBuilder.materialGathererJobs().size(), 0);
QCOMPARE(renderViewBuilder.buildJobHierachy().size(), 11 + 1 * Qt3DRender::Render::RenderViewBuilder::optimalJobCount());
}
{
@ -231,7 +232,22 @@ private Q_SLOTS:
QVERIFY(!renderViewBuilder.syncFilterEntityByLayerJob().isNull());
// mark jobs dirty and recheck
QCOMPARE(renderViewBuilder.buildJobHierachy().size(), 13 + 2 * Qt3DRender::Render::RenderViewBuilder::optimalJobCount());
QCOMPARE(renderViewBuilder.buildJobHierachy().size(), 13 + 1 * Qt3DRender::Render::RenderViewBuilder::optimalJobCount());
}
{
// WHEN
Qt3DRender::Render::RenderViewBuilder renderViewBuilder(leafNode, 0, testAspect.renderer());
renderViewBuilder.setMaterialGathererCacheNeedsToBeRebuilt(true);
renderViewBuilder.prepareJobs();
// THEN
QCOMPARE(renderViewBuilder.materialGathererCacheNeedsToBeRebuilt(), true);
QCOMPARE(renderViewBuilder.materialGathererJobs().size(), Qt3DRender::Render::RenderViewBuilder::optimalJobCount());
QVERIFY(!renderViewBuilder.syncMaterialGathererJob().isNull());
// mark jobs dirty and recheck
QCOMPARE(renderViewBuilder.buildJobHierachy().size(), 12 + 2 * Qt3DRender::Render::RenderViewBuilder::optimalJobCount());
}
}