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

source code of qtbase/tests/auto/gui/util/qshadergraph/tst_qshadergraph.cpp