1/****************************************************************************
2**
3** Copyright (C) 2016 Paul Lemire <paul.lemire350@gmail.com>
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt3D module 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// TODO Remove in Qt6
30#include <QtCore/qcompilerdetection.h>
31QT_WARNING_DISABLE_DEPRECATED
32
33#include <QtTest/QTest>
34#include <Qt3DRender/qshaderprogram.h>
35#include <Qt3DRender/private/qshaderprogram_p.h>
36#include <QObject>
37#include <QSignalSpy>
38#include <Qt3DCore/qpropertyupdatedchange.h>
39#include <Qt3DCore/private/qnodecreatedchangegenerator_p.h>
40#include <Qt3DCore/qnodecreatedchange.h>
41#include "testpostmanarbiter.h"
42
43// We need to call QShaderProgram::sceneChangeEvent
44class tst_QShaderProgram : public Qt3DRender::QShaderProgram
45{
46 Q_OBJECT
47
48public:
49 tst_QShaderProgram()
50 : Qt3DRender::QShaderProgram()
51 {
52 qRegisterMetaType<Qt3DRender::QShaderProgram::Format>(typeName: "Format");
53 }
54
55private Q_SLOTS:
56
57 void checkDefaultConstruction()
58 {
59 // GIVEN
60 Qt3DRender::QShaderProgram shaderProgram;
61
62 // THEN
63 QCOMPARE(shaderProgram.vertexShaderCode(), QByteArray());
64 QCOMPARE(shaderProgram.tessellationControlShaderCode(), QByteArray());
65 QCOMPARE(shaderProgram.tessellationEvaluationShaderCode(), QByteArray());
66 QCOMPARE(shaderProgram.geometryShaderCode(), QByteArray());
67 QCOMPARE(shaderProgram.fragmentShaderCode(), QByteArray());
68 QCOMPARE(shaderProgram.computeShaderCode(), QByteArray());
69 QCOMPARE(shaderProgram.log(), QString());
70 QCOMPARE(shaderProgram.status(), Qt3DRender::QShaderProgram::NotReady);
71 QCOMPARE(shaderProgram.format(), Qt3DRender::QShaderProgram::GLSL);
72 }
73
74 void checkPropertyChanges()
75 {
76 // GIVEN
77 Qt3DRender::QShaderProgram shaderProgram;
78
79 {
80 // WHEN
81 QSignalSpy spy(&shaderProgram, SIGNAL(vertexShaderCodeChanged(QByteArray)));
82 const QByteArray newValue = "VERTEX";
83 shaderProgram.setVertexShaderCode(newValue);
84
85 // THEN
86 QVERIFY(spy.isValid());
87 QCOMPARE(shaderProgram.vertexShaderCode(), newValue);
88 QCOMPARE(spy.count(), 1);
89
90 // WHEN
91 spy.clear();
92 shaderProgram.setVertexShaderCode(newValue);
93
94 // THEN
95 QCOMPARE(shaderProgram.vertexShaderCode(), newValue);
96 QCOMPARE(spy.count(), 0);
97 }
98 {
99 // WHEN
100 QSignalSpy spy(&shaderProgram, SIGNAL(tessellationControlShaderCodeChanged(QByteArray)));
101 const QByteArray newValue = "TESSELATION_CONTROL";
102 shaderProgram.setTessellationControlShaderCode(newValue);
103
104 // THEN
105 QVERIFY(spy.isValid());
106 QCOMPARE(shaderProgram.tessellationControlShaderCode(), newValue);
107 QCOMPARE(spy.count(), 1);
108
109 // WHEN
110 spy.clear();
111 shaderProgram.setTessellationControlShaderCode(newValue);
112
113 // THEN
114 QCOMPARE(shaderProgram.tessellationControlShaderCode(), newValue);
115 QCOMPARE(spy.count(), 0);
116 }
117 {
118 // WHEN
119 QSignalSpy spy(&shaderProgram, SIGNAL(tessellationEvaluationShaderCodeChanged(QByteArray)));
120 const QByteArray newValue = "TESSELATION_EVALUTATION";
121 shaderProgram.setTessellationEvaluationShaderCode(newValue);
122
123 // THEN
124 QVERIFY(spy.isValid());
125 QCOMPARE(shaderProgram.tessellationEvaluationShaderCode(), newValue);
126 QCOMPARE(spy.count(), 1);
127
128 // WHEN
129 spy.clear();
130 shaderProgram.setTessellationEvaluationShaderCode(newValue);
131
132 // THEN
133 QCOMPARE(shaderProgram.tessellationEvaluationShaderCode(), newValue);
134 QCOMPARE(spy.count(), 0);
135 }
136 {
137 // WHEN
138 QSignalSpy spy(&shaderProgram, SIGNAL(geometryShaderCodeChanged(QByteArray)));
139 const QByteArray newValue = "GEOMETRY";
140 shaderProgram.setGeometryShaderCode(newValue);
141
142 // THEN
143 QVERIFY(spy.isValid());
144 QCOMPARE(shaderProgram.geometryShaderCode(), newValue);
145 QCOMPARE(spy.count(), 1);
146
147 // WHEN
148 spy.clear();
149 shaderProgram.setGeometryShaderCode(newValue);
150
151 // THEN
152 QCOMPARE(shaderProgram.geometryShaderCode(), newValue);
153 QCOMPARE(spy.count(), 0);
154 }
155 {
156 // WHEN
157 QSignalSpy spy(&shaderProgram, SIGNAL(fragmentShaderCodeChanged(QByteArray)));
158 const QByteArray newValue = "FRAGMENT";
159 shaderProgram.setFragmentShaderCode(newValue);
160
161 // THEN
162 QVERIFY(spy.isValid());
163 QCOMPARE(shaderProgram.fragmentShaderCode(), newValue);
164 QCOMPARE(spy.count(), 1);
165
166 // WHEN
167 spy.clear();
168 shaderProgram.setFragmentShaderCode(newValue);
169
170 // THEN
171 QCOMPARE(shaderProgram.fragmentShaderCode(), newValue);
172 QCOMPARE(spy.count(), 0);
173 }
174 {
175 // WHEN
176 QSignalSpy spy(&shaderProgram, SIGNAL(computeShaderCodeChanged(QByteArray)));
177 const QByteArray newValue = "COMPUTE";
178 shaderProgram.setComputeShaderCode(newValue);
179
180 // THEN
181 QVERIFY(spy.isValid());
182 QCOMPARE(shaderProgram.computeShaderCode(), newValue);
183 QCOMPARE(spy.count(), 1);
184
185 // WHEN
186 spy.clear();
187 shaderProgram.setComputeShaderCode(newValue);
188
189 // THEN
190 QCOMPARE(shaderProgram.computeShaderCode(), newValue);
191 QCOMPARE(spy.count(), 0);
192 }
193 {
194 // WHEN
195 QSignalSpy spy(&shaderProgram, SIGNAL(formatChanged(Format)));
196 const QShaderProgram::Format newValue = QShaderProgram::SPIRV;
197 shaderProgram.setFormat(newValue);
198
199 // THEN
200 QVERIFY(spy.isValid());
201 QCOMPARE(shaderProgram.format(), newValue);
202 QCOMPARE(spy.count(), 1);
203
204 // WHEN
205 spy.clear();
206 shaderProgram.setFormat(newValue);
207
208 // THEN
209 QCOMPARE(shaderProgram.format(), newValue);
210 QCOMPARE(spy.count(), 0);
211 }
212 }
213
214 void checkCreationData()
215 {
216 // GIVEN
217 Qt3DRender::QShaderProgram shaderProgram;
218
219 shaderProgram.setVertexShaderCode(QByteArrayLiteral("Vertex"));
220 shaderProgram.setTessellationControlShaderCode(QByteArrayLiteral("TesselControl"));
221 shaderProgram.setTessellationEvaluationShaderCode(QByteArrayLiteral("TesselEval"));
222 shaderProgram.setGeometryShaderCode(QByteArrayLiteral("Geometry"));
223 shaderProgram.setFragmentShaderCode(QByteArrayLiteral("Fragment"));
224 shaderProgram.setComputeShaderCode(QByteArrayLiteral("Compute"));
225 shaderProgram.setFormat(QShaderProgram::SPIRV);
226
227 // WHEN
228 QVector<Qt3DCore::QNodeCreatedChangeBasePtr> creationChanges;
229
230 {
231 Qt3DCore::QNodeCreatedChangeGenerator creationChangeGenerator(&shaderProgram);
232 creationChanges = creationChangeGenerator.creationChanges();
233 }
234
235 // THEN
236 {
237 QCOMPARE(creationChanges.size(), 1);
238
239 const auto creationChangeData = qSharedPointerCast<Qt3DCore::QNodeCreatedChange<Qt3DRender::QShaderProgramData>>(src: creationChanges.first());
240 const Qt3DRender::QShaderProgramData cloneData = creationChangeData->data;
241
242 QCOMPARE(shaderProgram.vertexShaderCode(), cloneData.vertexShaderCode);
243 QCOMPARE(shaderProgram.tessellationControlShaderCode(), cloneData.tessellationControlShaderCode);
244 QCOMPARE(shaderProgram.tessellationEvaluationShaderCode(), cloneData.tessellationEvaluationShaderCode);
245 QCOMPARE(shaderProgram.geometryShaderCode(), cloneData.geometryShaderCode);
246 QCOMPARE(shaderProgram.fragmentShaderCode(), cloneData.fragmentShaderCode);
247 QCOMPARE(shaderProgram.computeShaderCode(), cloneData.computeShaderCode);
248 QCOMPARE(shaderProgram.format(), cloneData.format);
249 QCOMPARE(shaderProgram.id(), creationChangeData->subjectId());
250 QCOMPARE(shaderProgram.isEnabled(), true);
251 QCOMPARE(shaderProgram.isEnabled(), creationChangeData->isNodeEnabled());
252 QCOMPARE(shaderProgram.metaObject(), creationChangeData->metaObject());
253 }
254
255 // WHEN
256 shaderProgram.setEnabled(false);
257
258 {
259 Qt3DCore::QNodeCreatedChangeGenerator creationChangeGenerator(&shaderProgram);
260 creationChanges = creationChangeGenerator.creationChanges();
261 }
262
263 // THEN
264 {
265 QCOMPARE(creationChanges.size(), 1);
266
267 const auto creationChangeData = qSharedPointerCast<Qt3DCore::QNodeCreatedChange<Qt3DRender::QShaderProgramData>>(src: creationChanges.first());
268 const Qt3DRender::QShaderProgramData cloneData = creationChangeData->data;
269
270 QCOMPARE(shaderProgram.vertexShaderCode(), cloneData.vertexShaderCode);
271 QCOMPARE(shaderProgram.tessellationControlShaderCode(), cloneData.tessellationControlShaderCode);
272 QCOMPARE(shaderProgram.tessellationEvaluationShaderCode(), cloneData.tessellationEvaluationShaderCode);
273 QCOMPARE(shaderProgram.geometryShaderCode(), cloneData.geometryShaderCode);
274 QCOMPARE(shaderProgram.fragmentShaderCode(), cloneData.fragmentShaderCode);
275 QCOMPARE(shaderProgram.computeShaderCode(), cloneData.computeShaderCode);
276 QCOMPARE(shaderProgram.id(), creationChangeData->subjectId());
277 QCOMPARE(shaderProgram.isEnabled(), false);
278 QCOMPARE(shaderProgram.isEnabled(), creationChangeData->isNodeEnabled());
279 QCOMPARE(shaderProgram.metaObject(), creationChangeData->metaObject());
280 }
281 }
282
283 void checkVertexShaderCodeUpdate()
284 {
285 // GIVEN
286 TestArbiter arbiter;
287 Qt3DRender::QShaderProgram shaderProgram;
288 arbiter.setArbiterOnNode(&shaderProgram);
289
290 {
291 // WHEN
292 shaderProgram.setVertexShaderCode(QByteArrayLiteral("in vec3 toto;"));
293 QCoreApplication::processEvents();
294
295 // THEN
296 QCOMPARE(arbiter.events.size(), 0);
297 QCOMPARE(arbiter.dirtyNodes.size(), 1);
298 QCOMPARE(arbiter.dirtyNodes.front(), &shaderProgram);
299
300 arbiter.dirtyNodes.clear();
301 }
302
303 {
304 // WHEN
305 shaderProgram.setVertexShaderCode(QByteArrayLiteral("in vec3 toto;"));
306 QCoreApplication::processEvents();
307
308 // THEN
309 QCOMPARE(arbiter.events.size(), 0);
310 QCOMPARE(arbiter.dirtyNodes.size(), 0);
311 }
312
313 }
314
315 void checkTessellationControlShaderCodeUpdate()
316 {
317 // GIVEN
318 TestArbiter arbiter;
319 Qt3DRender::QShaderProgram shaderProgram;
320 arbiter.setArbiterOnNode(&shaderProgram);
321
322 {
323 // WHEN
324 shaderProgram.setTessellationControlShaderCode(QByteArrayLiteral("in vec3 toto2;"));
325 QCoreApplication::processEvents();
326
327 // THEN
328 QCOMPARE(arbiter.events.size(), 0);
329 QCOMPARE(arbiter.dirtyNodes.size(), 1);
330 QCOMPARE(arbiter.dirtyNodes.front(), &shaderProgram);
331
332 arbiter.dirtyNodes.clear();
333 }
334
335 {
336 // WHEN
337 shaderProgram.setTessellationControlShaderCode(QByteArrayLiteral("in vec3 toto2;"));
338 QCoreApplication::processEvents();
339
340 // THEN
341 QCOMPARE(arbiter.events.size(), 0);
342 QCOMPARE(arbiter.dirtyNodes.size(), 0);
343 }
344
345 }
346
347 void checkTessellationEvaluationShaderCodeUpdate()
348 {
349 // GIVEN
350 TestArbiter arbiter;
351 Qt3DRender::QShaderProgram shaderProgram;
352 arbiter.setArbiterOnNode(&shaderProgram);
353
354 {
355 // WHEN
356 shaderProgram.setTessellationEvaluationShaderCode(QByteArrayLiteral("in vec3 toto3;"));
357 QCoreApplication::processEvents();
358
359 // THEN
360 QCOMPARE(arbiter.events.size(), 0);
361 QCOMPARE(arbiter.dirtyNodes.size(), 1);
362 QCOMPARE(arbiter.dirtyNodes.front(), &shaderProgram);
363
364 arbiter.dirtyNodes.clear();
365 }
366
367 {
368 // WHEN
369 shaderProgram.setTessellationEvaluationShaderCode(QByteArrayLiteral("in vec3 toto3;"));
370 QCoreApplication::processEvents();
371
372 // THEN
373 QCOMPARE(arbiter.events.size(), 0);
374 QCOMPARE(arbiter.dirtyNodes.size(), 0);
375 }
376
377 }
378
379 void checkGeometryShaderCodeUpdate()
380 {
381 // GIVEN
382 TestArbiter arbiter;
383 Qt3DRender::QShaderProgram shaderProgram;
384 arbiter.setArbiterOnNode(&shaderProgram);
385
386 {
387 // WHEN
388 shaderProgram.setGeometryShaderCode(QByteArrayLiteral("in vec3 toto4;"));
389 QCoreApplication::processEvents();
390
391 // THEN
392 QCOMPARE(arbiter.events.size(), 0);
393 QCOMPARE(arbiter.dirtyNodes.size(), 1);
394 QCOMPARE(arbiter.dirtyNodes.front(), &shaderProgram);
395
396 arbiter.dirtyNodes.clear();
397 }
398
399 {
400 // WHEN
401 shaderProgram.setGeometryShaderCode(QByteArrayLiteral("in vec3 toto4;"));
402 QCoreApplication::processEvents();
403
404 // THEN
405 QCOMPARE(arbiter.events.size(), 0);
406 QCOMPARE(arbiter.dirtyNodes.size(), 0);
407 }
408
409 }
410
411 void checkFragmentShaderCodeUpdate()
412 {
413 // GIVEN
414 TestArbiter arbiter;
415 Qt3DRender::QShaderProgram shaderProgram;
416 arbiter.setArbiterOnNode(&shaderProgram);
417
418 {
419 // WHEN
420 shaderProgram.setFragmentShaderCode(QByteArrayLiteral("out vec4 fragColor;"));
421 QCoreApplication::processEvents();
422
423 // THEN
424 QCOMPARE(arbiter.events.size(), 0);
425 QCOMPARE(arbiter.dirtyNodes.size(), 1);
426 QCOMPARE(arbiter.dirtyNodes.front(), &shaderProgram);
427
428 arbiter.dirtyNodes.clear();
429 }
430
431 {
432 // WHEN
433 shaderProgram.setFragmentShaderCode(QByteArrayLiteral("out vec4 fragColor;"));
434 QCoreApplication::processEvents();
435
436 // THEN
437 QCOMPARE(arbiter.events.size(), 0);
438 QCOMPARE(arbiter.dirtyNodes.size(), 0);
439 }
440
441 }
442
443 void checkComputeShaderCodeUpdate()
444 {
445 // GIVEN
446 TestArbiter arbiter;
447 Qt3DRender::QShaderProgram shaderProgram;
448 arbiter.setArbiterOnNode(&shaderProgram);
449
450 {
451 // WHEN
452 shaderProgram.setComputeShaderCode(QByteArrayLiteral("uniform vec3 temp;"));
453 QCoreApplication::processEvents();
454
455 // THEN
456 QCOMPARE(arbiter.events.size(), 0);
457 QCOMPARE(arbiter.dirtyNodes.size(), 1);
458 QCOMPARE(arbiter.dirtyNodes.front(), &shaderProgram);
459
460 arbiter.dirtyNodes.clear();
461 }
462
463 {
464 // WHEN
465 shaderProgram.setComputeShaderCode(QByteArrayLiteral("uniform vec3 temp;"));
466 QCoreApplication::processEvents();
467
468 // THEN
469 QCOMPARE(arbiter.events.size(), 0);
470 QCOMPARE(arbiter.dirtyNodes.size(), 0);
471 }
472
473 }
474
475 void checkLogPropertyUpdate()
476 {
477 // GIVEN
478 TestArbiter arbiter;
479 arbiter.setArbiterOnNode(this);
480 QSignalSpy spy(this, SIGNAL(logChanged(QString)));
481 const QString logValue = QStringLiteral("Some log...");
482
483 // THEN
484 QVERIFY(spy.isValid());
485
486 // WHEN
487 Qt3DCore::QPropertyUpdatedChangePtr valueChange(new Qt3DCore::QPropertyUpdatedChange(Qt3DCore::QNodeId()));
488 valueChange->setPropertyName("log");
489 valueChange->setValue(QVariant::fromValue(value: logValue));
490 sceneChangeEvent(change: valueChange);
491
492 // THEN
493 QCOMPARE(spy.count(), 1);
494 QCOMPARE(arbiter.events.size(), 0);
495 QCOMPARE(log(), logValue);
496
497 // WHEN
498 spy.clear();
499 sceneChangeEvent(change: valueChange);
500
501 // THEN
502 QCOMPARE(spy.count(), 0);
503 QCOMPARE(arbiter.events.size(), 0);
504 QCOMPARE(log(), logValue);
505
506 // Cleanup
507 Qt3DCore::QNodePrivate::get(q: this)->setArbiter(nullptr);
508 }
509
510 void checkStatusPropertyUpdate()
511 {
512 // GIVEN
513 qRegisterMetaType<Qt3DRender::QShaderProgram::Status>(typeName: "Status");
514 TestArbiter arbiter;
515 arbiter.setArbiterOnNode(this);
516 QSignalSpy spy(this, SIGNAL(statusChanged(Status)));
517 const Qt3DRender::QShaderProgram::Status newStatus = Qt3DRender::QShaderProgram::Error;
518
519 // THEN
520 QVERIFY(spy.isValid());
521
522 // WHEN
523 Qt3DCore::QPropertyUpdatedChangePtr valueChange(new Qt3DCore::QPropertyUpdatedChange(Qt3DCore::QNodeId()));
524 valueChange->setPropertyName("status");
525 valueChange->setValue(QVariant::fromValue(value: newStatus));
526 sceneChangeEvent(change: valueChange);
527
528 // THEN
529 QCOMPARE(spy.count(), 1);
530 QCOMPARE(arbiter.events.size(), 0);
531 QCOMPARE(status(), newStatus);
532
533 // WHEN
534 spy.clear();
535 sceneChangeEvent(change: valueChange);
536
537 // THEN
538 QCOMPARE(spy.count(), 0);
539 QCOMPARE(arbiter.events.size(), 0);
540 QCOMPARE(status(), newStatus);
541
542 // Cleanup
543 Qt3DCore::QNodePrivate::get(q: this)->setArbiter(nullptr);
544 }
545
546 void checkIncludes()
547 {
548 // GIVEN
549 Qt3DRender::QShaderProgram shaderProgram;
550 QByteArray includedContent = shaderProgram.loadSource(sourceUrl: QUrl(QStringLiteral("qrc:/included.frag")));
551
552 // WHEN (test relative include)
553 QByteArray mainContent = shaderProgram.loadSource(sourceUrl: QUrl(QStringLiteral("qrc:/main.frag")));
554
555 // THEN
556 QVERIFY(mainContent.indexOf(includedContent) == 0);
557
558 // WHEN (test absolute include)
559 mainContent = shaderProgram.loadSource(sourceUrl: QUrl(QStringLiteral("qrc:/mainabsolute.frag")));
560
561 // THEN
562 QVERIFY(mainContent.indexOf(includedContent) == 0);
563 }
564
565 void checkFormatPropertyUpdate()
566 {
567 // GIVEN
568 TestArbiter arbiter;
569 Qt3DRender::QShaderProgram shaderProgram;
570 arbiter.setArbiterOnNode(&shaderProgram);
571
572 QSignalSpy spy(&shaderProgram, SIGNAL(formatChanged(Format)));
573
574 // THEN
575 QVERIFY(spy.isValid());
576
577 {
578 // WHEN
579 shaderProgram.setFormat(QShaderProgram::SPIRV);
580 QCoreApplication::processEvents();
581
582 // THEN
583 QCOMPARE(spy.count(), 1);
584 QCOMPARE(arbiter.events.size(), 0);
585
586 spy.clear();
587 }
588
589 {
590 // WHEN
591 shaderProgram.setFormat(QShaderProgram::SPIRV);
592 QCoreApplication::processEvents();
593
594 // THEN
595 QCOMPARE(spy.count(), 0);
596 QCOMPARE(arbiter.events.size(), 0);
597 }
598 }
599
600};
601
602QTEST_MAIN(tst_QShaderProgram)
603
604#include "tst_qshaderprogram.moc"
605

source code of qt3d/tests/auto/render/qshaderprogram/tst_qshaderprogram.cpp