1/****************************************************************************
2**
3** Copyright (C) 2014 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 "renderviewjobutils_p.h"
41#include <Qt3DRender/private/renderlogging_p.h>
42
43#include <Qt3DRender/qgraphicsapifilter.h>
44#include <Qt3DRender/private/sphere_p.h>
45#include <Qt3DRender/qshaderdata.h>
46
47#include <Qt3DRender/private/cameraselectornode_p.h>
48#include <Qt3DRender/private/clearbuffers_p.h>
49#include <Qt3DRender/private/layerfilternode_p.h>
50#include <Qt3DRender/private/nodemanagers_p.h>
51#include <Qt3DRender/private/effect_p.h>
52#include <Qt3DRender/private/renderpassfilternode_p.h>
53#include <Qt3DRender/private/rendertargetselectornode_p.h>
54#include <Qt3DRender/private/sortpolicy_p.h>
55#include <Qt3DRender/private/techniquefilternode_p.h>
56#include <Qt3DRender/private/viewportnode_p.h>
57#include <Qt3DRender/private/managers_p.h>
58#include <Qt3DRender/private/shaderdata_p.h>
59#include <Qt3DRender/private/statesetnode_p.h>
60#include <Qt3DRender/private/dispatchcompute_p.h>
61#include <Qt3DRender/private/rendersurfaceselector_p.h>
62#include <Qt3DRender/private/rendercapture_p.h>
63#include <Qt3DRender/private/buffercapture_p.h>
64#include <Qt3DRender/private/stringtoint_p.h>
65#include <Qt3DRender/private/techniquemanager_p.h>
66#include <Qt3DRender/private/memorybarrier_p.h>
67#include <Qt3DRender/private/blitframebuffer_p.h>
68#include <Qt3DRender/private/waitfence_p.h>
69#include <Qt3DRender/private/renderstateset_p.h>
70#include <renderview_p.h>
71#include <shadervariables_p.h>
72
73QT_BEGIN_NAMESPACE
74
75using namespace Qt3DCore;
76
77namespace Qt3DRender {
78namespace Render {
79namespace OpenGL {
80
81/*!
82 \internal
83 Walks up the framegraph tree from \a fgLeaf and builds up as much state
84 as possible and populates \a rv. For cases where we can't get the specific state
85 (e.g. because it depends upon more than just the framegraph) we store the data from
86 the framegraph that will be needed to later when the rest of the data becomes available
87*/
88void setRenderViewConfigFromFrameGraphLeafNode(RenderView *rv, const FrameGraphNode *fgLeaf)
89{
90 // The specific RenderPass to be used is also dependent upon the Effect and TechniqueFilter
91 // which is referenced by the Material which is referenced by the RenderMesh. So we can
92 // only store the filter info in the RenderView structure and use it to do the resolving
93 // when we build the RenderCommand list.
94 const NodeManagers *manager = rv->nodeManagers();
95 const FrameGraphNode *node = fgLeaf;
96
97 while (node) {
98 FrameGraphNode::FrameGraphNodeType type = node->nodeType();
99 if (node->isEnabled())
100 switch (type) {
101 case FrameGraphNode::InvalidNodeType:
102 // A base FrameGraphNode, can be used for grouping purposes
103 break;
104 case FrameGraphNode::CameraSelector:
105 // Can be set only once and we take camera nearest to the leaf node
106 if (!rv->renderCameraLens()) {
107 const CameraSelector *cameraSelector = static_cast<const CameraSelector *>(node);
108 Entity *camNode = manager->renderNodesManager()->lookupResource(id: cameraSelector->cameraUuid());
109 if (camNode) {
110 CameraLens *lens = camNode->renderComponent<CameraLens>();
111 rv->setRenderCameraEntity(camNode);
112 if (lens && lens->isEnabled()) {
113 rv->setRenderCameraLens(lens);
114 // ViewMatrix and ProjectionMatrix are computed
115 // later in updateMatrices()
116 // since at this point the transformation matrices
117 // may not yet have been updated
118 }
119 }
120 }
121 break;
122
123 case FrameGraphNode::LayerFilter: // Can be set multiple times in the tree
124 rv->appendLayerFilter(layerFilterId: static_cast<const LayerFilterNode *>(node)->peerId());
125 break;
126
127 case FrameGraphNode::ProximityFilter: // Can be set multiple times in the tree
128 rv->appendProximityFilterId(proximityFilterId: node->peerId());
129 break;
130
131 case FrameGraphNode::RenderPassFilter:
132 // Can be set once
133 // TODO: Amalgamate all render pass filters from leaf to root
134 if (!rv->renderPassFilter())
135 rv->setRenderPassFilter(static_cast<const RenderPassFilter *>(node));
136 break;
137
138 case FrameGraphNode::RenderTarget: {
139 // Can be set once and we take render target nearest to the leaf node
140 const RenderTargetSelector *targetSelector = static_cast<const RenderTargetSelector *>(node);
141 QNodeId renderTargetUid = targetSelector->renderTargetUuid();
142 HTarget renderTargetHandle = manager->renderTargetManager()->lookupHandle(id: renderTargetUid);
143
144 // Add renderTarget Handle and build renderCommand AttachmentPack
145 if (!rv->renderTargetId()) {
146 rv->setRenderTargetId(renderTargetUid);
147
148 RenderTarget *renderTarget = manager->renderTargetManager()->data(handle: renderTargetHandle);
149 if (renderTarget)
150 rv->setAttachmentPack(AttachmentPack(renderTarget, manager->attachmentManager(), targetSelector->outputs()));
151 }
152 break;
153 }
154
155 case FrameGraphNode::ClearBuffers: {
156 const ClearBuffers *cbNode = static_cast<const ClearBuffers *>(node);
157 rv->addClearBuffers(cb: cbNode);
158 break;
159 }
160
161 case FrameGraphNode::TechniqueFilter:
162 // Can be set once
163 // TODO Amalgamate all technique filters from leaf to root
164 if (!rv->techniqueFilter())
165 rv->setTechniqueFilter(static_cast<const TechniqueFilter *>(node));
166 break;
167
168 case FrameGraphNode::Viewport: {
169 // If the Viewport has already been set in a lower node
170 // Make it so that the new viewport is actually
171 // a subregion relative to that of the parent viewport
172 const ViewportNode *vpNode = static_cast<const ViewportNode *>(node);
173 rv->setViewport(ViewportNode::computeViewport(childViewport: rv->viewport(), parentViewport: vpNode));
174 rv->setGamma(vpNode->gamma());
175 break;
176 }
177
178 case FrameGraphNode::SortMethod: {
179 const Render::SortPolicy *sortPolicy = static_cast<const Render::SortPolicy *>(node);
180 rv->addSortType(sortTypes: sortPolicy->sortTypes());
181 break;
182 }
183
184 case FrameGraphNode::SubtreeEnabler:
185 // Has no meaning here. SubtreeEnabler was used
186 // in a prior step to filter the list of RenderViewJobs
187 break;
188
189 case FrameGraphNode::StateSet: {
190 const Render::StateSetNode *rStateSet = static_cast<const Render::StateSetNode *>(node);
191 // Add states from new stateSet we might be missing
192 // but don' t override existing states (lower StateSetNode always has priority)
193 if (rStateSet->hasRenderStates()) {
194 // Create global RenderStateSet for renderView if no stateSet was set before
195 RenderStateSet *stateSet = rv->getOrCreateStateSet();
196 addStatesToRenderStateSet(stateSet, stateIds: rStateSet->renderStates(), manager: manager->renderStateManager());
197 }
198 break;
199 }
200
201 case FrameGraphNode::NoDraw: {
202 rv->setNoDraw(true);
203 break;
204 }
205
206 case FrameGraphNode::FrustumCulling: {
207 rv->setFrustumCulling(true);
208 break;
209 }
210
211 case FrameGraphNode::ComputeDispatch: {
212 const Render::DispatchCompute *dispatchCompute = static_cast<const Render::DispatchCompute *>(node);
213 rv->setCompute(true);
214 rv->setComputeWorkgroups(x: dispatchCompute->x(),
215 y: dispatchCompute->y(),
216 z: dispatchCompute->z());
217 break;
218 }
219
220 case FrameGraphNode::Lighting: {
221 // TODO
222 break;
223 }
224
225 case FrameGraphNode::Surface: {
226 // Use the surface closest to leaf node
227 if (rv->surface() == nullptr) {
228 const Render::RenderSurfaceSelector *surfaceSelector
229 = static_cast<const Render::RenderSurfaceSelector *>(node);
230 rv->setSurface(surfaceSelector->surface());
231 rv->setSurfaceSize(surfaceSelector->renderTargetSize() * surfaceSelector->devicePixelRatio());
232 rv->setDevicePixelRatio(surfaceSelector->devicePixelRatio());
233 }
234 break;
235 }
236
237 case FrameGraphNode::DebugOverlay:
238 rv->setShowDebugOverlay(true);
239 break;
240
241 case FrameGraphNode::RenderCapture: {
242 auto *renderCapture = const_cast<Render::RenderCapture *>(
243 static_cast<const Render::RenderCapture *>(node));
244 if (rv->renderCaptureNodeId().isNull() && renderCapture->wasCaptureRequested()) {
245 rv->setRenderCaptureNodeId(renderCapture->peerId());
246 rv->setRenderCaptureRequest(renderCapture->takeCaptureRequest());
247 }
248 break;
249 }
250
251 case FrameGraphNode::MemoryBarrier: {
252 const Render::MemoryBarrier *barrier = static_cast<const Render::MemoryBarrier *>(node);
253 rv->setMemoryBarrier(barrier->waitOperations()|rv->memoryBarrier());
254 break;
255 }
256
257 case FrameGraphNode::BufferCapture: {
258 auto *bufferCapture = const_cast<Render::BufferCapture *>(
259 static_cast<const Render::BufferCapture *>(node));
260 if (bufferCapture != nullptr)
261 rv->setIsDownloadBuffersEnable(bufferCapture->isEnabled());
262 break;
263 }
264
265 case FrameGraphNode::BlitFramebuffer: {
266 const Render::BlitFramebuffer *blitFramebufferNode =
267 static_cast<const Render::BlitFramebuffer *>(node);
268 rv->setHasBlitFramebufferInfo(true);
269 BlitFramebufferInfo bfbInfo;
270 bfbInfo.sourceRenderTargetId = blitFramebufferNode->sourceRenderTargetId();
271 bfbInfo.destinationRenderTargetId = blitFramebufferNode->destinationRenderTargetId();
272 bfbInfo.sourceRect = blitFramebufferNode->sourceRect();
273 bfbInfo.destinationRect = blitFramebufferNode->destinationRect();
274 bfbInfo.sourceAttachmentPoint = blitFramebufferNode->sourceAttachmentPoint();
275 bfbInfo.destinationAttachmentPoint = blitFramebufferNode->destinationAttachmentPoint();
276 bfbInfo.interpolationMethod = blitFramebufferNode->interpolationMethod();
277 rv->setBlitFrameBufferInfo(bfbInfo);
278 break;
279 }
280
281 case FrameGraphNode::WaitFence: {
282 const Render::WaitFence *waitFence = static_cast<const Render::WaitFence *>(node);
283 rv->appendWaitFence(data: waitFence->data());
284 break;
285 }
286
287 case FrameGraphNode::SetFence: {
288 rv->appendInsertFenceId(setFenceId: node->peerId());
289 break;
290 }
291
292 case FrameGraphNode::NoPicking:
293 // Nothing to do RenderView wise for NoPicking
294 break;
295
296 default:
297 // Should never get here
298 qCWarning(Backend) << "Unhandled FrameGraphNode type";
299 }
300
301 node = node->parent();
302 }
303}
304
305/*!
306 \internal
307 Searches the best matching Technique from \a effect specified.
308*/
309Technique *findTechniqueForEffect(NodeManagers *manager,
310 const TechniqueFilter *techniqueFilter,
311 Effect *effect)
312{
313 if (!effect)
314 return nullptr;
315
316 QVector<Technique*> matchingTechniques;
317 const bool hasInvalidTechniqueFilter = (techniqueFilter == nullptr || techniqueFilter->filters().isEmpty());
318
319 // Iterate through the techniques in the effect
320 const auto techniqueIds = effect->techniques();
321 for (const QNodeId techniqueId : techniqueIds) {
322 Technique *technique = manager->techniqueManager()->lookupResource(id: techniqueId);
323
324 // Should be valid, if not there likely a problem with node addition/destruction changes
325 Q_ASSERT(technique);
326
327 // Check if the technique is compatible with the rendering API
328 // If no techniqueFilter is present, we return the technique as it satisfies OpenGL version
329 if (technique->isCompatibleWithRenderer() && (hasInvalidTechniqueFilter || technique->isCompatibleWithFilters(filterKeyIds: techniqueFilter->filters())))
330 matchingTechniques.append(t: technique);
331 }
332
333 if (matchingTechniques.size() == 0) // We failed to find a suitable technique to use :(
334 return nullptr;
335
336 if (matchingTechniques.size() == 1)
337 return matchingTechniques.first();
338
339 // Several compatible techniques, return technique with highest major and minor version
340 Technique* highest = matchingTechniques.first();
341 GraphicsApiFilterData filter = *highest->graphicsApiFilter();
342 for (auto it = matchingTechniques.cbegin() + 1; it < matchingTechniques.cend(); ++it) {
343 if (filter < *(*it)->graphicsApiFilter()) {
344 filter = *(*it)->graphicsApiFilter();
345 highest = *it;
346 }
347 }
348 return highest;
349}
350
351
352RenderPassList findRenderPassesForTechnique(NodeManagers *manager,
353 const RenderPassFilter *passFilter,
354 Technique *technique)
355{
356 Q_ASSERT(manager);
357 Q_ASSERT(technique);
358
359 RenderPassList passes;
360 const auto passIds = technique->renderPasses();
361 for (const QNodeId passId : passIds) {
362 RenderPass *renderPass = manager->renderPassManager()->lookupResource(id: passId);
363
364 if (renderPass && renderPass->isEnabled()) {
365 bool foundMatch = (!passFilter || passFilter->filters().size() == 0);
366
367 // A pass filter is present so we need to check for matching criteria
368 if (!foundMatch && renderPass->filterKeys().size() >= passFilter->filters().size()) {
369
370 // Iterate through the filter criteria and look for render passes with criteria that satisfy them
371 const auto filterKeyIds = passFilter->filters();
372 for (const QNodeId filterKeyId : filterKeyIds) {
373 foundMatch = false;
374 FilterKey *filterFilterKey = manager->filterKeyManager()->lookupResource(id: filterKeyId);
375
376 const auto passFilterKeyIds = renderPass->filterKeys();
377 for (const QNodeId passFilterKeyId : passFilterKeyIds) {
378 FilterKey *passFilterKey = manager->filterKeyManager()->lookupResource(id: passFilterKeyId);
379 if ((foundMatch = (*passFilterKey == *filterFilterKey)))
380 break;
381 }
382
383 if (!foundMatch) {
384 // No match for criterion in any of the render pass' criteria
385 break;
386 }
387 }
388 }
389
390 if (foundMatch) {
391 // Found a renderpass that satisfies our needs. Add it in order
392 passes << renderPass;
393 }
394 }
395 }
396
397 return passes;
398}
399
400
401ParameterInfoList::const_iterator findParamInfo(ParameterInfoList *params, const int nameId)
402{
403 const ParameterInfoList::const_iterator end = params->cend();
404 ParameterInfoList::const_iterator it = std::lower_bound(first: params->cbegin(), last: end, val: nameId);
405 if (it != end && it->nameId != nameId)
406 return end;
407 return it;
408}
409
410void addParametersForIds(ParameterInfoList *params, ParameterManager *manager,
411 const Qt3DCore::QNodeIdVector &parameterIds)
412{
413 for (const QNodeId paramId : parameterIds) {
414 const HParameter parameterHandle = manager->lookupHandle(id: paramId);
415 const Parameter *param = manager->data(handle: parameterHandle);
416 ParameterInfoList::iterator it = std::lower_bound(first: params->begin(), last: params->end(), val: param->nameId());
417 if (it == params->end() || it->nameId != param->nameId())
418 params->insert(before: it, t: ParameterInfo(param->nameId(), parameterHandle));
419 }
420}
421
422void parametersFromMaterialEffectTechnique(ParameterInfoList *infoList,
423 ParameterManager *manager,
424 Material *material,
425 Effect *effect,
426 Technique *technique)
427{
428 // The parameters are taken in the following priority order:
429 //
430 // 1) Material
431 // 2) Effect
432 // 3) Technique
433 //
434 // That way a user can override defaults in Effect's and Techniques on a
435 // object manner and a Technique can override global defaults from the Effect.
436 parametersFromParametersProvider(infoList, manager, provider: material);
437 parametersFromParametersProvider(infoList, manager, provider: effect);
438 parametersFromParametersProvider(infoList, manager, provider: technique);
439}
440
441// Only add states with types we don't already have
442void addStatesToRenderStateSet(RenderStateSet *stateSet,
443 const QVector<Qt3DCore::QNodeId> stateIds,
444 RenderStateManager *manager)
445{
446 for (const Qt3DCore::QNodeId &stateId : stateIds) {
447 RenderStateNode *node = manager->lookupResource(id: stateId);
448 if (node->isEnabled() && stateSet->canAddStateOfType(type: node->type())) {
449 stateSet->addState(state: node->impl());
450 }
451 }
452}
453
454namespace {
455
456const QString blockArray = QStringLiteral("[%1]");
457const int qNodeIdTypeId = qMetaTypeId<QNodeId>();
458
459}
460
461UniformBlockValueBuilder::UniformBlockValueBuilder()
462 : updatedPropertiesOnly(false)
463 , shaderDataManager(nullptr)
464 , textureManager(nullptr)
465{
466}
467
468UniformBlockValueBuilder::~UniformBlockValueBuilder()
469{
470}
471
472void UniformBlockValueBuilder::buildActiveUniformNameValueMapHelper(const ShaderData *currentShaderData,
473 const QString &blockName,
474 const QString &qmlPropertyName,
475 const QVariant &value)
476{
477 // In the end, values are either scalar or a scalar array
478 // Composed elements (structs, structs array) are simplified into simple scalars
479 if (value.userType() == QMetaType::QVariantList) { // Array
480 QVariantList list = value.value<QVariantList>();
481 if (list.at(i: 0).userType() == qNodeIdTypeId) { // Array of struct qmlPropertyName[i].structMember
482 for (int i = 0; i < list.size(); ++i) {
483 const QVariant variantElement = list.at(i);
484 if (list.at(i).userType() == qNodeIdTypeId) {
485 const auto nodeId = variantElement.value<QNodeId>();
486 ShaderData *subShaderData = shaderDataManager->lookupResource(id: nodeId);
487 if (subShaderData) {
488 buildActiveUniformNameValueMapStructHelper(rShaderData: subShaderData,
489 blockName: blockName + QLatin1Char('.') + qmlPropertyName + blockArray.arg(a: i),
490 qmlPropertyName: QLatin1String(""));
491 }
492 // Note: we only handle ShaderData as nested container nodes here
493 }
494 }
495 } else { // Array of scalar/vec qmlPropertyName[0]
496 QString varName;
497 varName.reserve(asize: blockName.length() + 1 + qmlPropertyName.length() + 3);
498 varName.append(s: blockName);
499 varName.append(s: QLatin1String("."));
500 varName.append(s: qmlPropertyName);
501 varName.append(s: QLatin1String("[0]"));
502 if (uniforms.contains(akey: varName)) {
503 qCDebug(Shaders) << "UBO array member " << varName << " set for update";
504 activeUniformNamesToValue.insert(akey: StringToInt::lookupId(str: varName), avalue: value);
505 }
506 }
507 } else if (value.userType() == qNodeIdTypeId) { // Struct qmlPropertyName.structMember
508 const auto nodeId = value.value<QNodeId>();
509 ShaderData *rSubShaderData = shaderDataManager->lookupResource(id: nodeId);
510 if (rSubShaderData) {
511 buildActiveUniformNameValueMapStructHelper(rShaderData: rSubShaderData,
512 blockName,
513 qmlPropertyName);
514 } else if (textureManager->contains(id: nodeId)) {
515 const auto varId = StringToInt::lookupId(str: blockName + QLatin1Char('.') + qmlPropertyName);
516 activeUniformNamesToValue.insert(akey: varId, avalue: value);
517 }
518 } else { // Scalar / Vec
519 QString varName;
520 varName.reserve(asize: blockName.length() + 1 + qmlPropertyName.length());
521 varName.append(s: blockName);
522 varName.append(s: QLatin1String("."));
523 varName.append(s: qmlPropertyName);
524 if (uniforms.contains(akey: varName)) {
525 qCDebug(Shaders) << "UBO scalar member " << varName << " set for update";
526
527 // If the property needs to be transformed, we transform it here as
528 // the shaderdata cannot hold transformed properties for multiple
529 // thread contexts at once
530 activeUniformNamesToValue.insert(akey: StringToInt::lookupId(str: varName),
531 avalue: currentShaderData->getTransformedProperty(name: qmlPropertyName, viewMatrix));
532 }
533 }
534}
535
536void UniformBlockValueBuilder::buildActiveUniformNameValueMapStructHelper(const ShaderData *rShaderData,
537 const QString &blockName,
538 const QString &qmlPropertyName)
539{
540 const QHash<QString, ShaderData::PropertyValue> &properties = rShaderData->properties();
541 auto it = properties.begin();
542 const auto end = properties.end();
543
544 while (it != end) {
545 QString fullBlockName;
546 fullBlockName.reserve(asize: blockName.length() + 1 + qmlPropertyName.length());
547 fullBlockName.append(s: blockName);
548 if (!qmlPropertyName.isEmpty()) {
549 fullBlockName.append(s: QLatin1String("."));
550 fullBlockName.append(s: qmlPropertyName);
551 }
552 buildActiveUniformNameValueMapHelper(currentShaderData: rShaderData, blockName: fullBlockName,
553 qmlPropertyName: it.key(), value: it.value().value);
554 ++it;
555 }
556}
557
558ParameterInfo::ParameterInfo(const int nameId, const HParameter &handle)
559 : nameId(nameId)
560 , handle(handle)
561{}
562
563bool ParameterInfo::operator<(const ParameterInfo &other) const Q_DECL_NOEXCEPT
564{
565 return nameId < other.nameId;
566}
567
568bool ParameterInfo::operator<(const int otherNameId) const Q_DECL_NOEXCEPT
569{
570 return nameId < otherNameId;
571}
572
573} // namespace OpenGL
574} // namespace Render
575} // namespace Qt3DRender
576
577QT_END_NAMESPACE
578

source code of qt3d/src/plugins/renderers/opengl/jobs/renderviewjobutils.cpp