summaryrefslogtreecommitdiffstats
path: root/src/quick3d/quick3drender
diff options
context:
space:
mode:
authorAntti Määttä <antti.maatta@qt.io>2016-10-21 13:24:59 +0300
committerAntti Määttä <antti.maatta@qt.io>2017-01-24 14:55:03 +0000
commitcea496ca35625d96d11185293a07ce600c3ff369 (patch)
tree62692da9386877661c55ccf2e4071bacfa3c8be4 /src/quick3d/quick3drender
parent1e0913cc267cea8da114ea1b903c4f3a0c675c5c (diff)
Move scene2d to quick3drender
Change-Id: Id3decbf809e69fe0957a168b3675b83933670641 Reviewed-by: Sean Harmer <sean.harmer@kdab.com>
Diffstat (limited to 'src/quick3d/quick3drender')
-rw-r--r--src/quick3d/quick3drender/scene2d/qscene2d.cpp621
-rw-r--r--src/quick3d/quick3drender/scene2d/qscene2d.h96
-rw-r--r--src/quick3d/quick3drender/scene2d/qscene2d_p.h211
-rw-r--r--src/quick3d/quick3drender/scene2d/scene2d.cpp303
-rw-r--r--src/quick3d/quick3drender/scene2d/scene2d_p.h115
5 files changed, 1346 insertions, 0 deletions
diff --git a/src/quick3d/quick3drender/scene2d/qscene2d.cpp b/src/quick3d/quick3drender/scene2d/qscene2d.cpp
new file mode 100644
index 000000000..62e766684
--- /dev/null
+++ b/src/quick3d/quick3drender/scene2d/qscene2d.cpp
@@ -0,0 +1,621 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** 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 "qscene2d.h"
+#include "qscene2d_p.h"
+
+#include <Qt3DCore/QPropertyUpdatedChange>
+
+#include <QtGui/QOpenGLContext>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt3DCore;
+
+namespace Qt3DRender {
+
+
+/*!
+ \class Qt3DRender::QScene2D
+ \inmodule Qt3DRender
+
+ \brief This class enables rendering qml into a texture, which then can be
+ used as a part of 3D scene.
+
+ The component uses QQuickRenderControl to render the given QML source into an
+ offscreen surface, which is attached to a texture provided by the user. This allows the
+ component to directly render into the texture without intermediate copy and the user to
+ freely specify how the texture is used in the 3D scene.
+
+ \since 5.9
+*/
+
+/*!
+ \qmltype Scene2D
+ \inqmlmodule Qt3D.Render
+ \since
+ \ingroup
+ \instantiates Qt3DRender::QScene2D
+ \brief Scene2D
+ *
+ */
+
+/*!
+ \qmlproperty Qt3DRender::QAbstractTexture Qt3D.Render::Scene2D::texture
+ Holds the texture being rendered to.
+ */
+
+/*!
+ \qmlproperty QUrl Qt3D.Render::Scene2D::source
+ Holds the qml source url.
+ */
+
+/*!
+ \qmlproperty bool Qt3D.Render::Scene2D::renderOnce
+ Holds whether the first rendered image to the texture is also the last, after which the
+ renderer releases resources needed for the rendering and the rendering is no longer possible
+ with this Scene2D object.
+ */
+
+/*!
+ \qmlproperty bool Qt3D.Render::Scene2D::loaded
+ Holds whether the source has been loaded.
+ */
+
+class RenderControl : public QQuickRenderControl
+{
+public:
+ RenderControl(QWindow *w) : m_window(w) { }
+ QWindow *renderWindow(QPoint *offset) Q_DECL_OVERRIDE;
+
+private:
+ QWindow *m_window;
+};
+
+QWindow *RenderControl::renderWindow(QPoint *offset)
+{
+ if (offset)
+ *offset = QPoint(0, 0);
+ return m_window;
+}
+
+/*!
+ \internal
+ Constructs object shared by the front-end and back-end to synchronize the rendering.
+ */
+Scene2DSharedObject::Scene2DSharedObject(Scene2DManager *manager)
+ : m_quit(false)
+ , m_requestSync(false)
+ , m_prepared(false)
+ , m_initialized(false)
+ , m_renderControl(nullptr)
+ , m_quickWindow(nullptr)
+ , m_renderManager(manager)
+ , m_surface(nullptr)
+ , m_renderObject(nullptr)
+ , m_disallowed(false)
+{
+}
+
+Scene2DSharedObject::~Scene2DSharedObject()
+{
+}
+
+void Scene2DSharedObject::cleanup()
+{
+ delete m_renderControl;
+ delete m_quickWindow;
+ delete m_surface;
+ m_renderControl = nullptr;
+ m_quickWindow = nullptr;
+ m_surface = nullptr;
+ m_initialized = false;
+}
+
+bool Scene2DSharedObject::canRender() const
+{
+ return m_initialized && m_prepared && !m_disallowed;
+}
+
+bool Scene2DSharedObject::isInitialized() const
+{
+ return m_initialized;
+}
+
+void Scene2DSharedObject::disallowRender()
+{
+ m_disallowed = true;
+}
+
+void Scene2DSharedObject::setInitialized()
+{
+ m_initialized = true;
+}
+
+bool Scene2DSharedObject::isPrepared() const
+{
+ return m_prepared;
+}
+
+void Scene2DSharedObject::setPrepared()
+{
+ m_prepared = true;
+}
+
+// not protected, call only from main thread
+bool Scene2DSharedObject::isQuit() const
+{
+ Q_ASSERT(QThread::currentThread() == QCoreApplication::instance()->thread());
+ return m_quit;
+}
+
+// not protected, call only from main thread
+void Scene2DSharedObject::requestQuit()
+{
+ Q_ASSERT(QThread::currentThread() == QCoreApplication::instance()->thread());
+ m_quit = true;
+ QCoreApplication::postEvent(m_renderObject, new QEvent(QUIT));
+}
+
+bool Scene2DSharedObject::isSyncRequested() const
+{
+ return m_requestSync;
+}
+
+void Scene2DSharedObject::requestRender(bool sync)
+{
+ m_requestSync = sync;
+ QCoreApplication::postEvent(m_renderObject, new QEvent(RENDER));
+}
+
+void Scene2DSharedObject::waitRender()
+{
+ m_cond.wait(&m_mutex);
+}
+
+void Scene2DSharedObject::wakeWaiting()
+{
+ m_cond.wakeOne();
+}
+
+void Scene2DSharedObject::clearSyncRequest()
+{
+ m_requestSync = false;
+}
+
+/*!
+ \internal
+ Constructs qml render manager.
+ */
+Scene2DManager::Scene2DManager(QScene2DPrivate *priv)
+ : m_priv(priv)
+ , m_qmlEngine(nullptr)
+ , m_qmlComponent(nullptr)
+ , m_rootItem(nullptr)
+ , m_source(nullptr)
+ , m_texture(nullptr)
+ , m_requested(false)
+ , m_initialized(false)
+ , m_renderSyncRequested(false)
+ , m_sharedObject(new Scene2DSharedObject(this))
+ , m_renderOnce(false)
+ , m_backendInitialized(false)
+{
+ setFormat(QSurfaceFormat::defaultFormat());
+
+ m_sharedObject->m_surface = new QOffscreenSurface;
+ m_sharedObject->m_surface->setFormat(QSurfaceFormat::defaultFormat());
+ m_sharedObject->m_surface->create();
+
+ // Create render control
+ m_sharedObject->m_renderControl = new RenderControl(this);
+
+ // Create window to render the QML with
+ m_sharedObject->m_quickWindow = new QQuickWindow(m_sharedObject->m_renderControl);
+ m_sharedObject->m_quickWindow->setClearBeforeRendering(true);
+ m_sharedObject->m_quickWindow->setDefaultAlphaBuffer(true);
+
+ // Create a QML engine.
+ m_qmlEngine = new QQmlEngine;
+ if (!m_qmlEngine->incubationController())
+ m_qmlEngine->setIncubationController(m_sharedObject->m_quickWindow->incubationController());
+
+ connect(m_sharedObject->m_renderControl, &QQuickRenderControl::renderRequested,
+ this, &Scene2DManager::requestRender);
+ connect(m_sharedObject->m_renderControl, &QQuickRenderControl::sceneChanged,
+ this, &Scene2DManager::requestRenderSync);
+}
+
+Scene2DManager::~Scene2DManager()
+{
+ m_sharedObject = nullptr;
+}
+
+void Scene2DManager::requestRender()
+{
+ // Don't request render until the backend is initialized.
+ if (m_sharedObject->canRender()) {
+ if (!m_requested) {
+ m_requested = true;
+ QCoreApplication::postEvent(this, new QEvent(RENDER));
+ }
+ }
+}
+
+void Scene2DManager::requestRenderSync()
+{
+ // Don't request render until the backed is initialized.
+ if (m_sharedObject->canRender()) {
+ if (!m_requested) {
+ m_requested = true;
+ QCoreApplication::postEvent(this, new QEvent(RENDERSYNC));
+ }
+ } else {
+ m_renderSyncRequested = true;
+ }
+}
+
+void Scene2DManager::startIfInitialized()
+{
+ if (!m_initialized) {
+ if (m_backendInitialized && m_source.isValid()) {
+ m_qmlComponent = new QQmlComponent(m_qmlEngine, m_source);
+ if (m_qmlComponent->isLoading()) {
+ connect(m_qmlComponent, &QQmlComponent::statusChanged,
+ this, &Scene2DManager::run);
+ } else {
+ run();
+ }
+ }
+ }
+}
+
+void Scene2DManager::stopAndClean()
+{
+ if (m_sharedObject->isInitialized()) {
+ QMutexLocker lock(&m_sharedObject->m_mutex);
+ m_sharedObject->requestQuit();
+ m_sharedObject->m_renderThread->wait();
+ m_sharedObject->cleanup();
+ delete m_qmlEngine;
+ delete m_qmlComponent;
+ m_qmlEngine = nullptr;
+ m_qmlComponent = nullptr;
+ }
+}
+
+void Scene2DManager::run()
+{
+ disconnect(m_qmlComponent, &QQmlComponent::statusChanged, this, &Scene2DManager::run);
+
+ if (m_qmlComponent->isError()) {
+ QList<QQmlError> errorList = m_qmlComponent->errors();
+ for (const QQmlError &error: errorList)
+ qWarning() << error.url() << error.line() << error;
+ return;
+ }
+
+ QObject *rootObject = m_qmlComponent->create();
+ if (m_qmlComponent->isError()) {
+ QList<QQmlError> errorList = m_qmlComponent->errors();
+ for (const QQmlError &error: errorList)
+ qWarning() << error.url() << error.line() << error;
+ return;
+ }
+
+ m_rootItem = qobject_cast<QQuickItem *>(rootObject);
+ if (!m_rootItem) {
+ qWarning("QScene2D: Root item is not a QQuickItem.");
+ delete rootObject;
+ return;
+ }
+
+ // The root item is ready. Associate it with the window.
+ m_rootItem->setParentItem(m_sharedObject->m_quickWindow->contentItem());
+
+ // Update window size.
+ updateSizes();
+
+ m_initialized = true;
+ m_sharedObject->setInitialized();
+
+ emit onLoadedChanged();
+}
+
+void Scene2DManager::updateSizes()
+{
+ const int width = m_rootItem->width();
+ const int height = m_rootItem->height();
+ if (width == 0 || height == 0) {
+ qWarning() << "QScene2D: Root item size not set.";
+ return;
+ }
+ resize(width, height);
+ m_sharedObject->m_quickWindow->setGeometry(0, 0, width, height);
+}
+
+void Scene2DManager::setTexture(QAbstractTexture *texture)
+{
+ m_texture = texture;
+ startIfInitialized();
+}
+
+void Scene2DManager::setSource(const QUrl &url)
+{
+ m_source = url;
+ startIfInitialized();
+}
+
+bool Scene2DManager::event(QEvent *e)
+{
+ switch (e->type()) {
+
+ case RENDER: {
+ // just render request, don't need to call sync in render thread
+ QMutexLocker lock(&m_sharedObject->m_mutex);
+ m_sharedObject->requestRender(false);
+
+ Qt3DCore::QPropertyUpdatedChangePtr change(new Qt3DCore::QPropertyUpdatedChange(m_priv->m_id));
+ change->setPropertyName("dirty");
+ change->setValue(QVariant::fromValue(true));
+ m_priv->notifyObservers(change);
+
+ m_requested = false;
+ return true;
+ }
+
+ case RENDERSYNC: {
+ // sync and render request, main and render threads must be synchronized
+ if (!m_sharedObject->isQuit())
+ doRenderSync();
+ m_requested = false;
+ return true;
+ }
+
+ case PREPARE: {
+ m_sharedObject->m_renderControl->prepareThread(m_sharedObject->m_renderThread);
+ m_sharedObject->setPrepared();
+
+ if (m_renderSyncRequested) {
+ if (!m_requested) {
+ m_requested = true;
+ QCoreApplication::postEvent(this, new QEvent(RENDERSYNC));
+ }
+ m_renderSyncRequested = false;
+ }
+ return true;
+ }
+
+ case INITIALIZED: {
+ // backend is initialized, start the qml
+ m_backendInitialized = true;
+ startIfInitialized();
+ return true;
+ }
+
+ case RENDERED: {
+ // render is done, excellent, now clean anything not needed anymore.
+ stopAndClean();
+ return true;
+ }
+
+ default:
+ break;
+ }
+ return QWindow::event(e);
+}
+
+void Scene2DManager::doRenderSync()
+{
+ QMutexLocker lock(&m_sharedObject->m_mutex);
+
+ m_sharedObject->requestRender(true);
+ m_sharedObject->m_renderControl->polishItems();
+
+ Qt3DCore::QPropertyUpdatedChangePtr change(new Qt3DCore::QPropertyUpdatedChange(m_priv->m_id));
+
+ change->setPropertyName("dirty");
+ change->setValue(QVariant::fromValue(true));
+ m_priv->notifyObservers(change);
+
+ // begin waiting render thread
+ m_sharedObject->waitRender();
+ m_requested = false;
+}
+
+void Scene2DManager::cleanup()
+{
+ stopAndClean();
+}
+
+
+QScene2DPrivate::QScene2DPrivate()
+ : QFrameGraphNodePrivate()
+ , m_renderManager(new Scene2DManager(this))
+{
+}
+
+QScene2DPrivate::~QScene2DPrivate()
+{
+ m_renderManager->cleanup();
+ delete m_renderManager;
+}
+
+
+Scene2DSharedObject *QScene2DPrivate::getSharedObject(QScene2D *rqtt)
+{
+ return rqtt->d_func()->m_renderManager->m_sharedObject.data();
+}
+
+
+/*!
+ The constructor creates an instance with the specified \a parent.
+ */
+QScene2D::QScene2D(Qt3DCore::QNode *parent)
+ : QFrameGraphNode(*new QScene2DPrivate, parent)
+{
+ Q_D(QScene2D);
+ connect(d->m_renderManager, &Scene2DManager::onLoadedChanged,
+ this, &QScene2D::sourceLoaded);
+}
+
+/*!
+ Destructor.
+ */
+QScene2D::~QScene2D()
+{
+}
+
+bool QScene2D::loaded() const
+{
+ Q_D(const QScene2D);
+ return d->m_renderManager->m_initialized;
+}
+
+/*!
+ \property QScene2D::source
+ \brief Specifies the url for the qml.
+
+ This property specifies the url to the qml being rendered to the texture.
+ The source must specify QQuickItem as a root. The item must specify width
+ and height. The rendered qml is scaled to the texture size.
+ The property can not be changed after the rendering has been initialized.
+ */
+QUrl QScene2D::source() const
+{
+ Q_D(const QScene2D);
+ return d->m_renderManager->m_source;
+}
+
+void QScene2D::setSource(const QUrl &url)
+{
+ Q_D(QScene2D);
+ if (d->m_renderManager->m_initialized) {
+ qWarning() << "Unable to set source after initialization.";
+ return;
+ }
+ d->m_renderManager->setSource(url);
+ emit sourceChanged(url);
+}
+
+/*!
+ \property QScene2D::renderOnce
+ \brief Property to specify if the texture will be rendered only once.
+
+ This property specifies that the texture will be rendered only one time.
+ Once the rendering has been done, resources reserved for rendering will be
+ released and the QScene2D will become unusable.
+ If set to false, which is the default, the rendering is continuous.
+ */
+bool QScene2D::renderOnce() const
+{
+ Q_D(const QScene2D);
+ return d->m_renderManager->m_renderOnce;
+}
+
+void QScene2D::setRenderOnce(bool once)
+{
+ Q_D(const QScene2D);
+ if (d->m_renderManager->m_renderOnce != once) {
+ d->m_renderManager->m_renderOnce = once;
+ emit renderOnceChanged(once);
+ }
+}
+
+/*!
+ \property QScene2D::texture
+ \brief The texture being rendered to.
+
+ This property specifies the texture being rendered to. Once the texture has been
+ set and the rendering begins, the texture can not be changed anymore.
+ */
+QAbstractTexture *QScene2D::texture() const
+{
+ Q_D(const QScene2D);
+ return d->m_renderManager->m_texture;
+}
+
+void QScene2D::setTexture(QAbstractTexture *texture)
+{
+ Q_D(QScene2D);
+ if (d->m_renderManager->m_initialized) {
+ qWarning() << "Unable to set texture after initialization.";
+ return;
+ }
+ if (d->m_renderManager->m_texture != texture) {
+ if (d->m_renderManager->m_texture)
+ QObject::disconnect(d->m_textureDestroyedConnection);
+ if (texture && !texture->parent())
+ texture->setParent(this);
+ d->m_renderManager->setTexture(texture);
+ if (texture)
+ d->m_textureDestroyedConnection
+ = QObject::connect(texture, &QAbstractTexture::destroyed,
+ this, &QScene2D::textureDestroyed);
+ emit textureChanged(texture);
+ }
+}
+
+void QScene2D::textureDestroyed(QObject *object)
+{
+ Q_D(QScene2D);
+ Q_UNUSED(object);
+ d->m_renderManager->setTexture(nullptr);
+}
+
+Qt3DCore::QNodeCreatedChangeBasePtr QScene2D::createNodeCreationChange() const
+{
+ auto creationChange = Qt3DCore::QNodeCreatedChangePtr<QScene2DData>::create(this);
+ auto &data = creationChange->data;
+ Q_D(const QScene2D);
+ data.renderOnce = d->m_renderManager->m_renderOnce;
+ data.textureId = d->m_renderManager->m_texture
+ ? d->m_renderManager->m_texture->id() : Qt3DCore::QNodeId();
+ data.sharedObject = d->m_renderManager->m_sharedObject;
+ return creationChange;
+}
+
+/*!
+ \internal
+ */
+void QScene2D::sourceLoaded()
+{
+ emit loadedChanged(true);
+}
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/quick3d/quick3drender/scene2d/qscene2d.h b/src/quick3d/quick3drender/scene2d/qscene2d.h
new file mode 100644
index 000000000..18e65a5ca
--- /dev/null
+++ b/src/quick3d/quick3drender/scene2d/qscene2d.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_QSCENE2D_H
+#define QT3DRENDER_QSCENE2D_H
+
+#include <QtCore/QUrl>
+#include <QtCore/QEvent>
+
+#include <Qt3DRender/qframegraphnode.h>
+#include <Qt3DRender/qabstracttexture.h>
+#include <Qt3DRender/qt3drender_global.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+class QScene2DPrivate;
+
+class QT3DRENDERSHARED_EXPORT QScene2D : public Qt3DRender::QFrameGraphNode
+{
+ Q_OBJECT
+
+ Q_PROPERTY(Qt3DRender::QAbstractTexture *texture READ texture WRITE setTexture NOTIFY textureChanged)
+ Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
+ Q_PROPERTY(bool renderOnce READ renderOnce WRITE setRenderOnce NOTIFY renderOnceChanged)
+ Q_PROPERTY(bool loaded READ loaded NOTIFY loadedChanged)
+
+public:
+ explicit QScene2D(Qt3DCore::QNode *parent = nullptr);
+ ~QScene2D();
+
+ QUrl source() const;
+ QAbstractTexture *texture() const;
+ bool loaded() const;
+ bool renderOnce() const;
+
+public Q_SLOTS:
+ void setSource(const QUrl &url);
+ void setTexture(QAbstractTexture *texture);
+ void setRenderOnce(bool once);
+
+Q_SIGNALS:
+ void sourceChanged(const QUrl &url);
+ void textureChanged(QAbstractTexture *texture);
+ void loadedChanged(bool loaded);
+ void renderOnceChanged(bool once);
+
+protected:
+ Q_DECLARE_PRIVATE(QScene2D)
+
+private:
+ Qt3DCore::QNodeCreatedChangeBasePtr createNodeCreationChange() const Q_DECL_OVERRIDE;
+ void textureDestroyed(QObject *object);
+
+ void sourceLoaded();
+};
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_QSCENE2D_H
diff --git a/src/quick3d/quick3drender/scene2d/qscene2d_p.h b/src/quick3d/quick3drender/scene2d/qscene2d_p.h
new file mode 100644
index 000000000..1020c1264
--- /dev/null
+++ b/src/quick3d/quick3drender/scene2d/qscene2d_p.h
@@ -0,0 +1,211 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_QSCENE2D_P_H
+#define QT3DRENDER_QSCENE2D_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qobject_p.h>
+#include <private/qframegraphnode_p.h>
+
+#include <Qt3DRender/qscene2d.h>
+#include <Qt3DRender/QAbstractTexture>
+
+#include <QtQml/QQmlEngine>
+#include <QtQml/QQmlComponent>
+#include <QtQuick/QQuickItem>
+#include <QtQuick/QQuickWindow>
+#include <QtQuick/QQuickRenderControl>
+#include <QtGui/QOffscreenSurface>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QWaitCondition>
+#include <QtCore/QThread>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+class QScene2D;
+class Scene2DManager;
+
+// render thread -> render thread
+static const QEvent::Type INITIALIZE = QEvent::Type(QEvent::User + 1);
+
+// main thread -> main thread, render thread
+static const QEvent::Type RENDER = QEvent::Type(QEvent::User + 2);
+
+// main thread -> main thread
+static const QEvent::Type RENDERSYNC = QEvent::Type(QEvent::User + 3);
+
+// render thread -> main thread
+static const QEvent::Type PREPARE = QEvent::Type(QEvent::User + 4);
+static const QEvent::Type INITIALIZED = QEvent::Type(QEvent::User + 5);
+static const QEvent::Type RENDERED = QEvent::Type(QEvent::User + 6);
+
+// main thread -> render thread
+static const QEvent::Type QUIT = QEvent::Type(QEvent::User + 7);
+
+class Q_AUTOTEST_EXPORT Scene2DSharedObject
+{
+public:
+ Scene2DSharedObject(Scene2DManager *manager);
+ ~Scene2DSharedObject();
+
+ QQuickRenderControl *m_renderControl;
+ QQuickWindow *m_quickWindow;
+ Scene2DManager *m_renderManager;
+ QOffscreenSurface *m_surface;
+
+ QThread *m_renderThread;
+ QObject *m_renderObject;
+
+ QWaitCondition m_cond;
+ QMutex m_mutex;
+
+ bool isInitialized() const;
+ void setInitialized();
+
+ void requestQuit();
+ bool isQuit() const;
+
+ void requestRender(bool sync);
+
+ bool isSyncRequested() const;
+ void clearSyncRequest();
+
+ void waitRender();
+ void wakeWaiting();
+
+ bool isPrepared() const;
+ void setPrepared();
+
+ void disallowRender();
+ bool canRender() const;
+
+ void cleanup();
+
+private:
+
+ bool m_disallowed;
+ bool m_quit;
+ bool m_requestSync;
+ bool m_requestRender;
+ bool m_prepared;
+ bool m_initialized;
+};
+
+typedef QSharedPointer<Scene2DSharedObject> Scene2DSharedObjectPtr;
+
+class Q_AUTOTEST_EXPORT QScene2DPrivate : public QFrameGraphNodePrivate
+{
+public:
+ Q_DECLARE_PUBLIC(QScene2D)
+
+ QScene2DPrivate();
+ ~QScene2DPrivate();
+
+ static Scene2DSharedObject *getSharedObject(QScene2D *rqtt);
+
+ Scene2DManager *m_renderManager;
+ QMetaObject::Connection m_textureDestroyedConnection;
+};
+
+struct QScene2DData
+{
+ bool renderOnce;
+ Qt3DCore::QNodeId textureId;
+ Scene2DSharedObjectPtr sharedObject;
+};
+
+
+class Scene2DManager : public QWindow
+{
+ Q_OBJECT
+public:
+ Scene2DManager(QScene2DPrivate *priv);
+ ~Scene2DManager();
+
+ QQmlEngine *m_qmlEngine;
+ QQmlComponent *m_qmlComponent;
+ QQuickItem *m_rootItem;
+
+ QScene2DPrivate *m_priv;
+ QSharedPointer<Scene2DSharedObject> m_sharedObject;
+
+ QAbstractTexture *m_texture;
+ QUrl m_source;
+ Qt3DCore::QNodeId m_id;
+
+ bool m_requested;
+ bool m_initialized;
+ bool m_renderSyncRequested;
+ bool m_renderOnce;
+ bool m_backendInitialized;
+
+ void requestRender();
+ void requestRenderSync();
+ void doRenderSync();
+ void startIfInitialized();
+ void stopAndClean();
+ void run();
+ void updateSizes();
+
+ void setTexture(QAbstractTexture *texture);
+ void setSource(const QUrl &url);
+
+ bool event(QEvent *e) Q_DECL_OVERRIDE;
+
+ Q_SIGNAL void onLoadedChanged();
+
+ void cleanup();
+};
+
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_QSCENE2D_P_H
diff --git a/src/quick3d/quick3drender/scene2d/scene2d.cpp b/src/quick3d/quick3drender/scene2d/scene2d.cpp
new file mode 100644
index 000000000..edf4fd643
--- /dev/null
+++ b/src/quick3d/quick3drender/scene2d/scene2d.cpp
@@ -0,0 +1,303 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** 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 <Qt3DRender/qscene2d.h>
+#include <Qt3DRender/private/qscene2d_p.h>
+#include <Qt3DRender/private/scene2d_p.h>
+#include <Qt3DRender/private/graphicscontext_p.h>
+#include <Qt3DRender/private/texture_p.h>
+#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DCore/qpropertyupdatedchange.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+RenderQmlEventHandler::RenderQmlEventHandler(Scene2D *node)
+ : QObject()
+ , m_node(node)
+{
+}
+
+// Event handler for the RenderQmlToTexture::renderThread
+bool RenderQmlEventHandler::event(QEvent *e)
+{
+ switch (e->type()) {
+
+ case RENDER: {
+ m_node->render();
+ return true;
+ }
+
+ case INITIALIZE: {
+ m_node->initializeRender();
+ return true;
+ }
+
+ case QUIT: {
+ m_node->cleanup();
+ return true;
+ }
+
+ default:
+ break;
+ }
+ return QObject::event(e);
+}
+
+Scene2D::Scene2D()
+ : FrameGraphNode(FrameGraphNode::InvalidNodeType)
+ , m_context(nullptr)
+ , m_sharedObject(nullptr)
+ , m_renderThread(nullptr)
+ , m_graphicsContext(nullptr)
+ , m_texture(nullptr)
+ , m_initialized(false)
+ , m_renderInitialized(false)
+ , m_renderOnce(false)
+{
+
+}
+
+Scene2D::~Scene2D()
+{
+ // this gets called from aspect thread. Wait for the render thread then delete it.
+ if (m_renderThread) {
+ m_renderThread->wait(1000);
+ delete m_renderThread;
+ }
+}
+
+void Scene2D::setTexture(Qt3DCore::QNodeId textureId)
+{
+ m_textureId = textureId;
+ attach();
+ checkInitialized();
+}
+
+void Scene2D::checkInitialized()
+{
+ if (!m_initialized && m_textureId != Qt3DCore::QNodeId()) {
+
+ // Create render thread
+ m_renderThread = new QThread();
+ m_renderThread->setObjectName(QStringLiteral("Scene2D::renderThread"));
+ m_sharedObject->m_renderThread = m_renderThread;
+
+ // Create event handler for the render thread
+ m_sharedObject->m_renderObject = new RenderQmlEventHandler(this);
+ m_sharedObject->m_renderObject->moveToThread(m_sharedObject->m_renderThread);
+ m_sharedObject->m_renderThread->start();
+
+ // Notify main thread we have been initialized
+ QCoreApplication::postEvent(m_sharedObject->m_renderManager, new QEvent(INITIALIZED));
+
+ // Initialize render thread
+ QCoreApplication::postEvent(m_sharedObject->m_renderObject, new QEvent(INITIALIZE));
+
+ m_initialized = true;
+ }
+}
+
+void Scene2D::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change)
+{
+ const auto typedChange = qSharedPointerCast<Qt3DCore::QNodeCreatedChange<QScene2DData>>(change);
+ const auto &data = typedChange->data;
+ m_renderOnce = m_renderOnce;
+ setSharedObject(data.sharedObject);
+ setTexture(data.textureId);
+}
+
+void Scene2D::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
+{
+ if (e->type() == Qt3DCore::PropertyUpdated) {
+ Qt3DCore::QPropertyUpdatedChangePtr propertyChange
+ = qSharedPointerCast<Qt3DCore::QPropertyUpdatedChange>(e);
+ if (propertyChange->propertyName() == QByteArrayLiteral("enabled"))
+ setEnabled(propertyChange->value().toBool());
+ else if (propertyChange->propertyName() == QByteArrayLiteral("dirty")) {
+ // sent to trigger backend update when the texture gets rendered
+ // so do nothing here
+ }
+ else if (propertyChange->propertyName() == QByteArrayLiteral("renderOnce"))
+ m_renderOnce = propertyChange->value().toBool();
+ else if (propertyChange->propertyName() == QByteArrayLiteral("texture")) {
+ Qt3DCore::QNodeId textureId = propertyChange->value().value<Qt3DCore::QNodeId>();
+ setTexture(textureId);
+ }
+ markDirty(AbstractRenderer::AllDirty);
+ }
+ FrameGraphNode::sceneChangeEvent(e);
+}
+
+void Scene2D::setSharedObject(Qt3DRender::Scene2DSharedObjectPtr sharedObject)
+{
+ m_sharedObject = sharedObject;
+}
+
+void Scene2D::initializeRender()
+{
+ if (!m_renderInitialized) {
+ Qt3DRender::Render::Renderer *renderer
+ = static_cast<Qt3DRender::Render::Renderer *>(this->renderer());
+ if (!renderer)
+ return;
+
+ QSurfaceFormat format;
+ format.setDepthBufferSize(24);
+ format.setStencilBufferSize(8);
+
+ m_context = new QOpenGLContext();
+ m_context->setFormat(format);
+
+ m_context->setShareContext(renderer->shareContext());
+ m_context->create();
+
+ m_graphicsContext = new GraphicsContext();
+ m_graphicsContext->setOpenGLContext(m_context);
+ m_graphicsContext->setRenderer(renderer);
+
+ m_graphicsContext->makeCurrent(m_sharedObject->m_surface);
+ m_sharedObject->m_renderControl->initialize(m_context);
+ m_graphicsContext->doneCurrent();
+
+ QCoreApplication::postEvent(m_sharedObject->m_renderManager, new QEvent(PREPARE));
+ m_renderInitialized = true;
+ }
+}
+
+void Scene2D::attach()
+{
+ m_attachments = AttachmentPack();
+ Attachment attach;
+ attach.m_mipLevel = 0;
+ attach.m_textureUuid = m_textureId;
+ attach.m_point = QRenderTargetOutput::AttachmentPoint::Color0;
+
+// m_attachments.addAttachment(attach);
+}
+
+void Scene2D::render()
+{
+ if (m_initialized && m_sharedObject && this->isEnabled()) {
+
+ QMutexLocker lock(&m_sharedObject->m_mutex);
+
+ // Lookup backend texture
+ if (m_texture == nullptr) {
+ m_texture = renderer()->nodeManagers()->textureManager()->lookupResource(m_textureId);
+ if (!m_texture) {
+ qCDebug(Render::Framegraph) << Q_FUNC_INFO << "Texture not set";
+ return;
+ }
+ }
+
+ m_graphicsContext->makeCurrent(m_sharedObject->m_surface);
+
+ // Don't create the OpenGL texture in this thread.
+ const bool canUseTexture = !m_texture->isTextureReset();
+
+ if (canUseTexture) {
+ // Activate fbo for the texture
+ QOpenGLTexture *glTex = m_texture->getOrCreateGLTexture();
+ const QSize textureSize = QSize(glTex->width(), glTex->height());
+
+ GLuint fbo = 0; //m_graphicsContext->activateRenderTargetForQmlRender(this, m_attachments, 0);
+
+ if (fbo != m_sharedObject->m_quickWindow->renderTargetId())
+ m_sharedObject->m_quickWindow->setRenderTarget(fbo, textureSize);
+
+ m_texture->textureLock()->lock();
+ }
+ // Call disallow rendering while mutex is locked
+ if (canUseTexture && m_renderOnce)
+ m_sharedObject->disallowRender();
+
+ // Need to call sync even if the texture is not in use
+ if (m_sharedObject->isSyncRequested()) {
+
+ m_sharedObject->clearSyncRequest();
+
+ m_sharedObject->m_renderControl->sync();
+
+ // gui thread can now continue
+ m_sharedObject->wakeWaiting();
+ lock.unlock();
+ }
+
+ if (canUseTexture) {
+
+ // Render
+ m_sharedObject->m_renderControl->render();
+
+ // Tell main thread we are done so it can begin cleanup
+ if (m_renderOnce)
+ QCoreApplication::postEvent(m_sharedObject->m_renderManager, new QEvent(RENDERED));
+
+ m_sharedObject->m_quickWindow->resetOpenGLState();
+ m_context->functions()->glFlush();
+ m_texture->textureLock()->unlock();
+ }
+ m_graphicsContext->doneCurrent();
+ }
+}
+
+void Scene2D::cleanup()
+{
+ if (m_renderInitialized && m_initialized) {
+ m_context->makeCurrent(m_sharedObject->m_surface);
+ m_sharedObject->m_renderControl->invalidate();
+ m_context->doneCurrent();
+ m_sharedObject->m_renderThread->quit();
+ delete m_sharedObject->m_renderObject;
+ m_sharedObject->m_renderObject = nullptr;
+ delete m_context;
+ m_context = nullptr;
+ m_sharedObject = nullptr;
+ delete m_graphicsContext;
+ m_graphicsContext = nullptr;
+ m_renderInitialized = false;
+ m_initialized = false;
+ }
+}
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/quick3d/quick3drender/scene2d/scene2d_p.h b/src/quick3d/quick3drender/scene2d/scene2d_p.h
new file mode 100644
index 000000000..f6e3f8903
--- /dev/null
+++ b/src/quick3d/quick3drender/scene2d/scene2d_p.h
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_SCENE2D_P_H
+#define QT3DRENDER_RENDER_SCENE2D_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DCore/qnodeid.h>
+
+#include <Qt3DRender/private/framegraphnode_p.h>
+#include <Qt3DRender/private/qscene2d_p.h>
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QSemaphore>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+class GraphicsContext;
+class Scene2D;
+
+class RenderQmlEventHandler : public QObject
+{
+ Q_OBJECT
+public:
+ RenderQmlEventHandler(Scene2D *node);
+ bool event(QEvent *e) Q_DECL_OVERRIDE;
+
+private:
+ Scene2D *m_node;
+};
+
+class Q_AUTOTEST_EXPORT Scene2D : public FrameGraphNode
+{
+public:
+ Scene2D();
+ ~Scene2D();
+
+ void attach();
+ void render();
+ void initializeRender();
+ void setSharedObject(Scene2DSharedObjectPtr sharedObject);
+ void cleanup();
+ void setTexture(Qt3DCore::QNodeId textureId);
+ void checkInitialized();
+
+ void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) Q_DECL_OVERRIDE;
+ void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) Q_DECL_FINAL;
+
+ QOpenGLContext *m_context;
+ GraphicsContext *m_graphicsContext;
+ QThread *m_renderThread;
+ Qt3DCore::QNodeId m_textureId;
+ QSharedPointer<Scene2DSharedObject> m_sharedObject;
+ AttachmentPack m_attachments;
+ Texture *m_texture;
+
+ bool m_initialized;
+ bool m_renderInitialized;
+ bool m_renderOnce;
+};
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_SCENE2D_P_H