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 "shaderdata_p.h"
41#include "qshaderdata.h"
42#include "qshaderdata_p.h"
43#include <QMetaProperty>
44#include <QMetaObject>
45#include <Qt3DCore/qdynamicpropertyupdatedchange.h>
46#include <Qt3DCore/qpropertyupdatedchange.h>
47#include <private/graphicscontext_p.h>
48#include <private/qbackendnode_p.h>
49#include <private/glbuffer_p.h>
50#include <private/managers_p.h>
51#include <private/nodemanagers_p.h>
52#include <private/renderviewjobutils_p.h>
53
54QT_BEGIN_NAMESPACE
55
56using namespace Qt3DCore;
57
58namespace Qt3DRender {
59namespace Render {
60
61namespace {
62
63const int qNodeIdTypeId = qMetaTypeId<Qt3DCore::QNodeId>();
64
65}
66
67QVector<Qt3DCore::QNodeId> ShaderData::m_updatedShaderData;
68
69ShaderData::ShaderData()
70 : m_managers(nullptr)
71{
72}
73
74ShaderData::~ShaderData()
75{
76}
77
78void ShaderData::setManagers(NodeManagers *managers)
79{
80 m_managers = managers;
81}
82
83void ShaderData::initializeFromPeer(const QNodeCreatedChangeBasePtr &change)
84{
85 const auto typedChange = qSharedPointerCast<Qt3DCore::QNodeCreatedChange<QShaderDataData>>(change);
86 const QShaderDataData &data = typedChange->data;
87
88 m_propertyReader = data.propertyReader;
89
90 for (const QPair<QByteArray, QVariant> &entry : data.properties) {
91 if (entry.first == QByteArrayLiteral("data") ||
92 entry.first == QByteArrayLiteral("childNodes")) // We don't handle default Node properties
93 continue;
94 const QVariant &propertyValue = entry.second;
95 const QString propertyName = QString::fromLatin1(entry.first);
96
97 m_originalProperties.insert(propertyName, propertyValue);
98
99 // We check if the property is a QNodeId or QVector<QNodeId> so that we can
100 // check nested QShaderData for update
101 if (propertyValue.userType() == qNodeIdTypeId) {
102 m_nestedShaderDataProperties.insert(propertyName, propertyValue);
103 } else if (propertyValue.userType() == QMetaType::QVariantList) {
104 QVariantList list = propertyValue.value<QVariantList>();
105 if (list.count() > 0 && list.at(0).userType() == qNodeIdTypeId)
106 m_nestedShaderDataProperties.insert(propertyName, propertyValue);
107 }
108 }
109
110 // We look for transformed properties once the complete hash of
111 // originalProperties is available
112 QHash<QString, QVariant>::iterator it = m_originalProperties.begin();
113 const QHash<QString, QVariant>::iterator end = m_originalProperties.end();
114
115 while (it != end) {
116 if (it.value().type() == QVariant::Vector3D) {
117 // if there is a matching QShaderData::TransformType propertyTransformed
118 QVariant value = m_originalProperties.value(it.key() + QLatin1String("Transformed"));
119 // if that's the case, we apply a space transformation to the property
120 if (value.isValid() && value.type() == QVariant::Int)
121 m_transformedProperties.insert(it.key(), static_cast<TransformType>(value.toInt()));
122 }
123 ++it;
124 }
125}
126
127
128ShaderData *ShaderData::lookupResource(NodeManagers *managers, QNodeId id)
129{
130 return managers->shaderDataManager()->lookupResource(id);
131}
132
133ShaderData *ShaderData::lookupResource(QNodeId id)
134{
135 return ShaderData::lookupResource(m_managers, id);
136}
137
138// Call by cleanup job (single thread)
139void ShaderData::clearUpdatedProperties()
140{
141 // DISABLED: Is only useful when building UBO from a ShaderData, which is disable since 5.7
142 // const QHash<QString, QVariant>::const_iterator end = m_nestedShaderDataProperties.end();
143 // QHash<QString, QVariant>::const_iterator it = m_nestedShaderDataProperties.begin();
144
145 // while (it != end) {
146 // if (it.value().userType() == QMetaType::QVariantList) {
147 // const auto values = it.value().value<QVariantList>();
148 // for (const QVariant &v : values) {
149 // ShaderData *nested = lookupResource(v.value<QNodeId>());
150 // if (nested != nullptr)
151 // nested->clearUpdatedProperties();
152 // }
153 // } else {
154 // ShaderData *nested = lookupResource(it.value().value<QNodeId>());
155 // if (nested != nullptr)
156 // nested->clearUpdatedProperties();
157 // }
158 // ++it;
159 // }
160}
161
162void ShaderData::cleanup(NodeManagers *managers)
163{
164 Q_UNUSED(managers)
165 // DISABLED: Is only useful when building UBO from a ShaderData, which is disable since 5.7
166 // for (Qt3DCore::QNodeId id : qAsConst(m_updatedShaderData)) {
167 // ShaderData *shaderData = ShaderData::lookupResource(managers, id);
168 // if (shaderData)
169 // shaderData->clearUpdatedProperties();
170 // }
171 m_updatedShaderData.clear();
172}
173
174QVariant ShaderData::getTransformedProperty(const QString &name, const Matrix4x4 &viewMatrix)
175{
176 // Note protecting m_worldMatrix at this point as we assume all world updates
177 // have been performed when reaching this point
178 auto it = m_transformedProperties.find(name);
179 if (it != m_transformedProperties.end()) {
180 const TransformType transformType = it.value();
181 switch (transformType) {
182 case ModelToEye:
183 return QVariant::fromValue(viewMatrix * m_worldMatrix * Vector3D(m_originalProperties.value(name).value<QVector3D>()));
184 case ModelToWorld:
185 return QVariant::fromValue(m_worldMatrix * Vector3D(m_originalProperties.value(it.key()).value<QVector3D>()));
186 case ModelToWorldDirection:
187 return QVariant::fromValue(Vector3D(m_worldMatrix * Vector4D(m_originalProperties.value(it.key()).value<QVector3D>(), 0.0f)));
188 case NoTransform:
189 break;
190 }
191 }
192 return QVariant();
193}
194
195// Called by FramePreparationJob or by RenderView when dealing with lights
196void ShaderData::updateWorldTransform(const Matrix4x4 &worldMatrix)
197{
198 QMutexLocker lock(&m_mutex);
199 if (m_worldMatrix != worldMatrix) {
200 m_worldMatrix = worldMatrix;
201 }
202}
203
204// This will add the ShaderData to be cleared from updates at the end of the frame
205// by the cleanup job
206// Called by renderview jobs (several concurrent threads)
207void ShaderData::markDirty()
208{
209 QMutexLocker lock(&m_mutex);
210 if (!ShaderData::m_updatedShaderData.contains(peerId()))
211 ShaderData::m_updatedShaderData.append(peerId());
212}
213
214/*!
215 \internal
216 Lookup if the current ShaderData or a nested ShaderData has updated properties.
217 UpdateProperties contains either the value of the propertie of a QNodeId if it's another ShaderData.
218 Transformed properties are updated for all of ShaderData that have ones at the point.
219
220 \note This needs to be performed for every top level ShaderData every time it is used.
221 As we don't know if the transformed properties use the same viewMatrix for all RenderViews.
222 */
223
224ShaderData::TransformType ShaderData::propertyTransformType(const QString &name) const
225{
226 return m_transformedProperties.value(name, TransformType::NoTransform);
227}
228
229void ShaderData::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
230{
231 if (!m_propertyReader.isNull() && e->type() == PropertyUpdated) {
232 QString propertyName;
233 QVariant propertyValue;
234
235 if (auto propertyChange = qSharedPointerDynamicCast<QPropertyUpdatedChange>(e)) {
236 propertyName = QString::fromLatin1(propertyChange->propertyName());
237 propertyValue = m_propertyReader->readProperty(propertyChange->value());
238 } else if (auto propertyChange = qSharedPointerDynamicCast<QDynamicPropertyUpdatedChange>(e)) {
239 propertyName = QString::fromLatin1(propertyChange->propertyName());
240 propertyValue = m_propertyReader->readProperty(propertyChange->value());
241 } else {
242 Q_UNREACHABLE();
243 }
244
245 // Note we aren't notified about nested QShaderData in this call
246 // only scalar / vec properties
247 m_originalProperties.insert(propertyName, propertyValue);
248 BackendNode::markDirty(AbstractRenderer::ParameterDirty);
249 }
250
251 BackendNode::sceneChangeEvent(e);
252}
253
254RenderShaderDataFunctor::RenderShaderDataFunctor(AbstractRenderer *renderer, NodeManagers *managers)
255 : m_managers(managers)
256 , m_renderer(renderer)
257{
258}
259
260Qt3DCore::QBackendNode *RenderShaderDataFunctor::create(const Qt3DCore::QNodeCreatedChangeBasePtr &change) const
261{
262 ShaderData *backend = m_managers->shaderDataManager()->getOrCreateResource(change->subjectId());
263 backend->setManagers(m_managers);
264 backend->setRenderer(m_renderer);
265 return backend;
266}
267
268Qt3DCore::QBackendNode *RenderShaderDataFunctor::get(Qt3DCore::QNodeId id) const
269{
270 return m_managers->shaderDataManager()->lookupResource(id);
271}
272
273void RenderShaderDataFunctor::destroy(Qt3DCore::QNodeId id) const
274{
275 m_managers->shaderDataManager()->releaseResource(id);
276}
277
278} // namespace Render
279} // namespace Qt3DRender
280
281QT_END_NAMESPACE
282