/**************************************************************************** ** ** 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 #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 using namespace Qt3DCore; namespace Qt3DRender { QRenderAspectPrivate::QRenderAspectPrivate(QRenderAspect::RenderType type) : QAbstractAspectPrivate() , m_nodeManagers(new Render::NodeManagers()) , m_renderer(Q_NULLPTR) , m_surfaceEventFilter(new Render::PlatformSurfaceFilter()) , m_surface(Q_NULLPTR) , m_initialized(false) , m_framePreparationJob(new Render::FramePreparationJob(m_nodeManagers)) , m_cleanupJob(new Render::FrameCleanupJob(m_nodeManagers)) , m_worldTransformJob(new Render::UpdateWorldTransformJob()) , m_updateBoundingVolumeJob(new Render::UpdateBoundingVolumeJob()) , m_calculateBoundingVolumeJob(new Render::CalculateBoundingVolumeJob(m_nodeManagers)) { initResources(); // Load the scene parsers loadSceneParsers(); // Create jobs to update transforms and bounding volumes // We can only update bounding volumes once all world transforms are known m_updateBoundingVolumeJob->addDependency(m_worldTransformJob); m_framePreparationJob->addDependency(m_worldTransformJob); // All world stuff depends on the RenderEntity's localBoundingVolume m_worldTransformJob->addDependency(m_calculateBoundingVolumeJob); // Create property renderer implementation given // a targeted rendering API -> only OpenGL for now m_renderer = new Render::Renderer(type); m_renderer->setNodeManagers(m_nodeManagers); m_surfaceEventFilter->setRenderer(m_renderer); } 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), 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), 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_nodeManagers))); registerBackendType(QBackendNodeFunctorPtr(new Render::NodeFunctor(d->m_nodeManagers->transformManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::NodeFunctor(d->m_nodeManagers->materialManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::NodeFunctor(d->m_nodeManagers->techniqueManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::TextureFunctor(d->m_nodeManagers->textureManager(), d->m_nodeManagers->textureImageManager(), d->m_nodeManagers->textureDataManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::NodeFunctor(d->m_nodeManagers->shaderManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::NodeFunctor(d->m_nodeManagers->effectManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::NodeFunctor(d->m_nodeManagers->criterionManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::NodeFunctor(d->m_nodeManagers->cameraManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::NodeFunctor(d->m_nodeManagers->layerManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::NodeFunctor(d->m_nodeManagers->renderPassManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::RenderSceneFunctor(d->m_nodeManagers->sceneManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::NodeFunctor(d->m_nodeManagers->renderTargetManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::NodeFunctor(d->m_nodeManagers->attachmentManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::NodeFunctor(d->m_nodeManagers->sortCriterionManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::FrameGraphNodeFunctor(d->m_nodeManagers->frameGraphManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::FrameGraphNodeFunctor(d->m_nodeManagers->frameGraphManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::FrameGraphNodeFunctor(d->m_nodeManagers->frameGraphManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::FrameGraphNodeFunctor(d->m_nodeManagers->frameGraphManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::FrameGraphNodeFunctor(d->m_nodeManagers->frameGraphManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::FrameGraphNodeFunctor(d->m_nodeManagers->frameGraphManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::FrameGraphNodeFunctor(d->m_nodeManagers->frameGraphManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::FrameGraphNodeFunctor(d->m_nodeManagers->frameGraphManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::FrameGraphNodeFunctor(d->m_nodeManagers->frameGraphManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::FrameGraphComponentFunctor(d->m_renderer))); registerBackendType(QBackendNodeFunctorPtr(new Render::NodeFunctor(d->m_nodeManagers->parameterManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::RenderShaderDataFunctor(d->m_nodeManagers))); registerBackendType(QBackendNodeFunctorPtr(new Render::TextureImageFunctor(d->m_nodeManagers->textureManager(), d->m_nodeManagers->textureImageManager(), d->m_nodeManagers->textureDataManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::FrameGraphNodeFunctor(d->m_nodeManagers->frameGraphManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::FrameGraphNodeFunctor(d->m_nodeManagers->frameGraphManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::BufferFunctor(d->m_nodeManagers->bufferManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::NodeFunctor(d->m_nodeManagers->attributeManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::NodeFunctor(d->m_nodeManagers->geometryManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::GeometryRendererFunctor(d->m_nodeManagers->geometryRendererManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::NodeFunctor(d->m_nodeManagers->objectPickerManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::NodeFunctor(d->m_nodeManagers->boundingVolumeDebugManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::FrameGraphNodeFunctor(d->m_nodeManagers->frameGraphManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::RenderLightFunctor(d->m_nodeManagers))); registerBackendType(QBackendNodeFunctorPtr(new Render::FrameGraphNodeFunctor(d->m_nodeManagers->frameGraphManager()))); registerBackendType(QBackendNodeFunctorPtr(new Render::FrameGraphNodeFunctor(d->m_nodeManagers->frameGraphManager()))); } void QRenderAspect::renderInitialize(QOpenGLContext *context) { Q_D(QRenderAspect); if (d->m_renderer->api() == Render::AbstractRenderer::OpenGL) static_cast(d->m_renderer)->setOpenGLContext(context); d->m_renderer->initialize(); } void QRenderAspect::renderSynchronous() { Q_D(QRenderAspect); d->m_renderer->doRender(); } /*! * Only called when rendering with QtQuick 2 and a Scene3D item */ void QRenderAspect::renderShutdown() { Q_D(QRenderAspect); d->m_renderer->shutdown(); } QVector QRenderAspect::jobsToExecute(qint64 time) { Q_D(QRenderAspect); d->m_renderer->setTime(time); // Create jobs that will get exectued by the threadpool QVector jobs; // 1 LoadBufferJobs, GeometryJobs, SceneLoaderJobs // 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) // Create jobs to load in any meshes that are pending if (d->m_renderer != Q_NULLPTR && d->m_renderer->isRunning()) { Render::NodeManagers *manager = d->m_renderer->nodeManagers(); QAspectJobPtr pickBoundingVolumeJob = d->m_renderer->pickBoundingVolumeJob(); // Create the jobs to build the frame d->m_framePreparationJob->setRoot(d->m_renderer->sceneRoot()); d->m_worldTransformJob->setRoot(d->m_renderer->sceneRoot()); d->m_updateBoundingVolumeJob->setRoot(d->m_renderer->sceneRoot()); d->m_calculateBoundingVolumeJob->setRoot(d->m_renderer->sceneRoot()); d->m_cleanupJob->setRoot(d->m_renderer->sceneRoot()); const QVector texturesPending = manager->textureDataManager()->texturesPending(); Q_FOREACH (const QNodeId &textureId, texturesPending) { Render::LoadTextureDataJobPtr loadTextureJob(new Render::LoadTextureDataJob(textureId)); loadTextureJob->setNodeManagers(manager); 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) const QVector sceneJobs = manager->sceneManager()->pendingSceneLoaderJobs(); Q_FOREACH (Render::LoadSceneJobPtr job, sceneJobs) { job->setNodeManagers(d->m_nodeManagers); job->setSceneParsers(d->m_sceneParsers); jobs.append(job); } // Clear any previous temporary dependency d->m_calculateBoundingVolumeJob->removeDependency(QWeakPointer()); const QVector bufferJobs = createRenderBufferJobs(); Q_FOREACH (const QAspectJobPtr bufferJob, bufferJobs) d->m_calculateBoundingVolumeJob->addDependency(bufferJob); jobs.append(bufferJobs); const QVector geometryJobs = createGeometryRendererJobs(); jobs.append(geometryJobs); const QVector geometryRendererTriangleUpdates = manager->geometryRendererManager()->geometryRenderersRequiringTriangleDataRefresh(); Q_FOREACH (const QNodeId geomRendererId, geometryRendererTriangleUpdates) { Render::CalcGeometryTriangleVolumesPtr triangleComputeJob(new Render::CalcGeometryTriangleVolumes(geomRendererId, manager)); triangleComputeJob->addDependency(d->m_framePreparationJob); pickBoundingVolumeJob->addDependency(triangleComputeJob); jobs.append(triangleComputeJob); } // Only add dependency if not already present const QVector > dependencies = pickBoundingVolumeJob->dependencies(); if (std::find(dependencies.begin(), dependencies.end(), d->m_updateBoundingVolumeJob) == dependencies.end()) pickBoundingVolumeJob->addDependency(d->m_updateBoundingVolumeJob); // Add all jobs to queue jobs.append(d->m_calculateBoundingVolumeJob); jobs.append(d->m_worldTransformJob); jobs.append(d->m_updateBoundingVolumeJob); jobs.append(d->m_framePreparationJob); jobs.append(pickBoundingVolumeJob); // Clear any old dependencies from previous frames d->m_cleanupJob->removeDependency(QWeakPointer()); // Do not create any more RenderView jobs when the platform surface is gone. if (d->m_renderer->surface()) { // Traverse the current framegraph and create jobs to populate // RenderBins with RenderCommands QVector renderBinJobs = d->m_renderer->renderBinJobs(); // TODO: Add wrapper around ThreadWeaver::Collection for (int i = 0; i < renderBinJobs.size(); ++i) { QAspectJobPtr renderBinJob = renderBinJobs.at(i); renderBinJob->addDependency(d->m_updateBoundingVolumeJob); jobs.append(renderBinJob); d->m_cleanupJob->addDependency(renderBinJob); } } jobs.append(d->m_cleanupJob); } return jobs; } void QRenderAspect::onRootEntityChanged(Qt3DCore::QEntity *rootEntity) { Q_D(QRenderAspect); d->m_renderer->setSceneRoot(d, d->m_renderer->nodeManagers()->lookupResource(rootEntity->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. Q_D(QRenderAspect); 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); } d->m_renderer->setServices(d->services()); d->m_renderer->createAllocators(d->jobManager()); 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); if (d->m_aspectManager) d->m_renderer->registerEventFilter(d->services()->eventFilterService()); } void QRenderAspect::onCleanup() { Q_D(QRenderAspect); if (d->m_renderer) d->m_renderer->destroyAllocators(d->jobManager()); delete d->m_renderer; d->m_renderer = Q_NULLPTR; } // Returns a vector of jobs to be performed for dirty buffers // 1 dirty buffer == 1 job, all job can be performed in parallel QVector QRenderAspect::createRenderBufferJobs() { Q_D(QRenderAspect); const QVector dirtyBuffers = d->m_nodeManagers->bufferManager()->dirtyBuffers(); QVector dirtyBuffersJobs; Q_FOREACH (const QNodeId &bufId, dirtyBuffers) { Render::HBuffer bufferHandle = d->m_nodeManagers->lookupHandle(bufId); if (!bufferHandle.isNull()) { // Create new buffer job Render::LoadBufferJobPtr job(new Render::LoadBufferJob(bufferHandle)); job->setNodeManager(d->m_nodeManagers); dirtyBuffersJobs.push_back(job); } } return dirtyBuffersJobs; } QVector QRenderAspect::createGeometryRendererJobs() { Q_D(QRenderAspect); Render::GeometryRendererManager *geomRendererManager = d->m_nodeManagers->geometryRendererManager(); const QVector dirtyGeometryRenderers = geomRendererManager->dirtyGeometryRenderers(); QVector dirtyGeometryRendererJobs; Q_FOREACH (const QNodeId &geoRendererId, dirtyGeometryRenderers) { Render::HGeometryRenderer geometryRendererHandle = geomRendererManager->lookupHandle(geoRendererId); if (!geometryRendererHandle.isNull()) { Render::LoadGeometryJobPtr job(new Render::LoadGeometryJob(geometryRendererHandle)); job->setNodeManagers(d->m_nodeManagers); dirtyGeometryRendererJobs.push_back(job); } } return dirtyGeometryRendererJobs; } void QRenderAspectPrivate::loadSceneParsers() { QStringList keys = QSceneParserFactory::keys(); Q_FOREACH (QString key, keys) { QAbstractSceneParser *sceneParser = QSceneParserFactory::create(key, QStringList()); if (sceneParser != Q_NULLPTR) m_sceneParsers.append(sceneParser); } } } // namespace Qt3DRender QT_END_NAMESPACE QT3D_REGISTER_NAMESPACED_ASPECT("render", QT_PREPEND_NAMESPACE(Qt3DRender), QRenderAspect)