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 | |