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