/**************************************************************************** ** ** Copyright (C) 2016 Paul Lemire ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt3D module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include "qmlscenereader.h" QT_BEGIN_NAMESPACE namespace Qt3DRender { // Needs to be in that namespace to be friend with QRenderAspect QVector getNodesForCreation(Qt3DCore::QNode *root) { using namespace Qt3DCore; QVector nodes; Qt3DCore::QNodeVisitor visitor; visitor.traverse(root, [&nodes](QNode *node) { nodes.append(node); // Store the metaobject of the node in the QNode so that we have it available // to us during destruction in the QNode destructor. This allows us to send // the QNodeId and the metaobject as typeinfo to the backend aspects so they // in turn can find the correct QBackendNodeMapper object to handle the destruction // of the corresponding backend nodes. QNodePrivate *d = QNodePrivate::get(node); d->m_typeInfo = const_cast(QNodePrivate::findStaticMetaObject(node->metaObject())); // Mark this node as having been handled for creation so that it is picked up d->m_hasBackendNode = true; }); return nodes; } QVector nodeTreeChangesForNodes(const QVector nodes) { QVector nodeTreeChanges; nodeTreeChanges.reserve(nodes.size()); for (Qt3DCore::QNode *n : nodes) { nodeTreeChanges.push_back({ n->id(), Qt3DCore::QNodePrivate::get(n)->m_typeInfo, Qt3DCore::NodeTreeChange::Added, n }); } return nodeTreeChanges; } class TestAspect : public Qt3DRender::QRenderAspect { public: TestAspect(Qt3DCore::QNode *root) : Qt3DRender::QRenderAspect(Qt3DRender::QRenderAspect::Synchronous) , m_sceneRoot(nullptr) { Qt3DRender::QRenderAspect::onRegistered(); const QVector nodes = getNodesForCreation(root); d_func()->setRootAndCreateNodes(qobject_cast(root), nodeTreeChangesForNodes(nodes)); Qt3DRender::Render::Entity *rootEntity = nodeManagers()->lookupResource(rootEntityId()); Q_ASSERT(rootEntity); m_sceneRoot = rootEntity; } ~TestAspect() { QRenderAspect::onUnregistered(); } void onRegistered() { Qt3DRender::QRenderAspect::onRegistered(); } void onUnregistered() { Qt3DRender::QRenderAspect::onUnregistered(); } Qt3DRender::Render::NodeManagers *nodeManagers() const { return d_func()->m_renderer->nodeManagers(); } Qt3DRender::Render::FrameGraphNode *frameGraphRoot() const { return d_func()->m_renderer->frameGraphRoot(); } Qt3DRender::Render::RenderSettings *renderSettings() const { return d_func()->m_renderer->settings(); } Qt3DRender::Render::Entity *sceneRoot() const { return m_sceneRoot; } private: Qt3DRender::Render::Entity *m_sceneRoot; }; } // Qt3DRender QT_END_NAMESPACE namespace { void runRequiredJobs(Qt3DRender::TestAspect *aspect) { Qt3DRender::Render::GeometryRendererManager *geomRendererManager = aspect->nodeManagers()->geometryRendererManager(); const QVector dirtyGeometryRenderers = geomRendererManager->dirtyGeometryRenderers(); QVector dirtyGeometryRendererJobs; dirtyGeometryRendererJobs.reserve(dirtyGeometryRenderers.size()); // Load Geometry for (const Qt3DCore::QNodeId geoRendererId : dirtyGeometryRenderers) { Qt3DRender::Render::HGeometryRenderer geometryRendererHandle = geomRendererManager->lookupHandle(geoRendererId); if (!geometryRendererHandle.isNull()) { auto job = Qt3DRender::Render::LoadGeometryJobPtr::create(geometryRendererHandle); job->setNodeManagers(aspect->nodeManagers()); dirtyGeometryRendererJobs.push_back(job); } } } struct NodeCollection { explicit NodeCollection(Qt3DRender::TestAspect *aspect, QObject *frontendRoot) : geometryRenderers(frontendRoot->findChildren()) , attributes(frontendRoot->findChildren()) , buffers(frontendRoot->findChildren()) { // THEN QCOMPARE(aspect->nodeManagers()->geometryManager()->activeHandles().size(), geometryRenderers.size()); QCOMPARE(aspect->nodeManagers()->attributeManager()->activeHandles().size(), attributes.size()); QCOMPARE(aspect->nodeManagers()->bufferManager()->activeHandles().size(), buffers.size()); for (const Qt3DRender::QGeometryRenderer *g : qAsConst(geometryRenderers)) { Qt3DRender::Render::GeometryRenderer *backend = aspect->nodeManagers()->geometryRendererManager()->lookupResource(g->id()); QVERIFY(backend != nullptr); backendGeometryRenderer.push_back(backend); } for (const Qt3DCore::QAttribute *a : qAsConst(attributes)) { Qt3DRender::Render::Attribute *backend = aspect->nodeManagers()->attributeManager()->lookupResource(a->id()); QVERIFY(backend != nullptr); backendAttributes.push_back(backend); } for (const Qt3DCore::QBuffer *b : qAsConst(buffers)) { Qt3DRender::Render::Buffer *backend = aspect->nodeManagers()->bufferManager()->lookupResource(b->id()); QVERIFY(backend != nullptr); backendBuffers.push_back(backend); } } QList geometryRenderers; QList attributes; QList buffers; QVector backendGeometryRenderer; QVector backendAttributes; QVector backendBuffers; }; } // anonymous class tst_UpdateMeshTriangleListJob : public QObject { Q_OBJECT private Q_SLOTS: void checkInitialState() { // GIVEN Qt3DRender::Render::UpdateMeshTriangleListJob backendUpdateMeshTriangleListJob; // THEN QVERIFY(backendUpdateMeshTriangleListJob.managers() == nullptr); } void checkInitialize() { // GIVEN Qt3DRender::Render::UpdateMeshTriangleListJob updateMeshTriangleListJob; Qt3DRender::Render::NodeManagers managers; // WHEN updateMeshTriangleListJob.setManagers(&managers); // THEN QVERIFY(updateMeshTriangleListJob.managers() == &managers); } void checkRunNoDirtyGeometryRenderNoDirtyAttributesNoDirtyBuffers() { // GIVEN QmlSceneReader sceneReader(QUrl("qrc:/test_scene.qml")); QScopedPointer root(qobject_cast(sceneReader.root())); QVERIFY(root); QScopedPointer test(new Qt3DRender::TestAspect(root.data())); // Run required jobs to load geometries and meshes runRequiredJobs(test.data()); // WHEN const NodeCollection collection(test.data(), root.data()); // THEN QCOMPARE(collection.geometryRenderers.size(), 1); QCOMPARE(collection.attributes.size(), 5); QCOMPARE(collection.buffers.size(), 2); // WHEN for (Qt3DRender::Render::GeometryRenderer *g : collection.backendGeometryRenderer) g->unsetDirty(); for (Qt3DRender::Render::Attribute *a : collection.backendAttributes) a->unsetDirty(); for (Qt3DRender::Render::Buffer *b : collection.backendBuffers) b->unsetDirty(); Qt3DRender::Render::UpdateMeshTriangleListJob backendUpdateMeshTriangleListJob; backendUpdateMeshTriangleListJob.setManagers(test->nodeManagers()); backendUpdateMeshTriangleListJob.run(); // THEN QCOMPARE(test->nodeManagers()->geometryRendererManager()->geometryRenderersRequiringTriangleDataRefresh().size(), 0); } void checkRunDirtyGeometryRenderNoDirtyAttributesNoDirtyBuffers() { // GIVEN QmlSceneReader sceneReader(QUrl("qrc:/test_scene.qml")); QScopedPointer root(qobject_cast(sceneReader.root())); QVERIFY(root); QScopedPointer test(new Qt3DRender::TestAspect(root.data())); // Run required jobs to load geometries and meshes runRequiredJobs(test.data()); // WHEN const NodeCollection collection(test.data(), root.data()); // THEN QCOMPARE(collection.geometryRenderers.size(), 1); QCOMPARE(collection.attributes.size(), 5); QCOMPARE(collection.buffers.size(), 2); // WHEN for (Qt3DRender::Render::Attribute *a : collection.backendAttributes) a->unsetDirty(); for (Qt3DRender::Render::Buffer *b : collection.backendBuffers) b->unsetDirty(); Qt3DRender::Render::UpdateMeshTriangleListJob backendUpdateMeshTriangleListJob; backendUpdateMeshTriangleListJob.setManagers(test->nodeManagers()); backendUpdateMeshTriangleListJob.run(); // THEN QCOMPARE(test->nodeManagers()->geometryRendererManager()->geometryRenderersRequiringTriangleDataRefresh().size(), 1); } void checkRunDirtyGeometryRenderDirtyAttributesNoDirtyBuffers() { // GIVEN QmlSceneReader sceneReader(QUrl("qrc:/test_scene.qml")); QScopedPointer root(qobject_cast(sceneReader.root())); QVERIFY(root); QScopedPointer test(new Qt3DRender::TestAspect(root.data())); // Run required jobs to load geometries and meshes runRequiredJobs(test.data()); // WHEN const NodeCollection collection(test.data(), root.data()); // THEN QCOMPARE(collection.geometryRenderers.size(), 1); QCOMPARE(collection.attributes.size(), 5); QCOMPARE(collection.buffers.size(), 2); // WHEN for (Qt3DRender::Render::Buffer *b : collection.backendBuffers) b->unsetDirty(); Qt3DRender::Render::UpdateMeshTriangleListJob backendUpdateMeshTriangleListJob; backendUpdateMeshTriangleListJob.setManagers(test->nodeManagers()); backendUpdateMeshTriangleListJob.run(); // THEN QCOMPARE(test->nodeManagers()->geometryRendererManager()->geometryRenderersRequiringTriangleDataRefresh().size(), 1); } void checkRunDirtyGeometryRenderDirtyAttributesDirtyBuffers() { // GIVEN QmlSceneReader sceneReader(QUrl("qrc:/test_scene.qml")); QScopedPointer root(qobject_cast(sceneReader.root())); QVERIFY(root); QScopedPointer test(new Qt3DRender::TestAspect(root.data())); // Run required jobs to load geometries and meshes runRequiredJobs(test.data()); // WHEN const NodeCollection collection(test.data(), root.data()); // THEN QCOMPARE(collection.geometryRenderers.size(), 1); QCOMPARE(collection.attributes.size(), 5); QCOMPARE(collection.buffers.size(), 2); // WHEN for (Qt3DRender::Render::Buffer *b : collection.backendBuffers) b->unsetDirty(); Qt3DRender::Render::UpdateMeshTriangleListJob backendUpdateMeshTriangleListJob; backendUpdateMeshTriangleListJob.setManagers(test->nodeManagers()); backendUpdateMeshTriangleListJob.run(); // THEN QCOMPARE(test->nodeManagers()->geometryRendererManager()->geometryRenderersRequiringTriangleDataRefresh().size(), 1); } }; QTEST_MAIN(tst_UpdateMeshTriangleListJob) #include "tst_updatemeshtrianglelistjob.moc"