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

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtquick3d/src/runtimerender/qssgrendershadermetadata.cpp