Revision control
Copy as Markdown
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// Qt-Security score:significant reason:default
#include "qssgrenderuserpass_p.h"
#include <QtQuick3DRuntimeRender/private/qssgrendercommands_p.h>
#include <QtCore/qregularexpression.h>
#include <QtCore/qcryptographichash.h>
#include <ssg/qssgrendercontextcore.h>
QT_BEGIN_NAMESPACE
Q_STATIC_LOGGING_CATEGORY(lcRenderUserPass, "qt.quick3d.runtimerender.renderuserpass")
static const QHash<const char*, const char*> AugmentMacros = {
{ "BASE_COLOR", "qt_diffuseColor" },
{ "ROUGHNESS", "qt_roughnessAmount" },
{ "METALNESS", "qt_metalnessAmount" },
{ "DIFFUSE_LIGHT", "global_diffuse_light.rgb" },
{ "SPECULAR_LIGHT", "global_specular_light" },
{ "EMISSIVE_LIGHT", "global_emission" },
{ "WORLD_NORMAL", "qt_world_normal" },
{ "WORLD_TANGENT", "qt_tangent" },
{ "WORLD_BINORMAL", "qt_binormal" },
{ "F0", "qt_f0" },
{ "F90", "qt_f90" }
};
// NOTE: While this class doesn't have graphics resources itself, it manages commands that do!
QSSGRenderUserPass::QSSGRenderUserPass()
: QSSGRenderGraphObject(Type::RenderPass, FlagT(Flags::HasGraphicsResources))
{
}
QSSGRenderUserPass::~QSSGRenderUserPass()
{
resetCommands();
}
bool QSSGRenderUserPass::isDirty(DirtyFlag flag) const
{
return (m_dirtyFlags & FlagT(flag)) != 0;
}
void QSSGRenderUserPass::markDirty(DirtyFlag flag)
{
m_dirtyFlags |= FlagT(flag);
}
void QSSGRenderUserPass::clearDirty(DirtyFlag flag)
{
m_dirtyFlags &= ~FlagT(flag);
}
QByteArray replaceAugmentMacros(const QByteArray &shaderCode)
{
QString modifiedCode = QString::fromUtf8(shaderCode);
for (auto it = AugmentMacros.constBegin(); it != AugmentMacros.constEnd(); ++it) {
QString pattern = QStringLiteral("\\b%1\\b").arg(QRegularExpression::escape(QString::fromUtf8(it.key())));
QRegularExpression regex(pattern);
modifiedCode.replace(regex, QString::fromUtf8(it.value()));
}
return modifiedCode.toUtf8();
}
void QSSGRenderUserPass::finalizeShaders(const QSSGRenderContextInterface &ctx)
{
Q_UNUSED(ctx);
if (materialMode != AugmentMaterial)
return;
if (!isDirty(DirtyFlag::ShaderDirty))
return;
clearDirty(DirtyFlag::ShaderDirty);
const bool hasShaderCode = (shaderAugmentation.hasUserAugmentation());
if (hasShaderCode) {
qCDebug(lcRenderUserPass) << "Finalizing shaders for user pass with augment shader code.";
// Replace any instances of the AugmentMacros with the appropriate built-in variable names
shaderAugmentation.preamble = replaceAugmentMacros(shaderAugmentation.preamble);
shaderAugmentation.body = replaceAugmentMacros(shaderAugmentation.body);
// Used to build the unique shader cache key. The shader key goes in the generated
// shader as a one line comment so it can't be multiline. Generate hash from the
// augmented shader preamble and body instead, which gets combined with the material key.
const QByteArray fullAugmentation = shaderAugmentation.preamble + shaderAugmentation.body;
shaderAugmentation.hash = QCryptographicHash::hash(QByteArrayView(fullAugmentation),
QCryptographicHash::Algorithm::Sha1).toHex();
// Check if body needs certain features
shaderAugmentation.needsBaseColor = shaderAugmentation.body.contains(AugmentMacros["BASE_COLOR"]);
shaderAugmentation.needsRoughness = shaderAugmentation.body.contains(AugmentMacros["ROUGHNESS"]);
shaderAugmentation.needsMetalness = shaderAugmentation.body.contains(AugmentMacros["METALNESS"]);
shaderAugmentation.needsDiffuseLight = shaderAugmentation.body.contains(AugmentMacros["DIFFUSE_LIGHT"]);
shaderAugmentation.needsSpecularLight = shaderAugmentation.body.contains(AugmentMacros["SPECULAR_LIGHT"]);
shaderAugmentation.needsEmissiveLight = shaderAugmentation.body.contains(AugmentMacros["EMISSIVE_LIGHT"]);
shaderAugmentation.needsWorldNormal = shaderAugmentation.body.contains(AugmentMacros["WORLD_NORMAL"]);
shaderAugmentation.needsWorldTangent = shaderAugmentation.body.contains(AugmentMacros["WORLD_TANGENT"]);
shaderAugmentation.needsWorldBinormal = shaderAugmentation.body.contains(AugmentMacros["WORLD_BINORMAL"]);
shaderAugmentation.needsF0 = shaderAugmentation.body.contains(AugmentMacros["F0"]);
shaderAugmentation.needsF90 = shaderAugmentation.body.contains(AugmentMacros["F90"]);
m_state = State::Ready;
} else {
m_state = State::Error;
qWarning() << "No augment shader code provided for user pass.";
}
// Finalize shaders for all commands
qCDebug(lcRenderUserPass) << "Finalizing shaders for user pass commands";
}
void QSSGRenderUserPass::resetCommands()
{
qDeleteAll(commands);
commands.clear();
}
QT_END_NAMESPACE