1// Copyright (C) 2014 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 "shaderdata_p.h"
5#include "qshaderdata.h"
6#include "qshaderdata_p.h"
7#include <QMetaProperty>
8#include <QMetaObject>
9#include <private/qbackendnode_p.h>
10#include <private/managers_p.h>
11#include <private/nodemanagers_p.h>
12#include <Qt3DRender/private/stringtoint_p.h>
13
14QT_BEGIN_NAMESPACE
15
16namespace Qt3DRender {
17namespace Render {
18
19using namespace Qt3DCore;
20
21namespace {
22
23const int qNodeIdTypeId = qMetaTypeId<Qt3DCore::QNodeId>();
24
25}
26
27ShaderData::ShaderData()
28 : m_managers(nullptr)
29{
30}
31
32ShaderData::~ShaderData()
33{
34}
35
36void ShaderData::setManagers(NodeManagers *managers)
37{
38 m_managers = managers;
39}
40
41void ShaderData::syncFromFrontEnd(const QNode *frontEnd, bool firstTime)
42{
43 const QShaderData *node = qobject_cast<const QShaderData *>(object: frontEnd);
44 if (!node)
45 return;
46 BackendNode::syncFromFrontEnd(frontEnd, firstTime);
47
48 if (firstTime) {
49 m_propertyReader = node->propertyReader();
50 m_blockNameToPropertyValues.clear();
51
52 const QMetaObject *metaObj = node->metaObject();
53 const int propertyOffset = QShaderData::staticMetaObject.propertyOffset();
54 const int propertyCount = metaObj->propertyCount();
55 // Dynamic properties names
56 const auto dynamicPropertyNames = node->dynamicPropertyNames();
57
58 QList<QString> propertyNames;
59 propertyNames.reserve(asize: propertyCount - propertyOffset + dynamicPropertyNames.size());
60
61 // Statiically defined properties
62 for (int i = propertyOffset; i < propertyCount; ++i) {
63 const QMetaProperty pro = metaObj->property(index: i);
64 if (pro.isWritable())
65 propertyNames.push_back(t: QString::fromLatin1(ba: pro.name()));
66 }
67 // Dynamic properties
68 for (const QByteArray &propertyName : dynamicPropertyNames)
69 propertyNames.push_back(t: QString::fromLatin1(ba: propertyName));
70
71 for (const QString &propertyName : propertyNames) {
72 if (propertyName == QStringLiteral("data") ||
73 propertyName == QStringLiteral("objectName") ||
74 propertyName == QStringLiteral("childNodes")) // We don't handle default Node properties
75 continue;
76
77 const QVariant &propertyValue = m_propertyReader->readProperty(v: node->property(name: propertyName.toLatin1()));
78 bool isNode = false;
79 bool isTransformed = false;
80 bool isArray = false;
81
82 // We check if the property is a QNodeId
83 isNode = (propertyValue.userType() == qNodeIdTypeId);
84 // We check if QList<QNodeId>
85 if (propertyValue.userType() == QMetaType::QVariantList) {
86 isArray = true;
87 QVariantList list = propertyValue.value<QVariantList>();
88 if (list.size() > 0 && list.at(i: 0).userType() == qNodeIdTypeId)
89 isNode = true;
90 }
91
92 // We check if property is a Transformed property
93 QString transformedPropertyName;
94 if (propertyValue.userType() == QMetaType::QVector3D) {
95 // if there is a matching QShaderData::TransformType propertyTransformed
96 transformedPropertyName = propertyName + QLatin1String("Transformed");
97 isTransformed = propertyNames.contains(str: transformedPropertyName);
98 if (!isTransformed)
99 transformedPropertyName.clear();
100 }
101 m_originalProperties.insert(key: propertyName, value: { .value: propertyValue, .isNode: isNode, .isArray: isArray, .isTransformed: isTransformed, .transformedPropertyName: transformedPropertyName });
102 }
103 BackendNode::markDirty(changes: AbstractRenderer::ParameterDirty);
104 } else {
105 // Updates
106 if (!m_propertyReader.isNull()) {
107 auto it = m_originalProperties.begin();
108 const auto end = m_originalProperties.end();
109
110 while (it != end) {
111 const QVariant newValue = m_propertyReader->readProperty(v: node->property(name: it.key().toLatin1()));
112 PropertyValue &propValue = it.value();
113 if (propValue.value != newValue) {
114 // Note we aren't notified about nested QShaderData in this call
115 // only scalar / vec properties
116 propValue.value = newValue;
117 BackendNode::markDirty(changes: AbstractRenderer::ParameterDirty);
118 }
119 ++it;
120 }
121 }
122 }
123}
124
125const ShaderData::PropertyValuesForBlock &ShaderData::propertyValuesForBlock(int blockName) const
126{
127 std::shared_lock readLocker(m_lock);
128 return m_blockNameToPropertyValues.at(k: blockName);
129}
130
131void ShaderData::generatePropertyValuesForBlockIfNeeded(const QString &fullBlockName)
132{
133 const int fullBlockNameId = StringToInt::lookupId(str: fullBlockName);
134
135 std::unique_lock readWriteLocker(m_lock);
136 const bool hasPropertyValuesForBlock = m_blockNameToPropertyValues.find(x: fullBlockNameId) != m_blockNameToPropertyValues.cend();
137 if (hasPropertyValuesForBlock) {
138 return;
139 }
140
141 const QHash<QString, ShaderData::PropertyValue> &props = properties();
142
143 ShaderData::PropertyValuesForBlock valueBlock;
144 valueBlock.reserve(n: props.size());
145
146 auto it = props.cbegin();
147 const auto end = props.cend();
148 while (it != end) {
149 QString propertyName = it.key();
150 // If we are dealing with a nested value, check if it is an an array
151 if (it->isArray && !it->isNode)
152 propertyName += QLatin1String("[0]");
153
154 QString fullPropertyName;
155 fullPropertyName.reserve(asize: fullBlockName.size() + 1 + it.key().size());
156 fullPropertyName.append(s: fullBlockName);
157 fullPropertyName.append(s: QLatin1String("."));
158 fullPropertyName.append(s: propertyName);
159
160 // We only do this for properties on root level
161 valueBlock.push_back(x: { StringToInt::lookupId(str: fullPropertyName),
162 StringToInt::lookupId(str: propertyName),
163 it.operator ->() });
164 ++it;
165 }
166
167 m_blockNameToPropertyValues[StringToInt::lookupId(str: fullBlockName)] = std::move(valueBlock);
168}
169
170ShaderData *ShaderData::lookupResource(NodeManagers *managers, QNodeId id)
171{
172 return managers->shaderDataManager()->lookupResource(id);
173}
174
175ShaderData *ShaderData::lookupResource(QNodeId id)
176{
177 return ShaderData::lookupResource(managers: m_managers, id);
178}
179
180// RenderCommand updater jobs
181QVariant ShaderData::getTransformedProperty(const PropertyValue *v, const Matrix4x4 &viewMatrix) const noexcept
182{
183 // Note protecting m_worldMatrix at this point as we assume all world updates
184 // have been performed when reaching this point
185 if (v->isTransformed) {
186 const auto transformedIt = m_originalProperties.constFind(key: v->transformedPropertyName);
187 if (transformedIt != m_originalProperties.constEnd()) {
188 const PropertyValue &transformedValue = transformedIt.value();
189 const TransformType transformType = static_cast<TransformType>(transformedValue.value.toInt());
190 switch (transformType) {
191 case ModelToEye:
192 return QVariant::fromValue(value: viewMatrix.map(point: m_worldMatrix.map(point: Vector3D(v->value.value<QVector3D>()))));
193 case ModelToWorld:
194 return QVariant::fromValue(value: m_worldMatrix.map(point: Vector3D(v->value.value<QVector3D>())));
195 case ModelToWorldDirection:
196 return QVariant::fromValue(value: Vector3D(m_worldMatrix * Vector4D(v->value.value<QVector3D>(), 0.0f)));
197 case NoTransform:
198 break;
199 }
200 }
201 }
202 return v->value;
203}
204
205// Unit tests only
206ShaderData::TransformType ShaderData::propertyTransformType(const QString &name) const
207{
208 const auto it = m_originalProperties.constFind(key: name);
209 if (it != m_originalProperties.constEnd()) {
210 const PropertyValue &propertyValue = it.value();
211 if (propertyValue.isTransformed) {
212 auto transformedIt = m_originalProperties.constFind(key: name + QLatin1String("Transformed"));
213 if (transformedIt != m_originalProperties.end())
214 return static_cast<TransformType>(transformedIt.value().value.toInt());
215 }
216 }
217 return NoTransform;
218}
219
220// Called by FramePreparationJob or by RenderView when dealing with lights
221void ShaderData::updateWorldTransform(const Matrix4x4 &worldMatrix)
222{
223 if (m_worldMatrix != worldMatrix) {
224 m_worldMatrix = worldMatrix;
225 }
226}
227
228RenderShaderDataFunctor::RenderShaderDataFunctor(AbstractRenderer *renderer, NodeManagers *managers)
229 : m_managers(managers)
230 , m_renderer(renderer)
231{
232}
233
234Qt3DCore::QBackendNode *RenderShaderDataFunctor::create(Qt3DCore::QNodeId id) const
235{
236 ShaderData *backend = m_managers->shaderDataManager()->getOrCreateResource(id);
237 backend->setManagers(m_managers);
238 backend->setRenderer(m_renderer);
239 return backend;
240}
241
242Qt3DCore::QBackendNode *RenderShaderDataFunctor::get(Qt3DCore::QNodeId id) const
243{
244 return m_managers->shaderDataManager()->lookupResource(id);
245}
246
247void RenderShaderDataFunctor::destroy(Qt3DCore::QNodeId id) const
248{
249 m_managers->shaderDataManager()->releaseResource(id);
250}
251
252} // namespace Render
253} // namespace Qt3DRender
254
255QT_END_NAMESPACE
256

source code of qt3d/src/render/materialsystem/shaderdata.cpp