Revision control
++ // Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++
++ #include "shaderdata_p.h"
++ #include "qshaderdata.h"
++ #include "qshaderdata_p.h"
++ #include <QMetaProperty>
++ #include <QMetaObject>
++ #include <private/qbackendnode_p.h>
++ #include <private/managers_p.h>
++ #include <private/nodemanagers_p.h>
++ #include <Qt3DRender/private/stringtoint_p.h>
++
++ QT_BEGIN_NAMESPACE
++
++ namespace Qt3DRender {
++ namespace Render {
++
++ using namespace Qt3DCore;
++
++ namespace {
++
++ const int qNodeIdTypeId = qMetaTypeId<Qt3DCore::QNodeId>();
++
++ }
++
++ ShaderData::ShaderData()
++ : m_managers(nullptr)
++ {
++ }
++
++ ShaderData::~ShaderData()
++ {
++ }
++
++ void ShaderData::setManagers(NodeManagers *managers)
++ {
++ m_managers = managers;
++ }
++
++ void ShaderData::syncFromFrontEnd(const QNode *frontEnd, bool firstTime)
++ {
++ const QShaderData *node = qobject_cast<const QShaderData *>(frontEnd);
++ if (!node)
++ return;
++ BackendNode::syncFromFrontEnd(frontEnd, firstTime);
++
++ if (firstTime) {
++ m_propertyReader = node->propertyReader();
++ m_blockNameToPropertyValues.clear();
++
++ const QMetaObject *metaObj = node->metaObject();
++ const int propertyOffset = QShaderData::staticMetaObject.propertyOffset();
++ const int propertyCount = metaObj->propertyCount();
++ // Dynamic properties names
++ const auto dynamicPropertyNames = node->dynamicPropertyNames();
++
++ QList<QString> propertyNames;
++ propertyNames.reserve(propertyCount - propertyOffset + dynamicPropertyNames.size());
++
++ // Statiically defined properties
++ for (int i = propertyOffset; i < propertyCount; ++i) {
++ const QMetaProperty pro = metaObj->property(i);
++ if (pro.isWritable())
++ propertyNames.push_back(QString::fromLatin1(pro.name()));
++ }
++ // Dynamic properties
++ for (const QByteArray &propertyName : dynamicPropertyNames)
++ propertyNames.push_back(QString::fromLatin1(propertyName));
++
++ for (const QString &propertyName : propertyNames) {
++ if (propertyName == QStringLiteral("data") ||
++ propertyName == QStringLiteral("objectName") ||
++ propertyName == QStringLiteral("childNodes")) // We don't handle default Node properties
++ continue;
++
++ const QVariant &propertyValue = m_propertyReader->readProperty(node->property(propertyName.toLatin1()));
++ bool isNode = false;
++ bool isTransformed = false;
++ bool isArray = false;
++
++ // We check if the property is a QNodeId
++ isNode = (propertyValue.userType() == qNodeIdTypeId);
++ // We check if QList<QNodeId>
++ if (propertyValue.userType() == QMetaType::QVariantList) {
++ isArray = true;
++ QVariantList list = propertyValue.value<QVariantList>();
++ if (list.size() > 0 && list.at(0).userType() == qNodeIdTypeId)
++ isNode = true;
++ }
++
++ // We check if property is a Transformed property
++ QString transformedPropertyName;
++ if (propertyValue.userType() == QMetaType::QVector3D) {
++ // if there is a matching QShaderData::TransformType propertyTransformed
++ transformedPropertyName = propertyName + QLatin1String("Transformed");
++ isTransformed = propertyNames.contains(transformedPropertyName);
++ if (!isTransformed)
++ transformedPropertyName.clear();
++ }
++ m_originalProperties.insert(propertyName, { propertyValue, isNode, isArray, isTransformed, transformedPropertyName });
++ }
++ BackendNode::markDirty(AbstractRenderer::ParameterDirty);
++ } else {
++ // Updates
++ if (!m_propertyReader.isNull()) {
++ auto it = m_originalProperties.begin();
++ const auto end = m_originalProperties.end();
++
++ while (it != end) {
++ const QVariant newValue = m_propertyReader->readProperty(node->property(it.key().toLatin1()));
++ PropertyValue &propValue = it.value();
++ if (propValue.value != newValue) {
++ // Note we aren't notified about nested QShaderData in this call
++ // only scalar / vec properties
++ propValue.value = newValue;
++ BackendNode::markDirty(AbstractRenderer::ParameterDirty);
++ }
++ ++it;
++ }
++ }
++ }
++ }
++
++ const ShaderData::PropertyValuesForBlock &ShaderData::propertyValuesForBlock(int blockName) const
++ {
++ std::shared_lock readLocker(m_lock);
++ return m_blockNameToPropertyValues.at(blockName);
++ }
++
++ void ShaderData::generatePropertyValuesForBlockIfNeeded(const QString &fullBlockName)
++ {
++ const int fullBlockNameId = StringToInt::lookupId(fullBlockName);
++
++ std::unique_lock readWriteLocker(m_lock);
++ const bool hasPropertyValuesForBlock = m_blockNameToPropertyValues.find(fullBlockNameId) != m_blockNameToPropertyValues.cend();
++ if (hasPropertyValuesForBlock) {
++ return;
++ }
++
++ const QHash<QString, ShaderData::PropertyValue> &props = properties();
++
++ ShaderData::PropertyValuesForBlock valueBlock;
++ valueBlock.reserve(props.size());
++
++ auto it = props.cbegin();
++ const auto end = props.cend();
++ while (it != end) {
++ QString propertyName = it.key();
++ // If we are dealing with a nested value, check if it is an an array
++ if (it->isArray && !it->isNode)
++ propertyName += QLatin1String("[0]");
++
++ QString fullPropertyName;
++ fullPropertyName.reserve(fullBlockName.size() + 1 + it.key().size());
++ fullPropertyName.append(fullBlockName);
++ fullPropertyName.append(QLatin1String("."));
++ fullPropertyName.append(propertyName);
++
++ // We only do this for properties on root level
++ valueBlock.push_back({ StringToInt::lookupId(fullPropertyName),
++ StringToInt::lookupId(propertyName),
++ it.operator ->() });
++ ++it;
++ }
++
++ m_blockNameToPropertyValues[StringToInt::lookupId(fullBlockName)] = std::move(valueBlock);
++ }
++
++ ShaderData *ShaderData::lookupResource(NodeManagers *managers, QNodeId id)
++ {
++ return managers->shaderDataManager()->lookupResource(id);
++ }
++
++ ShaderData *ShaderData::lookupResource(QNodeId id)
++ {
++ return ShaderData::lookupResource(m_managers, id);
++ }
++
++ // RenderCommand updater jobs
++ QVariant ShaderData::getTransformedProperty(const PropertyValue *v, const Matrix4x4 &viewMatrix) const noexcept
++ {
++ // Note protecting m_worldMatrix at this point as we assume all world updates
++ // have been performed when reaching this point
++ if (v->isTransformed) {
++ const auto transformedIt = m_originalProperties.constFind(v->transformedPropertyName);
++ if (transformedIt != m_originalProperties.constEnd()) {
++ const PropertyValue &transformedValue = transformedIt.value();
++ const TransformType transformType = static_cast<TransformType>(transformedValue.value.toInt());
++ switch (transformType) {
++ case ModelToEye:
++ return QVariant::fromValue(viewMatrix.map(m_worldMatrix.map(Vector3D(v->value.value<QVector3D>()))));
++ case ModelToWorld:
++ return QVariant::fromValue(m_worldMatrix.map(Vector3D(v->value.value<QVector3D>())));
++ case ModelToWorldDirection:
++ return QVariant::fromValue(Vector3D(m_worldMatrix * Vector4D(v->value.value<QVector3D>(), 0.0f)));
++ case NoTransform:
++ break;
++ }
++ }
++ }
++ return v->value;
++ }
++
++ // Unit tests only
++ ShaderData::TransformType ShaderData::propertyTransformType(const QString &name) const
++ {
++ const auto it = m_originalProperties.constFind(name);
++ if (it != m_originalProperties.constEnd()) {
++ const PropertyValue &propertyValue = it.value();
++ if (propertyValue.isTransformed) {
++ auto transformedIt = m_originalProperties.constFind(name + QLatin1String("Transformed"));
++ if (transformedIt != m_originalProperties.end())
++ return static_cast<TransformType>(transformedIt.value().value.toInt());
++ }
++ }
++ return NoTransform;
++ }
++
++ // Called by FramePreparationJob or by RenderView when dealing with lights
++ void ShaderData::updateWorldTransform(const Matrix4x4 &worldMatrix)
++ {
++ if (m_worldMatrix != worldMatrix) {
++ m_worldMatrix = worldMatrix;
++ }
++ }
++
++ RenderShaderDataFunctor::RenderShaderDataFunctor(AbstractRenderer *renderer, NodeManagers *managers)
++ : m_managers(managers)
++ , m_renderer(renderer)
++ {
++ }
++
++ Qt3DCore::QBackendNode *RenderShaderDataFunctor::create(Qt3DCore::QNodeId id) const
++ {
++ ShaderData *backend = m_managers->shaderDataManager()->getOrCreateResource(id);
++ backend->setManagers(m_managers);
++ backend->setRenderer(m_renderer);
++ return backend;
++ }
++
++ Qt3DCore::QBackendNode *RenderShaderDataFunctor::get(Qt3DCore::QNodeId id) const
++ {
++ return m_managers->shaderDataManager()->lookupResource(id);
++ }
++
++ void RenderShaderDataFunctor::destroy(Qt3DCore::QNodeId id) const
++ {
++ m_managers->shaderDataManager()->releaseResource(id);
++ }
++
++ } // namespace Render
++ } // namespace Qt3DRender
++
++ QT_END_NAMESPACE