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 test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
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 General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29
30#include <QtTest/QtTest>
31
32#include <QtCore/qbuffer.h>
33
34#include <Qt3DRender/private/qshadernodesloader_p.h>
35#include <Qt3DRender/private/qshaderlanguage_p.h>
36
37using namespace Qt3DRender;
38
39using QBufferPointer = QSharedPointer<QBuffer>;
40Q_DECLARE_METATYPE(QBufferPointer);
41
42using NodeHash = QHash<QString, QShaderNode>;
43Q_DECLARE_METATYPE(NodeHash);
44
45namespace
46{
47 QBufferPointer createBuffer(const QByteArray &data, QIODevice::OpenMode openMode = QIODevice::ReadOnly)
48 {
49 auto buffer = QBufferPointer::create();
50 buffer->setData(data);
51 if (openMode != QIODevice::NotOpen)
52 buffer->open(openMode);
53 return buffer;
54 }
55
56 QShaderFormat createFormat(QShaderFormat::Api api, int majorVersion, int minorVersion,
57 const QStringList &extensions = QStringList(),
58 const QString &vendor = QString())
59 {
60 auto format = QShaderFormat();
61 format.setApi(api);
62 format.setVersion(QVersionNumber(majorVersion, minorVersion));
63 format.setExtensions(extensions);
64 format.setVendor(vendor);
65 return format;
66 }
67
68 QShaderNodePort createPort(QShaderNodePort::Direction portDirection, const QString &portName)
69 {
70 auto port = QShaderNodePort();
71 port.direction = portDirection;
72 port.name = portName;
73 return port;
74 }
75
76 QShaderNode createNode(const QVector<QShaderNodePort> &ports)
77 {
78 auto node = QShaderNode();
79 for (const auto &port : ports)
80 node.addPort(port);
81 return node;
82 }
83}
84
85class tst_QShaderNodesLoader : public QObject
86{
87 Q_OBJECT
88private slots:
89 void shouldManipulateLoaderMembers();
90 void shouldLoadFromJsonStream_data();
91 void shouldLoadFromJsonStream();
92};
93
94void tst_QShaderNodesLoader::shouldManipulateLoaderMembers()
95{
96 // GIVEN
97 auto loader = QShaderNodesLoader();
98
99 // THEN (default state)
100 QCOMPARE(loader.status(), QShaderNodesLoader::Null);
101 QVERIFY(!loader.device());
102 QVERIFY(loader.nodes().isEmpty());
103
104 // WHEN
105 auto device1 = createBuffer(data: QByteArray("..........."), openMode: QIODevice::NotOpen);
106 loader.setDevice(device1.data());
107
108 // THEN
109 QCOMPARE(loader.status(), QShaderNodesLoader::Error);
110 QCOMPARE(loader.device(), device1.data());
111 QVERIFY(loader.nodes().isEmpty());
112
113 // WHEN
114 auto device2 = createBuffer(data: QByteArray("..........."), openMode: QIODevice::ReadOnly);
115 loader.setDevice(device2.data());
116
117 // THEN
118 QCOMPARE(loader.status(), QShaderNodesLoader::Waiting);
119 QCOMPARE(loader.device(), device2.data());
120 QVERIFY(loader.nodes().isEmpty());
121}
122
123void tst_QShaderNodesLoader::shouldLoadFromJsonStream_data()
124{
125 QTest::addColumn<QBufferPointer>(name: "device");
126 QTest::addColumn<NodeHash>(name: "nodes");
127 QTest::addColumn<QShaderNodesLoader::Status>(name: "status");
128
129 QTest::newRow(dataTag: "empty") << createBuffer(data: "", openMode: QIODevice::ReadOnly) << NodeHash() << QShaderNodesLoader::Error;
130
131 const auto smallJson = "{"
132 " \"inputValue\": {"
133 " \"outputs\": ["
134 " \"value\""
135 " ],"
136 " \"parameters\": {"
137 " \"name\": \"defaultName\","
138 " \"qualifier\": {"
139 " \"type\": \"QShaderLanguage::StorageQualifier\","
140 " \"value\": \"QShaderLanguage::Uniform\""
141 " },"
142 " \"type\": {"
143 " \"type\": \"QShaderLanguage::VariableType\","
144 " \"value\": \"QShaderLanguage::Vec3\""
145 " },"
146 " \"defaultValue\": {"
147 " \"type\": \"float\","
148 " \"value\": \"1.25\""
149 " }"
150 " },"
151 " \"rules\": ["
152 " {"
153 " \"format\": {"
154 " \"api\": \"OpenGLES\","
155 " \"major\": 2,"
156 " \"minor\": 0"
157 " },"
158 " \"substitution\": \"highp vec3 $value = $name;\","
159 " \"headerSnippets\": [ \"varying highp vec3 $name;\" ]"
160 " },"
161 " {"
162 " \"format\": {"
163 " \"api\": \"OpenGLCompatibilityProfile\","
164 " \"major\": 2,"
165 " \"minor\": 1"
166 " },"
167 " \"substitution\": \"vec3 $value = $name;\","
168 " \"headerSnippets\": [ \"in vec3 $name;\" ]"
169 " }"
170 " ]"
171 " },"
172 " \"fragColor\": {"
173 " \"inputs\": ["
174 " \"fragColor\""
175 " ],"
176 " \"rules\": ["
177 " {"
178 " \"format\": {"
179 " \"api\": \"OpenGLES\","
180 " \"major\": 2,"
181 " \"minor\": 0"
182 " },"
183 " \"substitution\": \"gl_fragColor = $fragColor;\""
184 " },"
185 " {"
186 " \"format\": {"
187 " \"api\": \"OpenGLNoProfile\","
188 " \"major\": 4,"
189 " \"minor\": 0"
190 " },"
191 " \"substitution\": \"fragColor = $fragColor;\","
192 " \"headerSnippets\": [ \"out vec4 fragColor;\" ]"
193 " }"
194 " ]"
195 " },"
196 " \"lightModel\": {"
197 " \"inputs\": ["
198 " \"baseColor\","
199 " \"position\","
200 " \"lightIntensity\""
201 " ],"
202 " \"outputs\": ["
203 " \"outputColor\""
204 " ],"
205 " \"rules\": ["
206 " {"
207 " \"format\": {"
208 " \"api\": \"OpenGLES\","
209 " \"major\": 2,"
210 " \"minor\": 0,"
211 " \"extensions\": [ \"ext1\", \"ext2\" ],"
212 " \"vendor\": \"kdab\""
213 " },"
214 " \"substitution\": \"highp vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);\","
215 " \"headerSnippets\": [ \"#pragma include es2/lightmodel.frag.inc\" ]"
216 " },"
217 " {"
218 " \"format\": {"
219 " \"api\": \"OpenGLCoreProfile\","
220 " \"major\": 3,"
221 " \"minor\": 3"
222 " },"
223 " \"substitution\": \"vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);\","
224 " \"headerSnippets\": [ \"#pragma include gl3/lightmodel.frag.inc\" ]"
225 " }"
226 " ]"
227 " }"
228 "}";
229
230 const auto smallProtos = [this]{
231 const auto openGLES2 = createFormat(api: QShaderFormat::OpenGLES, majorVersion: 2, minorVersion: 0);
232 const auto openGLES2Extended = createFormat(api: QShaderFormat::OpenGLES, majorVersion: 2, minorVersion: 0, extensions: {"ext1", "ext2"}, vendor: "kdab");
233 const auto openGL2 = createFormat(api: QShaderFormat::OpenGLCompatibilityProfile, majorVersion: 2, minorVersion: 1);
234 const auto openGL3 = createFormat(api: QShaderFormat::OpenGLCoreProfile, majorVersion: 3, minorVersion: 3);
235 const auto openGL4 = createFormat(api: QShaderFormat::OpenGLNoProfile, majorVersion: 4, minorVersion: 0);
236
237 auto protos = NodeHash();
238
239 auto inputValue = createNode(ports: {
240 createPort(portDirection: QShaderNodePort::Output, portName: "value")
241 });
242 inputValue.setParameter(name: "name", value: "defaultName");
243 inputValue.setParameter(name: "qualifier", value: QVariant::fromValue<QShaderLanguage::StorageQualifier>(value: QShaderLanguage::Uniform));
244 inputValue.setParameter(name: "type", value: QVariant::fromValue<QShaderLanguage::VariableType>(value: QShaderLanguage::Vec3));
245 inputValue.setParameter(name: "defaultValue", value: QVariant(1.25f));
246 inputValue.addRule(format: openGLES2, rule: QShaderNode::Rule("highp vec3 $value = $name;",
247 QByteArrayList() << "varying highp vec3 $name;"));
248 inputValue.addRule(format: openGL2, rule: QShaderNode::Rule("vec3 $value = $name;",
249 QByteArrayList() << "in vec3 $name;"));
250 protos.insert(akey: "inputValue", avalue: inputValue);
251
252 auto fragColor = createNode(ports: {
253 createPort(portDirection: QShaderNodePort::Input, portName: "fragColor")
254 });
255 fragColor.addRule(format: openGLES2, rule: QShaderNode::Rule("gl_fragColor = $fragColor;"));
256 fragColor.addRule(format: openGL4, rule: QShaderNode::Rule("fragColor = $fragColor;",
257 QByteArrayList() << "out vec4 fragColor;"));
258 protos.insert(QStringLiteral("fragColor"), avalue: fragColor);
259
260 auto lightModel = createNode(ports: {
261 createPort(portDirection: QShaderNodePort::Input, portName: "baseColor"),
262 createPort(portDirection: QShaderNodePort::Input, portName: "position"),
263 createPort(portDirection: QShaderNodePort::Input, portName: "lightIntensity"),
264 createPort(portDirection: QShaderNodePort::Output, portName: "outputColor")
265 });
266 lightModel.addRule(format: openGLES2Extended, rule: QShaderNode::Rule("highp vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);",
267 QByteArrayList() << "#pragma include es2/lightmodel.frag.inc"));
268 lightModel.addRule(format: openGL3, rule: QShaderNode::Rule("vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);",
269 QByteArrayList() << "#pragma include gl3/lightmodel.frag.inc"));
270 protos.insert(akey: "lightModel", avalue: lightModel);
271
272 return protos;
273 }();
274
275 QTest::newRow(dataTag: "NotOpen") << createBuffer(data: smallJson, openMode: QIODevice::NotOpen) << NodeHash() << QShaderNodesLoader::Error;
276 QTest::newRow(dataTag: "CorrectJSON") << createBuffer(data: smallJson) << smallProtos << QShaderNodesLoader::Ready;
277}
278
279void tst_QShaderNodesLoader::shouldLoadFromJsonStream()
280{
281 // GIVEN
282 QFETCH(QBufferPointer, device);
283
284 auto loader = QShaderNodesLoader();
285
286 // WHEN
287 loader.setDevice(device.data());
288 loader.load();
289
290 // THEN
291 QFETCH(QShaderNodesLoader::Status, status);
292 QCOMPARE(loader.status(), status);
293
294 QFETCH(NodeHash, nodes);
295 const auto sortedKeys = [](const NodeHash &nodes) {
296 auto res = nodes.keys();
297 res.sort();
298 return res;
299 };
300 const auto sortedParameters = [](const QShaderNode &node) {
301 auto res = node.parameterNames();
302 res.sort();
303 return res;
304 };
305 QCOMPARE(sortedKeys(loader.nodes()), sortedKeys(nodes));
306 for (const auto &key : nodes.keys()) {
307 const auto actual = loader.nodes().value(akey: key);
308 const auto expected = nodes.value(akey: key);
309
310 QVERIFY(actual.uuid().isNull());
311 QCOMPARE(actual.ports(), expected.ports());
312 QCOMPARE(sortedParameters(actual), sortedParameters(expected));
313 for (const auto &name : expected.parameterNames()) {
314 QCOMPARE(actual.parameter(name), expected.parameter(name));
315 }
316 QCOMPARE(actual.availableFormats(), expected.availableFormats());
317 for (const auto &format : expected.availableFormats()) {
318 QCOMPARE(actual.rule(format), expected.rule(format));
319 }
320 }
321}
322
323QTEST_MAIN(tst_QShaderNodesLoader)
324
325#include "tst_qshadernodesloader.moc"
326

source code of qt3d/tests/auto/render/shadergraph/qshadernodesloader/tst_qshadernodesloader.cpp