blob: 57bd7745db1e038b97136ffa4ee670d5cc2613cd [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
** 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 <QtTest/QtTest>
#include <QMatrix4x4>
#include <Qt3DCore/QEntity>
#include <Qt3DCore/QTransform>
#include <Qt3DRender/QMaterial>
#include <Qt3DExtras/QForwardRenderer>
#include <Qt3DExtras/QPhongMaterial>
#include <Qt3DExtras/QCylinderMesh>
#include <Qt3DRender/QRenderSettings>
#include <Qt3DRender/private/managers_p.h>
#include <Qt3DCore/private/qresourcemanager_p.h>
#include <Qt3DRender/qcamera.h>
#include <Qt3DRender/qrenderaspect.h>
#include <Qt3DRender/private/qrenderaspect_p.h>
#include <Qt3DRender/private/nodemanagers_p.h>
#include <Qt3DRender/private/updateworldtransformjob_p.h>
#include <Qt3DQuick/QQmlAspectEngine>
#include <Qt3DCore/private/qaspectjobmanager_p.h>
#include <Qt3DCore/private/qaspectengine_p.h>
#include <Qt3DCore/private/qaspectmanager_p.h>
#include <Qt3DCore/private/qnodevisitor_p.h>
#include <Qt3DCore/private/qnode_p.h>
#include <renderer_p.h>
#include <QQmlComponent>
#include <QScopedPointer>
QT_BEGIN_NAMESPACE
namespace Qt3DRender {
class QRenderAspectTester : public Qt3DRender::QRenderAspect
{
Q_OBJECT
public:
QRenderAspectTester(bool withWindow = false)
: Qt3DRender::QRenderAspect(QRenderAspect::Synchronous)
, m_jobManager(new Qt3DCore::QAspectJobManager())
{
Qt3DCore::QAbstractAspectPrivate::get(this)->m_jobManager = m_jobManager.data();
if (withWindow) {
m_window.reset(new QWindow());
m_window->resize(1024, 768);
QSurfaceFormat format;
if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) {
format.setVersion(4, 3);
format.setProfile(QSurfaceFormat::CoreProfile);
}
format.setDepthBufferSize( 24 );
format.setSamples( 4 );
format.setStencilBufferSize(8);
m_window->setFormat(format);
m_window->create();
QRenderAspect::onRegistered();
}
}
QVector<Qt3DCore::QAspectJobPtr> worldTransformJob()
{
auto renderer = static_cast<Render::OpenGL::Renderer *>(d_func()->m_renderer);
auto daspect = Qt3DRender::QRenderAspectPrivate::get(renderer->aspect());
daspect->m_worldTransformJob->setRoot(d_func()->m_renderer->sceneRoot());
return QVector<Qt3DCore::QAspectJobPtr>() << daspect->m_worldTransformJob;
}
QVector<Qt3DCore::QAspectJobPtr> updateBoundingJob()
{
auto renderer = static_cast<Render::OpenGL::Renderer *>(d_func()->m_renderer);
auto daspect = Qt3DRender::QRenderAspectPrivate::get(renderer->aspect());
daspect->m_updateWorldBoundingVolumeJob->setManager(d_func()->m_renderer->nodeManagers()->renderNodesManager());
return QVector<Qt3DCore::QAspectJobPtr>() << daspect->m_updateWorldBoundingVolumeJob;
}
QVector<Qt3DCore::QAspectJobPtr> calculateBoundingVolumeJob()
{
auto renderer = static_cast<Render::OpenGL::Renderer *>(d_func()->m_renderer);
auto daspect = Qt3DRender::QRenderAspectPrivate::get(renderer->aspect());
daspect->m_calculateBoundingVolumeJob->setRoot(d_func()->m_renderer->sceneRoot());
return QVector<Qt3DCore::QAspectJobPtr>() << daspect->m_calculateBoundingVolumeJob;
}
QVector<Qt3DCore::QAspectJobPtr> framePreparationJob()
{
static_cast<Render::OpenGL::Renderer *>(d_func()->m_renderer)->m_updateShaderDataTransformJob->setManagers(d_func()->m_renderer->nodeManagers());
return QVector<Qt3DCore::QAspectJobPtr>() << static_cast<Render::OpenGL::Renderer *>(d_func()->m_renderer)->m_updateShaderDataTransformJob;
}
QVector<Qt3DCore::QAspectJobPtr> frameCleanupJob()
{
static_cast<Render::OpenGL::Renderer *>(d_func()->m_renderer)->m_cleanupJob->setRoot(d_func()->m_renderer->sceneRoot());
return QVector<Qt3DCore::QAspectJobPtr>() << static_cast<Render::OpenGL::Renderer *>(d_func()->m_renderer)->m_cleanupJob;
}
QVector<Qt3DCore::QAspectJobPtr> renderBinJobs()
{
return d_func()->m_renderer->renderBinJobs();
}
void onRootEntityChanged(Qt3DCore::QEntity *root)
{
if (!m_window) {
QVector<Qt3DCore::NodeTreeChange> nodes;
Qt3DCore::QNodeVisitor v;
v.traverse(root, [&nodes](Qt3DCore::QNode *node) {
Qt3DCore::QNodePrivate *d = Qt3DCore::QNodePrivate::get(node);
d->m_typeInfo = const_cast<QMetaObject*>(Qt3DCore::QNodePrivate::findStaticMetaObject(node->metaObject()));
d->m_hasBackendNode = true;
nodes.push_back({
node->id(),
Qt3DCore::QNodePrivate::get(node)->m_typeInfo,
Qt3DCore::NodeTreeChange::Added,
node
});
});
for (const auto &node: nodes)
d_func()->createBackendNode(node);
static_cast<Qt3DRender::Render::OpenGL::Renderer *>(d_func()->m_renderer)->m_renderSceneRoot =
d_func()->m_renderer->nodeManagers()
->lookupResource<Qt3DRender::Render::Entity, Qt3DRender::Render::EntityManager>(root->id());
}
}
private:
QScopedPointer<Qt3DCore::QAspectJobManager> m_jobManager;
QScopedPointer<QWindow> m_window;
};
}
QT_END_NAMESPACE
using namespace Qt3DRender;
Qt3DCore::QEntity *loadFromQML(const QUrl &source)
{
QQmlEngine qmlEngine;
QQmlComponent *component = new QQmlComponent(&qmlEngine, source);
while (component->isLoading())
QThread::usleep(250);
QObject *rootObj = component->create();
return qobject_cast<Qt3DCore::QEntity *>(rootObj);
}
Qt3DCore::QEntity *buildBigScene()
{
Qt3DCore::QEntity *root = new Qt3DCore::QEntity();
// Camera
Qt3DRender::QCamera *cameraEntity = new Qt3DRender::QCamera(root);
cameraEntity->setObjectName(QStringLiteral("cameraEntity"));
cameraEntity->lens()->setPerspectiveProjection(45.0f, 16.0f/9.0f, 0.1f, 1000.0f);
cameraEntity->setPosition(QVector3D(0, -250.0f, -50.0f));
cameraEntity->setUpVector(QVector3D(0, 1, 0));
cameraEntity->setViewCenter(QVector3D(0, 0, 0));
// FrameGraph
Qt3DRender::QRenderSettings* renderSettings = new Qt3DRender::QRenderSettings();
Qt3DExtras::QForwardRenderer *forwardRenderer = new Qt3DExtras::QForwardRenderer();
forwardRenderer->setCamera(cameraEntity);
forwardRenderer->setClearColor(Qt::black);
renderSettings->setActiveFrameGraph(forwardRenderer);
root->addComponent(renderSettings);
const float radius = 100.0f;
const int max = 1000;
const float det = 1.0f / max;
// Scene
for (int i = 0; i < max; i++) {
Qt3DCore::QEntity *e = new Qt3DCore::QEntity();
Qt3DCore::QTransform *transform = new Qt3DCore::QTransform();
Qt3DExtras::QCylinderMesh *mesh = new Qt3DExtras::QCylinderMesh();
Qt3DExtras::QPhongMaterial *material = new Qt3DExtras::QPhongMaterial();
mesh->setRings(50.0f);
mesh->setSlices(30.0f);
mesh->setRadius(2.5f);
mesh->setLength(5.0f);
const float angle = M_PI * 2.0f * i * det * 10.;
material->setDiffuse(QColor(qFabs(qCos(angle)) * 255, 204, 75));
material->setAmbient(Qt::black);
material->setSpecular(Qt::white);
material->setShininess(150.0f);
QMatrix4x4 m;
m.translate(QVector3D(radius * qCos(angle), 200.* i * det, radius * qSin(angle)));
m.rotate(30.0f * i, QVector3D(1.0f, 0.0f, 0.0f));
m.rotate(45.0f * i, QVector3D(0.0f, 0.0f, 1.0f));
transform->setMatrix(m);
e->addComponent(transform);
e->addComponent(mesh);
e->addComponent(material);
e->setParent(root);
}
return root;
}
class tst_benchJobs : public QObject
{
Q_OBJECT
private:
Qt3DCore::QEntity *m_bigSceneRoot;
public:
tst_benchJobs()
: m_bigSceneRoot(buildBigScene())
{}
private Q_SLOTS:
void updateTransformJob_data()
{
QTest::addColumn<Qt3DCore::QEntity*>("rootEntity");
QTest::newRow("bigscene") << m_bigSceneRoot;
}
void updateTransformJob()
{
// GIVEN
QFETCH(Qt3DCore::QEntity*, rootEntity);
QRenderAspectTester aspect;
Qt3DCore::QAbstractAspectPrivate::get(&aspect)->setRootAndCreateNodes(qobject_cast<Qt3DCore::QEntity *>(rootEntity), {});
// WHEN
QVector<Qt3DCore::QAspectJobPtr> jobs = aspect.worldTransformJob();
QBENCHMARK {
Qt3DCore::QAbstractAspectPrivate::get(&aspect)->jobManager()->enqueueJobs(jobs);
Qt3DCore::QAbstractAspectPrivate::get(&aspect)->jobManager()->waitForAllJobs();
}
}
void updateBoundingJob_data()
{
QTest::addColumn<Qt3DCore::QEntity*>("rootEntity");
QTest::newRow("bigscene") << m_bigSceneRoot;
}
void updateBoundingJob()
{
// GIVEN
QFETCH(Qt3DCore::QEntity*, rootEntity);
QRenderAspectTester aspect;
Qt3DCore::QAbstractAspectPrivate::get(&aspect)->setRootAndCreateNodes(qobject_cast<Qt3DCore::QEntity *>(rootEntity), {});
// WHEN
QVector<Qt3DCore::QAspectJobPtr> jobs = aspect.updateBoundingJob();
QBENCHMARK {
Qt3DCore::QAbstractAspectPrivate::get(&aspect)->jobManager()->enqueueJobs(jobs);
Qt3DCore::QAbstractAspectPrivate::get(&aspect)->jobManager()->waitForAllJobs();
}
}
void calculateBoundingVolumeJob_data()
{
QTest::addColumn<Qt3DCore::QEntity*>("rootEntity");
QTest::newRow("bigscene") << m_bigSceneRoot;
}
void calculateBoundingVolumeJob()
{
// GIVEN
QFETCH(Qt3DCore::QEntity*, rootEntity);
QRenderAspectTester aspect;
Qt3DCore::QAbstractAspectPrivate::get(&aspect)->setRootAndCreateNodes(qobject_cast<Qt3DCore::QEntity *>(rootEntity), {});
// WHEN
QVector<Qt3DCore::QAspectJobPtr> jobs = aspect.calculateBoundingVolumeJob();
QBENCHMARK {
Qt3DCore::QAbstractAspectPrivate::get(&aspect)->jobManager()->enqueueJobs(jobs);
Qt3DCore::QAbstractAspectPrivate::get(&aspect)->jobManager()->waitForAllJobs();
}
}
void framePreparationJob_data()
{
QTest::addColumn<Qt3DCore::QEntity*>("rootEntity");
QTest::newRow("bigscene") << m_bigSceneRoot;
}
void framePreparationJob()
{
// GIVEN
QFETCH(Qt3DCore::QEntity*, rootEntity);
QRenderAspectTester aspect;
Qt3DCore::QAbstractAspectPrivate::get(&aspect)->setRootAndCreateNodes(qobject_cast<Qt3DCore::QEntity *>(rootEntity), {});
// WHEN
QVector<Qt3DCore::QAspectJobPtr> jobs = aspect.framePreparationJob();
QBENCHMARK {
Qt3DCore::QAbstractAspectPrivate::get(&aspect)->jobManager()->enqueueJobs(jobs);
Qt3DCore::QAbstractAspectPrivate::get(&aspect)->jobManager()->waitForAllJobs();
}
}
void frameCleanupJob_data()
{
QTest::addColumn<Qt3DCore::QEntity*>("rootEntity");
QTest::newRow("bigscene") << m_bigSceneRoot;
}
void frameCleanupJob()
{
// GIVEN
QFETCH(Qt3DCore::QEntity*, rootEntity);
QRenderAspectTester aspect;
Qt3DCore::QAbstractAspectPrivate::get(&aspect)->setRootAndCreateNodes(qobject_cast<Qt3DCore::QEntity *>(rootEntity), {});
// WHEN
QVector<Qt3DCore::QAspectJobPtr> jobs = aspect.frameCleanupJob();
QBENCHMARK {
Qt3DCore::QAbstractAspectPrivate::get(&aspect)->jobManager()->enqueueJobs(jobs);
Qt3DCore::QAbstractAspectPrivate::get(&aspect)->jobManager()->waitForAllJobs();
}
}
/* Note: The renderer still needs to be simplified to run
these jobs
void renderBinJobs_data()
{
QTest::addColumn<Qt3DCore::QEntity*>("rootEntity");
QTest::newRow("bigscene") << m_bigSceneRoot;
}
void renderBinJobs()
{
// GIVEN
QFETCH(Qt3DCore::QEntity*, rootEntity);
QScopedPointer<QThread> aspectThread(new QThread);
QRenderAspectTester aspect(true);
aspect.moveToThread(aspectThread.data());
qDebug() << 1;
Qt3DCore::QAbstractAspectPrivate::get(&aspect)->registerAspect(qobject_cast<Qt3DCore::QEntity *>(rootEntity));
qDebug() << 2;
// WHEN
QVector<Qt3DCore::QAspectJobPtr> jobs = aspect.renderBinJobs();
qDebug() << 3;
QBENCHMARK {
qDebug() << 4;
Qt3DCore::QAbstractAspectPrivate::get(&aspect)->jobManager()->enqueueJobs(jobs);
Qt3DCore::QAbstractAspectPrivate::get(&aspect)->jobManager()->waitForAllJobs();
}
}
*/
};
QTEST_MAIN(tst_benchJobs)
#include "tst_bench_jobs.moc"