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 "technique_p.h"
41
42#include <Qt3DRender/qrenderpass.h>
43#include <Qt3DRender/qparameter.h>
44#include <Qt3DRender/qtechnique.h>
45#include <Qt3DRender/qgraphicsapifilter.h>
46#include <Qt3DRender/private/renderer_p.h>
47#include <Qt3DRender/private/filterkey_p.h>
48#include <Qt3DRender/private/qtechnique_p.h>
49#include <Qt3DRender/private/shader_p.h>
50#include <Qt3DCore/private/qchangearbiter_p.h>
51#include <Qt3DCore/qpropertyupdatedchange.h>
52#include <Qt3DCore/qpropertynodeaddedchange.h>
53#include <Qt3DCore/qpropertynoderemovedchange.h>
54#include <Qt3DRender/private/managers_p.h>
55#include <Qt3DRender/private/techniquemanager_p.h>
56#include <Qt3DRender/private/nodemanagers_p.h>
57
58#include <QDebug>
59
60QT_BEGIN_NAMESPACE
61
62using namespace Qt3DCore;
63
64namespace Qt3DRender {
65namespace Render {
66
67Technique::Technique()
68 : BackendNode()
69 , m_isCompatibleWithRenderer(false)
70 , m_nodeManager(nullptr)
71{
72}
73
74Technique::~Technique()
75{
76 cleanup();
77}
78
79void Technique::cleanup()
80{
81 QBackendNode::setEnabled(false);
82 m_parameterPack.clear();
83 m_renderPasses.clear();
84 m_filterKeyList.clear();
85 m_isCompatibleWithRenderer = false;
86}
87
88void Technique::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change)
89{
90 const auto typedChange = qSharedPointerCast<Qt3DCore::QNodeCreatedChange<QTechniqueData>>(change);
91 const QTechniqueData &data = typedChange->data;
92
93 m_graphicsApiFilterData = data.graphicsApiFilterData;
94 m_filterKeyList = data.filterKeyIds;
95 m_parameterPack.setParameters(data.parameterIds);
96 m_renderPasses = data.renderPassIds;
97 m_nodeManager->techniqueManager()->addDirtyTechnique(peerId());
98}
99
100void Technique::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
101{
102 switch (e->type()) {
103 case PropertyUpdated: {
104 const auto change = qSharedPointerCast<QPropertyUpdatedChange>(e);
105 if (change->propertyName() == QByteArrayLiteral("enabled")) {
106 markDirty(AbstractRenderer::TechniquesDirty);
107 } else if (change->propertyName() == QByteArrayLiteral("graphicsApiFilterData")) {
108 GraphicsApiFilterData filterData = change->value().value<GraphicsApiFilterData>();
109 m_graphicsApiFilterData = filterData;
110 // Notify the manager that our graphicsApiFilterData has changed
111 // and that we therefore need to be check for compatibility again
112 m_isCompatibleWithRenderer = false;
113 m_nodeManager->techniqueManager()->addDirtyTechnique(peerId());
114 markDirty(AbstractRenderer::TechniquesDirty);
115 }
116 break;
117 }
118
119 case PropertyValueAdded: {
120 const auto change = qSharedPointerCast<QPropertyNodeAddedChange>(e);
121 if (change->propertyName() == QByteArrayLiteral("pass")) {
122 appendRenderPass(change->addedNodeId());
123 markDirty(AbstractRenderer::TechniquesDirty);
124 } else if (change->propertyName() == QByteArrayLiteral("parameter")) {
125 m_parameterPack.appendParameter(change->addedNodeId());
126 markDirty(AbstractRenderer::TechniquesDirty);
127 } else if (change->propertyName() == QByteArrayLiteral("filterKeys")) {
128 appendFilterKey(change->addedNodeId());
129 markDirty(AbstractRenderer::TechniquesDirty);
130 }
131 break;
132 }
133
134 case PropertyValueRemoved: {
135 const auto change = qSharedPointerCast<QPropertyNodeRemovedChange>(e);
136 if (change->propertyName() == QByteArrayLiteral("pass")) {
137 removeRenderPass(change->removedNodeId());
138 markDirty(AbstractRenderer::TechniquesDirty);
139 } else if (change->propertyName() == QByteArrayLiteral("parameter")) {
140 m_parameterPack.removeParameter(change->removedNodeId());
141 markDirty(AbstractRenderer::TechniquesDirty);
142 } else if (change->propertyName() == QByteArrayLiteral("filterKeys")) {
143 removeFilterKey(change->removedNodeId());
144 markDirty(AbstractRenderer::TechniquesDirty);
145 }
146 break;
147 }
148
149 default:
150 break;
151 }
152 BackendNode::sceneChangeEvent(e);
153}
154
155QVector<Qt3DCore::QNodeId> Technique::parameters() const
156{
157 return m_parameterPack.parameters();
158}
159
160void Technique::appendRenderPass(Qt3DCore::QNodeId renderPassId)
161{
162 if (!m_renderPasses.contains(renderPassId))
163 m_renderPasses.append(renderPassId);
164}
165
166void Technique::removeRenderPass(Qt3DCore::QNodeId renderPassId)
167{
168 m_renderPasses.removeOne(renderPassId);
169}
170
171QVector<Qt3DCore::QNodeId> Technique::filterKeys() const
172{
173 return m_filterKeyList;
174}
175
176QVector<Qt3DCore::QNodeId> Technique::renderPasses() const
177{
178 return m_renderPasses;
179}
180
181const GraphicsApiFilterData *Technique::graphicsApiFilter() const
182{
183 return &m_graphicsApiFilterData;
184}
185
186bool Technique::isCompatibleWithRenderer() const
187{
188 return m_isCompatibleWithRenderer;
189}
190
191void Technique::setCompatibleWithRenderer(bool compatible)
192{
193 m_isCompatibleWithRenderer = compatible;
194}
195
196bool Technique::isCompatibleWithFilters(const QNodeIdVector &filterKeyIds)
197{
198 // There is a technique filter so we need to check for a technique with suitable criteria.
199 // Check for early bail out if the technique doesn't have sufficient number of criteria and
200 // can therefore never satisfy the filter
201 if (m_filterKeyList.size() < filterKeyIds.size())
202 return false;
203
204 // Iterate through the filter criteria and for each one search for a criteria on the
205 // technique that satisfies it
206 for (const QNodeId filterKeyId : filterKeyIds) {
207 FilterKey *filterKey = m_nodeManager->filterKeyManager()->lookupResource(filterKeyId);
208
209 bool foundMatch = false;
210
211 for (const QNodeId techniqueFilterKeyId : qAsConst(m_filterKeyList)) {
212 FilterKey *techniqueFilterKey = m_nodeManager->filterKeyManager()->lookupResource(techniqueFilterKeyId);
213 if ((foundMatch = (*techniqueFilterKey == *filterKey)))
214 break;
215 }
216
217 // No match for TechniqueFilter criterion in any of the technique's criteria.
218 // So no way this can match. Don't bother checking the rest of the criteria.
219 if (!foundMatch)
220 return false;
221 }
222 return true;
223}
224
225void Technique::setNodeManager(NodeManagers *nodeManager)
226{
227 m_nodeManager = nodeManager;
228}
229
230NodeManagers *Technique::nodeManager() const
231{
232 return m_nodeManager;
233}
234
235void Technique::appendFilterKey(Qt3DCore::QNodeId criterionId)
236{
237 if (!m_filterKeyList.contains(criterionId))
238 m_filterKeyList.append(criterionId);
239}
240
241void Technique::removeFilterKey(Qt3DCore::QNodeId criterionId)
242{
243 m_filterKeyList.removeOne(criterionId);
244}
245
246TechniqueFunctor::TechniqueFunctor(AbstractRenderer *renderer, NodeManagers *manager)
247 : m_manager(manager)
248 , m_renderer(renderer)
249{
250}
251
252QBackendNode *TechniqueFunctor::create(const QNodeCreatedChangeBasePtr &change) const
253{
254 Technique *technique = m_manager->techniqueManager()->getOrCreateResource(change->subjectId());
255 technique->setNodeManager(m_manager);
256 technique->setRenderer(m_renderer);
257 return technique;
258}
259
260QBackendNode *TechniqueFunctor::get(QNodeId id) const
261{
262 return m_manager->techniqueManager()->lookupResource(id);
263}
264
265void TechniqueFunctor::destroy(QNodeId id) const
266{
267 m_manager->techniqueManager()->releaseResource(id);
268
269}
270
271} // namespace Render
272} // namespace Qt3DRender
273
274QT_END_NAMESPACE
275