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

source code of qtbase/tests/auto/gui/util/qshadergraphloader/tst_qshadergraphloader.cpp