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
45QT_BEGIN_NAMESPACE
46
47namespace Qt3DRender {
48
49namespace Render {
50namespace OpenGL {
51
52namespace {
53
54int 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
62class SyncPreCommandBuilding
63{
64public:
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
106private:
107 RenderViewInitializerJobPtr m_renderViewInitializer;
108 QVector<RenderViewCommandBuilderJobPtr> m_renderViewCommandBuilderJobs;
109 Renderer *m_renderer;
110 FrameGraphNode *m_leafNode;
111};
112
113class SyncRenderViewPostCommandUpdate
114{
115public:
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
165private:
166 RenderViewInitializerJobPtr m_renderViewJob;
167 QVector<RenderViewCommandUpdaterJobPtr> m_renderViewCommandUpdaterJobs;
168 Renderer *m_renderer;
169 FrameGraphNode *m_leafNode;
170};
171
172class SyncPreFrustumCulling
173{
174public:
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
192private:
193 RenderViewInitializerJobPtr m_renderViewJob;
194 FrustumCullingJobPtr m_frustumCullingJob;
195};
196
197class SyncRenderViewPostInitialization
198{
199public:
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
243private:
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
255class SyncRenderViewPreCommandUpdate
256{
257public:
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
462private:
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
474class SetClearDrawBufferIndex
475{
476public:
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
491private:
492 RenderViewInitializerJobPtr m_renderViewJob;
493};
494
495class SyncFilterEntityByLayer
496{
497public:
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
517private:
518 FilterLayerEntityJobPtr m_filterEntityByLayerJob;
519 Renderer *m_renderer;
520 FrameGraphNode *m_leafNode;
521};
522
523class SyncMaterialParameterGatherer
524{
525public:
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
547private:
548 QVector<MaterialParameterGathererJobPtr> m_materialParameterGathererJobs;
549 Renderer *m_renderer;
550 FrameGraphNode *m_leafNode;
551};
552
553} // anonymous
554
555RenderViewBuilder::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
573RenderViewInitializerJobPtr RenderViewBuilder::renderViewJob() const
574{
575 return m_renderViewJob;
576}
577
578FilterLayerEntityJobPtr RenderViewBuilder::filterEntityByLayerJob() const
579{
580 return m_filterEntityByLayerJob;
581}
582
583FrustumCullingJobPtr RenderViewBuilder::frustumCullingJob() const
584{
585 return m_frustumCullingJob;
586}
587
588QVector<RenderViewCommandUpdaterJobPtr> RenderViewBuilder::renderViewCommandUpdaterJobs() const
589{
590 return m_renderViewCommandUpdaterJobs;
591}
592
593QVector<RenderViewCommandBuilderJobPtr> RenderViewBuilder::renderViewCommandBuilderJobs() const
594{
595 return m_renderViewCommandBuilderJobs;
596}
597
598QVector<MaterialParameterGathererJobPtr> RenderViewBuilder::materialGathererJobs() const
599{
600 return m_materialGathererJobs;
601}
602
603SynchronizerJobPtr RenderViewBuilder::syncRenderViewPostInitializationJob() const
604{
605 return m_syncRenderViewPostInitializationJob;
606}
607
608SynchronizerJobPtr RenderViewBuilder::syncPreFrustumCullingJob() const
609{
610 return m_syncPreFrustumCullingJob;
611}
612
613SynchronizerJobPtr RenderViewBuilder::syncRenderViewPreCommandBuildingJob() const
614{
615 return m_syncRenderViewPreCommandBuildingJob;
616}
617
618SynchronizerJobPtr RenderViewBuilder::syncRenderViewPreCommandUpdateJob() const
619{
620 return m_syncRenderViewPreCommandUpdateJob;
621}
622
623SynchronizerJobPtr RenderViewBuilder::syncRenderViewPostCommandUpdateJob() const
624{
625 return m_syncRenderViewPostCommandUpdateJob;
626}
627
628SynchronizerJobPtr RenderViewBuilder::setClearDrawBufferIndexJob() const
629{
630 return m_setClearDrawBufferIndexJob;
631}
632
633SynchronizerJobPtr RenderViewBuilder::syncFilterEntityByLayerJob() const
634{
635 return m_syncFilterEntityByLayerJob;
636}
637
638SynchronizerJobPtr RenderViewBuilder::syncMaterialGathererJob() const
639{
640 return m_syncMaterialGathererJob;
641}
642
643FilterProximityDistanceJobPtr RenderViewBuilder::filterProximityJob() const
644{
645 return m_filterProximityJob;
646}
647
648void 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
751QVector<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
868Renderer *RenderViewBuilder::renderer() const
869{
870 return m_renderer;
871}
872
873int RenderViewBuilder::renderViewIndex() const
874{
875 return m_renderViewIndex;
876}
877
878void RenderViewBuilder::setLayerCacheNeedsToBeRebuilt(bool needsToBeRebuilt)
879{
880 m_rebuildFlags.setFlag(flag: RebuildFlag::LayerCacheRebuild, on: needsToBeRebuilt);
881}
882
883bool RenderViewBuilder::layerCacheNeedsToBeRebuilt() const
884{
885 return m_rebuildFlags.testFlag(flag: RebuildFlag::LayerCacheRebuild);
886}
887
888void RenderViewBuilder::setMaterialGathererCacheNeedsToBeRebuilt(bool needsToBeRebuilt)
889{
890 m_rebuildFlags.setFlag(flag: RebuildFlag::MaterialCacheRebuild, on: needsToBeRebuilt);
891}
892
893bool RenderViewBuilder::materialGathererCacheNeedsToBeRebuilt() const
894{
895 return m_rebuildFlags.testFlag(flag: RebuildFlag::MaterialCacheRebuild);
896}
897
898void RenderViewBuilder::setRenderCommandCacheNeedsToBeRebuilt(bool needsToBeRebuilt)
899{
900 m_rebuildFlags.setFlag(flag: RebuildFlag::FullCommandRebuild, on: needsToBeRebuilt);
901}
902
903bool RenderViewBuilder::renderCommandCacheNeedsToBeRebuilt() const
904{
905 return m_rebuildFlags.testFlag(flag: RebuildFlag::FullCommandRebuild);
906}
907
908void RenderViewBuilder::setLightCacheNeedsToBeRebuilt(bool needsToBeRebuilt)
909{
910 m_rebuildFlags.setFlag(flag: RebuildFlag::LightCacheRebuild, on: needsToBeRebuilt);
911}
912
913bool RenderViewBuilder::lightCacheNeedsToBeRebuilt() const
914{
915 return m_rebuildFlags.testFlag(flag: RebuildFlag::LightCacheRebuild);
916}
917
918int 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
938int RenderViewBuilder::optimalJobCount() const
939{
940 return m_optimalParallelJobCount;
941}
942
943void RenderViewBuilder::setOptimalJobCount(int v)
944{
945 m_optimalParallelJobCount = v;
946}
947
948QVector<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
965QT_END_NAMESPACE
966

source code of qt3d/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp