/**************************************************************************** ** ** 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:LGPL$ ** 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 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.LGPL3 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-3.0.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 (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** 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-2.0.html and ** https://www.gnu.org/licenses/gpl-3.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 #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 QT_BEGIN_NAMESPACE using namespace Qt3DCore; namespace Qt3DRender { /*! * \class Qt3DRender::QRenderAspect * \inheaderfile Qt3DRender/QRenderAspect * \brief The QRenderAspect class. * \since 5.7 * \inmodule Qt3DRender */ /*! \namespace Qt3DRender::Render \inmodule Qt3DRender \brief Namespace used for accessing the classes Renderer and QRenderPlugin. */ /*! \internal */ QRenderAspectPrivate::QRenderAspectPrivate(QRenderAspect::RenderType type) : QAbstractAspectPrivate() , m_nodeManagers(nullptr) , m_renderer(nullptr) , m_initialized(false) , m_renderAfterJobs(false) , m_renderType(type) , m_offscreenHelper(nullptr) { m_instances.append(this); loadSceneParsers(); if (m_renderType == QRenderAspect::Threaded && !QOpenGLContext::supportsThreadedOpenGL()) { m_renderType = QRenderAspect::Synchronous; m_renderAfterJobs = true; } } /*! \internal */ QRenderAspectPrivate::~QRenderAspectPrivate() { // The renderer should have been shutdown as part of onUnregistered(). // If it still exists then this aspect is being deleted before the aspect // engine is finished with it. if (m_renderer != nullptr) qWarning() << Q_FUNC_INFO << "The renderer should have been deleted when reaching this point (this warning may be normal when running tests)"; delete m_nodeManagers; m_instances.removeAll(this); qDeleteAll(m_sceneImporter); } QRenderAspectPrivate *QRenderAspectPrivate::findPrivate(Qt3DCore::QAspectEngine *engine) { const QVector aspects = engine->aspects(); for (QAbstractAspect* aspect : aspects) { QRenderAspect *renderAspect = qobject_cast(aspect); if (renderAspect) return static_cast(renderAspect->d_ptr.data()); } return nullptr; } void QRenderAspectPrivate::syncDirtyFrontEndNode(QNode *node, QBackendNode *backend, bool firstTime) const { Render::BackendNode *renderBackend = static_cast(backend); renderBackend->syncFromFrontEnd(node, firstTime); } void QRenderAspectPrivate::jobsDone() { if (m_renderAfterJobs) m_renderer->doRender(true); } /*! \internal */ void QRenderAspectPrivate::registerBackendTypes() { Q_Q(QRenderAspect); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); registerBackendType(QSharedPointer::create(m_renderer, m_nodeManagers)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer::create(m_renderer, q)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer::create(m_renderer, m_nodeManagers->sceneManager())); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); // Geometry + Compute registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer::create(m_renderer, m_nodeManagers->bufferManager())); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer::create(m_renderer, m_nodeManagers->geometryRendererManager())); registerBackendType(QSharedPointer>::create(m_renderer)); registerBackendType(QSharedPointer::create(m_renderer, m_nodeManagers->skeletonManager(), m_nodeManagers->jointManager())); registerBackendType(QSharedPointer::create(m_renderer, m_nodeManagers->jointManager(), m_nodeManagers->skeletonManager())); // Textures registerBackendType(QSharedPointer::create(m_renderer, m_nodeManagers->textureManager())); registerBackendType(QSharedPointer::create(m_renderer, m_nodeManagers->textureImageManager())); // Material system registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer::create(m_renderer, m_nodeManagers)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer::create(m_renderer, m_nodeManagers)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer::create(m_renderer, m_nodeManagers)); registerBackendType(QSharedPointer>::create(m_renderer)); // Framegraph registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); // Picking registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); registerBackendType(QSharedPointer >::create(m_renderer)); // Plugins for (const QString &plugin : qAsConst(m_pluginConfig)) loadRenderPlugin(plugin); } /*! \internal */ void QRenderAspectPrivate::unregisterBackendTypes() { Q_Q(QRenderAspect); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); // Geometry + Compute unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); // Textures unregisterBackendType(); unregisterBackendType(); // Material system unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); // Framegraph unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); // Picking unregisterBackendType(); unregisterBackendType(); unregisterBackendType(); // Plugins for (Render::QRenderPlugin *plugin : qAsConst(m_renderPlugins)) plugin->unregisterBackendTypes(q); } /*! * The constructor creates a new QRenderAspect::QRenderAspect instance with the * specified \a parent. * \param parent */ QRenderAspect::QRenderAspect(QObject *parent) : QRenderAspect(Threaded, parent) {} /*! * The constructor creates a new QRenderAspect::QRenderAspect instance with the * specified \a type and \a parent. * \param type * \param parent */ QRenderAspect::QRenderAspect(QRenderAspect::RenderType type, QObject *parent) : QRenderAspect(*new QRenderAspectPrivate(type), parent) {} /*! \internal */ QRenderAspect::QRenderAspect(QRenderAspectPrivate &dd, QObject *parent) : QAbstractAspect(dd, parent) { setObjectName(QStringLiteral("Render Aspect")); } /*! \internal */ QRenderAspect::~QRenderAspect() { } // Called by Scene3DRenderer only void QRenderAspectPrivate::renderInitialize(QOpenGLContext *context) { if (m_renderer->api() == Render::AbstractRenderer::OpenGL) m_renderer->setOpenGLContext(context); m_renderer->initialize(); } /*! \internal */ void QRenderAspectPrivate::renderSynchronous(bool swapBuffers) { m_renderer->doRender(swapBuffers); } /* * \internal * Only called when rendering with QtQuick 2 and a Scene3D item */ void QRenderAspectPrivate::renderShutdown() { m_renderer->shutdown(); } QVector QRenderAspect::jobsToExecute(qint64 time) { Q_D(QRenderAspect); d->m_renderer->setTime(time); #if defined(QT3D_RENDER_DUMP_BACKEND_NODES) d->m_renderer->dumpInfo(); #endif // Create jobs that will get executed by the threadpool QVector jobs; // 1 LoadBufferJobs, GeometryJobs, SceneLoaderJobs, LoadTextureJobs // 2 CalculateBoundingVolumeJob (depends on LoadBuffer) // 3 WorldTransformJob // 4 UpdateBoundingVolume, FramePreparationJob (depend on WorlTransformJob) // 5 CalcGeometryTriangleVolumes (frame preparation job), RenderViewJobs // 6 PickBoundingVolumeJob // 7 Cleanup Job (depends on RV) // Ensure we have a settings object. It may get deleted by the call to // QChangeArbiter::syncChanges() that happens just before the render aspect is // asked for jobs to execute (this function). If that is the case, the RenderSettings will // be null and we should not generate any jobs. if (d->m_renderer->isRunning() && d->m_renderer->settings()) { Render::NodeManagers *manager = d->m_renderer->nodeManagers(); QAspectJobPtr loadingJobSync = d->m_renderer->syncLoadingJobs(); loadingJobSync->removeDependency(QWeakPointer()); // Launch skeleton loader jobs once all loading jobs have completed. const QVector skeletonsToLoad = manager->skeletonManager()->takeDirtySkeletons(Render::SkeletonManager::SkeletonDataDirty); for (const auto &skeletonHandle : skeletonsToLoad) { auto loadSkeletonJob = Render::LoadSkeletonJobPtr::create(skeletonHandle); loadSkeletonJob->setNodeManagers(manager); loadingJobSync->addDependency(loadSkeletonJob); jobs.append(loadSkeletonJob); } // 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) const QVector sceneJobs = manager->sceneManager()->takePendingSceneLoaderJobs(); for (const Render::LoadSceneJobPtr &job : sceneJobs) { job->setNodeManagers(d->m_nodeManagers); job->setSceneImporters(d->m_sceneImporter); jobs.append(job); } const QVector geometryJobs = d->createGeometryRendererJobs(); jobs.append(geometryJobs); const QVector preRenderingJobs = d->m_renderer->preRenderingJobs(); jobs.append(preRenderingJobs); // Don't spawn any rendering jobs, if the renderer decides to skip this frame // Note: this only affects rendering jobs (jobs that load buffers, // perform picking,... must still be run) if (!d->m_renderer->shouldRender()) { d->m_renderer->skipNextFrame(); QThread::msleep(1); return jobs; } // Traverse the current framegraph and create jobs to populate // RenderBins with RenderCommands // All jobs needed to create the frame and their dependencies are set by // renderBinJobs() const QVector renderBinJobs = d->m_renderer->renderBinJobs(); jobs.append(renderBinJobs); } return jobs; } QVariant QRenderAspect::executeCommand(const QStringList &args) { Q_D(QRenderAspect); return d->m_renderer->executeCommand(args); } void QRenderAspect::onEngineStartup() { Q_D(QRenderAspect); if (d->m_renderAfterJobs) // synchronous rendering but using QWindow d->m_renderer->initialize(); Render::NodeManagers *managers = d->m_renderer->nodeManagers(); Render::Entity *rootEntity = managers->lookupResource(rootEntityId()); Q_ASSERT(rootEntity); d->m_renderer->setSceneRoot(rootEntity); } void QRenderAspect::onRegistered() { // Create a renderer each time as this is destroyed in onUnregistered below. If // using a threaded renderer, this blocks until the render thread has been created // and started. Q_D(QRenderAspect); d->m_nodeManagers = new Render::NodeManagers(); // TO DO: Load proper Renderer class based on Qt configuration preferences d->m_renderer = new Render::Renderer(d->m_renderType); d->m_renderer->setScreen(d->m_screen); d->m_renderer->setNodeManagers(d->m_nodeManagers); // Create a helper for deferring creation of an offscreen surface used during cleanup // to the main thread, after we know what the surface format in use is. d->m_offscreenHelper = new Render::OffscreenSurfaceHelper(d->m_renderer); d->m_offscreenHelper->moveToThread(QCoreApplication::instance()->thread()); d->m_renderer->setOffscreenSurfaceHelper(d->m_offscreenHelper); // Register backend types now that we have a renderer d->registerBackendTypes(); if (!d->m_initialized) { // Register the VSyncFrameAdvanceService to drive the aspect manager loop // depending on the vsync if (d->m_aspectManager) { QAbstractFrameAdvanceService *advanceService = d->m_renderer->frameAdvanceService(); if (advanceService) d->services()->registerServiceProvider(Qt3DCore::QServiceLocator::FrameAdvanceService, advanceService); } if (d->services()) d->m_renderer->setServices(d->services()); d->m_initialized = true; } if (d->m_aspectManager) d->m_renderer->registerEventFilter(d->services()->eventFilterService()); } void QRenderAspect::onUnregistered() { Q_D(QRenderAspect); if (d->m_renderer) { // Request the renderer shuts down. In the threaded renderer case, the // Renderer destructor is the synchronization point where we wait for the // thread to join (see below). d->m_renderer->shutdown(); } d->unregisterBackendTypes(); delete d->m_nodeManagers; d->m_nodeManagers = nullptr; // Waits for the render thread to join (if using threaded renderer) delete d->m_renderer; d->m_renderer = nullptr; // Queue the offscreen surface helper for deletion on the main thread. // That will take care of deleting the offscreen surface itself. d->m_offscreenHelper->deleteLater(); d->m_offscreenHelper = nullptr; } QVector QRenderAspectPrivate::createGeometryRendererJobs() { Render::GeometryRendererManager *geomRendererManager = m_nodeManagers->geometryRendererManager(); const QVector dirtyGeometryRenderers = geomRendererManager->dirtyGeometryRenderers(); QVector dirtyGeometryRendererJobs; dirtyGeometryRendererJobs.reserve(dirtyGeometryRenderers.size()); for (const QNodeId geoRendererId : dirtyGeometryRenderers) { Render::HGeometryRenderer geometryRendererHandle = geomRendererManager->lookupHandle(geoRendererId); if (!geometryRendererHandle.isNull()) { auto job = Render::LoadGeometryJobPtr::create(geometryRendererHandle); job->setNodeManagers(m_nodeManagers); dirtyGeometryRendererJobs.push_back(job); } } return dirtyGeometryRendererJobs; } void QRenderAspectPrivate::loadSceneParsers() { const QStringList keys = QSceneImportFactory::keys(); for (const QString &key : keys) { QSceneImporter *sceneIOHandler = QSceneImportFactory::create(key, QStringList()); if (sceneIOHandler != nullptr) m_sceneImporter.append(sceneIOHandler); } } void QRenderAspectPrivate::loadRenderPlugin(const QString &pluginName) { Q_Q(QRenderAspect); const QStringList keys = Render::QRenderPluginFactory::keys(); if (!keys.contains(pluginName)) return; if (m_pluginConfig.contains(pluginName) && !m_loadedPlugins.contains(pluginName)) { Render::QRenderPlugin *plugin = Render::QRenderPluginFactory::create(pluginName, QStringList()); if (plugin != nullptr) { m_loadedPlugins.append(pluginName); m_renderPlugins.append(plugin); plugin->registerBackendTypes(q, m_renderer); } } } QVector QRenderAspectPrivate::m_pluginConfig; QMutex QRenderAspectPrivate::m_pluginLock; QVector QRenderAspectPrivate::m_instances; void QRenderAspectPrivate::configurePlugin(const QString &plugin) { QMutexLocker lock(&m_pluginLock); if (!m_pluginConfig.contains(plugin)) { m_pluginConfig.append(plugin); for (QRenderAspectPrivate *instance : qAsConst(m_instances)) instance->loadRenderPlugin(plugin); } } } // namespace Qt3DRender QT_END_NAMESPACE QT3D_REGISTER_NAMESPACED_ASPECT("render", QT_PREPEND_NAMESPACE(Qt3DRender), QRenderAspect)