Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qssgrendershadermetadata.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5
7
8#include <QPair>
9#include <QJsonDocument>
10#include <QJsonArray>
11#include <QJsonObject>
12#include <QJsonValue>
13
14#include <QtDebug>
15
16// Example snippet
17// The comment in the ifdefed block is necessary to keep weird shader compilers (Vivante) happy.
18
19// #ifdef QQ3D_SHADER_META
20// /*{
21// "uniforms": [ { "type": "mat44", "name": "qt_viewProjectionMatrix" },
22// { "type": "mat4", "name": "qt_viewMatrix" },
23// { "type": "vec2", "name": "qt_cameraProperties" },
24// { "type": "vec3", "name": "qt_cameraPosition", "condition": "!SSAO_CUSTOM_MATERIAL_GLSLLIB" }
25// ]
26// }*/
27// #endif // QQ3D_SHADER_META
28
29QT_BEGIN_NAMESPACE
30
32
33const char *shaderMetaStart() { return "#ifdef QQ3D_SHADER_META"; }
34const char *shaderMetaEnd() { return "#endif"; }
35
36Uniform::Condition Uniform::conditionFromString(const QString &condition)
37{
38 if (condition.isEmpty())
40
41 if (condition.at(0) == QChar::fromLatin1('!'))
42 return Uniform::Negated;
43
44 return Uniform::Regular;
45}
46
47QSSGShaderGeneratorStage InputOutput::stageFromString(const QString &stage)
48{
49 if (stage == QLatin1String("vertex")) {
50 return QSSGShaderGeneratorStage::Vertex;
51 } else if (stage == QLatin1String("fragment"))
52 return QSSGShaderGeneratorStage::Fragment;
53 else {
54 qWarning("Unknown stage in shader metadata: %s, assuming vertex", qPrintable(stage));
55 return QSSGShaderGeneratorStage::Vertex;
56 }
57}
58
59ShaderMetaData getShaderMetaData(const QByteArray &data)
60{
61 ShaderMetaData result;
62 if (data.isEmpty())
63 return result;
64
65 int jsonStart = 0, jsonEnd = 0;
66 for ( ; ; ) {
67 jsonStart = data.indexOf(shaderMetaStart(), jsonEnd);
68 if (jsonStart)
69 jsonEnd = data.indexOf(shaderMetaEnd(), jsonStart);
70
71 if (jsonEnd) // adjust start position
72 jsonStart += int(strlen(shaderMetaStart()));
73
74 if (jsonStart <= 0 || jsonEnd <= 0)
75 break;
76
77 const int size = jsonEnd - jsonStart;
78 // /*{"inputs":[]}*/ => 17
79 if (size < 17) {
80 qWarning("Shader metadata section found, but content to small to be valid!");
81 break;
82 }
83
84 QByteArray jsonData = data.mid(jsonStart, size).trimmed();
85 if (!jsonData.startsWith(QByteArrayLiteral("/*{"))) {
86 qWarning("Missing /*{ prefix");
87 break;
88 }
89 if (!jsonData.endsWith(QByteArrayLiteral("}*/"))) {
90 qWarning("Missing }*/ suffix");
91 break;
92 }
93 jsonData = jsonData.mid(2, jsonData.size() - 4);
94
95 QJsonParseError error;
96 const auto doc = QJsonDocument::fromJson(jsonData, &error);
97 if (error.error != QJsonParseError::NoError) {
98 qWarning() << "Shader metadata parse error at offset: " << error.offset;
99 break;
100 }
101
102 static const auto toUniform = [](const QJsonObject &uObj) {
103 Uniform uniform;
104 auto it = uObj.constBegin();
105 const auto end = uObj.constEnd();
106 if (it != end) {
107 it = uObj.constFind(QLatin1String("type"));
108 uniform.type = (it != end) ? it->toString().toLatin1() : QByteArray();
109 it = uObj.constFind(QLatin1String("name"));
110 uniform.name = (it != end) ? it->toString().toLatin1() : QByteArray();
111 it = uObj.constFind(QLatin1String("multiview_dependent"));
112 uniform.multiview = (it != end) ? it->toBool() : false;
113
114 it = uObj.constFind(QLatin1String("condition"));
115 const QString conditionString = (it != end) ? it->toString() : QString();
116 uniform.condition = Uniform::conditionFromString(conditionString);
117 if (uniform.condition == Uniform::Negated)
118 uniform.conditionName = conditionString.mid(1).toLatin1();
119 else if (uniform.condition == Uniform::Regular)
120 uniform.conditionName = conditionString.toLatin1();
121 }
122 return uniform;
123 };
124
125 static const auto toImage = [](const QJsonObject &uObj, const Uniform &uniform)
126 {
127 Image img(uniform);
128 auto it = uObj.constBegin();
129 const auto end = uObj.constEnd();
130 if (it != end) {
131 it = uObj.constFind(QLatin1String("imgtype"));
132 img.imageType = (it != end) ? it->toString().toLatin1() : QByteArray();
133 it = uObj.constFind(QLatin1String("qualifiers"));
134 img.qualifiers = (it != end) ? it->toString().toLatin1() : QByteArray();
135 }
136 return img;
137 };
138
139 static const auto toInputOutput = [](const QJsonObject &uObj) {
140 InputOutput inOutVar;
141 auto it = uObj.constBegin();
142 const auto end = uObj.constEnd();
143 if (it != end) {
144 it = uObj.constFind(QLatin1String("type"));
145 inOutVar.type = (it != end) ? it->toString().toLatin1() : QByteArray();
146 it = uObj.constFind(QLatin1String("name"));
147 inOutVar.name = (it != end) ? it->toString().toLatin1() : QByteArray();
148 it = uObj.constFind(QLatin1String("stage"));
149 inOutVar.stage = InputOutput::stageFromString((it != end) ? it->toString() : QString());
150 it = uObj.constFind(QLatin1String("flat"));
151 inOutVar.flat = (it != end) ? it->toBool() : false;
152 }
153 return inOutVar;
154 };
155
156 const QJsonObject obj = doc.object();
157 auto it = obj.constBegin();
158 const auto end = obj.constEnd();
159 if (it != end) {
160 // Uniforms
161 it = obj.constFind(QLatin1String("uniforms"));
162 if (it != obj.constEnd()) {
163 // Check if it's an array or a single object
164 if (it->type() == QJsonValue::Array) {
165 const auto uniformArray = it->toArray();
166 for (const auto valueRef : uniformArray) {
167 if (!valueRef.isObject())
168 continue;
169
170 const QJsonObject obj = valueRef.toObject();
171 const auto uniform = toUniform(obj);
172 if (!uniform.type.isEmpty() && !uniform.name.isEmpty()) {
173 if (obj.constFind(QLatin1String("imgtype")) != obj.constEnd())
174 result.images.push_back(toImage(obj, uniform));
175 else
176 result.uniforms.push_back(uniform);
177 } else {
178 qWarning("Invalid uniform, skipping");
179 }
180 }
181 } else if (it->type() == QJsonValue::Object) {
182 const auto uniform = toUniform(it->toObject());
183 if (!uniform.type.isEmpty() && !uniform.name.isEmpty()) {
184 if (obj.constFind(QLatin1String("imgtype")) != obj.constEnd())
185 result.images.push_back(toImage(obj, uniform));
186 else
187 result.uniforms.push_back(uniform);
188 } else {
189 qWarning("Invalid uniform, skipping");
190 }
191 }
192 }
193
194 // Inputs
195 it = obj.constFind(QLatin1String("inputs"));
196 if (it != end) {
197 if (it->type() == QJsonValue::Array) {
198 for (const auto valueRef : it->toArray()) {
199 if (!valueRef.isObject())
200 continue;
201 const auto inOutVar = toInputOutput(valueRef.toObject());
202 if (!inOutVar.type.isEmpty() && !inOutVar.name.isEmpty())
203 result.inputs.push_back(inOutVar);
204 else
205 qWarning("Invalid input variable, skipping");
206 }
207 } else if (it->type() == QJsonValue::Object) {
208 const QJsonObject obj = it->toObject();
209 const auto inOutVar = toInputOutput(obj);
210 if (!inOutVar.type.isEmpty() && !inOutVar.name.isEmpty()) {
211 result.inputs.push_back(inOutVar);
212 } else {
213 qWarning("Invalid input variable, skipping");
214 }
215 }
216 }
217
218 // Outputs
219 it = obj.constFind(QLatin1String("outputs"));
220 if (it != end) {
221 if (it->type() == QJsonValue::Array) {
222 for (const auto valueRef : it->toArray()) {
223 if (!valueRef.isObject())
224 continue;
225 const auto inOutVar = toInputOutput(valueRef.toObject());
226 if (!inOutVar.type.isEmpty() && !inOutVar.name.isEmpty())
227 result.outputs.push_back(inOutVar);
228 else
229 qWarning("Invalid output variable, skipping");
230 }
231 } else if (it->type() == QJsonValue::Object) {
232 const QJsonObject inputJObj = it->toObject();
233 const auto inOutVar = toInputOutput(inputJObj);
234 if (!inOutVar.type.isEmpty() && !inOutVar.name.isEmpty()) {
235 result.outputs.push_back(inOutVar);
236 } else {
237 qWarning("Invalid output variable, skipping");
238 }
239 }
240 }
241 }
242 }
243
244 return result;
245}
246
247} // namespace QSSGRenderShaderMetadata
248
249QT_END_NAMESPACE
ShaderMetaData getShaderMetaData(const QByteArray &data)