1// Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "renderviewbuilder_p.h"
5#include <Qt3DRender/private/qrenderaspect_p.h>
6#include <Qt3DCore/private/qaspectjobmanager_p.h>
7
8QT_BEGIN_NAMESPACE
9
10namespace Qt3DRender {
11
12namespace Render {
13namespace Rhi {
14
15RenderViewBuilder::RenderViewBuilder(Render::FrameGraphNode *leafNode, int renderViewIndex, Renderer *renderer)
16 : m_leafNode(leafNode)
17 , m_renderViewIndex(renderViewIndex)
18 , m_renderer(renderer)
19 , m_renderViewJob(RenderViewInitializerJobPtr::create())
20 , m_filterEntityByLayerJob()
21 , m_frustumCullingJob(new Render::FrustumCullingJob())
22 , m_syncPreFrustumCullingJob(CreateSynchronizerJobPtr(SyncPreFrustumCulling(m_renderViewJob, m_frustumCullingJob), JobTypes::SyncFrustumCulling))
23 , m_syncFilterEntityByLayerJob()
24 , m_filterProximityJob(Render::FilterProximityDistanceJobPtr::create())
25{
26 // In some cases having less jobs is better (especially on fast cpus where
27 // splitting just adds more overhead). Ideally, we should try to set the value
28 // depending on the platform/CPU/nbr of cores
29 m_optimalParallelJobCount = Qt3DCore::QAspectJobManager::idealThreadCount();
30}
31
32RenderViewInitializerJobPtr RenderViewBuilder::renderViewJob() const
33{
34 return m_renderViewJob;
35}
36
37FilterLayerEntityJobPtr RenderViewBuilder::filterEntityByLayerJob() const
38{
39 return m_filterEntityByLayerJob;
40}
41
42FrustumCullingJobPtr RenderViewBuilder::frustumCullingJob() const
43{
44 return m_frustumCullingJob;
45}
46
47const std::vector<RenderViewCommandUpdaterJobPtr> &RenderViewBuilder::renderViewCommandUpdaterJobs() const
48{
49 return m_renderViewCommandUpdaterJobs;
50}
51
52const std::vector<RenderViewCommandBuilderJobPtr> &RenderViewBuilder::renderViewCommandBuilderJobs() const
53{
54 return m_renderViewCommandBuilderJobs;
55}
56
57const std::vector<MaterialParameterGathererJobPtr> &RenderViewBuilder::materialGathererJobs() const
58{
59 return m_materialGathererJobs;
60}
61
62SynchronizerJobPtr RenderViewBuilder::syncRenderViewPostInitializationJob() const
63{
64 return m_syncRenderViewPostInitializationJob;
65}
66
67SynchronizerJobPtr RenderViewBuilder::syncPreFrustumCullingJob() const
68{
69 return m_syncPreFrustumCullingJob;
70}
71
72SynchronizerJobPtr RenderViewBuilder::syncRenderViewPreCommandBuildingJob() const
73{
74 return m_syncRenderViewPreCommandBuildingJob;
75}
76
77SynchronizerJobPtr RenderViewBuilder::syncRenderViewPreCommandUpdateJob() const
78{
79 return m_syncRenderViewPreCommandUpdateJob;
80}
81
82SynchronizerJobPtr RenderViewBuilder::syncRenderViewPostCommandUpdateJob() const
83{
84 return m_syncRenderViewPostCommandUpdateJob;
85}
86
87SynchronizerJobPtr RenderViewBuilder::syncFilterEntityByLayerJob() const
88{
89 return m_syncFilterEntityByLayerJob;
90}
91
92SynchronizerJobPtr RenderViewBuilder::syncMaterialGathererJob() const
93{
94 return m_syncMaterialGathererJob;
95}
96
97FilterProximityDistanceJobPtr RenderViewBuilder::filterProximityJob() const
98{
99 return m_filterProximityJob;
100}
101
102void RenderViewBuilder::prepareJobs()
103{
104 // Init what we can here
105 m_filterProximityJob->setManager(m_renderer->nodeManagers());
106 m_frustumCullingJob->setRoot(m_renderer->sceneRoot());
107
108 const bool commandsNeedRebuild = m_rebuildFlags.testFlag(flag: RebuildFlag::FullCommandRebuild);
109 if (commandsNeedRebuild) {
110 m_renderViewCommandBuilderJobs.reserve(n: m_optimalParallelJobCount);
111 for (auto i = 0; i < m_optimalParallelJobCount; ++i) {
112 auto renderViewCommandBuilder = Render::Rhi::RenderViewCommandBuilderJobPtr::create();
113 m_renderViewCommandBuilderJobs.push_back(x: renderViewCommandBuilder);
114 }
115 m_syncRenderViewPreCommandBuildingJob = CreateSynchronizerJobPtr(SyncPreCommandBuilding(m_renderViewJob,
116 m_renderViewCommandBuilderJobs,
117 m_renderer,
118 m_leafNode),
119 JobTypes::SyncRenderViewPreCommandBuilding);
120 }
121
122 m_renderViewJob->setRenderer(m_renderer);
123 m_renderViewJob->setFrameGraphLeafNode(m_leafNode);
124 m_renderViewJob->setSubmitOrderIndex(m_renderViewIndex);
125
126 // RenderCommand building is the most consuming task -> split it
127 // Estimate the number of jobs to create based on the number of entities
128 m_renderViewCommandUpdaterJobs.reserve(n: m_optimalParallelJobCount);
129 for (auto i = 0; i < m_optimalParallelJobCount; ++i) {
130 auto renderViewCommandUpdater = RenderViewCommandUpdaterJobPtr::create();
131 m_renderViewCommandUpdaterJobs.push_back(x: renderViewCommandUpdater);
132 }
133
134 const bool materialCacheNeedsRebuild = m_rebuildFlags.testFlag(flag: RebuildFlag::MaterialCacheRebuild);
135 if (materialCacheNeedsRebuild) {
136 // Since Material gathering is an heavy task, we split it
137 const std::vector<HMaterial> &materialHandles = m_renderer->nodeManagers()->materialManager()->activeHandles();
138 const size_t handlesCount = materialHandles.size();
139 if (handlesCount) {
140 m_materialGathererJobs.reserve(n: m_optimalParallelJobCount);
141 const size_t elementsPerJob = std::max(a: handlesCount / m_optimalParallelJobCount, b: size_t(1));
142 size_t elementCount = 0;
143 while (elementCount < handlesCount) {
144 auto materialGatherer = MaterialParameterGathererJobPtr::create();
145 materialGatherer->setNodeManagers(m_renderer->nodeManagers());
146 // TO DO: Candidate for std::span if C++20
147 materialGatherer->setHandles({materialHandles.begin() + elementCount,
148 materialHandles.begin() + std::min(a: elementCount + elementsPerJob, b: handlesCount)});
149 m_materialGathererJobs.push_back(x: materialGatherer);
150
151 elementCount += elementsPerJob;
152 }
153 }
154 m_syncMaterialGathererJob = CreateSynchronizerJobPtr(SyncMaterialParameterGatherer(m_materialGathererJobs,
155 m_renderer,
156 m_leafNode),
157 JobTypes::SyncMaterialGatherer);
158 }
159
160 const bool layerCacheNeedsRebuild = m_rebuildFlags.testFlag(flag: RebuildFlag::LayerCacheRebuild);
161 if (layerCacheNeedsRebuild) {
162 m_filterEntityByLayerJob = Render::FilterLayerEntityJobPtr::create();
163 m_filterEntityByLayerJob->setManager(m_renderer->nodeManagers());
164 m_syncFilterEntityByLayerJob = CreateSynchronizerJobPtr(SyncFilterEntityByLayer(m_filterEntityByLayerJob,
165 m_renderer,
166 m_leafNode),
167 JobTypes::SyncFilterEntityByLayer);
168 }
169
170 m_syncRenderViewPreCommandUpdateJob = CreateSynchronizerJobPtr(SyncRenderViewPreCommandUpdate(m_renderViewJob,
171 m_frustumCullingJob,
172 m_filterProximityJob,
173 m_materialGathererJobs,
174 m_renderViewCommandUpdaterJobs,
175 m_renderViewCommandBuilderJobs,
176 m_renderer,
177 m_leafNode,
178 m_rebuildFlags),
179 JobTypes::SyncRenderViewPreCommandUpdate);
180
181 m_syncRenderViewPostCommandUpdateJob = CreateSynchronizerJobPtr(SyncRenderViewPostCommandUpdate(m_renderViewJob,
182 m_renderViewCommandUpdaterJobs,
183 m_renderer),
184 JobTypes::SyncRenderViewPostCommandUpdate);
185
186 m_syncRenderViewPostInitializationJob = CreateSynchronizerJobPtr(SyncRenderViewPostInitialization(m_renderViewJob,
187 m_frustumCullingJob,
188 m_filterEntityByLayerJob,
189 m_filterProximityJob,
190 m_materialGathererJobs,
191 m_renderViewCommandUpdaterJobs,
192 m_renderViewCommandBuilderJobs),
193 JobTypes::SyncRenderViewInitialization);
194}
195
196std::vector<Qt3DCore::QAspectJobPtr> RenderViewBuilder::buildJobHierachy() const
197{
198 std::vector<Qt3DCore::QAspectJobPtr> jobs;
199 auto daspect = QRenderAspectPrivate::get(q: m_renderer->aspect());
200 auto expandBVJob = daspect->m_expandBoundingVolumeJob;
201 auto wordTransformJob = daspect->m_worldTransformJob;
202 auto updateTreeEnabledJob = daspect->m_updateTreeEnabledJob;
203 auto updateSkinningPaletteJob = daspect->m_updateSkinningPaletteJob;
204 auto updateEntityLayersJob = daspect->m_updateEntityLayersJob;
205
206 jobs.reserve(n: m_materialGathererJobs.size() + m_renderViewCommandUpdaterJobs.size() + 11);
207
208 // Set dependencies
209
210 // Finish the skinning palette job before processing renderviews
211 // TODO: Maybe only update skinning palettes for non-culled entities
212 m_renderViewJob->addDependency(dependency: updateSkinningPaletteJob);
213
214 m_syncPreFrustumCullingJob->addDependency(dependency: wordTransformJob);
215 m_syncPreFrustumCullingJob->addDependency(dependency: m_renderer->updateShaderDataTransformJob());
216 m_syncPreFrustumCullingJob->addDependency(dependency: m_syncRenderViewPostInitializationJob);
217
218 m_frustumCullingJob->addDependency(dependency: expandBVJob);
219 m_frustumCullingJob->addDependency(dependency: m_syncPreFrustumCullingJob);
220
221 m_syncRenderViewPostInitializationJob->addDependency(dependency: m_renderViewJob);
222
223 m_filterProximityJob->addDependency(dependency: expandBVJob);
224 m_filterProximityJob->addDependency(dependency: m_syncRenderViewPostInitializationJob);
225
226 m_syncRenderViewPreCommandUpdateJob->addDependency(dependency: m_syncRenderViewPostInitializationJob);
227 m_syncRenderViewPreCommandUpdateJob->addDependency(dependency: m_filterProximityJob);
228 m_syncRenderViewPreCommandUpdateJob->addDependency(dependency: m_frustumCullingJob);
229
230 // Ensure the RenderThread won't be able to process dirtyResources
231 // before they have been completely gathered
232 m_syncRenderViewPreCommandUpdateJob->addDependency(dependency: m_renderer->introspectShadersJob());
233 m_syncRenderViewPreCommandUpdateJob->addDependency(dependency: m_renderer->bufferGathererJob());
234 m_syncRenderViewPreCommandUpdateJob->addDependency(dependency: m_renderer->textureGathererJob());
235 m_syncRenderViewPreCommandUpdateJob->addDependency(dependency: m_renderer->lightGathererJob());
236
237 for (const auto &renderViewCommandUpdater : m_renderViewCommandUpdaterJobs) {
238 renderViewCommandUpdater->addDependency(dependency: m_syncRenderViewPreCommandUpdateJob);
239 m_syncRenderViewPostCommandUpdateJob->addDependency(dependency: renderViewCommandUpdater);
240 }
241
242 m_renderer->frameCleanupJob()->addDependency(dependency: m_syncRenderViewPostCommandUpdateJob);
243
244 // Add jobs
245 jobs.push_back(x: m_renderViewJob); // Step 1
246
247 jobs.push_back(x: m_syncRenderViewPostInitializationJob); // Step 2
248
249 const bool commandsNeedRebuild = m_rebuildFlags.testFlag(flag: RebuildFlag::FullCommandRebuild);
250 const bool materialCacheNeedsRebuild = m_rebuildFlags.testFlag(flag: RebuildFlag::MaterialCacheRebuild);
251 const bool layerCacheNeedsRebuild = m_rebuildFlags.testFlag(flag: RebuildFlag::LayerCacheRebuild);
252
253 if (commandsNeedRebuild) { // Step 3
254 m_syncRenderViewPreCommandBuildingJob->addDependency(dependency: m_renderer->computableEntityFilterJob());
255 m_syncRenderViewPreCommandBuildingJob->addDependency(dependency: m_renderer->renderableEntityFilterJob());
256 m_syncRenderViewPreCommandBuildingJob->addDependency(dependency: m_syncRenderViewPostInitializationJob);
257
258 if (materialCacheNeedsRebuild)
259 m_syncRenderViewPreCommandBuildingJob->addDependency(dependency: m_syncMaterialGathererJob);
260
261 jobs.push_back(x: m_syncRenderViewPreCommandBuildingJob);
262
263 for (const auto &renderViewCommandBuilder : m_renderViewCommandBuilderJobs) {
264 renderViewCommandBuilder->addDependency(dependency: m_syncRenderViewPreCommandBuildingJob);
265 m_syncRenderViewPreCommandUpdateJob->addDependency(dependency: renderViewCommandBuilder);
266 jobs.push_back(x: renderViewCommandBuilder);
267 }
268 }
269
270 if (layerCacheNeedsRebuild) {
271 m_filterEntityByLayerJob->addDependency(dependency: updateEntityLayersJob);
272 m_filterEntityByLayerJob->addDependency(dependency: m_syncRenderViewPostInitializationJob);
273 m_filterEntityByLayerJob->addDependency(dependency: updateTreeEnabledJob);
274
275 m_syncFilterEntityByLayerJob->addDependency(dependency: m_filterEntityByLayerJob);
276 m_syncRenderViewPreCommandUpdateJob->addDependency(dependency: m_syncFilterEntityByLayerJob);
277
278 jobs.push_back(x: m_filterEntityByLayerJob); // Step 3
279 jobs.push_back(x: m_syncFilterEntityByLayerJob); // Step 4
280 }
281 jobs.push_back(x: m_syncPreFrustumCullingJob); // Step 3
282 jobs.push_back(x: m_filterProximityJob); // Step 3
283
284 if (materialCacheNeedsRebuild) {
285 for (const auto &materialGatherer : m_materialGathererJobs) {
286 materialGatherer->addDependency(dependency: m_syncRenderViewPostInitializationJob);
287 materialGatherer->addDependency(dependency: m_renderer->introspectShadersJob());
288 materialGatherer->addDependency(dependency: m_renderer->filterCompatibleTechniqueJob());
289 jobs.push_back(x: materialGatherer); // Step3
290 m_syncMaterialGathererJob->addDependency(dependency: materialGatherer);
291 }
292 m_syncRenderViewPreCommandUpdateJob->addDependency(dependency: m_syncMaterialGathererJob);
293
294 jobs.push_back(x: m_syncMaterialGathererJob); // Step 3
295 }
296
297 jobs.push_back(x: m_frustumCullingJob); // Step 4
298 jobs.push_back(x: m_syncRenderViewPreCommandUpdateJob); // Step 5
299
300 // Build RenderCommands or Update RenderCommand Uniforms
301 for (const auto &renderViewCommandBuilder : m_renderViewCommandUpdaterJobs) // Step 6
302 jobs.push_back(x: renderViewCommandBuilder);
303
304 jobs.push_back(x: m_syncRenderViewPostCommandUpdateJob); // Step 7
305
306 return jobs;
307}
308
309Renderer *RenderViewBuilder::renderer() const
310{
311 return m_renderer;
312}
313
314int RenderViewBuilder::renderViewIndex() const
315{
316 return m_renderViewIndex;
317}
318
319void RenderViewBuilder::setLayerCacheNeedsToBeRebuilt(bool needsToBeRebuilt)
320{
321 m_rebuildFlags.setFlag(flag: RebuildFlag::LayerCacheRebuild, on: needsToBeRebuilt);
322}
323
324bool RenderViewBuilder::layerCacheNeedsToBeRebuilt() const
325{
326 return m_rebuildFlags.testFlag(flag: RebuildFlag::LayerCacheRebuild);
327}
328
329void RenderViewBuilder::setMaterialGathererCacheNeedsToBeRebuilt(bool needsToBeRebuilt)
330{
331 m_rebuildFlags.setFlag(flag: RebuildFlag::MaterialCacheRebuild, on: needsToBeRebuilt);
332}
333
334bool RenderViewBuilder::materialGathererCacheNeedsToBeRebuilt() const
335{
336 return m_rebuildFlags.testFlag(flag: RebuildFlag::MaterialCacheRebuild);
337}
338
339void RenderViewBuilder::setRenderCommandCacheNeedsToBeRebuilt(bool needsToBeRebuilt)
340{
341 m_rebuildFlags.setFlag(flag: RebuildFlag::FullCommandRebuild, on: needsToBeRebuilt);
342}
343
344bool RenderViewBuilder::renderCommandCacheNeedsToBeRebuilt() const
345{
346 return m_rebuildFlags.testFlag(flag: RebuildFlag::FullCommandRebuild);
347}
348
349void RenderViewBuilder::setLightCacheNeedsToBeRebuilt(bool needsToBeRebuilt)
350{
351 m_rebuildFlags.setFlag(flag: RebuildFlag::LightCacheRebuild, on: needsToBeRebuilt);
352}
353
354bool RenderViewBuilder::lightCacheNeedsToBeRebuilt() const
355{
356 return m_rebuildFlags.testFlag(flag: RebuildFlag::LightCacheRebuild);
357}
358
359int RenderViewBuilder::optimalJobCount() const
360{
361 return m_optimalParallelJobCount;
362}
363
364void RenderViewBuilder::setOptimalJobCount(int v)
365{
366 m_optimalParallelJobCount = v;
367}
368
369} // Rhi
370
371} // Render
372
373} // Qt3DRender
374
375QT_END_NAMESPACE
376

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