diff options
-rw-r--r-- | src/plugins/renderers/opengl/debug/imguirenderer.cpp | 1 | ||||
-rw-r--r-- | src/plugins/renderers/opengl/renderer/rendercommand_p.h | 9 | ||||
-rw-r--r-- | src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp | 466 | ||||
-rw-r--r-- | src/plugins/renderers/opengl/renderer/renderviewbuilder_p.h | 5 | ||||
-rw-r--r-- | src/plugins/renderers/rhi/renderer/rendercommand_p.h | 9 | ||||
-rw-r--r-- | src/plugins/renderers/rhi/renderer/renderview_p.h | 1 | ||||
-rw-r--r-- | src/plugins/renderers/rhi/renderer/renderviewbuilder.cpp | 467 | ||||
-rw-r--r-- | src/plugins/renderers/rhi/renderer/renderviewbuilder_p.h | 5 | ||||
-rw-r--r-- | src/render/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/render/jobs/jobs.pri | 3 | ||||
-rw-r--r-- | src/render/jobs/renderercache_p.h | 3 | ||||
-rw-r--r-- | src/render/jobs/rendersyncjobs_p.h | 539 | ||||
-rw-r--r-- | src/render/jobs/renderviewjobutils.cpp | 18 | ||||
-rw-r--r-- | src/render/jobs/renderviewjobutils_p.h | 6 | ||||
-rw-r--r-- | tests/auto/render/opengl/renderviewbuilder/tst_renderviewbuilder.cpp | 3 |
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); |