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 QByteArray qualifiers = (it != end) ? it->toString().toLatin1() : QByteArray();
135 if (!qualifiers.isEmpty()) {
136 if (!QSSGBaseTypeHelpers::parseMemoryQualifiers(qualifiers, img.qualifiers))
137 qWarning("Invalid qualifiers %s for image %s", qualifiers.constData(), uniform.name.constData());
138 }
139 }
140 return img;
141 };
142 static const auto toBuffer = [](const QJsonObject &uObj, const Uniform &uniform)
143 {
144 Buffer buf(uniform);
145 auto it = uObj.constBegin();
146 const auto end = uObj.constEnd();
147 if (it != end) {
148 it = uObj.constFind(QLatin1String("contentType"));
149 buf.contentType = (it != end) ? it->toString().toLatin1() : QByteArray();
150 it = uObj.constFind(QLatin1String("qualifiers"));
151 QByteArray qualifiers = (it != end) ? it->toString().toLatin1() : QByteArray();
152 if (!qualifiers.isEmpty()) {
153 if (!QSSGBaseTypeHelpers::parseMemoryQualifiers(qualifiers, buf.qualifiers))
154 qWarning("Invalid qualifiers %s for buffer %s", qualifiers.constData(), uniform.name.constData());
155 }
156 }
157 return buf;
158 };
159
160 static const auto toInputOutput = [](const QJsonObject &uObj) {
161 InputOutput inOutVar;
162 auto it = uObj.constBegin();
163 const auto end = uObj.constEnd();
164 if (it != end) {
165 it = uObj.constFind(QLatin1String("type"));
166 inOutVar.type = (it != end) ? it->toString().toLatin1() : QByteArray();
167 it = uObj.constFind(QLatin1String("name"));
168 inOutVar.name = (it != end) ? it->toString().toLatin1() : QByteArray();
169 it = uObj.constFind(QLatin1String("stage"));
170 inOutVar.stage = InputOutput::stageFromString((it != end) ? it->toString() : QString());
171 it = uObj.constFind(QLatin1String("flat"));
172 inOutVar.flat = (it != end) ? it->toBool() : false;
173 }
174 return inOutVar;
175 };
176
177 const auto addUniformEntry = [&result](const QJsonObject &uObj) {
178 const auto uniform = toUniform(uObj);
179 if (uniform.type.isEmpty() || uniform.name.isEmpty()) {
180 qWarning("Invalid uniform, skipping");
181 return;
182 }
183 if (uObj.constFind(QLatin1String("imgtype")) != uObj.constEnd()) {
184 Image img(toImage(uObj, uniform));
185 if (img.imageType.isEmpty()) {
186 qWarning("Image type missing for image %s, skipping", img.name.constData());
187 return;
188 }
189 result.images.push_back(img);
190 } else if (uniform.type == "buffer") {
191 Buffer buf(toBuffer(uObj, uniform));
192 if (buf.contentType.isEmpty()) {
193 qWarning("Content type missing for buffer %s, skipping", buf.name.constData());
194 return;
195 }
196 result.buffers.push_back(buf);
197 } else {
198 result.uniforms.push_back(uniform);
199 }
200 };
201
202 const QJsonObject obj = doc.object();
203 auto it = obj.constBegin();
204 const auto end = obj.constEnd();
205 if (it != end) {
206 // Uniforms
207 it = obj.constFind(QLatin1String("uniforms"));
208 if (it != obj.constEnd()) {
209 // Check if it's an array or a single object
210 if (it->type() == QJsonValue::Array) {
211 for (const auto valueRef : it->toArray()) {
212 if (valueRef.isObject())
213 addUniformEntry(valueRef.toObject());
214 }
215 } else if (it->type() == QJsonValue::Object) {
216 addUniformEntry(it->toObject());
217 }
218 }
219
220 // Inputs
221 it = obj.constFind(QLatin1String("inputs"));
222 if (it != end) {
223 if (it->type() == QJsonValue::Array) {
224 const auto array = it->toArray();
225 for (const auto &valueRef : array) {
226 if (!valueRef.isObject())
227 continue;
228 const auto inOutVar = toInputOutput(valueRef.toObject());
229 if (!inOutVar.type.isEmpty() && !inOutVar.name.isEmpty())
230 result.inputs.push_back(inOutVar);
231 else
232 qWarning("Invalid input variable, skipping");
233 }
234 } else if (it->type() == QJsonValue::Object) {
235 const QJsonObject obj = it->toObject();
236 const auto inOutVar = toInputOutput(obj);
237 if (!inOutVar.type.isEmpty() && !inOutVar.name.isEmpty()) {
238 result.inputs.push_back(inOutVar);
239 } else {
240 qWarning("Invalid input variable, skipping");
241 }
242 }
243 }
244
245 // Outputs
246 it = obj.constFind(QLatin1String("outputs"));
247 if (it != end) {
248 if (it->type() == QJsonValue::Array) {
249 const auto array = it->toArray();
250 for (const auto &valueRef : array) {
251 if (!valueRef.isObject())
252 continue;
253 const auto inOutVar = toInputOutput(valueRef.toObject());
254 if (!inOutVar.type.isEmpty() && !inOutVar.name.isEmpty())
255 result.outputs.push_back(inOutVar);
256 else
257 qWarning("Invalid output variable, skipping");
258 }
259 } else if (it->type() == QJsonValue::Object) {
260 const QJsonObject inputJObj = it->toObject();
261 const auto inOutVar = toInputOutput(inputJObj);
262 if (!inOutVar.type.isEmpty() && !inOutVar.name.isEmpty()) {
263 result.outputs.push_back(inOutVar);
264 } else {
265 qWarning("Invalid output variable, skipping");
266 }
267 }
268 }
269 }
270 }
271
272 return result;
273}
274
275} // namespace QSSGRenderShaderMetadata
276
277QT_END_NAMESPACE
ShaderMetaData getShaderMetaData(const QByteArray &data)