Revision control
Copy as Markdown
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// Qt-Security score:significant reason:default
#include <QtQuick3DRuntimeRender/private/qssgrenderer_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrendereffect_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrenderlayer_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrendercommands_p.h>
#include "../qssgrendercontextcore.h"
#include "../rendererimpl/qssglayerrenderdata_p.h"
#include <QtGui/QVector2D>
#include <QtGui/QVector3D>
QT_BEGIN_NAMESPACE
QSSGRenderEffect::QSSGRenderEffect() : QSSGRenderGraphObject(Type::Effect) {}
QSSGRenderEffect::~QSSGRenderEffect()
{
resetCommands();
}
void QSSGRenderEffect::setFlag(QSSGRenderEffect::Flags flag, bool enabled)
{
if (enabled)
flags |= FlagT(flag);
else
flags &= ~FlagT(flag);
}
// Suffix snippets added to the end of the shader strings. These are appended
// after processing so it must be valid GLSL as-is, no more magic keywords.
static const char *effect_vertex_main_pre =
"void main()\n"
"{\n"
" qt_inputUV = attr_uv;\n"
" qt_textureUV = qt_effectTextureMapUV(attr_uv);\n"
" vec4 qt_vertPosition = vec4(attr_pos, 1.0);\n"
"#if QSHADER_VIEW_COUNT >= 2\n"
" qt_viewIndex = gl_ViewIndex;\n"
"#else\n"
" qt_viewIndex = 0;\n"
"#endif\n"
" qt_customMain(qt_vertPosition.xyz);\n";
static const char *effect_vertex_main_position =
" gl_Position = qt_modelViewProjection * qt_vertPosition;\n";
static const char *effect_vertex_main_post =
"}\n";
static const char *effect_fragment_main =
"void main()\n"
"{\n"
" qt_customMain();\n"
"}\n";
static const char *effect_fragment_main_with_tonemapping =
"#include \"tonemapping.glsllib\"\n"
"void main()\n"
"{\n"
" qt_customMain();\n"
" fragOutput = qt_tonemap(fragOutput);\n"
"}\n";
void QSSGRenderEffect::finalizeShaders(const QSSGRenderLayer &layer, QSSGRenderContextInterface *renderContext)
{
Q_UNUSED(layer);
// this is called on every frame, so do nothing if there are no changes
if (!shaderPrepData.valid)
return;
QRhi *rhi = renderContext->rhiContext()->rhi();
for (int i = 0, ie = shaderPrepData.passes.size(); i != ie; ++i) {
const ShaderPrepPassData &pass(shaderPrepData.passes[i]);
// The fragment shader of the last pass of the last effect may need to
// perform the built-in tonemapping.
const bool isLastEffect = m_nextEffect == nullptr;
const bool isLastPass = i == ie - 1;
const bool shouldTonemapIfEnabled = isLastEffect && isLastPass;
QSSGShaderFeatures features;
QByteArray completeVertexShader;
QByteArray completeFragmentShader;
QByteArray sourceCodeForHash;
const bool multiview = layer.viewCount >= 2;
const int srcIdx = multiview ? QSSGRenderCustomMaterial::MultiViewShaderPathKeyIndex : QSSGRenderCustomMaterial::RegularShaderPathKeyIndex;
if (!pass.vertexShaderCode[srcIdx].isEmpty()) {
QByteArray code = pass.vertexShaderCode[srcIdx];
// add the real main(), with or without assigning gl_Position at the end
code.append(effect_vertex_main_pre);
if (!pass.vertexMetaData[srcIdx].flags.testFlag(QSSGCustomShaderMetaData::OverridesPosition))
code.append(effect_vertex_main_position);
code.append(effect_vertex_main_post);
completeVertexShader = code;
sourceCodeForHash += code;
}
if (!pass.fragmentShaderCode[srcIdx].isEmpty()) {
QByteArray code = pass.fragmentShaderCode[srcIdx];
if (shouldTonemapIfEnabled)
code.append(effect_fragment_main_with_tonemapping);
else
code.append(effect_fragment_main);
completeFragmentShader = code;
sourceCodeForHash += code;
}
QByteArray shaderPathKey = pass.shaderPathKeyPrefix;
shaderPathKey.append(':' + QCryptographicHash::hash(sourceCodeForHash, QCryptographicHash::Algorithm::Sha1).toHex());
// QSSGRhiEffectSystem will vary the vertex shader code based on this
// flag from the QRhi. It is therefore important to capture this in the
// cache key as well.
shaderPathKey.append(rhi->isYUpInFramebuffer() ? QByteArrayLiteral(":1") : QByteArrayLiteral(":0"));
if (shouldTonemapIfEnabled) {
// This does not always mean there will be tonemapping: if the mode
// is TonemapModeNone, then no extra feature defines are set, and
// so qt_tonemap() in the shader will not alter the color.
const QSSGRenderLayer::TonemapMode tonemapMode = layer.tonemapMode;
shaderPathKey.append(':' + QByteArray::number(int(tonemapMode)));
QSSGLayerRenderData::setTonemapFeatures(features, tonemapMode);
}
shaderPathKey.append(multiview ? QByteArrayLiteral(":1") : QByteArrayLiteral(":0"));
// Now that the final shaderPathKey is known, store the source and
// related data; it will be retrieved later by the QSSGRhiEffectSystem.
if (!completeVertexShader.isEmpty()) {
renderContext->shaderLibraryManager()->setShaderSource(shaderPathKey,
QSSGShaderCache::ShaderType::Vertex,
completeVertexShader,
pass.vertexMetaData[srcIdx]);
}
if (!completeFragmentShader.isEmpty()) {
QSSGCustomShaderMetaData metaData = pass.fragmentMetaData[srcIdx];
metaData.features = features;
renderContext->shaderLibraryManager()->setShaderSource(shaderPathKey,
QSSGShaderCache::ShaderType::Fragment,
completeFragmentShader,
metaData);
}
// and update the command
delete commands[pass.bindShaderCmdIndex];
commands[pass.bindShaderCmdIndex] = new QSSGBindShader(shaderPathKey);
}
shaderPrepData.valid = false;
}
void QSSGRenderEffect::resetCommands()
{
qDeleteAll(commands);
commands.clear();
shaderPrepData.passes.clear();
}
QT_END_NAMESPACE