1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include <QtQuick3DRuntimeRender/private/qssgrendereffect_p.h>
5#include <QtQuick3DRuntimeRender/private/qssgrenderlayer_p.h>
6#include <QtQuick3DRuntimeRender/private/qssgrendercommands_p.h>
7#include <QtQuick3DRuntimeRender/private/qssgrendercontextcore_p.h>
8
9#include <QtGui/QVector2D>
10#include <QtGui/QVector3D>
11
12QT_BEGIN_NAMESPACE
13
14QSSGRenderEffect::QSSGRenderEffect() : QSSGRenderGraphObject(Type::Effect) {}
15
16QSSGRenderEffect::~QSSGRenderEffect()
17{
18 resetCommands();
19}
20
21void QSSGRenderEffect::markDirty()
22{
23 flags |= FlagT(Flags::Dirty);
24}
25
26void QSSGRenderEffect::clearDirty()
27{
28 flags &= ~FlagT(Flags::Dirty);
29}
30
31// Suffix snippets added to the end of the shader strings. These are appended
32// after processing so it must be valid GLSL as-is, no more magic keywords.
33
34static const char *effect_vertex_main_pre =
35 "void main()\n"
36 "{\n"
37 " qt_inputUV = attr_uv;\n"
38 " qt_textureUV = qt_effectTextureMapUV(attr_uv);\n"
39 " vec4 qt_vertPosition = vec4(attr_pos, 1.0);\n"
40 " qt_customMain(qt_vertPosition.xyz);\n";
41
42static const char *effect_vertex_main_position =
43 " gl_Position = qt_modelViewProjection * qt_vertPosition;\n";
44
45static const char *effect_vertex_main_post =
46 "}\n";
47
48static const char *effect_fragment_main =
49 "void main()\n"
50 "{\n"
51 " qt_customMain();\n"
52 "}\n";
53
54static const char *effect_fragment_main_with_tonemapping =
55 "#include \"tonemapping.glsllib\"\n"
56 "void main()\n"
57 "{\n"
58 " qt_customMain();\n"
59 " fragOutput = qt_tonemap(fragOutput);\n"
60 "}\n";
61
62void QSSGRenderEffect::finalizeShaders(const QSSGRenderLayer &layer, QSSGRenderContextInterface *renderContext)
63{
64 Q_UNUSED(layer);
65
66 // this is called on every frame, so do nothing if there are no changes
67 if (!shaderPrepData.valid)
68 return;
69
70 QRhi *rhi = renderContext->rhiContext()->rhi();
71
72 for (int i = 0, ie = shaderPrepData.passes.size(); i != ie; ++i) {
73 const ShaderPrepPassData &pass(shaderPrepData.passes[i]);
74
75 // The fragment shader of the last pass of the last effect may need to
76 // perform the built-in tonemapping.
77 const bool isLastEffect = m_nextEffect == nullptr;
78 const bool isLastPass = i == ie - 1;
79 const bool shouldTonemapIfEnabled = isLastEffect && isLastPass;
80
81 QSSGShaderFeatures features;
82 QByteArray completeVertexShader;
83 QByteArray completeFragmentShader;
84 QByteArray sourceCodeForHash;
85 if (!pass.vertexShaderCode.isEmpty()) {
86 QByteArray code = pass.vertexShaderCode;
87 // add the real main(), with or without assigning gl_Position at the end
88 code.append(s: effect_vertex_main_pre);
89 if (!pass.vertexMetaData.flags.testFlag(flag: QSSGCustomShaderMetaData::OverridesPosition))
90 code.append(s: effect_vertex_main_position);
91 code.append(s: effect_vertex_main_post);
92 completeVertexShader = code;
93 sourceCodeForHash += code;
94 }
95 if (!pass.fragmentShaderCode.isEmpty()) {
96 QByteArray code = pass.fragmentShaderCode;
97 if (shouldTonemapIfEnabled)
98 code.append(s: effect_fragment_main_with_tonemapping);
99 else
100 code.append(s: effect_fragment_main);
101 completeFragmentShader = code;
102 sourceCodeForHash += code;
103 }
104
105 QByteArray shaderPathKey = pass.shaderPathKeyPrefix;
106 shaderPathKey.append(a: ':' + QCryptographicHash::hash(data: sourceCodeForHash, method: QCryptographicHash::Algorithm::Sha1).toHex());
107
108 // QSSGRhiEffectSystem will vary the vertex shader code based on this
109 // flag from the QRhi. It is therefore important to capture this in the
110 // cache key as well.
111 shaderPathKey.append(a: ':' + QByteArray::number(rhi->isYUpInFramebuffer() ? 1 : 0));
112
113 if (shouldTonemapIfEnabled) {
114 // This does not always mean there will be tonemapping: if the mode
115 // is TonemapModeNone, then no extra feature defines are set, and
116 // so qt_tonemap() in the shader will not alter the color.
117 const QSSGRenderLayer::TonemapMode tonemapMode = layer.tonemapMode;
118 shaderPathKey.append(a: ':' + QByteArray::number(int(tonemapMode)));
119 QSSGRenderer::setTonemapFeatures(features, tonemapMode);
120 }
121
122 // Now that the final shaderPathKey is known, store the source and
123 // related data; it will be retrieved later by the QSSGRhiEffectSystem.
124 if (!completeVertexShader.isEmpty()) {
125 renderContext->shaderLibraryManager()->setShaderSource(inShaderPathKey: shaderPathKey,
126 type: QSSGShaderCache::ShaderType::Vertex,
127 inSource: completeVertexShader,
128 meta: pass.vertexMetaData);
129 }
130 if (!completeFragmentShader.isEmpty()) {
131 QSSGCustomShaderMetaData metaData = pass.fragmentMetaData;
132 metaData.features = features;
133 renderContext->shaderLibraryManager()->setShaderSource(inShaderPathKey: shaderPathKey,
134 type: QSSGShaderCache::ShaderType::Fragment,
135 inSource: completeFragmentShader,
136 meta: metaData);
137 }
138
139 // and update the command
140 delete commands[pass.bindShaderCmdIndex].command;
141 commands[pass.bindShaderCmdIndex] = { .command: new QSSGBindShader(shaderPathKey), .own: true };
142 }
143
144 shaderPrepData.valid = false;
145}
146
147void QSSGRenderEffect::resetCommands()
148{
149 for (const Command &cmd : commands) {
150 if (cmd.own)
151 delete cmd.command;
152 }
153 commands.clear();
154 shaderPrepData.passes.clear();
155}
156
157QT_END_NAMESPACE
158

source code of qtquick3d/src/runtimerender/graphobjects/qssgrendereffect.cpp