| 1 | /**************************************************************************** | 
| 2 | ** | 
| 3 | ** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB). | 
| 4 | ** Contact: https://www.qt.io/licensing/ | 
| 5 | ** | 
| 6 | ** This file is part of the Qt3D module of the Qt Toolkit. | 
| 7 | ** | 
| 8 | ** $QT_BEGIN_LICENSE:LGPL$ | 
| 9 | ** Commercial License Usage | 
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in | 
| 11 | ** accordance with the commercial license agreement provided with the | 
| 12 | ** Software or, alternatively, in accordance with the terms contained in | 
| 13 | ** a written agreement between you and The Qt Company. For licensing terms | 
| 14 | ** and conditions see https://www.qt.io/terms-conditions. For further | 
| 15 | ** information use the contact form at https://www.qt.io/contact-us. | 
| 16 | ** | 
| 17 | ** GNU Lesser General Public License Usage | 
| 18 | ** Alternatively, this file may be used under the terms of the GNU Lesser | 
| 19 | ** General Public License version 3 as published by the Free Software | 
| 20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the | 
| 21 | ** packaging of this file. Please review the following information to | 
| 22 | ** ensure the GNU Lesser General Public License version 3 requirements | 
| 23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. | 
| 24 | ** | 
| 25 | ** GNU General Public License Usage | 
| 26 | ** Alternatively, this file may be used under the terms of the GNU | 
| 27 | ** General Public License version 2.0 or (at your option) the GNU General | 
| 28 | ** Public license version 3 or any later version approved by the KDE Free | 
| 29 | ** Qt Foundation. The licenses are as published by the Free Software | 
| 30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 | 
| 31 | ** included in the packaging of this file. Please review the following | 
| 32 | ** information to ensure the GNU General Public License requirements will | 
| 33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and | 
| 34 | ** https://www.gnu.org/licenses/gpl-3.0.html. | 
| 35 | ** | 
| 36 | ** $QT_END_LICENSE$ | 
| 37 | ** | 
| 38 | ****************************************************************************/ | 
| 39 |  | 
| 40 | #include "renderviewbuilder_p.h" | 
| 41 | #include <Qt3DRender/private/qrenderaspect_p.h> | 
| 42 |  | 
| 43 | #include <QThread> | 
| 44 |  | 
| 45 | QT_BEGIN_NAMESPACE | 
| 46 |  | 
| 47 | namespace Qt3DRender { | 
| 48 |  | 
| 49 | namespace Render { | 
| 50 | namespace OpenGL { | 
| 51 |  | 
| 52 | namespace { | 
| 53 |  | 
| 54 | int findIdealNumberOfWorkers(int elementCount, int packetSize = 100, int maxJobCount = 1) | 
| 55 | { | 
| 56 |     if (elementCount == 0 || packetSize == 0) | 
| 57 |         return 0; | 
| 58 |     return std::min(a: std::max(a: elementCount / packetSize, b: 1), b: maxJobCount); | 
| 59 | } | 
| 60 |  | 
| 61 |  | 
| 62 | class SyncPreCommandBuilding | 
| 63 | { | 
| 64 | public: | 
| 65 |     explicit SyncPreCommandBuilding(RenderViewInitializerJobPtr renderViewInitializerJob, | 
| 66 |                                     const QVector<RenderViewCommandBuilderJobPtr> &renderViewCommandBuilderJobs, | 
| 67 |                                     Renderer *renderer, | 
| 68 |                                     FrameGraphNode *leafNode) | 
| 69 |         : m_renderViewInitializer(renderViewInitializerJob) | 
| 70 |         , m_renderViewCommandBuilderJobs(renderViewCommandBuilderJobs) | 
| 71 |         , m_renderer(renderer) | 
| 72 |         , m_leafNode(leafNode) | 
| 73 |     { | 
| 74 |     } | 
| 75 |  | 
| 76 |     void operator()() | 
| 77 |     { | 
| 78 |         // Split commands to build among jobs | 
| 79 |  | 
| 80 |         // Rebuild RenderCommands for all entities in RV (ignoring filtering) | 
| 81 |         RendererCache *cache = m_renderer->cache(); | 
| 82 |         QMutexLocker lock(cache->mutex()); | 
| 83 |  | 
| 84 |         Q_ASSERT(cache->leafNodeCache.contains(m_leafNode)); | 
| 85 |         // The cache leaf should already have been created so we don't need to protect the access | 
| 86 |         const RendererCache::LeafNodeData &dataCacheForLeaf = cache->leafNodeCache[m_leafNode]; | 
| 87 |         RenderView *rv = m_renderViewInitializer->renderView(); | 
| 88 |         const auto entities = !rv->isCompute() ? cache->renderableEntities : cache->computeEntities; | 
| 89 |  | 
| 90 |         rv->setMaterialParameterTable(dataCacheForLeaf.materialParameterGatherer); | 
| 91 |  | 
| 92 |         // Split among the ideal number of command builders | 
| 93 |         const int jobCount = m_renderViewCommandBuilderJobs.size(); | 
| 94 |         const int entityCount = entities.size(); | 
| 95 |         const int idealPacketSize = std::min(a: std::max(a: 10, b: entityCount / jobCount), b: entityCount); | 
| 96 |         // Try to split work into an ideal number of workers | 
| 97 |         const int m = findIdealNumberOfWorkers(elementCount: entityCount, packetSize: idealPacketSize, maxJobCount: jobCount); | 
| 98 |  | 
| 99 |         for (int i = 0; i < m; ++i) { | 
| 100 |             const RenderViewCommandBuilderJobPtr renderViewCommandBuilder = m_renderViewCommandBuilderJobs.at(i); | 
| 101 |             const int count = (i == m - 1) ? entityCount - (i * idealPacketSize) : idealPacketSize; | 
| 102 |             renderViewCommandBuilder->setEntities(entities, offset: i * idealPacketSize, count); | 
| 103 |         } | 
| 104 |     } | 
| 105 |  | 
| 106 | private: | 
| 107 |     RenderViewInitializerJobPtr m_renderViewInitializer; | 
| 108 |     QVector<RenderViewCommandBuilderJobPtr> m_renderViewCommandBuilderJobs; | 
| 109 |     Renderer *m_renderer; | 
| 110 |     FrameGraphNode *m_leafNode; | 
| 111 | }; | 
| 112 |  | 
| 113 | class SyncRenderViewPostCommandUpdate | 
| 114 | { | 
| 115 | public: | 
| 116 |     explicit SyncRenderViewPostCommandUpdate(const RenderViewInitializerJobPtr &renderViewJob, | 
| 117 |                                              const QVector<RenderViewCommandUpdaterJobPtr> &renderViewCommandUpdateJobs, | 
| 118 |                                              Renderer *renderer, | 
| 119 |                                              FrameGraphNode *leafNode) | 
| 120 |         : m_renderViewJob(renderViewJob) | 
| 121 |         , m_renderViewCommandUpdaterJobs(renderViewCommandUpdateJobs) | 
| 122 |         , m_renderer(renderer) | 
| 123 |         , m_leafNode(leafNode) | 
| 124 |     {} | 
| 125 |  | 
| 126 |     void operator()() | 
| 127 |     { | 
| 128 |         // Append all the commands and sort them | 
| 129 |         RenderView *rv = m_renderViewJob->renderView(); | 
| 130 |  | 
| 131 |         if (!rv->noDraw()) { | 
| 132 |             RendererCache *cache = m_renderer->cache(); | 
| 133 |             RendererCache::LeafNodeData &writableCacheForLeaf = cache->leafNodeCache[m_leafNode]; | 
| 134 |  | 
| 135 |             // Sort command on RenderView | 
| 136 |             rv->sort(); | 
| 137 |  | 
| 138 |             // Flip between the 2 EntityRenderCommandDataView on the leaf node case | 
| 139 |             { | 
| 140 |                 const int currentViewIdx = writableCacheForLeaf.viewIdx; | 
| 141 |                 const int nextViewIdx = 1 - currentViewIdx; | 
| 142 |                 EntityRenderCommandDataViewPtr currentDataView = writableCacheForLeaf.filteredRenderCommandDataViews[currentViewIdx]; | 
| 143 |  | 
| 144 |                 // In case the next view has yet to be initialized, we make a copy of the current | 
| 145 |                 // view | 
| 146 |                 if (writableCacheForLeaf.filteredRenderCommandDataViews[nextViewIdx].isNull()) { | 
| 147 |                     EntityRenderCommandDataViewPtr nextDataView = EntityRenderCommandDataViewPtr::create(); | 
| 148 |                     nextDataView->data = currentDataView->data; | 
| 149 |                     nextDataView->indices = currentDataView->indices; | 
| 150 |                     writableCacheForLeaf.filteredRenderCommandDataViews[nextViewIdx] = nextDataView; | 
| 151 |                 } | 
| 152 |                 // Flip index for next frame | 
| 153 |                 writableCacheForLeaf.viewIdx = nextViewIdx; | 
| 154 |             } | 
| 155 |         } | 
| 156 |  | 
| 157 |         // TO DO: Record the commandData information with the idea of being to | 
| 158 |         // reuse it next frame without having to allocate everything again and | 
| 159 |         // minimizing the uniform updates we need to make | 
| 160 |  | 
| 161 |         // Enqueue our fully populated RenderView with the RenderThread | 
| 162 |         m_renderer->enqueueRenderView(renderView: rv, submitOrder: m_renderViewJob->submitOrderIndex()); | 
| 163 |     } | 
| 164 |  | 
| 165 | private: | 
| 166 |     RenderViewInitializerJobPtr m_renderViewJob; | 
| 167 |     QVector<RenderViewCommandUpdaterJobPtr> m_renderViewCommandUpdaterJobs; | 
| 168 |     Renderer *m_renderer; | 
| 169 |     FrameGraphNode *m_leafNode; | 
| 170 | }; | 
| 171 |  | 
| 172 | class SyncPreFrustumCulling | 
| 173 | { | 
| 174 | public: | 
| 175 |     explicit SyncPreFrustumCulling(const RenderViewInitializerJobPtr &renderViewJob, | 
| 176 |                                    const FrustumCullingJobPtr &frustumCulling) | 
| 177 |         : m_renderViewJob(renderViewJob) | 
| 178 |         , m_frustumCullingJob(frustumCulling) | 
| 179 |     {} | 
| 180 |  | 
| 181 |     void operator()() | 
| 182 |     { | 
| 183 |         RenderView *rv = m_renderViewJob->renderView(); | 
| 184 |  | 
| 185 |         // Update matrices now that all transforms have been updated | 
| 186 |         rv->updateMatrices(); | 
| 187 |  | 
| 188 |         // Frustum culling | 
| 189 |         m_frustumCullingJob->setViewProjection(rv->viewProjectionMatrix()); | 
| 190 |     } | 
| 191 |  | 
| 192 | private: | 
| 193 |     RenderViewInitializerJobPtr m_renderViewJob; | 
| 194 |     FrustumCullingJobPtr m_frustumCullingJob; | 
| 195 | }; | 
| 196 |  | 
| 197 | class SyncRenderViewPostInitialization | 
| 198 | { | 
| 199 | public: | 
| 200 |     explicit SyncRenderViewPostInitialization(const RenderViewInitializerJobPtr &renderViewJob, | 
| 201 |                                               const FrustumCullingJobPtr &frustumCullingJob, | 
| 202 |                                               const FilterLayerEntityJobPtr &filterEntityByLayerJob, | 
| 203 |                                               const FilterProximityDistanceJobPtr &filterProximityJob, | 
| 204 |                                               const QVector<MaterialParameterGathererJobPtr> &materialGathererJobs, | 
| 205 |                                               const QVector<RenderViewCommandUpdaterJobPtr> &renderViewCommandUpdaterJobs, | 
| 206 |                                               const QVector<RenderViewCommandBuilderJobPtr> &renderViewCommandBuilderJobs) | 
| 207 |         : m_renderViewJob(renderViewJob) | 
| 208 |         , m_frustumCullingJob(frustumCullingJob) | 
| 209 |         , m_filterEntityByLayerJob(filterEntityByLayerJob) | 
| 210 |         , m_filterProximityJob(filterProximityJob) | 
| 211 |         , m_materialGathererJobs(materialGathererJobs) | 
| 212 |         , m_renderViewCommandUpdaterJobs(renderViewCommandUpdaterJobs) | 
| 213 |         , m_renderViewCommandBuilderJobs(renderViewCommandBuilderJobs) | 
| 214 |     {} | 
| 215 |  | 
| 216 |     void operator()() | 
| 217 |     { | 
| 218 |         RenderView *rv = m_renderViewJob->renderView(); | 
| 219 |  | 
| 220 |         // Layer filtering | 
| 221 |         if (!m_filterEntityByLayerJob.isNull()) | 
| 222 |             m_filterEntityByLayerJob->setLayerFilters(rv->layerFilters()); | 
| 223 |  | 
| 224 |         // Proximity filtering | 
| 225 |         m_filterProximityJob->setProximityFilterIds(rv->proximityFilterIds()); | 
| 226 |  | 
| 227 |         // Material Parameter building | 
| 228 |         for (const auto &materialGatherer : qAsConst(t&: m_materialGathererJobs)) { | 
| 229 |             materialGatherer->setRenderPassFilter(const_cast<RenderPassFilter *>(rv->renderPassFilter())); | 
| 230 |             materialGatherer->setTechniqueFilter(const_cast<TechniqueFilter *>(rv->techniqueFilter())); | 
| 231 |         } | 
| 232 |  | 
| 233 |         // Command builders and updates | 
| 234 |         for (const auto &renderViewCommandUpdater : qAsConst(t&: m_renderViewCommandUpdaterJobs)) | 
| 235 |             renderViewCommandUpdater->setRenderView(rv); | 
| 236 |         for (const auto &renderViewCommandBuilder : qAsConst(t&: m_renderViewCommandBuilderJobs)) | 
| 237 |             renderViewCommandBuilder->setRenderView(rv); | 
| 238 |  | 
| 239 |         // Set whether frustum culling is enabled or not | 
| 240 |         m_frustumCullingJob->setActive(rv->frustumCulling()); | 
| 241 |     } | 
| 242 |  | 
| 243 | private: | 
| 244 |     RenderViewInitializerJobPtr m_renderViewJob; | 
| 245 |     FrustumCullingJobPtr m_frustumCullingJob; | 
| 246 |     FilterLayerEntityJobPtr m_filterEntityByLayerJob; | 
| 247 |     FilterProximityDistanceJobPtr m_filterProximityJob; | 
| 248 |     QVector<MaterialParameterGathererJobPtr> m_materialGathererJobs; | 
| 249 |     QVector<RenderViewCommandUpdaterJobPtr> m_renderViewCommandUpdaterJobs; | 
| 250 |     QVector<RenderViewCommandBuilderJobPtr> m_renderViewCommandBuilderJobs; | 
| 251 |     Renderer *m_renderer; | 
| 252 |     FrameGraphNode *m_leafNode; | 
| 253 | }; | 
| 254 |  | 
| 255 | class SyncRenderViewPreCommandUpdate | 
| 256 | { | 
| 257 | public: | 
| 258 |     explicit SyncRenderViewPreCommandUpdate(const RenderViewInitializerJobPtr &renderViewJob, | 
| 259 |                                             const FrustumCullingJobPtr &frustumCullingJob, | 
| 260 |                                             const FilterProximityDistanceJobPtr &filterProximityJob, | 
| 261 |                                             const QVector<MaterialParameterGathererJobPtr> &materialGathererJobs, | 
| 262 |                                             const QVector<RenderViewCommandUpdaterJobPtr> &renderViewCommandUpdaterJobs, | 
| 263 |                                             const QVector<RenderViewCommandBuilderJobPtr> &renderViewCommandBuilderJobs, | 
| 264 |                                             Renderer *renderer, | 
| 265 |                                             FrameGraphNode *leafNode, | 
| 266 |                                             RebuildFlagSet rebuildFlags) | 
| 267 |         : m_renderViewJob(renderViewJob) | 
| 268 |         , m_frustumCullingJob(frustumCullingJob) | 
| 269 |         , m_filterProximityJob(filterProximityJob) | 
| 270 |         , m_materialGathererJobs(materialGathererJobs) | 
| 271 |         , m_renderViewCommandUpdaterJobs(renderViewCommandUpdaterJobs) | 
| 272 |         , m_renderViewCommandBuilderJobs(renderViewCommandBuilderJobs) | 
| 273 |         , m_renderer(renderer) | 
| 274 |         , m_leafNode(leafNode) | 
| 275 |         , m_rebuildFlags(rebuildFlags) | 
| 276 |     {} | 
| 277 |  | 
| 278 |     void operator()() | 
| 279 |     { | 
| 280 |         // Set the result of previous job computations | 
| 281 |         // for final RenderCommand building | 
| 282 |         RenderView *rv = m_renderViewJob->renderView(); | 
| 283 |  | 
| 284 |         if (!rv->noDraw()) { | 
| 285 |             ///////// CACHE LOCKED //////////// | 
| 286 |             // Retrieve Data from Cache | 
| 287 |             RendererCache *cache = m_renderer->cache(); | 
| 288 |             QMutexLocker lock(cache->mutex()); | 
| 289 |             Q_ASSERT(cache->leafNodeCache.contains(m_leafNode)); | 
| 290 |  | 
| 291 |             // We don't need to protect the cache access as | 
| 292 |             // 1) The cache leaf is created | 
| 293 |             // 2) We are only reading conccurently the cache values that are shared across all RV | 
| 294 |             // 3) Each instance of this job is reading and writing in its own cache leaf so there's | 
| 295 |             // no conflict | 
| 296 |  | 
| 297 |             const bool isDraw = !rv->isCompute(); | 
| 298 |             RendererCache::LeafNodeData &cacheForLeaf = cache->leafNodeCache[m_leafNode]; | 
| 299 |  | 
| 300 |             const bool fullRebuild = m_rebuildFlags.testFlag(flag: RebuildFlag::FullCommandRebuild); | 
| 301 |             const bool layerFilteringRebuild = m_rebuildFlags.testFlag(flag: RebuildFlag::LayerCacheRebuild); | 
| 302 |             const bool lightsCacheRebuild = m_rebuildFlags.testFlag(flag: RebuildFlag::LightCacheRebuild); | 
| 303 |             const bool cameraDirty = cacheForLeaf.viewProjectionMatrix != rv->viewProjectionMatrix(); | 
| 304 |             const bool hasProximityFilter = !rv->proximityFilterIds().empty(); | 
| 305 |             const bool commandFilteringRequired = | 
| 306 |                     fullRebuild || | 
| 307 |                     layerFilteringRebuild || | 
| 308 |                     lightsCacheRebuild || | 
| 309 |                     cameraDirty || | 
| 310 |                     hasProximityFilter || | 
| 311 |                     cacheForLeaf.requestFilteringAtNextFrame; | 
| 312 |  | 
| 313 |             // Reset flag on leaf | 
| 314 |             cacheForLeaf.requestFilteringAtNextFrame = false; | 
| 315 |  | 
| 316 |             // If we have no filteredRenderCommandDataViews then we should have fullRebuild set to true | 
| 317 |             // otherwise something is wrong | 
| 318 |             Q_ASSERT(fullRebuild || cacheForLeaf.filteredRenderCommandDataViews[cacheForLeaf.viewIdx]); | 
| 319 |  | 
| 320 |             // Rebuild RenderCommands if required | 
| 321 |             // This should happen fairly infrequently (FrameGraph Change, Geometry/Material change) | 
| 322 |             // and allow to skip that step most of the time | 
| 323 |             if (fullRebuild) { | 
| 324 |                 EntityRenderCommandData commandData; | 
| 325 |                 // Reduction | 
| 326 |                 { | 
| 327 |                     int totalCommandCount = 0; | 
| 328 |                     for (const RenderViewCommandBuilderJobPtr &renderViewCommandBuilder : qAsConst(t&: m_renderViewCommandBuilderJobs)) | 
| 329 |                         totalCommandCount += renderViewCommandBuilder->commandData().size(); | 
| 330 |                     commandData.reserve(size: totalCommandCount); | 
| 331 |                     for (const RenderViewCommandBuilderJobPtr &renderViewCommandBuilder : qAsConst(t&: m_renderViewCommandBuilderJobs)) | 
| 332 |                         commandData += std::move(renderViewCommandBuilder->commandData()); | 
| 333 |                 } | 
| 334 |  | 
| 335 |                 // Store new cache | 
| 336 |                 EntityRenderCommandDataViewPtr dataView = EntityRenderCommandDataViewPtr::create(); | 
| 337 |                 dataView->data = std::move(commandData); | 
| 338 |                 // Store the update dataView | 
| 339 |                 cacheForLeaf.filteredRenderCommandDataViews[cacheForLeaf.viewIdx] = dataView; | 
| 340 |                 // Clear the other dataView | 
| 341 |                 cacheForLeaf.filteredRenderCommandDataViews[1 - cacheForLeaf.viewIdx].clear(); | 
| 342 |             } | 
| 343 |  | 
| 344 |             // Should be fairly infrequent | 
| 345 |             if (layerFilteringRebuild || fullRebuild) { | 
| 346 |                 // Filter out renderable entities that weren't selected by the layer filters and store that in cache | 
| 347 |                 cacheForLeaf.layeredFilteredRenderables = RenderViewBuilder::entitiesInSubset( | 
| 348 |                             entities: isDraw ? cache->renderableEntities : cache->computeEntities, | 
| 349 |                             subset: cacheForLeaf.filterEntitiesByLayer); | 
| 350 |                 // Set default value for filteredAndCulledRenderables | 
| 351 |                 if (isDraw) | 
| 352 |                     cacheForLeaf.filteredAndCulledRenderables = cacheForLeaf.layeredFilteredRenderables; | 
| 353 |             } | 
| 354 |  | 
| 355 |             // Should be fairly infrequent | 
| 356 |             if (lightsCacheRebuild) { | 
| 357 |                 // Filter out renderable entities that weren't selected by the | 
| 358 |                 // layer filters and store that in cache | 
| 359 |                 const QVector<Entity *> &layeredFilteredEntities = cacheForLeaf.filterEntitiesByLayer; | 
| 360 |                 QVector<LightSource> filteredLightSources = cache->gatheredLights; | 
| 361 |                 for (int i = 0; i < filteredLightSources.count(); ++i) { | 
| 362 |                     if (!layeredFilteredEntities.contains(t: filteredLightSources[i].entity)) | 
| 363 |                         filteredLightSources.removeAt(i: i--); | 
| 364 |                 } | 
| 365 |                 cacheForLeaf.layeredFilteredLightSources = filteredLightSources; | 
| 366 |             } | 
| 367 |  | 
| 368 |             // This is likely very frequent | 
| 369 |             if (cameraDirty) { | 
| 370 |                 // Record the updated viewProjectionMatrix in the cache to allow check to be performed | 
| 371 |                 // next frame | 
| 372 |                 cacheForLeaf.viewProjectionMatrix = rv->viewProjectionMatrix(); | 
| 373 |             } | 
| 374 |  | 
| 375 |             // Filter out frustum culled entity for drawable entities and store in cache | 
| 376 |             // We need to check this regardless of whether the camera has moved since | 
| 377 |             // entities in the scene themselves could have moved | 
| 378 |             if (isDraw && rv->frustumCulling()) { | 
| 379 |                 cacheForLeaf.filteredAndCulledRenderables = RenderViewBuilder::entitiesInSubset( | 
| 380 |                             entities: cacheForLeaf.layeredFilteredRenderables, | 
| 381 |                             subset: m_frustumCullingJob->visibleEntities()); | 
| 382 |             } | 
| 383 |  | 
| 384 |             rv->setMaterialParameterTable(cacheForLeaf.materialParameterGatherer); | 
| 385 |             rv->setEnvironmentLight(cache->environmentLight); | 
| 386 |  | 
| 387 |             // Set the light sources, with layer filters applied. | 
| 388 |             rv->setLightSources(cacheForLeaf.layeredFilteredLightSources); | 
| 389 |  | 
| 390 |             QVector<Entity *> renderableEntities = isDraw ? cacheForLeaf.filteredAndCulledRenderables : cacheForLeaf.layeredFilteredRenderables; | 
| 391 |  | 
| 392 |             // TO DO: Find a way to do that only if proximity entities has changed | 
| 393 |             if (isDraw) { | 
| 394 |                 // Filter out entities which didn't satisfy proximity filtering | 
| 395 |                 if (hasProximityFilter) | 
| 396 |                     renderableEntities = RenderViewBuilder::entitiesInSubset(entities: renderableEntities, | 
| 397 |                                                                              subset: m_filterProximityJob->filteredEntities()); | 
| 398 |             } | 
| 399 |  | 
| 400 |             EntityRenderCommandDataViewPtr filteredCommandData = cacheForLeaf.filteredRenderCommandDataViews[cacheForLeaf.viewIdx]; | 
| 401 |  | 
| 402 |             // Set RenderCommandDataView on RV (will be used later on to sort commands ...) | 
| 403 |             rv->setRenderCommandDataView(filteredCommandData); | 
| 404 |  | 
| 405 |             // Filter out Render commands for which the Entity wasn't selected because | 
| 406 |             // of frustum, proximity or layer filtering | 
| 407 |             if (commandFilteringRequired) { | 
| 408 |                 const std::vector<Entity *> &entities = filteredCommandData->data.entities; | 
| 409 |                 // Because cacheForLeaf.renderableEntities or computeEntities are sorted | 
| 410 |                 // What we get out of EntityRenderCommandData is also sorted by Entity | 
| 411 |                 auto eIt = renderableEntities.cbegin(); | 
| 412 |                 const auto eEnd = renderableEntities.cend(); | 
| 413 |                 size_t cIt = 0; | 
| 414 |                 const size_t cEnd = entities.size(); | 
| 415 |  | 
| 416 |                 std::vector<size_t> filteredCommandIndices; | 
| 417 |                 filteredCommandIndices.reserve(n: renderableEntities.size()); | 
| 418 |  | 
| 419 |                 while (eIt != eEnd) { | 
| 420 |                     const Entity *targetEntity = *eIt; | 
| 421 |                     // Advance until we have commands whose Entity has a lower address | 
| 422 |                     // than the selected filtered entity | 
| 423 |                     while (cIt != cEnd && entities[cIt] < targetEntity) | 
| 424 |                         ++cIt; | 
| 425 |  | 
| 426 |                     // Push pointers to command data for all commands that match the | 
| 427 |                     // entity | 
| 428 |                     while (cIt != cEnd && entities[cIt] == targetEntity) { | 
| 429 |                         filteredCommandIndices.push_back(x: cIt); | 
| 430 |                         ++cIt; | 
| 431 |                     } | 
| 432 |                     ++eIt; | 
| 433 |                 } | 
| 434 |  | 
| 435 |                 // Store result in cache | 
| 436 |                 cacheForLeaf.filteredRenderCommandDataViews[cacheForLeaf.viewIdx]->indices = std::move(filteredCommandIndices); | 
| 437 |  | 
| 438 |                 // Request filtering at next frame (indices for view0 and view1 | 
| 439 |                 // could mistmatch if something is dirty for frame 0 and not at | 
| 440 |                 // frame 1 (given we have 2 views we alternate with) | 
| 441 |                 cacheForLeaf.requestFilteringAtNextFrame = true; | 
| 442 |             } | 
| 443 |  | 
| 444 |             // Split among the number of command updaters | 
| 445 |             const int jobCount = m_renderViewCommandUpdaterJobs.size(); | 
| 446 |             const int commandCount = filteredCommandData->size(); | 
| 447 |             const int idealPacketSize = std::min(a: std::max(a: 10, b: commandCount), b: commandCount); | 
| 448 |             const int m = findIdealNumberOfWorkers(elementCount: commandCount, packetSize: idealPacketSize, maxJobCount: jobCount); | 
| 449 |  | 
| 450 |             for (int i = 0; i < m; ++i) { | 
| 451 |                 // TO DO: Based on whether we had to update the commands | 
| 452 |                 // we should be able to know what needs to be recomputed | 
| 453 |                 // -> lights/standard uniforms ... might no have to be set over and over again | 
| 454 |                 // if they are identical | 
| 455 |                 const RenderViewCommandUpdaterJobPtr &renderViewCommandUpdater = m_renderViewCommandUpdaterJobs.at(i); | 
| 456 |                 const size_t count = (i == m - 1) ? commandCount - (i * idealPacketSize) : idealPacketSize; | 
| 457 |                 renderViewCommandUpdater->setRenderablesSubView({.view: filteredCommandData, .offset: size_t(i * idealPacketSize), .count: count}); | 
| 458 |             } | 
| 459 |         } | 
| 460 |     } | 
| 461 |  | 
| 462 | private: | 
| 463 |     RenderViewInitializerJobPtr m_renderViewJob; | 
| 464 |     FrustumCullingJobPtr m_frustumCullingJob; | 
| 465 |     FilterProximityDistanceJobPtr m_filterProximityJob; | 
| 466 |     QVector<MaterialParameterGathererJobPtr> m_materialGathererJobs; | 
| 467 |     QVector<RenderViewCommandUpdaterJobPtr> m_renderViewCommandUpdaterJobs; | 
| 468 |     QVector<RenderViewCommandBuilderJobPtr> m_renderViewCommandBuilderJobs; | 
| 469 |     Renderer *m_renderer; | 
| 470 |     FrameGraphNode *m_leafNode; | 
| 471 |     RebuildFlagSet m_rebuildFlags; | 
| 472 | }; | 
| 473 |  | 
| 474 | class SetClearDrawBufferIndex | 
| 475 | { | 
| 476 | public: | 
| 477 |     explicit SetClearDrawBufferIndex(const RenderViewInitializerJobPtr &renderViewJob) | 
| 478 |         : m_renderViewJob(renderViewJob) | 
| 479 |     {} | 
| 480 |  | 
| 481 |     void operator()() | 
| 482 |     { | 
| 483 |         RenderView *rv = m_renderViewJob->renderView(); | 
| 484 |         QVector<ClearBufferInfo> &clearBuffersInfo = rv->specificClearColorBufferInfo(); | 
| 485 |         const AttachmentPack &attachmentPack = rv->attachmentPack(); | 
| 486 |         for (ClearBufferInfo &clearBufferInfo : clearBuffersInfo) | 
| 487 |             clearBufferInfo.drawBufferIndex = attachmentPack.getDrawBufferIndex(attachmentPoint: clearBufferInfo.attchmentPoint); | 
| 488 |  | 
| 489 |     } | 
| 490 |  | 
| 491 | private: | 
| 492 |     RenderViewInitializerJobPtr m_renderViewJob; | 
| 493 | }; | 
| 494 |  | 
| 495 | class SyncFilterEntityByLayer | 
| 496 | { | 
| 497 | public: | 
| 498 |     explicit SyncFilterEntityByLayer(const FilterLayerEntityJobPtr &filterEntityByLayerJob, | 
| 499 |                                      Renderer *renderer, | 
| 500 |                                      FrameGraphNode *leafNode) | 
| 501 |         : m_filterEntityByLayerJob(filterEntityByLayerJob) | 
| 502 |         , m_renderer(renderer) | 
| 503 |         , m_leafNode(leafNode) | 
| 504 |     { | 
| 505 |     } | 
| 506 |  | 
| 507 |     void operator()() | 
| 508 |     { | 
| 509 |         QMutexLocker lock(m_renderer->cache()->mutex()); | 
| 510 |         Q_ASSERT(m_renderer->cache()->leafNodeCache.contains(m_leafNode)); | 
| 511 |         // The cache leaf should already have been created so we don't need to protect the access | 
| 512 |         RendererCache::LeafNodeData &dataCacheForLeaf = m_renderer->cache()->leafNodeCache[m_leafNode]; | 
| 513 |         // Save the filtered by layer subset into the cache | 
| 514 |         dataCacheForLeaf.filterEntitiesByLayer = std::move(m_filterEntityByLayerJob->filteredEntities()); | 
| 515 |     } | 
| 516 |  | 
| 517 | private: | 
| 518 |     FilterLayerEntityJobPtr m_filterEntityByLayerJob; | 
| 519 |     Renderer *m_renderer; | 
| 520 |     FrameGraphNode *m_leafNode; | 
| 521 | }; | 
| 522 |  | 
| 523 | class SyncMaterialParameterGatherer | 
| 524 | { | 
| 525 | public: | 
| 526 |     explicit SyncMaterialParameterGatherer(const QVector<MaterialParameterGathererJobPtr> &materialParameterGathererJobs, | 
| 527 |                                            Renderer *renderer, | 
| 528 |                                            FrameGraphNode *leafNode) | 
| 529 |         : m_materialParameterGathererJobs(materialParameterGathererJobs) | 
| 530 |         , m_renderer(renderer) | 
| 531 |         , m_leafNode(leafNode) | 
| 532 |     { | 
| 533 |     } | 
| 534 |  | 
| 535 |     void operator()() | 
| 536 |     { | 
| 537 |         // The cache leaf was created by SyncRenderViewPostInitialization on which we depend | 
| 538 |         // so we don't need to protect the access | 
| 539 |         QMutexLocker lock(m_renderer->cache()->mutex()); | 
| 540 |         RendererCache::LeafNodeData &dataCacheForLeaf = m_renderer->cache()->leafNodeCache[m_leafNode]; | 
| 541 |         dataCacheForLeaf.materialParameterGatherer.clear(); | 
| 542 |  | 
| 543 |         for (const auto &materialGatherer : qAsConst(t&: m_materialParameterGathererJobs)) | 
| 544 |             dataCacheForLeaf.materialParameterGatherer.unite(other: materialGatherer->materialToPassAndParameter()); | 
| 545 |     } | 
| 546 |  | 
| 547 | private: | 
| 548 |     QVector<MaterialParameterGathererJobPtr> m_materialParameterGathererJobs; | 
| 549 |     Renderer *m_renderer; | 
| 550 |     FrameGraphNode *m_leafNode; | 
| 551 | }; | 
| 552 |  | 
| 553 | } // anonymous | 
| 554 |  | 
| 555 | RenderViewBuilder::RenderViewBuilder(Render::FrameGraphNode *leafNode, int renderViewIndex, Renderer *renderer) | 
| 556 |     : m_leafNode(leafNode) | 
| 557 |     , m_renderViewIndex(renderViewIndex) | 
| 558 |     , m_renderer(renderer) | 
| 559 |     , m_renderViewJob(RenderViewInitializerJobPtr::create()) | 
| 560 |     , m_filterEntityByLayerJob() | 
| 561 |     , m_frustumCullingJob(new Render::FrustumCullingJob()) | 
| 562 |     , m_syncPreFrustumCullingJob(CreateSynchronizerJobPtr(SyncPreFrustumCulling(m_renderViewJob, m_frustumCullingJob), JobTypes::SyncFrustumCulling, renderViewIndex)) | 
| 563 |     , m_setClearDrawBufferIndexJob(CreateSynchronizerJobPtr(SetClearDrawBufferIndex(m_renderViewJob), JobTypes::ClearBufferDrawIndex, renderViewIndex)) | 
| 564 |     , m_syncFilterEntityByLayerJob() | 
| 565 |     , m_filterProximityJob(Render::FilterProximityDistanceJobPtr::create()) | 
| 566 | { | 
| 567 |     // In some cases having less jobs is better (especially on fast cpus where | 
| 568 |     // splitting just adds more overhead). Ideally, we should try to set the value | 
| 569 |     // depending on the platform/CPU/nbr of cores | 
| 570 |     m_optimalParallelJobCount = QThread::idealThreadCount(); | 
| 571 | } | 
| 572 |  | 
| 573 | RenderViewInitializerJobPtr RenderViewBuilder::renderViewJob() const | 
| 574 | { | 
| 575 |     return m_renderViewJob; | 
| 576 | } | 
| 577 |  | 
| 578 | FilterLayerEntityJobPtr RenderViewBuilder::filterEntityByLayerJob() const | 
| 579 | { | 
| 580 |     return m_filterEntityByLayerJob; | 
| 581 | } | 
| 582 |  | 
| 583 | FrustumCullingJobPtr RenderViewBuilder::frustumCullingJob() const | 
| 584 | { | 
| 585 |     return m_frustumCullingJob; | 
| 586 | } | 
| 587 |  | 
| 588 | QVector<RenderViewCommandUpdaterJobPtr> RenderViewBuilder::renderViewCommandUpdaterJobs() const | 
| 589 | { | 
| 590 |     return m_renderViewCommandUpdaterJobs; | 
| 591 | } | 
| 592 |  | 
| 593 | QVector<RenderViewCommandBuilderJobPtr> RenderViewBuilder::renderViewCommandBuilderJobs() const | 
| 594 | { | 
| 595 |     return m_renderViewCommandBuilderJobs; | 
| 596 | } | 
| 597 |  | 
| 598 | QVector<MaterialParameterGathererJobPtr> RenderViewBuilder::materialGathererJobs() const | 
| 599 | { | 
| 600 |     return m_materialGathererJobs; | 
| 601 | } | 
| 602 |  | 
| 603 | SynchronizerJobPtr RenderViewBuilder::syncRenderViewPostInitializationJob() const | 
| 604 | { | 
| 605 |     return m_syncRenderViewPostInitializationJob; | 
| 606 | } | 
| 607 |  | 
| 608 | SynchronizerJobPtr RenderViewBuilder::syncPreFrustumCullingJob() const | 
| 609 | { | 
| 610 |     return m_syncPreFrustumCullingJob; | 
| 611 | } | 
| 612 |  | 
| 613 | SynchronizerJobPtr RenderViewBuilder::syncRenderViewPreCommandBuildingJob() const | 
| 614 | { | 
| 615 |     return m_syncRenderViewPreCommandBuildingJob; | 
| 616 | } | 
| 617 |  | 
| 618 | SynchronizerJobPtr RenderViewBuilder::syncRenderViewPreCommandUpdateJob() const | 
| 619 | { | 
| 620 |     return m_syncRenderViewPreCommandUpdateJob; | 
| 621 | } | 
| 622 |  | 
| 623 | SynchronizerJobPtr RenderViewBuilder::syncRenderViewPostCommandUpdateJob() const | 
| 624 | { | 
| 625 |     return m_syncRenderViewPostCommandUpdateJob; | 
| 626 | } | 
| 627 |  | 
| 628 | SynchronizerJobPtr RenderViewBuilder::setClearDrawBufferIndexJob() const | 
| 629 | { | 
| 630 |     return m_setClearDrawBufferIndexJob; | 
| 631 | } | 
| 632 |  | 
| 633 | SynchronizerJobPtr RenderViewBuilder::syncFilterEntityByLayerJob() const | 
| 634 | { | 
| 635 |     return m_syncFilterEntityByLayerJob; | 
| 636 | } | 
| 637 |  | 
| 638 | SynchronizerJobPtr RenderViewBuilder::syncMaterialGathererJob() const | 
| 639 | { | 
| 640 |     return m_syncMaterialGathererJob; | 
| 641 | } | 
| 642 |  | 
| 643 | FilterProximityDistanceJobPtr RenderViewBuilder::filterProximityJob() const | 
| 644 | { | 
| 645 |     return m_filterProximityJob; | 
| 646 | } | 
| 647 |  | 
| 648 | void RenderViewBuilder::prepareJobs() | 
| 649 | { | 
| 650 |     // Init what we can here | 
| 651 |     m_filterProximityJob->setManager(m_renderer->nodeManagers()); | 
| 652 |     m_frustumCullingJob->setRoot(m_renderer->sceneRoot()); | 
| 653 |  | 
| 654 |     const bool commandsNeedRebuild = m_rebuildFlags.testFlag(flag: RebuildFlag::FullCommandRebuild); | 
| 655 |     if (commandsNeedRebuild) { | 
| 656 |         m_renderViewCommandBuilderJobs.reserve(asize: m_optimalParallelJobCount); | 
| 657 |         for (auto i = 0; i < m_optimalParallelJobCount; ++i) { | 
| 658 |             auto renderViewCommandBuilder = Render::OpenGL::RenderViewCommandBuilderJobPtr::create(); | 
| 659 |             m_renderViewCommandBuilderJobs.push_back(t: renderViewCommandBuilder); | 
| 660 |         } | 
| 661 |         m_syncRenderViewPreCommandBuildingJob = CreateSynchronizerJobPtr(SyncPreCommandBuilding(m_renderViewJob, | 
| 662 |                                                                                                 m_renderViewCommandBuilderJobs, | 
| 663 |                                                                                                 m_renderer, | 
| 664 |                                                                                                 m_leafNode), | 
| 665 |                                                                          JobTypes::SyncRenderViewPreCommandBuilding, | 
| 666 |                                                                          m_renderViewIndex); | 
| 667 |     } | 
| 668 |  | 
| 669 |     m_renderViewJob->setRenderer(m_renderer); | 
| 670 |     m_renderViewJob->setFrameGraphLeafNode(m_leafNode); | 
| 671 |     m_renderViewJob->setSubmitOrderIndex(m_renderViewIndex); | 
| 672 |  | 
| 673 |     // RenderCommand building is the most consuming task -> split it | 
| 674 |     // Estimate the number of jobs to create based on the number of entities | 
| 675 |     m_renderViewCommandUpdaterJobs.reserve(asize: m_optimalParallelJobCount); | 
| 676 |     for (auto i = 0; i < m_optimalParallelJobCount; ++i) { | 
| 677 |         auto renderViewCommandUpdater = Render::OpenGL::RenderViewCommandUpdaterJobPtr::create(); | 
| 678 |         renderViewCommandUpdater->setRenderer(m_renderer); | 
| 679 |         renderViewCommandUpdater->setRebuildFlags(m_rebuildFlags); | 
| 680 |         m_renderViewCommandUpdaterJobs.push_back(t: renderViewCommandUpdater); | 
| 681 |     } | 
| 682 |  | 
| 683 |     const bool materialCacheNeedsRebuild = m_rebuildFlags.testFlag(flag: RebuildFlag::MaterialCacheRebuild); | 
| 684 |     if (materialCacheNeedsRebuild) { | 
| 685 |         // Since Material gathering is an heavy task, we split it | 
| 686 |         const std::vector<HMaterial> &materialHandles = m_renderer->nodeManagers()->materialManager()->activeHandles(); | 
| 687 |         const size_t handlesCount = materialHandles.size(); | 
| 688 |         if (handlesCount) { | 
| 689 |             m_materialGathererJobs.reserve(asize: m_optimalParallelJobCount); | 
| 690 |             const size_t elementsPerJob =  std::max(a: handlesCount / m_optimalParallelJobCount, b: size_t(1)); | 
| 691 |             size_t elementCount = 0; | 
| 692 |             while (elementCount < handlesCount) { | 
| 693 |                 auto materialGatherer = MaterialParameterGathererJobPtr::create(); | 
| 694 |                 materialGatherer->setNodeManagers(m_renderer->nodeManagers()); | 
| 695 |                 // TO DO: Candidate for std::span if C++20 | 
| 696 |                 materialGatherer->setHandles({materialHandles.begin() + elementCount, | 
| 697 |                                               materialHandles.begin() + std::min(a: elementCount + elementsPerJob, b: handlesCount)}); | 
| 698 |                 m_materialGathererJobs.push_back(t: materialGatherer); | 
| 699 |  | 
| 700 |                 elementCount += elementsPerJob; | 
| 701 |             } | 
| 702 |         } | 
| 703 |         m_syncMaterialGathererJob = CreateSynchronizerJobPtr(SyncMaterialParameterGatherer(m_materialGathererJobs, | 
| 704 |                                                                                            m_renderer, | 
| 705 |                                                                                            m_leafNode), | 
| 706 |                                                              JobTypes::SyncMaterialGatherer, | 
| 707 |                                                              m_renderViewIndex); | 
| 708 |     } | 
| 709 |  | 
| 710 |     const bool layerCacheNeedsRebuild = m_rebuildFlags.testFlag(flag: RebuildFlag::LayerCacheRebuild); | 
| 711 |     if (layerCacheNeedsRebuild) { | 
| 712 |         m_filterEntityByLayerJob = Render::FilterLayerEntityJobPtr::create(); | 
| 713 |         m_filterEntityByLayerJob->setManager(m_renderer->nodeManagers()); | 
| 714 |         m_syncFilterEntityByLayerJob = CreateSynchronizerJobPtr(SyncFilterEntityByLayer(m_filterEntityByLayerJob, | 
| 715 |                                                                                         m_renderer, | 
| 716 |                                                                                         m_leafNode), | 
| 717 |                                                                 JobTypes::SyncFilterEntityByLayer, | 
| 718 |                                                                 m_renderViewIndex); | 
| 719 |     } | 
| 720 |  | 
| 721 |     m_syncRenderViewPreCommandUpdateJob = CreateSynchronizerJobPtr(SyncRenderViewPreCommandUpdate(m_renderViewJob, | 
| 722 |                                                                                                   m_frustumCullingJob, | 
| 723 |                                                                                                   m_filterProximityJob, | 
| 724 |                                                                                                   m_materialGathererJobs, | 
| 725 |                                                                                                   m_renderViewCommandUpdaterJobs, | 
| 726 |                                                                                                   m_renderViewCommandBuilderJobs, | 
| 727 |                                                                                                   m_renderer, | 
| 728 |                                                                                                   m_leafNode, | 
| 729 |                                                                                                   m_rebuildFlags), | 
| 730 |                                                                    JobTypes::SyncRenderViewPreCommandUpdate, | 
| 731 |                                                                    m_renderViewIndex); | 
| 732 |  | 
| 733 |     m_syncRenderViewPostCommandUpdateJob = CreateSynchronizerJobPtr(SyncRenderViewPostCommandUpdate(m_renderViewJob, | 
| 734 |                                                                                                     m_renderViewCommandUpdaterJobs, | 
| 735 |                                                                                                     m_renderer, | 
| 736 |                                                                                                     m_leafNode), | 
| 737 |                                                                     JobTypes::SyncRenderViewPostCommandUpdate, | 
| 738 |                                                                     m_renderViewIndex); | 
| 739 |  | 
| 740 |     m_syncRenderViewPostInitializationJob = CreateSynchronizerJobPtr(SyncRenderViewPostInitialization(m_renderViewJob, | 
| 741 |                                                                                                       m_frustumCullingJob, | 
| 742 |                                                                                                       m_filterEntityByLayerJob, | 
| 743 |                                                                                                       m_filterProximityJob, | 
| 744 |                                                                                                       m_materialGathererJobs, | 
| 745 |                                                                                                       m_renderViewCommandUpdaterJobs, | 
| 746 |                                                                                                       m_renderViewCommandBuilderJobs), | 
| 747 |                                                                      JobTypes::SyncRenderViewInitialization, | 
| 748 |                                                                      m_renderViewIndex); | 
| 749 | } | 
| 750 |  | 
| 751 | QVector<Qt3DCore::QAspectJobPtr> RenderViewBuilder::buildJobHierachy() const | 
| 752 | { | 
| 753 |     QVector<Qt3DCore::QAspectJobPtr> jobs; | 
| 754 |     auto daspect = QRenderAspectPrivate::get(q: m_renderer->aspect()); | 
| 755 |     auto expandBVJob = daspect->m_expandBoundingVolumeJob; | 
| 756 |     auto wordTransformJob = daspect->m_worldTransformJob; | 
| 757 |     auto updateTreeEnabledJob = daspect->m_updateTreeEnabledJob; | 
| 758 |     auto updateSkinningPaletteJob = daspect->m_updateSkinningPaletteJob; | 
| 759 |     auto updateEntityLayersJob = daspect->m_updateEntityLayersJob; | 
| 760 |  | 
| 761 |     jobs.reserve(asize: m_materialGathererJobs.size() + m_renderViewCommandUpdaterJobs.size() + 11); | 
| 762 |  | 
| 763 |     // Set dependencies | 
| 764 |  | 
| 765 |     // Finish the skinning palette job before processing renderviews | 
| 766 |     // TODO: Maybe only update skinning palettes for non-culled entities | 
| 767 |     m_renderViewJob->addDependency(dependency: updateSkinningPaletteJob); | 
| 768 |  | 
| 769 |     m_syncPreFrustumCullingJob->addDependency(dependency: wordTransformJob); | 
| 770 |     m_syncPreFrustumCullingJob->addDependency(dependency: m_renderer->updateShaderDataTransformJob()); | 
| 771 |     m_syncPreFrustumCullingJob->addDependency(dependency: m_syncRenderViewPostInitializationJob); | 
| 772 |  | 
| 773 |     m_frustumCullingJob->addDependency(dependency: expandBVJob); | 
| 774 |     m_frustumCullingJob->addDependency(dependency: m_syncPreFrustumCullingJob); | 
| 775 |  | 
| 776 |     m_setClearDrawBufferIndexJob->addDependency(dependency: m_syncRenderViewPostInitializationJob); | 
| 777 |  | 
| 778 |     m_syncRenderViewPostInitializationJob->addDependency(dependency: m_renderViewJob); | 
| 779 |  | 
| 780 |     m_filterProximityJob->addDependency(dependency: expandBVJob); | 
| 781 |     m_filterProximityJob->addDependency(dependency: m_syncRenderViewPostInitializationJob); | 
| 782 |  | 
| 783 |     m_syncRenderViewPreCommandUpdateJob->addDependency(dependency: m_syncRenderViewPostInitializationJob); | 
| 784 |     m_syncRenderViewPreCommandUpdateJob->addDependency(dependency: m_filterProximityJob); | 
| 785 |     m_syncRenderViewPreCommandUpdateJob->addDependency(dependency: m_frustumCullingJob); | 
| 786 |  | 
| 787 |     // Ensure the RenderThread won't be able to process dirtyResources | 
| 788 |     // before they have been completely gathered | 
| 789 |     m_syncRenderViewPreCommandUpdateJob->addDependency(dependency: m_renderer->introspectShadersJob()); | 
| 790 |     m_syncRenderViewPreCommandUpdateJob->addDependency(dependency: m_renderer->bufferGathererJob()); | 
| 791 |     m_syncRenderViewPreCommandUpdateJob->addDependency(dependency: m_renderer->textureGathererJob()); | 
| 792 |     m_syncRenderViewPreCommandUpdateJob->addDependency(dependency: m_renderer->lightGathererJob()); | 
| 793 |  | 
| 794 |     for (const auto &renderViewCommandUpdater : qAsConst(t: m_renderViewCommandUpdaterJobs)) { | 
| 795 |         renderViewCommandUpdater->addDependency(dependency: m_syncRenderViewPreCommandUpdateJob); | 
| 796 |         m_syncRenderViewPostCommandUpdateJob->addDependency(dependency: renderViewCommandUpdater); | 
| 797 |     } | 
| 798 |  | 
| 799 |     m_renderer->frameCleanupJob()->addDependency(dependency: m_syncRenderViewPostCommandUpdateJob); | 
| 800 |     m_renderer->frameCleanupJob()->addDependency(dependency: m_setClearDrawBufferIndexJob); | 
| 801 |  | 
| 802 |     // Add jobs | 
| 803 |     jobs.push_back(t: m_renderViewJob); // Step 1 | 
| 804 |  | 
| 805 |     jobs.push_back(t: m_syncRenderViewPostInitializationJob); // Step 2 | 
| 806 |  | 
| 807 |     const bool commandsNeedRebuild = m_rebuildFlags.testFlag(flag: RebuildFlag::FullCommandRebuild); | 
| 808 |     const bool materialCacheNeedsRebuild = m_rebuildFlags.testFlag(flag: RebuildFlag::MaterialCacheRebuild); | 
| 809 |     const bool layerCacheNeedsRebuild = m_rebuildFlags.testFlag(flag: RebuildFlag::LayerCacheRebuild); | 
| 810 |  | 
| 811 |     if (commandsNeedRebuild) { // Step 3 | 
| 812 |         m_syncRenderViewPreCommandBuildingJob->addDependency(dependency: m_renderer->computableEntityFilterJob()); | 
| 813 |         m_syncRenderViewPreCommandBuildingJob->addDependency(dependency: m_renderer->renderableEntityFilterJob()); | 
| 814 |         m_syncRenderViewPreCommandBuildingJob->addDependency(dependency: m_syncRenderViewPostInitializationJob); | 
| 815 |  | 
| 816 |         if (materialCacheNeedsRebuild) | 
| 817 |             m_syncRenderViewPreCommandBuildingJob->addDependency(dependency: m_syncMaterialGathererJob); | 
| 818 |  | 
| 819 |         jobs.push_back(t: m_syncRenderViewPreCommandBuildingJob); | 
| 820 |  | 
| 821 |         for (const auto &renderViewCommandBuilder : qAsConst(t: m_renderViewCommandBuilderJobs)) { | 
| 822 |             renderViewCommandBuilder->addDependency(dependency: m_syncRenderViewPreCommandBuildingJob); | 
| 823 |             m_syncRenderViewPreCommandUpdateJob->addDependency(dependency: renderViewCommandBuilder); | 
| 824 |             jobs.push_back(t: renderViewCommandBuilder); | 
| 825 |         } | 
| 826 |     } | 
| 827 |  | 
| 828 |     if (layerCacheNeedsRebuild) { | 
| 829 |         m_filterEntityByLayerJob->addDependency(dependency: updateEntityLayersJob); | 
| 830 |         m_filterEntityByLayerJob->addDependency(dependency: m_syncRenderViewPostInitializationJob); | 
| 831 |         m_filterEntityByLayerJob->addDependency(dependency: updateTreeEnabledJob); | 
| 832 |  | 
| 833 |         m_syncFilterEntityByLayerJob->addDependency(dependency: m_filterEntityByLayerJob); | 
| 834 |         m_syncRenderViewPreCommandUpdateJob->addDependency(dependency: m_syncFilterEntityByLayerJob); | 
| 835 |  | 
| 836 |         jobs.push_back(t: m_filterEntityByLayerJob); // Step 3 | 
| 837 |         jobs.push_back(t: m_syncFilterEntityByLayerJob); // Step 4 | 
| 838 |     } | 
| 839 |     jobs.push_back(t: m_syncPreFrustumCullingJob); // Step 3 | 
| 840 |     jobs.push_back(t: m_filterProximityJob); // Step 3 | 
| 841 |     jobs.push_back(t: m_setClearDrawBufferIndexJob); // Step 3 | 
| 842 |  | 
| 843 |     if (materialCacheNeedsRebuild) { | 
| 844 |         for (const auto &materialGatherer : qAsConst(t: m_materialGathererJobs))  { | 
| 845 |             materialGatherer->addDependency(dependency: m_syncRenderViewPostInitializationJob); | 
| 846 |             materialGatherer->addDependency(dependency: m_renderer->introspectShadersJob()); | 
| 847 |             materialGatherer->addDependency(dependency: m_renderer->filterCompatibleTechniqueJob()); | 
| 848 |             jobs.push_back(t: materialGatherer); // Step3 | 
| 849 |             m_syncMaterialGathererJob->addDependency(dependency: materialGatherer); | 
| 850 |         } | 
| 851 |         m_syncRenderViewPreCommandUpdateJob->addDependency(dependency: m_syncMaterialGathererJob); | 
| 852 |  | 
| 853 |         jobs.push_back(t: m_syncMaterialGathererJob); // Step 3 | 
| 854 |     } | 
| 855 |  | 
| 856 |     jobs.push_back(t: m_frustumCullingJob); // Step 4 | 
| 857 |     jobs.push_back(t: m_syncRenderViewPreCommandUpdateJob); // Step 5 | 
| 858 |  | 
| 859 |     // Build RenderCommands or Update RenderCommand Uniforms | 
| 860 |     for (const auto &renderViewCommandBuilder : qAsConst(t: m_renderViewCommandUpdaterJobs)) // Step 6 | 
| 861 |         jobs.push_back(t: renderViewCommandBuilder); | 
| 862 |  | 
| 863 |     jobs.push_back(t: m_syncRenderViewPostCommandUpdateJob); // Step 7 | 
| 864 |  | 
| 865 |     return jobs; | 
| 866 | } | 
| 867 |  | 
| 868 | Renderer *RenderViewBuilder::renderer() const | 
| 869 | { | 
| 870 |     return m_renderer; | 
| 871 | } | 
| 872 |  | 
| 873 | int RenderViewBuilder::renderViewIndex() const | 
| 874 | { | 
| 875 |     return m_renderViewIndex; | 
| 876 | } | 
| 877 |  | 
| 878 | void RenderViewBuilder::setLayerCacheNeedsToBeRebuilt(bool needsToBeRebuilt) | 
| 879 | { | 
| 880 |     m_rebuildFlags.setFlag(flag: RebuildFlag::LayerCacheRebuild, on: needsToBeRebuilt); | 
| 881 | } | 
| 882 |  | 
| 883 | bool RenderViewBuilder::layerCacheNeedsToBeRebuilt() const | 
| 884 | { | 
| 885 |     return m_rebuildFlags.testFlag(flag: RebuildFlag::LayerCacheRebuild); | 
| 886 | } | 
| 887 |  | 
| 888 | void RenderViewBuilder::setMaterialGathererCacheNeedsToBeRebuilt(bool needsToBeRebuilt) | 
| 889 | { | 
| 890 |     m_rebuildFlags.setFlag(flag: RebuildFlag::MaterialCacheRebuild, on: needsToBeRebuilt); | 
| 891 | } | 
| 892 |  | 
| 893 | bool RenderViewBuilder::materialGathererCacheNeedsToBeRebuilt() const | 
| 894 | { | 
| 895 |     return m_rebuildFlags.testFlag(flag: RebuildFlag::MaterialCacheRebuild); | 
| 896 | } | 
| 897 |  | 
| 898 | void RenderViewBuilder::setRenderCommandCacheNeedsToBeRebuilt(bool needsToBeRebuilt) | 
| 899 | { | 
| 900 |     m_rebuildFlags.setFlag(flag: RebuildFlag::FullCommandRebuild, on: needsToBeRebuilt); | 
| 901 | } | 
| 902 |  | 
| 903 | bool RenderViewBuilder::renderCommandCacheNeedsToBeRebuilt() const | 
| 904 | { | 
| 905 |     return m_rebuildFlags.testFlag(flag: RebuildFlag::FullCommandRebuild); | 
| 906 | } | 
| 907 |  | 
| 908 | void RenderViewBuilder::setLightCacheNeedsToBeRebuilt(bool needsToBeRebuilt) | 
| 909 | { | 
| 910 |     m_rebuildFlags.setFlag(flag: RebuildFlag::LightCacheRebuild, on: needsToBeRebuilt); | 
| 911 | } | 
| 912 |  | 
| 913 | bool RenderViewBuilder::lightCacheNeedsToBeRebuilt() const | 
| 914 | { | 
| 915 |     return m_rebuildFlags.testFlag(flag: RebuildFlag::LightCacheRebuild); | 
| 916 | } | 
| 917 |  | 
| 918 | int RenderViewBuilder::defaultJobCount() | 
| 919 | { | 
| 920 |     static int jobCount = 0; | 
| 921 |     if (jobCount) | 
| 922 |         return jobCount; | 
| 923 |  | 
| 924 |     const QByteArray maxThreadCount = qgetenv(varName: "QT3D_MAX_THREAD_COUNT" ); | 
| 925 |     if (!maxThreadCount.isEmpty()) { | 
| 926 |         bool conversionOK = false; | 
| 927 |         const int maxThreadCountValue = maxThreadCount.toInt(ok: &conversionOK); | 
| 928 |         if (conversionOK) { | 
| 929 |             jobCount = maxThreadCountValue; | 
| 930 |             return jobCount; | 
| 931 |         } | 
| 932 |     } | 
| 933 |  | 
| 934 |     jobCount = QThread::idealThreadCount(); | 
| 935 |     return jobCount; | 
| 936 | } | 
| 937 |  | 
| 938 | int RenderViewBuilder::optimalJobCount() const | 
| 939 | { | 
| 940 |     return m_optimalParallelJobCount; | 
| 941 | } | 
| 942 |  | 
| 943 | void RenderViewBuilder::setOptimalJobCount(int v) | 
| 944 | { | 
| 945 |     m_optimalParallelJobCount = v; | 
| 946 | } | 
| 947 |  | 
| 948 | QVector<Entity *> RenderViewBuilder::entitiesInSubset(const QVector<Entity *> &entities, const QVector<Entity *> &subset) | 
| 949 | { | 
| 950 |     QVector<Entity *> intersection; | 
| 951 |     intersection.reserve(asize: qMin(a: entities.size(), b: subset.size())); | 
| 952 |     std::set_intersection(first1: entities.begin(), last1: entities.end(), | 
| 953 |                           first2: subset.begin(), last2: subset.end(), | 
| 954 |                           result: std::back_inserter(x&: intersection)); | 
| 955 |  | 
| 956 |     return intersection; | 
| 957 | } | 
| 958 |  | 
| 959 | } // OpenGL | 
| 960 |  | 
| 961 | } // Render | 
| 962 |  | 
| 963 | } // Qt3DRender | 
| 964 |  | 
| 965 | QT_END_NAMESPACE | 
| 966 |  |