--- /dev/null
- virtual void bind() const;
+ /****************************************************************************
+ **
+ ** 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 "qsgdefaultlayer_p.h"
+
+ #include <private/qqmlglobal_p.h>
+ #include <private/qsgrenderer_p.h>
+
+ #include <QOpenGLFramebufferObject>
+
+ #ifdef QSG_DEBUG_FBO_OVERLAY
+ DEFINE_BOOL_CONFIG_OPTION(qmlFboOverlay, QML_FBO_OVERLAY)
+ #endif
+ DEFINE_BOOL_CONFIG_OPTION(qmlFboFlushBeforeDetach, QML_FBO_FLUSH_BEFORE_DETACH)
+
+ namespace
+ {
+ class BindableFbo : public QSGBindable
+ {
+ public:
+ BindableFbo(QOpenGLFramebufferObject *fbo, QSGDepthStencilBuffer *depthStencil);
+ virtual ~BindableFbo();
++ void bind() const Q_DECL_OVERRIDE;
+ private:
+ QOpenGLFramebufferObject *m_fbo;
+ QSGDepthStencilBuffer *m_depthStencil;
+ };
+
+ BindableFbo::BindableFbo(QOpenGLFramebufferObject *fbo, QSGDepthStencilBuffer *depthStencil)
+ : m_fbo(fbo)
+ , m_depthStencil(depthStencil)
+ {
+ }
+
+ BindableFbo::~BindableFbo()
+ {
+ if (qmlFboFlushBeforeDetach())
+ QOpenGLContext::currentContext()->functions()->glFlush();
+ if (m_depthStencil)
+ m_depthStencil->detach();
+ }
+
+ void BindableFbo::bind() const
+ {
+ m_fbo->bind();
+ if (m_depthStencil)
+ m_depthStencil->attach();
+ }
+ }
+
+ QSGDefaultLayer::QSGDefaultLayer(QSGRenderContext *context)
+ : QSGLayer()
+ , m_item(0)
+ , m_shaderSourceNode(0)
+ , m_device_pixel_ratio(1)
+ , m_format(GL_RGBA)
+ , m_renderer(0)
+ , m_fbo(0)
+ , m_secondaryFbo(0)
+ , m_transparentTexture(0)
+ #ifdef QSG_DEBUG_FBO_OVERLAY
+ , m_debugOverlay(0)
+ #endif
+ , m_context(context)
+ , m_mipmap(false)
+ , m_live(true)
+ , m_recursive(false)
+ , m_dirtyTexture(true)
+ , m_multisamplingChecked(false)
+ , m_multisampling(false)
+ , m_grab(false)
+ {
+ }
+
+ QSGDefaultLayer::~QSGDefaultLayer()
+ {
+ invalidated();
+ }
+
+ void QSGDefaultLayer::invalidated()
+ {
+ delete m_renderer;
+ m_renderer = 0;
+ delete m_fbo;
+ delete m_secondaryFbo;
+ m_fbo = m_secondaryFbo = 0;
+ #ifdef QSG_DEBUG_FBO_OVERLAY
+ delete m_debugOverlay;
+ m_debugOverlay = 0;
+ #endif
+ if (m_transparentTexture) {
+ QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &m_transparentTexture);
+ m_transparentTexture = 0;
+ }
+ }
+
+ int QSGDefaultLayer::textureId() const
+ {
+ return m_fbo ? m_fbo->texture() : 0;
+ }
+
+ bool QSGDefaultLayer::hasAlphaChannel() const
+ {
+ return m_format != GL_RGB;
+ }
+
+ bool QSGDefaultLayer::hasMipmaps() const
+ {
+ return m_mipmap;
+ }
+
+
+ void QSGDefaultLayer::bind()
+ {
+ #ifndef QT_NO_DEBUG
+ if (!m_recursive && m_fbo && ((m_multisampling && m_secondaryFbo->isBound()) || m_fbo->isBound()))
+ qWarning("ShaderEffectSource: \'recursive\' must be set to true when rendering recursively.");
+ #endif
+ QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
+ if (!m_fbo && m_format == GL_RGBA) {
+ if (m_transparentTexture == 0) {
+ funcs->glGenTextures(1, &m_transparentTexture);
+ funcs->glBindTexture(GL_TEXTURE_2D, m_transparentTexture);
+ const uint zero = 0;
+ funcs->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &zero);
+ } else {
+ funcs->glBindTexture(GL_TEXTURE_2D, m_transparentTexture);
+ }
+ } else {
+ funcs->glBindTexture(GL_TEXTURE_2D, m_fbo ? m_fbo->texture() : 0);
+ updateBindOptions();
+ }
+ }
+
+ bool QSGDefaultLayer::updateTexture()
+ {
+ bool doGrab = (m_live || m_grab) && m_dirtyTexture;
+ if (doGrab)
+ grab();
+ if (m_grab)
+ emit scheduledUpdateCompleted();
+ m_grab = false;
+ return doGrab;
+ }
+
+ void QSGDefaultLayer::setHasMipmaps(bool mipmap)
+ {
+ if (mipmap == m_mipmap)
+ return;
+ m_mipmap = mipmap;
+ if (m_mipmap && m_fbo && !m_fbo->format().mipmap())
+ markDirtyTexture();
+ }
+
+
+ void QSGDefaultLayer::setItem(QSGNode *item)
+ {
+ if (item == m_item)
+ return;
+ m_item = item;
+
+ if (m_live && !m_item) {
+ delete m_fbo;
+ delete m_secondaryFbo;
+ m_fbo = m_secondaryFbo = 0;
+ m_depthStencilBuffer.clear();
+ }
+
+ markDirtyTexture();
+ }
+
+ void QSGDefaultLayer::setRect(const QRectF &rect)
+ {
+ if (rect == m_rect)
+ return;
+ m_rect = rect;
+ markDirtyTexture();
+ }
+
+ void QSGDefaultLayer::setSize(const QSize &size)
+ {
+ if (size == m_size)
+ return;
+ m_size = size;
+
+ if (m_live && m_size.isNull()) {
+ delete m_fbo;
+ delete m_secondaryFbo;
+ m_fbo = m_secondaryFbo = 0;
+ m_depthStencilBuffer.clear();
+ }
+
+ markDirtyTexture();
+ }
+
+ void QSGDefaultLayer::setFormat(GLenum format)
+ {
+ if (format == m_format)
+ return;
+ m_format = format;
+ markDirtyTexture();
+ }
+
+ void QSGDefaultLayer::setLive(bool live)
+ {
+ if (live == m_live)
+ return;
+ m_live = live;
+
+ if (m_live && (!m_item || m_size.isNull())) {
+ delete m_fbo;
+ delete m_secondaryFbo;
+ m_fbo = m_secondaryFbo = 0;
+ m_depthStencilBuffer.clear();
+ }
+
+ markDirtyTexture();
+ }
+
+ void QSGDefaultLayer::scheduleUpdate()
+ {
+ if (m_grab)
+ return;
+ m_grab = true;
+ if (m_dirtyTexture) {
+ emit updateRequested();
+ if (m_shaderSourceNode)
+ m_shaderSourceNode->markDirty(QSGNode::DirtyMaterial);
+ }
+ }
+
+ void QSGDefaultLayer::setRecursive(bool recursive)
+ {
+ m_recursive = recursive;
+ }
+
+ void QSGDefaultLayer::markDirtyTexture()
+ {
+ m_dirtyTexture = true;
+ if (m_live || m_grab) {
+ emit updateRequested();
+ if (m_shaderSourceNode)
+ m_shaderSourceNode->markDirty(QSGNode::DirtyMaterial);
+ }
+ }
+
+ void QSGDefaultLayer::grab()
+ {
+ if (!m_item || m_size.isNull()) {
+ delete m_fbo;
+ delete m_secondaryFbo;
+ m_fbo = m_secondaryFbo = 0;
+ m_depthStencilBuffer.clear();
+ m_dirtyTexture = false;
+ return;
+ }
+ QSGNode *root = m_item;
+ while (root->firstChild() && root->type() != QSGNode::RootNodeType)
+ root = root->firstChild();
+ if (root->type() != QSGNode::RootNodeType)
+ return;
+
+ if (!m_renderer) {
+ m_renderer = m_context->createRenderer();
+ connect(m_renderer, SIGNAL(sceneGraphChanged()), this, SLOT(markDirtyTextureLater()));
+ }
+ m_renderer->setDevicePixelRatio(m_device_pixel_ratio);
+ m_renderer->setRootNode(static_cast<QSGRootNode *>(root));
+
+ QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
+ bool deleteFboLater = false;
+ if (!m_fbo || m_fbo->size() != m_size || m_fbo->format().internalTextureFormat() != m_format
+ || (!m_fbo->format().mipmap() && m_mipmap))
+ {
+ if (!m_multisamplingChecked) {
+ if (m_context->openglContext()->format().samples() <= 1) {
+ m_multisampling = false;
+ } else {
+ const QSet<QByteArray> extensions = m_context->openglContext()->extensions();
+ m_multisampling = extensions.contains(QByteArrayLiteral("GL_EXT_framebuffer_multisample"))
+ && extensions.contains(QByteArrayLiteral("GL_EXT_framebuffer_blit"));
+ }
+ m_multisamplingChecked = true;
+ }
+ if (m_multisampling) {
+ // Don't delete the FBO right away in case it is used recursively.
+ deleteFboLater = true;
+ delete m_secondaryFbo;
+ QOpenGLFramebufferObjectFormat format;
+
+ format.setInternalTextureFormat(m_format);
+ format.setSamples(m_context->openglContext()->format().samples());
+ m_secondaryFbo = new QOpenGLFramebufferObject(m_size, format);
+ m_depthStencilBuffer = m_context->depthStencilBufferForFbo(m_secondaryFbo);
+ } else {
+ QOpenGLFramebufferObjectFormat format;
+ format.setInternalTextureFormat(m_format);
+ format.setMipmap(m_mipmap);
+ if (m_recursive) {
+ deleteFboLater = true;
+ delete m_secondaryFbo;
+ m_secondaryFbo = new QOpenGLFramebufferObject(m_size, format);
+ funcs->glBindTexture(GL_TEXTURE_2D, m_secondaryFbo->texture());
+ updateBindOptions(true);
+ m_depthStencilBuffer = m_context->depthStencilBufferForFbo(m_secondaryFbo);
+ } else {
+ delete m_fbo;
+ delete m_secondaryFbo;
+ m_fbo = new QOpenGLFramebufferObject(m_size, format);
+ m_secondaryFbo = 0;
+ funcs->glBindTexture(GL_TEXTURE_2D, m_fbo->texture());
+ updateBindOptions(true);
+ m_depthStencilBuffer = m_context->depthStencilBufferForFbo(m_fbo);
+ }
+ }
+ }
+
+ if (m_recursive && !m_secondaryFbo) {
+ // m_fbo already created, m_recursive was just set.
+ Q_ASSERT(m_fbo);
+ Q_ASSERT(!m_multisampling);
+
+ m_secondaryFbo = new QOpenGLFramebufferObject(m_size, m_fbo->format());
+ funcs->glBindTexture(GL_TEXTURE_2D, m_secondaryFbo->texture());
+ updateBindOptions(true);
+ }
+
+ // Render texture.
+ root->markDirty(QSGNode::DirtyForceUpdate); // Force matrix, clip and opacity update.
+ m_renderer->nodeChanged(root, QSGNode::DirtyForceUpdate); // Force render list update.
+
+ #ifdef QSG_DEBUG_FBO_OVERLAY
+ if (qmlFboOverlay()) {
+ if (!m_debugOverlay)
+ m_debugOverlay = new QSGSimpleRectNode();
+ m_debugOverlay->setRect(QRectF(0, 0, m_size.width(), m_size.height()));
+ m_debugOverlay->setColor(QColor(0xff, 0x00, 0x80, 0x40));
+ root->appendChildNode(m_debugOverlay);
+ }
+ #endif
+
+ m_dirtyTexture = false;
+
+ m_renderer->setDeviceRect(m_size);
+ m_renderer->setViewportRect(m_size);
+ QRectF mirrored(m_rect.left(), m_rect.bottom(), m_rect.width(), -m_rect.height());
+ m_renderer->setProjectionMatrixToRect(mirrored);
+ m_renderer->setClearColor(Qt::transparent);
+
+ if (m_multisampling) {
+ m_renderer->renderScene(BindableFbo(m_secondaryFbo, m_depthStencilBuffer.data()));
+
+ if (deleteFboLater) {
+ delete m_fbo;
+ QOpenGLFramebufferObjectFormat format;
+ format.setInternalTextureFormat(m_format);
+ format.setAttachment(QOpenGLFramebufferObject::NoAttachment);
+ format.setMipmap(m_mipmap);
+ format.setSamples(0);
+ m_fbo = new QOpenGLFramebufferObject(m_size, format);
+ funcs->glBindTexture(GL_TEXTURE_2D, m_fbo->texture());
+ updateBindOptions(true);
+ }
+
+ QRect r(QPoint(), m_size);
+ QOpenGLFramebufferObject::blitFramebuffer(m_fbo, r, m_secondaryFbo, r);
+ } else {
+ if (m_recursive) {
+ m_renderer->renderScene(BindableFbo(m_secondaryFbo, m_depthStencilBuffer.data()));
+
+ if (deleteFboLater) {
+ delete m_fbo;
+ QOpenGLFramebufferObjectFormat format;
+ format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
+ format.setInternalTextureFormat(m_format);
+ format.setMipmap(m_mipmap);
+ m_fbo = new QOpenGLFramebufferObject(m_size, format);
+ funcs->glBindTexture(GL_TEXTURE_2D, m_fbo->texture());
+ updateBindOptions(true);
+ }
+ qSwap(m_fbo, m_secondaryFbo);
+ } else {
+ m_renderer->renderScene(BindableFbo(m_fbo, m_depthStencilBuffer.data()));
+ }
+ }
+
+ if (m_mipmap) {
+ funcs->glBindTexture(GL_TEXTURE_2D, textureId());
+ funcs->glGenerateMipmap(GL_TEXTURE_2D);
+ }
+
+ root->markDirty(QSGNode::DirtyForceUpdate); // Force matrix, clip, opacity and render list update.
+
+ #ifdef QSG_DEBUG_FBO_OVERLAY
+ if (qmlFboOverlay())
+ root->removeChildNode(m_debugOverlay);
+ #endif
+ if (m_recursive)
+ markDirtyTexture(); // Continuously update if 'live' and 'recursive'.
+ }
+
+ QImage QSGDefaultLayer::toImage() const
+ {
+ if (m_fbo)
+ return m_fbo->toImage();
+
+ return QImage();
+ }
--- /dev/null
- virtual bool updateTexture();
+ /****************************************************************************
+ **
+ ** 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 QSGDEFAULTLAYER_P_H
+ #define QSGDEFAULTLAYER_P_H
+
+ #include <private/qsgadaptationlayer_p.h>
+ #include <private/qsgcontext_p.h>
+ #include <qsgsimplerectnode.h>
+
+ #define QSG_DEBUG_FBO_OVERLAY
+
+ class Q_QUICK_PRIVATE_EXPORT QSGDefaultLayer : public QSGLayer
+ {
+ Q_OBJECT
+ public:
+ QSGDefaultLayer(QSGRenderContext *context);
+ ~QSGDefaultLayer();
+
++ bool updateTexture() Q_DECL_OVERRIDE;
+
+ // The item's "paint node", not effect node.
+ QSGNode *item() const { return m_item; }
+ void setItem(QSGNode *item);
+
+ void setShaderSourceNode(QSGNode *node) { m_shaderSourceNode = node; }
+
+ QRectF rect() const { return m_rect; }
+ void setRect(const QRectF &rect);
+
+ QSize size() const { return m_size; }
+ void setSize(const QSize &size);
+
+ void setHasMipmaps(bool mipmap);
+
+ void bind();
+
+ bool hasAlphaChannel() const;
+ bool hasMipmaps() const;
+ int textureId() const;
+ QSize textureSize() const { return m_size; }
+
+ GLenum format() const { return m_format; }
+ void setFormat(GLenum format);
+
+ bool live() const { return bool(m_live); }
+ void setLive(bool live);
+
+ bool recursive() const { return bool(m_recursive); }
+ void setRecursive(bool recursive);
+
+ void setDevicePixelRatio(qreal ratio) { m_device_pixel_ratio = ratio; }
+
+ void scheduleUpdate();
+
+ QImage toImage() const;
+
+ public Q_SLOTS:
+ void markDirtyTexture();
+ void invalidated();
+
+ private:
+ void grab();
+
+ QSGNode *m_item;
+ QSGNode *m_shaderSourceNode;
+ QRectF m_rect;
+ QSize m_size;
+ qreal m_device_pixel_ratio;
+ GLenum m_format;
+
+ QSGRenderer *m_renderer;
+ QOpenGLFramebufferObject *m_fbo;
+ QOpenGLFramebufferObject *m_secondaryFbo;
+ QSharedPointer<QSGDepthStencilBuffer> m_depthStencilBuffer;
+
+ GLuint m_transparentTexture;
+
+ #ifdef QSG_DEBUG_FBO_OVERLAY
+ QSGSimpleRectNode *m_debugOverlay;
+ #endif
+
+ QSGRenderContext *m_context;
+
+ uint m_mipmap : 1;
+ uint m_live : 1;
+ uint m_recursive : 1;
+ uint m_dirtyTexture : 1;
+ uint m_multisamplingChecked : 1;
+ uint m_multisampling : 1;
+ uint m_grab : 1;
+ };
+
+ #endif // QSGDEFAULTLAYER_P_H