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

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