| /**************************************************************************** |
| ** |
| ** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the Qt3D module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "renderviewjobutils_p.h" |
| #include <Qt3DRender/private/renderlogging_p.h> |
| |
| #include <Qt3DRender/qgraphicsapifilter.h> |
| #include <Qt3DRender/private/sphere_p.h> |
| #include <Qt3DRender/qshaderdata.h> |
| |
| #include <Qt3DRender/private/cameraselectornode_p.h> |
| #include <Qt3DRender/private/clearbuffers_p.h> |
| #include <Qt3DRender/private/layerfilternode_p.h> |
| #include <Qt3DRender/private/nodemanagers_p.h> |
| #include <Qt3DRender/private/effect_p.h> |
| #include <Qt3DRender/private/renderpassfilternode_p.h> |
| #include <Qt3DRender/private/rendertargetselectornode_p.h> |
| #include <Qt3DRender/private/sortpolicy_p.h> |
| #include <Qt3DRender/private/techniquefilternode_p.h> |
| #include <Qt3DRender/private/viewportnode_p.h> |
| #include <Qt3DRender/private/managers_p.h> |
| #include <Qt3DRender/private/shaderdata_p.h> |
| #include <Qt3DRender/private/statesetnode_p.h> |
| #include <Qt3DRender/private/dispatchcompute_p.h> |
| #include <Qt3DRender/private/rendersurfaceselector_p.h> |
| #include <Qt3DRender/private/rendercapture_p.h> |
| #include <Qt3DRender/private/buffercapture_p.h> |
| #include <Qt3DRender/private/stringtoint_p.h> |
| #include <Qt3DRender/private/techniquemanager_p.h> |
| #include <Qt3DRender/private/memorybarrier_p.h> |
| #include <Qt3DRender/private/blitframebuffer_p.h> |
| #include <Qt3DRender/private/waitfence_p.h> |
| #include <Qt3DRender/private/renderstateset_p.h> |
| #include <renderview_p.h> |
| #include <shadervariables_p.h> |
| |
| QT_BEGIN_NAMESPACE |
| |
| using namespace Qt3DCore; |
| |
| namespace Qt3DRender { |
| namespace Render { |
| namespace OpenGL { |
| |
| /*! |
| \internal |
| Walks up the framegraph tree from \a fgLeaf and builds up as much state |
| as possible and populates \a rv. For cases where we can't get the specific state |
| (e.g. because it depends upon more than just the framegraph) we store the data from |
| the framegraph that will be needed to later when the rest of the data becomes available |
| */ |
| void setRenderViewConfigFromFrameGraphLeafNode(RenderView *rv, const FrameGraphNode *fgLeaf) |
| { |
| // The specific RenderPass to be used is also dependent upon the Effect and TechniqueFilter |
| // which is referenced by the Material which is referenced by the RenderMesh. So we can |
| // only store the filter info in the RenderView structure and use it to do the resolving |
| // when we build the RenderCommand list. |
| const NodeManagers *manager = rv->nodeManagers(); |
| const FrameGraphNode *node = fgLeaf; |
| |
| while (node) { |
| FrameGraphNode::FrameGraphNodeType type = node->nodeType(); |
| if (node->isEnabled()) |
| switch (type) { |
| case FrameGraphNode::InvalidNodeType: |
| // A base FrameGraphNode, can be used for grouping purposes |
| break; |
| case FrameGraphNode::CameraSelector: |
| // Can be set only once and we take camera nearest to the leaf node |
| if (!rv->renderCameraLens()) { |
| const CameraSelector *cameraSelector = static_cast<const CameraSelector *>(node); |
| Entity *camNode = manager->renderNodesManager()->lookupResource(cameraSelector->cameraUuid()); |
| if (camNode) { |
| CameraLens *lens = camNode->renderComponent<CameraLens>(); |
| rv->setRenderCameraEntity(camNode); |
| if (lens && lens->isEnabled()) { |
| rv->setRenderCameraLens(lens); |
| // ViewMatrix and ProjectionMatrix are computed |
| // later in updateMatrices() |
| // since at this point the transformation matrices |
| // may not yet have been updated |
| } |
| } |
| } |
| break; |
| |
| case FrameGraphNode::LayerFilter: // Can be set multiple times in the tree |
| rv->appendLayerFilter(static_cast<const LayerFilterNode *>(node)->peerId()); |
| break; |
| |
| case FrameGraphNode::ProximityFilter: // Can be set multiple times in the tree |
| rv->appendProximityFilterId(node->peerId()); |
| break; |
| |
| case FrameGraphNode::RenderPassFilter: |
| // Can be set once |
| // TODO: Amalgamate all render pass filters from leaf to root |
| if (!rv->renderPassFilter()) |
| rv->setRenderPassFilter(static_cast<const RenderPassFilter *>(node)); |
| break; |
| |
| case FrameGraphNode::RenderTarget: { |
| // Can be set once and we take render target nearest to the leaf node |
| const RenderTargetSelector *targetSelector = static_cast<const RenderTargetSelector *>(node); |
| QNodeId renderTargetUid = targetSelector->renderTargetUuid(); |
| HTarget renderTargetHandle = manager->renderTargetManager()->lookupHandle(renderTargetUid); |
| |
| // Add renderTarget Handle and build renderCommand AttachmentPack |
| if (!rv->renderTargetId()) { |
| rv->setRenderTargetId(renderTargetUid); |
| |
| RenderTarget *renderTarget = manager->renderTargetManager()->data(renderTargetHandle); |
| if (renderTarget) |
| rv->setAttachmentPack(AttachmentPack(renderTarget, manager->attachmentManager(), targetSelector->outputs())); |
| } |
| break; |
| } |
| |
| case FrameGraphNode::ClearBuffers: { |
| const ClearBuffers *cbNode = static_cast<const ClearBuffers *>(node); |
| rv->addClearBuffers(cbNode); |
| break; |
| } |
| |
| case FrameGraphNode::TechniqueFilter: |
| // Can be set once |
| // TODO Amalgamate all technique filters from leaf to root |
| if (!rv->techniqueFilter()) |
| rv->setTechniqueFilter(static_cast<const TechniqueFilter *>(node)); |
| break; |
| |
| case FrameGraphNode::Viewport: { |
| // If the Viewport has already been set in a lower node |
| // Make it so that the new viewport is actually |
| // a subregion relative to that of the parent viewport |
| const ViewportNode *vpNode = static_cast<const ViewportNode *>(node); |
| rv->setViewport(ViewportNode::computeViewport(rv->viewport(), vpNode)); |
| rv->setGamma(vpNode->gamma()); |
| break; |
| } |
| |
| case FrameGraphNode::SortMethod: { |
| const Render::SortPolicy *sortPolicy = static_cast<const Render::SortPolicy *>(node); |
| rv->addSortType(sortPolicy->sortTypes()); |
| break; |
| } |
| |
| case FrameGraphNode::SubtreeEnabler: |
| // Has no meaning here. SubtreeEnabler was used |
| // in a prior step to filter the list of RenderViewJobs |
| break; |
| |
| case FrameGraphNode::StateSet: { |
| const Render::StateSetNode *rStateSet = static_cast<const Render::StateSetNode *>(node); |
| // Add states from new stateSet we might be missing |
| // but don' t override existing states (lower StateSetNode always has priority) |
| if (rStateSet->hasRenderStates()) { |
| // Create global RenderStateSet for renderView if no stateSet was set before |
| RenderStateSet *stateSet = rv->getOrCreateStateSet(); |
| addStatesToRenderStateSet(stateSet, rStateSet->renderStates(), manager->renderStateManager()); |
| } |
| break; |
| } |
| |
| case FrameGraphNode::NoDraw: { |
| rv->setNoDraw(true); |
| break; |
| } |
| |
| case FrameGraphNode::FrustumCulling: { |
| rv->setFrustumCulling(true); |
| break; |
| } |
| |
| case FrameGraphNode::ComputeDispatch: { |
| const Render::DispatchCompute *dispatchCompute = static_cast<const Render::DispatchCompute *>(node); |
| rv->setCompute(true); |
| rv->setComputeWorkgroups(dispatchCompute->x(), |
| dispatchCompute->y(), |
| dispatchCompute->z()); |
| break; |
| } |
| |
| case FrameGraphNode::Lighting: { |
| // TODO |
| break; |
| } |
| |
| case FrameGraphNode::Surface: { |
| // Use the surface closest to leaf node |
| if (rv->surface() == nullptr) { |
| const Render::RenderSurfaceSelector *surfaceSelector |
| = static_cast<const Render::RenderSurfaceSelector *>(node); |
| rv->setSurface(surfaceSelector->surface()); |
| rv->setSurfaceSize(surfaceSelector->renderTargetSize() * surfaceSelector->devicePixelRatio()); |
| rv->setDevicePixelRatio(surfaceSelector->devicePixelRatio()); |
| } |
| break; |
| } |
| |
| case FrameGraphNode::DebugOverlay: |
| rv->setShowDebugOverlay(true); |
| break; |
| |
| case FrameGraphNode::RenderCapture: { |
| auto *renderCapture = const_cast<Render::RenderCapture *>( |
| static_cast<const Render::RenderCapture *>(node)); |
| if (rv->renderCaptureNodeId().isNull() && renderCapture->wasCaptureRequested()) { |
| rv->setRenderCaptureNodeId(renderCapture->peerId()); |
| rv->setRenderCaptureRequest(renderCapture->takeCaptureRequest()); |
| } |
| break; |
| } |
| |
| case FrameGraphNode::MemoryBarrier: { |
| const Render::MemoryBarrier *barrier = static_cast<const Render::MemoryBarrier *>(node); |
| rv->setMemoryBarrier(barrier->waitOperations()|rv->memoryBarrier()); |
| break; |
| } |
| |
| case FrameGraphNode::BufferCapture: { |
| auto *bufferCapture = const_cast<Render::BufferCapture *>( |
| static_cast<const Render::BufferCapture *>(node)); |
| if (bufferCapture != nullptr) |
| rv->setIsDownloadBuffersEnable(bufferCapture->isEnabled()); |
| break; |
| } |
| |
| case FrameGraphNode::BlitFramebuffer: { |
| const Render::BlitFramebuffer *blitFramebufferNode = |
| static_cast<const Render::BlitFramebuffer *>(node); |
| rv->setHasBlitFramebufferInfo(true); |
| BlitFramebufferInfo bfbInfo; |
| bfbInfo.sourceRenderTargetId = blitFramebufferNode->sourceRenderTargetId(); |
| bfbInfo.destinationRenderTargetId = blitFramebufferNode->destinationRenderTargetId(); |
| bfbInfo.sourceRect = blitFramebufferNode->sourceRect(); |
| bfbInfo.destinationRect = blitFramebufferNode->destinationRect(); |
| bfbInfo.sourceAttachmentPoint = blitFramebufferNode->sourceAttachmentPoint(); |
| bfbInfo.destinationAttachmentPoint = blitFramebufferNode->destinationAttachmentPoint(); |
| bfbInfo.interpolationMethod = blitFramebufferNode->interpolationMethod(); |
| rv->setBlitFrameBufferInfo(bfbInfo); |
| break; |
| } |
| |
| case FrameGraphNode::WaitFence: { |
| const Render::WaitFence *waitFence = static_cast<const Render::WaitFence *>(node); |
| rv->appendWaitFence(waitFence->data()); |
| break; |
| } |
| |
| case FrameGraphNode::SetFence: { |
| rv->appendInsertFenceId(node->peerId()); |
| break; |
| } |
| |
| case FrameGraphNode::NoPicking: |
| // Nothing to do RenderView wise for NoPicking |
| break; |
| |
| default: |
| // Should never get here |
| qCWarning(Backend) << "Unhandled FrameGraphNode type"; |
| } |
| |
| node = node->parent(); |
| } |
| } |
| |
| /*! |
| \internal |
| Searches the best matching Technique from \a effect specified. |
| */ |
| Technique *findTechniqueForEffect(NodeManagers *manager, |
| const TechniqueFilter *techniqueFilter, |
| Effect *effect) |
| { |
| if (!effect) |
| return nullptr; |
| |
| QVector<Technique*> matchingTechniques; |
| const bool hasInvalidTechniqueFilter = (techniqueFilter == nullptr || techniqueFilter->filters().isEmpty()); |
| |
| // Iterate through the techniques in the effect |
| const auto techniqueIds = effect->techniques(); |
| for (const QNodeId techniqueId : techniqueIds) { |
| Technique *technique = manager->techniqueManager()->lookupResource(techniqueId); |
| |
| // Should be valid, if not there likely a problem with node addition/destruction changes |
| Q_ASSERT(technique); |
| |
| // Check if the technique is compatible with the rendering API |
| // If no techniqueFilter is present, we return the technique as it satisfies OpenGL version |
| if (technique->isCompatibleWithRenderer() && (hasInvalidTechniqueFilter || technique->isCompatibleWithFilters(techniqueFilter->filters()))) |
| matchingTechniques.append(technique); |
| } |
| |
| if (matchingTechniques.size() == 0) // We failed to find a suitable technique to use :( |
| return nullptr; |
| |
| if (matchingTechniques.size() == 1) |
| return matchingTechniques.first(); |
| |
| // Several compatible techniques, return technique with highest major and minor version |
| Technique* highest = matchingTechniques.first(); |
| GraphicsApiFilterData filter = *highest->graphicsApiFilter(); |
| for (auto it = matchingTechniques.cbegin() + 1; it < matchingTechniques.cend(); ++it) { |
| if (filter < *(*it)->graphicsApiFilter()) { |
| filter = *(*it)->graphicsApiFilter(); |
| highest = *it; |
| } |
| } |
| return highest; |
| } |
| |
| |
| RenderPassList findRenderPassesForTechnique(NodeManagers *manager, |
| const RenderPassFilter *passFilter, |
| Technique *technique) |
| { |
| Q_ASSERT(manager); |
| Q_ASSERT(technique); |
| |
| RenderPassList passes; |
| const auto passIds = technique->renderPasses(); |
| for (const QNodeId passId : passIds) { |
| RenderPass *renderPass = manager->renderPassManager()->lookupResource(passId); |
| |
| if (renderPass && renderPass->isEnabled()) { |
| bool foundMatch = (!passFilter || passFilter->filters().size() == 0); |
| |
| // A pass filter is present so we need to check for matching criteria |
| if (!foundMatch && renderPass->filterKeys().size() >= passFilter->filters().size()) { |
| |
| // Iterate through the filter criteria and look for render passes with criteria that satisfy them |
| const auto filterKeyIds = passFilter->filters(); |
| for (const QNodeId filterKeyId : filterKeyIds) { |
| foundMatch = false; |
| FilterKey *filterFilterKey = manager->filterKeyManager()->lookupResource(filterKeyId); |
| |
| const auto passFilterKeyIds = renderPass->filterKeys(); |
| for (const QNodeId passFilterKeyId : passFilterKeyIds) { |
| FilterKey *passFilterKey = manager->filterKeyManager()->lookupResource(passFilterKeyId); |
| if ((foundMatch = (*passFilterKey == *filterFilterKey))) |
| break; |
| } |
| |
| if (!foundMatch) { |
| // No match for criterion in any of the render pass' criteria |
| break; |
| } |
| } |
| } |
| |
| if (foundMatch) { |
| // Found a renderpass that satisfies our needs. Add it in order |
| passes << renderPass; |
| } |
| } |
| } |
| |
| return passes; |
| } |
| |
| |
| ParameterInfoList::const_iterator findParamInfo(ParameterInfoList *params, const int nameId) |
| { |
| const ParameterInfoList::const_iterator end = params->cend(); |
| ParameterInfoList::const_iterator it = std::lower_bound(params->cbegin(), end, nameId); |
| if (it != end && it->nameId != nameId) |
| return end; |
| return it; |
| } |
| |
| void addParametersForIds(ParameterInfoList *params, ParameterManager *manager, |
| const Qt3DCore::QNodeIdVector ¶meterIds) |
| { |
| for (const QNodeId paramId : parameterIds) { |
| const HParameter parameterHandle = manager->lookupHandle(paramId); |
| const Parameter *param = manager->data(parameterHandle); |
| ParameterInfoList::iterator it = std::lower_bound(params->begin(), params->end(), param->nameId()); |
| if (it == params->end() || it->nameId != param->nameId()) |
| params->insert(it, ParameterInfo(param->nameId(), parameterHandle)); |
| } |
| } |
| |
| void parametersFromMaterialEffectTechnique(ParameterInfoList *infoList, |
| ParameterManager *manager, |
| Material *material, |
| Effect *effect, |
| Technique *technique) |
| { |
| // The parameters are taken in the following priority order: |
| // |
| // 1) Material |
| // 2) Effect |
| // 3) Technique |
| // |
| // That way a user can override defaults in Effect's and Techniques on a |
| // object manner and a Technique can override global defaults from the Effect. |
| parametersFromParametersProvider(infoList, manager, material); |
| parametersFromParametersProvider(infoList, manager, effect); |
| parametersFromParametersProvider(infoList, manager, technique); |
| } |
| |
| // Only add states with types we don't already have |
| void addStatesToRenderStateSet(RenderStateSet *stateSet, |
| const QVector<Qt3DCore::QNodeId> stateIds, |
| RenderStateManager *manager) |
| { |
| for (const Qt3DCore::QNodeId &stateId : stateIds) { |
| RenderStateNode *node = manager->lookupResource(stateId); |
| if (node->isEnabled() && stateSet->canAddStateOfType(node->type())) { |
| stateSet->addState(node->impl()); |
| } |
| } |
| } |
| |
| namespace { |
| |
| const QString blockArray = QStringLiteral("[%1]"); |
| const int qNodeIdTypeId = qMetaTypeId<QNodeId>(); |
| |
| } |
| |
| UniformBlockValueBuilder::UniformBlockValueBuilder() |
| : updatedPropertiesOnly(false) |
| , shaderDataManager(nullptr) |
| , textureManager(nullptr) |
| { |
| } |
| |
| UniformBlockValueBuilder::~UniformBlockValueBuilder() |
| { |
| } |
| |
| void UniformBlockValueBuilder::buildActiveUniformNameValueMapHelper(const ShaderData *currentShaderData, |
| const QString &blockName, |
| const QString &qmlPropertyName, |
| const QVariant &value) |
| { |
| // In the end, values are either scalar or a scalar array |
| // Composed elements (structs, structs array) are simplified into simple scalars |
| if (value.userType() == QMetaType::QVariantList) { // Array |
| QVariantList list = value.value<QVariantList>(); |
| if (list.at(0).userType() == qNodeIdTypeId) { // Array of struct qmlPropertyName[i].structMember |
| for (int i = 0; i < list.size(); ++i) { |
| const QVariant variantElement = list.at(i); |
| if (list.at(i).userType() == qNodeIdTypeId) { |
| const auto nodeId = variantElement.value<QNodeId>(); |
| ShaderData *subShaderData = shaderDataManager->lookupResource(nodeId); |
| if (subShaderData) { |
| buildActiveUniformNameValueMapStructHelper(subShaderData, |
| blockName + QLatin1Char('.') + qmlPropertyName + blockArray.arg(i), |
| QLatin1String("")); |
| } |
| // Note: we only handle ShaderData as nested container nodes here |
| } |
| } |
| } else { // Array of scalar/vec qmlPropertyName[0] |
| QString varName; |
| varName.reserve(blockName.length() + 1 + qmlPropertyName.length() + 3); |
| varName.append(blockName); |
| varName.append(QLatin1String(".")); |
| varName.append(qmlPropertyName); |
| varName.append(QLatin1String("[0]")); |
| if (uniforms.contains(varName)) { |
| qCDebug(Shaders) << "UBO array member " << varName << " set for update"; |
| activeUniformNamesToValue.insert(StringToInt::lookupId(varName), value); |
| } |
| } |
| } else if (value.userType() == qNodeIdTypeId) { // Struct qmlPropertyName.structMember |
| const auto nodeId = value.value<QNodeId>(); |
| ShaderData *rSubShaderData = shaderDataManager->lookupResource(nodeId); |
| if (rSubShaderData) { |
| buildActiveUniformNameValueMapStructHelper(rSubShaderData, |
| blockName, |
| qmlPropertyName); |
| } else if (textureManager->contains(nodeId)) { |
| const auto varId = StringToInt::lookupId(blockName + QLatin1Char('.') + qmlPropertyName); |
| activeUniformNamesToValue.insert(varId, value); |
| } |
| } else { // Scalar / Vec |
| QString varName; |
| varName.reserve(blockName.length() + 1 + qmlPropertyName.length()); |
| varName.append(blockName); |
| varName.append(QLatin1String(".")); |
| varName.append(qmlPropertyName); |
| if (uniforms.contains(varName)) { |
| qCDebug(Shaders) << "UBO scalar member " << varName << " set for update"; |
| |
| // If the property needs to be transformed, we transform it here as |
| // the shaderdata cannot hold transformed properties for multiple |
| // thread contexts at once |
| activeUniformNamesToValue.insert(StringToInt::lookupId(varName), |
| currentShaderData->getTransformedProperty(qmlPropertyName, viewMatrix)); |
| } |
| } |
| } |
| |
| void UniformBlockValueBuilder::buildActiveUniformNameValueMapStructHelper(const ShaderData *rShaderData, |
| const QString &blockName, |
| const QString &qmlPropertyName) |
| { |
| const QHash<QString, ShaderData::PropertyValue> &properties = rShaderData->properties(); |
| auto it = properties.begin(); |
| const auto end = properties.end(); |
| |
| while (it != end) { |
| QString fullBlockName; |
| fullBlockName.reserve(blockName.length() + 1 + qmlPropertyName.length()); |
| fullBlockName.append(blockName); |
| if (!qmlPropertyName.isEmpty()) { |
| fullBlockName.append(QLatin1String(".")); |
| fullBlockName.append(qmlPropertyName); |
| } |
| buildActiveUniformNameValueMapHelper(rShaderData, fullBlockName, |
| it.key(), it.value().value); |
| ++it; |
| } |
| } |
| |
| ParameterInfo::ParameterInfo(const int nameId, const HParameter &handle) |
| : nameId(nameId) |
| , handle(handle) |
| {} |
| |
| bool ParameterInfo::operator<(const ParameterInfo &other) const Q_DECL_NOEXCEPT |
| { |
| return nameId < other.nameId; |
| } |
| |
| bool ParameterInfo::operator<(const int otherNameId) const Q_DECL_NOEXCEPT |
| { |
| return nameId < otherNameId; |
| } |
| |
| } // namespace OpenGL |
| } // namespace Render |
| } // namespace Qt3DRender |
| |
| QT_END_NAMESPACE |