summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Lemire <paul.lemire@kdab.com>2020-08-24 14:47:17 +0200
committerPaul Lemire <paul.lemire@kdab.com>2020-08-26 12:53:22 +0200
commit9a07ff46ac224e810126622685385993acc6f16b (patch)
tree62e880dd78b53469510d151b89cd857d1a1c9cb0
parent607235b72918a416268b45772c6bbc2f86c32e9b (diff)
Move sync jobs to a common place so that they can be used by rhi and opengl
This reduces duplication and will make it easier in the long run to maintain both backends since what these jobs are doing are doesn't really on the rendering backend in use. Change-Id: I9e51f964880874de52a2fa55c7753a1a5633d023 Reviewed-by: Mike Krus <mike.krus@kdab.com>
-rw-r--r--src/plugins/renderers/opengl/debug/imguirenderer.cpp1
-rw-r--r--src/plugins/renderers/opengl/renderer/rendercommand_p.h9
-rw-r--r--src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp466
-rw-r--r--src/plugins/renderers/opengl/renderer/renderviewbuilder_p.h5
-rw-r--r--src/plugins/renderers/rhi/renderer/rendercommand_p.h9
-rw-r--r--src/plugins/renderers/rhi/renderer/renderview_p.h1
-rw-r--r--src/plugins/renderers/rhi/renderer/renderviewbuilder.cpp467
-rw-r--r--src/plugins/renderers/rhi/renderer/renderviewbuilder_p.h5
-rw-r--r--src/render/CMakeLists.txt1
-rw-r--r--src/render/jobs/jobs.pri3
-rw-r--r--src/render/jobs/renderercache_p.h3
-rw-r--r--src/render/jobs/rendersyncjobs_p.h539
-rw-r--r--src/render/jobs/renderviewjobutils.cpp18
-rw-r--r--src/render/jobs/renderviewjobutils_p.h6
-rw-r--r--tests/auto/render/opengl/renderviewbuilder/tst_renderviewbuilder.cpp3
15 files changed, 576 insertions, 960 deletions
diff --git a/src/plugins/renderers/opengl/debug/imguirenderer.cpp b/src/plugins/renderers/opengl/debug/imguirenderer.cpp
index 5ee72985a..3d4e9e614 100644
--- a/src/plugins/renderers/opengl/debug/imguirenderer.cpp
+++ b/src/plugins/renderers/opengl/debug/imguirenderer.cpp
@@ -46,6 +46,7 @@
#include "imguirenderer_p.h"
#include <renderview_p.h>
+#include <rendercommand_p.h>
#include <renderer_p.h>
#include <submissioncontext_p.h>
#include <Qt3DRender/private/geometryrenderermanager_p.h>
diff --git a/src/plugins/renderers/opengl/renderer/rendercommand_p.h b/src/plugins/renderers/opengl/renderer/rendercommand_p.h
index 710430073..09ed04855 100644
--- a/src/plugins/renderers/opengl/renderer/rendercommand_p.h
+++ b/src/plugins/renderers/opengl/renderer/rendercommand_p.h
@@ -75,15 +75,6 @@ namespace Render {
class RenderStateSet;
using RenderStateSetPtr = QSharedPointer<RenderStateSet>;
-enum RebuildFlag {
- FullCommandRebuild = 1 << 0,
- LayerCacheRebuild = 1 << 1,
- MaterialCacheRebuild = 1 << 2,
- LightCacheRebuild = 1 << 3
-};
-Q_DECLARE_FLAGS(RebuildFlagSet, RebuildFlag)
-Q_DECLARE_OPERATORS_FOR_FLAGS(RebuildFlagSet)
-
namespace OpenGL {
class GLShader;
diff --git a/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp b/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp
index 26ad8f0ec..6ba85db9e 100644
--- a/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp
+++ b/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp
@@ -39,6 +39,7 @@
#include "renderviewbuilder_p.h"
#include <Qt3DRender/private/qrenderaspect_p.h>
+#include <Qt3DRender/private/rendersyncjobs_p.h>
#include <QThread>
@@ -49,398 +50,8 @@ namespace Qt3DRender {
namespace Render {
namespace OpenGL {
-using RendererCache = Render::RendererCache<RenderCommand>;
-
namespace {
-int findIdealNumberOfWorkers(int elementCount, int packetSize = 100, int maxJobCount = 1)
-{
- if (elementCount == 0 || packetSize == 0)
- return 0;
- return std::min(std::max(elementCount / packetSize, 1), maxJobCount);
-}
-
-
-class SyncPreCommandBuilding
-{
-public:
- explicit SyncPreCommandBuilding(RenderViewInitializerJobPtr renderViewInitializerJob,
- const std::vector<RenderViewCommandBuilderJobPtr> &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
-
- // Rebuild RenderCommands for all entities in RV (ignoring filtering)
- RendererCache *cache = m_renderer->cache();
- QMutexLocker lock(cache->mutex());
-
- Q_ASSERT(cache->leafNodeCache.contains(m_leafNode));
- // The cache leaf should already have been created so we don't need to protect the access
- 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);
-
- // Split among the ideal number of command builders
- const int jobCount = m_renderViewCommandBuilderJobs.size();
- const int entityCount = entities.size();
- const int idealPacketSize = std::min(std::max(10, entityCount / jobCount), entityCount);
- // Try to split work into an ideal number of workers
- const int m = findIdealNumberOfWorkers(entityCount, idealPacketSize, jobCount);
-
- const Entity **entitiesPtr = const_cast<const Entity **>(entities.data());
- for (int i = 0; i < m; ++i) {
- const RenderViewCommandBuilderJobPtr renderViewCommandBuilder = m_renderViewCommandBuilderJobs.at(i);
- const int count = (i == m - 1) ? entityCount - (i * idealPacketSize) : idealPacketSize;
- renderViewCommandBuilder->setEntities(entitiesPtr, i * idealPacketSize, count);
- }
- }
-
-private:
- RenderViewInitializerJobPtr m_renderViewInitializer;
- std::vector<RenderViewCommandBuilderJobPtr> m_renderViewCommandBuilderJobs;
- Renderer *m_renderer;
- FrameGraphNode *m_leafNode;
-};
-
-class SyncRenderViewPostCommandUpdate
-{
-public:
- explicit SyncRenderViewPostCommandUpdate(const RenderViewInitializerJobPtr &renderViewJob,
- const std::vector<RenderViewCommandUpdaterJobPtr> &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();
-
- if (!rv->noDraw()) {
- // Sort command on RenderView
- rv->sort();
- }
- // Enqueue our fully populated RenderView with the RenderThread
- m_renderer->enqueueRenderView(rv, m_renderViewJob->submitOrderIndex());
- }
-
-private:
- RenderViewInitializerJobPtr m_renderViewJob;
- std::vector<RenderViewCommandUpdaterJobPtr> 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 std::vector<MaterialParameterGathererJobPtr> &materialGathererJobs,
- const std::vector<RenderViewCommandUpdaterJobPtr> &renderViewCommandUpdaterJobs,
- const std::vector<RenderViewCommandBuilderJobPtr> &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 : m_materialGathererJobs) {
- materialGatherer->setRenderPassFilter(const_cast<RenderPassFilter *>(rv->renderPassFilter()));
- materialGatherer->setTechniqueFilter(const_cast<TechniqueFilter *>(rv->techniqueFilter()));
- }
-
- // Command builders and updates
- for (const auto &renderViewCommandUpdater : m_renderViewCommandUpdaterJobs)
- renderViewCommandUpdater->setRenderView(rv);
- for (const auto &renderViewCommandBuilder : 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;
- std::vector<MaterialParameterGathererJobPtr> m_materialGathererJobs;
- std::vector<RenderViewCommandUpdaterJobPtr> m_renderViewCommandUpdaterJobs;
- std::vector<RenderViewCommandBuilderJobPtr> m_renderViewCommandBuilderJobs;
-};
-
-class SyncRenderViewPreCommandUpdate
-{
-public:
- explicit SyncRenderViewPreCommandUpdate(const RenderViewInitializerJobPtr &renderViewJob,
- const FrustumCullingJobPtr &frustumCullingJob,
- const FilterProximityDistanceJobPtr &filterProximityJob,
- const std::vector<MaterialParameterGathererJobPtr> &materialGathererJobs,
- const std::vector<RenderViewCommandUpdaterJobPtr> &renderViewCommandUpdaterJobs,
- const std::vector<RenderViewCommandBuilderJobPtr> &renderViewCommandBuilderJobs,
- Renderer *renderer,
- FrameGraphNode *leafNode,
- RebuildFlagSet rebuildFlags)
- : 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_rebuildFlags(rebuildFlags)
- {}
-
- 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));
-
- // We don't need to protect the cache access as
- // 1) The cache leaf is created
- // 2) We are only reading conccurently the cache values that are shared across all RV
- // 3) Each instance of this job is reading and writing in its own cache leaf so there's
- // no conflict
-
- const bool isDraw = !rv->isCompute();
- RendererCache::LeafNodeData &cacheForLeaf = cache->leafNodeCache[m_leafNode];
-
- const bool fullRebuild = m_rebuildFlags.testFlag(RebuildFlag::FullCommandRebuild);
- const bool layerFilteringRebuild = m_rebuildFlags.testFlag(RebuildFlag::LayerCacheRebuild);
- const bool lightsCacheRebuild = m_rebuildFlags.testFlag(RebuildFlag::LightCacheRebuild);
- const bool cameraDirty = cacheForLeaf.viewProjectionMatrix != rv->viewProjectionMatrix();
- const bool hasProximityFilter = !rv->proximityFilterIds().empty();
- const bool commandFilteringRequired =
- fullRebuild ||
- layerFilteringRebuild ||
- lightsCacheRebuild ||
- cameraDirty ||
- hasProximityFilter;
-
- // If we have no filteredRenderCommandDataViews then we should have fullRebuild set to true
- // otherwise something is wrong
- Q_ASSERT(fullRebuild || cacheForLeaf.filteredRenderCommandDataViews);
-
- // 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 (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
- EntityRenderCommandDataViewPtr dataView = EntityRenderCommandDataViewPtr::create();
- dataView->data = std::move(commandData);
- // Store the update dataView
- cacheForLeaf.filteredRenderCommandDataViews = dataView;
- }
-
-
- // Should be fairly infrequent
- if (layerFilteringRebuild || fullRebuild) {
- // Filter out renderable entities that weren't selected by the layer filters and store that in cache
- cacheForLeaf.layeredFilteredRenderables = RenderViewBuilder::entitiesInSubset(
- isDraw ? cache->renderableEntities : cache->computeEntities,
- cacheForLeaf.filterEntitiesByLayer);
- // Set default value for filteredAndCulledRenderables
- if (isDraw)
- cacheForLeaf.filteredAndCulledRenderables = cacheForLeaf.layeredFilteredRenderables;
- }
-
- // Should be fairly infrequent
- if (lightsCacheRebuild) {
- // Filter out light sources that weren't selected by the
- // layer filters and store that in cache
- const std::vector<Entity *> &layeredFilteredEntities = cacheForLeaf.filterEntitiesByLayer;
- std::vector<LightSource> filteredLightSources = cache->gatheredLights;
-
- auto it = filteredLightSources.begin();
-
- while (it != filteredLightSources.end()) {
- if (!std::binary_search(layeredFilteredEntities.begin(),
- layeredFilteredEntities.end(),
- it->entity))
- it = filteredLightSources.erase(it);
- else
- ++it;
- }
- cacheForLeaf.layeredFilteredLightSources = std::move(filteredLightSources);
- }
-
- // This is likely very frequent
- if (cameraDirty) {
- // Record the updated viewProjectionMatrix in the cache to allow check to be performed
- // next frame
- cacheForLeaf.viewProjectionMatrix = rv->viewProjectionMatrix();
- }
-
- // Filter out frustum culled entity for drawable entities and store in cache
- // We need to check this regardless of whether the camera has moved since
- // entities in the scene themselves could have moved
- if (isDraw && rv->frustumCulling()) {
- cacheForLeaf.filteredAndCulledRenderables = RenderViewBuilder::entitiesInSubset(
- cacheForLeaf.layeredFilteredRenderables,
- m_frustumCullingJob->visibleEntities());
- }
-
- rv->setMaterialParameterTable(cacheForLeaf.materialParameterGatherer);
- rv->setEnvironmentLight(cache->environmentLight);
-
- // Set the light sources, with layer filters applied.
- rv->setLightSources(cacheForLeaf.layeredFilteredLightSources);
-
- std::vector<Entity *> renderableEntities = isDraw ? cacheForLeaf.filteredAndCulledRenderables : cacheForLeaf.layeredFilteredRenderables;
-
- // TO DO: Find a way to do that only if proximity entities has changed
- if (isDraw) {
- // Filter out entities which didn't satisfy proximity filtering
- if (hasProximityFilter)
- renderableEntities = RenderViewBuilder::entitiesInSubset(renderableEntities,
- m_filterProximityJob->filteredEntities());
- }
-
- EntityRenderCommandDataViewPtr filteredCommandData = cacheForLeaf.filteredRenderCommandDataViews;
-
- // Set RenderCommandDataView on RV (will be used later on to sort commands ...)
- rv->setRenderCommandDataView(filteredCommandData);
-
- // Filter out Render commands for which the Entity wasn't selected because
- // of frustum, proximity or layer filtering
- if (commandFilteringRequired) {
- const std::vector<const Entity *> &entities = filteredCommandData->data.entities;
- // Because cacheForLeaf.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();
- size_t cIt = 0;
- const size_t cEnd = entities.size();
-
- std::vector<size_t> filteredCommandIndices;
- filteredCommandIndices.reserve(renderableEntities.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 && entities[cIt] < targetEntity)
- ++cIt;
-
- // Push pointers to command data for all commands that match the
- // entity
- while (cIt != cEnd && entities[cIt] == targetEntity) {
- filteredCommandIndices.push_back(cIt);
- ++cIt;
- }
- ++eIt;
- }
-
- // Store result in cache
- cacheForLeaf.filteredRenderCommandDataViews->indices = std::move(filteredCommandIndices);
- }
-
- // Split among the number of command updaters
- const int jobCount = m_renderViewCommandUpdaterJobs.size();
- const int commandCount = filteredCommandData->size();
- const int idealPacketSize = std::min(std::max(10, commandCount), commandCount);
- const int m = findIdealNumberOfWorkers(commandCount, idealPacketSize, jobCount);
-
- for (int i = 0; i < m; ++i) {
- // TO DO: Based on whether we had to update the commands
- // we should be able to know what needs to be recomputed
- // -> lights/standard uniforms ... might no have to be set over and over again
- // if they are identical
- const RenderViewCommandUpdaterJobPtr &renderViewCommandUpdater = m_renderViewCommandUpdaterJobs.at(i);
- const size_t count = (i == m - 1) ? commandCount - (i * idealPacketSize) : idealPacketSize;
- renderViewCommandUpdater->setRenderablesSubView({filteredCommandData, size_t(i * idealPacketSize), count});
- }
- }
- }
-
-private:
- RenderViewInitializerJobPtr m_renderViewJob;
- FrustumCullingJobPtr m_frustumCullingJob;
- FilterProximityDistanceJobPtr m_filterProximityJob;
- std::vector<MaterialParameterGathererJobPtr> m_materialGathererJobs;
- std::vector<RenderViewCommandUpdaterJobPtr> m_renderViewCommandUpdaterJobs;
- std::vector<RenderViewCommandBuilderJobPtr> m_renderViewCommandBuilderJobs;
- Renderer *m_renderer;
- FrameGraphNode *m_leafNode;
- RebuildFlagSet m_rebuildFlags;
-};
-
class SetClearDrawBufferIndex
{
public:
@@ -462,69 +73,6 @@ 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());
- Q_ASSERT(m_renderer->cache()->leafNodeCache.contains(m_leafNode));
- // The cache leaf should already have been created so we don't need to protect the access
- RendererCache::LeafNodeData &dataCacheForLeaf = m_renderer->cache()->leafNodeCache[m_leafNode];
- // Save the filtered by layer subset into the cache
- dataCacheForLeaf.filterEntitiesByLayer = std::move(m_filterEntityByLayerJob->filteredEntities());
- }
-
-private:
- FilterLayerEntityJobPtr m_filterEntityByLayerJob;
- Renderer *m_renderer;
- FrameGraphNode *m_leafNode;
-};
-
-class SyncMaterialParameterGatherer
-{
-public:
- explicit SyncMaterialParameterGatherer(const std::vector<MaterialParameterGathererJobPtr> &materialParameterGathererJobs,
- Renderer *renderer,
- FrameGraphNode *leafNode)
- : m_materialParameterGathererJobs(materialParameterGathererJobs)
- , m_renderer(renderer)
- , m_leafNode(leafNode)
- {
- }
-
- void operator()()
- {
- // The cache leaf was created by SyncRenderViewPostInitialization on which we depend
- // so we don't need to protect the access
- QMutexLocker lock(m_renderer->cache()->mutex());
- RendererCache::LeafNodeData &dataCacheForLeaf = m_renderer->cache()->leafNodeCache[m_leafNode];
- dataCacheForLeaf.materialParameterGatherer.clear();
-
- for (const auto &materialGatherer : m_materialParameterGathererJobs) {
- const MaterialParameterGathererData &source = materialGatherer->materialToPassAndParameter();
- for (auto it = std::begin(source); it != std::end(source); ++it) {
- Q_ASSERT(!dataCacheForLeaf.materialParameterGatherer.contains(it.key()));
- dataCacheForLeaf.materialParameterGatherer.insert(it.key(), it.value());
- }
- }
- }
-
-private:
- std::vector<MaterialParameterGathererJobPtr> m_materialParameterGathererJobs;
- Renderer *m_renderer;
- FrameGraphNode *m_leafNode;
-};
-
} // anonymous
RenderViewBuilder::RenderViewBuilder(Render::FrameGraphNode *leafNode, int renderViewIndex, Renderer *renderer)
@@ -917,18 +465,6 @@ void RenderViewBuilder::setOptimalJobCount(int v)
m_optimalParallelJobCount = v;
}
-std::vector<Entity *> RenderViewBuilder::entitiesInSubset(const std::vector<Entity *> &entities,
- const std::vector<Entity *> &subset)
-{
- std::vector<Entity *> 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;
-}
-
} // OpenGL
} // Render
diff --git a/src/plugins/renderers/opengl/renderer/renderviewbuilder_p.h b/src/plugins/renderers/opengl/renderer/renderviewbuilder_p.h
index 77fec9f0c..a2a515d1a 100644
--- a/src/plugins/renderers/opengl/renderer/renderviewbuilder_p.h
+++ b/src/plugins/renderers/opengl/renderer/renderviewbuilder_p.h
@@ -61,6 +61,8 @@
#include <Qt3DRender/private/materialparametergathererjob_p.h>
#include <Qt3DRender/private/renderviewcommandbuilderjob_p.h>
#include <Qt3DRender/private/renderviewcommandupdaterjob_p.h>
+#include <Qt3DRender/private/rendersyncjobs_p.h>
+#include <rendercommand_p.h>
#include <renderview_p.h>
QT_BEGIN_NAMESPACE
@@ -121,9 +123,6 @@ public:
int optimalJobCount() const;
void setOptimalJobCount(int v);
- static std::vector<Entity *> entitiesInSubset(const std::vector<Entity *> &entities,
- const std::vector<Entity *> &subset);
-
private:
Render::FrameGraphNode *m_leafNode;
const int m_renderViewIndex;
diff --git a/src/plugins/renderers/rhi/renderer/rendercommand_p.h b/src/plugins/renderers/rhi/renderer/rendercommand_p.h
index 031b4fca2..a4f7332de 100644
--- a/src/plugins/renderers/rhi/renderer/rendercommand_p.h
+++ b/src/plugins/renderers/rhi/renderer/rendercommand_p.h
@@ -77,15 +77,6 @@ namespace Render {
class RenderStateSet;
using RenderStateSetPtr = QSharedPointer<RenderStateSet>;
-enum RebuildFlag {
- FullCommandRebuild = 1 << 0,
- LayerCacheRebuild = 1 << 1,
- MaterialCacheRebuild = 1 << 2,
- LightCacheRebuild = 1 << 3
-};
-Q_DECLARE_FLAGS(RebuildFlagSet, RebuildFlag)
-Q_DECLARE_OPERATORS_FOR_FLAGS(RebuildFlagSet)
-
namespace Rhi {
class RHIShader;
diff --git a/src/plugins/renderers/rhi/renderer/renderview_p.h b/src/plugins/renderers/rhi/renderer/renderview_p.h
index 8600b1591..87ff7407f 100644
--- a/src/plugins/renderers/rhi/renderer/renderview_p.h
+++ b/src/plugins/renderers/rhi/renderer/renderview_p.h
@@ -97,6 +97,7 @@ namespace Rhi {
class Renderer;
class RenderCommand;
+class RHIShader;
typedef QPair<ShaderUniform, QVariant> ActivePropertyContent;
typedef QPair<QString, ActivePropertyContent> ActiveProperty;
diff --git a/src/plugins/renderers/rhi/renderer/renderviewbuilder.cpp b/src/plugins/renderers/rhi/renderer/renderviewbuilder.cpp
index 9b78c3136..138d53612 100644
--- a/src/plugins/renderers/rhi/renderer/renderviewbuilder.cpp
+++ b/src/plugins/renderers/rhi/renderer/renderviewbuilder.cpp
@@ -49,461 +49,6 @@ namespace Qt3DRender {
namespace Render {
namespace Rhi {
-using RendererCache = Render::RendererCache<RenderCommand>;
-
-namespace {
-
-int findIdealNumberOfWorkers(int elementCount, int packetSize = 100, int maxJobCount = 1)
-{
- if (elementCount == 0 || packetSize == 0)
- return 0;
- return std::min(std::max(elementCount / packetSize, 1), maxJobCount);
-}
-
-
-class SyncPreCommandBuilding
-{
-public:
- explicit SyncPreCommandBuilding(RenderViewInitializerJobPtr renderViewInitializerJob,
- const std::vector<RenderViewCommandBuilderJobPtr> &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
-
- // Rebuild RenderCommands for all entities in RV (ignoring filtering)
- RendererCache *cache = m_renderer->cache();
- QMutexLocker lock(m_renderer->cache()->mutex());
- Q_ASSERT(cache->leafNodeCache.contains(m_leafNode));
- // The cache leaf should already have been created so we don't need to protect the access
- 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);
-
- // Split among the ideal number of command builders
- const int jobCount = m_renderViewCommandBuilderJobs.size();
- const int entityCount = entities.size();
- const int idealPacketSize = std::min(std::max(10, entityCount / jobCount), entityCount);
- // Try to split work into an ideal number of workers
- const int m = findIdealNumberOfWorkers(entityCount, idealPacketSize, jobCount);
-
- const Entity **entitiesPtr = const_cast<const Entity **>(entities.data());
- for (int i = 0; i < m; ++i) {
- const RenderViewCommandBuilderJobPtr renderViewCommandBuilder = m_renderViewCommandBuilderJobs.at(i);
- const int count = (i == m - 1) ? entityCount - (i * idealPacketSize) : idealPacketSize;
- renderViewCommandBuilder->setEntities(entitiesPtr, i * idealPacketSize, count);
- }
- }
-
-private:
- RenderViewInitializerJobPtr m_renderViewInitializer;
- std::vector<RenderViewCommandBuilderJobPtr> m_renderViewCommandBuilderJobs;
- Renderer *m_renderer;
- FrameGraphNode *m_leafNode;
-};
-
-class SyncRenderViewPostCommandUpdate
-{
-public:
- explicit SyncRenderViewPostCommandUpdate(const RenderViewInitializerJobPtr &renderViewJob,
- const std::vector<RenderViewCommandUpdaterJobPtr> &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();
-
- if (!rv->noDraw()) {
- // Sort command on RenderView
- rv->sort();
- }
- // Enqueue our fully populated RenderView with the RenderThread
- m_renderer->enqueueRenderView(rv, m_renderViewJob->submitOrderIndex());
- }
-
-private:
- RenderViewInitializerJobPtr m_renderViewJob;
- std::vector<RenderViewCommandUpdaterJobPtr> 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 std::vector<MaterialParameterGathererJobPtr> &materialGathererJobs,
- const std::vector<RenderViewCommandUpdaterJobPtr> &renderViewCommandUpdaterJobs,
- const std::vector<RenderViewCommandBuilderJobPtr> &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 : m_materialGathererJobs) {
- materialGatherer->setRenderPassFilter(const_cast<RenderPassFilter *>(rv->renderPassFilter()));
- materialGatherer->setTechniqueFilter(const_cast<TechniqueFilter *>(rv->techniqueFilter()));
- }
-
- // Command builders and updates
- for (const auto &renderViewCommandUpdater : m_renderViewCommandUpdaterJobs)
- renderViewCommandUpdater->setRenderView(rv);
- for (const auto &renderViewCommandBuilder : 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;
- std::vector<MaterialParameterGathererJobPtr> m_materialGathererJobs;
- std::vector<RenderViewCommandUpdaterJobPtr> m_renderViewCommandUpdaterJobs;
- std::vector<RenderViewCommandBuilderJobPtr> m_renderViewCommandBuilderJobs;
-};
-
-class SyncRenderViewPreCommandUpdate
-{
-public:
- explicit SyncRenderViewPreCommandUpdate(const RenderViewInitializerJobPtr &renderViewJob,
- const FrustumCullingJobPtr &frustumCullingJob,
- const FilterProximityDistanceJobPtr &filterProximityJob,
- const std::vector<MaterialParameterGathererJobPtr> &materialGathererJobs,
- const std::vector<RenderViewCommandUpdaterJobPtr> &renderViewCommandUpdaterJobs,
- const std::vector<RenderViewCommandBuilderJobPtr> &renderViewCommandBuilderJobs,
- Renderer *renderer,
- FrameGraphNode *leafNode,
- RebuildFlagSet rebuildFlags)
- : 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_rebuildFlags(rebuildFlags)
- {}
-
- 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));
-
- // We don't need to protect the cache access as
- // 1) The cache leaf is created
- // 2) We are only reading conccurently the cache values that are shared across all RV
- // 3) Each instance of this job is reading and writing in its own cache leaf so there's
- // no conflict
-
- const bool isDraw = !rv->isCompute();
- RendererCache::LeafNodeData &cacheForLeaf = cache->leafNodeCache[m_leafNode];
-
- const bool fullRebuild = m_rebuildFlags.testFlag(RebuildFlag::FullCommandRebuild);
- const bool layerFilteringRebuild = m_rebuildFlags.testFlag(RebuildFlag::LayerCacheRebuild);
- const bool lightsCacheRebuild = m_rebuildFlags.testFlag(RebuildFlag::LightCacheRebuild);
- const bool cameraDirty = cacheForLeaf.viewProjectionMatrix != rv->viewProjectionMatrix();
- const bool hasProximityFilter = !rv->proximityFilterIds().empty();
- const bool commandFilteringRequired =
- fullRebuild ||
- layerFilteringRebuild ||
- lightsCacheRebuild ||
- cameraDirty ||
- hasProximityFilter;
-
- // If we have no filteredRenderCommandDataViews then we should have fullRebuild set to true
- // otherwise something is wrong
- Q_ASSERT(fullRebuild || cacheForLeaf.filteredRenderCommandDataViews);
-
- // 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 (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
- EntityRenderCommandDataViewPtr dataView = EntityRenderCommandDataViewPtr::create();
- dataView->data = std::move(commandData);
- // Store the update dataView
- cacheForLeaf.filteredRenderCommandDataViews = dataView;
- }
-
-
- // Should be fairly infrequent
- if (layerFilteringRebuild || fullRebuild) {
- // Filter out renderable entities that weren't selected by the layer filters and store that in cache
- cacheForLeaf.layeredFilteredRenderables = RenderViewBuilder::entitiesInSubset(
- isDraw ? cache->renderableEntities : cache->computeEntities,
- cacheForLeaf.filterEntitiesByLayer);
- // Set default value for filteredAndCulledRenderables
- if (isDraw)
- cacheForLeaf.filteredAndCulledRenderables = cacheForLeaf.layeredFilteredRenderables;
- }
-
- // Should be fairly infrequent
- if (lightsCacheRebuild) {
- // Filter out light sources that weren't selected by the
- // layer filters and store that in cache
- const std::vector<Entity *> &layeredFilteredEntities = cacheForLeaf.filterEntitiesByLayer;
- std::vector<LightSource> filteredLightSources = cache->gatheredLights;
-
- auto it = filteredLightSources.begin();
-
- while (it != filteredLightSources.end()) {
- if (!std::binary_search(layeredFilteredEntities.begin(),
- layeredFilteredEntities.end(),
- it->entity))
- it = filteredLightSources.erase(it);
- else
- ++it;
- }
- cacheForLeaf.layeredFilteredLightSources = std::move(filteredLightSources);
- }
-
- // This is likely very frequent
- if (cameraDirty) {
- // Record the updated viewProjectionMatrix in the cache to allow check to be performed
- // next frame
- cacheForLeaf.viewProjectionMatrix = rv->viewProjectionMatrix();
- }
-
- // Filter out frustum culled entity for drawable entities and store in cache
- // We need to check this regardless of whether the camera has moved since
- // entities in the scene themselves could have moved
- if (isDraw && rv->frustumCulling()) {
- cacheForLeaf.filteredAndCulledRenderables = RenderViewBuilder::entitiesInSubset(
- cacheForLeaf.layeredFilteredRenderables,
- m_frustumCullingJob->visibleEntities());
- }
-
- rv->setMaterialParameterTable(cacheForLeaf.materialParameterGatherer);
- rv->setEnvironmentLight(cache->environmentLight);
-
- // Set the light sources, with layer filters applied.
- rv->setLightSources(cacheForLeaf.layeredFilteredLightSources);
-
- std::vector<Entity *> renderableEntities = isDraw ? cacheForLeaf.filteredAndCulledRenderables : cacheForLeaf.layeredFilteredRenderables;
-
- // TO DO: Find a way to do that only if proximity entities has changed
- if (isDraw) {
- // Filter out entities which didn't satisfy proximity filtering
- if (hasProximityFilter)
- renderableEntities = RenderViewBuilder::entitiesInSubset(renderableEntities,
- m_filterProximityJob->filteredEntities());
- }
-
- EntityRenderCommandDataViewPtr filteredCommandData = cacheForLeaf.filteredRenderCommandDataViews;
-
- // Set RenderCommandDataView on RV (will be used later on to sort commands ...)
- rv->setRenderCommandDataView(filteredCommandData);
-
- // Filter out Render commands for which the Entity wasn't selected because
- // of frustum, proximity or layer filtering
- if (commandFilteringRequired) {
- const std::vector<const Entity *> &entities = filteredCommandData->data.entities;
- // Because cacheForLeaf.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();
- size_t cIt = 0;
- const size_t cEnd = entities.size();
-
- std::vector<size_t> filteredCommandIndices;
- filteredCommandIndices.reserve(renderableEntities.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 && entities[cIt] < targetEntity)
- ++cIt;
-
- // Push pointers to command data for all commands that match the
- // entity
- while (cIt != cEnd && entities[cIt] == targetEntity) {
- filteredCommandIndices.push_back(cIt);
- ++cIt;
- }
- ++eIt;
- }
-
- // Store result in cache
- cacheForLeaf.filteredRenderCommandDataViews->indices = std::move(filteredCommandIndices);
- }
-
- // Split among the number of command updaters
- const int jobCount = m_renderViewCommandUpdaterJobs.size();
- const int commandCount = filteredCommandData->size();
- const int idealPacketSize = std::min(std::max(10, commandCount), commandCount);
- const int m = findIdealNumberOfWorkers(commandCount, idealPacketSize, jobCount);
-
- for (int i = 0; i < m; ++i) {
- // TO DO: Based on whether we had to update the commands
- // we should be able to know what needs to be recomputed
- // -> lights/standard uniforms ... might no have to be set over and over again
- // if they are identical
- const RenderViewCommandUpdaterJobPtr &renderViewCommandUpdater = m_renderViewCommandUpdaterJobs.at(i);
- const size_t count = (i == m - 1) ? commandCount - (i * idealPacketSize) : idealPacketSize;
- renderViewCommandUpdater->setRenderablesSubView({filteredCommandData, size_t(i * idealPacketSize), count});
- }
- }
- }
-
-private:
- RenderViewInitializerJobPtr m_renderViewJob;
- FrustumCullingJobPtr m_frustumCullingJob;
- FilterProximityDistanceJobPtr m_filterProximityJob;
- std::vector<MaterialParameterGathererJobPtr> m_materialGathererJobs;
- std::vector<RenderViewCommandUpdaterJobPtr> m_renderViewCommandUpdaterJobs;
- std::vector<RenderViewCommandBuilderJobPtr> m_renderViewCommandBuilderJobs;
- Renderer *m_renderer;
- FrameGraphNode *m_leafNode;
- RebuildFlagSet m_rebuildFlags;
-};
-
-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());
- Q_ASSERT(m_renderer->cache()->leafNodeCache.contains(m_leafNode));
- // The cache leaf should already have been created so we don't need to protect the access
- RendererCache::LeafNodeData &dataCacheForLeaf = m_renderer->cache()->leafNodeCache[m_leafNode];
- // Save the filtered by layer subset into the cache
- dataCacheForLeaf.filterEntitiesByLayer = std::move(m_filterEntityByLayerJob->filteredEntities());
- }
-
-private:
- FilterLayerEntityJobPtr m_filterEntityByLayerJob;
- Renderer *m_renderer;
- FrameGraphNode *m_leafNode;
-};
-
-class SyncMaterialParameterGatherer
-{
-public:
- explicit SyncMaterialParameterGatherer(const std::vector<MaterialParameterGathererJobPtr> &materialParameterGathererJobs,
- Renderer *renderer,
- FrameGraphNode *leafNode)
- : m_materialParameterGathererJobs(materialParameterGathererJobs)
- , m_renderer(renderer)
- , m_leafNode(leafNode)
- {
- }
-
- void operator()()
- {
- // The cache leaf was created by SyncRenderViewPostInitialization on which we depend
- // so we don't need to protect the access
- QMutexLocker lock(m_renderer->cache()->mutex());
- RendererCache::LeafNodeData &dataCacheForLeaf = m_renderer->cache()->leafNodeCache[m_leafNode];
- dataCacheForLeaf.materialParameterGatherer.clear();
-
- for (const auto &materialGatherer : m_materialParameterGathererJobs) {
- const MaterialParameterGathererData &source = materialGatherer->materialToPassAndParameter();
- for (auto it = std::begin(source); it != std::end(source); ++it) {
- Q_ASSERT(!dataCacheForLeaf.materialParameterGatherer.contains(it.key()));
- dataCacheForLeaf.materialParameterGatherer.insert(it.key(), it.value());
- }
- }
- }
-
-private:
- std::vector<MaterialParameterGathererJobPtr> m_materialParameterGathererJobs;
- Renderer *m_renderer;
- FrameGraphNode *m_leafNode;
-};
-
-} // anonymous
-
RenderViewBuilder::RenderViewBuilder(Render::FrameGraphNode *leafNode, int renderViewIndex, Renderer *renderer)
: m_leafNode(leafNode)
, m_renderViewIndex(renderViewIndex)
@@ -878,18 +423,6 @@ void RenderViewBuilder::setOptimalJobCount(int v)
m_optimalParallelJobCount = v;
}
-std::vector<Entity *> RenderViewBuilder::entitiesInSubset(const std::vector<Entity *> &entities,
- const std::vector<Entity *> &subset)
-{
- std::vector<Entity *> 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;
-}
-
} // Rhi
} // Render
diff --git a/src/plugins/renderers/rhi/renderer/renderviewbuilder_p.h b/src/plugins/renderers/rhi/renderer/renderviewbuilder_p.h
index 6883a10e0..790d2d5e6 100644
--- a/src/plugins/renderers/rhi/renderer/renderviewbuilder_p.h
+++ b/src/plugins/renderers/rhi/renderer/renderviewbuilder_p.h
@@ -61,6 +61,8 @@
#include <Qt3DRender/private/materialparametergathererjob_p.h>
#include <Qt3DRender/private/renderviewcommandbuilderjob_p.h>
#include <Qt3DRender/private/renderviewcommandupdaterjob_p.h>
+#include <Qt3DRender/private/rendersyncjobs_p.h>
+#include <rendercommand_p.h>
#include <renderview_p.h>
QT_BEGIN_NAMESPACE
@@ -120,9 +122,6 @@ public:
int optimalJobCount() const;
void setOptimalJobCount(int v);
- static std::vector<Entity *> entitiesInSubset(const std::vector<Entity *> &entities,
- const std::vector<Entity *> &subset);
-
private:
Render::FrameGraphNode *m_leafNode;
const int m_renderViewIndex;
diff --git a/src/render/CMakeLists.txt b/src/render/CMakeLists.txt
index 061dae524..5380659cc 100644
--- a/src/render/CMakeLists.txt
+++ b/src/render/CMakeLists.txt
@@ -170,6 +170,7 @@ qt_add_module(3DRender
jobs/renderviewcommandbuilderjob_p.h
jobs/renderviewcommandupdaterjob_p.h
jobs/renderviewinitializerjob_p.h
+ jobs/rendersyncjobs_p.h
lights/environmentlight.cpp lights/environmentlight_p.h
lights/light.cpp lights/light_p.h
lights/lightsource.cpp lights/lightsource_p.h
diff --git a/src/render/jobs/jobs.pri b/src/render/jobs/jobs.pri
index 20dc9d1f9..c258202ac 100644
--- a/src/render/jobs/jobs.pri
+++ b/src/render/jobs/jobs.pri
@@ -35,7 +35,8 @@ HEADERS += \
$$PWD/renderercache_p.h \
$$PWD/renderviewcommandbuilderjob_p.h \
$$PWD/renderviewcommandupdaterjob_p.h \
- $$PWD/renderviewinitializerjob_p.h
+ $$PWD/renderviewinitializerjob_p.h \
+ $$PWD/rendersyncjobs_p.h
SOURCES += \
$$PWD/updateworldtransformjob.cpp \
diff --git a/src/render/jobs/renderercache_p.h b/src/render/jobs/renderercache_p.h
index 46e9aadc2..1be46a3d4 100644
--- a/src/render/jobs/renderercache_p.h
+++ b/src/render/jobs/renderercache_p.h
@@ -51,12 +51,11 @@
// We mean it.
//
+#include <Qt3DCore/private/vector_helper_p.h>
#include <Qt3DRender/QFrameGraphNode>
-
#include <Qt3DRender/private/entity_p.h>
#include <Qt3DRender/private/renderviewjobutils_p.h>
#include <Qt3DRender/private/lightsource_p.h>
-#include <rendercommand_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/render/jobs/rendersyncjobs_p.h b/src/render/jobs/rendersyncjobs_p.h
new file mode 100644
index 000000000..c2ad714a3
--- /dev/null
+++ b/src/render/jobs/rendersyncjobs_p.h
@@ -0,0 +1,539 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 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$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_RENDERSYNCJOBS_H
+#define QT3DRENDER_RENDER_RENDERSYNCJOBS_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.h>
+#include <Qt3DRender/private/renderviewinitializerjob_p.h>
+#include <Qt3DRender/private/frustumcullingjob_p.h>
+#include <Qt3DRender/private/filterlayerentityjob_p.h>
+#include <Qt3DRender/private/filterproximitydistancejob_p.h>
+#include <Qt3DRender/private/materialparametergathererjob_p.h>
+#include <Qt3DRender/private/renderviewcommandbuilderjob_p.h>
+#include <Qt3DRender/private/renderviewcommandupdaterjob_p.h>
+#include <Qt3DRender/private/renderercache_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+enum RebuildFlag {
+ FullCommandRebuild = 1 << 0,
+ LayerCacheRebuild = 1 << 1,
+ MaterialCacheRebuild = 1 << 2,
+ LightCacheRebuild = 1 << 3
+};
+Q_DECLARE_FLAGS(RebuildFlagSet, RebuildFlag)
+Q_DECLARE_OPERATORS_FOR_FLAGS(RebuildFlagSet)
+
+#define RenderViewInitializerJobPtrAlias RenderViewInitializerJobPtr<RenderView, Renderer>
+#define RenderViewCommandBuilderJobPtrAlias RenderViewCommandBuilderJobPtr<RenderView, RenderCommand>
+#define RenderViewCommandUpdaterJobPtrAlias RenderViewCommandUpdaterJobPtr<RenderView, RenderCommand>
+
+template<class RenderView, class Renderer, class RenderCommand>
+class SyncPreCommandBuilding
+{
+public:
+ explicit SyncPreCommandBuilding(RenderViewInitializerJobPtrAlias renderViewInitializerJob,
+ const std::vector<RenderViewCommandBuilderJobPtrAlias> &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
+
+ // Rebuild RenderCommands for all entities in RV (ignoring filtering)
+ auto *cache = m_renderer->cache();
+ QMutexLocker lock(cache->mutex());
+
+ Q_ASSERT(cache->leafNodeCache.contains(m_leafNode));
+ // The cache leaf should already have been created so we don't need to protect the access
+ const auto &dataCacheForLeaf = cache->leafNodeCache[m_leafNode];
+ RenderView *rv = m_renderViewInitializer->renderView();
+ const auto &entities = !rv->isCompute() ? cache->renderableEntities : cache->computeEntities;
+
+ rv->setMaterialParameterTable(dataCacheForLeaf.materialParameterGatherer);
+
+ // Split among the ideal number of command builders
+ const int jobCount = m_renderViewCommandBuilderJobs.size();
+ const int entityCount = entities.size();
+ const int idealPacketSize = std::min(std::max(10, entityCount / jobCount), entityCount);
+ // Try to split work into an ideal number of workers
+ const int m = findIdealNumberOfWorkers(entityCount, idealPacketSize, jobCount);
+
+ const Entity **entitiesPtr = const_cast<const Entity **>(entities.data());
+ for (int i = 0; i < m; ++i) {
+ const auto &renderViewCommandBuilder = m_renderViewCommandBuilderJobs[i];
+ const int count = (i == m - 1) ? entityCount - (i * idealPacketSize) : idealPacketSize;
+ renderViewCommandBuilder->setEntities(entitiesPtr, i * idealPacketSize, count);
+ }
+ }
+
+private:
+ RenderViewInitializerJobPtrAlias m_renderViewInitializer;
+ std::vector<RenderViewCommandBuilderJobPtrAlias> m_renderViewCommandBuilderJobs;
+ Renderer *m_renderer;
+ FrameGraphNode *m_leafNode;
+};
+
+template<class RenderView, class Renderer, class RenderCommand>
+class SyncRenderViewPostCommandUpdate
+{
+public:
+ explicit SyncRenderViewPostCommandUpdate(const RenderViewInitializerJobPtrAlias &renderViewJob,
+ const std::vector<RenderViewCommandUpdaterJobPtrAlias> &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();
+
+ if (!rv->noDraw()) {
+ // Sort command on RenderView
+ rv->sort();
+ }
+ // Enqueue our fully populated RenderView with the RenderThread
+ m_renderer->enqueueRenderView(rv, m_renderViewJob->submitOrderIndex());
+ }
+
+private:
+ RenderViewInitializerJobPtrAlias m_renderViewJob;
+ std::vector<RenderViewCommandUpdaterJobPtrAlias> m_renderViewCommandUpdaterJobs;
+ Renderer *m_renderer;
+};
+
+template<class RenderView, class Renderer>
+class SyncPreFrustumCulling
+{
+public:
+ explicit SyncPreFrustumCulling(const RenderViewInitializerJobPtrAlias &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:
+ RenderViewInitializerJobPtrAlias m_renderViewJob;
+ FrustumCullingJobPtr m_frustumCullingJob;
+};
+
+template<class RenderView, class Renderer, class RenderCommand>
+class SyncRenderViewPostInitialization
+{
+public:
+ explicit SyncRenderViewPostInitialization(const RenderViewInitializerJobPtrAlias &renderViewJob,
+ const FrustumCullingJobPtr &frustumCullingJob,
+ const FilterLayerEntityJobPtr &filterEntityByLayerJob,
+ const FilterProximityDistanceJobPtr &filterProximityJob,
+ const std::vector<MaterialParameterGathererJobPtr> &materialGathererJobs,
+ const std::vector<RenderViewCommandUpdaterJobPtrAlias> &renderViewCommandUpdaterJobs,
+ const std::vector<RenderViewCommandBuilderJobPtrAlias> &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 : m_materialGathererJobs) {
+ materialGatherer->setRenderPassFilter(const_cast<RenderPassFilter *>(rv->renderPassFilter()));
+ materialGatherer->setTechniqueFilter(const_cast<TechniqueFilter *>(rv->techniqueFilter()));
+ }
+
+ // Command builders and updates
+ for (const auto &renderViewCommandUpdater : m_renderViewCommandUpdaterJobs)
+ renderViewCommandUpdater->setRenderView(rv);
+ for (const auto &renderViewCommandBuilder : m_renderViewCommandBuilderJobs)
+ renderViewCommandBuilder->setRenderView(rv);
+
+ // Set whether frustum culling is enabled or not
+ m_frustumCullingJob->setActive(rv->frustumCulling());
+ }
+
+private:
+ RenderViewInitializerJobPtrAlias m_renderViewJob;
+ FrustumCullingJobPtr m_frustumCullingJob;
+ FilterLayerEntityJobPtr m_filterEntityByLayerJob;
+ FilterProximityDistanceJobPtr m_filterProximityJob;
+ std::vector<MaterialParameterGathererJobPtr> m_materialGathererJobs;
+ std::vector<RenderViewCommandUpdaterJobPtrAlias> m_renderViewCommandUpdaterJobs;
+ std::vector<RenderViewCommandBuilderJobPtrAlias> m_renderViewCommandBuilderJobs;
+};
+
+template<class RenderView, class Renderer, class RenderCommand>
+class SyncRenderViewPreCommandUpdate
+{
+public:
+ explicit SyncRenderViewPreCommandUpdate(const RenderViewInitializerJobPtrAlias &renderViewJob,
+ const FrustumCullingJobPtr &frustumCullingJob,
+ const FilterProximityDistanceJobPtr &filterProximityJob,
+ const std::vector<MaterialParameterGathererJobPtr> &materialGathererJobs,
+ const std::vector<RenderViewCommandUpdaterJobPtrAlias> &renderViewCommandUpdaterJobs,
+ const std::vector<RenderViewCommandBuilderJobPtrAlias> &renderViewCommandBuilderJobs,
+ Renderer *renderer,
+ FrameGraphNode *leafNode,
+ RebuildFlagSet rebuildFlags)
+ : 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_rebuildFlags(rebuildFlags)
+ {}
+
+ 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
+ auto *cache = m_renderer->cache();
+ QMutexLocker lock(cache->mutex());
+ Q_ASSERT(cache->leafNodeCache.contains(m_leafNode));
+
+ // We don't need to protect the cache access as
+ // 1) The cache leaf is created
+ // 2) We are only reading conccurently the cache values that are shared across all RV
+ // 3) Each instance of this job is reading and writing in its own cache leaf so there's
+ // no conflict
+
+ const bool isDraw = !rv->isCompute();
+ auto &cacheForLeaf = cache->leafNodeCache[m_leafNode];
+
+ const bool fullRebuild = m_rebuildFlags.testFlag(RebuildFlag::FullCommandRebuild);
+ const bool layerFilteringRebuild = m_rebuildFlags.testFlag(RebuildFlag::LayerCacheRebuild);
+ const bool lightsCacheRebuild = m_rebuildFlags.testFlag(RebuildFlag::LightCacheRebuild);
+ const bool cameraDirty = cacheForLeaf.viewProjectionMatrix != rv->viewProjectionMatrix();
+ const bool hasProximityFilter = !rv->proximityFilterIds().empty();
+ const bool commandFilteringRequired =
+ fullRebuild ||
+ layerFilteringRebuild ||
+ lightsCacheRebuild ||
+ cameraDirty ||
+ hasProximityFilter;
+
+ // If we have no filteredRenderCommandDataViews then we should have fullRebuild set to true
+ // otherwise something is wrong
+ Q_ASSERT(fullRebuild || cacheForLeaf.filteredRenderCommandDataViews);
+
+ // 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 (fullRebuild) {
+ EntityRenderCommandData<RenderCommand> commandData;
+ // Reduction
+ {
+ int totalCommandCount = 0;
+ for (const RenderViewCommandBuilderJobPtrAlias &renderViewCommandBuilder : qAsConst(m_renderViewCommandBuilderJobs))
+ totalCommandCount += renderViewCommandBuilder->commandData().size();
+ commandData.reserve(totalCommandCount);
+ for (const RenderViewCommandBuilderJobPtrAlias &renderViewCommandBuilder : qAsConst(m_renderViewCommandBuilderJobs))
+ commandData += std::move(renderViewCommandBuilder->commandData());
+ }
+
+ // Store new cache
+ auto dataView = EntityRenderCommandDataViewPtr<RenderCommand>::create();
+ dataView->data = std::move(commandData);
+ // Store the update dataView
+ cacheForLeaf.filteredRenderCommandDataViews = dataView;
+ }
+
+
+ // Should be fairly infrequent
+ if (layerFilteringRebuild || fullRebuild) {
+ // Filter out renderable entities that weren't selected by the layer filters and store that in cache
+ cacheForLeaf.layeredFilteredRenderables = entitiesInSubset(
+ isDraw ? cache->renderableEntities : cache->computeEntities,
+ cacheForLeaf.filterEntitiesByLayer);
+ // Set default value for filteredAndCulledRenderables
+ if (isDraw)
+ cacheForLeaf.filteredAndCulledRenderables = cacheForLeaf.layeredFilteredRenderables;
+ }
+
+ // Should be fairly infrequent
+ if (lightsCacheRebuild) {
+ // Filter out light sources that weren't selected by the
+ // layer filters and store that in cache
+ const std::vector<Entity *> &layeredFilteredEntities = cacheForLeaf.filterEntitiesByLayer;
+ std::vector<LightSource> filteredLightSources = cache->gatheredLights;
+
+ auto it = filteredLightSources.begin();
+
+ while (it != filteredLightSources.end()) {
+ if (!std::binary_search(layeredFilteredEntities.begin(),
+ layeredFilteredEntities.end(),
+ it->entity))
+ it = filteredLightSources.erase(it);
+ else
+ ++it;
+ }
+ cacheForLeaf.layeredFilteredLightSources = std::move(filteredLightSources);
+ }
+
+ // This is likely very frequent
+ if (cameraDirty) {
+ // Record the updated viewProjectionMatrix in the cache to allow check to be performed
+ // next frame
+ cacheForLeaf.viewProjectionMatrix = rv->viewProjectionMatrix();
+ }
+
+ // Filter out frustum culled entity for drawable entities and store in cache
+ // We need to check this regardless of whether the camera has moved since
+ // entities in the scene themselves could have moved
+ if (isDraw && rv->frustumCulling()) {
+ cacheForLeaf.filteredAndCulledRenderables = entitiesInSubset(
+ cacheForLeaf.layeredFilteredRenderables,
+ m_frustumCullingJob->visibleEntities());
+ }
+
+ rv->setMaterialParameterTable(cacheForLeaf.materialParameterGatherer);
+ rv->setEnvironmentLight(cache->environmentLight);
+
+ // Set the light sources, with layer filters applied.
+ rv->setLightSources(cacheForLeaf.layeredFilteredLightSources);
+
+ std::vector<Entity *> renderableEntities = isDraw ? cacheForLeaf.filteredAndCulledRenderables : cacheForLeaf.layeredFilteredRenderables;
+
+ // TO DO: Find a way to do that only if proximity entities has changed
+ if (isDraw) {
+ // Filter out entities which didn't satisfy proximity filtering
+ if (hasProximityFilter)
+ renderableEntities = entitiesInSubset(renderableEntities,
+ m_filterProximityJob->filteredEntities());
+ }
+
+ EntityRenderCommandDataViewPtr<RenderCommand> filteredCommandData = cacheForLeaf.filteredRenderCommandDataViews;
+
+ // Set RenderCommandDataView on RV (will be used later on to sort commands ...)
+ rv->setRenderCommandDataView(filteredCommandData);
+
+ // Filter out Render commands for which the Entity wasn't selected because
+ // of frustum, proximity or layer filtering
+ if (commandFilteringRequired) {
+ const std::vector<const Entity *> &entities = filteredCommandData->data.entities;
+ // Because cacheForLeaf.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();
+ size_t cIt = 0;
+ const size_t cEnd = entities.size();
+
+ std::vector<size_t> filteredCommandIndices;
+ filteredCommandIndices.reserve(renderableEntities.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 && entities[cIt] < targetEntity)
+ ++cIt;
+
+ // Push pointers to command data for all commands that match the
+ // entity
+ while (cIt != cEnd && entities[cIt] == targetEntity) {
+ filteredCommandIndices.push_back(cIt);
+ ++cIt;
+ }
+ ++eIt;
+ }
+
+ // Store result in cache
+ cacheForLeaf.filteredRenderCommandDataViews->indices = std::move(filteredCommandIndices);
+ }
+
+ // Split among the number of command updaters
+ const int jobCount = m_renderViewCommandUpdaterJobs.size();
+ const int commandCount = filteredCommandData->size();
+ const int idealPacketSize = std::min(std::max(10, commandCount), commandCount);
+ const int m = findIdealNumberOfWorkers(commandCount, idealPacketSize, jobCount);
+
+ for (int i = 0; i < m; ++i) {
+ // TO DO: Based on whether we had to update the commands
+ // we should be able to know what needs to be recomputed
+ // -> lights/standard uniforms ... might no have to be set over and over again
+ // if they are identical
+ const RenderViewCommandUpdaterJobPtrAlias &renderViewCommandUpdater = m_renderViewCommandUpdaterJobs.at(i);
+ const size_t count = (i == m - 1) ? commandCount - (i * idealPacketSize) : idealPacketSize;
+ renderViewCommandUpdater->setRenderablesSubView({filteredCommandData, size_t(i * idealPacketSize), count});
+ }
+ }
+ }
+
+private:
+ RenderViewInitializerJobPtrAlias m_renderViewJob;
+ FrustumCullingJobPtr m_frustumCullingJob;
+ FilterProximityDistanceJobPtr m_filterProximityJob;
+ std::vector<MaterialParameterGathererJobPtr> m_materialGathererJobs;
+ std::vector<RenderViewCommandUpdaterJobPtrAlias> m_renderViewCommandUpdaterJobs;
+ std::vector<RenderViewCommandBuilderJobPtrAlias> m_renderViewCommandBuilderJobs;
+ Renderer *m_renderer;
+ FrameGraphNode *m_leafNode;
+ RebuildFlagSet m_rebuildFlags;
+};
+
+template<class Renderer>
+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());
+ Q_ASSERT(m_renderer->cache()->leafNodeCache.contains(m_leafNode));
+ // The cache leaf should already have been created so we don't need to protect the access
+ auto &dataCacheForLeaf = m_renderer->cache()->leafNodeCache[m_leafNode];
+ // Save the filtered by layer subset into the cache
+ dataCacheForLeaf.filterEntitiesByLayer = std::move(m_filterEntityByLayerJob->filteredEntities());
+ }
+
+private:
+ FilterLayerEntityJobPtr m_filterEntityByLayerJob;
+ Renderer *m_renderer;
+ FrameGraphNode *m_leafNode;
+};
+
+template<class Renderer>
+class SyncMaterialParameterGatherer
+{
+public:
+ explicit SyncMaterialParameterGatherer(const std::vector<MaterialParameterGathererJobPtr> &materialParameterGathererJobs,
+ Renderer *renderer,
+ FrameGraphNode *leafNode)
+ : m_materialParameterGathererJobs(materialParameterGathererJobs)
+ , m_renderer(renderer)
+ , m_leafNode(leafNode)
+ {
+ }
+
+ void operator()()
+ {
+ // The cache leaf was created by SyncRenderViewPostInitialization on which we depend
+ // so we don't need to protect the access
+ QMutexLocker lock(m_renderer->cache()->mutex());
+ auto &dataCacheForLeaf = m_renderer->cache()->leafNodeCache[m_leafNode];
+ dataCacheForLeaf.materialParameterGatherer.clear();
+
+ for (const auto &materialGatherer : m_materialParameterGathererJobs) {
+ const MaterialParameterGathererData &source = materialGatherer->materialToPassAndParameter();
+ for (auto it = std::begin(source); it != std::end(source); ++it) {
+ Q_ASSERT(!dataCacheForLeaf.materialParameterGatherer.contains(it.key()));
+ dataCacheForLeaf.materialParameterGatherer.insert(it.key(), it.value());
+ }
+ }
+ }
+
+private:
+ std::vector<MaterialParameterGathererJobPtr> m_materialParameterGathererJobs;
+ Renderer *m_renderer;
+ FrameGraphNode *m_leafNode;
+};
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // RENDERSYNCJOBS_H
diff --git a/src/render/jobs/renderviewjobutils.cpp b/src/render/jobs/renderviewjobutils.cpp
index 369ad2f66..11c7cbd98 100644
--- a/src/render/jobs/renderviewjobutils.cpp
+++ b/src/render/jobs/renderviewjobutils.cpp
@@ -225,6 +225,24 @@ bool ParameterInfo::operator<(const int otherNameId) const Q_DECL_NOEXCEPT
return nameId < otherNameId;
}
+int findIdealNumberOfWorkers(int elementCount, int packetSize, int maxJobCount)
+{
+ if (elementCount == 0 || packetSize == 0)
+ return 0;
+ return std::min(std::max(elementCount / packetSize, 1), maxJobCount);
+}
+
+std::vector<Entity *> entitiesInSubset(const std::vector<Entity *> &entities, const std::vector<Entity *> &subset)
+{
+ std::vector<Entity *> 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;
+}
+
} // namespace Render
} // namespace Qt3DRender
diff --git a/src/render/jobs/renderviewjobutils_p.h b/src/render/jobs/renderviewjobutils_p.h
index 0ef7ab00d..db46a114a 100644
--- a/src/render/jobs/renderviewjobutils_p.h
+++ b/src/render/jobs/renderviewjobutils_p.h
@@ -143,6 +143,12 @@ Q_3DRENDERSHARED_PRIVATE_EXPORT void addStatesToRenderStateSet(RenderStateSet *s
const QList<Qt3DCore::QNodeId> stateIds,
RenderStateManager *manager);
+Q_3DRENDERSHARED_PRIVATE_EXPORT int findIdealNumberOfWorkers(int elementCount, int packetSize = 100, int maxJobCount = 1)
+;
+
+Q_3DRENDERSHARED_PRIVATE_EXPORT std::vector<Entity *> entitiesInSubset(const std::vector<Entity *> &entities,
+const std::vector<Entity *> &subset);
+
} // namespace Render
} // namespace Qt3DRender
diff --git a/tests/auto/render/opengl/renderviewbuilder/tst_renderviewbuilder.cpp b/tests/auto/render/opengl/renderviewbuilder/tst_renderviewbuilder.cpp
index 5c378c6d1..6627e3c08 100644
--- a/tests/auto/render/opengl/renderviewbuilder/tst_renderviewbuilder.cpp
+++ b/tests/auto/render/opengl/renderviewbuilder/tst_renderviewbuilder.cpp
@@ -54,6 +54,7 @@
#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
@@ -675,7 +676,7 @@ private Q_SLOTS:
std::sort(renderableEntity.begin(), renderableEntity.end());
// WHEN
- renderableEntity = Qt3DRender::Render::OpenGL::RenderViewBuilder::entitiesInSubset(renderableEntity, filteredEntity);
+ renderableEntity = Qt3DRender::Render::entitiesInSubset(renderableEntity, filteredEntity);
// THEN
QCOMPARE(renderableEntity.size(), 100);