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
30#include <QtTest/QTest>
31#include <Qt3DRender/private/updatemeshtrianglelistjob_p.h>
32#include <Qt3DRender/private/nodemanagers_p.h>
33#include <Qt3DRender/private/managers_p.h>
34#include <Qt3DRender/private/geometryrenderermanager_p.h>
35#include <Qt3DRender/private/buffermanager_p.h>
36#include <Qt3DRender/private/loadgeometryjob_p.h>
37#include <Qt3DRender/qrenderaspect.h>
38#include <Qt3DRender/private/qrenderaspect_p.h>
39#include <Qt3DCore/private/qnodevisitor_p.h>
40#include "qmlscenereader.h"
41
42QT_BEGIN_NAMESPACE
43
44namespace Qt3DRender { // Needs to be in that namespace to be friend with QRenderAspect
45
46QVector<Qt3DCore::QNode *> getNodesForCreation(Qt3DCore::QNode *root)
47{
48 using namespace Qt3DCore;
49
50 QVector<QNode *> nodes;
51 Qt3DCore::QNodeVisitor visitor;
52 visitor.traverse(rootNode_: root, fN: [&nodes](QNode *node) {
53 nodes.append(t: node);
54
55 // Store the metaobject of the node in the QNode so that we have it available
56 // to us during destruction in the QNode destructor. This allows us to send
57 // the QNodeId and the metaobject as typeinfo to the backend aspects so they
58 // in turn can find the correct QBackendNodeMapper object to handle the destruction
59 // of the corresponding backend nodes.
60 QNodePrivate *d = QNodePrivate::get(q: node);
61 d->m_typeInfo = const_cast<QMetaObject*>(QNodePrivate::findStaticMetaObject(metaObject: node->metaObject()));
62
63 // Mark this node as having been handled for creation so that it is picked up
64 d->m_hasBackendNode = true;
65 });
66
67 return nodes;
68}
69
70QVector<Qt3DCore::NodeTreeChange> nodeTreeChangesForNodes(const QVector<Qt3DCore::QNode *> nodes)
71{
72 QVector<Qt3DCore::NodeTreeChange> nodeTreeChanges;
73 nodeTreeChanges.reserve(size: nodes.size());
74
75 for (Qt3DCore::QNode *n : nodes) {
76 nodeTreeChanges.push_back(t: {
77 .id: n->id(),
78 .metaObj: Qt3DCore::QNodePrivate::get(q: n)->m_typeInfo,
79 .type: Qt3DCore::NodeTreeChange::Added,
80 .node: n
81 });
82 }
83
84 return nodeTreeChanges;
85}
86
87class TestAspect : public Qt3DRender::QRenderAspect
88{
89public:
90 TestAspect(Qt3DCore::QNode *root)
91 : Qt3DRender::QRenderAspect(Qt3DRender::QRenderAspect::Synchronous)
92 , m_sceneRoot(nullptr)
93 {
94 Qt3DRender::QRenderAspect::onRegistered();
95
96 const QVector<Qt3DCore::QNode *> nodes = getNodesForCreation(root);
97 d_func()->setRootAndCreateNodes(rootObject: qobject_cast<Qt3DCore::QEntity *>(object: root), nodesTreeChanges: nodeTreeChangesForNodes(nodes));
98
99 Qt3DRender::Render::Entity *rootEntity = nodeManagers()->lookupResource<Qt3DRender::Render::Entity, Render::EntityManager>(id: rootEntityId());
100 Q_ASSERT(rootEntity);
101 m_sceneRoot = rootEntity;
102 }
103
104 ~TestAspect()
105 {
106 QRenderAspect::onUnregistered();
107 }
108
109 void onRegistered() { Qt3DRender::QRenderAspect::onRegistered(); }
110 void onUnregistered() { Qt3DRender::QRenderAspect::onUnregistered(); }
111
112 Qt3DRender::Render::NodeManagers *nodeManagers() const { return d_func()->m_renderer->nodeManagers(); }
113 Qt3DRender::Render::FrameGraphNode *frameGraphRoot() const { return d_func()->m_renderer->frameGraphRoot(); }
114 Qt3DRender::Render::RenderSettings *renderSettings() const { return d_func()->m_renderer->settings(); }
115 Qt3DRender::Render::Entity *sceneRoot() const { return m_sceneRoot; }
116
117private:
118 Qt3DRender::Render::Entity *m_sceneRoot;
119};
120
121} // Qt3DRender
122
123QT_END_NAMESPACE
124
125namespace {
126
127void runRequiredJobs(Qt3DRender::TestAspect *aspect)
128{
129 Qt3DRender::Render::GeometryRendererManager *geomRendererManager = aspect->nodeManagers()->geometryRendererManager();
130 const QVector<Qt3DCore::QNodeId> dirtyGeometryRenderers = geomRendererManager->dirtyGeometryRenderers();
131 QVector<Qt3DCore::QAspectJobPtr> dirtyGeometryRendererJobs;
132 dirtyGeometryRendererJobs.reserve(size: dirtyGeometryRenderers.size());
133
134 // Load Geometry
135 for (const Qt3DCore::QNodeId geoRendererId : dirtyGeometryRenderers) {
136 Qt3DRender::Render::HGeometryRenderer geometryRendererHandle = geomRendererManager->lookupHandle(id: geoRendererId);
137 if (!geometryRendererHandle.isNull()) {
138 auto job = Qt3DRender::Render::LoadGeometryJobPtr::create(arguments&: geometryRendererHandle);
139 job->setNodeManagers(aspect->nodeManagers());
140 dirtyGeometryRendererJobs.push_back(t: job);
141 }
142 }
143}
144
145struct NodeCollection
146{
147 explicit NodeCollection(Qt3DRender::TestAspect *aspect, QObject *frontendRoot)
148 : geometryRenderers(frontendRoot->findChildren<Qt3DRender::QGeometryRenderer *>())
149 , attributes(frontendRoot->findChildren<Qt3DRender::QAttribute *>())
150 , buffers(frontendRoot->findChildren<Qt3DRender::QBuffer *>())
151 {
152 // THEN
153 QCOMPARE(aspect->nodeManagers()->geometryManager()->activeHandles().size(), geometryRenderers.size());
154 QCOMPARE(aspect->nodeManagers()->attributeManager()->activeHandles().size(), attributes.size());
155 QCOMPARE(aspect->nodeManagers()->bufferManager()->activeHandles().size(), buffers.size());
156
157 for (const Qt3DRender::QGeometryRenderer *g : qAsConst(t&: geometryRenderers)) {
158 Qt3DRender::Render::GeometryRenderer *backend = aspect->nodeManagers()->geometryRendererManager()->lookupResource(id: g->id());
159 QVERIFY(backend != nullptr);
160 backendGeometryRenderer.push_back(t: backend);
161 }
162
163 for (const Qt3DRender::QAttribute *a : qAsConst(t&: attributes)) {
164 Qt3DRender::Render::Attribute *backend = aspect->nodeManagers()->attributeManager()->lookupResource(id: a->id());
165 QVERIFY(backend != nullptr);
166 backendAttributes.push_back(t: backend);
167 }
168
169 for (const Qt3DRender::QBuffer *b : qAsConst(t&: buffers)) {
170 Qt3DRender::Render::Buffer *backend = aspect->nodeManagers()->bufferManager()->lookupResource(id: b->id());
171 QVERIFY(backend != nullptr);
172 backendBuffers.push_back(t: backend);
173 }
174 }
175
176 QList<Qt3DRender::QGeometryRenderer *> geometryRenderers;
177 QList<Qt3DRender::QAttribute *> attributes;
178 QList<Qt3DRender::QBuffer *> buffers;
179
180 QVector<Qt3DRender::Render::GeometryRenderer *> backendGeometryRenderer;
181 QVector<Qt3DRender::Render::Attribute *> backendAttributes;
182 QVector<Qt3DRender::Render::Buffer *> backendBuffers;
183};
184
185
186} // anonymous
187
188class tst_UpdateMeshTriangleListJob : public QObject
189{
190 Q_OBJECT
191
192private Q_SLOTS:
193
194 void checkInitialState()
195 {
196 // GIVEN
197 Qt3DRender::Render::UpdateMeshTriangleListJob backendUpdateMeshTriangleListJob;
198
199 // THEN
200 QVERIFY(backendUpdateMeshTriangleListJob.managers() == nullptr);
201 }
202
203 void checkInitialize()
204 {
205 // GIVEN
206 Qt3DRender::Render::UpdateMeshTriangleListJob updateMeshTriangleListJob;
207 Qt3DRender::Render::NodeManagers managers;
208
209 // WHEN
210 updateMeshTriangleListJob.setManagers(&managers);
211
212 // THEN
213 QVERIFY(updateMeshTriangleListJob.managers() == &managers);
214 }
215
216 void checkRunNoDirtyGeometryRenderNoDirtyAttributesNoDirtyBuffers()
217 {
218 // GIVEN
219 QmlSceneReader sceneReader(QUrl("qrc:/test_scene.qml"));
220 QScopedPointer<Qt3DCore::QNode> root(qobject_cast<Qt3DCore::QNode *>(object: sceneReader.root()));
221 QVERIFY(root);
222 QScopedPointer<Qt3DRender::TestAspect> test(new Qt3DRender::TestAspect(root.data()));
223
224 // Run required jobs to load geometries and meshes
225 runRequiredJobs(aspect: test.data());
226
227 // WHEN
228 const NodeCollection collection(test.data(), root.data());
229
230 // THEN
231 QCOMPARE(collection.geometryRenderers.size(), 1);
232 QCOMPARE(collection.attributes.size(), 5);
233 QCOMPARE(collection.buffers.size(), 2);
234
235 // WHEN
236 for (Qt3DRender::Render::GeometryRenderer *g : collection.backendGeometryRenderer)
237 g->unsetDirty();
238 for (Qt3DRender::Render::Attribute *a : collection.backendAttributes)
239 a->unsetDirty();
240 for (Qt3DRender::Render::Buffer *b : collection.backendBuffers)
241 b->unsetDirty();
242
243 Qt3DRender::Render::UpdateMeshTriangleListJob backendUpdateMeshTriangleListJob;
244 backendUpdateMeshTriangleListJob.setManagers(test->nodeManagers());
245
246 backendUpdateMeshTriangleListJob.run();
247
248 // THEN
249 QCOMPARE(test->nodeManagers()->geometryRendererManager()->geometryRenderersRequiringTriangleDataRefresh().size(), 0);
250 }
251
252 void checkRunDirtyGeometryRenderNoDirtyAttributesNoDirtyBuffers()
253 {
254 // GIVEN
255 QmlSceneReader sceneReader(QUrl("qrc:/test_scene.qml"));
256 QScopedPointer<Qt3DCore::QNode> root(qobject_cast<Qt3DCore::QNode *>(object: sceneReader.root()));
257 QVERIFY(root);
258 QScopedPointer<Qt3DRender::TestAspect> test(new Qt3DRender::TestAspect(root.data()));
259
260 // Run required jobs to load geometries and meshes
261 runRequiredJobs(aspect: test.data());
262
263 // WHEN
264 const NodeCollection collection(test.data(), root.data());
265
266 // THEN
267 QCOMPARE(collection.geometryRenderers.size(), 1);
268 QCOMPARE(collection.attributes.size(), 5);
269 QCOMPARE(collection.buffers.size(), 2);
270
271 // WHEN
272 for (Qt3DRender::Render::Attribute *a : collection.backendAttributes)
273 a->unsetDirty();
274 for (Qt3DRender::Render::Buffer *b : collection.backendBuffers)
275 b->unsetDirty();
276
277 Qt3DRender::Render::UpdateMeshTriangleListJob backendUpdateMeshTriangleListJob;
278 backendUpdateMeshTriangleListJob.setManagers(test->nodeManagers());
279
280 backendUpdateMeshTriangleListJob.run();
281
282 // THEN
283 QCOMPARE(test->nodeManagers()->geometryRendererManager()->geometryRenderersRequiringTriangleDataRefresh().size(), 1);
284 }
285
286 void checkRunDirtyGeometryRenderDirtyAttributesNoDirtyBuffers()
287 {
288 // GIVEN
289 QmlSceneReader sceneReader(QUrl("qrc:/test_scene.qml"));
290 QScopedPointer<Qt3DCore::QNode> root(qobject_cast<Qt3DCore::QNode *>(object: sceneReader.root()));
291 QVERIFY(root);
292 QScopedPointer<Qt3DRender::TestAspect> test(new Qt3DRender::TestAspect(root.data()));
293
294 // Run required jobs to load geometries and meshes
295 runRequiredJobs(aspect: test.data());
296
297 // WHEN
298 const NodeCollection collection(test.data(), root.data());
299
300 // THEN
301 QCOMPARE(collection.geometryRenderers.size(), 1);
302 QCOMPARE(collection.attributes.size(), 5);
303 QCOMPARE(collection.buffers.size(), 2);
304
305 // WHEN
306 for (Qt3DRender::Render::Buffer *b : collection.backendBuffers)
307 b->unsetDirty();
308
309 Qt3DRender::Render::UpdateMeshTriangleListJob backendUpdateMeshTriangleListJob;
310 backendUpdateMeshTriangleListJob.setManagers(test->nodeManagers());
311
312 backendUpdateMeshTriangleListJob.run();
313
314 // THEN
315 QCOMPARE(test->nodeManagers()->geometryRendererManager()->geometryRenderersRequiringTriangleDataRefresh().size(), 1);
316 }
317
318 void checkRunDirtyGeometryRenderDirtyAttributesDirtyBuffers()
319 {
320 // GIVEN
321 QmlSceneReader sceneReader(QUrl("qrc:/test_scene.qml"));
322 QScopedPointer<Qt3DCore::QNode> root(qobject_cast<Qt3DCore::QNode *>(object: sceneReader.root()));
323 QVERIFY(root);
324 QScopedPointer<Qt3DRender::TestAspect> test(new Qt3DRender::TestAspect(root.data()));
325
326 // Run required jobs to load geometries and meshes
327 runRequiredJobs(aspect: test.data());
328
329 // WHEN
330 const NodeCollection collection(test.data(), root.data());
331
332 // THEN
333 QCOMPARE(collection.geometryRenderers.size(), 1);
334 QCOMPARE(collection.attributes.size(), 5);
335 QCOMPARE(collection.buffers.size(), 2);
336
337 // WHEN
338 for (Qt3DRender::Render::Buffer *b : collection.backendBuffers)
339 b->unsetDirty();
340
341 Qt3DRender::Render::UpdateMeshTriangleListJob backendUpdateMeshTriangleListJob;
342 backendUpdateMeshTriangleListJob.setManagers(test->nodeManagers());
343
344 backendUpdateMeshTriangleListJob.run();
345
346 // THEN
347 QCOMPARE(test->nodeManagers()->geometryRendererManager()->geometryRenderersRequiringTriangleDataRefresh().size(), 1);
348 }
349
350};
351
352QTEST_MAIN(tst_UpdateMeshTriangleListJob)
353
354#include "tst_updatemeshtrianglelistjob.moc"
355

source code of qt3d/tests/auto/render/updatemeshtrianglelistjob/tst_updatemeshtrianglelistjob.cpp