1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB). |
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:LGPL$ |
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 Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include <QtTest/QTest> |
41 | #include <Qt3DRender/qshaderimage.h> |
42 | #include <Qt3DRender/qtexture.h> |
43 | #include <Qt3DRender/private/qshaderimage_p.h> |
44 | #include <QObject> |
45 | #include <QSignalSpy> |
46 | #include <Qt3DCore/private/qnodecreatedchangegenerator_p.h> |
47 | #include <Qt3DCore/qnodecreatedchange.h> |
48 | #include "testpostmanarbiter.h" |
49 | |
50 | class tst_QShaderImage : public QObject |
51 | { |
52 | Q_OBJECT |
53 | |
54 | private Q_SLOTS: |
55 | |
56 | void initTestCase() |
57 | { |
58 | qRegisterMetaType<Qt3DRender::QAbstractTexture*>(typeName: "Qt3DRender::QAbstractTexture*" ); |
59 | |
60 | } |
61 | |
62 | void checkDefaultConstruction() |
63 | { |
64 | // GIVEN |
65 | Qt3DRender::QShaderImage shaderImage; |
66 | |
67 | // THEN |
68 | QVERIFY(shaderImage.texture() == nullptr); |
69 | QCOMPARE(shaderImage.layered(), false); |
70 | QCOMPARE(shaderImage.mipLevel(), 0); |
71 | QCOMPARE(shaderImage.layer(), 0); |
72 | QCOMPARE(shaderImage.access(), Qt3DRender::QShaderImage::ReadWrite); |
73 | QCOMPARE(shaderImage.format(), Qt3DRender::QShaderImage::Automatic); |
74 | } |
75 | |
76 | void checkPropertyChanges() |
77 | { |
78 | // GIVEN |
79 | Qt3DRender::QShaderImage shaderImage; |
80 | |
81 | { |
82 | // WHEN |
83 | QSignalSpy spy(&shaderImage, SIGNAL(textureChanged(Qt3DRender::QAbstractTexture *))); |
84 | Qt3DRender::QTexture2D newValue; |
85 | shaderImage.setTexture(&newValue); |
86 | |
87 | // THEN |
88 | QVERIFY(spy.isValid()); |
89 | QCOMPARE(shaderImage.texture(), &newValue); |
90 | QCOMPARE(spy.count(), 1); |
91 | |
92 | // WHEN |
93 | spy.clear(); |
94 | shaderImage.setTexture(&newValue); |
95 | |
96 | // THEN |
97 | QCOMPARE(shaderImage.texture(), &newValue); |
98 | QCOMPARE(spy.count(), 0); |
99 | } |
100 | { |
101 | // WHEN |
102 | QSignalSpy spy(&shaderImage, SIGNAL(layeredChanged(bool))); |
103 | const bool newValue = true; |
104 | shaderImage.setLayered(newValue); |
105 | |
106 | // THEN |
107 | QVERIFY(spy.isValid()); |
108 | QCOMPARE(shaderImage.layered(), newValue); |
109 | QCOMPARE(spy.count(), 1); |
110 | |
111 | // WHEN |
112 | spy.clear(); |
113 | shaderImage.setLayered(newValue); |
114 | |
115 | // THEN |
116 | QCOMPARE(shaderImage.layered(), newValue); |
117 | QCOMPARE(spy.count(), 0); |
118 | } |
119 | { |
120 | // WHEN |
121 | QSignalSpy spy(&shaderImage, SIGNAL(mipLevelChanged(int))); |
122 | const int newValue = 12; |
123 | shaderImage.setMipLevel(newValue); |
124 | |
125 | // THEN |
126 | QVERIFY(spy.isValid()); |
127 | QCOMPARE(shaderImage.mipLevel(), newValue); |
128 | QCOMPARE(spy.count(), 1); |
129 | |
130 | // WHEN |
131 | spy.clear(); |
132 | shaderImage.setMipLevel(newValue); |
133 | |
134 | // THEN |
135 | QCOMPARE(shaderImage.mipLevel(), newValue); |
136 | QCOMPARE(spy.count(), 0); |
137 | } |
138 | { |
139 | // WHEN |
140 | QSignalSpy spy(&shaderImage, SIGNAL(layerChanged(int))); |
141 | const int newValue = 2; |
142 | shaderImage.setLayer(newValue); |
143 | |
144 | // THEN |
145 | QVERIFY(spy.isValid()); |
146 | QCOMPARE(shaderImage.layer(), newValue); |
147 | QCOMPARE(spy.count(), 1); |
148 | |
149 | // WHEN |
150 | spy.clear(); |
151 | shaderImage.setLayer(newValue); |
152 | |
153 | // THEN |
154 | QCOMPARE(shaderImage.layer(), newValue); |
155 | QCOMPARE(spy.count(), 0); |
156 | } |
157 | { |
158 | // WHEN |
159 | QSignalSpy spy(&shaderImage, SIGNAL(accessChanged(Access))); |
160 | const Qt3DRender::QShaderImage::Access newValue = Qt3DRender::QShaderImage::ReadOnly; |
161 | shaderImage.setAccess(newValue); |
162 | |
163 | // THEN |
164 | QVERIFY(spy.isValid()); |
165 | QCOMPARE(shaderImage.access(), newValue); |
166 | QCOMPARE(spy.count(), 1); |
167 | |
168 | // WHEN |
169 | spy.clear(); |
170 | shaderImage.setAccess(newValue); |
171 | |
172 | // THEN |
173 | QCOMPARE(shaderImage.access(), newValue); |
174 | QCOMPARE(spy.count(), 0); |
175 | } |
176 | { |
177 | // WHEN |
178 | QSignalSpy spy(&shaderImage, SIGNAL(formatChanged(ImageFormat))); |
179 | const Qt3DRender::QShaderImage::ImageFormat newValue = Qt3DRender::QShaderImage::RG8U; |
180 | shaderImage.setFormat(newValue); |
181 | |
182 | // THEN |
183 | QVERIFY(spy.isValid()); |
184 | QCOMPARE(shaderImage.format(), newValue); |
185 | QCOMPARE(spy.count(), 1); |
186 | |
187 | // WHEN |
188 | spy.clear(); |
189 | shaderImage.setFormat(newValue); |
190 | |
191 | // THEN |
192 | QCOMPARE(shaderImage.format(), newValue); |
193 | QCOMPARE(spy.count(), 0); |
194 | } |
195 | } |
196 | |
197 | void checkCreationData() |
198 | { |
199 | // GIVEN |
200 | Qt3DRender::QShaderImage shaderImage; |
201 | Qt3DRender::QTexture2D t; |
202 | |
203 | shaderImage.setTexture(&t); |
204 | shaderImage.setLayered(true); |
205 | shaderImage.setMipLevel(883); |
206 | shaderImage.setLayer(1584); |
207 | shaderImage.setAccess(Qt3DRender::QShaderImage::WriteOnly); |
208 | shaderImage.setFormat(Qt3DRender::QShaderImage::R32F); |
209 | |
210 | // WHEN |
211 | QVector<Qt3DCore::QNodeCreatedChangeBasePtr> creationChanges; |
212 | |
213 | { |
214 | Qt3DCore::QNodeCreatedChangeGenerator creationChangeGenerator(&shaderImage); |
215 | creationChanges = creationChangeGenerator.creationChanges(); |
216 | } |
217 | |
218 | // THEN |
219 | { |
220 | // ShaderImage + Texture creation |
221 | QCOMPARE(creationChanges.size(), 2); |
222 | |
223 | const auto creationChangeData = qSharedPointerCast<Qt3DCore::QNodeCreatedChange<Qt3DRender::QShaderImageData>>(src: creationChanges.first()); |
224 | const Qt3DRender::QShaderImageData cloneData = creationChangeData->data; |
225 | |
226 | QCOMPARE(shaderImage.texture()->id(), cloneData.textureId); |
227 | QCOMPARE(shaderImage.layered(), cloneData.layered); |
228 | QCOMPARE(shaderImage.mipLevel(), cloneData.mipLevel); |
229 | QCOMPARE(shaderImage.layer(), cloneData.layer); |
230 | QCOMPARE(shaderImage.access(), cloneData.access); |
231 | QCOMPARE(shaderImage.format(), cloneData.format); |
232 | QCOMPARE(shaderImage.id(), creationChangeData->subjectId()); |
233 | QCOMPARE(shaderImage.isEnabled(), true); |
234 | QCOMPARE(shaderImage.isEnabled(), creationChangeData->isNodeEnabled()); |
235 | QCOMPARE(shaderImage.metaObject(), creationChangeData->metaObject()); |
236 | } |
237 | |
238 | // WHEN |
239 | shaderImage.setEnabled(false); |
240 | |
241 | { |
242 | Qt3DCore::QNodeCreatedChangeGenerator creationChangeGenerator(&shaderImage); |
243 | creationChanges = creationChangeGenerator.creationChanges(); |
244 | } |
245 | |
246 | // THEN |
247 | { |
248 | // ShaderImage + Texture creation |
249 | QCOMPARE(creationChanges.size(), 2); |
250 | |
251 | const auto creationChangeData = qSharedPointerCast<Qt3DCore::QNodeCreatedChange<Qt3DRender::QShaderImageData>>(src: creationChanges.first()); |
252 | const Qt3DRender::QShaderImageData cloneData = creationChangeData->data; |
253 | |
254 | QCOMPARE(shaderImage.texture()->id(), cloneData.textureId); |
255 | QCOMPARE(shaderImage.layered(), cloneData.layered); |
256 | QCOMPARE(shaderImage.mipLevel(), cloneData.mipLevel); |
257 | QCOMPARE(shaderImage.layer(), cloneData.layer); |
258 | QCOMPARE(shaderImage.access(), cloneData.access); |
259 | QCOMPARE(shaderImage.format(), cloneData.format); |
260 | QCOMPARE(shaderImage.id(), creationChangeData->subjectId()); |
261 | QCOMPARE(shaderImage.isEnabled(), false); |
262 | QCOMPARE(shaderImage.isEnabled(), creationChangeData->isNodeEnabled()); |
263 | QCOMPARE(shaderImage.metaObject(), creationChangeData->metaObject()); |
264 | } |
265 | } |
266 | |
267 | void checkTextureUpdate() |
268 | { |
269 | // GIVEN |
270 | TestArbiter arbiter; |
271 | Qt3DRender::QShaderImage shaderImage; |
272 | arbiter.setArbiterOnNode(&shaderImage); |
273 | Qt3DRender::QTexture2D t; |
274 | |
275 | { |
276 | // WHEN |
277 | shaderImage.setTexture(&t); |
278 | QCoreApplication::processEvents(); |
279 | |
280 | // THEN |
281 | QCOMPARE(arbiter.events.size(), 0); |
282 | QCOMPARE(arbiter.dirtyNodes.size(), 1); |
283 | QCOMPARE(arbiter.dirtyNodes.front(), &shaderImage); |
284 | |
285 | arbiter.dirtyNodes.clear(); |
286 | } |
287 | |
288 | { |
289 | // WHEN |
290 | shaderImage.setTexture(&t); |
291 | QCoreApplication::processEvents(); |
292 | |
293 | // THEN |
294 | QCOMPARE(arbiter.events.size(), 0); |
295 | QCOMPARE(arbiter.dirtyNodes.size(), 0); |
296 | } |
297 | |
298 | } |
299 | |
300 | void checkLayeredUpdate() |
301 | { |
302 | // GIVEN |
303 | TestArbiter arbiter; |
304 | Qt3DRender::QShaderImage shaderImage; |
305 | arbiter.setArbiterOnNode(&shaderImage); |
306 | |
307 | { |
308 | // WHEN |
309 | shaderImage.setLayered(true); |
310 | QCoreApplication::processEvents(); |
311 | |
312 | // THEN |
313 | QCOMPARE(arbiter.events.size(), 0); |
314 | QCOMPARE(arbiter.dirtyNodes.size(), 1); |
315 | QCOMPARE(arbiter.dirtyNodes.front(), &shaderImage); |
316 | |
317 | arbiter.dirtyNodes.clear(); |
318 | } |
319 | |
320 | { |
321 | // WHEN |
322 | shaderImage.setLayered(true); |
323 | QCoreApplication::processEvents(); |
324 | |
325 | // THEN |
326 | QCOMPARE(arbiter.events.size(), 0); |
327 | QCOMPARE(arbiter.dirtyNodes.size(), 0); |
328 | } |
329 | |
330 | } |
331 | |
332 | void checkMipLevelUpdate() |
333 | { |
334 | // GIVEN |
335 | TestArbiter arbiter; |
336 | Qt3DRender::QShaderImage shaderImage; |
337 | arbiter.setArbiterOnNode(&shaderImage); |
338 | |
339 | { |
340 | // WHEN |
341 | shaderImage.setMipLevel(5); |
342 | QCoreApplication::processEvents(); |
343 | |
344 | // THEN |
345 | QCOMPARE(arbiter.events.size(), 0); |
346 | QCOMPARE(arbiter.dirtyNodes.size(), 1); |
347 | QCOMPARE(arbiter.dirtyNodes.front(), &shaderImage); |
348 | |
349 | arbiter.dirtyNodes.clear(); |
350 | } |
351 | |
352 | { |
353 | // WHEN |
354 | shaderImage.setMipLevel(5); |
355 | QCoreApplication::processEvents(); |
356 | |
357 | // THEN |
358 | QCOMPARE(arbiter.events.size(), 0); |
359 | QCOMPARE(arbiter.dirtyNodes.size(), 0); |
360 | } |
361 | |
362 | } |
363 | |
364 | void checkLayerUpdate() |
365 | { |
366 | // GIVEN |
367 | TestArbiter arbiter; |
368 | Qt3DRender::QShaderImage shaderImage; |
369 | arbiter.setArbiterOnNode(&shaderImage); |
370 | |
371 | { |
372 | // WHEN |
373 | shaderImage.setLayer(8); |
374 | QCoreApplication::processEvents(); |
375 | |
376 | // THEN |
377 | QCOMPARE(arbiter.events.size(), 0); |
378 | QCOMPARE(arbiter.dirtyNodes.size(), 1); |
379 | QCOMPARE(arbiter.dirtyNodes.front(), &shaderImage); |
380 | |
381 | arbiter.dirtyNodes.clear(); |
382 | } |
383 | |
384 | { |
385 | // WHEN |
386 | shaderImage.setLayer(8); |
387 | QCoreApplication::processEvents(); |
388 | |
389 | // THEN |
390 | QCOMPARE(arbiter.events.size(), 0); |
391 | QCOMPARE(arbiter.dirtyNodes.size(), 0); |
392 | } |
393 | |
394 | } |
395 | |
396 | void checkAccessUpdate() |
397 | { |
398 | // GIVEN |
399 | TestArbiter arbiter; |
400 | Qt3DRender::QShaderImage shaderImage; |
401 | arbiter.setArbiterOnNode(&shaderImage); |
402 | |
403 | { |
404 | // WHEN |
405 | shaderImage.setAccess(Qt3DRender::QShaderImage::WriteOnly); |
406 | QCoreApplication::processEvents(); |
407 | |
408 | // THEN |
409 | QCOMPARE(arbiter.events.size(), 0); |
410 | QCOMPARE(arbiter.dirtyNodes.size(), 1); |
411 | QCOMPARE(arbiter.dirtyNodes.front(), &shaderImage); |
412 | |
413 | arbiter.dirtyNodes.clear(); |
414 | } |
415 | |
416 | { |
417 | // WHEN |
418 | shaderImage.setAccess(Qt3DRender::QShaderImage::WriteOnly); |
419 | QCoreApplication::processEvents(); |
420 | |
421 | // THEN |
422 | QCOMPARE(arbiter.events.size(), 0); |
423 | QCOMPARE(arbiter.dirtyNodes.size(), 0); |
424 | } |
425 | |
426 | } |
427 | |
428 | void checkFormatUpdate() |
429 | { |
430 | // GIVEN |
431 | TestArbiter arbiter; |
432 | Qt3DRender::QShaderImage shaderImage; |
433 | arbiter.setArbiterOnNode(&shaderImage); |
434 | |
435 | { |
436 | // WHEN |
437 | shaderImage.setFormat(Qt3DRender::QShaderImage::RG16F); |
438 | QCoreApplication::processEvents(); |
439 | |
440 | // THEN |
441 | QCOMPARE(arbiter.events.size(), 0); |
442 | QCOMPARE(arbiter.dirtyNodes.size(), 1); |
443 | QCOMPARE(arbiter.dirtyNodes.front(), &shaderImage); |
444 | |
445 | arbiter.dirtyNodes.clear(); |
446 | } |
447 | |
448 | { |
449 | // WHEN |
450 | shaderImage.setFormat(Qt3DRender::QShaderImage::RG16F); |
451 | QCoreApplication::processEvents(); |
452 | |
453 | // THEN |
454 | QCOMPARE(arbiter.events.size(), 0); |
455 | QCOMPARE(arbiter.dirtyNodes.size(), 0); |
456 | } |
457 | |
458 | } |
459 | |
460 | void checkTextureBookeeping() |
461 | { |
462 | // GIVEN |
463 | Qt3DRender::QShaderImage shaderImage; |
464 | QSignalSpy spy(&shaderImage, SIGNAL(textureChanged(Qt3DRender::QAbstractTexture *))); |
465 | |
466 | { |
467 | // WHEN |
468 | Qt3DRender::QTexture2D tex; |
469 | shaderImage.setTexture(&tex); |
470 | |
471 | // THEN |
472 | QVERIFY(spy.isValid()); |
473 | QCOMPARE(shaderImage.texture(), &tex); |
474 | QCOMPARE(spy.count(), 1); |
475 | |
476 | // WHEN |
477 | spy.clear(); |
478 | } |
479 | // THEN |
480 | QCOMPARE(spy.count(), 1); |
481 | QVERIFY(shaderImage.texture() == nullptr); |
482 | } |
483 | |
484 | }; |
485 | |
486 | QTEST_MAIN(tst_QShaderImage) |
487 | |
488 | #include "tst_qshaderimage.moc" |
489 | |