aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGunnar Sletta <gunnar.sletta@digia.com>2013-09-23 09:53:58 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-09-25 16:16:15 +0200
commitf2233c725078d49f6b185e642325dacb47b33240 (patch)
tree7e612d02f2d4c84882bc5294640ecb99333f139d
parent0e62896fdcbad014177ba11d88bbe46eb496ab2d (diff)
Convenience class for FBO rendering in Qt Quick
Change-Id: I5712bbfa0cde9f2fe5fddc35c4cc23597717065c Reviewed-by: Lars Knoll <lars.knoll@digia.com>
-rw-r--r--examples/quick/scenegraph/textureinsgnode/fboinsgrenderer.cpp82
-rw-r--r--examples/quick/scenegraph/textureinsgnode/fboinsgrenderer.h12
-rw-r--r--examples/quick/scenegraph/textureinsgnode/main.qml2
-rw-r--r--src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc17
-rw-r--r--src/quick/items/items.pri6
-rw-r--r--src/quick/items/qquickframebufferobject.cpp397
-rw-r--r--src/quick/items/qquickframebufferobject.h98
-rw-r--r--tests/auto/quick/qquickframebufferobject/data/testStuff.qml56
-rw-r--r--tests/auto/quick/qquickframebufferobject/qquickframebufferobject.pro15
-rw-r--r--tests/auto/quick/qquickframebufferobject/tst_qquickframebufferobject.cpp255
-rw-r--r--tests/auto/quick/quick.pro1
11 files changed, 852 insertions, 89 deletions
diff --git a/examples/quick/scenegraph/textureinsgnode/fboinsgrenderer.cpp b/examples/quick/scenegraph/textureinsgnode/fboinsgrenderer.cpp
index 67df392555..0a75f82889 100644
--- a/examples/quick/scenegraph/textureinsgnode/fboinsgrenderer.cpp
+++ b/examples/quick/scenegraph/textureinsgnode/fboinsgrenderer.cpp
@@ -46,84 +46,30 @@
#include <QtQuick/QQuickWindow>
#include <qsgsimpletexturenode.h>
-
-
-
-class TextureNode : public QObject, public QSGSimpleTextureNode
+class LogoInFboRenderer : public QQuickFramebufferObject::Renderer
{
- Q_OBJECT
-
public:
- TextureNode(QQuickWindow *window)
- : m_fbo(0)
- , m_texture(0)
- , m_window(window)
- , m_logoRenderer(0)
+ LogoInFboRenderer()
{
- connect(m_window, SIGNAL(beforeRendering()), this, SLOT(renderFBO()));
+ logo.initialize();
}
- ~TextureNode()
- {
- delete m_texture;
- delete m_fbo;
- delete m_logoRenderer;
+ void render() {
+ logo.render();
+ update();
}
-public slots:
- void renderFBO()
- {
- QSize size = rect().size().toSize();
-
- if (!m_fbo) {
-
- QOpenGLFramebufferObjectFormat format;
- format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
- m_fbo = new QOpenGLFramebufferObject(size, format);
- m_texture = m_window->createTextureFromId(m_fbo->texture(), size);
- m_logoRenderer = new LogoRenderer();
- m_logoRenderer->initialize();
- setTexture(m_texture);
- }
-
- m_fbo->bind();
-
- glViewport(0, 0, size.width(), size.height());
-
- m_logoRenderer->render();
-
- m_fbo->bindDefault();
-
- m_window->update();
+ QOpenGLFramebufferObject *createFramebufferObject(const QSize &size) {
+ QOpenGLFramebufferObjectFormat format;
+ format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
+ format.setSamples(4);
+ return new QOpenGLFramebufferObject(size, format);
}
-private:
- QOpenGLFramebufferObject *m_fbo;
- QSGTexture *m_texture;
- QQuickWindow *m_window;
-
- LogoRenderer *m_logoRenderer;
+ LogoRenderer logo;
};
-
-
-FboInSGRenderer::FboInSGRenderer()
+QQuickFramebufferObject::Renderer *FboInSGRenderer::createRenderer() const
{
- setFlag(ItemHasContents, true);
+ return new LogoInFboRenderer();
}
-
-
-QSGNode *FboInSGRenderer::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
-{
- // Don't bother with resize and such, just recreate the node from scratch
- // when geometry changes.
- if (oldNode)
- delete oldNode;
-
- TextureNode *node = new TextureNode(window());
- node->setRect(boundingRect());
-
- return node;
-}
-
-#include "fboinsgrenderer.moc"
diff --git a/examples/quick/scenegraph/textureinsgnode/fboinsgrenderer.h b/examples/quick/scenegraph/textureinsgnode/fboinsgrenderer.h
index 42e10aef6d..d36f855d6c 100644
--- a/examples/quick/scenegraph/textureinsgnode/fboinsgrenderer.h
+++ b/examples/quick/scenegraph/textureinsgnode/fboinsgrenderer.h
@@ -41,19 +41,15 @@
#ifndef FBOINSGRENDERER_H
#define FBOINSGRENDERER_H
-#include <QQuickItem>
+#include <QtQuick/QQuickFramebufferObject>
+class LogoRenderer;
-class FboInSGRenderer : public QQuickItem
+class FboInSGRenderer : public QQuickFramebufferObject
{
Q_OBJECT
-
public:
- FboInSGRenderer();
-
-protected:
- QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
-
+ Renderer *createRenderer() const;
};
#endif
diff --git a/examples/quick/scenegraph/textureinsgnode/main.qml b/examples/quick/scenegraph/textureinsgnode/main.qml
index 6f6d7d1c1a..f461cb29a8 100644
--- a/examples/quick/scenegraph/textureinsgnode/main.qml
+++ b/examples/quick/scenegraph/textureinsgnode/main.qml
@@ -125,7 +125,7 @@ Item {
anchors.right: renderer.right
anchors.margins: 20
wrapMode: Text.WordWrap
- text: "The blue rectangle with the vintage 'Q' is an FBO, rendered by the application on the scene graph rendering thread. It is displayed using a QSGSimpleTextureNode."
+ text: "The blue rectangle with the vintage 'Q' is an FBO, rendered by the application on the scene graph rendering thread. The FBO is managed and displayed using the QQuickFramebufferObject convenience class."
}
diff --git a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
index edb99b03e8..384e8209c1 100644
--- a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
+++ b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
@@ -286,16 +286,13 @@ application is allowed to draw.
The \l {Scene Graph - OpenGL Under QML} example gives an example on
how to use use these signals.
-The other alternative is to create a FramebufferObject, render into it
-and use the result as a textured node in the scene graph, for instance
-using a QSGSimpleTextureNode. The \l {Scene Graph - Rendering FBOs}
-and \l {Scene Graph - Rendering FBOs in a thread} examples show how
-this can be done in an optimal manner.
-
-A simple way of doing the same is to use a QQuickPaintedItem with
-QQuickPaintedItem::FramebufferObject as render target and by calling
-QPainter::beginNativePainting() before the OpenGL rendering and
-QPainter::endNativePainting() after.
+The other alternative is to create a QQuickFramebufferObject, render
+into it, and let it be displayed in the scene graph as a texture.
+The \l {Scene Graph - Rendering FBOs} example shows how this can be
+done. It is also possible to combine multiple rendering contexts and
+multiple threads to create content to be displayed in the scene graph.
+The \l {Scene Graph - Rendering FBOs in a thread} examples show how
+this can be done.
\warning When mixing OpenGL content with scene graph rendering, it is
important the application does not leave the OpenGL context in a state
diff --git a/src/quick/items/items.pri b/src/quick/items/items.pri
index 5aaf7d3ac6..cbdc5fd80f 100644
--- a/src/quick/items/items.pri
+++ b/src/quick/items/items.pri
@@ -72,7 +72,8 @@ HEADERS += \
$$PWD/qquickitemview_p_p.h \
$$PWD/qquickitemviewtransition_p.h \
$$PWD/qquickscreen_p.h \
- $$PWD/qquickwindowmodule_p.h
+ $$PWD/qquickwindowmodule_p.h \
+ $$PWD/qquickframebufferobject.h
SOURCES += \
$$PWD/qquickevents.cpp \
@@ -123,7 +124,8 @@ SOURCES += \
$$PWD/qquickitemview.cpp \
$$PWD/qquickitemviewtransition.cpp \
$$PWD/qquickwindowmodule.cpp \
- $$PWD/qquickscreen.cpp
+ $$PWD/qquickscreen.cpp \
+ $$PWD/qquickframebufferobject.cpp
SOURCES += \
$$PWD/qquickshadereffect.cpp \
diff --git a/src/quick/items/qquickframebufferobject.cpp b/src/quick/items/qquickframebufferobject.cpp
new file mode 100644
index 0000000000..268c2d5342
--- /dev/null
+++ b/src/quick/items/qquickframebufferobject.cpp
@@ -0,0 +1,397 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 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 "qquickframebufferobject.h"
+
+#include <QtGui/QOpenGLFramebufferObject>
+
+#include <private/qquickitem_p.h>
+
+#include <QSGSimpleTextureNode>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickFramebufferObjectPrivate : public QQuickItemPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickFramebufferObject)
+public:
+ QQuickFramebufferObjectPrivate()
+ : followsItemSize(true)
+ {
+ }
+
+ bool followsItemSize;
+};
+
+/*!
+ * \class QQuickFramebufferObject
+ * \inmodule QtQuick
+ * \since 5.2
+ *
+ * \brief The QQuickFramebufferObject class is a convenience class
+ * for integrating OpenGL rendering using a framebuffer object (FBO)
+ * with Qt Quick.
+ *
+ * On most platforms, the rendering will occur on a dedicated thread.
+ * For this reason, the QQuickFramebufferObject class enforces a strict
+ * separation between the item implementation and the FBO rendering. All
+ * item logic, such as properties and UI-related helper functions needed by
+ * QML should be located in a QQuickFramebufferObject class subclass.
+ * Everything that relates to rendering must be located in the
+ * QQuickFramebufferObject::Renderer class.
+ *
+ * To avoid race conditions and read/write issues from two threads
+ * it is important that the renderer and the item never read or
+ * write shared variables. Communication between the item and the renderer
+ * should primarily happen via the
+ * QQuickFramebufferObject::Renderer::synchronize() function. This function
+ * will be called on the render thread while the GUI thread is blocked.
+ *
+ * Using queued connections or events for communication between item
+ * and renderer is also possible.
+ *
+ * Both the Renderer and the FBO are memory managed internally.
+ *
+ * To render into the FBO, the user should subclass the Renderer class
+ * and reimplement its Renderer::render() function. The Renderer subclass
+ * is returned from createRenderer().
+ *
+ * The size of the FBO will by default adapt to the size of
+ * the item. If a fixed size is preferred, set textureFollowsItemSize
+ * to \c false and return a texture of your choosing from
+ * QQuickFramebufferObject::Renderer::createFramebufferObject().
+ *
+ * \sa {Scene Graph - Rendering FBOs}
+ */
+
+/*!
+ * Constructs a new QQuickFramebufferObject with parent \a parent.
+ */
+QQuickFramebufferObject::QQuickFramebufferObject(QQuickItem *parent) :
+ QQuickItem(*new QQuickFramebufferObjectPrivate, parent)
+{
+ setFlag(ItemHasContents);
+}
+
+/*!
+ * \property QQuickFramebufferObject::textureFollowsItemSize
+ *
+ * This property controls if the size of the FBO's texture should follow
+ * the dimensions of the QQuickFramebufferObject item. When this property
+ * is false, the FBO will be created once the first time it is displayed.
+ * If it is set to true, the FBO will be recreated every time the dimensions
+ * of the item change.
+ *
+ * The default value is \c {true}.
+ */
+
+void QQuickFramebufferObject::setTextureFollowsItemSize(bool follows)
+{
+ Q_D(QQuickFramebufferObject);
+ if (d->followsItemSize == follows)
+ return;
+ d->followsItemSize = follows;
+ emit textureFollowsItemSizeChanged(d->followsItemSize);
+}
+
+bool QQuickFramebufferObject::textureFollowsItemSize() const
+{
+ Q_D(const QQuickFramebufferObject);
+ return d->followsItemSize;
+}
+
+/*!
+ * \internal
+ */
+void QQuickFramebufferObject::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ QQuickItem::geometryChanged(newGeometry, oldGeometry);
+
+ Q_D(QQuickFramebufferObject);
+ if (newGeometry.size() != oldGeometry.size() && d->followsItemSize)
+ update();
+}
+
+class QSGFramebufferObjectNode : public QObject, public QSGSimpleTextureNode
+{
+ Q_OBJECT
+
+public:
+ QSGFramebufferObjectNode()
+ : window(0)
+ , fbo(0)
+ , msDisplayFbo(0)
+ , renderer(0)
+ , renderPending(true)
+ , invalidatePending(false)
+ {
+
+ }
+
+ ~QSGFramebufferObjectNode()
+ {
+ delete renderer;
+ delete texture();
+ delete fbo;
+ delete msDisplayFbo;
+ }
+
+ void scheduleRender()
+ {
+ renderPending = true;
+ window->update();
+ }
+
+public Q_SLOTS:
+ void render()
+ {
+ if (renderPending) {
+ renderPending = false;
+ fbo->bind();
+ glViewport(0, 0, fbo->width(), fbo->height());
+ renderer->render();
+ fbo->bindDefault();
+
+ if (msDisplayFbo)
+ QOpenGLFramebufferObject::blitFramebuffer(msDisplayFbo, fbo);
+ }
+ }
+
+public:
+ QQuickWindow *window;
+ QOpenGLFramebufferObject *fbo;
+ QOpenGLFramebufferObject *msDisplayFbo;
+ QQuickFramebufferObject::Renderer *renderer;
+
+ bool renderPending;
+ bool invalidatePending;
+};
+
+/*!
+ * \internal
+ */
+QSGNode *QQuickFramebufferObject::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
+{
+ QSGFramebufferObjectNode *n = static_cast<QSGFramebufferObjectNode *>(node);
+
+ // We only abort if we never had a node before. This is so that we
+ // don't recreate the renderer object if the thing becomes tiny. In
+ // terms of API it would be horrible if the renderer would go away
+ // that easily so with this logic, the renderer only goes away when
+ // the scenegraph is invalidated or it is removed from the scene.
+ if (!n && (width() <= 0 || height() <= 0))
+ return 0;
+
+ Q_D(QQuickFramebufferObject);
+
+ if (!n) {
+ n = new QSGFramebufferObjectNode;
+ n->window = window();
+ }
+
+ if (!n->renderer) {
+ n->renderer = createRenderer();
+ n->renderer->data = n;
+ connect(window(), SIGNAL(beforeRendering()), n, SLOT(render()));
+ }
+
+ n->renderer->synchronize(this);
+
+ if (n->fbo && (d->followsItemSize || n->invalidatePending)) {
+ if (n->fbo->width() != width() || n->fbo->height() != height()) {
+ delete n->fbo;
+ n->fbo = 0;
+ delete n->msDisplayFbo;
+ n->msDisplayFbo = 0;
+ }
+ }
+
+ if (!n->fbo) {
+ QSize minFboSize = d->sceneGraphContext()->minimumFBOSize();
+ QSize fboSize(qMax<int>(minFboSize.width(), width()),
+ qMax<int>(minFboSize.height(), height()));
+ n->fbo = n->renderer->createFramebufferObject(fboSize);
+
+ GLuint displayTexture = n->fbo->texture();
+
+ if (n->fbo->format().samples() > 0) {
+ n->msDisplayFbo = new QOpenGLFramebufferObject(n->fbo->size());
+ displayTexture = n->msDisplayFbo->texture();
+ }
+
+ n->setTexture(window()->createTextureFromId(displayTexture,
+ n->fbo->size(),
+ QQuickWindow::TextureHasAlphaChannel));
+ }
+
+ n->setFiltering(d->smooth ? QSGTexture::Linear : QSGTexture::Nearest);
+ n->setRect(0, 0, width(), height());
+
+ n->scheduleRender();
+
+ return n;
+}
+
+/*!
+ * \class QQuickFramebufferObject::Renderer
+ * \inmodule QtQuick
+ * \since 5.2
+ *
+ * The QQuickFramebufferObject::Renderer class is used to implement the
+ * rendering logic of a QQuickFramebufferObject.
+ */
+
+/*!
+ * Constructs a new renderer.
+ *
+ * This function is called during the scene graph sync phase when the
+ * GUI thread is blocked.
+ */
+QQuickFramebufferObject::Renderer::Renderer()
+ : data(0)
+{
+}
+
+/*!
+ * \fn QQuickFramebufferObject::Renderer *QQuickFramebufferObject::createRenderer() const
+ *
+ * Reimplement this function to create a renderer used to render into the FBO.
+ *
+ * This function will be called on the rendering thread while the GUI thread is
+ * blocked.
+ */
+
+/*!
+ * The Renderer is automatically deleted when the scene graph resources
+ * for the QQuickFramebufferObject item is cleaned up.
+ *
+ * This function is called on the rendering thread.
+ */
+QQuickFramebufferObject::Renderer::~Renderer()
+{
+}
+
+/*!
+ * Returns the framebuffer object currently being rendered to.
+ */
+QOpenGLFramebufferObject *QQuickFramebufferObject::Renderer::framebufferObject() const
+{
+ return data ? ((QSGFramebufferObjectNode *) data)->fbo : 0;
+}
+
+/*!
+ * \fn void QQuickFramebufferObject::Renderer::render()
+ *
+ * This function is called when the FBO should be rendered into. The framebuffer
+ * is bound at this point and the \c glViewport has been set up to match
+ * the FBO size.
+ *
+ * The FBO will be automatically unbound after the function returns.
+ */
+
+/*!
+ * This function is called as a result of QQuickFramebufferObject::update().
+ *
+ * Use this function to update the renderer with changes that have occurred
+ * in the item. \a item is the item that instantiated this renderer. The function
+ * is called once before the FBO is created.
+ *
+ * \e {For instance, if the item has a color property which is controlled by
+ * QML, one should call QQuickFramebufferObject::update() and use
+ * synchronize() to copy the new color into the renderer so that it can be
+ * used to render the next frame.}
+ *
+ * This function is the only place when it is safe for the renderer and the
+ * item to read and write each others members.
+ */
+void QQuickFramebufferObject::Renderer::synchronize(QQuickFramebufferObject *item)
+{
+ Q_UNUSED(item);
+}
+
+/*!
+ * Call this function during synchronize() to invalidate the current FBO. This
+ * will result in a new FBO being created with createFramebufferObject().
+ */
+void QQuickFramebufferObject::Renderer::invalidateFramebufferObject()
+{
+ if (data)
+ ((QSGFramebufferObjectNode *) data)->invalidatePending = true;
+}
+
+/*!
+ * This function is called when a new FBO is needed. This happens on the
+ * initial frame. If QQuickFramebufferObject::textureFollowsItemSize is set to true,
+ * it is called again every time the dimensions of the item changes.
+ *
+ * The returned FBO can have any attachment. If the QOpenGLFramebufferObjectFormat
+ * indicates that the FBO should be multisampled, the internal implementation
+ * of the Renderer will allocate a second FBO and blit the multisampled FBO
+ * into the FBO used to display the texture.
+ *
+ * \note Some hardware has issues with small FBO sizes. \a size takes that into account, so
+ * be cautious when overriding the size with a fixed size. A minimal size of 64x64 should
+ * always work.
+ */
+QOpenGLFramebufferObject *QQuickFramebufferObject::Renderer::createFramebufferObject(const QSize &size)
+{
+ return new QOpenGLFramebufferObject(size);
+}
+
+/*!
+ * Call this function when the FBO should be rendered again.
+ *
+ * This function can be called from render() to force the FBO to be rendered
+ * again before the next frame.
+ *
+ * \note This function should be used from inside the renderer. To update
+ * the item on the GUI thread, use QQuickFramebufferObject::update().
+ */
+void QQuickFramebufferObject::Renderer::update()
+{
+ if (data)
+ ((QSGFramebufferObjectNode *) data)->scheduleRender();
+}
+
+
+#include "qquickframebufferobject.moc"
+
+QT_END_NAMESPACE
diff --git a/src/quick/items/qquickframebufferobject.h b/src/quick/items/qquickframebufferobject.h
new file mode 100644
index 0000000000..789679d74c
--- /dev/null
+++ b/src/quick/items/qquickframebufferobject.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 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 QQUICKFRAMEBUFFEROBJECT_H
+#define QQUICKFRAMEBUFFEROBJECT_H
+
+#include <QQuickItem>
+
+QT_BEGIN_NAMESPACE
+
+
+class QOpenGLFramebufferObject;
+class QQuickFramebufferObjectPrivate;
+class QSGFramebufferObjectNode;
+
+class Q_QUICK_EXPORT QQuickFramebufferObject : public QQuickItem
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QQuickFramebufferObject)
+
+ Q_PROPERTY(bool textureFollowsItemSize READ textureFollowsItemSize WRITE setTextureFollowsItemSize NOTIFY textureFollowsItemSizeChanged)
+
+public:
+
+ class Q_QUICK_EXPORT Renderer {
+ protected:
+ Renderer();
+ virtual ~Renderer();
+ virtual void render() = 0;
+ virtual QOpenGLFramebufferObject *createFramebufferObject(const QSize &size);
+ virtual void synchronize(QQuickFramebufferObject *);
+ QOpenGLFramebufferObject *framebufferObject() const;
+ void update();
+ void invalidateFramebufferObject();
+ private:
+ friend class QSGFramebufferObjectNode;
+ friend class QQuickFramebufferObject;
+ void *data;
+ };
+
+ QQuickFramebufferObject(QQuickItem *parent = 0);
+
+ bool textureFollowsItemSize() const;
+ void setTextureFollowsItemSize(bool follows);
+
+ virtual Renderer *createRenderer() const = 0;
+
+protected:
+ void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry);
+
+protected:
+ QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) Q_DECL_OVERRIDE;
+
+Q_SIGNALS:
+ void textureFollowsItemSizeChanged(bool);
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKFRAMEBUFFEROBJECT_H
diff --git a/tests/auto/quick/qquickframebufferobject/data/testStuff.qml b/tests/auto/quick/qquickframebufferobject/data/testStuff.qml
new file mode 100644
index 0000000000..b4ac4e81eb
--- /dev/null
+++ b/tests/auto/quick/qquickframebufferobject/data/testStuff.qml
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the tests 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$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import FBOItem 1.0
+
+Item {
+ width: 400
+ height: 400
+
+ FBOItem {
+ objectName: "fbo"
+ color: "red"
+ width: 100
+ height: 100
+ }
+
+}
diff --git a/tests/auto/quick/qquickframebufferobject/qquickframebufferobject.pro b/tests/auto/quick/qquickframebufferobject/qquickframebufferobject.pro
new file mode 100644
index 0000000000..612b6f7283
--- /dev/null
+++ b/tests/auto/quick/qquickframebufferobject/qquickframebufferobject.pro
@@ -0,0 +1,15 @@
+CONFIG += testcase
+TARGET = tst_qquickframebufferobject
+SOURCES += tst_qquickframebufferobject.cpp
+
+TESTDATA = data/*
+include(../../shared/util.pri)
+
+macx:CONFIG -= app_bundle
+
+CONFIG += parallel_test
+QT += quick testlib
+
+OTHER_FILES += \
+ data/testStuff.qml
+
diff --git a/tests/auto/quick/qquickframebufferobject/tst_qquickframebufferobject.cpp b/tests/auto/quick/qquickframebufferobject/tst_qquickframebufferobject.cpp
new file mode 100644
index 0000000000..b7b58c6d3d
--- /dev/null
+++ b/tests/auto/quick/qquickframebufferobject/tst_qquickframebufferobject.cpp
@@ -0,0 +1,255 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the test suite 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 <qtest.h>
+
+#include <QtQuick/qquickitem.h>
+#include <QtQuick/qquickview.h>
+#include <QtGui/qopenglcontext.h>
+#include <QtGui/qopenglframebufferobject.h>
+
+#include <QtQuick/QQuickFramebufferObject>
+
+#include "../../shared/util.h"
+
+#ifndef GL_MAX_SAMPLES
+#define GL_MAX_SAMPLES 0x8D57
+#endif
+
+struct FrameInfo {
+ int renderCount;
+ int createFBOCount;
+ bool msaaSupported;
+ bool msaaEnabled;
+ QSize fboSize;
+} frameInfo;
+
+class ColorRenderer : public QQuickFramebufferObject::Renderer
+{
+public:
+ void render();
+ void synchronize(QQuickFramebufferObject *item);
+ QOpenGLFramebufferObject *createFramebufferObject(const QSize &size);
+
+ QSize textureSize;
+ QColor color;
+ bool msaa;
+};
+
+class FBOItem : public QQuickFramebufferObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(bool msaa READ msaa WRITE setMsaa NOTIFY msaaChanged)
+ Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
+
+ Q_PROPERTY(QSize textureSize READ textureSize WRITE setTextureSize NOTIFY textureSizeChanged)
+
+public:
+ Renderer *createRenderer() const { return new ColorRenderer(); }
+
+ void setColor(const QColor &color) { m_color = color; colorChanged(m_color); }
+ QColor color() const { return m_color; }
+
+ void setMsaa(bool msaa) { m_msaa = msaa; msaaChanged(m_msaa); }
+ bool msaa() const { return m_msaa; }
+
+ void setTextureSize(const QSize &size) { m_textureSize = size; textureSizeChanged(m_textureSize); }
+ QSize textureSize() const { return m_textureSize; }
+
+signals:
+ void colorChanged(const QColor &color);
+ void msaaChanged(bool msaa);
+ void textureSizeChanged(const QSize &size);
+
+public:
+ bool m_msaa;
+ QColor m_color;
+ QSize m_textureSize;
+
+};
+
+void ColorRenderer::render()
+{
+ glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF());
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ int maxSamples = 0;
+ glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
+
+ QByteArray extensions((const char *) glGetString(GL_EXTENSIONS));
+ frameInfo.msaaSupported = maxSamples > 0
+ && extensions.contains("GL_EXT_framebuffer_multisample")
+ && extensions.contains("GL_EXT_framebuffer_blit");
+
+ int samples;
+ glGetIntegerv(GL_SAMPLES, &samples);
+ frameInfo.msaaEnabled = samples > 0;
+
+ frameInfo.fboSize = framebufferObject()->size();
+
+ frameInfo.renderCount++;
+}
+
+void ColorRenderer::synchronize(QQuickFramebufferObject *item)
+{
+ FBOItem *fboItem = qobject_cast<FBOItem *>(item);
+ color = fboItem->color();
+ msaa = fboItem->msaa();
+ if (textureSize != fboItem->textureSize()) {
+ textureSize = fboItem->textureSize();
+ invalidateFramebufferObject();
+ }
+}
+
+QOpenGLFramebufferObject *ColorRenderer::createFramebufferObject(const QSize &size)
+{
+ frameInfo.createFBOCount++;
+ QOpenGLFramebufferObjectFormat format;
+ if (msaa)
+ format.setSamples(4);
+ QSize s = textureSize.isValid() ? textureSize : size;
+ return new QOpenGLFramebufferObject(s, format);
+}
+
+class tst_QQuickFramebufferObject: public QQmlDataTest
+{
+ Q_OBJECT
+public:
+private slots:
+ void testThatStuffWorks_data();
+ void testThatStuffWorks();
+
+ void testInvalidate();
+};
+
+void tst_QQuickFramebufferObject::testThatStuffWorks_data()
+{
+ QTest::addColumn<uint>("color");
+ QTest::addColumn<bool>("msaa");
+ QTest::addColumn<QSize>("textureSize");
+
+ QTest::newRow("red, !aa, item-size") << 0xffff0000 << false << QSize();
+ QTest::newRow("green, !aa, 80x80") << 0xff00ff00 << false << QSize(80, 80);
+ QTest::newRow("blue, aa, item-size") << 0xff0000ff << true << QSize();
+ QTest::newRow("pink, aa, 80x80") << 0xffff00ff << true << QSize(80, 80);
+}
+
+void tst_QQuickFramebufferObject::testThatStuffWorks()
+{
+ QFETCH(uint, color);
+ QFETCH(bool, msaa);
+ QFETCH(QSize, textureSize);
+
+ frameInfo.renderCount = 0;
+ frameInfo.msaaEnabled = false;
+ frameInfo.msaaSupported = false;
+ frameInfo.fboSize = QSize();
+
+ qmlRegisterType<FBOItem>("FBOItem", 1, 0, "FBOItem");
+
+ QQuickView view;
+ view.setSource(QUrl::fromLocalFile("data/testStuff.qml"));
+
+ FBOItem *item = view.rootObject()->findChild<FBOItem *>("fbo");
+
+ item->setColor(color);
+ if (textureSize.isValid()) {
+ item->setTextureFollowsItemSize(false);
+ item->setTextureSize(textureSize);
+ }
+ item->setMsaa(msaa);
+
+ view.show();
+ QTest::qWaitForWindowExposed(&view);
+
+ QImage result = view.grabWindow();
+
+ QCOMPARE(frameInfo.renderCount, 1);
+ QCOMPARE(result.pixel(0, 0), color);
+ if (textureSize.isValid())
+ QCOMPARE(frameInfo.fboSize, textureSize);
+ else
+ QCOMPARE(frameInfo.fboSize, QSize(item->width(), item->height()) * view.devicePixelRatio());
+ if (frameInfo.msaaSupported && msaa)
+ QVERIFY(frameInfo.msaaEnabled);
+
+ // Resize the item and grab again
+ item->setSize(QSize(200, 200));
+ result = view.grabWindow();
+
+ QCOMPARE(frameInfo.renderCount, 2);
+ QCOMPARE(result.pixel(150, 150), color);
+ if (textureSize.isValid())
+ QCOMPARE(frameInfo.fboSize, textureSize);
+ else
+ QCOMPARE(frameInfo.fboSize, QSize(item->width(), item->height()) * view.devicePixelRatio());
+ if (frameInfo.msaaSupported && msaa)
+ QVERIFY(frameInfo.msaaEnabled);
+}
+
+void tst_QQuickFramebufferObject::testInvalidate()
+{
+ qmlRegisterType<FBOItem>("FBOItem", 1, 0, "FBOItem");
+
+ QQuickView view;
+ view.setSource(QUrl::fromLocalFile("data/testStuff.qml"));
+
+ FBOItem *item = view.rootObject()->findChild<FBOItem *>("fbo");
+ item->setTextureFollowsItemSize(false);
+ item->setTextureSize(QSize(200, 200));
+
+ view.show();
+ QTest::qWaitForWindowExposed(&view);
+
+ QCOMPARE(frameInfo.fboSize, QSize(200, 200));
+
+ frameInfo.createFBOCount = 0;
+ item->setTextureSize(QSize(300, 300));
+ item->update();
+
+ QTRY_COMPARE(frameInfo.createFBOCount, 1);
+ QCOMPARE(frameInfo.fboSize, QSize(300, 300));
+}
+
+QTEST_MAIN(tst_QQuickFramebufferObject)
+
+#include "tst_qquickframebufferobject.moc"
diff --git a/tests/auto/quick/quick.pro b/tests/auto/quick/quick.pro
index f8c19bae63..92bbe7e0fb 100644
--- a/tests/auto/quick/quick.pro
+++ b/tests/auto/quick/quick.pro
@@ -44,6 +44,7 @@ QUICKTESTS = \
qquickflickable \
qquickflipable \
qquickfocusscope \
+ qquickframebufferobject \
qquickgridview \
qquickimage \
qquickitem \