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 <Qt3DRender/private/qshadergraph_p.h>
33
34using namespace Qt3DRender;
35namespace
36{
37 QShaderNodePort createPort(QShaderNodePort::Direction portDirection, const QString &portName)
38 {
39 auto port = QShaderNodePort();
40 port.direction = portDirection;
41 port.name = portName;
42 return port;
43 }
44
45 QShaderNode createNode(const QVector<QShaderNodePort> &ports, const QStringList &layers = QStringList())
46 {
47 auto node = QShaderNode();
48 node.setUuid(QUuid::createUuid());
49 node.setLayers(layers);
50 for (const auto &port : ports)
51 node.addPort(port);
52 return node;
53 }
54
55 QShaderGraph::Edge createEdge(const QUuid &sourceUuid, const QString &sourceName,
56 const QUuid &targetUuid, const QString &targetName,
57 const QStringList &layers = QStringList())
58 {
59 auto edge = QShaderGraph::Edge();
60 edge.sourceNodeUuid = sourceUuid;
61 edge.sourcePortName = sourceName;
62 edge.targetNodeUuid = targetUuid;
63 edge.targetPortName = targetName;
64 edge.layers = layers;
65 return edge;
66 }
67
68 QShaderGraph::Statement createStatement(const QShaderNode &node,
69 const QVector<int> &inputs = QVector<int>(),
70 const QVector<int> &outputs = QVector<int>())
71 {
72 auto statement = QShaderGraph::Statement();
73 statement.node = node;
74 statement.inputs = inputs;
75 statement.outputs = outputs;
76 return statement;
77 }
78
79 void debugStatement(const QString &prefix, const QShaderGraph::Statement &statement)
80 {
81 qDebug() << prefix << statement.inputs << statement.uuid().toString() << statement.outputs;
82 }
83
84 void dumpStatementsIfNeeded(const QVector<QShaderGraph::Statement> &statements, const QVector<QShaderGraph::Statement> &expected)
85 {
86 if (statements != expected) {
87 for (int i = 0; i < qMax(a: statements.size(), b: expected.size()); i++) {
88 qDebug() << "----" << i << "----";
89 if (i < statements.size())
90 debugStatement(prefix: "A:", statement: statements.at(i));
91 if (i < expected.size())
92 debugStatement(prefix: "E:", statement: expected.at(i));
93 qDebug() << "-----------";
94 }
95 }
96 }
97}
98
99class tst_QShaderGraph : public QObject
100{
101 Q_OBJECT
102private slots:
103 void shouldHaveEdgeDefaultState();
104 void shouldTestEdgesEquality_data();
105 void shouldTestEdgesEquality();
106 void shouldManipulateStatementMembers();
107 void shouldTestStatementsEquality_data();
108 void shouldTestStatementsEquality();
109 void shouldFindIndexFromPortNameInStatements_data();
110 void shouldFindIndexFromPortNameInStatements();
111 void shouldManageNodeList();
112 void shouldManageEdgeList();
113 void shouldSerializeGraphForCodeGeneration();
114 void shouldHandleUnboundPortsDuringGraphSerialization();
115 void shouldSurviveCyclesDuringGraphSerialization();
116 void shouldDealWithEdgesJumpingOverLayers();
117 void shouldGenerateDifferentStatementsDependingOnActiveLayers();
118 void shouldDealWithBranchesWithoutOutput();
119 void shouldDiscardEdgesConnectedToDiscardedNodes();
120};
121
122void tst_QShaderGraph::shouldHaveEdgeDefaultState()
123{
124 // GIVEN
125 auto edge = QShaderGraph::Edge();
126
127 // THEN
128 QVERIFY(edge.sourceNodeUuid.isNull());
129 QVERIFY(edge.sourcePortName.isEmpty());
130 QVERIFY(edge.targetNodeUuid.isNull());
131 QVERIFY(edge.targetPortName.isEmpty());
132}
133
134void tst_QShaderGraph::shouldTestEdgesEquality_data()
135{
136 QTest::addColumn<QShaderGraph::Edge>(name: "left");
137 QTest::addColumn<QShaderGraph::Edge>(name: "right");
138 QTest::addColumn<bool>(name: "expected");
139
140 const auto sourceUuid1 = QUuid::createUuid();
141 const auto sourceUuid2 = QUuid::createUuid();
142 const auto targetUuid1 = QUuid::createUuid();
143 const auto targetUuid2 = QUuid::createUuid();
144
145 QTest::newRow(dataTag: "Equals") << createEdge(sourceUuid: sourceUuid1, sourceName: "foo", targetUuid: targetUuid1, targetName: "bar")
146 << createEdge(sourceUuid: sourceUuid1, sourceName: "foo", targetUuid: targetUuid1, targetName: "bar")
147 << true;
148 QTest::newRow(dataTag: "SourceUuid") << createEdge(sourceUuid: sourceUuid1, sourceName: "foo", targetUuid: targetUuid1, targetName: "bar")
149 << createEdge(sourceUuid: sourceUuid2, sourceName: "foo", targetUuid: targetUuid1, targetName: "bar")
150 << false;
151 QTest::newRow(dataTag: "SourceName") << createEdge(sourceUuid: sourceUuid1, sourceName: "foo", targetUuid: targetUuid1, targetName: "bar")
152 << createEdge(sourceUuid: sourceUuid1, sourceName: "bleh", targetUuid: targetUuid1, targetName: "bar")
153 << false;
154 QTest::newRow(dataTag: "TargetUuid") << createEdge(sourceUuid: sourceUuid1, sourceName: "foo", targetUuid: targetUuid1, targetName: "bar")
155 << createEdge(sourceUuid: sourceUuid1, sourceName: "foo", targetUuid: targetUuid2, targetName: "bar")
156 << false;
157 QTest::newRow(dataTag: "TargetName") << createEdge(sourceUuid: sourceUuid1, sourceName: "foo", targetUuid: targetUuid1, targetName: "bar")
158 << createEdge(sourceUuid: sourceUuid1, sourceName: "foo", targetUuid: targetUuid1, targetName: "bleh")
159 << false;
160}
161
162void tst_QShaderGraph::shouldTestEdgesEquality()
163{
164 // GIVEN
165 QFETCH(QShaderGraph::Edge, left);
166 QFETCH(QShaderGraph::Edge, right);
167
168 // WHEN
169 const auto equal = (left == right);
170 const auto notEqual = (left != right);
171
172 // THEN
173 QFETCH(bool, expected);
174 QCOMPARE(equal, expected);
175 QCOMPARE(notEqual, !expected);
176}
177
178void tst_QShaderGraph::shouldManipulateStatementMembers()
179{
180 // GIVEN
181 auto statement = QShaderGraph::Statement();
182
183 // THEN (default state)
184 QVERIFY(statement.inputs.isEmpty());
185 QVERIFY(statement.outputs.isEmpty());
186 QVERIFY(statement.node.uuid().isNull());
187 QVERIFY(statement.uuid().isNull());
188
189 // WHEN
190 const auto node = createNode(ports: {});
191 statement.node = node;
192
193 // THEN
194 QCOMPARE(statement.uuid(), node.uuid());
195
196 // WHEN
197 statement.node = QShaderNode();
198
199 // THEN
200 QVERIFY(statement.uuid().isNull());
201}
202
203void tst_QShaderGraph::shouldTestStatementsEquality_data()
204{
205 QTest::addColumn<QShaderGraph::Statement>(name: "left");
206 QTest::addColumn<QShaderGraph::Statement>(name: "right");
207 QTest::addColumn<bool>(name: "expected");
208
209 const auto node1 = createNode(ports: {});
210 const auto node2 = createNode(ports: {});
211
212 QTest::newRow(dataTag: "EqualNodes") << createStatement(node: node1, inputs: {1, 2}, outputs: {3, 4})
213 << createStatement(node: node1, inputs: {1, 2}, outputs: {3, 4})
214 << true;
215 QTest::newRow(dataTag: "EqualInvalids") << createStatement(node: QShaderNode(), inputs: {1, 2}, outputs: {3, 4})
216 << createStatement(node: QShaderNode(), inputs: {1, 2}, outputs: {3, 4})
217 << true;
218 QTest::newRow(dataTag: "Nodes") << createStatement(node: node1, inputs: {1, 2}, outputs: {3, 4})
219 << createStatement(node: node2, inputs: {1, 2}, outputs: {3, 4})
220 << false;
221 QTest::newRow(dataTag: "Inputs") << createStatement(node: node1, inputs: {1, 2}, outputs: {3, 4})
222 << createStatement(node: node1, inputs: {1, 2, 0}, outputs: {3, 4})
223 << false;
224 QTest::newRow(dataTag: "Outputs") << createStatement(node: node1, inputs: {1, 2}, outputs: {3, 4})
225 << createStatement(node: node1, inputs: {1, 2}, outputs: {3, 0, 4})
226 << false;
227}
228
229void tst_QShaderGraph::shouldTestStatementsEquality()
230{
231 // GIVEN
232 QFETCH(QShaderGraph::Statement, left);
233 QFETCH(QShaderGraph::Statement, right);
234
235 // WHEN
236 const auto equal = (left == right);
237 const auto notEqual = (left != right);
238
239 // THEN
240 QFETCH(bool, expected);
241 QCOMPARE(equal, expected);
242 QCOMPARE(notEqual, !expected);
243}
244
245void tst_QShaderGraph::shouldFindIndexFromPortNameInStatements_data()
246{
247 QTest::addColumn<QShaderGraph::Statement>(name: "statement");
248 QTest::addColumn<QString>(name: "portName");
249 QTest::addColumn<int>(name: "expectedInputIndex");
250 QTest::addColumn<int>(name: "expectedOutputIndex");
251
252 const auto inputNodeStatement = createStatement(node: createNode(ports: {
253 createPort(portDirection: QShaderNodePort::Output, portName: "input")
254 }));
255 const auto outputNodeStatement = createStatement(node: createNode(ports: {
256 createPort(portDirection: QShaderNodePort::Input, portName: "output")
257 }));
258 const auto functionNodeStatement = createStatement(node: createNode(ports: {
259 createPort(portDirection: QShaderNodePort::Input, portName: "input1"),
260 createPort(portDirection: QShaderNodePort::Output, portName: "output1"),
261 createPort(portDirection: QShaderNodePort::Input, portName: "input2"),
262 createPort(portDirection: QShaderNodePort::Output, portName: "output2"),
263 createPort(portDirection: QShaderNodePort::Output, portName: "output3"),
264 createPort(portDirection: QShaderNodePort::Input, portName: "input3")
265 }));
266
267 QTest::newRow(dataTag: "Invalid") << QShaderGraph::Statement() << "foo" << -1 << -1;
268 QTest::newRow(dataTag: "InputNodeWrongName") << inputNodeStatement << "foo" << -1 << -1;
269 QTest::newRow(dataTag: "InputNodeExistingName") << inputNodeStatement << "input" << -1 << 0;
270 QTest::newRow(dataTag: "OutputNodeWrongName") << outputNodeStatement << "foo" << -1 << -1;
271 QTest::newRow(dataTag: "OutputNodeExistingName") << outputNodeStatement << "output" << 0 << -1;
272 QTest::newRow(dataTag: "FunctionNodeWrongName") << functionNodeStatement << "foo" << -1 << -1;
273 QTest::newRow(dataTag: "FunctionNodeInput1") << functionNodeStatement << "input1" << 0 << -1;
274 QTest::newRow(dataTag: "FunctionNodeOutput1") << functionNodeStatement << "output1" << -1 << 0;
275 QTest::newRow(dataTag: "FunctionNodeInput2") << functionNodeStatement << "input2" << 1 << -1;
276 QTest::newRow(dataTag: "FunctionNodeOutput2") << functionNodeStatement << "output2" << -1 << 1;
277 QTest::newRow(dataTag: "FunctionNodeInput3") << functionNodeStatement << "input3" << 2 << -1;
278 QTest::newRow(dataTag: "FunctionNodeOutput3") << functionNodeStatement << "output3" << -1 << 2;
279}
280
281void tst_QShaderGraph::shouldFindIndexFromPortNameInStatements()
282{
283 // GIVEN
284 QFETCH(QShaderGraph::Statement, statement);
285 QFETCH(QString, portName);
286 QFETCH(int, expectedInputIndex);
287 QFETCH(int, expectedOutputIndex);
288
289 // WHEN
290 const auto inputIndex = statement.portIndex(direction: QShaderNodePort::Input, portName);
291 const auto outputIndex = statement.portIndex(direction: QShaderNodePort::Output, portName);
292
293 // THEN
294 QCOMPARE(inputIndex, expectedInputIndex);
295 QCOMPARE(outputIndex, expectedOutputIndex);
296}
297
298void tst_QShaderGraph::shouldManageNodeList()
299{
300 // GIVEN
301 const auto node1 = createNode(ports: {createPort(portDirection: QShaderNodePort::Output, portName: "node1")});
302 const auto node2 = createNode(ports: {createPort(portDirection: QShaderNodePort::Output, portName: "node2")});
303
304 auto graph = QShaderGraph();
305
306 // THEN (default state)
307 QVERIFY(graph.nodes().isEmpty());
308
309 // WHEN
310 graph.addNode(node: node1);
311
312 // THEN
313 QCOMPARE(graph.nodes().size(), 1);
314 QCOMPARE(graph.nodes().at(0).uuid(), node1.uuid());
315 QCOMPARE(graph.nodes().at(0).ports().at(0).name, node1.ports().at(0).name);
316
317 // WHEN
318 graph.addNode(node: node2);
319
320 // THEN
321 QCOMPARE(graph.nodes().size(), 2);
322 QCOMPARE(graph.nodes().at(0).uuid(), node1.uuid());
323 QCOMPARE(graph.nodes().at(0).ports().at(0).name, node1.ports().at(0).name);
324 QCOMPARE(graph.nodes().at(1).uuid(), node2.uuid());
325 QCOMPARE(graph.nodes().at(1).ports().at(0).name, node2.ports().at(0).name);
326
327
328 // WHEN
329 graph.removeNode(node: node2);
330
331 // THEN
332 QCOMPARE(graph.nodes().size(), 1);
333 QCOMPARE(graph.nodes().at(0).uuid(), node1.uuid());
334 QCOMPARE(graph.nodes().at(0).ports().at(0).name, node1.ports().at(0).name);
335
336 // WHEN
337 graph.addNode(node: node2);
338
339 // THEN
340 QCOMPARE(graph.nodes().size(), 2);
341 QCOMPARE(graph.nodes().at(0).uuid(), node1.uuid());
342 QCOMPARE(graph.nodes().at(0).ports().at(0).name, node1.ports().at(0).name);
343 QCOMPARE(graph.nodes().at(1).uuid(), node2.uuid());
344 QCOMPARE(graph.nodes().at(1).ports().at(0).name, node2.ports().at(0).name);
345
346 // WHEN
347 const auto node1bis = [node1] {
348 auto res = node1;
349 auto port = res.ports().at(i: 0);
350 port.name = QStringLiteral("node1bis");
351 res.addPort(port);
352 return res;
353 }();
354 graph.addNode(node: node1bis);
355
356 // THEN
357 QCOMPARE(graph.nodes().size(), 2);
358 QCOMPARE(graph.nodes().at(0).uuid(), node2.uuid());
359 QCOMPARE(graph.nodes().at(0).ports().at(0).name, node2.ports().at(0).name);
360 QCOMPARE(graph.nodes().at(1).uuid(), node1bis.uuid());
361 QCOMPARE(graph.nodes().at(1).ports().at(0).name, node1bis.ports().at(0).name);
362}
363
364void tst_QShaderGraph::shouldManageEdgeList()
365{
366 // GIVEN
367 const auto edge1 = createEdge(sourceUuid: QUuid::createUuid(), sourceName: "foo", targetUuid: QUuid::createUuid(), targetName: "bar");
368 const auto edge2 = createEdge(sourceUuid: QUuid::createUuid(), sourceName: "baz", targetUuid: QUuid::createUuid(), targetName: "boo");
369
370 auto graph = QShaderGraph();
371
372 // THEN (default state)
373 QVERIFY(graph.edges().isEmpty());
374
375 // WHEN
376 graph.addEdge(edge: edge1);
377
378 // THEN
379 QCOMPARE(graph.edges().size(), 1);
380 QCOMPARE(graph.edges().at(0), edge1);
381
382 // WHEN
383 graph.addEdge(edge: edge2);
384
385 // THEN
386 QCOMPARE(graph.edges().size(), 2);
387 QCOMPARE(graph.edges().at(0), edge1);
388 QCOMPARE(graph.edges().at(1), edge2);
389
390
391 // WHEN
392 graph.removeEdge(edge: edge2);
393
394 // THEN
395 QCOMPARE(graph.edges().size(), 1);
396 QCOMPARE(graph.edges().at(0), edge1);
397
398 // WHEN
399 graph.addEdge(edge: edge2);
400
401 // THEN
402 QCOMPARE(graph.edges().size(), 2);
403 QCOMPARE(graph.edges().at(0), edge1);
404 QCOMPARE(graph.edges().at(1), edge2);
405
406 // WHEN
407 graph.addEdge(edge: edge1);
408
409 // THEN
410 QCOMPARE(graph.edges().size(), 2);
411 QCOMPARE(graph.edges().at(0), edge1);
412 QCOMPARE(graph.edges().at(1), edge2);
413}
414
415void tst_QShaderGraph::shouldSerializeGraphForCodeGeneration()
416{
417 // GIVEN
418 const auto input1 = createNode(ports: {
419 createPort(portDirection: QShaderNodePort::Output, portName: "input1Value")
420 });
421 const auto input2 = createNode(ports: {
422 createPort(portDirection: QShaderNodePort::Output, portName: "input2Value")
423 });
424 const auto output1 = createNode(ports: {
425 createPort(portDirection: QShaderNodePort::Input, portName: "output1Value")
426 });
427 const auto output2 = createNode(ports: {
428 createPort(portDirection: QShaderNodePort::Input, portName: "output2Value")
429 });
430 const auto function1 = createNode(ports: {
431 createPort(portDirection: QShaderNodePort::Input, portName: "function1Input"),
432 createPort(portDirection: QShaderNodePort::Output, portName: "function1Output")
433 });
434 const auto function2 = createNode(ports: {
435 createPort(portDirection: QShaderNodePort::Input, portName: "function2Input1"),
436 createPort(portDirection: QShaderNodePort::Input, portName: "function2Input2"),
437 createPort(portDirection: QShaderNodePort::Output, portName: "function2Output")
438 });
439 const auto function3 = createNode(ports: {
440 createPort(portDirection: QShaderNodePort::Input, portName: "function3Input1"),
441 createPort(portDirection: QShaderNodePort::Input, portName: "function3Input2"),
442 createPort(portDirection: QShaderNodePort::Output, portName: "function3Output1"),
443 createPort(portDirection: QShaderNodePort::Output, portName: "function3Output2")
444 });
445
446 const auto graph = [=] {
447 auto res = QShaderGraph();
448 res.addNode(node: input1);
449 res.addNode(node: input2);
450 res.addNode(node: output1);
451 res.addNode(node: output2);
452 res.addNode(node: function1);
453 res.addNode(node: function2);
454 res.addNode(node: function3);
455 res.addEdge(edge: createEdge(sourceUuid: input1.uuid(), sourceName: "input1Value", targetUuid: function1.uuid(), targetName: "function1Input"));
456 res.addEdge(edge: createEdge(sourceUuid: input1.uuid(), sourceName: "input1Value", targetUuid: function2.uuid(), targetName: "function2Input1"));
457 res.addEdge(edge: createEdge(sourceUuid: input2.uuid(), sourceName: "input2Value", targetUuid: function2.uuid(), targetName: "function2Input2"));
458 res.addEdge(edge: createEdge(sourceUuid: function1.uuid(), sourceName: "function1Output", targetUuid: function3.uuid(), targetName: "function3Input1"));
459 res.addEdge(edge: createEdge(sourceUuid: function2.uuid(), sourceName: "function2Output", targetUuid: function3.uuid(), targetName: "function3Input2"));
460 res.addEdge(edge: createEdge(sourceUuid: function3.uuid(), sourceName: "function3Output1", targetUuid: output1.uuid(), targetName: "output1Value"));
461 res.addEdge(edge: createEdge(sourceUuid: function3.uuid(), sourceName: "function3Output2", targetUuid: output2.uuid(), targetName: "output2Value"));
462 return res;
463 }();
464
465 // WHEN
466 const auto statements = graph.createStatements();
467
468 // THEN
469 const auto expected = QVector<QShaderGraph::Statement>()
470 << createStatement(node: input2, inputs: {}, outputs: {1})
471 << createStatement(node: input1, inputs: {}, outputs: {0})
472 << createStatement(node: function2, inputs: {0, 1}, outputs: {3})
473 << createStatement(node: function1, inputs: {0}, outputs: {2})
474 << createStatement(node: function3, inputs: {2, 3}, outputs: {4, 5})
475 << createStatement(node: output2, inputs: {5}, outputs: {})
476 << createStatement(node: output1, inputs: {4}, outputs: {});
477 dumpStatementsIfNeeded(statements, expected);
478 QCOMPARE(statements, expected);
479}
480
481void tst_QShaderGraph::shouldHandleUnboundPortsDuringGraphSerialization()
482{
483 // GIVEN
484 const auto input = createNode(ports: {
485 createPort(portDirection: QShaderNodePort::Output, portName: "input")
486 });
487 const auto unboundInput = createNode(ports: {
488 createPort(portDirection: QShaderNodePort::Output, portName: "unbound")
489 });
490 const auto output = createNode(ports: {
491 createPort(portDirection: QShaderNodePort::Input, portName: "output")
492 });
493 const auto unboundOutput = createNode(ports: {
494 createPort(portDirection: QShaderNodePort::Input, portName: "unbound")
495 });
496 const auto function = createNode(ports: {
497 createPort(portDirection: QShaderNodePort::Input, portName: "functionInput1"),
498 createPort(portDirection: QShaderNodePort::Input, portName: "functionInput2"),
499 createPort(portDirection: QShaderNodePort::Input, portName: "functionInput3"),
500 createPort(portDirection: QShaderNodePort::Output, portName: "functionOutput1"),
501 createPort(portDirection: QShaderNodePort::Output, portName: "functionOutput2"),
502 createPort(portDirection: QShaderNodePort::Output, portName: "functionOutput3")
503 });
504
505 const auto graph = [=] {
506 auto res = QShaderGraph();
507 res.addNode(node: input);
508 res.addNode(node: unboundInput);
509 res.addNode(node: output);
510 res.addNode(node: unboundOutput);
511 res.addNode(node: function);
512 res.addEdge(edge: createEdge(sourceUuid: input.uuid(), sourceName: "input", targetUuid: function.uuid(), targetName: "functionInput2"));
513 res.addEdge(edge: createEdge(sourceUuid: function.uuid(), sourceName: "functionOutput2", targetUuid: output.uuid(), targetName: "output"));
514 return res;
515 }();
516
517 // WHEN
518 const auto statements = graph.createStatements();
519
520 // THEN
521 // Note that no statement has any unbound input
522 const auto expected = QVector<QShaderGraph::Statement>()
523 << createStatement(node: input, inputs: {}, outputs: {0});
524 dumpStatementsIfNeeded(statements, expected);
525 QCOMPARE(statements, expected);
526}
527
528void tst_QShaderGraph::shouldSurviveCyclesDuringGraphSerialization()
529{
530 // GIVEN
531 const auto input = createNode(ports: {
532 createPort(portDirection: QShaderNodePort::Output, portName: "input")
533 });
534 const auto output = createNode(ports: {
535 createPort(portDirection: QShaderNodePort::Input, portName: "output")
536 });
537 const auto function1 = createNode(ports: {
538 createPort(portDirection: QShaderNodePort::Input, portName: "function1Input1"),
539 createPort(portDirection: QShaderNodePort::Input, portName: "function1Input2"),
540 createPort(portDirection: QShaderNodePort::Output, portName: "function1Output")
541 });
542 const auto function2 = createNode(ports: {
543 createPort(portDirection: QShaderNodePort::Input, portName: "function2Input"),
544 createPort(portDirection: QShaderNodePort::Output, portName: "function2Output")
545 });
546 const auto function3 = createNode(ports: {
547 createPort(portDirection: QShaderNodePort::Input, portName: "function3Input"),
548 createPort(portDirection: QShaderNodePort::Output, portName: "function3Output")
549 });
550
551 const auto graph = [=] {
552 auto res = QShaderGraph();
553 res.addNode(node: input);
554 res.addNode(node: output);
555 res.addNode(node: function1);
556 res.addNode(node: function2);
557 res.addNode(node: function3);
558 res.addEdge(edge: createEdge(sourceUuid: input.uuid(), sourceName: "input", targetUuid: function1.uuid(), targetName: "function1Input1"));
559 res.addEdge(edge: createEdge(sourceUuid: function1.uuid(), sourceName: "function1Output", targetUuid: function2.uuid(), targetName: "function2Input"));
560 res.addEdge(edge: createEdge(sourceUuid: function2.uuid(), sourceName: "function2Output", targetUuid: function3.uuid(), targetName: "function3Input"));
561 res.addEdge(edge: createEdge(sourceUuid: function3.uuid(), sourceName: "function3Output", targetUuid: function1.uuid(), targetName: "function1Input2"));
562 res.addEdge(edge: createEdge(sourceUuid: function2.uuid(), sourceName: "function2Output", targetUuid: output.uuid(), targetName: "output"));
563 return res;
564 }();
565
566 // WHEN
567 const auto statements = graph.createStatements();
568
569 // THEN
570 // The cycle is ignored
571 const auto expected = QVector<QShaderGraph::Statement>();
572 dumpStatementsIfNeeded(statements, expected);
573 QCOMPARE(statements, expected);
574}
575
576void tst_QShaderGraph::shouldDealWithEdgesJumpingOverLayers()
577{
578 // GIVEN
579 const auto worldPosition = createNode(ports: {
580 createPort(portDirection: QShaderNodePort::Output, portName: "worldPosition")
581 });
582 const auto texture = createNode(ports: {
583 createPort(portDirection: QShaderNodePort::Output, portName: "texture")
584 });
585 const auto texCoord = createNode(ports: {
586 createPort(portDirection: QShaderNodePort::Output, portName: "texCoord")
587 });
588 const auto lightIntensity = createNode(ports: {
589 createPort(portDirection: QShaderNodePort::Output, portName: "lightIntensity")
590 });
591 const auto exposure = createNode(ports: {
592 createPort(portDirection: QShaderNodePort::Output, portName: "exposure")
593 });
594 const auto fragColor = createNode(ports: {
595 createPort(portDirection: QShaderNodePort::Input, portName: "fragColor")
596 });
597 const auto sampleTexture = createNode(ports: {
598 createPort(portDirection: QShaderNodePort::Input, portName: "sampler"),
599 createPort(portDirection: QShaderNodePort::Input, portName: "coord"),
600 createPort(portDirection: QShaderNodePort::Output, portName: "color")
601 });
602 const auto lightFunction = createNode(ports: {
603 createPort(portDirection: QShaderNodePort::Input, portName: "baseColor"),
604 createPort(portDirection: QShaderNodePort::Input, portName: "position"),
605 createPort(portDirection: QShaderNodePort::Input, portName: "lightIntensity"),
606 createPort(portDirection: QShaderNodePort::Output, portName: "outputColor")
607 });
608 const auto exposureFunction = createNode(ports: {
609 createPort(portDirection: QShaderNodePort::Input, portName: "inputColor"),
610 createPort(portDirection: QShaderNodePort::Input, portName: "exposure"),
611 createPort(portDirection: QShaderNodePort::Output, portName: "outputColor")
612 });
613
614 const auto graph = [=] {
615 auto res = QShaderGraph();
616
617 res.addNode(node: worldPosition);
618 res.addNode(node: texture);
619 res.addNode(node: texCoord);
620 res.addNode(node: lightIntensity);
621 res.addNode(node: exposure);
622 res.addNode(node: fragColor);
623 res.addNode(node: sampleTexture);
624 res.addNode(node: lightFunction);
625 res.addNode(node: exposureFunction);
626
627 res.addEdge(edge: createEdge(sourceUuid: texture.uuid(), sourceName: "texture", targetUuid: sampleTexture.uuid(), targetName: "sampler"));
628 res.addEdge(edge: createEdge(sourceUuid: texCoord.uuid(), sourceName: "texCoord", targetUuid: sampleTexture.uuid(), targetName: "coord"));
629
630 res.addEdge(edge: createEdge(sourceUuid: worldPosition.uuid(), sourceName: "worldPosition", targetUuid: lightFunction.uuid(), targetName: "position"));
631 res.addEdge(edge: createEdge(sourceUuid: sampleTexture.uuid(), sourceName: "color", targetUuid: lightFunction.uuid(), targetName: "baseColor"));
632 res.addEdge(edge: createEdge(sourceUuid: lightIntensity.uuid(), sourceName: "lightIntensity", targetUuid: lightFunction.uuid(), targetName: "lightIntensity"));
633
634 res.addEdge(edge: createEdge(sourceUuid: lightFunction.uuid(), sourceName: "outputColor", targetUuid: exposureFunction.uuid(), targetName: "inputColor"));
635 res.addEdge(edge: createEdge(sourceUuid: exposure.uuid(), sourceName: "exposure", targetUuid: exposureFunction.uuid(), targetName: "exposure"));
636
637 res.addEdge(edge: createEdge(sourceUuid: exposureFunction.uuid(), sourceName: "outputColor", targetUuid: fragColor.uuid(), targetName: "fragColor"));
638
639 return res;
640 }();
641
642 // WHEN
643 const auto statements = graph.createStatements();
644
645 // THEN
646 const auto expected = QVector<QShaderGraph::Statement>()
647 << createStatement(node: texCoord, inputs: {}, outputs: {2})
648 << createStatement(node: texture, inputs: {}, outputs: {1})
649 << createStatement(node: lightIntensity, inputs: {}, outputs: {3})
650 << createStatement(node: sampleTexture, inputs: {1, 2}, outputs: {5})
651 << createStatement(node: worldPosition, inputs: {}, outputs: {0})
652 << createStatement(node: exposure, inputs: {}, outputs: {4})
653 << createStatement(node: lightFunction, inputs: {5, 0, 3}, outputs: {6})
654 << createStatement(node: exposureFunction, inputs: {6, 4}, outputs: {7})
655 << createStatement(node: fragColor, inputs: {7}, outputs: {});
656 dumpStatementsIfNeeded(statements, expected);
657 QCOMPARE(statements, expected);
658}
659
660void tst_QShaderGraph::shouldGenerateDifferentStatementsDependingOnActiveLayers()
661{
662 // GIVEN
663 const auto texCoord = createNode(ports: {
664 createPort(portDirection: QShaderNodePort::Output, portName: "texCoord")
665 }, layers: {
666 "diffuseTexture",
667 "normalTexture"
668 });
669 const auto diffuseUniform = createNode(ports: {
670 createPort(portDirection: QShaderNodePort::Output, portName: "color")
671 }, layers: {"diffuseUniform"});
672 const auto diffuseTexture = createNode(ports: {
673 createPort(portDirection: QShaderNodePort::Input, portName: "coord"),
674 createPort(portDirection: QShaderNodePort::Output, portName: "color")
675 }, layers: {"diffuseTexture"});
676 const auto normalUniform = createNode(ports: {
677 createPort(portDirection: QShaderNodePort::Output, portName: "normal")
678 }, layers: {"normalUniform"});
679 const auto normalTexture = createNode(ports: {
680 createPort(portDirection: QShaderNodePort::Input, portName: "coord"),
681 createPort(portDirection: QShaderNodePort::Output, portName: "normal")
682 }, layers: {"normalTexture"});
683 const auto lightFunction = createNode(ports: {
684 createPort(portDirection: QShaderNodePort::Input, portName: "color"),
685 createPort(portDirection: QShaderNodePort::Input, portName: "normal"),
686 createPort(portDirection: QShaderNodePort::Output, portName: "output")
687 });
688 const auto fragColor = createNode(ports: {
689 createPort(portDirection: QShaderNodePort::Input, portName: "fragColor")
690 });
691
692 const auto graph = [=] {
693 auto res = QShaderGraph();
694
695 res.addNode(node: texCoord);
696 res.addNode(node: diffuseUniform);
697 res.addNode(node: diffuseTexture);
698 res.addNode(node: normalUniform);
699 res.addNode(node: normalTexture);
700 res.addNode(node: lightFunction);
701 res.addNode(node: fragColor);
702
703 res.addEdge(edge: createEdge(sourceUuid: diffuseUniform.uuid(), sourceName: "color", targetUuid: lightFunction.uuid(), targetName: "color", layers: {"diffuseUniform"}));
704 res.addEdge(edge: createEdge(sourceUuid: texCoord.uuid(), sourceName: "texCoord", targetUuid: diffuseTexture.uuid(), targetName: "coord", layers: {"diffuseTexture"}));
705 res.addEdge(edge: createEdge(sourceUuid: diffuseTexture.uuid(), sourceName: "color", targetUuid: lightFunction.uuid(), targetName: "color", layers: {"diffuseTexture"}));
706
707 res.addEdge(edge: createEdge(sourceUuid: normalUniform.uuid(), sourceName: "normal", targetUuid: lightFunction.uuid(), targetName: "normal", layers: {"normalUniform"}));
708 res.addEdge(edge: createEdge(sourceUuid: texCoord.uuid(), sourceName: "texCoord", targetUuid: normalTexture.uuid(), targetName: "coord", layers: {"normalTexture"}));
709 res.addEdge(edge: createEdge(sourceUuid: normalTexture.uuid(), sourceName: "normal", targetUuid: lightFunction.uuid(), targetName: "normal", layers: {"normalTexture"}));
710
711 res.addEdge(edge: createEdge(sourceUuid: lightFunction.uuid(), sourceName: "output", targetUuid: fragColor.uuid(), targetName: "fragColor"));
712
713 return res;
714 }();
715
716 {
717 // WHEN
718 const auto statements = graph.createStatements(enabledLayers: {"diffuseUniform", "normalUniform"});
719
720 // THEN
721 const auto expected = QVector<QShaderGraph::Statement>()
722 << createStatement(node: normalUniform, inputs: {}, outputs: {1})
723 << createStatement(node: diffuseUniform, inputs: {}, outputs: {0})
724 << createStatement(node: lightFunction, inputs: {0, 1}, outputs: {2})
725 << createStatement(node: fragColor, inputs: {2}, outputs: {});
726 dumpStatementsIfNeeded(statements, expected);
727 QCOMPARE(statements, expected);
728 }
729
730 {
731 // WHEN
732 const auto statements = graph.createStatements(enabledLayers: {"diffuseUniform", "normalTexture"});
733
734 // THEN
735 const auto expected = QVector<QShaderGraph::Statement>()
736 << createStatement(node: texCoord, inputs: {}, outputs: {0})
737 << createStatement(node: normalTexture, inputs: {0}, outputs: {2})
738 << createStatement(node: diffuseUniform, inputs: {}, outputs: {1})
739 << createStatement(node: lightFunction, inputs: {1, 2}, outputs: {3})
740 << createStatement(node: fragColor, inputs: {3}, outputs: {});
741 dumpStatementsIfNeeded(statements, expected);
742 QCOMPARE(statements, expected);
743 }
744
745 {
746 // WHEN
747 const auto statements = graph.createStatements(enabledLayers: {"diffuseTexture", "normalUniform"});
748
749 // THEN
750 const auto expected = QVector<QShaderGraph::Statement>()
751 << createStatement(node: texCoord, inputs: {}, outputs: {0})
752 << createStatement(node: normalUniform, inputs: {}, outputs: {2})
753 << createStatement(node: diffuseTexture, inputs: {0}, outputs: {1})
754 << createStatement(node: lightFunction, inputs: {1, 2}, outputs: {3})
755 << createStatement(node: fragColor, inputs: {3}, outputs: {});
756 dumpStatementsIfNeeded(statements, expected);
757 QCOMPARE(statements, expected);
758 }
759
760 {
761 // WHEN
762 const auto statements = graph.createStatements(enabledLayers: {"diffuseTexture", "normalTexture"});
763
764 // THEN
765 const auto expected = QVector<QShaderGraph::Statement>()
766 << createStatement(node: texCoord, inputs: {}, outputs: {0})
767 << createStatement(node: normalTexture, inputs: {0}, outputs: {2})
768 << createStatement(node: diffuseTexture, inputs: {0}, outputs: {1})
769 << createStatement(node: lightFunction, inputs: {1, 2}, outputs: {3})
770 << createStatement(node: fragColor, inputs: {3}, outputs: {});
771 dumpStatementsIfNeeded(statements, expected);
772 QCOMPARE(statements, expected);
773 }
774}
775
776void tst_QShaderGraph::shouldDealWithBranchesWithoutOutput()
777{
778 // GIVEN
779 const auto input = createNode(ports: {
780 createPort(portDirection: QShaderNodePort::Output, portName: "input")
781 });
782 const auto output = createNode(ports: {
783 createPort(portDirection: QShaderNodePort::Input, portName: "output")
784 });
785 const auto danglingFunction = createNode(ports: {
786 createPort(portDirection: QShaderNodePort::Input, portName: "functionInput"),
787 createPort(portDirection: QShaderNodePort::Output, portName: "unbound")
788 });
789 const auto function = createNode(ports: {
790 createPort(portDirection: QShaderNodePort::Input, portName: "functionInput"),
791 createPort(portDirection: QShaderNodePort::Output, portName: "functionOutput")
792 });
793
794 const auto graph = [=] {
795 auto res = QShaderGraph();
796 res.addNode(node: input);
797 res.addNode(node: function);
798 res.addNode(node: danglingFunction);
799 res.addNode(node: output);
800 res.addEdge(edge: createEdge(sourceUuid: input.uuid(), sourceName: "input", targetUuid: function.uuid(), targetName: "functionInput"));
801 res.addEdge(edge: createEdge(sourceUuid: input.uuid(), sourceName: "input", targetUuid: danglingFunction.uuid(), targetName: "functionInput"));
802 res.addEdge(edge: createEdge(sourceUuid: function.uuid(), sourceName: "functionOutput", targetUuid: output.uuid(), targetName: "output"));
803 return res;
804 }();
805
806 // WHEN
807 const auto statements = graph.createStatements();
808
809 // THEN
810 // Note that no edge leads to the unbound input
811 const auto expected = QVector<QShaderGraph::Statement>()
812 << createStatement(node: input, inputs: {}, outputs: {0})
813 << createStatement(node: function, inputs: {0}, outputs: {1})
814 << createStatement(node: output, inputs: {1}, outputs: {})
815 << createStatement(node: danglingFunction, inputs: {0}, outputs: {2});
816 dumpStatementsIfNeeded(statements, expected);
817 QCOMPARE(statements, expected);
818}
819
820void tst_QShaderGraph::shouldDiscardEdgesConnectedToDiscardedNodes()
821{
822 // GIVEN
823 const auto input = createNode(ports: {
824 createPort(portDirection: QShaderNodePort::Output, portName: "input")
825 });
826 const auto output = createNode(ports: {
827 createPort(portDirection: QShaderNodePort::Input, portName: "output")
828 });
829 const auto function0 = createNode(ports: {
830 createPort(portDirection: QShaderNodePort::Input, portName: "function0Input"),
831 createPort(portDirection: QShaderNodePort::Output, portName: "function0Output")
832 }, layers: {"0"});
833 const auto function1 = createNode(ports: {
834 createPort(portDirection: QShaderNodePort::Input, portName: "function1Input"),
835 createPort(portDirection: QShaderNodePort::Output, portName: "function1Output")
836 }, layers: {"1"});
837
838 const auto graph = [=] {
839 auto res = QShaderGraph();
840 res.addNode(node: input);
841 res.addNode(node: function0);
842 res.addNode(node: function1);
843 res.addNode(node: output);
844 res.addEdge(edge: createEdge(sourceUuid: input.uuid(), sourceName: "input", targetUuid: function0.uuid(), targetName: "function0Input", layers: {"0"}));
845 res.addEdge(edge: createEdge(sourceUuid: input.uuid(), sourceName: "input", targetUuid: function1.uuid(), targetName: "function1Input"));
846 res.addEdge(edge: createEdge(sourceUuid: function0.uuid(), sourceName: "function0Output", targetUuid: output.uuid(), targetName: "output"));
847 res.addEdge(edge: createEdge(sourceUuid: function1.uuid(), sourceName: "function1Output", targetUuid: output.uuid(), targetName: "output"));
848 return res;
849 }();
850
851 // WHEN
852 const auto statements = graph.createStatements(enabledLayers: {"0"});
853
854 // THEN
855 const auto expected = QVector<QShaderGraph::Statement>()
856 << createStatement(node: input, inputs: {}, outputs: {0})
857 << createStatement(node: function0, inputs: {0}, outputs: {1})
858 << createStatement(node: output, inputs: {1}, outputs: {});
859 dumpStatementsIfNeeded(statements, expected);
860 QCOMPARE(statements, expected);
861}
862
863QTEST_MAIN(tst_QShaderGraph)
864
865#include "tst_qshadergraph.moc"
866

source code of qt3d/tests/auto/render/shadergraph/qshadergraph/tst_qshadergraph.cpp