From 78923902089d0a70c096e5f47f42e24fbcd4fa56 Mon Sep 17 00:00:00 2001
From: Jocelyn Turcotte <jocelyn.turcotte@digia.com>
Date: Tue, 1 Jul 2014 19:19:56 +0200
Subject: [PATCH] Expose the scene graph publically through a QSGEngine class

This change wraps QSGRenderContext and QSGContext in a new QSGEngine
class, and expose a public interface of QSGRenderer through a
QSGAbstractRenderer to make it usable on a standalone window or FBO.

Change-Id: I2d41187472424f5ea64650a006bcd61f2711f6b9
Reviewed-by: Gunnar Sletta <gunnar.sletta@jollamobile.com>
---
 examples/quick/scenegraph/scenegraph.pro      |   1 +
 .../quick/scenegraph/sgengine/face-smile.png  | Bin 0 -> 15408 bytes
 examples/quick/scenegraph/sgengine/main.cpp   |  52 +++
 .../quick/scenegraph/sgengine/sgengine.pro    |  11 +
 .../quick/scenegraph/sgengine/sgengine.qrc    |   5 +
 examples/quick/scenegraph/sgengine/window.cpp | 241 +++++++++++++
 examples/quick/scenegraph/sgengine/window.h   |  85 +++++
 src/quick/items/qquickwindow.cpp              |   4 +-
 .../coreapi/qsgabstractrenderer.cpp           | 318 ++++++++++++++++++
 .../scenegraph/coreapi/qsgabstractrenderer.h  | 103 ++++++
 .../coreapi/qsgabstractrenderer_p.h           |  77 +++++
 src/quick/scenegraph/coreapi/qsgnode.h        |   5 +-
 src/quick/scenegraph/coreapi/qsgrenderer.cpp  |  96 ++----
 src/quick/scenegraph/coreapi/qsgrenderer_p.h  |  65 +---
 src/quick/scenegraph/qsgcontext.cpp           |  22 +-
 src/quick/scenegraph/qsgcontext_p.h           |   3 +
 src/quick/scenegraph/scenegraph.pri           |   6 +
 src/quick/scenegraph/util/qsgengine.cpp       | 204 +++++++++++
 src/quick/scenegraph/util/qsgengine.h         |  80 +++++
 src/quick/scenegraph/util/qsgengine_p.h       |  66 ++++
 20 files changed, 1304 insertions(+), 140 deletions(-)
 create mode 100644 examples/quick/scenegraph/sgengine/face-smile.png
 create mode 100644 examples/quick/scenegraph/sgengine/main.cpp
 create mode 100644 examples/quick/scenegraph/sgengine/sgengine.pro
 create mode 100644 examples/quick/scenegraph/sgengine/sgengine.qrc
 create mode 100644 examples/quick/scenegraph/sgengine/window.cpp
 create mode 100644 examples/quick/scenegraph/sgengine/window.h
 create mode 100644 src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp
 create mode 100644 src/quick/scenegraph/coreapi/qsgabstractrenderer.h
 create mode 100644 src/quick/scenegraph/coreapi/qsgabstractrenderer_p.h
 create mode 100644 src/quick/scenegraph/util/qsgengine.cpp
 create mode 100644 src/quick/scenegraph/util/qsgengine.h
 create mode 100644 src/quick/scenegraph/util/qsgengine_p.h

diff --git a/examples/quick/scenegraph/scenegraph.pro b/examples/quick/scenegraph/scenegraph.pro
index 4354954445..85ae25907a 100644
--- a/examples/quick/scenegraph/scenegraph.pro
+++ b/examples/quick/scenegraph/scenegraph.pro
@@ -2,6 +2,7 @@ TEMPLATE = subdirs
 SUBDIRS += \
         customgeometry \
         openglunderqml \
+        sgengine \
         simplematerial \
         textureinsgnode \
         textureinthread \
