1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2015 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: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 | #include <QtTest/QTest> |
30 | #include <qbackendnodetester.h> |
31 | #include <Qt3DCore/qdynamicpropertyupdatedchange.h> |
32 | #include <renderviewjobutils_p.h> |
33 | #include <shadervariables_p.h> |
34 | #include <Qt3DRender/private/shaderdata_p.h> |
35 | #include <Qt3DRender/private/managers_p.h> |
36 | #include <Qt3DRender/private/stringtoint_p.h> |
37 | #include <Qt3DRender/qshaderdata.h> |
38 | #include "testrenderer.h" |
39 | #include "testpostmanarbiter.h" |
40 | |
41 | class tst_RenderViewUtils : public Qt3DCore::QBackendNodeTester |
42 | { |
43 | Q_OBJECT |
44 | private Q_SLOTS: |
45 | void topLevelScalarValueNoUniforms(); |
46 | void topLevelScalarValue(); |
47 | void topLevelTextureValueNoUniforms(); |
48 | void topLevelTextureValue(); |
49 | void topLevelArrayValue(); |
50 | void nestedShaderDataValue(); |
51 | void topLevelStructValue_data(); |
52 | void topLevelStructValue(); |
53 | void topLevelDynamicProperties(); |
54 | void transformedProperties(); |
55 | void shouldNotifyDynamicPropertyChanges(); |
56 | |
57 | private: |
58 | void initBackendShaderData(Qt3DRender::Render::AbstractRenderer *renderer, |
59 | Qt3DRender::QShaderData *frontend, |
60 | Qt3DRender::Render::ShaderDataManager *manager) |
61 | { |
62 | // Create children first |
63 | for (QObject *c : frontend->children()) { |
64 | Qt3DRender::QShaderData *cShaderData = qobject_cast<Qt3DRender::QShaderData *>(object: c); |
65 | if (cShaderData) |
66 | initBackendShaderData(renderer, frontend: cShaderData, manager); |
67 | } |
68 | |
69 | // Create backend element for frontend one |
70 | Qt3DRender::Render::ShaderData *backend = manager->getOrCreateResource(id: frontend->id()); |
71 | // Init the backend element |
72 | backend->setRenderer(renderer); |
73 | simulateInitializationSync(frontend, backend); |
74 | } |
75 | |
76 | void initBackendTexture(Qt3DRender::QAbstractTexture *frontend, |
77 | Qt3DRender::Render::TextureManager *manager) |
78 | { |
79 | // Create backend element for frontend one |
80 | Qt3DRender::Render::Texture *backend = manager->getOrCreateResource(id: frontend->id()); |
81 | // Init the backend element |
82 | simulateInitialization(frontend, backend); |
83 | } |
84 | }; |
85 | |
86 | class ScalarShaderData : public Qt3DRender::QShaderData |
87 | { |
88 | Q_OBJECT |
89 | Q_PROPERTY(float scalar READ scalar WRITE setScalar NOTIFY scalarChanged) |
90 | |
91 | public: |
92 | ScalarShaderData(Qt3DCore::QNode *parent = nullptr) |
93 | : Qt3DRender::QShaderData(parent) |
94 | , m_scalar(0.0f) |
95 | { |
96 | } |
97 | |
98 | void setScalar(float scalar) |
99 | { |
100 | if (scalar != m_scalar) { |
101 | m_scalar = scalar; |
102 | emit scalarChanged(); |
103 | } |
104 | } |
105 | |
106 | float scalar() const |
107 | { |
108 | return m_scalar; |
109 | } |
110 | |
111 | QHash<QString, Qt3DRender::Render::OpenGL::ShaderUniform> buildUniformMap(const QString &blockName) |
112 | { |
113 | QHash<QString, Qt3DRender::Render::OpenGL::ShaderUniform> uniforms; |
114 | |
115 | uniforms.insert(akey: blockName + QStringLiteral(".scalar" ), avalue: Qt3DRender::Render::OpenGL::ShaderUniform()); |
116 | |
117 | return uniforms; |
118 | } |
119 | |
120 | Q_SIGNALS: |
121 | void scalarChanged(); |
122 | |
123 | private: |
124 | float m_scalar; |
125 | }; |
126 | |
127 | class TextureShaderData : public Qt3DRender::QShaderData |
128 | { |
129 | Q_OBJECT |
130 | Q_PROPERTY(Qt3DRender::QAbstractTexture* texture READ texture WRITE setTexture NOTIFY textureChanged) |
131 | |
132 | public: |
133 | TextureShaderData() |
134 | : Qt3DRender::QShaderData() |
135 | , m_texture(nullptr) |
136 | { |
137 | } |
138 | |
139 | void setTexture(Qt3DRender::QAbstractTexture *texture) |
140 | { |
141 | if (texture != m_texture) { |
142 | m_texture = texture; |
143 | emit textureChanged(); |
144 | } |
145 | } |
146 | |
147 | Qt3DRender::QAbstractTexture *texture() const |
148 | { |
149 | return m_texture; |
150 | } |
151 | |
152 | QHash<QString, Qt3DRender::Render::OpenGL::ShaderUniform> buildUniformMap(const QString &blockName) |
153 | { |
154 | QHash<QString, Qt3DRender::Render::OpenGL::ShaderUniform> uniforms; |
155 | |
156 | uniforms.insert(akey: blockName + QStringLiteral(".texture" ), avalue: Qt3DRender::Render::OpenGL::ShaderUniform()); |
157 | |
158 | return uniforms; |
159 | } |
160 | |
161 | Q_SIGNALS: |
162 | void textureChanged(); |
163 | |
164 | private: |
165 | Qt3DRender::QAbstractTexture *m_texture; |
166 | }; |
167 | |
168 | |
169 | class ArrayShaderData : public Qt3DRender::QShaderData |
170 | { |
171 | Q_OBJECT |
172 | Q_PROPERTY(QVariantList array READ array WRITE setArray NOTIFY arrayChanged) |
173 | |
174 | public: |
175 | ArrayShaderData() |
176 | : Qt3DRender::QShaderData() |
177 | { |
178 | } |
179 | |
180 | void setArray(const QVariantList &array) |
181 | { |
182 | if (array != m_array) { |
183 | m_array = array; |
184 | emit arrayChanged(); |
185 | } |
186 | } |
187 | |
188 | QVariantList array() const |
189 | { |
190 | return m_array; |
191 | } |
192 | |
193 | QHash<QString, Qt3DRender::Render::OpenGL::ShaderUniform> buildUniformMap(const QString &blockName) |
194 | { |
195 | QHash<QString, Qt3DRender::Render::OpenGL::ShaderUniform> uniforms; |
196 | |
197 | uniforms.insert(akey: blockName + QStringLiteral(".array[0]" ), avalue: Qt3DRender::Render::OpenGL::ShaderUniform()); |
198 | |
199 | return uniforms; |
200 | } |
201 | |
202 | Q_SIGNALS: |
203 | void arrayChanged(); |
204 | |
205 | private: |
206 | QVariantList m_array; |
207 | }; |
208 | |
209 | class StructShaderData : public Qt3DRender::QShaderData |
210 | { |
211 | Q_OBJECT |
212 | Q_PROPERTY(float scalar READ scalar WRITE setScalar NOTIFY scalarChanged) |
213 | Q_PROPERTY(QVariantList array READ array WRITE setArray NOTIFY arrayChanged) |
214 | |
215 | public: |
216 | StructShaderData() |
217 | : Qt3DRender::QShaderData() |
218 | , m_scalar(0.0f) |
219 | { |
220 | } |
221 | |
222 | void setScalar(float scalar) |
223 | { |
224 | if (scalar != m_scalar) { |
225 | m_scalar = scalar; |
226 | emit scalarChanged(); |
227 | } |
228 | } |
229 | |
230 | float scalar() const |
231 | { |
232 | return m_scalar; |
233 | } |
234 | |
235 | void setArray(const QVariantList &array) |
236 | { |
237 | if (array != m_array) { |
238 | m_array = array; |
239 | emit arrayChanged(); |
240 | } |
241 | } |
242 | |
243 | QVariantList array() const |
244 | { |
245 | return m_array; |
246 | } |
247 | |
248 | virtual QHash<QString, Qt3DRender::Render::OpenGL::ShaderUniform> buildUniformMap(const QString &blockName) |
249 | { |
250 | QHash<QString, Qt3DRender::Render::OpenGL::ShaderUniform> uniforms; |
251 | |
252 | uniforms.insert(akey: blockName + QStringLiteral(".scalar" ), avalue: Qt3DRender::Render::OpenGL::ShaderUniform()); |
253 | uniforms.insert(akey: blockName + QStringLiteral(".array[0]" ), avalue: Qt3DRender::Render::OpenGL::ShaderUniform()); |
254 | |
255 | return uniforms; |
256 | } |
257 | |
258 | virtual QHash<QString, QVariant> buildUniformMapValues(const QString &blockName) |
259 | { |
260 | QHash<QString, QVariant> uniforms; |
261 | |
262 | uniforms.insert(akey: blockName + QStringLiteral(".scalar" ), avalue: QVariant(scalar())); |
263 | uniforms.insert(akey: blockName + QStringLiteral(".array[0]" ), avalue: QVariant(array())); |
264 | |
265 | return uniforms; |
266 | } |
267 | |
268 | Q_SIGNALS: |
269 | void scalarChanged(); |
270 | void arrayChanged(); |
271 | |
272 | private: |
273 | float m_scalar; |
274 | QVariantList m_array; |
275 | }; |
276 | |
277 | class MultiLevelStructShaderData : public StructShaderData |
278 | { |
279 | Q_OBJECT |
280 | Q_PROPERTY(Qt3DRender::QShaderData *inner READ inner WRITE setInner NOTIFY innerChanged) |
281 | |
282 | public: |
283 | MultiLevelStructShaderData() |
284 | : StructShaderData() |
285 | , m_inner(nullptr) |
286 | { |
287 | } |
288 | |
289 | void setInner(Qt3DRender::QShaderData *inner) |
290 | { |
291 | if (inner != m_inner) { |
292 | m_inner = inner; |
293 | emit innerChanged(); |
294 | } |
295 | } |
296 | |
297 | Qt3DRender::QShaderData *inner() const |
298 | { |
299 | return m_inner; |
300 | } |
301 | |
302 | QHash<QString, Qt3DRender::Render::OpenGL::ShaderUniform> buildUniformMap(const QString &blockName) override |
303 | { |
304 | QHash<QString, Qt3DRender::Render::OpenGL::ShaderUniform> innerUniforms; |
305 | |
306 | StructShaderData *innerData = nullptr; |
307 | if ((innerData = qobject_cast<StructShaderData *>(object: m_inner)) != nullptr) |
308 | innerUniforms = innerData->buildUniformMap(QStringLiteral(".inner" )); |
309 | |
310 | QHash<QString, Qt3DRender::Render::OpenGL::ShaderUniform> uniforms = StructShaderData::buildUniformMap(blockName); |
311 | QHash<QString, Qt3DRender::Render::OpenGL::ShaderUniform>::const_iterator it = innerUniforms.begin(); |
312 | const QHash<QString, Qt3DRender::Render::OpenGL::ShaderUniform>::const_iterator end = innerUniforms.end(); |
313 | |
314 | while (it != end) { |
315 | uniforms.insert(akey: blockName + it.key(), avalue: it.value()); |
316 | ++it; |
317 | } |
318 | return uniforms; |
319 | } |
320 | |
321 | QHash<QString, QVariant> buildUniformMapValues(const QString &blockName) override |
322 | { |
323 | QHash<QString, QVariant> innerUniformsValues; |
324 | |
325 | StructShaderData *innerData = nullptr; |
326 | if ((innerData = qobject_cast<StructShaderData *>(object: m_inner)) != nullptr) |
327 | innerUniformsValues = innerData->buildUniformMapValues(QStringLiteral(".inner" )); |
328 | |
329 | QHash<QString, QVariant> uniformsValues = StructShaderData::buildUniformMapValues(blockName); |
330 | QHash<QString, QVariant>::const_iterator it = innerUniformsValues.begin(); |
331 | const QHash<QString, QVariant>::const_iterator end = innerUniformsValues.end(); |
332 | |
333 | while (it != end) { |
334 | uniformsValues.insert(akey: blockName + it.key(), avalue: it.value()); |
335 | ++it; |
336 | } |
337 | |
338 | return uniformsValues; |
339 | } |
340 | |
341 | Q_SIGNALS: |
342 | void innerChanged(); |
343 | |
344 | private: |
345 | Qt3DRender::QShaderData *m_inner; |
346 | }; |
347 | |
348 | void tst_RenderViewUtils::topLevelScalarValueNoUniforms() |
349 | { |
350 | // GIVEN |
351 | TestRenderer renderer; |
352 | QScopedPointer<ScalarShaderData> shaderData(new ScalarShaderData()); |
353 | QScopedPointer<Qt3DRender::Render::ShaderDataManager> manager(new Qt3DRender::Render::ShaderDataManager()); |
354 | QScopedPointer<Qt3DRender::Render::TextureManager> textureManager(new Qt3DRender::Render::TextureManager()); |
355 | |
356 | // WHEN |
357 | shaderData->setScalar(883.0f); |
358 | initBackendShaderData(renderer: &renderer, frontend: shaderData.data(), manager: manager.data()); |
359 | |
360 | // THEN |
361 | Qt3DRender::Render::ShaderData *backendShaderData = manager->lookupResource(id: shaderData->id()); |
362 | QVERIFY(backendShaderData != nullptr); |
363 | |
364 | // WHEB |
365 | Qt3DRender::Render::OpenGL::UniformBlockValueBuilder blockBuilder; |
366 | blockBuilder.shaderDataManager = manager.data(); |
367 | blockBuilder.textureManager = textureManager.data(); |
368 | blockBuilder.updatedPropertiesOnly = false; |
369 | // build name-value map |
370 | blockBuilder.buildActiveUniformNameValueMapStructHelper(rShaderData: backendShaderData, QStringLiteral("" )); |
371 | |
372 | // THEN |
373 | // activeUniformNamesToValue should be empty as blockBuilder.uniforms is |
374 | QVERIFY(blockBuilder.activeUniformNamesToValue.isEmpty()); |
375 | } |
376 | |
377 | void tst_RenderViewUtils::topLevelScalarValue() |
378 | { |
379 | // GIVEN |
380 | TestRenderer renderer; |
381 | QScopedPointer<ScalarShaderData> shaderData(new ScalarShaderData()); |
382 | QScopedPointer<Qt3DRender::Render::ShaderDataManager> manager(new Qt3DRender::Render::ShaderDataManager()); |
383 | QScopedPointer<Qt3DRender::Render::TextureManager> textureManager(new Qt3DRender::Render::TextureManager()); |
384 | |
385 | // WHEN |
386 | shaderData->setScalar(883.0f); |
387 | initBackendShaderData(renderer: &renderer, frontend: shaderData.data(), manager: manager.data()); |
388 | |
389 | // THEN |
390 | Qt3DRender::Render::ShaderData *backendShaderData = manager->lookupResource(id: shaderData->id()); |
391 | QVERIFY(backendShaderData != nullptr); |
392 | |
393 | // WHEN |
394 | Qt3DRender::Render::OpenGL::UniformBlockValueBuilder blockBuilder; |
395 | blockBuilder.shaderDataManager = manager.data(); |
396 | blockBuilder.textureManager = textureManager.data(); |
397 | blockBuilder.updatedPropertiesOnly = false; |
398 | blockBuilder.uniforms = shaderData->buildUniformMap(QStringLiteral("MyBlock" )); |
399 | // build name-value map |
400 | blockBuilder.buildActiveUniformNameValueMapStructHelper(rShaderData: backendShaderData, QStringLiteral("MyBlock" )); |
401 | |
402 | // THEN |
403 | QVERIFY(blockBuilder.uniforms.count() == 1); |
404 | QCOMPARE(blockBuilder.activeUniformNamesToValue.count(), 1); |
405 | |
406 | // WHEN |
407 | Qt3DRender::Render::OpenGL::UniformBlockValueBuilderHash::const_iterator it = blockBuilder.activeUniformNamesToValue.begin(); |
408 | const Qt3DRender::Render::OpenGL::UniformBlockValueBuilderHash::const_iterator end = blockBuilder.activeUniformNamesToValue.end(); |
409 | |
410 | while (it != end) { |
411 | // THEN |
412 | QVERIFY(blockBuilder.uniforms.contains(Qt3DRender::Render::StringToInt::lookupString(it.key()))); |
413 | QCOMPARE(it.value(), QVariant(shaderData->scalar())); |
414 | ++it; |
415 | } |
416 | } |
417 | |
418 | void tst_RenderViewUtils::topLevelTextureValueNoUniforms() |
419 | { |
420 | // GIVEN |
421 | TestRenderer renderer; |
422 | QScopedPointer<TextureShaderData> shaderData(new TextureShaderData); |
423 | QScopedPointer<Qt3DRender::Render::ShaderDataManager> manager(new Qt3DRender::Render::ShaderDataManager); |
424 | QScopedPointer<Qt3DRender::QAbstractTexture> texture(new Qt3DRender::QTexture2D); |
425 | QScopedPointer<Qt3DRender::Render::TextureManager> textureManager(new Qt3DRender::Render::TextureManager()); |
426 | |
427 | // WHEN |
428 | shaderData->setTexture(texture.data()); |
429 | initBackendShaderData(renderer: &renderer, frontend: shaderData.data(), manager: manager.data()); |
430 | |
431 | // THEN |
432 | Qt3DRender::Render::ShaderData *backendShaderData = manager->lookupResource(id: shaderData->id()); |
433 | QVERIFY(backendShaderData != nullptr); |
434 | |
435 | // WHEB |
436 | Qt3DRender::Render::OpenGL::UniformBlockValueBuilder blockBuilder; |
437 | blockBuilder.shaderDataManager = manager.data(); |
438 | blockBuilder.textureManager = textureManager.data(); |
439 | blockBuilder.updatedPropertiesOnly = false; |
440 | // build name-value map |
441 | blockBuilder.buildActiveUniformNameValueMapStructHelper(rShaderData: backendShaderData, QStringLiteral("" )); |
442 | |
443 | // THEN |
444 | // activeUniformNamesToValue should be empty as blockBuilder.uniforms is |
445 | QVERIFY(blockBuilder.activeUniformNamesToValue.isEmpty()); |
446 | } |
447 | |
448 | void tst_RenderViewUtils::topLevelTextureValue() |
449 | { |
450 | // GIVEN |
451 | TestRenderer renderer; |
452 | QScopedPointer<TextureShaderData> shaderData(new TextureShaderData); |
453 | QScopedPointer<Qt3DRender::Render::ShaderDataManager> manager(new Qt3DRender::Render::ShaderDataManager); |
454 | QScopedPointer<Qt3DRender::QAbstractTexture> texture(new Qt3DRender::QTexture2D); |
455 | QScopedPointer<Qt3DRender::Render::TextureManager> textureManager(new Qt3DRender::Render::TextureManager()); |
456 | |
457 | // WHEN |
458 | initBackendTexture(frontend: texture.data(), manager: textureManager.data()); |
459 | shaderData->setTexture(texture.data()); |
460 | initBackendShaderData(renderer: &renderer, frontend: shaderData.data(), manager: manager.data()); |
461 | |
462 | // THEN |
463 | Qt3DRender::Render::ShaderData *backendShaderData = manager->lookupResource(id: shaderData->id()); |
464 | QVERIFY(backendShaderData != nullptr); |
465 | |
466 | // WHEN |
467 | Qt3DRender::Render::OpenGL::UniformBlockValueBuilder blockBuilder; |
468 | blockBuilder.shaderDataManager = manager.data(); |
469 | blockBuilder.textureManager = textureManager.data(); |
470 | blockBuilder.updatedPropertiesOnly = false; |
471 | blockBuilder.uniforms = shaderData->buildUniformMap(QStringLiteral("MyBlock" )); |
472 | // build name-value map |
473 | blockBuilder.buildActiveUniformNameValueMapStructHelper(rShaderData: backendShaderData, QStringLiteral("MyBlock" )); |
474 | |
475 | // THEN |
476 | QVERIFY(blockBuilder.uniforms.count() == 1); |
477 | QCOMPARE(blockBuilder.activeUniformNamesToValue.count(), 1); |
478 | |
479 | // WHEN |
480 | Qt3DRender::Render::OpenGL::UniformBlockValueBuilderHash::const_iterator it = blockBuilder.activeUniformNamesToValue.begin(); |
481 | const Qt3DRender::Render::OpenGL::UniformBlockValueBuilderHash::const_iterator end = blockBuilder.activeUniformNamesToValue.end(); |
482 | |
483 | while (it != end) { |
484 | // THEN |
485 | QVERIFY(blockBuilder.uniforms.contains(Qt3DRender::Render::StringToInt::lookupString(it.key()))); |
486 | QCOMPARE(it.value(), QVariant::fromValue(shaderData->texture()->id())); |
487 | ++it; |
488 | } |
489 | } |
490 | |
491 | void tst_RenderViewUtils::topLevelArrayValue() |
492 | { |
493 | // GIVEN |
494 | TestRenderer renderer; |
495 | QScopedPointer<ArrayShaderData> shaderData(new ArrayShaderData()); |
496 | QScopedPointer<Qt3DRender::Render::ShaderDataManager> manager(new Qt3DRender::Render::ShaderDataManager()); |
497 | QScopedPointer<Qt3DRender::Render::TextureManager> textureManager(new Qt3DRender::Render::TextureManager()); |
498 | |
499 | // WHEN |
500 | QVariantList arrayValues = QVariantList() << 454 << 350 << 383 << 427 << 552; |
501 | shaderData->setArray(arrayValues); |
502 | initBackendShaderData(renderer: &renderer, frontend: shaderData.data(), manager: manager.data()); |
503 | |
504 | // THEN |
505 | Qt3DRender::Render::ShaderData *backendShaderData = manager->lookupResource(id: shaderData->id()); |
506 | QVERIFY(backendShaderData != nullptr); |
507 | |
508 | // WHEN |
509 | Qt3DRender::Render::OpenGL::UniformBlockValueBuilder blockBuilder; |
510 | blockBuilder.shaderDataManager = manager.data(); |
511 | blockBuilder.textureManager = textureManager.data(); |
512 | blockBuilder.updatedPropertiesOnly = false; |
513 | blockBuilder.uniforms = shaderData->buildUniformMap(QStringLiteral("MyBlock" )); |
514 | // build name-value map |
515 | blockBuilder.buildActiveUniformNameValueMapStructHelper(rShaderData: backendShaderData, QStringLiteral("MyBlock" )); |
516 | |
517 | // THEN |
518 | QVERIFY(blockBuilder.uniforms.count() == 1); |
519 | QCOMPARE(blockBuilder.activeUniformNamesToValue.count(), 1); |
520 | |
521 | // WHEN |
522 | Qt3DRender::Render::OpenGL::UniformBlockValueBuilderHash::const_iterator it = blockBuilder.activeUniformNamesToValue.begin(); |
523 | const Qt3DRender::Render::OpenGL::UniformBlockValueBuilderHash::const_iterator end = blockBuilder.activeUniformNamesToValue.end(); |
524 | |
525 | while (it != end) { |
526 | // THEN |
527 | QVERIFY(blockBuilder.uniforms.contains(Qt3DRender::Render::StringToInt::lookupString(it.key()))); |
528 | QCOMPARE(it.value(), QVariant(arrayValues)); |
529 | ++it; |
530 | } |
531 | } |
532 | |
533 | void tst_RenderViewUtils::nestedShaderDataValue() |
534 | { |
535 | // GIVEN |
536 | TestRenderer renderer; |
537 | QScopedPointer<ArrayShaderData> arrayShaderData(new ArrayShaderData()); |
538 | QScopedPointer<Qt3DRender::Render::ShaderDataManager> manager(new Qt3DRender::Render::ShaderDataManager()); |
539 | QScopedPointer<Qt3DRender::Render::TextureManager> textureManager(new Qt3DRender::Render::TextureManager()); |
540 | |
541 | QScopedPointer<ScalarShaderData> shaderData1(new ScalarShaderData(arrayShaderData.data())); |
542 | QScopedPointer<ScalarShaderData> shaderData2(new ScalarShaderData(arrayShaderData.data())); |
543 | QScopedPointer<ScalarShaderData> shaderData3(new ScalarShaderData(arrayShaderData.data())); |
544 | |
545 | shaderData1->setScalar(883.0f); |
546 | shaderData2->setScalar(1200.0f); |
547 | shaderData3->setScalar(1340.0f); |
548 | QHash<QString, QVariant> scalarValues; |
549 | scalarValues[QStringLiteral("MyBlock.array[0].scalar" )] = shaderData1->scalar(); |
550 | scalarValues[QStringLiteral("MyBlock.array[1].scalar" )] = shaderData2->scalar(); |
551 | scalarValues[QStringLiteral("MyBlock.array[2].scalar" )] = shaderData3->scalar(); |
552 | |
553 | |
554 | const Qt3DCore::QNodeId id1 = shaderData1->id(); |
555 | const Qt3DCore::QNodeId id2 = shaderData2->id(); |
556 | const Qt3DCore::QNodeId id3 = shaderData3->id(); |
557 | |
558 | // WHEN |
559 | const QVariantList arrayValues = QVariantList() << QVariant::fromValue(value: id1) << QVariant::fromValue(value: id2) << QVariant::fromValue(value: id3); |
560 | arrayShaderData->setArray(arrayValues); |
561 | initBackendShaderData(renderer: &renderer, frontend: arrayShaderData.data(), manager: manager.data()); |
562 | |
563 | // THEN |
564 | Qt3DRender::Render::ShaderData *backendArrayShaderData = manager->lookupResource(id: arrayShaderData->id()); |
565 | Qt3DRender::Render::ShaderData *backendShaderData1 = manager->lookupResource(id: id1); |
566 | Qt3DRender::Render::ShaderData *backendShaderData2 = manager->lookupResource(id: id2); |
567 | Qt3DRender::Render::ShaderData *backendShaderData3 = manager->lookupResource(id: id3); |
568 | QVERIFY(backendArrayShaderData != nullptr); |
569 | QVERIFY(backendShaderData1 != nullptr); |
570 | QVERIFY(backendShaderData2 != nullptr); |
571 | QVERIFY(backendShaderData3 != nullptr); |
572 | |
573 | // WHEN |
574 | Qt3DRender::Render::OpenGL::UniformBlockValueBuilder blockBuilder; |
575 | blockBuilder.shaderDataManager = manager.data(); |
576 | blockBuilder.textureManager = textureManager.data(); |
577 | blockBuilder.updatedPropertiesOnly = false; |
578 | blockBuilder.uniforms.insert(QStringLiteral("MyBlock.array[0].scalar" ), avalue: Qt3DRender::Render::OpenGL::ShaderUniform()); |
579 | blockBuilder.uniforms.insert(QStringLiteral("MyBlock.array[1].scalar" ), avalue: Qt3DRender::Render::OpenGL::ShaderUniform()); |
580 | blockBuilder.uniforms.insert(QStringLiteral("MyBlock.array[2].scalar" ), avalue: Qt3DRender::Render::OpenGL::ShaderUniform()); |
581 | // build name-value map |
582 | blockBuilder.buildActiveUniformNameValueMapStructHelper(rShaderData: backendArrayShaderData, QStringLiteral("MyBlock" )); |
583 | |
584 | // THEN |
585 | QVERIFY(blockBuilder.uniforms.count() == 3); |
586 | QCOMPARE(blockBuilder.activeUniformNamesToValue.count(), 3); |
587 | |
588 | // WHEN |
589 | auto it = blockBuilder.uniforms.cbegin(); |
590 | const auto end = blockBuilder.uniforms.cend(); |
591 | |
592 | while (it != end) { |
593 | // THEN |
594 | const int nameId = Qt3DRender::Render::StringToInt::lookupId(str: it.key()); |
595 | QVERIFY(blockBuilder.activeUniformNamesToValue.contains(nameId)); |
596 | QCOMPARE(blockBuilder.activeUniformNamesToValue[nameId], scalarValues.value(it.key())); |
597 | ++it; |
598 | } |
599 | } |
600 | |
601 | void tst_RenderViewUtils::topLevelStructValue_data() |
602 | { |
603 | QTest::addColumn<StructShaderData*>(name: "shaderData" ); |
604 | QTest::addColumn<QString>(name: "blockName" ); |
605 | |
606 | QVariantList arrayValues2 = QVariantList() << 180 << 220 << 250 << 270 << 300 << 350 << 550; |
607 | QVariantList arrayValues = QVariantList() << 454 << 350 << 383 << 427 << 552; |
608 | |
609 | MultiLevelStructShaderData *twoLevelsNestedShaderData = new MultiLevelStructShaderData(); |
610 | MultiLevelStructShaderData *singleLevelShaderData = new MultiLevelStructShaderData(); |
611 | StructShaderData *shaderData = new StructShaderData(); |
612 | |
613 | // Don't forget to set the parent so that initBackendShaderData |
614 | // properly initializes nested members |
615 | shaderData->setParent(singleLevelShaderData); |
616 | shaderData->setArray(arrayValues); |
617 | shaderData->setScalar(1584.0f); |
618 | |
619 | singleLevelShaderData->setParent(twoLevelsNestedShaderData); |
620 | singleLevelShaderData->setInner(shaderData); |
621 | singleLevelShaderData->setScalar(1200.0f); |
622 | singleLevelShaderData->setArray(arrayValues2); |
623 | |
624 | twoLevelsNestedShaderData->setInner(singleLevelShaderData); |
625 | twoLevelsNestedShaderData->setArray(arrayValues + arrayValues2); |
626 | twoLevelsNestedShaderData->setScalar(1340.0f); |
627 | |
628 | QTest::newRow(dataTag: "simple struct" ) << shaderData << QStringLiteral("Block" ); |
629 | QTest::newRow(dataTag: "single level inner struct" ) << (StructShaderData *)singleLevelShaderData << QStringLiteral("Block" ); |
630 | QTest::newRow(dataTag: "tow level inner struct" ) << (StructShaderData *)twoLevelsNestedShaderData << QStringLiteral("Block" ); |
631 | } |
632 | |
633 | void tst_RenderViewUtils::topLevelStructValue() |
634 | { |
635 | // GIVEN |
636 | TestRenderer renderer; |
637 | QFETCH(StructShaderData *, shaderData); |
638 | QFETCH(QString, blockName); |
639 | QScopedPointer<Qt3DRender::Render::ShaderDataManager> manager(new Qt3DRender::Render::ShaderDataManager()); |
640 | QScopedPointer<Qt3DRender::Render::TextureManager> textureManager(new Qt3DRender::Render::TextureManager()); |
641 | |
642 | // WHEN |
643 | initBackendShaderData(renderer: &renderer, frontend: shaderData, manager: manager.data()); |
644 | |
645 | // THEN |
646 | Qt3DRender::Render::ShaderData *backendShaderData = manager->lookupResource(id: shaderData->id()); |
647 | QVERIFY(backendShaderData != nullptr); |
648 | |
649 | // WHEN |
650 | Qt3DRender::Render::OpenGL::UniformBlockValueBuilder blockBuilder; |
651 | blockBuilder.shaderDataManager = manager.data(); |
652 | blockBuilder.textureManager = textureManager.data(); |
653 | blockBuilder.updatedPropertiesOnly = false; |
654 | blockBuilder.uniforms = shaderData->buildUniformMap(blockName); |
655 | const QHash<QString, QVariant> expectedValues = shaderData->buildUniformMapValues(blockName); |
656 | // build name-value map |
657 | blockBuilder.buildActiveUniformNameValueMapStructHelper(rShaderData: backendShaderData, blockName); |
658 | |
659 | // THEN |
660 | QCOMPARE(blockBuilder.activeUniformNamesToValue.count(), blockBuilder.uniforms.count()); |
661 | |
662 | // WHEN |
663 | Qt3DRender::Render::OpenGL::UniformBlockValueBuilderHash::const_iterator it = blockBuilder.activeUniformNamesToValue.begin(); |
664 | const Qt3DRender::Render::OpenGL::UniformBlockValueBuilderHash::const_iterator end = blockBuilder.activeUniformNamesToValue.end(); |
665 | |
666 | while (it != end) { |
667 | // THEN |
668 | QVERIFY(blockBuilder.uniforms.contains(Qt3DRender::Render::StringToInt::lookupString(it.key()))); |
669 | QVERIFY(expectedValues.contains(Qt3DRender::Render::StringToInt::lookupString(it.key()))); |
670 | QCOMPARE(it.value(), expectedValues.value(Qt3DRender::Render::StringToInt::lookupString(it.key()))); |
671 | ++it; |
672 | } |
673 | } |
674 | |
675 | void tst_RenderViewUtils::topLevelDynamicProperties() |
676 | { |
677 | // GIVEN |
678 | TestRenderer renderer; |
679 | QScopedPointer<Qt3DRender::QShaderData> shaderData(new Qt3DRender::QShaderData()); |
680 | QScopedPointer<Qt3DRender::Render::ShaderDataManager> manager(new Qt3DRender::Render::ShaderDataManager()); |
681 | QScopedPointer<Qt3DRender::QAbstractTexture> texture(new Qt3DRender::QTexture2D); |
682 | QScopedPointer<Qt3DRender::Render::TextureManager> textureManager(new Qt3DRender::Render::TextureManager()); |
683 | |
684 | // WHEN |
685 | initBackendTexture(frontend: texture.data(), manager: textureManager.data()); |
686 | shaderData->setProperty(name: "scalar" , value: 883.0f); |
687 | shaderData->setProperty(name: "array" , value: QVariantList() << 454 << 350 << 383 << 427 << 552); |
688 | shaderData->setProperty(name: "texture" , value: QVariant::fromValue(value: texture.data())); |
689 | initBackendShaderData(renderer: &renderer, frontend: shaderData.data(), manager: manager.data()); |
690 | |
691 | // THEN |
692 | Qt3DRender::Render::ShaderData *backendShaderData = manager->lookupResource(id: shaderData->id()); |
693 | QVERIFY(backendShaderData != nullptr); |
694 | |
695 | // WHEN |
696 | Qt3DRender::Render::OpenGL::UniformBlockValueBuilder blockBuilder; |
697 | blockBuilder.shaderDataManager = manager.data(); |
698 | blockBuilder.textureManager = textureManager.data(); |
699 | blockBuilder.updatedPropertiesOnly = false; |
700 | blockBuilder.uniforms.insert(QStringLiteral("MyBlock.scalar" ), avalue: Qt3DRender::Render::OpenGL::ShaderUniform()); |
701 | blockBuilder.uniforms.insert(QStringLiteral("MyBlock.array[0]" ), avalue: Qt3DRender::Render::OpenGL::ShaderUniform()); |
702 | blockBuilder.uniforms.insert(QStringLiteral("MyBlock.texture" ), avalue: Qt3DRender::Render::OpenGL::ShaderUniform()); |
703 | // build name-value map |
704 | blockBuilder.buildActiveUniformNameValueMapStructHelper(rShaderData: backendShaderData, QStringLiteral("MyBlock" )); |
705 | |
706 | // THEN |
707 | QVERIFY(blockBuilder.uniforms.count() == 3); |
708 | QCOMPARE(blockBuilder.activeUniformNamesToValue.count(), 3); |
709 | |
710 | QCOMPARE(blockBuilder.activeUniformNamesToValue.value(Qt3DRender::Render::StringToInt::lookupId("MyBlock.scalar" )), |
711 | shaderData->property("scalar" )); |
712 | QCOMPARE(blockBuilder.activeUniformNamesToValue.value(Qt3DRender::Render::StringToInt::lookupId("MyBlock.array[0]" )), |
713 | shaderData->property("array" )); |
714 | QCOMPARE(blockBuilder.activeUniformNamesToValue.value(Qt3DRender::Render::StringToInt::lookupId("MyBlock.texture" )), |
715 | QVariant::fromValue(texture->id())); |
716 | } |
717 | |
718 | void tst_RenderViewUtils::transformedProperties() |
719 | { |
720 | // GIVEN |
721 | QScopedPointer<Qt3DRender::QShaderData> shaderData(new Qt3DRender::QShaderData()); |
722 | QScopedPointer<Qt3DRender::Render::ShaderDataManager> manager(new Qt3DRender::Render::ShaderDataManager()); |
723 | TestRenderer renderer; |
724 | |
725 | // WHEN |
726 | const Vector3D position = Vector3D(15.0f, -5.0f, 10.0f); |
727 | const QVector3D positionQt = convertToQVector3D(v: position); |
728 | Matrix4x4 worldMatrix; |
729 | { |
730 | QMatrix4x4 m; |
731 | m.translate(x: -3.0f, y: 2.0f, z: 7.5f); |
732 | worldMatrix = Matrix4x4(m); |
733 | } |
734 | Matrix4x4 viewMatrix; |
735 | { |
736 | QMatrix4x4 m; |
737 | m.translate(x: 9.0f, y: 6.0f, z: 12.0f); |
738 | viewMatrix = Matrix4x4(m); |
739 | } |
740 | |
741 | shaderData->setProperty(name: "position0" , value: positionQt); |
742 | shaderData->setProperty(name: "position1" , value: positionQt); |
743 | shaderData->setProperty(name: "position2" , value: positionQt); |
744 | shaderData->setProperty(name: "position3" , value: positionQt); |
745 | shaderData->setProperty(name: "position1Transformed" , value: Qt3DRender::Render::ShaderData::ModelToEye); |
746 | shaderData->setProperty(name: "position2Transformed" , value: Qt3DRender::Render::ShaderData::ModelToWorld); |
747 | shaderData->setProperty(name: "position3Transformed" , value: Qt3DRender::Render::ShaderData::ModelToWorldDirection); |
748 | initBackendShaderData(renderer: &renderer, frontend: shaderData.data(), manager: manager.data()); |
749 | |
750 | // THEN |
751 | Qt3DRender::Render::ShaderData *backendShaderData = manager->lookupResource(id: shaderData->id()); |
752 | QVERIFY(backendShaderData != nullptr); |
753 | QCOMPARE(backendShaderData->propertyTransformType(QStringLiteral("position0" )), Qt3DRender::Render::ShaderData::NoTransform); |
754 | QCOMPARE(backendShaderData->propertyTransformType(QStringLiteral("position1" )), Qt3DRender::Render::ShaderData::ModelToEye); |
755 | QCOMPARE(backendShaderData->propertyTransformType(QStringLiteral("position2" )), Qt3DRender::Render::ShaderData::ModelToWorld); |
756 | QCOMPARE(backendShaderData->propertyTransformType(QStringLiteral("position3" )), Qt3DRender::Render::ShaderData::ModelToWorldDirection); |
757 | |
758 | // WHEN |
759 | backendShaderData->updateWorldTransform(worldMatrix); |
760 | const Vector3D position1Value = backendShaderData->getTransformedProperty(QStringLiteral("position1" ), viewMatrix).value<Vector3D>(); |
761 | const Vector3D position2Value = backendShaderData->getTransformedProperty(QStringLiteral("position2" ), viewMatrix).value<Vector3D>(); |
762 | const Vector3D position3Value = backendShaderData->getTransformedProperty(QStringLiteral("position3" ), viewMatrix).value<Vector3D>(); |
763 | const QVariant position0Value = backendShaderData->getTransformedProperty(QStringLiteral("position0" ), viewMatrix); |
764 | |
765 | // THEN |
766 | QCOMPARE(position0Value, positionQt); |
767 | QCOMPARE(position1Value, viewMatrix * worldMatrix * position); |
768 | QCOMPARE(position2Value, worldMatrix * position); |
769 | QCOMPARE(position3Value, Vector3D((worldMatrix * Vector4D(position, 0.0f)))); |
770 | } |
771 | |
772 | void tst_RenderViewUtils::shouldNotifyDynamicPropertyChanges() |
773 | { |
774 | // GIVEN |
775 | TestArbiter arbiter; |
776 | QScopedPointer<Qt3DRender::QShaderData> shaderData(new Qt3DRender::QShaderData()); |
777 | arbiter.setArbiterOnNode(shaderData.data()); |
778 | |
779 | // WHEN |
780 | shaderData->setProperty(name: "scalar" , value: 883.0f); |
781 | |
782 | // THEN |
783 | QCOMPARE(arbiter.events.size(), 0); |
784 | QCOMPARE(arbiter.dirtyNodes.size(), 1); |
785 | QCOMPARE(arbiter.dirtyNodes.front(), shaderData.data()); |
786 | |
787 | arbiter.dirtyNodes.clear(); |
788 | |
789 | // WHEN |
790 | QScopedPointer<Qt3DRender::QAbstractTexture> texture(new Qt3DRender::QTexture2D); |
791 | shaderData->setProperty(name: "texture" , value: QVariant::fromValue(value: texture.data())); |
792 | |
793 | // THEN |
794 | QCOMPARE(arbiter.events.size(), 0); |
795 | QCOMPARE(arbiter.dirtyNodes.size(), 1); |
796 | QCOMPARE(arbiter.dirtyNodes.front(), shaderData.data()); |
797 | } |
798 | |
799 | QTEST_MAIN(tst_RenderViewUtils) |
800 | |
801 | #include "tst_renderviewutils.moc" |
802 | |