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/qshadergraphloader_p.h>
35#include <Qt3DRender/private/qshaderlanguage_p.h>
36
37using namespace Qt3DRender;
38
39using QBufferPointer = QSharedPointer<QBuffer>;
40Q_DECLARE_METATYPE(QBufferPointer);
41
42using PrototypeHash = QHash<QString, QShaderNode>;
43Q_DECLARE_METATYPE(PrototypeHash);
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 {
58 auto format = QShaderFormat();
59 format.setApi(api);
60 format.setVersion(QVersionNumber(majorVersion, minorVersion));
61 return format;
62 }
63
64 QShaderNodePort createPort(QShaderNodePort::Direction portDirection, const QString &portName)
65 {
66 auto port = QShaderNodePort();
67 port.direction = portDirection;
68 port.name = portName;
69 return port;
70 }
71
72 QShaderNode createNode(const QVector<QShaderNodePort> &ports, const QStringList &layers = QStringList())
73 {
74 auto node = QShaderNode();
75 node.setUuid(QUuid::createUuid());
76 node.setLayers(layers);
77 for (const auto &port : ports)
78 node.addPort(port);
79 return node;
80 }
81
82 QShaderGraph::Edge createEdge(const QUuid &sourceUuid, const QString &sourceName,
83 const QUuid &targetUuid, const QString &targetName,
84 const QStringList &layers = QStringList())
85 {
86 auto edge = QShaderGraph::Edge();
87 edge.sourceNodeUuid = sourceUuid;
88 edge.sourcePortName = sourceName;
89 edge.targetNodeUuid = targetUuid;
90 edge.targetPortName = targetName;
91 edge.layers = layers;
92 return edge;
93 }
94
95 QShaderGraph createGraph()
96 {
97 const auto openGLES2 = createFormat(api: QShaderFormat::OpenGLES, majorVersion: 2, minorVersion: 0);
98 const auto openGL3 = createFormat(api: QShaderFormat::OpenGLCoreProfile, majorVersion: 3, minorVersion: 0);
99
100 auto graph = QShaderGraph();
101
102 auto worldPosition = createNode(ports: {
103 createPort(portDirection: QShaderNodePort::Output, portName: "value")
104 });
105 worldPosition.setUuid(QUuid("{00000000-0000-0000-0000-000000000001}"));
106 worldPosition.setParameter(name: "name", value: "worldPosition");
107 worldPosition.setParameter(name: "qualifier", value: QVariant::fromValue<QShaderLanguage::StorageQualifier>(value: QShaderLanguage::Input));
108 worldPosition.setParameter(name: "type", value: QVariant::fromValue<QShaderLanguage::VariableType>(value: QShaderLanguage::Vec3));
109 worldPosition.addRule(format: openGLES2, rule: QShaderNode::Rule("highp $type $value = $name;",
110 QByteArrayList() << "$qualifier highp $type $name;"));
111 worldPosition.addRule(format: openGL3, rule: QShaderNode::Rule("$type $value = $name;",
112 QByteArrayList() << "$qualifier $type $name;"));
113
114 auto texture = createNode(ports: {
115 createPort(portDirection: QShaderNodePort::Output, portName: "texture")
116 });
117 texture.setUuid(QUuid("{00000000-0000-0000-0000-000000000002}"));
118 texture.addRule(format: openGLES2, rule: QShaderNode::Rule("sampler2D $texture = texture;",
119 QByteArrayList() << "uniform sampler2D texture;"));
120 texture.addRule(format: openGL3, rule: QShaderNode::Rule("sampler2D $texture = texture;",
121 QByteArrayList() << "uniform sampler2D texture;"));
122
123 auto texCoord = createNode(ports: {
124 createPort(portDirection: QShaderNodePort::Output, portName: "texCoord")
125 });
126 texCoord.setUuid(QUuid("{00000000-0000-0000-0000-000000000003}"));
127 texCoord.addRule(format: openGLES2, rule: QShaderNode::Rule("highp vec2 $texCoord = texCoord;",
128 QByteArrayList() << "varying highp vec2 texCoord;"));
129 texCoord.addRule(format: openGL3, rule: QShaderNode::Rule("vec2 $texCoord = texCoord;",
130 QByteArrayList() << "in vec2 texCoord;"));
131
132 auto lightIntensity = createNode(ports: {
133 createPort(portDirection: QShaderNodePort::Output, portName: "value")
134 });
135 lightIntensity.setUuid(QUuid("{00000000-0000-0000-0000-000000000004}"));
136 lightIntensity.setParameter(name: "name", value: "defaultName");
137 lightIntensity.setParameter(name: "qualifier", value: QVariant::fromValue<QShaderLanguage::StorageQualifier>(value: QShaderLanguage::Uniform));
138 lightIntensity.setParameter(name: "type", value: QVariant::fromValue<QShaderLanguage::VariableType>(value: QShaderLanguage::Float));
139 lightIntensity.addRule(format: openGLES2, rule: QShaderNode::Rule("highp $type $value = $name;",
140 QByteArrayList() << "$qualifier highp $type $name;"));
141 lightIntensity.addRule(format: openGL3, rule: QShaderNode::Rule("$type $value = $name;",
142 QByteArrayList() << "$qualifier $type $name;"));
143
144 auto exposure = createNode(ports: {
145 createPort(portDirection: QShaderNodePort::Output, portName: "exposure")
146 });
147 exposure.setUuid(QUuid("{00000000-0000-0000-0000-000000000005}"));
148 exposure.addRule(format: openGLES2, rule: QShaderNode::Rule("highp float $exposure = exposure;",
149 QByteArrayList() << "uniform highp float exposure;"));
150 exposure.addRule(format: openGL3, rule: QShaderNode::Rule("float $exposure = exposure;",
151 QByteArrayList() << "uniform float exposure;"));
152
153 auto fragColor = createNode(ports: {
154 createPort(portDirection: QShaderNodePort::Input, portName: "fragColor")
155 });
156 fragColor.setUuid(QUuid("{00000000-0000-0000-0000-000000000006}"));
157 fragColor.addRule(format: openGLES2, rule: QShaderNode::Rule("gl_fragColor = $fragColor;"));
158 fragColor.addRule(format: openGL3, rule: QShaderNode::Rule("fragColor = $fragColor;",
159 QByteArrayList() << "out vec4 fragColor;"));
160
161 auto sampleTexture = createNode(ports: {
162 createPort(portDirection: QShaderNodePort::Input, portName: "sampler"),
163 createPort(portDirection: QShaderNodePort::Input, portName: "coord"),
164 createPort(portDirection: QShaderNodePort::Output, portName: "color")
165 });
166 sampleTexture.setUuid(QUuid("{00000000-0000-0000-0000-000000000007}"));
167 sampleTexture.addRule(format: openGLES2, rule: QShaderNode::Rule("highp vec4 $color = texture2D($sampler, $coord);"));
168 sampleTexture.addRule(format: openGL3, rule: QShaderNode::Rule("vec4 $color = texture2D($sampler, $coord);"));
169
170 auto lightFunction = createNode(ports: {
171 createPort(portDirection: QShaderNodePort::Input, portName: "baseColor"),
172 createPort(portDirection: QShaderNodePort::Input, portName: "position"),
173 createPort(portDirection: QShaderNodePort::Input, portName: "lightIntensity"),
174 createPort(portDirection: QShaderNodePort::Output, portName: "outputColor")
175 });
176 lightFunction.setUuid(QUuid("{00000000-0000-0000-0000-000000000008}"));
177 lightFunction.addRule(format: openGLES2, rule: QShaderNode::Rule("highp vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);",
178 QByteArrayList() << "#pragma include es2/lightmodel.frag.inc"));
179 lightFunction.addRule(format: openGL3, rule: QShaderNode::Rule("vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);",
180 QByteArrayList() << "#pragma include gl3/lightmodel.frag.inc"));
181
182 auto exposureFunction = createNode(ports: {
183 createPort(portDirection: QShaderNodePort::Input, portName: "inputColor"),
184 createPort(portDirection: QShaderNodePort::Input, portName: "exposure"),
185 createPort(portDirection: QShaderNodePort::Output, portName: "outputColor")
186 });
187 exposureFunction.setUuid(QUuid("{00000000-0000-0000-0000-000000000009}"));
188 exposureFunction.addRule(format: openGLES2, rule: QShaderNode::Rule("highp vec4 $outputColor = $inputColor * pow(2.0, $exposure);"));
189 exposureFunction.addRule(format: openGL3, rule: QShaderNode::Rule("vec4 $outputColor = $inputColor * pow(2.0, $exposure);"));
190
191 graph.addNode(node: worldPosition);
192 graph.addNode(node: texture);
193 graph.addNode(node: texCoord);
194 graph.addNode(node: lightIntensity);
195 graph.addNode(node: exposure);
196 graph.addNode(node: fragColor);
197 graph.addNode(node: sampleTexture);
198 graph.addNode(node: lightFunction);
199 graph.addNode(node: exposureFunction);
200
201 graph.addEdge(edge: createEdge(sourceUuid: texture.uuid(), sourceName: "texture", targetUuid: sampleTexture.uuid(), targetName: "sampler"));
202 graph.addEdge(edge: createEdge(sourceUuid: texCoord.uuid(), sourceName: "texCoord", targetUuid: sampleTexture.uuid(), targetName: "coord"));
203
204 graph.addEdge(edge: createEdge(sourceUuid: worldPosition.uuid(), sourceName: "value", targetUuid: lightFunction.uuid(), targetName: "position"));
205 graph.addEdge(edge: createEdge(sourceUuid: sampleTexture.uuid(), sourceName: "color", targetUuid: lightFunction.uuid(), targetName: "baseColor"));
206 graph.addEdge(edge: createEdge(sourceUuid: lightIntensity.uuid(), sourceName: "value", targetUuid: lightFunction.uuid(), targetName: "lightIntensity"));
207
208 graph.addEdge(edge: createEdge(sourceUuid: lightFunction.uuid(), sourceName: "outputColor", targetUuid: exposureFunction.uuid(), targetName: "inputColor"));
209 graph.addEdge(edge: createEdge(sourceUuid: exposure.uuid(), sourceName: "exposure", targetUuid: exposureFunction.uuid(), targetName: "exposure"));
210
211 graph.addEdge(edge: createEdge(sourceUuid: exposureFunction.uuid(), sourceName: "outputColor", targetUuid: fragColor.uuid(), targetName: "fragColor"));
212
213 return graph;
214 }
215
216 void debugStatement(const QString &prefix, const QShaderGraph::Statement &statement)
217 {
218 qDebug() << prefix << statement.inputs << statement.uuid().toString() << statement.outputs;
219 }
220
221 void dumpStatementsIfNeeded(const QVector<QShaderGraph::Statement> &statements, const QVector<QShaderGraph::Statement> &expected)
222 {
223 if (statements != expected) {
224 for (int i = 0; i < qMax(a: statements.size(), b: expected.size()); i++) {
225 qDebug() << "----" << i << "----";
226 if (i < statements.size())
227 debugStatement(prefix: "A:", statement: statements.at(i));
228 if (i < expected.size())
229 debugStatement(prefix: "E:", statement: expected.at(i));
230 qDebug() << "-----------";
231 }
232 }
233 }
234}
235
236class tst_QShaderGraphLoader : public QObject
237{
238 Q_OBJECT
239private slots:
240 void shouldManipulateLoaderMembers();
241 void shouldLoadFromJsonStream_data();
242 void shouldLoadFromJsonStream();
243};
244
245void tst_QShaderGraphLoader::shouldManipulateLoaderMembers()
246{
247 // GIVEN
248 auto loader = QShaderGraphLoader();
249
250 // THEN (default state)
251 QCOMPARE(loader.status(), QShaderGraphLoader::Null);
252 QVERIFY(!loader.device());
253 QVERIFY(loader.graph().nodes().isEmpty());
254 QVERIFY(loader.graph().edges().isEmpty());
255 QVERIFY(loader.prototypes().isEmpty());
256
257 // WHEN
258 auto device1 = createBuffer(data: QByteArray("..........."), openMode: QIODevice::NotOpen);
259 loader.setDevice(device1.data());
260
261 // THEN
262 QCOMPARE(loader.status(), QShaderGraphLoader::Error);
263 QCOMPARE(loader.device(), device1.data());
264 QVERIFY(loader.graph().nodes().isEmpty());
265 QVERIFY(loader.graph().edges().isEmpty());
266
267 // WHEN
268 auto device2 = createBuffer(data: QByteArray("..........."), openMode: QIODevice::ReadOnly);
269 loader.setDevice(device2.data());
270
271 // THEN
272 QCOMPARE(loader.status(), QShaderGraphLoader::Waiting);
273 QCOMPARE(loader.device(), device2.data());
274 QVERIFY(loader.graph().nodes().isEmpty());
275 QVERIFY(loader.graph().edges().isEmpty());
276
277
278 // WHEN
279 const auto prototypes = [this]{
280 auto res = QHash<QString, QShaderNode>();
281 res.insert(akey: "foo", avalue: createNode(ports: {}));
282 return res;
283 }();
284 loader.setPrototypes(prototypes);
285
286 // THEN
287 QCOMPARE(loader.prototypes().size(), prototypes.size());
288 QVERIFY(loader.prototypes().contains("foo"));
289 QCOMPARE(loader.prototypes().value("foo").uuid(), prototypes.value("foo").uuid());
290}
291
292void tst_QShaderGraphLoader::shouldLoadFromJsonStream_data()
293{
294 QTest::addColumn<QBufferPointer>(name: "device");
295 QTest::addColumn<PrototypeHash>(name: "prototypes");
296 QTest::addColumn<QShaderGraph>(name: "graph");
297 QTest::addColumn<QShaderGraphLoader::Status>(name: "status");
298
299 QTest::newRow(dataTag: "empty") << createBuffer(data: "", openMode: QIODevice::ReadOnly) << PrototypeHash()
300 << QShaderGraph() << QShaderGraphLoader::Error;
301
302 const auto smallJson = "{"
303 " \"nodes\": ["
304 " {"
305 " \"uuid\": \"{00000000-0000-0000-0000-000000000001}\","
306 " \"type\": \"MyInput\","
307 " \"layers\": [\"foo\", \"bar\"]"
308 " },"
309 " {"
310 " \"uuid\": \"{00000000-0000-0000-0000-000000000002}\","
311 " \"type\": \"MyOutput\""
312 " },"
313 " {"
314 " \"uuid\": \"{00000000-0000-0000-0000-000000000003}\","
315 " \"type\": \"MyFunction\""
316 " }"
317 " ],"
318 " \"edges\": ["
319 " {"
320 " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000001}\","
321 " \"sourcePort\": \"input\","
322 " \"targetUuid\": \"{00000000-0000-0000-0000-000000000003}\","
323 " \"targetPort\": \"functionInput\","
324 " \"layers\": [\"bar\", \"baz\"]"
325 " },"
326 " {"
327 " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000003}\","
328 " \"sourcePort\": \"functionOutput\","
329 " \"targetUuid\": \"{00000000-0000-0000-0000-000000000002}\","
330 " \"targetPort\": \"output\""
331 " }"
332 " ]"
333 "}";
334
335 const auto smallProtos = [this]{
336 auto protos = PrototypeHash();
337
338 auto input = createNode(ports: {
339 createPort(portDirection: QShaderNodePort::Output, portName: "input")
340 });
341 protos.insert(akey: "MyInput", avalue: input);
342
343 auto output = createNode(ports: {
344 createPort(portDirection: QShaderNodePort::Input, portName: "output")
345 });
346 protos.insert(akey: "MyOutput", avalue: output);
347
348 auto function = createNode(ports: {
349 createPort(portDirection: QShaderNodePort::Input, portName: "functionInput"),
350 createPort(portDirection: QShaderNodePort::Output, portName: "functionOutput")
351 });
352 protos.insert(akey: "MyFunction", avalue: function);
353 return protos;
354 }();
355
356 const auto smallGraph = [this]{
357 auto graph = QShaderGraph();
358
359 auto input = createNode(ports: {
360 createPort(portDirection: QShaderNodePort::Output, portName: "input")
361 }, layers: {"foo", "bar"});
362 input.setUuid(QUuid("{00000000-0000-0000-0000-000000000001}"));
363 auto output = createNode(ports: {
364 createPort(portDirection: QShaderNodePort::Input, portName: "output")
365 });
366 output.setUuid(QUuid("{00000000-0000-0000-0000-000000000002}"));
367 auto function = createNode(ports: {
368 createPort(portDirection: QShaderNodePort::Input, portName: "functionInput"),
369 createPort(portDirection: QShaderNodePort::Output, portName: "functionOutput")
370 });
371 function.setUuid(QUuid("{00000000-0000-0000-0000-000000000003}"));
372
373 graph.addNode(node: input);
374 graph.addNode(node: output);
375 graph.addNode(node: function);
376 graph.addEdge(edge: createEdge(sourceUuid: input.uuid(), sourceName: "input", targetUuid: function.uuid(), targetName: "functionInput", layers: {"bar", "baz"}));
377 graph.addEdge(edge: createEdge(sourceUuid: function.uuid(), sourceName: "functionOutput", targetUuid: output.uuid(), targetName: "output"));
378
379 return graph;
380 }();
381
382 QTest::newRow(dataTag: "TwoNodesOneEdge") << createBuffer(data: smallJson) << smallProtos << smallGraph << QShaderGraphLoader::Ready;
383 QTest::newRow(dataTag: "NotOpen") << createBuffer(data: smallJson, openMode: QIODevice::NotOpen) << smallProtos << QShaderGraph() << QShaderGraphLoader::Error;
384 QTest::newRow(dataTag: "NoPrototype") << createBuffer(data: smallJson) << PrototypeHash() << QShaderGraph() << QShaderGraphLoader::Error;
385
386 const auto complexJson = "{"
387 " \"nodes\": ["
388 " {"
389 " \"uuid\": \"{00000000-0000-0000-0000-000000000001}\","
390 " \"type\": \"inputValue\","
391 " \"parameters\": {"
392 " \"name\": \"worldPosition\","
393 " \"qualifier\": {"
394 " \"type\": \"QShaderLanguage::StorageQualifier\","
395 " \"value\": \"QShaderLanguage::Input\""
396 " },"
397 " \"type\": {"
398 " \"type\": \"QShaderLanguage::VariableType\","
399 " \"value\": \"QShaderLanguage::Vec3\""
400 " }"
401 " }"
402 " },"
403 " {"
404 " \"uuid\": \"{00000000-0000-0000-0000-000000000002}\","
405 " \"type\": \"texture\""
406 " },"
407 " {"
408 " \"uuid\": \"{00000000-0000-0000-0000-000000000003}\","
409 " \"type\": \"texCoord\""
410 " },"
411 " {"
412 " \"uuid\": \"{00000000-0000-0000-0000-000000000004}\","
413 " \"type\": \"inputValue\""
414 " },"
415 " {"
416 " \"uuid\": \"{00000000-0000-0000-0000-000000000005}\","
417 " \"type\": \"exposure\""
418 " },"
419 " {"
420 " \"uuid\": \"{00000000-0000-0000-0000-000000000006}\","
421 " \"type\": \"fragColor\""
422 " },"
423 " {"
424 " \"uuid\": \"{00000000-0000-0000-0000-000000000007}\","
425 " \"type\": \"sampleTexture\""
426 " },"
427 " {"
428 " \"uuid\": \"{00000000-0000-0000-0000-000000000008}\","
429 " \"type\": \"lightModel\""
430 " },"
431 " {"
432 " \"uuid\": \"{00000000-0000-0000-0000-000000000009}\","
433 " \"type\": \"exposureFunction\""
434 " }"
435 " ],"
436 " \"edges\": ["
437 " {"
438 " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000002}\","
439 " \"sourcePort\": \"texture\","
440 " \"targetUuid\": \"{00000000-0000-0000-0000-000000000007}\","
441 " \"targetPort\": \"sampler\""
442 " },"
443 " {"
444 " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000003}\","
445 " \"sourcePort\": \"texCoord\","
446 " \"targetUuid\": \"{00000000-0000-0000-0000-000000000007}\","
447 " \"targetPort\": \"coord\""
448 " },"
449 " {"
450 " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000001}\","
451 " \"sourcePort\": \"value\","
452 " \"targetUuid\": \"{00000000-0000-0000-0000-000000000008}\","
453 " \"targetPort\": \"position\""
454 " },"
455 " {"
456 " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000007}\","
457 " \"sourcePort\": \"color\","
458 " \"targetUuid\": \"{00000000-0000-0000-0000-000000000008}\","
459 " \"targetPort\": \"baseColor\""
460 " },"
461 " {"
462 " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000004}\","
463 " \"sourcePort\": \"value\","
464 " \"targetUuid\": \"{00000000-0000-0000-0000-000000000008}\","
465 " \"targetPort\": \"lightIntensity\""
466 " },"
467 " {"
468 " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000008}\","
469 " \"sourcePort\": \"outputColor\","
470 " \"targetUuid\": \"{00000000-0000-0000-0000-000000000009}\","
471 " \"targetPort\": \"inputColor\""
472 " },"
473 " {"
474 " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000005}\","
475 " \"sourcePort\": \"exposure\","
476 " \"targetUuid\": \"{00000000-0000-0000-0000-000000000009}\","
477 " \"targetPort\": \"exposure\""
478 " },"
479 " {"
480 " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000009}\","
481 " \"sourcePort\": \"outputColor\","
482 " \"targetUuid\": \"{00000000-0000-0000-0000-000000000006}\","
483 " \"targetPort\": \"fragColor\""
484 " }"
485 " ]"
486 "}";
487
488 const auto complexProtos = [this]{
489 const auto openGLES2 = createFormat(api: QShaderFormat::OpenGLES, majorVersion: 2, minorVersion: 0);
490 const auto openGL3 = createFormat(api: QShaderFormat::OpenGLCoreProfile, majorVersion: 3, minorVersion: 0);
491
492 auto protos = PrototypeHash();
493
494 auto inputValue = createNode(ports: {
495 createPort(portDirection: QShaderNodePort::Output, portName: "value")
496 });
497 inputValue.setParameter(name: "name", value: "defaultName");
498 inputValue.setParameter(name: "qualifier", value: QVariant::fromValue<QShaderLanguage::StorageQualifier>(value: QShaderLanguage::Uniform));
499 inputValue.setParameter(name: "type", value: QVariant::fromValue<QShaderLanguage::VariableType>(value: QShaderLanguage::Float));
500 inputValue.addRule(format: openGLES2, rule: QShaderNode::Rule("highp $type $value = $name;",
501 QByteArrayList() << "$qualifier highp $type $name;"));
502 inputValue.addRule(format: openGL3, rule: QShaderNode::Rule("$type $value = $name;",
503 QByteArrayList() << "$qualifier $type $name;"));
504 protos.insert(akey: "inputValue", avalue: inputValue);
505
506 auto texture = createNode(ports: {
507 createPort(portDirection: QShaderNodePort::Output, portName: "texture")
508 });
509 texture.addRule(format: openGLES2, rule: QShaderNode::Rule("sampler2D $texture = texture;",
510 QByteArrayList() << "uniform sampler2D texture;"));
511 texture.addRule(format: openGL3, rule: QShaderNode::Rule("sampler2D $texture = texture;",
512 QByteArrayList() << "uniform sampler2D texture;"));
513 protos.insert(akey: "texture", avalue: texture);
514
515 auto texCoord = createNode(ports: {
516 createPort(portDirection: QShaderNodePort::Output, portName: "texCoord")
517 });
518 texCoord.addRule(format: openGLES2, rule: QShaderNode::Rule("highp vec2 $texCoord = texCoord;",
519 QByteArrayList() << "varying highp vec2 texCoord;"));
520 texCoord.addRule(format: openGL3, rule: QShaderNode::Rule("vec2 $texCoord = texCoord;",
521 QByteArrayList() << "in vec2 texCoord;"));
522 protos.insert(akey: "texCoord", avalue: texCoord);
523
524 auto exposure = createNode(ports: {
525 createPort(portDirection: QShaderNodePort::Output, portName: "exposure")
526 });
527 exposure.addRule(format: openGLES2, rule: QShaderNode::Rule("highp float $exposure = exposure;",
528 QByteArrayList() << "uniform highp float exposure;"));
529 exposure.addRule(format: openGL3, rule: QShaderNode::Rule("float $exposure = exposure;",
530 QByteArrayList() << "uniform float exposure;"));
531 protos.insert(akey: "exposure", avalue: exposure);
532
533 auto fragColor = createNode(ports: {
534 createPort(portDirection: QShaderNodePort::Input, portName: "fragColor")
535 });
536 fragColor.addRule(format: openGLES2, rule: QShaderNode::Rule("gl_fragColor = $fragColor;"));
537 fragColor.addRule(format: openGL3, rule: QShaderNode::Rule("fragColor = $fragColor;",
538 QByteArrayList() << "out vec4 fragColor;"));
539 protos.insert(akey: "fragColor", avalue: fragColor);
540
541 auto sampleTexture = createNode(ports: {
542 createPort(portDirection: QShaderNodePort::Input, portName: "sampler"),
543 createPort(portDirection: QShaderNodePort::Input, portName: "coord"),
544 createPort(portDirection: QShaderNodePort::Output, portName: "color")
545 });
546 sampleTexture.addRule(format: openGLES2, rule: QShaderNode::Rule("highp vec4 $color = texture2D($sampler, $coord);"));
547 sampleTexture.addRule(format: openGL3, rule: QShaderNode::Rule("vec4 $color = texture2D($sampler, $coord);"));
548 protos.insert(akey: "sampleTexture", avalue: sampleTexture);
549
550 auto lightModel = createNode(ports: {
551 createPort(portDirection: QShaderNodePort::Input, portName: "baseColor"),
552 createPort(portDirection: QShaderNodePort::Input, portName: "position"),
553 createPort(portDirection: QShaderNodePort::Input, portName: "lightIntensity"),
554 createPort(portDirection: QShaderNodePort::Output, portName: "outputColor")
555 });
556 lightModel.addRule(format: openGLES2, rule: QShaderNode::Rule("highp vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);",
557 QByteArrayList() << "#pragma include es2/lightmodel.frag.inc"));
558 lightModel.addRule(format: openGL3, rule: QShaderNode::Rule("vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);",
559 QByteArrayList() << "#pragma include gl3/lightmodel.frag.inc"));
560 protos.insert(akey: "lightModel", avalue: lightModel);
561
562 auto exposureFunction = createNode(ports: {
563 createPort(portDirection: QShaderNodePort::Input, portName: "inputColor"),
564 createPort(portDirection: QShaderNodePort::Input, portName: "exposure"),
565 createPort(portDirection: QShaderNodePort::Output, portName: "outputColor")
566 });
567 exposureFunction.addRule(format: openGLES2, rule: QShaderNode::Rule("highp vec4 $outputColor = $inputColor * pow(2.0, $exposure);"));
568 exposureFunction.addRule(format: openGL3, rule: QShaderNode::Rule("vec4 $outputColor = $inputColor * pow(2.0, $exposure);"));
569 protos.insert(akey: "exposureFunction", avalue: exposureFunction);
570
571 return protos;
572 }();
573
574 const auto complexGraph = createGraph();
575
576 QTest::newRow(dataTag: "ComplexGraph") << createBuffer(data: complexJson) << complexProtos << complexGraph << QShaderGraphLoader::Ready;
577}
578
579void tst_QShaderGraphLoader::shouldLoadFromJsonStream()
580{
581 // GIVEN
582 QFETCH(QBufferPointer, device);
583 QFETCH(PrototypeHash, prototypes);
584
585 auto loader = QShaderGraphLoader();
586
587 // WHEN
588 loader.setPrototypes(prototypes);
589 loader.setDevice(device.data());
590 loader.load();
591
592 // THEN
593 QFETCH(QShaderGraphLoader::Status, status);
594 QCOMPARE(loader.status(), status);
595
596 QFETCH(QShaderGraph, graph);
597 const auto statements = loader.graph().createStatements(enabledLayers: {"foo", "bar", "baz"});
598 const auto expected = graph.createStatements(enabledLayers: {"foo", "bar", "baz"});
599 dumpStatementsIfNeeded(statements, expected);
600 QCOMPARE(statements, expected);
601
602 const auto sortedParameters = [](const QShaderNode &node) {
603 auto res = node.parameterNames();
604 res.sort();
605 return res;
606 };
607
608 for (int i = 0; i < statements.size(); i++) {
609 const auto actualNode = statements.at(i).node;
610 const auto expectedNode = expected.at(i).node;
611
612 QCOMPARE(actualNode.layers(), expectedNode.layers());
613 QCOMPARE(actualNode.ports(), expectedNode.ports());
614 QCOMPARE(sortedParameters(actualNode), sortedParameters(expectedNode));
615 for (const auto &name : expectedNode.parameterNames()) {
616 QCOMPARE(actualNode.parameter(name), expectedNode.parameter(name));
617 }
618 QCOMPARE(actualNode.availableFormats(), expectedNode.availableFormats());
619 for (const auto &format : expectedNode.availableFormats()) {
620 QCOMPARE(actualNode.rule(format), expectedNode.rule(format));
621 }
622 }
623}
624
625QTEST_MAIN(tst_QShaderGraphLoader)
626
627#include "tst_qshadergraphloader.moc"
628

source code of qt3d/tests/auto/render/shadergraph/qshadergraphloader/tst_qshadergraphloader.cpp