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

source code of qt3d/tests/auto/render/shadergraph/qshadergenerator/tst_qshadergenerator.cpp