1/****************************************************************************
2**
3** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qshadernodesloader_p.h"
41
42#include <QtCore/qdebug.h>
43#include <QtCore/qiodevice.h>
44#include <QtCore/qjsonarray.h>
45#include <QtCore/qjsondocument.h>
46#include <QtCore/qjsonobject.h>
47#include <QtCore/qmetaobject.h>
48
49QT_BEGIN_NAMESPACE
50
51namespace Qt3DRender
52{
53QShaderNodesLoader::QShaderNodesLoader() noexcept
54 : m_status(Null),
55 m_device(nullptr)
56{
57}
58
59QShaderNodesLoader::Status QShaderNodesLoader::status() const noexcept
60{
61 return m_status;
62}
63
64QHash<QString, QShaderNode> QShaderNodesLoader::nodes() const noexcept
65{
66 return m_nodes;
67}
68
69QIODevice *QShaderNodesLoader::device() const noexcept
70{
71 return m_device;
72}
73
74void QShaderNodesLoader::setDevice(QIODevice *device) noexcept
75{
76 m_device = device;
77 m_nodes.clear();
78 m_status = !m_device ? Null
79 : (m_device->openMode() & QIODevice::ReadOnly) ? Waiting
80 : Error;
81}
82
83void QShaderNodesLoader::load()
84{
85 if (m_status == Error)
86 return;
87
88 auto error = QJsonParseError();
89 const QJsonDocument document = QJsonDocument::fromJson(json: m_device->readAll(), error: &error);
90
91 if (error.error != QJsonParseError::NoError) {
92 qWarning() << "Invalid JSON document:" << error.errorString();
93 m_status = Error;
94 return;
95 }
96
97 if (document.isEmpty() || !document.isObject()) {
98 qWarning() << "Invalid JSON document, root should be an object";
99 m_status = Error;
100 return;
101 }
102
103 const QJsonObject root = document.object();
104 load(prototypesObject: root);
105}
106
107void QShaderNodesLoader::load(const QJsonObject &prototypesObject)
108{
109 bool hasError = false;
110
111 for (const QString &property : prototypesObject.keys()) {
112 const QJsonValue nodeValue = prototypesObject.value(key: property);
113 if (!nodeValue.isObject()) {
114 qWarning() << "Invalid node found";
115 hasError = true;
116 break;
117 }
118
119 const QJsonObject nodeObject = nodeValue.toObject();
120
121 auto node = QShaderNode();
122
123 const QJsonValue inputsValue = nodeObject.value(QStringLiteral("inputs"));
124 if (inputsValue.isArray()) {
125 const QJsonArray inputsArray = inputsValue.toArray();
126 for (const QJsonValue &inputValue : inputsArray) {
127 if (!inputValue.isString()) {
128 qWarning() << "Non-string value in inputs";
129 hasError = true;
130 break;
131 }
132
133 auto input = QShaderNodePort();
134 input.direction = QShaderNodePort::Input;
135 input.name = inputValue.toString();
136 node.addPort(port: input);
137 }
138 }
139
140 const QJsonValue outputsValue = nodeObject.value(QStringLiteral("outputs"));
141 if (outputsValue.isArray()) {
142 const QJsonArray outputsArray = outputsValue.toArray();
143 for (const QJsonValue &outputValue : outputsArray) {
144 if (!outputValue.isString()) {
145 qWarning() << "Non-string value in outputs";
146 hasError = true;
147 break;
148 }
149
150 auto output = QShaderNodePort();
151 output.direction = QShaderNodePort::Output;
152 output.name = outputValue.toString();
153 node.addPort(port: output);
154 }
155 }
156
157 const QJsonValue parametersValue = nodeObject.value(QStringLiteral("parameters"));
158 if (parametersValue.isObject()) {
159 const QJsonObject parametersObject = parametersValue.toObject();
160 for (const QString &parameterName : parametersObject.keys()) {
161 const QJsonValue parameterValue = parametersObject.value(key: parameterName);
162 if (parameterValue.isObject()) {
163 const QJsonObject parameterObject = parameterValue.toObject();
164 const QString type = parameterObject.value(QStringLiteral("type")).toString();
165 const int typeId = QMetaType::type(typeName: type.toUtf8());
166
167 const QString value = parameterObject.value(QStringLiteral("value")).toString();
168 auto variant = QVariant(value);
169
170 if (QMetaType::typeFlags(type: typeId) & QMetaType::IsEnumeration) {
171 const QMetaObject *metaObject = QMetaType::metaObjectForType(type: typeId);
172 const char *className = metaObject->className();
173 const QByteArray enumName = type.mid(position: static_cast<int>(qstrlen(str: className)) + 2).toUtf8();
174 const QMetaEnum metaEnum = metaObject->enumerator(index: metaObject->indexOfEnumerator(name: enumName));
175 const int enumValue = metaEnum.keyToValue(key: value.toUtf8());
176 variant = QVariant(enumValue);
177 variant.convert(targetTypeId: typeId);
178 } else {
179 variant.convert(targetTypeId: typeId);
180 }
181 node.setParameter(name: parameterName, value: variant);
182 } else {
183 node.setParameter(name: parameterName, value: parameterValue.toVariant());
184 }
185 }
186 }
187
188 const QJsonValue rulesValue = nodeObject.value(QStringLiteral("rules"));
189 if (rulesValue.isArray()) {
190 const QJsonArray rulesArray = rulesValue.toArray();
191 for (const QJsonValue &ruleValue : rulesArray) {
192 if (!ruleValue.isObject()) {
193 qWarning() << "Rules should be objects";
194 hasError = true;
195 break;
196 }
197
198 const QJsonObject ruleObject = ruleValue.toObject();
199
200 const QJsonValue formatValue = ruleObject.value(QStringLiteral("format"));
201 if (!formatValue.isObject()) {
202 qWarning() << "Format is mandatory in rules and should be an object";
203 hasError = true;
204 break;
205 }
206
207 const QJsonObject formatObject = formatValue.toObject();
208 auto format = QShaderFormat();
209
210 const QJsonValue apiValue = formatObject.value(QStringLiteral("api"));
211 if (!apiValue.isString()) {
212 qWarning() << "Format API must be a string";
213 hasError = true;
214 break;
215 }
216
217 const QString api = apiValue.toString();
218 format.setApi(api == QStringLiteral("OpenGLES") ? QShaderFormat::OpenGLES
219 : api == QStringLiteral("OpenGLNoProfile") ? QShaderFormat::OpenGLNoProfile
220 : api == QStringLiteral("OpenGLCoreProfile") ? QShaderFormat::OpenGLCoreProfile
221 : api == QStringLiteral("OpenGLCompatibilityProfile") ? QShaderFormat::OpenGLCompatibilityProfile
222 : api == QStringLiteral("VulkanFlavoredGLSL") ? QShaderFormat::VulkanFlavoredGLSL
223 : api == QStringLiteral("RHI") ? QShaderFormat::RHI
224 : QShaderFormat::NoApi);
225 if (format.api() == QShaderFormat::NoApi) {
226 qWarning() << "Format API must be one of: OpenGLES, OpenGLNoProfile, OpenGLCoreProfile OpenGLCompatibilityProfile, VulkanFlavoredGLSL or RHI";
227 hasError = true;
228 break;
229 }
230
231 const QJsonValue majorValue = formatObject.value(QStringLiteral("major"));
232 const QJsonValue minorValue = formatObject.value(QStringLiteral("minor"));
233 if (!majorValue.isDouble() || !minorValue.isDouble()) {
234 qWarning() << "Format major and minor version must be values";
235 hasError = true;
236 break;
237 }
238 format.setVersion(QVersionNumber(majorValue.toInt(), minorValue.toInt()));
239
240 const QJsonValue extensionsValue = formatObject.value(QStringLiteral("extensions"));
241 const QJsonArray extensionsArray = extensionsValue.toArray();
242 auto extensions = QStringList();
243 std::transform(first: extensionsArray.constBegin(), last: extensionsArray.constEnd(),
244 result: std::back_inserter(x&: extensions),
245 unary_op: [] (const QJsonValue &extensionValue) { return extensionValue.toString(); });
246 format.setExtensions(extensions);
247
248 const QString vendor = formatObject.value(QStringLiteral("vendor")).toString();
249 format.setVendor(vendor);
250
251 const QJsonValue substitutionValue = ruleObject.value(QStringLiteral("substitution"));
252 if (!substitutionValue.isString()) {
253 qWarning() << "Substitution needs to be a string";
254 hasError = true;
255 break;
256 }
257
258 // We default out to a Fragment ShaderType if nothing is specified
259 // as that was the initial behavior we introduced
260 const QString shaderType = formatObject.value(QStringLiteral("shaderType")).toString();
261 format.setShaderType(shaderType == QStringLiteral("Fragment") ? QShaderFormat::Fragment
262 : shaderType == QStringLiteral("Vertex") ? QShaderFormat::Vertex
263 : shaderType == QStringLiteral("TessellationControl") ? QShaderFormat::TessellationControl
264 : shaderType == QStringLiteral("TessellationEvaluation") ? QShaderFormat::TessellationEvaluation
265 : shaderType == QStringLiteral("Geometry") ? QShaderFormat::Geometry
266 : shaderType == QStringLiteral("Compute") ? QShaderFormat::Compute
267 : QShaderFormat::Fragment);
268
269 const QByteArray substitution = substitutionValue.toString().toUtf8();
270
271 const QJsonValue snippetsValue = ruleObject.value(QStringLiteral("headerSnippets"));
272 const QJsonArray snippetsArray = snippetsValue.toArray();
273 auto snippets = QByteArrayList();
274 std::transform(first: snippetsArray.constBegin(), last: snippetsArray.constEnd(),
275 result: std::back_inserter(x&: snippets),
276 unary_op: [] (const QJsonValue &snippetValue) { return snippetValue.toString().toUtf8(); });
277
278 node.addRule(format, rule: QShaderNode::Rule(substitution, snippets));
279 }
280 }
281
282 m_nodes.insert(akey: property, avalue: node);
283 }
284
285 if (hasError) {
286 m_status = Error;
287 m_nodes.clear();
288 } else {
289 m_status = Ready;
290 }
291}
292}
293QT_END_NAMESPACE
294

source code of qt3d/src/render/shadergraph/qshadernodesloader.cpp