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 | |
12 | QT_BEGIN_NAMESPACE |
13 | |
14 | QSSGRenderEffect::QSSGRenderEffect() : QSSGRenderGraphObject(Type::Effect) {} |
15 | |
16 | QSSGRenderEffect::~QSSGRenderEffect() |
17 | { |
18 | resetCommands(); |
19 | } |
20 | |
21 | void QSSGRenderEffect::markDirty() |
22 | { |
23 | flags |= FlagT(Flags::Dirty); |
24 | } |
25 | |
26 | void 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 | |
34 | static 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 | |
42 | static const char *effect_vertex_main_position = |
43 | " gl_Position = qt_modelViewProjection * qt_vertPosition;\n" ; |
44 | |
45 | static const char *effect_vertex_main_post = |
46 | "}\n" ; |
47 | |
48 | static const char *effect_fragment_main = |
49 | "void main()\n" |
50 | "{\n" |
51 | " qt_customMain();\n" |
52 | "}\n" ; |
53 | |
54 | static 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 | |
62 | void 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 | |
147 | void 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 | |
157 | QT_END_NAMESPACE |
158 | |