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

source code of qtbase/tests/auto/gui/util/qshadergenerator/tst_qshadergenerator.cpp