summaryrefslogtreecommitdiffstats
path: root/src/quick3d/imports/scene3d/scene3drenderer.cpp
diff options
context:
space:
mode:
authorSean Harmer <sean.harmer@kdab.com>2016-01-18 18:32:22 +0000
committerSean Harmer <sean.harmer@kdab.com>2016-01-18 23:28:50 +0000
commit550a9173f56c2c4a536f2003c232fb5f2b04a158 (patch)
tree4355e86a9b5b037a0b6294c169bbf311f6bc29c1 /src/quick3d/imports/scene3d/scene3drenderer.cpp
parentce8e5ed4a6b2c4b1e1b26a33b74e92c3edc8bfd1 (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.cpp294
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