/**************************************************************************** ** ** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt3D module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL3$ ** 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 http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPLv3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or later as published by the Free ** Software Foundation and appearing in the file LICENSE.GPL included in ** the packaging of this file. Please review the following information to ** ensure the GNU General Public License version 2.0 requirements will be ** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qrenderaspect.h" #include "qrenderaspect_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void initResources() { Q_INIT_RESOURCE(render); } QT_BEGIN_NAMESPACE namespace Qt3D { /*! \class Qt3D::QRenderAspectPrivate \internal */ QRenderAspectPrivate::QRenderAspectPrivate(QRenderAspect::RenderType type, QRenderAspect *qq) : QAbstractAspectPrivate(qq) , m_renderer(new Render::Renderer(type)) , m_surfaceEventFilter(new Render::PlatformSurfaceFilter(m_renderer)) , m_surface(Q_NULLPTR) , m_time(0) , m_initialized(false) , m_shuttingDown(false) { initResources(); m_aspectType = QAbstractAspect::AspectRenderer; } void QRenderAspectPrivate::setSurface(QSurface *surface) { if (m_surface == surface) return; m_surface = surface; // If we have a new surface, install the platform surface event filter onto it // so that we get informed when the underlying platform surface is about to be // deleted and we can tell the renderer about it before it's too late. if (m_surface) { bool hasPlatformSurface = false; switch (m_surface->surfaceClass()) { case QSurface::Window: { QWindow *window = static_cast(m_surface); m_surfaceEventFilter->setWindow(window); hasPlatformSurface = (window->handle() != Q_NULLPTR); break; } case QSurface::Offscreen: { QOffscreenSurface *offscreen = static_cast(m_surface); m_surfaceEventFilter->setOffscreenSurface(offscreen); hasPlatformSurface = (offscreen->handle() != Q_NULLPTR); break; } } // If the window/offscreen surface has a native surface, tell the renderer if (hasPlatformSurface) m_renderer->setSurface(surface); } } QRenderAspect::QRenderAspect(QObject *parent) : QAbstractAspect(*new QRenderAspectPrivate(Threaded, this), parent) { // Won't return until the private RenderThread in Renderer has been created // The Renderer is set to wait the surface with a wait condition // Threads modifying the Renderer should be synchronized using the Renderer's mutex registerBackendTypes(); } QRenderAspect::QRenderAspect(QRenderAspect::RenderType type, QObject *parent) : QAbstractAspect(*new QRenderAspectPrivate(type, this), parent) { registerBackendTypes(); } /*! \internal */ QRenderAspect::QRenderAspect(QRenderAspectPrivate &dd, QObject *parent) : QAbstractAspect(dd, parent) { registerBackendTypes(); } void QRenderAspect::registerBackendTypes() { Q_D(QRenderAspect); registerBackendType(QBackendNodeFunctorPtr(new Render::RenderEntityFunctor(d->m_renderer))); registerBackendType(QBackendNodeFunctorPtr(new Render::RenderNodeFunctor(d->m_renderer->transformManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::RenderNodeFunctor(d->m_renderer->materialManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::RenderNodeFunctor(d->m_renderer->techniqueManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::RenderTextureFunctor(d->m_renderer->textureManager(), d->m_renderer->textureImageManager(), d->m_renderer->textureDataManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::RenderNodeFunctor(d->m_renderer->shaderManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::RenderNodeFunctor(d->m_renderer->effectManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::RenderNodeFunctor(d->m_renderer->criterionManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::RenderNodeFunctor(d->m_renderer->cameraManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::RenderNodeFunctor(d->m_renderer->layerManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::RenderMeshCreatorFunctor(d->m_renderer->meshManager(), d->m_renderer->meshDataManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::RenderNodeFunctor(d->m_renderer->renderPassManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::RenderSceneFunctor(d->m_renderer->sceneManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::RenderNodeFunctor(d->m_renderer->renderTargetManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::RenderNodeFunctor(d->m_renderer->attachmentManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::RenderNodeFunctor(d->m_renderer->sortCriterionManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::FrameGraphNodeFunctor(d->m_renderer->frameGraphManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::FrameGraphNodeFunctor(d->m_renderer->frameGraphManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::FrameGraphNodeFunctor(d->m_renderer->frameGraphManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::FrameGraphNodeFunctor(d->m_renderer->frameGraphManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::FrameGraphNodeFunctor(d->m_renderer->frameGraphManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::FrameGraphNodeFunctor(d->m_renderer->frameGraphManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::FrameGraphNodeFunctor(d->m_renderer->frameGraphManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::FrameGraphNodeFunctor(d->m_renderer->frameGraphManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::FrameGraphNodeFunctor(d->m_renderer->frameGraphManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::FrameGraphComponentFunctor(d->m_renderer))); registerBackendType(QBackendNodeFunctorPtr(new Render::RenderNodeFunctor(d->m_renderer->parameterManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::RenderShaderDataFunctor(d->m_renderer->shaderDataManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::RenderTextureImageFunctor(d->m_renderer->textureManager(), d->m_renderer->textureImageManager(), d->m_renderer->textureDataManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::FrameGraphNodeFunctor(d->m_renderer->frameGraphManager()))); } void QRenderAspect::renderInitialize(QOpenGLContext *context) { Q_D(QRenderAspect); d->m_renderer->initialize(context); } void QRenderAspect::renderSynchronous() { Q_D(QRenderAspect); d->m_renderer->doRender(1); } void QRenderAspect::renderShutdown() { Q_D(QRenderAspect); d->m_shuttingDown = true; d->m_renderer->doRender(); // Consume the remaining frames } QVector QRenderAspect::jobsToExecute(qint64 time) { Q_D(QRenderAspect); d->m_time = time; // Create jobs that will get exectued by the threadpool QVector jobs; // Create jobs to load in any meshes that are pending if (d->m_renderer != Q_NULLPTR && !d->m_shuttingDown) { d->m_framePreparationJob.reset(new Render::FramePreparationJob(d->m_renderer, d->m_renderer->renderSceneRoot())); d->m_cleanupJob.reset(new Render::FrameCleanupJob(d->m_renderer)); d->m_worldTransformJob.reset(new Render::UpdateWorldTransformJob(d->m_renderer->renderSceneRoot())); d->m_boundingVolumeJob.reset(new Render::UpdateBoundingVolumeJob(d->m_renderer->renderSceneRoot())); QHash meshSources = d->m_renderer->meshDataManager()->meshesPending(); Q_FOREACH (const QNodeId &meshId, meshSources.keys()) { Render::LoadMeshDataJobPtr loadMeshJob(new Render::LoadMeshDataJob(meshSources[meshId], meshId)); loadMeshJob->setRenderer(d->m_renderer); jobs.append(loadMeshJob); } QVector texturesPending = d->m_renderer->textureDataManager()->texturesPending(); Q_FOREACH (const QNodeId &textureId, texturesPending) { Render::LoadTextureDataJobPtr loadTextureJob(new Render::LoadTextureDataJob(textureId)); loadTextureJob->setRenderer(d->m_renderer); jobs.append(loadTextureJob); } // TO DO: Have 2 jobs queue // One for urgent jobs that are mandatory for the rendering of a frame // Another for jobs that can span across multiple frames (Scene/Mesh loading) QVector sceneJobs = d->m_renderer->sceneManager()->pendingSceneLoaderJobs(); Q_FOREACH (Render::LoadSceneJobPtr job, sceneJobs) { job->setRenderer(d->m_renderer); jobs.append(job); } // Create jobs to update transforms and bounding volumes // We can only update bounding volumes once all world transforms are known d->m_boundingVolumeJob->addDependency(d->m_worldTransformJob); d->m_framePreparationJob->addDependency(d->m_worldTransformJob); // Add all jobs to queue jobs.append(d->m_worldTransformJob); jobs.append(d->m_boundingVolumeJob); jobs.append(d->m_framePreparationJob); // Traverse the current framegraph and create jobs to populate // RenderBins with RenderCommands QVector renderBinJobs = d->m_renderer->createRenderBinJobs(); // TODO: Add wrapper around ThreadWeaver::Collection for (int i = 0; i < renderBinJobs.size(); ++i) { QAspectJobPtr renderBinJob = renderBinJobs.at(i); renderBinJob->addDependency(d->m_boundingVolumeJob); jobs.append(renderBinJob); d->m_cleanupJob->addDependency(renderBinJob); } jobs.append(d->m_cleanupJob); } return jobs; } void QRenderAspect::sceneNodeAdded(QSceneChangePtr &e) { QScenePropertyChangePtr propertyChange = e.staticCast(); QNodePtr nodePtr = propertyChange->value().value(); QNode *n = nodePtr.data(); QNodeVisitor visitor; visitor.traverse(n, this, &QRenderAspect::visitNode, &QRenderAspect::visitNode); } void QRenderAspect::sceneNodeRemoved(QSceneChangePtr &e) { QScenePropertyChangePtr propertyChange = e.staticCast(); QNodePtr nodePtr = propertyChange->value().value(); QNode *n = nodePtr.data(); QAbstractAspect::clearBackendNode(n); } qint64 QRenderAspect::time() const { Q_D(const QRenderAspect); return d->m_time; } void QRenderAspect::setRootEntity(QEntity *rootObject) { // setSceneGraphRoot is synchronized using the Renderer's mutex Q_D(QRenderAspect); QNodeVisitor visitor; visitor.traverse(rootObject, this, &QRenderAspect::visitNode, &QRenderAspect::visitNode); d->m_renderer->setSceneGraphRoot(d->m_renderer->renderNodesManager()->lookupResource(rootObject->id())); } void QRenderAspect::onInitialize(const QVariantMap &data) { // TODO: Remove the m_initialized variable and split out onInitialize() // and setting a resource (the QSurface) on the aspects. // TODO: d-pointer this class too as it is public Q_D(QRenderAspect); if (!d->m_initialized) { d->m_renderer->setQRenderAspect(this); d->m_renderer->createAllocators(); d->m_initialized = true; } QSurface *surface = Q_NULLPTR; const QVariant &v = data.value(QStringLiteral("surface")); if (v.isValid()) surface = v.value(); if (surface) d->setSurface(surface); } void QRenderAspect::onCleanup() { Q_D(QRenderAspect); delete d->m_renderer; d->m_renderer = Q_NULLPTR; } void QRenderAspect::visitNode(QNode *node) { QAbstractAspect::createBackendNode(node); } } // Qt3D QT_END_NAMESPACE QT3D_REGISTER_NAMESPACED_ASPECT("render", QT_PREPEND_NAMESPACE(Qt3D), QRenderAspect)