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/qmetaobject.h>
33#include <QtGui/private/qshadergenerator_p.h>
34#include <QtGui/private/qshaderlanguage_p.h>
35
36namespace
37{
38 QShaderFormat createFormat(QShaderFormat::Api api, int majorVersion, int minorVersion,
39 QShaderFormat::ShaderType shaderType= QShaderFormat::Fragment)
40 {
41 auto format = QShaderFormat();
42 format.setApi(api);
43 format.setVersion(QVersionNumber(majorVersion, minorVersion));
44 format.setShaderType(shaderType);
45 return format;
46 }
47
48 QShaderNodePort createPort(QShaderNodePort::Direction portDirection, const QString &portName)
49 {
50 auto port = QShaderNodePort();
51 port.direction = portDirection;
52 port.name = portName;
53 return port;
54 }
55
56 QShaderNode createNode(const QVector<QShaderNodePort> &ports, const QStringList &layers = QStringList())
57 {
58 auto node = QShaderNode();
59 node.setUuid(QUuid::createUuid());
60 node.setLayers(layers);
61 for (const auto &port : ports)
62 node.addPort(port);
63 return node;
64 }
65
66 QShaderGraph::Edge createEdge(const QUuid &sourceUuid, const QString &sourceName,
67 const QUuid &targetUuid, const QString &targetName,
68 const QStringList &layers = QStringList())
69 {
70 auto edge = QShaderGraph::Edge();
71 edge.sourceNodeUuid = sourceUuid;
72 edge.sourcePortName = sourceName;
73 edge.targetNodeUuid = targetUuid;
74 edge.targetPortName = targetName;
75 edge.layers = layers;
76 return edge;
77 }
78
79 QShaderGraph createFragmentShaderGraph()
80 {
81 const auto openGLES2 = createFormat(QShaderFormat::OpenGLES, 2, 0);
82 const auto openGL3 = createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0);
83
84 auto graph = QShaderGraph();
85
86 auto worldPosition = createNode({
87 createPort(QShaderNodePort::Output, "value")
88 });
89 worldPosition.setParameter("name", "worldPosition");
90 worldPosition.addRule(openGLES2, QShaderNode::Rule("highp vec3 $value = $name;",
91 QByteArrayList() << "varying highp vec3 $name;"));
92 worldPosition.addRule(openGL3, QShaderNode::Rule("vec3 $value = $name;",
93 QByteArrayList() << "in vec3 $name;"));
94
95 auto texture = createNode({
96 createPort(QShaderNodePort::Output, "texture")
97 });
98 texture.addRule(openGLES2, QShaderNode::Rule("sampler2D $texture = texture;",
99 QByteArrayList() << "uniform sampler2D texture;"));
100 texture.addRule(openGL3, QShaderNode::Rule("sampler2D $texture = texture;",
101 QByteArrayList() << "uniform sampler2D texture;"));
102
103 auto texCoord = createNode({
104 createPort(QShaderNodePort::Output, "texCoord")
105 });
106 texCoord.addRule(openGLES2, QShaderNode::Rule("highp vec2 $texCoord = texCoord;",
107 QByteArrayList() << "varying highp vec2 texCoord;"));
108 texCoord.addRule(openGL3, QShaderNode::Rule("vec2 $texCoord = texCoord;",
109 QByteArrayList() << "in vec2 texCoord;"));
110
111 auto lightIntensity = createNode({
112 createPort(QShaderNodePort::Output, "lightIntensity")
113 });
114 lightIntensity.addRule(openGLES2, QShaderNode::Rule("highp float $lightIntensity = lightIntensity;",
115 QByteArrayList() << "uniform highp float lightIntensity;"));
116 lightIntensity.addRule(openGL3, QShaderNode::Rule("float $lightIntensity = lightIntensity;",
117 QByteArrayList() << "uniform float lightIntensity;"));
118
119 auto exposure = createNode({
120 createPort(QShaderNodePort::Output, "exposure")
121 });
122 exposure.addRule(openGLES2, QShaderNode::Rule("highp float $exposure = exposure;",
123 QByteArrayList() << "uniform highp float exposure;"));
124 exposure.addRule(openGL3, QShaderNode::Rule("float $exposure = exposure;",
125 QByteArrayList() << "uniform float exposure;"));
126
127 auto fragColor = createNode({
128 createPort(QShaderNodePort::Input, "fragColor")
129 });
130 fragColor.addRule(openGLES2, QShaderNode::Rule("gl_fragColor = $fragColor;"));
131 fragColor.addRule(openGL3, QShaderNode::Rule("fragColor = $fragColor;",
132 QByteArrayList() << "out vec4 fragColor;"));
133
134 auto sampleTexture = createNode({
135 createPort(QShaderNodePort::Input, "sampler"),
136 createPort(QShaderNodePort::Input, "coord"),
137 createPort(QShaderNodePort::Output, "color")
138 });
139 sampleTexture.addRule(openGLES2, QShaderNode::Rule("highp vec4 $color = texture2D($sampler, $coord);"));
140 sampleTexture.addRule(openGL3, QShaderNode::Rule("vec4 $color = texture($sampler, $coord);"));
141
142 auto lightFunction = createNode({
143 createPort(QShaderNodePort::Input, "baseColor"),
144 createPort(QShaderNodePort::Input, "position"),
145 createPort(QShaderNodePort::Input, "lightIntensity"),
146 createPort(QShaderNodePort::Output, "outputColor")
147 });
148 lightFunction.addRule(openGLES2, QShaderNode::Rule("highp vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);",
149 QByteArrayList() << "#pragma include es2/lightmodel.frag.inc"));
150 lightFunction.addRule(openGL3, QShaderNode::Rule("vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);",
151 QByteArrayList() << "#pragma include gl3/lightmodel.frag.inc"));
152
153 auto exposureFunction = createNode({
154 createPort(QShaderNodePort::Input, "inputColor"),
155 createPort(QShaderNodePort::Input, "exposure"),
156 createPort(QShaderNodePort::Output, "outputColor")
157 });
158 exposureFunction.addRule(openGLES2, QShaderNode::Rule("highp vec4 $outputColor = $inputColor * pow(2.0, $exposure);"));
159 exposureFunction.addRule(openGL3, QShaderNode::Rule("vec4 $outputColor = $inputColor * pow(2.0, $exposure);"));
160
161 graph.addNode(worldPosition);
162 graph.addNode(texture);
163 graph.addNode(texCoord);
164 graph.addNode(lightIntensity);
165 graph.addNode(exposure);
166 graph.addNode(fragColor);
167 graph.addNode(sampleTexture);
168 graph.addNode(lightFunction);
169 graph.addNode(exposureFunction);
170
171 graph.addEdge(createEdge(texture.uuid(), "texture", sampleTexture.uuid(), "sampler"));
172 graph.addEdge(createEdge(texCoord.uuid(), "texCoord", sampleTexture.uuid(), "coord"));
173
174 graph.addEdge(createEdge(worldPosition.uuid(), "value", lightFunction.uuid(), "position"));
175 graph.addEdge(createEdge(sampleTexture.uuid(), "color", lightFunction.uuid(), "baseColor"));
176 graph.addEdge(createEdge(lightIntensity.uuid(), "lightIntensity", lightFunction.uuid(), "lightIntensity"));
177
178 graph.addEdge(createEdge(lightFunction.uuid(), "outputColor", exposureFunction.uuid(), "inputColor"));
179 graph.addEdge(createEdge(exposure.uuid(), "exposure", exposureFunction.uuid(), "exposure"));
180
181 graph.addEdge(createEdge(exposureFunction.uuid(), "outputColor", fragColor.uuid(), "fragColor"));
182
183 return graph;
184 }
185}
186
187class tst_QShaderGenerator : public QObject
188{
189 Q_OBJECT
190private slots:
191 void shouldHaveDefaultState();
192 void shouldGenerateShaderCode_data();
193 void shouldGenerateShaderCode();
194 void shouldGenerateVersionCommands_data();
195 void shouldGenerateVersionCommands();
196 void shouldProcessLanguageQualifierAndTypeEnums_data();
197 void shouldProcessLanguageQualifierAndTypeEnums();
198 void shouldGenerateDifferentCodeDependingOnActiveLayers();
199 void shouldUseGlobalVariableRatherThanTemporaries();
200 void shouldGenerateTemporariesWisely();
201};
202
203void tst_QShaderGenerator::shouldHaveDefaultState()
204{
205 // GIVEN
206 auto generator = QShaderGenerator();
207
208 // THEN
209 QVERIFY(generator.graph.nodes().isEmpty());
210 QVERIFY(generator.graph.edges().isEmpty());
211 QVERIFY(!generator.format.isValid());
212}
213
214void tst_QShaderGenerator::shouldGenerateShaderCode_data()
215{
216 QTest::addColumn<QShaderGraph>("graph");
217 QTest::addColumn<QShaderFormat>("format");
218 QTest::addColumn<QByteArray>("expectedCode");
219
220 const auto graph = createFragmentShaderGraph();
221
222 const auto openGLES2 = createFormat(QShaderFormat::OpenGLES, 2, 0);
223 const auto openGL3 = createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0);
224 const auto openGL32 = createFormat(QShaderFormat::OpenGLCoreProfile, 3, 2);
225 const auto openGL4 = createFormat(QShaderFormat::OpenGLCoreProfile, 4, 0);
226
227 const auto versionGLES2 = QByteArrayList() << "#version 100" << "";
228 const auto versionGL3 = QByteArrayList() << "#version 130" << "";
229 const auto versionGL32 = QByteArrayList() << "#version 150 core" << "";
230 const auto versionGL4 = QByteArrayList() << "#version 400 core" << "";
231
232 const auto es2Code = QByteArrayList() << "varying highp vec3 worldPosition;"
233 << "uniform sampler2D texture;"
234 << "varying highp vec2 texCoord;"
235 << "uniform highp float lightIntensity;"
236 << "uniform highp float exposure;"
237 << "#pragma include es2/lightmodel.frag.inc"
238 << ""
239 << "void main()"
240 << "{"
241 << " gl_fragColor = (((((lightModel(((texture2D(texture, texCoord))), worldPosition, lightIntensity)))) * pow(2.0, exposure)));"
242 << "}"
243 << "";
244
245 const auto gl3Code = QByteArrayList() << "in vec3 worldPosition;"
246 << "uniform sampler2D texture;"
247 << "in vec2 texCoord;"
248 << "uniform float lightIntensity;"
249 << "uniform float exposure;"
250 << "out vec4 fragColor;"
251 << "#pragma include gl3/lightmodel.frag.inc"
252 << ""
253 << "void main()"
254 << "{"
255 << " fragColor = (((((lightModel(((texture(texture, texCoord))), worldPosition, lightIntensity)))) * pow(2.0, exposure)));"
256 << "}"
257 << "";
258
259 QTest::newRow("EmptyGraphAndFormat") << QShaderGraph() << QShaderFormat() << QByteArrayLiteral("\nvoid main()\n{\n}\n");
260 QTest::newRow("LightExposureGraphAndES2") << graph << openGLES2 << (versionGLES2 + es2Code).join('\n');
261 QTest::newRow("LightExposureGraphAndGL3") << graph << openGL3 << (versionGL3 + gl3Code).join('\n');
262 QTest::newRow("LightExposureGraphAndGL32") << graph << openGL32 << (versionGL32 + gl3Code).join('\n');
263 QTest::newRow("LightExposureGraphAndGL4") << graph << openGL4 << (versionGL4 + gl3Code).join('\n');
264}
265
266void tst_QShaderGenerator::shouldGenerateShaderCode()
267{
268 // GIVEN
269 QFETCH(QShaderGraph, graph);
270 QFETCH(QShaderFormat, format);
271
272 auto generator = QShaderGenerator();
273 generator.graph = graph;
274 generator.format = format;
275
276 // WHEN
277 const auto code = generator.createShaderCode();
278
279 // THEN
280 QFETCH(QByteArray, expectedCode);
281 QCOMPARE(code, expectedCode);
282}
283
284void tst_QShaderGenerator::shouldGenerateVersionCommands_data()
285{
286 QTest::addColumn<QShaderFormat>("format");
287 QTest::addColumn<QByteArray>("version");
288
289 QTest::newRow("GLES2") << createFormat(QShaderFormat::OpenGLES, 2, 0) << QByteArrayLiteral("#version 100");
290 QTest::newRow("GLES3") << createFormat(QShaderFormat::OpenGLES, 3, 0) << QByteArrayLiteral("#version 300 es");
291
292 QTest::newRow("GL20") << createFormat(QShaderFormat::OpenGLNoProfile, 2, 0) << QByteArrayLiteral("#version 110");
293 QTest::newRow("GL21") << createFormat(QShaderFormat::OpenGLNoProfile, 2, 1) << QByteArrayLiteral("#version 120");
294 QTest::newRow("GL30") << createFormat(QShaderFormat::OpenGLNoProfile, 3, 0) << QByteArrayLiteral("#version 130");
295 QTest::newRow("GL31") << createFormat(QShaderFormat::OpenGLNoProfile, 3, 1) << QByteArrayLiteral("#version 140");
296 QTest::newRow("GL32") << createFormat(QShaderFormat::OpenGLNoProfile, 3, 2) << QByteArrayLiteral("#version 150");
297 QTest::newRow("GL33") << createFormat(QShaderFormat::OpenGLNoProfile, 3, 3) << QByteArrayLiteral("#version 330");
298 QTest::newRow("GL40") << createFormat(QShaderFormat::OpenGLNoProfile, 4, 0) << QByteArrayLiteral("#version 400");
299 QTest::newRow("GL41") << createFormat(QShaderFormat::OpenGLNoProfile, 4, 1) << QByteArrayLiteral("#version 410");
300 QTest::newRow("GL42") << createFormat(QShaderFormat::OpenGLNoProfile, 4, 2) << QByteArrayLiteral("#version 420");
301 QTest::newRow("GL43") << createFormat(QShaderFormat::OpenGLNoProfile, 4, 3) << QByteArrayLiteral("#version 430");
302
303 QTest::newRow("GL20core") << createFormat(QShaderFormat::OpenGLCoreProfile, 2, 0) << QByteArrayLiteral("#version 110");
304 QTest::newRow("GL21core") << createFormat(QShaderFormat::OpenGLCoreProfile, 2, 1) << QByteArrayLiteral("#version 120");
305 QTest::newRow("GL30core") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0) << QByteArrayLiteral("#version 130");
306 QTest::newRow("GL31core") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 1) << QByteArrayLiteral("#version 140");
307 QTest::newRow("GL32core") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 2) << QByteArrayLiteral("#version 150 core");
308 QTest::newRow("GL33core") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 3) << QByteArrayLiteral("#version 330 core");
309 QTest::newRow("GL40core") << createFormat(QShaderFormat::OpenGLCoreProfile, 4, 0) << QByteArrayLiteral("#version 400 core");
310 QTest::newRow("GL41core") << createFormat(QShaderFormat::OpenGLCoreProfile, 4, 1) << QByteArrayLiteral("#version 410 core");
311 QTest::newRow("GL42core") << createFormat(QShaderFormat::OpenGLCoreProfile, 4, 2) << QByteArrayLiteral("#version 420 core");
312 QTest::newRow("GL43core") << createFormat(QShaderFormat::OpenGLCoreProfile, 4, 3) << QByteArrayLiteral("#version 430 core");
313
314 QTest::newRow("GL20compatibility") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 2, 0) << QByteArrayLiteral("#version 110");
315 QTest::newRow("GL21compatibility") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 2, 1) << QByteArrayLiteral("#version 120");
316 QTest::newRow("GL30compatibility") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 3, 0) << QByteArrayLiteral("#version 130");
317 QTest::newRow("GL31compatibility") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 3, 1) << QByteArrayLiteral("#version 140");
318 QTest::newRow("GL32compatibility") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 3, 2) << QByteArrayLiteral("#version 150 compatibility");
319 QTest::newRow("GL33compatibility") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 3, 3) << QByteArrayLiteral("#version 330 compatibility");
320 QTest::newRow("GL40compatibility") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 4, 0) << QByteArrayLiteral("#version 400 compatibility");
321 QTest::newRow("GL41compatibility") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 4, 1) << QByteArrayLiteral("#version 410 compatibility");
322 QTest::newRow("GL42compatibility") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 4, 2) << QByteArrayLiteral("#version 420 compatibility");
323 QTest::newRow("GL43compatibility") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 4, 3) << QByteArrayLiteral("#version 430 compatibility");
324}
325
326void tst_QShaderGenerator::shouldGenerateVersionCommands()
327{
328 // GIVEN
329 QFETCH(QShaderFormat, format);
330
331 auto generator = QShaderGenerator();
332 generator.format = format;
333
334 // WHEN
335 const auto code = generator.createShaderCode();
336
337 // THEN
338 QFETCH(QByteArray, version);
339 const auto expectedCode = (QByteArrayList() << version
340 << ""
341 << ""
342 << "void main()"
343 << "{"
344 << "}"
345 << "").join('\n');
346 QCOMPARE(code, expectedCode);
347}
348
349
350namespace {
351 QString toGlsl(QShaderLanguage::StorageQualifier qualifier, const QShaderFormat &format)
352 {
353 if (format.version().majorVersion() <= 2) {
354 // Note we're assuming fragment shader only here, it'd be different
355 // values for vertex shader, will need to be fixed properly at some
356 // point but isn't necessary yet (this problem already exists in past
357 // commits anyway)
358 switch (qualifier) {
359 case QShaderLanguage::Const:
360 return "const";
361 case QShaderLanguage::Input:
362 return "varying";
363 case QShaderLanguage::BuiltIn:
364 return "//";
365 case QShaderLanguage::Output:
366 return ""; // Although fragment shaders for <=2 only have fixed outputs
367 case QShaderLanguage::Uniform:
368 return "uniform";
369 }
370 } else {
371 switch (qualifier) {
372 case QShaderLanguage::Const:
373 return "const";
374 case QShaderLanguage::Input:
375 return "in";
376 case QShaderLanguage::BuiltIn:
377 return "//";
378 case QShaderLanguage::Output:
379 return "out";
380 case QShaderLanguage::Uniform:
381 return "uniform";
382 }
383 }
384
385 Q_UNREACHABLE();
386 }
387
388 QString toGlsl(QShaderLanguage::VariableType type)
389 {
390 switch (type) {
391 case QShaderLanguage::Bool:
392 return "bool";
393 case QShaderLanguage::Int:
394 return "int";
395 case QShaderLanguage::Uint:
396 return "uint";
397 case QShaderLanguage::Float:
398 return "float";
399 case QShaderLanguage::Double:
400 return "double";
401 case QShaderLanguage::Vec2:
402 return "vec2";
403 case QShaderLanguage::Vec3:
404 return "vec3";
405 case QShaderLanguage::Vec4:
406 return "vec4";
407 case QShaderLanguage::DVec2:
408 return "dvec2";
409 case QShaderLanguage::DVec3:
410 return "dvec3";
411 case QShaderLanguage::DVec4:
412 return "dvec4";
413 case QShaderLanguage::BVec2:
414 return "bvec2";
415 case QShaderLanguage::BVec3:
416 return "bvec3";
417 case QShaderLanguage::BVec4:
418 return "bvec4";
419 case QShaderLanguage::IVec2:
420 return "ivec2";
421 case QShaderLanguage::IVec3:
422 return "ivec3";
423 case QShaderLanguage::IVec4:
424 return "ivec4";
425 case QShaderLanguage::UVec2:
426 return "uvec2";
427 case QShaderLanguage::UVec3:
428 return "uvec3";
429 case QShaderLanguage::UVec4:
430 return "uvec4";
431 case QShaderLanguage::Mat2:
432 return "mat2";
433 case QShaderLanguage::Mat3:
434 return "mat3";
435 case QShaderLanguage::Mat4:
436 return "mat4";
437 case QShaderLanguage::Mat2x2:
438 return "mat2x2";
439 case QShaderLanguage::Mat2x3:
440 return "mat2x3";
441 case QShaderLanguage::Mat2x4:
442 return "mat2x4";
443 case QShaderLanguage::Mat3x2:
444 return "mat3x2";
445 case QShaderLanguage::Mat3x3:
446 return "mat3x3";
447 case QShaderLanguage::Mat3x4:
448 return "mat3x4";
449 case QShaderLanguage::Mat4x2:
450 return "mat4x2";
451 case QShaderLanguage::Mat4x3:
452 return "mat4x3";
453 case QShaderLanguage::Mat4x4:
454 return "mat4x4";
455 case QShaderLanguage::DMat2:
456 return "dmat2";
457 case QShaderLanguage::DMat3:
458 return "dmat3";
459 case QShaderLanguage::DMat4:
460 return "dmat4";
461 case QShaderLanguage::DMat2x2:
462 return "dmat2x2";
463 case QShaderLanguage::DMat2x3:
464 return "dmat2x3";
465 case QShaderLanguage::DMat2x4:
466 return "dmat2x4";
467 case QShaderLanguage::DMat3x2:
468 return "dmat3x2";
469 case QShaderLanguage::DMat3x3:
470 return "dmat3x3";
471 case QShaderLanguage::DMat3x4:
472 return "dmat3x4";
473 case QShaderLanguage::DMat4x2:
474 return "dmat4x2";
475 case QShaderLanguage::DMat4x3:
476 return "dmat4x3";
477 case QShaderLanguage::DMat4x4:
478 return "dmat4x4";
479 case QShaderLanguage::Sampler1D:
480 return "sampler1D";
481 case QShaderLanguage::Sampler2D:
482 return "sampler2D";
483 case QShaderLanguage::Sampler3D:
484 return "sampler3D";
485 case QShaderLanguage::SamplerCube:
486 return "samplerCube";
487 case QShaderLanguage::Sampler2DRect:
488 return "sampler2DRect";
489 case QShaderLanguage::Sampler2DMs:
490 return "sampler2DMS";
491 case QShaderLanguage::SamplerBuffer:
492 return "samplerBuffer";
493 case QShaderLanguage::Sampler1DArray:
494 return "sampler1DArray";
495 case QShaderLanguage::Sampler2DArray:
496 return "sampler2DArray";
497 case QShaderLanguage::Sampler2DMsArray:
498 return "sampler2DMSArray";
499 case QShaderLanguage::SamplerCubeArray:
500 return "samplerCubeArray";
501 case QShaderLanguage::Sampler1DShadow:
502 return "sampler1DShadow";
503 case QShaderLanguage::Sampler2DShadow:
504 return "sampler2DShadow";
505 case QShaderLanguage::Sampler2DRectShadow:
506 return "sampler2DRectShadow";
507 case QShaderLanguage::Sampler1DArrayShadow:
508 return "sampler1DArrayShadow";
509 case QShaderLanguage::Sampler2DArrayShadow:
510 return "sample2DArrayShadow";
511 case QShaderLanguage::SamplerCubeShadow:
512 return "samplerCubeShadow";
513 case QShaderLanguage::SamplerCubeArrayShadow:
514 return "samplerCubeArrayShadow";
515 case QShaderLanguage::ISampler1D:
516 return "isampler1D";
517 case QShaderLanguage::ISampler2D:
518 return "isampler2D";
519 case QShaderLanguage::ISampler3D:
520 return "isampler3D";
521 case QShaderLanguage::ISamplerCube:
522 return "isamplerCube";
523 case QShaderLanguage::ISampler2DRect:
524 return "isampler2DRect";
525 case QShaderLanguage::ISampler2DMs:
526 return "isampler2DMS";
527 case QShaderLanguage::ISamplerBuffer:
528 return "isamplerBuffer";
529 case QShaderLanguage::ISampler1DArray:
530 return "isampler1DArray";
531 case QShaderLanguage::ISampler2DArray:
532 return "isampler2DArray";
533 case QShaderLanguage::ISampler2DMsArray:
534 return "isampler2DMSArray";
535 case QShaderLanguage::ISamplerCubeArray:
536 return "isamplerCubeArray";
537 case QShaderLanguage::USampler1D:
538 return "usampler1D";
539 case QShaderLanguage::USampler2D:
540 return "usampler2D";
541 case QShaderLanguage::USampler3D:
542 return "usampler3D";
543 case QShaderLanguage::USamplerCube:
544 return "usamplerCube";
545 case QShaderLanguage::USampler2DRect:
546 return "usampler2DRect";
547 case QShaderLanguage::USampler2DMs:
548 return "usampler2DMS";
549 case QShaderLanguage::USamplerBuffer:
550 return "usamplerBuffer";
551 case QShaderLanguage::USampler1DArray:
552 return "usampler1DArray";
553 case QShaderLanguage::USampler2DArray:
554 return "usampler2DArray";
555 case QShaderLanguage::USampler2DMsArray:
556 return "usampler2DMSArray";
557 case QShaderLanguage::USamplerCubeArray:
558 return "usamplerCubeArray";
559 }
560
561 Q_UNREACHABLE();
562 }
563}
564
565void tst_QShaderGenerator::shouldProcessLanguageQualifierAndTypeEnums_data()
566{
567 QTest::addColumn<QShaderGraph>("graph");
568 QTest::addColumn<QShaderFormat>("format");
569 QTest::addColumn<QByteArray>("expectedCode");
570
571 {
572 const auto es2 = createFormat(QShaderFormat::OpenGLES, 2, 0);
573 const auto es3 = createFormat(QShaderFormat::OpenGLES, 3, 0);
574 const auto gl2 = createFormat(QShaderFormat::OpenGLNoProfile, 2, 0);
575 const auto gl3 = createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0);
576 const auto gl4 = createFormat(QShaderFormat::OpenGLCoreProfile, 4, 0);
577
578 const auto qualifierEnum = QMetaEnum::fromType<QShaderLanguage::StorageQualifier>();
579 const auto typeEnum = QMetaEnum::fromType<QShaderLanguage::VariableType>();
580
581 for (int qualifierIndex = 0; qualifierIndex < qualifierEnum.keyCount(); qualifierIndex++) {
582 const auto qualifierName = qualifierEnum.key(qualifierIndex);
583 const auto qualifierValue = static_cast<QShaderLanguage::StorageQualifier>(qualifierEnum.value(qualifierIndex));
584
585 for (int typeIndex = 0; typeIndex < typeEnum.keyCount(); typeIndex++) {
586 const auto typeName = typeEnum.key(typeIndex);
587 const auto typeValue = static_cast<QShaderLanguage::VariableType>(typeEnum.value(typeIndex));
588
589 auto graph = QShaderGraph();
590
591 auto worldPosition = createNode({
592 createPort(QShaderNodePort::Output, "value")
593 });
594 worldPosition.setParameter("name", "worldPosition");
595 worldPosition.setParameter("qualifier", QVariant::fromValue<QShaderLanguage::StorageQualifier>(qualifierValue));
596 worldPosition.setParameter("type", QVariant::fromValue<QShaderLanguage::VariableType>(typeValue));
597 worldPosition.addRule(es2, QShaderNode::Rule("highp $type $value = $name;",
598 QByteArrayList() << "$qualifier highp $type $name;"));
599 worldPosition.addRule(gl2, QShaderNode::Rule("$type $value = $name;",
600 QByteArrayList() << "$qualifier $type $name;"));
601 worldPosition.addRule(gl3, QShaderNode::Rule("$type $value = $name;",
602 QByteArrayList() << "$qualifier $type $name;"));
603
604 auto fragColor = createNode({
605 createPort(QShaderNodePort::Input, "fragColor")
606 });
607 fragColor.addRule(es2, QShaderNode::Rule("gl_fragColor = $fragColor;"));
608 fragColor.addRule(gl2, QShaderNode::Rule("gl_fragColor = $fragColor;"));
609 fragColor.addRule(gl3, QShaderNode::Rule("fragColor = $fragColor;",
610 QByteArrayList() << "out vec4 fragColor;"));
611
612 graph.addNode(worldPosition);
613 graph.addNode(fragColor);
614
615 graph.addEdge(createEdge(worldPosition.uuid(), "value", fragColor.uuid(), "fragColor"));
616
617 const auto gl2Code = (QByteArrayList() << "#version 110"
618 << ""
619 << QStringLiteral("%1 %2 worldPosition;").arg(toGlsl(qualifierValue, gl2))
620 .arg(toGlsl(typeValue))
621 .toUtf8()
622 << ""
623 << "void main()"
624 << "{"
625 << " gl_fragColor = worldPosition;"
626 << "}"
627 << "").join("\n");
628 const auto gl3Code = (QByteArrayList() << "#version 130"
629 << ""
630 << QStringLiteral("%1 %2 worldPosition;").arg(toGlsl(qualifierValue, gl3))
631 .arg(toGlsl(typeValue))
632 .toUtf8()
633 << "out vec4 fragColor;"
634 << ""
635 << "void main()"
636 << "{"
637 << " fragColor = worldPosition;"
638 << "}"
639 << "").join("\n");
640 const auto gl4Code = (QByteArrayList() << "#version 400 core"
641 << ""
642 << QStringLiteral("%1 %2 worldPosition;").arg(toGlsl(qualifierValue, gl4))
643 .arg(toGlsl(typeValue))
644 .toUtf8()
645 << "out vec4 fragColor;"
646 << ""
647 << "void main()"
648 << "{"
649 << " fragColor = worldPosition;"
650 << "}"
651 << "").join("\n");
652 const auto es2Code = (QByteArrayList() << "#version 100"
653 << ""
654 << QStringLiteral("%1 highp %2 worldPosition;").arg(toGlsl(qualifierValue, es2))
655 .arg(toGlsl(typeValue))
656 .toUtf8()
657 << ""
658 << "void main()"
659 << "{"
660 << " gl_fragColor = worldPosition;"
661 << "}"
662 << "").join("\n");
663 const auto es3Code = (QByteArrayList() << "#version 300 es"
664 << ""
665 << QStringLiteral("%1 highp %2 worldPosition;").arg(toGlsl(qualifierValue, es3))
666 .arg(toGlsl(typeValue))
667 .toUtf8()
668 << ""
669 << "void main()"
670 << "{"
671 << " gl_fragColor = worldPosition;"
672 << "}"
673 << "").join("\n");
674
675 QTest::addRow("%s %s ES2", qualifierName, typeName) << graph << es2 << es2Code;
676 QTest::addRow("%s %s ES3", qualifierName, typeName) << graph << es3 << es3Code;
677 QTest::addRow("%s %s GL2", qualifierName, typeName) << graph << gl2 << gl2Code;
678 QTest::addRow("%s %s GL3", qualifierName, typeName) << graph << gl3 << gl3Code;
679 QTest::addRow("%s %s GL4", qualifierName, typeName) << graph << gl4 << gl4Code;
680 }
681 }
682 }
683
684 {
685 const auto es2 = createFormat(QShaderFormat::OpenGLES, 2, 0, QShaderFormat::Vertex);
686 const auto es3 = createFormat(QShaderFormat::OpenGLES, 3, 0, QShaderFormat::Vertex);
687 const auto gl2 = createFormat(QShaderFormat::OpenGLNoProfile, 2, 0, QShaderFormat::Vertex);
688 const auto gl3 = createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, QShaderFormat::Vertex);
689 const auto gl4 = createFormat(QShaderFormat::OpenGLCoreProfile, 4, 0, QShaderFormat::Vertex);
690
691 auto graph = QShaderGraph();
692
693 auto vertexPosition = createNode({
694 createPort(QShaderNodePort::Output, "value")
695 });
696 vertexPosition.setParameter("name", "vertexPosition");
697 vertexPosition.setParameter("qualifier", QVariant::fromValue<QShaderLanguage::StorageQualifier>(QShaderLanguage::Input));
698 vertexPosition.setParameter("type", QVariant::fromValue<QShaderLanguage::VariableType>(QShaderLanguage::Vec4));
699
700 vertexPosition.addRule(es2, QShaderNode::Rule("",
701 QByteArrayList() << "$qualifier highp $type $name;"));
702 vertexPosition.addRule(gl2, QShaderNode::Rule("",
703 QByteArrayList() << "$qualifier $type $name;"));
704 vertexPosition.addRule(gl3, QShaderNode::Rule("",
705 QByteArrayList() << "$qualifier $type $name;"));
706
707 graph.addNode(vertexPosition);
708
709 const auto gl2Code = (QByteArrayList() << "#version 110"
710 << ""
711 << "attribute vec4 vertexPosition;"
712 << ""
713 << "void main()"
714 << "{"
715 << "}"
716 << "").join("\n");
717 const auto gl3Code = (QByteArrayList() << "#version 130"
718 << ""
719 << "in vec4 vertexPosition;"
720 << ""
721 << "void main()"
722 << "{"
723 << "}"
724 << "").join("\n");
725 const auto gl4Code = (QByteArrayList() << "#version 400 core"
726 << ""
727 << "in vec4 vertexPosition;"
728 << ""
729 << "void main()"
730 << "{"
731 << "}"
732 << "").join("\n");
733 const auto es2Code = (QByteArrayList() << "#version 100"
734 << ""
735 << "attribute highp vec4 vertexPosition;"
736 << ""
737 << "void main()"
738 << "{"
739 << "}"
740 << "").join("\n");
741 const auto es3Code = (QByteArrayList() << "#version 300 es"
742 << ""
743 << "in highp vec4 vertexPosition;"
744 << ""
745 << "void main()"
746 << "{"
747 << "}"
748 << "").join("\n");
749
750 QTest::addRow("Attribute header substitution ES2") << graph << es2 << es2Code;
751 QTest::addRow("Attribute header substitution ES3") << graph << es3 << es3Code;
752 QTest::addRow("Attribute header substitution GL2") << graph << gl2 << gl2Code;
753 QTest::addRow("Attribute header substitution GL3") << graph << gl3 << gl3Code;
754 QTest::addRow("Attribute header substitution GL4") << graph << gl4 << gl4Code;
755 }
756}
757
758void tst_QShaderGenerator::shouldProcessLanguageQualifierAndTypeEnums()
759{
760 // GIVEN
761 QFETCH(QShaderGraph, graph);
762 QFETCH(QShaderFormat, format);
763
764 auto generator = QShaderGenerator();
765 generator.graph = graph;
766 generator.format = format;
767
768 // WHEN
769 const auto code = generator.createShaderCode();
770
771 // THEN
772 QFETCH(QByteArray, expectedCode);
773 QCOMPARE(code, expectedCode);
774}
775
776void tst_QShaderGenerator::shouldGenerateDifferentCodeDependingOnActiveLayers()
777{
778 // GIVEN
779 const auto gl4 = createFormat(QShaderFormat::OpenGLCoreProfile, 4, 0);
780
781 auto texCoord = createNode({
782 createPort(QShaderNodePort::Output, "texCoord")
783 }, {
784 "diffuseTexture",
785 "normalTexture"
786 });
787 texCoord.addRule(gl4, QShaderNode::Rule("vec2 $texCoord = texCoord;",
788 QByteArrayList() << "in vec2 texCoord;"));
789 auto diffuseUniform = createNode({
790 createPort(QShaderNodePort::Output, "color")
791 }, {"diffuseUniform"});
792 diffuseUniform.addRule(gl4, QShaderNode::Rule("vec4 $color = diffuseUniform;",
793 QByteArrayList() << "uniform vec4 diffuseUniform;"));
794 auto diffuseTexture = createNode({
795 createPort(QShaderNodePort::Input, "coord"),
796 createPort(QShaderNodePort::Output, "color")
797 }, {"diffuseTexture"});
798 diffuseTexture.addRule(gl4, QShaderNode::Rule("vec4 $color = texture2D(diffuseTexture, $coord);",
799 QByteArrayList() << "uniform sampler2D diffuseTexture;"));
800 auto normalUniform = createNode({
801 createPort(QShaderNodePort::Output, "normal")
802 }, {"normalUniform"});
803 normalUniform.addRule(gl4, QShaderNode::Rule("vec3 $normal = normalUniform;",
804 QByteArrayList() << "uniform vec3 normalUniform;"));
805 auto normalTexture = createNode({
806 createPort(QShaderNodePort::Input, "coord"),
807 createPort(QShaderNodePort::Output, "normal")
808 }, {"normalTexture"});
809 normalTexture.addRule(gl4, QShaderNode::Rule("vec3 $normal = texture2D(normalTexture, $coord).rgb;",
810 QByteArrayList() << "uniform sampler2D normalTexture;"));
811 auto lightFunction = createNode({
812 createPort(QShaderNodePort::Input, "color"),
813 createPort(QShaderNodePort::Input, "normal"),
814 createPort(QShaderNodePort::Output, "output")
815 });
816 lightFunction.addRule(gl4, QShaderNode::Rule("vec4 $output = lightModel($color, $normal);",
817 QByteArrayList() << "#pragma include gl4/lightmodel.frag.inc"));
818 auto fragColor = createNode({
819 createPort(QShaderNodePort::Input, "fragColor")
820 });
821 fragColor.addRule(gl4, QShaderNode::Rule("fragColor = $fragColor;",
822 QByteArrayList() << "out vec4 fragColor;"));
823
824 const auto graph = [=] {
825 auto res = QShaderGraph();
826
827 res.addNode(texCoord);
828 res.addNode(diffuseUniform);
829 res.addNode(diffuseTexture);
830 res.addNode(normalUniform);
831 res.addNode(normalTexture);
832 res.addNode(lightFunction);
833 res.addNode(fragColor);
834
835 res.addEdge(createEdge(diffuseUniform.uuid(), "color", lightFunction.uuid(), "color", {"diffuseUniform"}));
836 res.addEdge(createEdge(texCoord.uuid(), "texCoord", diffuseTexture.uuid(), "coord", {"diffuseTexture"}));
837 res.addEdge(createEdge(diffuseTexture.uuid(), "color", lightFunction.uuid(), "color", {"diffuseTexture"}));
838
839 res.addEdge(createEdge(normalUniform.uuid(), "normal", lightFunction.uuid(), "normal", {"normalUniform"}));
840 res.addEdge(createEdge(texCoord.uuid(), "texCoord", normalTexture.uuid(), "coord", {"normalTexture"}));
841 res.addEdge(createEdge(normalTexture.uuid(), "normal", lightFunction.uuid(), "normal", {"normalTexture"}));
842
843 res.addEdge(createEdge(lightFunction.uuid(), "output", fragColor.uuid(), "fragColor"));
844
845 return res;
846 }();
847
848 auto generator = QShaderGenerator();
849 generator.graph = graph;
850 generator.format = gl4;
851
852 {
853 // WHEN
854 const auto code = generator.createShaderCode({"diffuseUniform", "normalUniform"});
855
856 // THEN
857 const auto expected = QByteArrayList()
858 << "#version 400 core"
859 << ""
860 << "uniform vec4 diffuseUniform;"
861 << "uniform vec3 normalUniform;"
862 << "#pragma include gl4/lightmodel.frag.inc"
863 << "out vec4 fragColor;"
864 << ""
865 << "void main()"
866 << "{"
867 << " fragColor = ((lightModel(diffuseUniform, normalUniform)));"
868 << "}"
869 << "";
870 QCOMPARE(code, expected.join("\n"));
871 }
872
873 {
874 // WHEN
875 const auto code = generator.createShaderCode({"diffuseUniform", "normalTexture"});
876
877 // THEN
878 const auto expected = QByteArrayList()
879 << "#version 400 core"
880 << ""
881 << "in vec2 texCoord;"
882 << "uniform vec4 diffuseUniform;"
883 << "uniform sampler2D normalTexture;"
884 << "#pragma include gl4/lightmodel.frag.inc"
885 << "out vec4 fragColor;"
886 << ""
887 << "void main()"
888 << "{"
889 << " fragColor = ((lightModel(diffuseUniform, texture2D(normalTexture, texCoord).rgb)));"
890 << "}"
891 << "";
892 QCOMPARE(code, expected.join("\n"));
893 }
894
895 {
896 // WHEN
897 const auto code = generator.createShaderCode({"diffuseTexture", "normalUniform"});
898
899 // THEN
900 const auto expected = QByteArrayList()
901 << "#version 400 core"
902 << ""
903 << "in vec2 texCoord;"
904 << "uniform sampler2D diffuseTexture;"
905 << "uniform vec3 normalUniform;"
906 << "#pragma include gl4/lightmodel.frag.inc"
907 << "out vec4 fragColor;"
908 << ""
909 << "void main()"
910 << "{"
911 << " fragColor = ((lightModel(texture2D(diffuseTexture, texCoord), normalUniform)));"
912 << "}"
913 << "";
914 QCOMPARE(code, expected.join("\n"));
915 }
916
917 {
918 // WHEN
919 const auto code = generator.createShaderCode({"diffuseTexture", "normalTexture"});
920
921 // THEN
922 const auto expected = QByteArrayList()
923 << "#version 400 core"
924 << ""
925 << "in vec2 texCoord;"
926 << "uniform sampler2D diffuseTexture;"
927 << "uniform sampler2D normalTexture;"
928 << "#pragma include gl4/lightmodel.frag.inc"
929 << "out vec4 fragColor;"
930 << ""
931 << "void main()"
932 << "{"
933 << " fragColor = ((lightModel(texture2D(diffuseTexture, texCoord), texture2D(normalTexture, texCoord).rgb)));"
934 << "}"
935 << "";
936 QCOMPARE(code, expected.join("\n"));
937 }
938}
939
940void tst_QShaderGenerator::shouldUseGlobalVariableRatherThanTemporaries()
941{
942 // GIVEN
943 const auto gl4 = createFormat(QShaderFormat::OpenGLCoreProfile, 4, 0);
944
945 {
946 // WHEN
947 auto vertexPosition = createNode({
948 createPort(QShaderNodePort::Output, "vertexPosition")
949 });
950 vertexPosition.addRule(gl4, QShaderNode::Rule("vec4 $vertexPosition = vertexPosition;",
951 QByteArrayList() << "in vec4 vertexPosition;"));
952
953 auto fakeMultiPlyNoSpace = createNode({
954 createPort(QShaderNodePort::Input, "varName"),
955 createPort(QShaderNodePort::Output, "out")
956 });
957 fakeMultiPlyNoSpace.addRule(gl4, QShaderNode::Rule("vec4 $out = $varName*speed;"));
958
959 auto fakeMultiPlySpace = createNode({
960 createPort(QShaderNodePort::Input, "varName"),
961 createPort(QShaderNodePort::Output, "out")
962 });
963 fakeMultiPlySpace.addRule(gl4, QShaderNode::Rule("vec4 $out = $varName * speed;"));
964
965 auto fakeJoinNoSpace = createNode({
966 createPort(QShaderNodePort::Input, "varName"),
967 createPort(QShaderNodePort::Output, "out")
968 });
969 fakeJoinNoSpace.addRule(gl4, QShaderNode::Rule("vec4 $out = vec4($varName.xyz,$varName.w);"));
970
971 auto fakeJoinSpace = createNode({
972 createPort(QShaderNodePort::Input, "varName"),
973 createPort(QShaderNodePort::Output, "out")
974 });
975 fakeJoinSpace.addRule(gl4, QShaderNode::Rule("vec4 $out = vec4($varName.xyz, $varName.w);"));
976
977 auto fakeAdd = createNode({
978 createPort(QShaderNodePort::Input, "varName"),
979 createPort(QShaderNodePort::Output, "out")
980 });
981 fakeAdd.addRule(gl4, QShaderNode::Rule("vec4 $out = $varName.xyzw + $varName;"));
982
983 auto fakeSub = createNode({
984 createPort(QShaderNodePort::Input, "varName"),
985 createPort(QShaderNodePort::Output, "out")
986 });
987 fakeSub.addRule(gl4, QShaderNode::Rule("vec4 $out = $varName.xyzw - $varName;"));
988
989 auto fakeDiv = createNode({
990 createPort(QShaderNodePort::Input, "varName"),
991 createPort(QShaderNodePort::Output, "out")
992 });
993 fakeDiv.addRule(gl4, QShaderNode::Rule("vec4 $out = $varName / v0;"));
994
995 auto fragColor = createNode({
996 createPort(QShaderNodePort::Input, "input1"),
997 createPort(QShaderNodePort::Input, "input2"),
998 createPort(QShaderNodePort::Input, "input3"),
999 createPort(QShaderNodePort::Input, "input4"),
1000 createPort(QShaderNodePort::Input, "input5"),
1001 createPort(QShaderNodePort::Input, "input6"),
1002 createPort(QShaderNodePort::Input, "input7")
1003 });
1004 fragColor.addRule(gl4, QShaderNode::Rule("fragColor = $input1 + $input2 + $input3 + $input4 + $input5 + $input6 + $input7;",
1005 QByteArrayList() << "out vec4 fragColor;"));
1006
1007 const auto graph = [=] {
1008 auto res = QShaderGraph();
1009
1010 res.addNode(vertexPosition);
1011 res.addNode(fakeMultiPlyNoSpace);
1012 res.addNode(fakeMultiPlySpace);
1013 res.addNode(fakeJoinNoSpace);
1014 res.addNode(fakeJoinSpace);
1015 res.addNode(fakeAdd);
1016 res.addNode(fakeSub);
1017 res.addNode(fakeDiv);
1018 res.addNode(fragColor);
1019
1020 res.addEdge(createEdge(vertexPosition.uuid(), "vertexPosition", fakeMultiPlyNoSpace.uuid(), "varName"));
1021 res.addEdge(createEdge(vertexPosition.uuid(), "vertexPosition", fakeMultiPlySpace.uuid(), "varName"));
1022 res.addEdge(createEdge(vertexPosition.uuid(), "vertexPosition", fakeJoinNoSpace.uuid(), "varName"));
1023 res.addEdge(createEdge(vertexPosition.uuid(), "vertexPosition", fakeJoinSpace.uuid(), "varName"));
1024 res.addEdge(createEdge(vertexPosition.uuid(), "vertexPosition", fakeAdd.uuid(), "varName"));
1025 res.addEdge(createEdge(vertexPosition.uuid(), "vertexPosition", fakeSub.uuid(), "varName"));
1026 res.addEdge(createEdge(vertexPosition.uuid(), "vertexPosition", fakeDiv.uuid(), "varName"));
1027 res.addEdge(createEdge(fakeMultiPlyNoSpace.uuid(), "out", fragColor.uuid(), "input1"));
1028 res.addEdge(createEdge(fakeMultiPlySpace.uuid(), "out", fragColor.uuid(), "input2"));
1029 res.addEdge(createEdge(fakeJoinNoSpace.uuid(), "out", fragColor.uuid(), "input3"));
1030 res.addEdge(createEdge(fakeJoinSpace.uuid(), "out", fragColor.uuid(), "input4"));
1031 res.addEdge(createEdge(fakeAdd.uuid(), "out", fragColor.uuid(), "input5"));
1032 res.addEdge(createEdge(fakeSub.uuid(), "out", fragColor.uuid(), "input6"));
1033 res.addEdge(createEdge(fakeDiv.uuid(), "out", fragColor.uuid(), "input7"));
1034
1035 return res;
1036 }();
1037
1038 auto generator = QShaderGenerator();
1039 generator.graph = graph;
1040 generator.format = gl4;
1041
1042 const auto code = generator.createShaderCode({"diffuseUniform", "normalUniform"});
1043
1044 // THEN
1045 const auto expected = QByteArrayList()
1046 << "#version 400 core"
1047 << ""
1048 << "in vec4 vertexPosition;"
1049 << "out vec4 fragColor;"
1050 << ""
1051 << "void main()"
1052 << "{"
1053 << " fragColor = (((((((vertexPosition*speed + vertexPosition * speed + ((vec4(vertexPosition.xyz,vertexPosition.w))) + ((vec4(vertexPosition.xyz, vertexPosition.w))) + ((vertexPosition.xyzw + vertexPosition)) + ((vertexPosition.xyzw - vertexPosition)) + ((vertexPosition / vertexPosition)))))))));"
1054 << "}"
1055 << "";
1056 QCOMPARE(code, expected.join("\n"));
1057 }
1058}
1059
1060void tst_QShaderGenerator::shouldGenerateTemporariesWisely()
1061{
1062 // GIVEN
1063 const auto gl4 = createFormat(QShaderFormat::OpenGLCoreProfile, 4, 0);
1064
1065 {
1066 auto attribute = createNode({
1067 createPort(QShaderNodePort::Output, "vertexPosition")
1068 });
1069 attribute.addRule(gl4, QShaderNode::Rule("vec4 $vertexPosition = vertexPosition;",
1070 QByteArrayList() << "in vec4 vertexPosition;"));
1071
1072 auto complexFunction = createNode({
1073 createPort(QShaderNodePort::Input, "inputVarName"),
1074 createPort(QShaderNodePort::Output, "out")
1075 });
1076 complexFunction.addRule(gl4, QShaderNode::Rule("vec4 $out = $inputVarName * 2.0;"));
1077
1078 auto complexFunction2 = createNode({
1079 createPort(QShaderNodePort::Input, "inputVarName"),
1080 createPort(QShaderNodePort::Output, "out")
1081 });
1082 complexFunction2.addRule(gl4, QShaderNode::Rule("vec4 $out = $inputVarName * 4.0;"));
1083
1084 auto complexFunction3 = createNode({
1085 createPort(QShaderNodePort::Input, "a"),
1086 createPort(QShaderNodePort::Input, "b"),
1087 createPort(QShaderNodePort::Output, "out")
1088 });
1089 complexFunction3.addRule(gl4, QShaderNode::Rule("vec4 $out = $a + $b;"));
1090
1091 auto shaderOutput1 = createNode({
1092 createPort(QShaderNodePort::Input, "input")
1093 });
1094
1095 shaderOutput1.addRule(gl4, QShaderNode::Rule("shaderOutput1 = $input;",
1096 QByteArrayList() << "out vec4 shaderOutput1;"));
1097
1098 auto shaderOutput2 = createNode({
1099 createPort(QShaderNodePort::Input, "input")
1100 });
1101
1102 shaderOutput2.addRule(gl4, QShaderNode::Rule("shaderOutput2 = $input;",
1103 QByteArrayList() << "out vec4 shaderOutput2;"));
1104
1105 {
1106 // WHEN
1107 const auto graph = [=] {
1108 auto res = QShaderGraph();
1109
1110 res.addNode(attribute);
1111 res.addNode(complexFunction);
1112 res.addNode(shaderOutput1);
1113
1114 res.addEdge(createEdge(attribute.uuid(), "vertexPosition", complexFunction.uuid(), "inputVarName"));
1115 res.addEdge(createEdge(complexFunction.uuid(), "out", shaderOutput1.uuid(), "input"));
1116
1117 return res;
1118 }();
1119
1120 auto generator = QShaderGenerator();
1121 generator.graph = graph;
1122 generator.format = gl4;
1123
1124 const auto code = generator.createShaderCode();
1125
1126 // THEN
1127 const auto expected = QByteArrayList()
1128 << "#version 400 core"
1129 << ""
1130 << "in vec4 vertexPosition;"
1131 << "out vec4 shaderOutput1;"
1132 << ""
1133 << "void main()"
1134 << "{"
1135 << " shaderOutput1 = vertexPosition * 2.0;"
1136 << "}"
1137 << "";
1138 QCOMPARE(code, expected.join("\n"));
1139 }
1140
1141 {
1142 // WHEN
1143 const auto graph = [=] {
1144 auto res = QShaderGraph();
1145
1146 res.addNode(attribute);
1147 res.addNode(complexFunction);
1148 res.addNode(shaderOutput1);
1149 res.addNode(shaderOutput2);
1150
1151 res.addEdge(createEdge(attribute.uuid(), "vertexPosition", complexFunction.uuid(), "inputVarName"));
1152 res.addEdge(createEdge(complexFunction.uuid(), "out", shaderOutput1.uuid(), "input"));
1153 res.addEdge(createEdge(complexFunction.uuid(), "out", shaderOutput2.uuid(), "input"));
1154
1155 return res;
1156 }();
1157
1158 auto generator = QShaderGenerator();
1159 generator.graph = graph;
1160 generator.format = gl4;
1161
1162 const auto code = generator.createShaderCode();
1163
1164 // THEN
1165 const auto expected = QByteArrayList()
1166 << "#version 400 core"
1167 << ""
1168 << "in vec4 vertexPosition;"
1169 << "out vec4 shaderOutput1;"
1170 << "out vec4 shaderOutput2;"
1171 << ""
1172 << "void main()"
1173 << "{"
1174 << " vec4 v1 = vertexPosition * 2.0;"
1175 << " shaderOutput2 = v1;"
1176 << " shaderOutput1 = v1;"
1177 << "}"
1178 << "";
1179 QCOMPARE(code, expected.join("\n"));
1180 }
1181
1182 {
1183 // WHEN
1184 const auto graph = [=] {
1185 auto res = QShaderGraph();
1186
1187 res.addNode(attribute);
1188 res.addNode(complexFunction);
1189 res.addNode(complexFunction2);
1190 res.addNode(complexFunction3);
1191 res.addNode(shaderOutput1);
1192 res.addNode(shaderOutput2);
1193
1194 res.addEdge(createEdge(attribute.uuid(), "vertexPosition", complexFunction.uuid(), "inputVarName"));
1195 res.addEdge(createEdge(attribute.uuid(), "vertexPosition", complexFunction2.uuid(), "inputVarName"));
1196
1197 res.addEdge(createEdge(complexFunction.uuid(), "out", complexFunction3.uuid(), "a"));
1198 res.addEdge(createEdge(complexFunction2.uuid(), "out", complexFunction3.uuid(), "b"));
1199
1200 res.addEdge(createEdge(complexFunction3.uuid(), "out", shaderOutput1.uuid(), "input"));
1201 res.addEdge(createEdge(complexFunction2.uuid(), "out", shaderOutput2.uuid(), "input"));
1202
1203 return res;
1204 }();
1205
1206 auto generator = QShaderGenerator();
1207 generator.graph = graph;
1208 generator.format = gl4;
1209
1210 const auto code = generator.createShaderCode();
1211
1212 // THEN
1213 const auto expected = QByteArrayList()
1214 << "#version 400 core"
1215 << ""
1216 << "in vec4 vertexPosition;"
1217 << "out vec4 shaderOutput1;"
1218 << "out vec4 shaderOutput2;"
1219 << ""
1220 << "void main()"
1221 << "{"
1222 << " vec4 v2 = vertexPosition * 4.0;"
1223 << " shaderOutput2 = v2;"
1224 << " shaderOutput1 = (vertexPosition * 2.0 + v2);"
1225 << "}"
1226 << "";
1227 QCOMPARE(code, expected.join("\n"));
1228 }
1229 }
1230}
1231
1232QTEST_MAIN(tst_QShaderGenerator)
1233
1234#include "tst_qshadergenerator.moc"
1235