diff --git a/examples/quick/scenegraph/sgengine/face-smile.png b/examples/quick/scenegraph/sgengine/face-smile.png
new file mode 100644
index 0000000000000000000000000000000000000000..3d66d725781730c7a9376a25113164f8882d9795
GIT binary patch
literal 15408
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
SNxgs4ATkn);&q}%LH`e8$k9yz

literal 0
HcmV?d00001

diff --git a/examples/quick/scenegraph/sgengine/main.cpp b/examples/quick/scenegraph/sgengine/main.cpp
new file mode 100644
index 0000000000..9a9450710b
--- /dev/null
+++ b/examples/quick/scenegraph/sgengine/main.cpp
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**   * Redistributions of source code must retain the above copyright
+**     notice, this list of conditions and the following disclaimer.
+**   * Redistributions in binary form must reproduce the above copyright
+**     notice, this list of conditions and the following disclaimer in
+**     the documentation and/or other materials provided with the
+**     distribution.
+**   * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+**     of its contributors may be used to endorse or promote products derived
+**     from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "window.h"
+#include <QGuiApplication>
+
+int main(int argc, char *argv[])
+{
+    QGuiApplication app(argc, argv);
+
+    Window window;
+    window.show();
+
+    return app.exec();
+}
diff --git a/examples/quick/scenegraph/sgengine/sgengine.pro b/examples/quick/scenegraph/sgengine/sgengine.pro
new file mode 100644
index 0000000000..c6507a4982
--- /dev/null
+++ b/examples/quick/scenegraph/sgengine/sgengine.pro
@@ -0,0 +1,11 @@
+QT += quick
+
+HEADERS += window.h
+SOURCES += window.cpp main.cpp
+RESOURCES += \
+    sgengine.qrc \
+    ../../shared/shared.qrc
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/quick/scenegraph/sgengine
+INSTALLS += target
diff --git a/examples/quick/scenegraph/sgengine/sgengine.qrc b/examples/quick/scenegraph/sgengine/sgengine.qrc
new file mode 100644
index 0000000000..5d55bcfb6f
--- /dev/null
+++ b/examples/quick/scenegraph/sgengine/sgengine.qrc
@@ -0,0 +1,5 @@
+<RCC>
+    <qresource prefix="/scenegraph/sgengine">
+        <file>face-smile.png</file>
+    </qresource>
+</RCC>
diff --git a/examples/quick/scenegraph/sgengine/window.cpp b/examples/quick/scenegraph/sgengine/window.cpp
new file mode 100644
index 0000000000..9af4029165
--- /dev/null
+++ b/examples/quick/scenegraph/sgengine/window.cpp
@@ -0,0 +1,241 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**   * Redistributions of source code must retain the above copyright
+**     notice, this list of conditions and the following disclaimer.
+**   * Redistributions in binary form must reproduce the above copyright
+**     notice, this list of conditions and the following disclaimer in
+**     the documentation and/or other materials provided with the
+**     distribution.
+**   * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+**     of its contributors may be used to endorse or promote products derived
+**     from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "window.h"
+
+#include <QOpenGLContext>
+#include <QSGAbstractRenderer>
+#include <QSGEngine>
+#include <QSGSimpleTextureNode>
+#include <QSGTransformNode>
+#include <QScreen>
+#include <QVariantAnimation>
+
+class Item {
+public:
+    Item(QSGNode *parentNode, QSGTexture *texture, const QPointF &fromPos, const QPointF &toPos) {
+        textureNode = new QSGSimpleTextureNode;
+        textureNode->setRect(QRect(QPoint(), texture->textureSize()));
+        textureNode->setTexture(texture);
+
+        transformNode = new QSGTransformNode;
+        transformNode->setFlag(QSGNode::OwnedByParent, false);
+        transformNode->appendChildNode(textureNode);
+        parentNode->appendChildNode(transformNode);
+
+        int duration = qrand() / float(RAND_MAX) * 400 + 800;
+        rotAnimation.setStartValue(qrand() / float(RAND_MAX) * 720 - 180);
+        rotAnimation.setEndValue(qrand() / float(RAND_MAX) * 720 - 180);
+        rotAnimation.setDuration(duration);
+        rotAnimation.start();
+
+        posAnimation.setStartValue(fromPos);
+        posAnimation.setEndValue(toPos);
+        posAnimation.setDuration(duration);
+        posAnimation.start();
+    }
+
+    ~Item() {
+        delete transformNode;
+    }
+
+    bool isDone() const { return posAnimation.state() != QAbstractAnimation::Running; }
+
+    void sync() {
+        QPointF currentPos = posAnimation.currentValue().toPointF();
+        QPointF center = textureNode->rect().center();
+        QMatrix4x4 m;
+        m.translate(currentPos.x(), currentPos.y());
+        m.translate(center.x(), center.y());
+        m.rotate(rotAnimation.currentValue().toFloat(), 0, 0, 1);
+        m.translate(-center.x(), -center.y());
+        transformNode->setMatrix(m);
+    }
+
+private:
+    QSGTransformNode *transformNode;
+    QSGSimpleTextureNode *textureNode;
+    QVariantAnimation posAnimation;
+    QVariantAnimation rotAnimation;
+};
+
+Window::Window()
+    : m_initialized(false)
+    , m_context(new QOpenGLContext)
+    , m_sgEngine(new QSGEngine)
+    , m_sgRootNode(new QSGRootNode)
+{
+    setSurfaceType(QWindow::OpenGLSurface);
+    QRect g(0, 0, 640, 480);
+    g.moveCenter(screen()->geometry().center());
+    setGeometry(g);
+    setTitle(QStringLiteral("Click me!"));
+
+    QSurfaceFormat format;
+    format.setDepthBufferSize(16);
+    setFormat(format);
+    m_context->setFormat(format);
+    m_context->create();
+
+    m_animationDriver.install();
+    connect(&m_animationDriver, &QAnimationDriver::started, this, &Window::update);
+}
+
+Window::~Window()
+{
+}
+
+void Window::timerEvent(QTimerEvent *e)
+{
+    if (e->timerId() == m_updateTimer.timerId()) {
+        m_updateTimer.stop();
+
+        if (!m_context->makeCurrent(this))
+            return;
+
+        if (!m_initialized)
+            initialize();
+
+        sync();
+        render();
+
+        if (m_animationDriver.isRunning()) {
+            m_animationDriver.advance();
+            update();
+        }
+    } else
+        QWindow::timerEvent(e);
+}
+
+void Window::exposeEvent(QExposeEvent *)
+{
+    if (isExposed())
+        update();
+    else
+        invalidate();
+}
+
+void Window::mousePressEvent(QMouseEvent *)
+{
+    addItems();
+}
+
+void Window::keyPressEvent(QKeyEvent *)
+{
+    addItems();
+}
+
+void Window::addItems()
+{
+    if (!m_initialized)
+        return;
+
+    QSGTexture *textures[] = { m_smileTexture.data(), m_qtTexture.data() };
+    for (int i = 0; i < 50; ++i) {
+        QSGTexture *tex = textures[i%2];
+        QPointF fromPos(-tex->textureSize().width(), qrand() / float(RAND_MAX) * (height() - tex->textureSize().height()));
+        QPointF toPos(width(), qrand() / float(RAND_MAX) * height() * 1.5 - height() * 0.25);
+        m_items.append(QSharedPointer<Item>(new Item(m_sgRootNode.data(), tex, fromPos, toPos)));
+    }
+    update();
+}
+
+void Window::update()
+{
+    if (!m_updateTimer.isActive())
+        m_updateTimer.start(0, this);
+}
+
+void Window::sync()
+{
+    QList<QSharedPointer<Item> > validItems;
+    foreach (QSharedPointer<Item> item, m_items) {
+        if (!item->isDone()) {
+            validItems.append(item);
+            item->sync();
+        }
+    }
+    m_items.swap(validItems);
+}
+
+void Window::render()
+{
+    m_sgRenderer->setDeviceRect(size());
+    m_sgRenderer->setViewportRect(size());
+    m_sgRenderer->setProjectionMatrixToRect(QRectF(QPointF(), size()));
+    m_sgRenderer->renderScene();
+
+    m_context->swapBuffers(this);
+}
+
+void Window::initialize()
+{
+    m_sgEngine->initialize(m_context.data());
+    m_sgRenderer.reset(m_sgEngine->createRenderer());
+    m_sgRenderer->setRootNode(m_sgRootNode.data());
+    m_sgRenderer->setClearColor(QColor(32, 32, 32));
+
+    // With QSGEngine::createTextureFromId
+    GLuint glTexture;
+    glGenTextures(1, &glTexture);
+    glBindTexture(GL_TEXTURE_2D, glTexture);
+    QImage smile = QImage(":/scenegraph/sgengine/face-smile.png").scaled(48, 48, Qt::KeepAspectRatio, Qt::SmoothTransformation);
+    smile = smile.convertToFormat(QImage::Format_RGBA8888_Premultiplied);
+    Q_ASSERT(!smile.isNull());
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, smile.width(), smile.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, smile.constBits());
+    m_smileTexture.reset(m_sgEngine->createTextureFromId(glTexture, smile.size(), QFlag(QSGEngine::TextureOwnsGLTexture | QSGEngine::TextureHasAlphaChannel)));
+
+    // With QSGEngine::createTextureFromImage
+    QImage qtLogo = QImage(":/shared/images/qt-logo.png").scaled(48, 48, Qt::KeepAspectRatio, Qt::SmoothTransformation);
+    Q_ASSERT(!qtLogo.isNull());
+    m_qtTexture.reset(m_sgEngine->createTextureFromImage(qtLogo));
+    m_initialized = true;
+}
+
+void Window::invalidate()
+{
+    m_updateTimer.stop();
+    m_items.clear();
+    m_smileTexture.reset();
+    m_qtTexture.reset();
+    m_sgRenderer.reset();
+    m_sgEngine->invalidate();
+    m_initialized = false;
+}
diff --git a/examples/quick/scenegraph/sgengine/window.h b/examples/quick/scenegraph/sgengine/window.h
new file mode 100644
index 0000000000..2b239ea430
--- /dev/null
+++ b/examples/quick/scenegraph/sgengine/window.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**   * Redistributions of source code must retain the above copyright
+**     notice, this list of conditions and the following disclaimer.
+**   * Redistributions in binary form must reproduce the above copyright
+**     notice, this list of conditions and the following disclaimer in
+**     the documentation and/or other materials provided with the
+**     distribution.
+**   * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+**     of its contributors may be used to endorse or promote products derived
+**     from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QWindow>
+
+#include <QAnimationDriver>
+#include <QBasicTimer>
+#include <QSharedPointer>
+
+class Item;
+QT_FORWARD_DECLARE_CLASS(QSGAbstractRenderer)
+QT_FORWARD_DECLARE_CLASS(QSGEngine)
+QT_FORWARD_DECLARE_CLASS(QSGRootNode)
+QT_FORWARD_DECLARE_CLASS(QSGTexture)
+
+class Window : public QWindow
+{
+    Q_OBJECT
+public:
+    Window();
+    ~Window();
+
+    void addItems();
+    void update();
+
+private:
+    void timerEvent(QTimerEvent *);
+    void exposeEvent(QExposeEvent *);
+    void mousePressEvent(QMouseEvent *);
+    void keyPressEvent(QKeyEvent *);
+
+    void sync();
+    void render();
+    void initialize();
+    void invalidate();
+
+    bool m_initialized;
+    QScopedPointer<QOpenGLContext> m_context;
+    QScopedPointer<QSGEngine> m_sgEngine;
+    QScopedPointer<QSGRootNode> m_sgRootNode;
+    QScopedPointer<QSGAbstractRenderer> m_sgRenderer;
+    QScopedPointer<QSGTexture> m_smileTexture;
+    QScopedPointer<QSGTexture> m_qtTexture;
+    QList<QSharedPointer<Item> > m_items;
+
+    QBasicTimer m_updateTimer;
+    QAnimationDriver m_animationDriver;
+};
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index 12be33d790..f1d2f63756 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -350,9 +350,9 @@ void QQuickWindowPrivate::syncSceneGraph()
 
     // Copy the current state of clearing from window into renderer.
     renderer->setClearColor(clearColor);
-    QSGRenderer::ClearMode mode = QSGRenderer::ClearStencilBuffer | QSGRenderer::ClearDepthBuffer;
+    QSGAbstractRenderer::ClearMode mode = QSGAbstractRenderer::ClearStencilBuffer | QSGAbstractRenderer::ClearDepthBuffer;
     if (clearBeforeRendering)
-        mode |= QSGRenderer::ClearColorBuffer;
+        mode |= QSGAbstractRenderer::ClearColorBuffer;
     renderer->setClearMode(mode);
 
     renderer->setCustomRenderMode(customRenderMode);
diff --git a/src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp b/src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp
new file mode 100644
index 0000000000..9409822c8f
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp
@@ -0,0 +1,318 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQuick 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 Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 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 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgabstractrenderer_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+    \class QSGAbstractRenderer
+    \brief QSGAbstractRenderer gives access to the scene graph nodes and rendering of a QSGEngine.
+    \inmodule QtQuick
+    \since 5.4
+
+    A QSGAbstractRenderer created by a QSGEngine allows you to set your QSGNode
+    tree through setRootNode() and control the rendering viewport through
+    setDeviceRect(), setViewportRect() and setProjectionMatrixToRect().
+    You can finally trigger the rendering to the desired framebuffer through
+    renderScene().
+
+    The QSGAbstractRenderer is only available when used with a QSGEngine
+    and isn't exposed when used internally by QQuickWindow.
+
+    \sa QSGEngine, QSGNode
+ */
+
+/*!
+    \enum QSGAbstractRenderer::ClearModeBit
+
+    Used with setClearMode() to indicate which buffer should
+    be cleared before the scene render.
+
+    \value ClearColorBuffer Clear the color buffer using clearColor().
+    \value ClearDepthBuffer Clear the depth buffer.
+    \value ClearStencilBuffer Clear the stencil buffer.
+
+    \sa setClearMode(), setClearColor()
+ */
+
+/*!
+    \fn void QSGAbstractRenderer::renderScene(GLuint fboId = 0)
+
+    Render the scene to the specified \a fboId
+
+    If \a fboId isn't specified, the scene graph will be rendered
+    to the default framebuffer. You will have to call
+    QOpenGLContext::swapBuffers() yourself afterward.
+
+    The framebuffer specified by \a fboId will be bound automatically.
+
+    \sa QOpenGLContext::swapBuffers(), QOpenGLFramebufferObject::handle()
+ */
+
+/*!
+    \fn void QSGAbstractRenderer::sceneGraphChanged()
+
+    This signal is emitted on the first modification of a node in
+    the tree after the last scene render.
+ */
+
+/*!
+    \internal
+ */
+QSGAbstractRendererPrivate::QSGAbstractRendererPrivate()
+    : m_root_node(0)
+    , m_clear_color(Qt::transparent)
+    , m_clear_mode(QSGAbstractRenderer::ClearColorBuffer | QSGAbstractRenderer::ClearDepthBuffer)
+{
+}
+
+/*!
+    \internal
+ */
+QSGAbstractRenderer::QSGAbstractRenderer(QObject *parent)
+    : QObject(*new QSGAbstractRendererPrivate, parent)
+{
+}
+
+/*!
+    \internal
+ */
+QSGAbstractRenderer::~QSGAbstractRenderer()
+{
+}
+
+/*!
+    Sets the \a node as the root of the QSGNode scene
+    that you want to render. You need to provide a \a node
+    before trying to render the scene.
+
+    \note This doesn't take ownership of \a node.
+
+    \sa rootNode()
+*/
+void QSGAbstractRenderer::setRootNode(QSGRootNode *node)
+{
+    Q_D(QSGAbstractRenderer);
+    if (d->m_root_node == node)
+        return;
+    if (d->m_root_node) {
+        d->m_root_node->m_renderers.removeOne(this);
+        nodeChanged(d->m_root_node, QSGNode::DirtyNodeRemoved);
+    }
+    d->m_root_node = node;
+    if (d->m_root_node) {
+        Q_ASSERT(!d->m_root_node->m_renderers.contains(this));
+        d->m_root_node->m_renderers << this;
+        nodeChanged(d->m_root_node, QSGNode::DirtyNodeAdded);
+    }
+}
+
+/*!
+    Returns the root of the QSGNode scene.
+
+    \sa setRootNode()
+*/
+QSGRootNode *QSGAbstractRenderer::rootNode() const
+{
+    Q_D(const QSGAbstractRenderer);
+    return d->m_root_node;
+}
+
+
+/*!
+    \fn void QSGAbstractRenderer::setDeviceRect(const QSize &size)
+    \overload
+
+    Sets the \a size of the surface being rendered to.
+
+    \sa deviceRect()
+ */
+
+/*!
+    Sets \a rect as the geometry of the surface being rendered to.
+
+    \sa deviceRect()
+ */
+void QSGAbstractRenderer::setDeviceRect(const QRect &rect)
+{
+    Q_D(QSGAbstractRenderer);
+    d->m_device_rect = rect;
+}
+
+/*!
+    Returns the device rect of the surface being rendered to.
+
+    \sa setDeviceRect()
+ */
+QRect QSGAbstractRenderer::deviceRect() const
+{
+    Q_D(const QSGAbstractRenderer);
+    return d->m_device_rect;
+}
+
+/*!
+    \fn void QSGAbstractRenderer::setViewportRect(const QSize &size)
+    \overload
+
+    Sets the \a size of the viewport to render
+    on the surface.
+
+    \sa viewportRect()
+ */
+
+/*!
+    Sets \a rect as the geometry of the viewport to render
+    on the surface.
+
+    \sa viewportRect()
+ */
+void QSGAbstractRenderer::setViewportRect(const QRect &rect)
+{
+    Q_D(QSGAbstractRenderer);
+    d->m_viewport_rect = rect;
+}
+
+/*!
+    Returns the rect of the viewport to render.
+
+    \sa setViewportRect()
+ */
+QRect QSGAbstractRenderer::viewportRect() const
+{
+    Q_D(const QSGAbstractRenderer);
+    return d->m_viewport_rect;
+}
+
+/*!
+    Convenience method that calls setProjectionMatrix() with an
+    orthographic matrix generated from \a rect.
+
+    \sa setProjectionMatrix(), projectionMatrix()
+ */
+void QSGAbstractRenderer::setProjectionMatrixToRect(const QRectF &rect)
+{
+    QMatrix4x4 matrix;
+    matrix.ortho(rect.x(),
+                 rect.x() + rect.width(),
+                 rect.y() + rect.height(),
+                 rect.y(),
+                 1,
+                 -1);
+    setProjectionMatrix(matrix);
+}
+
+/*!
+    Use \a matrix to project the QSGNode coordinates onto surface pixels.
+
+    \sa projectionMatrix(), setProjectionMatrixToRect()
+ */
+void QSGAbstractRenderer::setProjectionMatrix(const QMatrix4x4 &matrix)
+{
+    Q_D(QSGAbstractRenderer);
+    d->m_projection_matrix = matrix;
+}
+
+/*!
+    Returns the projection matrix
+
+    \sa setProjectionMatrix(), setProjectionMatrixToRect()
+ */
+QMatrix4x4 QSGAbstractRenderer::projectionMatrix() const
+{
+    Q_D(const QSGAbstractRenderer);
+    return d->m_projection_matrix;
+}
+
+/*!
+    Use \a color to clear the framebuffer when clearMode() is
+    set to QSGAbstractRenderer::ClearColorBuffer.
+
+    \sa clearColor(), setClearMode()
+ */
+void QSGAbstractRenderer::setClearColor(const QColor &color)
+{
+    Q_D(QSGAbstractRenderer);
+    d->m_clear_color = color;
+}
+
+/*!
+    Returns the color that clears the framebuffer at the beginning
+    of the rendering.
+
+    \sa setClearColor(), clearMode()
+ */
+QColor QSGAbstractRenderer::clearColor() const
+{
+    Q_D(const QSGAbstractRenderer);
+    return d->m_clear_color;
+}
+
+/*!
+    Defines which attachment of the framebuffer should be cleared
+    before each scene render with the \a mode flag.
+
+    \sa clearMode(), setClearColor()
+ */
+void QSGAbstractRenderer::setClearMode(ClearMode mode)
+{
+    Q_D(QSGAbstractRenderer);
+    d->m_clear_mode = mode;
+}
+
+/*!
+    Flags defining which attachment of the framebuffer will be cleared
+    before each scene render.
+
+    \sa setClearMode(), clearColor()
+ */
+QSGAbstractRenderer::ClearMode QSGAbstractRenderer::clearMode() const
+{
+    Q_D(const QSGAbstractRenderer);
+    return d->m_clear_mode;
+}
+
+/*!
+    \fn void QSGAbstractRenderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state)
+    \internal
+ */
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/coreapi/qsgabstractrenderer.h b/src/quick/scenegraph/coreapi/qsgabstractrenderer.h
new file mode 100644
index 0000000000..671d62e931
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgabstractrenderer.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQuick 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 Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 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 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGABSTRACTRENDERER_H
+#define QSGABSTRACTRENDERER_H
+
+#include "qsgnode.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSGAbstractRendererPrivate;
+
+class Q_QUICK_EXPORT QSGAbstractRenderer : public QObject
+{
+    Q_OBJECT
+public:
+    enum ClearModeBit
+    {
+        ClearColorBuffer    = 0x0001,
+        ClearDepthBuffer    = 0x0002,
+        ClearStencilBuffer  = 0x0004
+    };
+    Q_DECLARE_FLAGS(ClearMode, ClearModeBit)
+
+    virtual ~QSGAbstractRenderer();
+
+    void setRootNode(QSGRootNode *node);
+    QSGRootNode *rootNode() const;
+    void setDeviceRect(const QRect &rect);
+    inline void setDeviceRect(const QSize &size) { setDeviceRect(QRect(QPoint(), size)); }
+    QRect deviceRect() const;
+
+    void setViewportRect(const QRect &rect);
+    inline void setViewportRect(const QSize &size) { setViewportRect(QRect(QPoint(), size)); }
+    QRect viewportRect() const;
+
+    void setProjectionMatrixToRect(const QRectF &rect);
+    void setProjectionMatrix(const QMatrix4x4 &matrix);
+    QMatrix4x4 projectionMatrix() const;
+
+    void setClearColor(const QColor &color);
+    QColor clearColor() const;
+
+    void setClearMode(ClearMode mode);
+    ClearMode clearMode() const;
+
+    virtual void renderScene(GLuint fboId = 0) = 0;
+
+Q_SIGNALS:
+    void sceneGraphChanged();
+
+protected:
+    QSGAbstractRenderer(QObject *parent = 0);
+    virtual void nodeChanged(QSGNode *node, QSGNode::DirtyState state) = 0;
+
+private:
+    Q_DECLARE_PRIVATE(QSGAbstractRenderer)
+    friend class QSGRootNode;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGAbstractRenderer::ClearMode)
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/scenegraph/coreapi/qsgabstractrenderer_p.h b/src/quick/scenegraph/coreapi/qsgabstractrenderer_p.h
new file mode 100644
index 0000000000..c60a488cc8
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgabstractrenderer_p.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQuick 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 Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 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 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGABSTRACTRENDERER_P_H
+#define QSGABSTRACTRENDERER_P_H
+
+#include "qsgabstractrenderer.h"
+
+#include "qsgnode.h"
+#include <qcolor.h>
+
+#include <QtCore/private/qobject_p.h>
+#include <QtQuick/private/qtquickglobal_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICK_PRIVATE_EXPORT QSGAbstractRendererPrivate : public QObjectPrivate
+{
+    Q_DECLARE_PUBLIC(QSGAbstractRenderer)
+public:
+    static const QSGAbstractRendererPrivate *get(const QSGAbstractRenderer *q) { return q->d_func(); }
+
+    QSGAbstractRendererPrivate();
+    void updateProjectionMatrix();
+
+    QSGRootNode *m_root_node;
+    QColor m_clear_color;
+    QSGAbstractRenderer::ClearMode m_clear_mode;
+
+    QRect m_device_rect;
+    QRect m_viewport_rect;
+
+    QMatrix4x4 m_projection_matrix;
+    uint m_mirrored : 1;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/scenegraph/coreapi/qsgnode.h b/src/quick/scenegraph/coreapi/qsgnode.h
index 380b7e01e1..be93fd1fce 100644
--- a/src/quick/scenegraph/coreapi/qsgnode.h
+++ b/src/quick/scenegraph/coreapi/qsgnode.h
@@ -53,6 +53,7 @@ QT_BEGIN_NAMESPACE
 #define QSG_RUNTIME_DESCRIPTION
 #endif
 
+class QSGAbstractRenderer;
 class QSGRenderer;
 
 class QSGNode;
@@ -298,11 +299,11 @@ public:
 private:
     void notifyNodeChange(QSGNode *node, DirtyState state);
 
-    friend class QSGRenderer;
+    friend class QSGAbstractRenderer;
     friend class QSGNode;
     friend class QSGGeometryNode;
 
-    QList<QSGRenderer *> m_renderers;
+    QList<QSGAbstractRenderer *> m_renderers;
 };
 
 
diff --git a/src/quick/scenegraph/coreapi/qsgrenderer.cpp b/src/quick/scenegraph/coreapi/qsgrenderer.cpp
index c056507729..3c9042fee5 100644
--- a/src/quick/scenegraph/coreapi/qsgrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgrenderer.cpp
@@ -40,18 +40,9 @@
 ****************************************************************************/
 
 #include "qsgrenderer_p.h"
-#include "qsgnode.h"
-#include "qsgmaterial.h"
 #include "qsgnodeupdater_p.h"
-#include "qsggeometry_p.h"
-
-#include <private/qsgadaptationlayer_p.h>
-#include <private/qsgshadersourcebuilder_p.h>
 
 #include <qopenglframebufferobject.h>
-#include <QtGui/qguiapplication.h>
-
-#include <qdatetime.h>
 
 #include <private/qquickprofiler_p.h>
 
@@ -63,12 +54,12 @@ static QElapsedTimer frameTimer;
 static qint64 preprocessTime;
 static qint64 updatePassTime;
 
-void QSGBindable::clear(QSGRenderer::ClearMode mode) const
+void QSGBindable::clear(QSGAbstractRenderer::ClearMode mode) const
 {
     GLuint bits = 0;
-    if (mode & QSGRenderer::ClearColorBuffer) bits |= GL_COLOR_BUFFER_BIT;
-    if (mode & QSGRenderer::ClearDepthBuffer) bits |= GL_DEPTH_BUFFER_BIT;
-    if (mode & QSGRenderer::ClearStencilBuffer) bits |= GL_STENCIL_BUFFER_BIT;
+    if (mode & QSGAbstractRenderer::ClearColorBuffer) bits |= GL_COLOR_BUFFER_BIT;
+    if (mode & QSGAbstractRenderer::ClearDepthBuffer) bits |= GL_DEPTH_BUFFER_BIT;
+    if (mode & QSGAbstractRenderer::ClearStencilBuffer) bits |= GL_STENCIL_BUFFER_BIT;
     QOpenGLContext::currentContext()->functions()->glClear(bits);
 }
 
@@ -118,18 +109,13 @@ void QSGBindableFboId::bind() const
 
 
 QSGRenderer::QSGRenderer(QSGRenderContext *context)
-    : QObject()
-    , m_clear_color(Qt::transparent)
-    , m_clear_mode(ClearColorBuffer | ClearDepthBuffer)
-    , m_current_opacity(1)
+    : m_current_opacity(1)
     , m_current_determinant(1)
     , m_device_pixel_ratio(1)
     , m_context(context)
-    , m_root_node(0)
     , m_node_updater(0)
     , m_bindable(0)
     , m_changed_emitted(false)
-    , m_mirrored(false)
     , m_is_rendering(false)
 {
 }
@@ -169,37 +155,30 @@ void QSGRenderer::setNodeUpdater(QSGNodeUpdater *updater)
     m_node_updater = updater;
 }
 
-
-void QSGRenderer::setRootNode(QSGRootNode *node)
+bool QSGRenderer::isMirrored() const
 {
-    if (m_root_node == node)
-        return;
-    if (m_root_node) {
-        m_root_node->m_renderers.removeOne(this);
-        nodeChanged(m_root_node, QSGNode::DirtyNodeRemoved);
-    }
-    m_root_node = node;
-    if (m_root_node) {
-        Q_ASSERT(!m_root_node->m_renderers.contains(this));
-        m_root_node->m_renderers << this;
-        nodeChanged(m_root_node, QSGNode::DirtyNodeAdded);
-    }
+    QMatrix4x4 matrix = projectionMatrix();
+    // Mirrored relative to the usual Qt coordinate system with origin in the top left corner.
+    return matrix(0, 0) * matrix(1, 1) - matrix(0, 1) * matrix(1, 0) > 0;
 }
 
-
-void QSGRenderer::renderScene()
+void QSGRenderer::renderScene(GLuint fboId)
 {
-    class B : public QSGBindable
-    {
-    public:
-        void bind() const { QOpenGLFramebufferObject::bindDefault(); }
-    } b;
-    renderScene(b);
+    if (fboId) {
+        QSGBindableFboId bindable(fboId);
+        renderScene(bindable);
+    } else {
+        class B : public QSGBindable
+        {
+        public:
+            void bind() const { QOpenGLFramebufferObject::bindDefault(); }
+        } bindable;
+        renderScene(bindable);
+    }
 }
-
 void QSGRenderer::renderScene(const QSGBindable &bindable)
 {
-    if (!m_root_node)
+    if (!rootNode())
         return;
 
     m_is_rendering = true;
@@ -255,30 +234,6 @@ void QSGRenderer::renderScene(const QSGBindable &bindable)
 
 }
 
-void QSGRenderer::setProjectionMatrixToRect(const QRectF &rect)
-{
-    QMatrix4x4 matrix;
-    matrix.ortho(rect.x(),
-                 rect.x() + rect.width(),
-                 rect.y() + rect.height(),
-                 rect.y(),
-                 1,
-                 -1);
-    setProjectionMatrix(matrix);
-}
-
-void QSGRenderer::setProjectionMatrix(const QMatrix4x4 &matrix)
-{
-    m_projection_matrix = matrix;
-    // Mirrored relative to the usual Qt coordinate system with origin in the top left corner.
-    m_mirrored = matrix(0, 0) * matrix(1, 1) - matrix(0, 1) * matrix(1, 0) > 0;
-}
-
-void QSGRenderer::setClearColor(const QColor &color)
-{
-    m_clear_color = color;
-}
-
 /*!
     Updates internal data structures and emits the sceneGraphChanged() signal.
 
@@ -309,7 +264,8 @@ void QSGRenderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state)
 
 void QSGRenderer::preprocess()
 {
-    Q_ASSERT(m_root_node);
+    QSGRootNode *root = rootNode();
+    Q_ASSERT(root);
 
     // We need to take a copy here, in case any of the preprocess calls deletes a node that
     // is in the preprocess list and thus, changes the m_nodes_to_preprocess behind our backs
@@ -319,7 +275,7 @@ void QSGRenderer::preprocess()
     for (QSet<QSGNode *>::const_iterator it = items.constBegin();
          it != items.constEnd(); ++it) {
         QSGNode *n = *it;
-        if (!nodeUpdater()->isNodeBlocked(n, m_root_node)) {
+        if (!nodeUpdater()->isNodeBlocked(n, root)) {
             n->preprocess();
         }
     }
@@ -328,7 +284,7 @@ void QSGRenderer::preprocess()
     if (profileFrames)
         preprocessTime = frameTimer.nsecsElapsed();
 
-    nodeUpdater()->updateStates(m_root_node);
+    nodeUpdater()->updateStates(root);
 
     if (profileFrames)
         updatePassTime = frameTimer.nsecsElapsed();
diff --git a/src/quick/scenegraph/coreapi/qsgrenderer_p.h b/src/quick/scenegraph/coreapi/qsgrenderer_p.h
index e24a6b652f..e6e7c13a1d 100644
--- a/src/quick/scenegraph/coreapi/qsgrenderer_p.h
+++ b/src/quick/scenegraph/coreapi/qsgrenderer_p.h
@@ -42,54 +42,28 @@
 #ifndef QSGRENDERER_P_H
 #define QSGRENDERER_P_H
 
-#include <qcolor.h>
-#include <qset.h>
-#include <qhash.h>
-
+#include "qsgabstractrenderer.h"
+#include "qsgabstractrenderer_p.h"
 #include "qsgnode.h"
 #include "qsgmaterial.h"
-#include <QtQuick/qsgtexture.h>
 
 #include <QtQuick/private/qsgcontext_p.h>
 
 QT_BEGIN_NAMESPACE
 
-class QSGMaterialShader;
-struct QSGMaterialType;
-class QOpenGLFramebufferObject;
-class TextureReference;
 class QSGBindable;
 class QSGNodeUpdater;
 
 Q_QUICK_PRIVATE_EXPORT bool qsg_test_and_clear_fatal_render_error();
 Q_QUICK_PRIVATE_EXPORT void qsg_set_fatal_renderer_error();
 
-class Q_QUICK_PRIVATE_EXPORT QSGRenderer : public QObject
+class Q_QUICK_PRIVATE_EXPORT QSGRenderer : public QSGAbstractRenderer
 {
-    Q_OBJECT
 public:
-    enum ClearModeBit
-    {
-        ClearColorBuffer    = 0x0001,
-        ClearDepthBuffer    = 0x0002,
-        ClearStencilBuffer  = 0x0004
-    };
-    Q_DECLARE_FLAGS(ClearMode, ClearModeBit)
 
     QSGRenderer(QSGRenderContext *context);
     virtual ~QSGRenderer();
 
-    void setRootNode(QSGRootNode *node);
-    QSGRootNode *rootNode() const { return m_root_node; }
-
-    void setDeviceRect(const QRect &rect) { m_device_rect = rect; }
-    inline void setDeviceRect(const QSize &size) { setDeviceRect(QRect(QPoint(), size)); }
-    QRect deviceRect() const { return m_device_rect; }
-
-    void setViewportRect(const QRect &rect) { m_viewport_rect = rect; }
-    inline void setViewportRect(const QSize &size) { setViewportRect(QRect(QPoint(), size)); }
-    QRect viewportRect() const { return m_viewport_rect; }
-
     // Accessed by QSGMaterialShader::RenderState.
     QMatrix4x4 currentProjectionMatrix() const { return m_current_projection_matrix; }
     QMatrix4x4 currentModelViewMatrix() const { return m_current_model_view_matrix; }
@@ -99,36 +73,22 @@ public:
 
     void setDevicePixelRatio(qreal ratio) { m_device_pixel_ratio = ratio; }
     qreal devicePixelRatio() const { return m_device_pixel_ratio; }
-
-    virtual void setProjectionMatrixToRect(const QRectF &rect);
-    void setProjectionMatrix(const QMatrix4x4 &matrix);
-    QMatrix4x4 projectionMatrix() const { return m_projection_matrix; }
-    bool isMirrored() const { return m_mirrored; }
-
-    void setClearColor(const QColor &color);
-    QColor clearColor() const { return m_clear_color; }
-
     QSGRenderContext *context() const { return m_context; }
 
-    void renderScene();
+    bool isMirrored() const;
     void renderScene(const QSGBindable &bindable);
-    virtual void nodeChanged(QSGNode *node, QSGNode::DirtyState state);
+    virtual void renderScene(GLuint fboId = 0) Q_DECL_FINAL Q_DECL_OVERRIDE;
+    virtual void nodeChanged(QSGNode *node, QSGNode::DirtyState state) Q_DECL_OVERRIDE;
 
     QSGNodeUpdater *nodeUpdater() const;
     void setNodeUpdater(QSGNodeUpdater *updater);
 
     inline QSGMaterialShader::RenderState state(QSGMaterialShader::RenderState::DirtyStates dirty) const;
 
-    void setClearMode(ClearMode mode) { m_clear_mode = mode; }
-    ClearMode clearMode() const { return m_clear_mode; }
-
     virtual void setCustomRenderMode(const QByteArray &) { };
 
     void clearChangedFlag() { m_changed_emitted = false; }
 
-Q_SIGNALS:
-    void sceneGraphChanged(); // Add, remove, ChangeFlags changes...
-
 protected:
     virtual void render() = 0;
 
@@ -141,8 +101,6 @@ protected:
 
     void markNodeDirtyState(QSGNode *node, QSGNode::DirtyState state) { node->m_dirtyState |= state; }
 
-    QColor m_clear_color;
-    ClearMode m_clear_mode;
     QMatrix4x4 m_current_projection_matrix;
     QMatrix4x4 m_current_model_view_matrix;
     qreal m_current_opacity;
@@ -152,31 +110,22 @@ protected:
     QSGRenderContext *m_context;
 
 private:
-    QSGRootNode *m_root_node;
     QSGNodeUpdater *m_node_updater;
 
-    QRect m_device_rect;
-    QRect m_viewport_rect;
-
     QSet<QSGNode *> m_nodes_to_preprocess;
 
-    QMatrix4x4 m_projection_matrix;
-
     const QSGBindable *m_bindable;
 
     uint m_changed_emitted : 1;
-    uint m_mirrored : 1;
     uint m_is_rendering : 1;
 };
 
-Q_DECLARE_OPERATORS_FOR_FLAGS(QSGRenderer::ClearMode)
-
 class Q_QUICK_PRIVATE_EXPORT QSGBindable
 {
 public:
     virtual ~QSGBindable() { }
     virtual void bind() const = 0;
-    virtual void clear(QSGRenderer::ClearMode mode) const;
+    virtual void clear(QSGAbstractRenderer::ClearMode mode) const;
     virtual void reactivate() const;
 };
 
diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp
index 1a3565b630..5bc91992e9 100644
--- a/src/quick/scenegraph/qsgcontext.cpp
+++ b/src/quick/scenegraph/qsgcontext.cpp
@@ -363,6 +363,7 @@ QSGRenderContext::QSGRenderContext(QSGContext *context)
     , m_distanceFieldCacheManager(0)
     , m_brokenIBOs(false)
     , m_serializedRender(false)
+    , m_attachToGLContext(true)
 {
 }
 
@@ -384,12 +385,7 @@ void QSGRenderContext::renderNextFrame(QSGRenderer *renderer, GLuint fboId)
     if (m_serializedRender)
         qsg_framerender_mutex.lock();
 
-    if (fboId) {
-        QSGBindableFboId bindable(fboId);
-        renderer->renderScene(bindable);
-    } else {
-        renderer->renderScene();
-    }
+    renderer->renderScene(fboId);
 
     if (m_serializedRender)
         qsg_framerender_mutex.unlock();
@@ -441,6 +437,12 @@ QSGDistanceFieldGlyphCache *QSGRenderContext::distanceFieldGlyphCache(const QRaw
     return cache;
 }
 
+void QSGRenderContext::setAttachToGLContext(bool attach)
+{
+    Q_ASSERT(!isValid());
+    m_attachToGLContext = attach;
+}
+
 #define QSG_RENDERCONTEXT_PROPERTY "_q_sgrendercontext"
 
 QSGRenderContext *QSGRenderContext::from(QOpenGLContext *context)
@@ -473,7 +475,10 @@ void QSGRenderContext::initialize(QOpenGLContext *context)
 
     Q_ASSERT_X(!m_gl, "QSGRenderContext::initialize", "already initialized!");
     m_gl = context;
-    m_gl->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant::fromValue(this));
+    if (m_attachToGLContext) {
+        Q_ASSERT(!context->property(QSG_RENDERCONTEXT_PROPERTY).isValid());
+        context->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant::fromValue(this));
+    }
     m_sg->renderContextInitialized(this);
 
 #ifdef Q_OS_LINUX
@@ -541,7 +546,8 @@ void QSGRenderContext::invalidate()
     delete m_distanceFieldCacheManager;
     m_distanceFieldCacheManager = 0;
 
-    m_gl->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant());
+    if (m_gl->property(QSG_RENDERCONTEXT_PROPERTY) == QVariant::fromValue(this))
+        m_gl->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant());
     m_gl = 0;
 
     m_sg->renderContextInvalidated(this);
diff --git a/src/quick/scenegraph/qsgcontext_p.h b/src/quick/scenegraph/qsgcontext_p.h
index faad57b893..7113ef8530 100644
--- a/src/quick/scenegraph/qsgcontext_p.h
+++ b/src/quick/scenegraph/qsgcontext_p.h
@@ -97,6 +97,7 @@ public:
 
     QOpenGLContext *openglContext() const { return m_gl; }
     QSGContext *sceneGraphContext() const { return m_sg; }
+    bool isValid() const { return m_gl; }
 
     virtual void initialize(QOpenGLContext *context);
     virtual void invalidate();
@@ -117,6 +118,7 @@ public:
     virtual void compile(QSGMaterialShader *shader, QSGMaterial *material, const char *vertexCode = 0, const char *fragmentCode = 0);
     virtual void initialize(QSGMaterialShader *shader);
 
+    void setAttachToGLContext(bool attach);
     void registerFontengineForCleanup(QFontEngine *engine);
 
     static QSGRenderContext *from(QOpenGLContext *context);
@@ -146,6 +148,7 @@ protected:
 
     bool m_brokenIBOs;
     bool m_serializedRender;
+    bool m_attachToGLContext;
 };
 
 
diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri
index 570d6b92b5..d08fce0336 100644
--- a/src/quick/scenegraph/scenegraph.pri
+++ b/src/quick/scenegraph/scenegraph.pri
@@ -10,12 +10,15 @@ HEADERS += \
     $$PWD/coreapi/qsgnode.h \
     $$PWD/coreapi/qsgnode_p.h \
     $$PWD/coreapi/qsgnodeupdater_p.h \
+    $$PWD/coreapi/qsgabstractrenderer.h \
+    $$PWD/coreapi/qsgabstractrenderer_p.h \
     $$PWD/coreapi/qsgrenderer_p.h \
     $$PWD/coreapi/qsgrendernode_p.h \
     $$PWD/coreapi/qsggeometry_p.h \
     $$PWD/coreapi/qsgmaterialshader_p.h
 
 SOURCES += \
+    $$PWD/coreapi/qsgabstractrenderer.cpp \
     $$PWD/coreapi/qsgbatchrenderer.cpp \
     $$PWD/coreapi/qsggeometry.cpp \
     $$PWD/coreapi/qsgmaterial.cpp \
@@ -30,6 +33,8 @@ HEADERS += \
     $$PWD/util/qsgareaallocator_p.h \
     $$PWD/util/qsgatlastexture_p.h \
     $$PWD/util/qsgdepthstencilbuffer_p.h \
+    $$PWD/util/qsgengine.h \
+    $$PWD/util/qsgengine_p.h \
     $$PWD/util/qsgflatcolormaterial.h \
     $$PWD/util/qsgsimplematerial.h \
     $$PWD/util/qsgsimplerectnode.h \
@@ -48,6 +53,7 @@ SOURCES += \
     $$PWD/util/qsgareaallocator.cpp \
     $$PWD/util/qsgatlastexture.cpp \
     $$PWD/util/qsgdepthstencilbuffer.cpp \
+    $$PWD/util/qsgengine.cpp \
     $$PWD/util/qsgflatcolormaterial.cpp \
     $$PWD/util/qsgsimplerectnode.cpp \
     $$PWD/util/qsgsimpletexturenode.cpp \
diff --git a/src/quick/scenegraph/util/qsgengine.cpp b/src/quick/scenegraph/util/qsgengine.cpp
new file mode 100644
index 0000000000..127f624d8b
--- /dev/null
+++ b/src/quick/scenegraph/util/qsgengine.cpp
@@ -0,0 +1,204 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQuick 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 Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 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 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgengine_p.h"
+
+#include <QtQuick/qsgtexture.h>
+#include <private/qsgcontext_p.h>
+#include <private/qsgrenderer_p.h>
+#include <private/qsgtexture_p.h>
+
+QT_BEGIN_NAMESPACE
+
+
+/*!
+    \class QSGEngine
+    \brief The QSGEngine class allows low level rendering of a scene graph.
+    \inmodule QtQuick
+    \since 5.4
+
+    A QSGEngine can be used to render a tree of QSGNode directly on a QWindow
+    or QOpenGLFramebufferObject without any integration with QML, QQuickWindow
+    or QQuickItem and the convenience that they provide.
+
+    This means that you must handle event propagation, animation timing,
+    and node lifetime yourself.
+
+    \note This class is for very low level access to an independent scene graph.
+    Most of the time you will instead want to subclass QQuickItem and insert
+    your QSGNode in a normal QtQuick scene by overriding QQuickItem::updatePaintNode().
+
+    \sa QSGAbstractRenderer
+ */
+
+/*!
+    \enum QSGEngine::CreateTextureOption
+
+    The CreateTextureOption enums are used to customize how a texture is wrapped.
+
+    \value TextureHasAlphaChannel The texture has an alpha channel and should
+    be drawn using blending.
+
+    \value TextureOwnsGLTexture The texture object owns the texture id and
+    will delete the GL texture when the texture object is deleted.
+
+    \value TextureCanUseAtlas The image can be uploaded into a texture atlas.
+ */
+
+QSGEnginePrivate::QSGEnginePrivate()
+    : sgContext(QSGContext::createDefaultContext())
+    , sgRenderContext(new QSGRenderContext(sgContext.data()))
+{
+}
+
+/*!
+    Constructs a new QSGEngine with its \a parent
+ */
+QSGEngine::QSGEngine(QObject *parent)
+    : QObject(*(new QSGEnginePrivate), parent)
+{
+}
+
+/*!
+    Destroys the engine
+ */
+QSGEngine::~QSGEngine()
+{
+}
+
+/*!
+    Initialize the engine with \a context.
+
+    \warning You have to make sure that you call
+    QOpenGLContext::makeCurrent() on \a context before calling this.
+ */
+void QSGEngine::initialize(QOpenGLContext *context)
+{
+    Q_D(QSGEngine);
+    if (QOpenGLContext::currentContext() != context) {
+        qWarning("WARNING: The context must be current before calling QSGEngine::initialize.");
+        return;
+    }
+
+    if (!d->sgRenderContext->isValid()) {
+        d->sgRenderContext->setAttachToGLContext(false);
+        d->sgRenderContext->initialize(context);
+        connect(context, &QOpenGLContext::aboutToBeDestroyed, this, &QSGEngine::invalidate);
+    }
+}
+
+/*!
+    Invalidate the engine releasing its resources
+
+    You will have to call initialize() and createRenderer() if you
+    want to use it again.
+ */
+void QSGEngine::invalidate()
+{
+    Q_D(QSGEngine);
+    d->sgRenderContext->invalidate();
+}
+
+/*!
+    Returns a renderer that can be used to render a QSGNode tree
+
+    You call initialize() first with the QOpenGLContext that you
+    want to use with this renderer. This will return a null
+    renderer otherwise.
+ */
+QSGAbstractRenderer *QSGEngine::createRenderer() const
+{
+    Q_D(const QSGEngine);
+    if (!d->sgRenderContext->isValid())
+        return 0;
+
+    QSGRenderer *renderer = d->sgRenderContext->createRenderer();
+    renderer->setCustomRenderMode(qgetenv("QSG_VISUALIZE"));
+    return renderer;
+}
+
+/*!
+    Creates a texture using the data of \a image
+
+    Valid \a options are TextureCanUseAtlas
+
+    The caller takes ownership of the texture and the
+    texture should only be used with this engine.
+
+    \sa createTextureFromId(), QSGSimpleTextureNode::setOwnsTexture(), QQuickWindow::createTextureFromImage()
+ */
+QSGTexture *QSGEngine::createTextureFromImage(const QImage &image, CreateTextureOptions options) const
+{
+    Q_D(const QSGEngine);
+    if (!d->sgRenderContext->isValid())
+        return 0;
+
+    if (options & TextureCanUseAtlas)
+        return d->sgRenderContext->createTexture(image);
+    else
+        return d->sgRenderContext->createTextureNoAtlas(image);
+}
+
+/*!
+    Creates a texture object that wraps the GL texture \a id uploaded with \a size
+
+    Valid \a options are TextureHasAlphaChannel and TextureOwnsGLTexture
+
+    The caller takes ownership of the texture object and the
+    texture should only be used with this engine.
+
+    \sa createTextureFromImage(), QSGSimpleTextureNode::setOwnsTexture(), QQuickWindow::createTextureFromId()
+ */
+QSGTexture *QSGEngine::createTextureFromId(uint id, const QSize &size, CreateTextureOptions options) const
+{
+    Q_D(const QSGEngine);
+    if (d->sgRenderContext->isValid()) {
+        QSGPlainTexture *texture = new QSGPlainTexture();
+        texture->setTextureId(id);
+        texture->setHasAlphaChannel(options & TextureHasAlphaChannel);
+        texture->setOwnsTexture(options & TextureOwnsGLTexture);
+        texture->setTextureSize(size);
+        return texture;
+    }
+    return 0;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/util/qsgengine.h b/src/quick/scenegraph/util/qsgengine.h
new file mode 100644
index 0000000000..7aae882a70
--- /dev/null
+++ b/src/quick/scenegraph/util/qsgengine.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQuick 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 Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 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 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGENGINE_H
+#define QSGENGINE_H
+
+#include <QtCore/QObject>
+#include <QtQuick/qtquickglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOpenGLContext;
+class QSGAbstractRenderer;
+class QSGEnginePrivate;
+class QSGTexture;
+
+class Q_QUICK_EXPORT QSGEngine : public QObject
+{
+    Q_OBJECT
+    Q_DECLARE_PRIVATE(QSGEngine)
+public:
+    enum CreateTextureOption {
+        TextureHasAlphaChannel  = 0x0001,
+        TextureOwnsGLTexture    = 0x0004,
+        TextureCanUseAtlas      = 0x0008
+    };
+    Q_DECLARE_FLAGS(CreateTextureOptions, CreateTextureOption)
+
+    QSGEngine(QObject *parent = 0);
+    ~QSGEngine();
+
+    void initialize(QOpenGLContext *context);
+    void invalidate();
+
+    QSGAbstractRenderer *createRenderer() const;
+    QSGTexture *createTextureFromImage(const QImage &image, CreateTextureOptions options = CreateTextureOption(0)) const;
+    QSGTexture *createTextureFromId(uint id, const QSize &size, CreateTextureOptions options = CreateTextureOption(0)) const;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGENGINE_H
diff --git a/src/quick/scenegraph/util/qsgengine_p.h b/src/quick/scenegraph/util/qsgengine_p.h
new file mode 100644
index 0000000000..a7e1599fd5
--- /dev/null
+++ b/src/quick/scenegraph/util/qsgengine_p.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQuick 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 Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 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 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGENGINE_P_H
+#define QSGENGINE_P_H
+
+#include "qsgengine.h"
+#include <private/qobject_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGContext;
+class QSGRenderContext;
+
+class QSGEnginePrivate : public QObjectPrivate
+{
+    Q_DECLARE_PUBLIC(QSGEngine)
+
+public:
+    QSGEnginePrivate();
+
+    QScopedPointer<QSGContext> sgContext;
+    QScopedPointer<QSGRenderContext> sgRenderContext;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGENGINE_P_H
-- 
GitLab