diff options
author | Sean Harmer <sean.harmer@kdab.com> | 2016-01-18 18:32:22 +0000 |
---|---|---|
committer | Sean Harmer <sean.harmer@kdab.com> | 2016-01-18 23:28:50 +0000 |
commit | 550a9173f56c2c4a536f2003c232fb5f2b04a158 (patch) | |
tree | 4355e86a9b5b037a0b6294c169bbf311f6bc29c1 /src/quick3d/imports/scene3d/scene3drenderer.cpp | |
parent | ce8e5ed4a6b2c4b1e1b26a33b74e92c3edc8bfd1 (diff) |
Refactor the scene3d classes into one class per pair of files
No functional changes.
Change-Id: I5895e3bcaf65fb7524e6adb10bca1f8c6fdb6c44
Reviewed-by: Paul Lemire <paul.lemire@kdab.com>
Diffstat (limited to 'src/quick3d/imports/scene3d/scene3drenderer.cpp')
-rw-r--r-- | src/quick3d/imports/scene3d/scene3drenderer.cpp | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/src/quick3d/imports/scene3d/scene3drenderer.cpp b/src/quick3d/imports/scene3d/scene3drenderer.cpp new file mode 100644 index 000000000..6d69e72d6 --- /dev/null +++ b/src/quick3d/imports/scene3d/scene3drenderer.cpp @@ -0,0 +1,294 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "scene3drenderer_p.h" +#include "scene3dcleaner_p.h" +#include "scene3ditem_p.h" +#include "scene3dlogging_p.h" +#include "scene3dsgnode_p.h" + +#include <Qt3DRender/qrenderaspect.h> +#include <Qt3DCore/qaspectengine.h> + +#include <QtQuick/qquickwindow.h> + +#include <QtGui/qopenglcontext.h> +#include <QtGui/qopenglframebufferobject.h> + +#include <QtCore/qthread.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +class ContextSaver +{ +public: + explicit ContextSaver(QOpenGLContext *context = QOpenGLContext::currentContext()) + : m_context(context), + m_surface(context ? context->surface() : Q_NULLPTR) + { + } + + ~ContextSaver() + { + if (m_context) + m_context->makeCurrent(m_surface); + } + + QOpenGLContext *context() const { return m_context; } + QSurface *surface() const { return m_surface; } + +private: + QOpenGLContext * const m_context; + QSurface * const m_surface; +}; + +/*! + \class Qt3DCore::Scene3DRenderer + \internal + + \brief The Qt3DCore::Scene3DRenderer class takes care of rendering a Qt3D scene + within a Framebuffer object to be used by the QtQuick 2 renderer. + + The Qt3DCore::Scene3DRenderer class renders a Qt3D scene as provided by a Qt3DCore::Scene3DItem. + It owns the aspectEngine even though it doesn't instantiate it. + + The shutdown procedure is a two steps process that goes as follow: + + \li The window is closed + + \li This triggers the windowsChanged signal which the Scene3DRenderer + uses to perform the necessary cleanups in the QSGRenderThread (destroys + DebugLogger ...) with the shutdown slot (queued connection). + + \li The destroyed signal of the window is also connected to the + Scene3DRenderer. When triggered in the context of the main thread, the + cleanup slot is called. + + There is an alternate shutdown procedure in case the QQuickItem is + destroyed with an active window which can happen in the case where the + Scene3D is used with a QtQuick Loader + + In that case the shutdown procedure goes the same except that the destroyed + signal of the window is not called. Therefore the cleanup method is invoked + to properly destroy the aspect engine. + */ +Scene3DRenderer::Scene3DRenderer(Scene3DItem *item, Qt3DCore::QAspectEngine *aspectEngine, QRenderAspect *renderAspect) + : QObject() + , m_item(item) + , m_aspectEngine(aspectEngine) + , m_renderAspect(renderAspect) + , m_multisampledFBO(Q_NULLPTR) + , m_finalFBO(Q_NULLPTR) + , m_texture(Q_NULLPTR) + , m_multisample(false) // this value is not used, will be synced from the Scene3DItem instead + , m_lastMultisample(false) +{ + Q_CHECK_PTR(m_item); + Q_CHECK_PTR(m_item->window()); + + QObject::connect(m_item->window(), &QQuickWindow::beforeRendering, this, &Scene3DRenderer::render, Qt::DirectConnection); + QObject::connect(m_item, &QQuickItem::windowChanged, this, &Scene3DRenderer::onWindowChangedQueued, Qt::QueuedConnection); + + ContextSaver saver; + + QVariantMap data; + data.insert(QStringLiteral("surface"), QVariant::fromValue(saver.surface())); + m_aspectEngine->setData(data); + + m_renderAspect->renderInitialize(saver.context()); + scheduleRootEntityChange(); +} + +Scene3DRenderer::~Scene3DRenderer() +{ + qCDebug(Scene3D) << Q_FUNC_INFO << QThread::currentThread(); +} + +QOpenGLFramebufferObject *Scene3DRenderer::createMultisampledFramebufferObject(const QSize &size) +{ + QOpenGLFramebufferObjectFormat format; + format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); + format.setSamples(4); + return new QOpenGLFramebufferObject(size, format); +} + +QOpenGLFramebufferObject *Scene3DRenderer::createFramebufferObject(const QSize &size) +{ + QOpenGLFramebufferObjectFormat format; + format.setAttachment(QOpenGLFramebufferObject::Depth); + return new QOpenGLFramebufferObject(size, format); +} + +void Scene3DRenderer::scheduleRootEntityChange() +{ + QMetaObject::invokeMethod(m_item, "applyRootEntityChange", Qt::QueuedConnection); +} + +void Scene3DRenderer::setCleanerHelper(Scene3DCleaner *cleaner) +{ + m_cleaner = cleaner; + if (m_cleaner) { + // Window closed case + QObject::connect(m_item->window(), &QQuickWindow::destroyed, m_cleaner, &Scene3DCleaner::cleanup); + m_cleaner->setRenderer(this); + } +} + +// Executed in the QtQuick render thread. +void Scene3DRenderer::shutdown() +{ + qCDebug(Scene3D) << Q_FUNC_INFO << QThread::currentThread(); + + // Set to null so that subsequent calls to render + // would return early + m_item = Q_NULLPTR; + + // Shutdown the Renderer Aspect while the OpenGL context + // is still valid + if (m_renderAspect) + m_renderAspect->renderShutdown(); +} + +// SGThread +void Scene3DRenderer::onWindowChangedQueued(QQuickWindow *w) +{ + if (w == Q_NULLPTR) { + qCDebug(Scene3D) << Q_FUNC_INFO << QThread::currentThread(); + shutdown(); + // Will only trigger something with the Loader case + // The window closed cases is handled by the window's destroyed + // signal + QMetaObject::invokeMethod(m_cleaner, "cleanup"); + } +} + +void Scene3DRenderer::synchronize() +{ + m_multisample = m_item->multisample(); +} + +void Scene3DRenderer::setSGNode(Scene3DSGNode *node) Q_DECL_NOEXCEPT +{ + m_node = node; + if (!m_texture.isNull()) + node->setTexture(m_texture.data()); +} + +void Scene3DRenderer::render() +{ + if (!m_item || !m_item->window()) + return; + + QQuickWindow *window = m_item->window(); + + if (m_aspectEngine->rootEntity() != m_item->entity()) + scheduleRootEntityChange(); + + ContextSaver saver; + + const QSize currentSize = m_item->boundingRect().size().toSize() * window->effectiveDevicePixelRatio(); + const bool forceRecreate = currentSize != m_lastSize || m_multisample != m_lastMultisample; + + // Rebuild FBO and textures if never created or a resize has occurred + if ((m_multisampledFBO.isNull() || forceRecreate) && m_multisample) { + qCDebug(Scene3D) << Q_FUNC_INFO << "Creating multisample framebuffer"; + m_multisampledFBO.reset(createMultisampledFramebufferObject(currentSize)); + if (m_multisampledFBO->format().samples() == 0 || !QOpenGLFramebufferObject::hasOpenGLFramebufferBlit()) { + qCDebug(Scene3D) << Q_FUNC_INFO << "Failed to create multisample framebuffer"; + m_multisample = false; + m_multisampledFBO.reset(Q_NULLPTR); + } + } + + if (m_finalFBO.isNull() || forceRecreate) { + m_finalFBO.reset(createFramebufferObject(currentSize)); + m_texture.reset(window->createTextureFromId(m_finalFBO->texture(), m_finalFBO->size(), QQuickWindow::TextureHasAlphaChannel)); + m_node->setTexture(m_texture.data()); + } + + // Store the current size as a comparison + // point for the next frame + m_lastSize = currentSize; + m_lastMultisample = m_multisample; + + //Only try to use MSAA when available + if (m_multisample) { + // Bind FBO + m_multisampledFBO->bind(); + + // Render Qt3D Scene + m_renderAspect->renderSynchronous(); + + // We may have called doneCurrent() so restore the context. + if (saver.context()->surface() != saver.surface()) + saver.context()->makeCurrent(saver.surface()); + + // Blit multisampled FBO with non multisampled FBO with texture attachment + QOpenGLFramebufferObject::blitFramebuffer(m_finalFBO.data(), m_multisampledFBO.data()); + + // Restore QtQuick FBO + m_multisampledFBO->bindDefault(); + } else { + // Bind FBO + m_finalFBO->bind(); + + // Render Qt3D Scene + m_renderAspect->renderSynchronous(); + + // We may have called doneCurrent() so restore the context. + if (saver.context()->surface() != saver.surface()) + saver.context()->makeCurrent(saver.surface()); + + // Restore QtQuick FBO + m_finalFBO->bindDefault(); + } + + // Reset the state used by the Qt Quick scenegraph to avoid any + // interference when rendering the rest of the UI. + window->resetOpenGLState(); + + // Mark material as dirty to request a new frame + m_node->markDirty(QSGNode::DirtyMaterial); + + // Request next frame + window->update(); +} + +} // namespace Qt3DRender + +QT_END_NAMESPACE |