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