Cleanup: Separate the sub-tree layering out of QtQuick into the scene graph

This basically renames QQuickShaderEffectTexture to QSGDefaultLayer and
introduces QSGLayer as interface to be used. QQuickShaderEffectTexture is
generic for the scene graph and has no QtQuick dependencies. The interface
separation allows scene graph backends to customize layers.

Change-Id: I9a7f37addaa4b80a34ff9a1456b0cb9b16d4e9f3
Reviewed-by: Gunnar Sletta <gunnar.sletta@jollamobile.com>
This commit is contained in:
Simon Hausmann 2014-08-11 13:21:11 +02:00
parent 253e9257e1
commit 15ee12508e
15 changed files with 663 additions and 516 deletions

View File

@ -65,10 +65,10 @@ DesignerSupport::DesignerSupport()
DesignerSupport::~DesignerSupport()
{
QHash<QQuickItem*, QQuickShaderEffectTexture*>::iterator iterator;
QHash<QQuickItem*, QSGLayer*>::iterator iterator;
for (iterator = m_itemTextureHash.begin(); iterator != m_itemTextureHash.end(); ++iterator) {
QQuickShaderEffectTexture *texture = iterator.value();
QSGLayer *texture = iterator.value();
QQuickItem *item = iterator.key();
QQuickItemPrivate::get(item)->derefFromEffectItem(true);
delete texture;
@ -86,7 +86,8 @@ void DesignerSupport::refFromEffectItem(QQuickItem *referencedItem, bool hide)
Q_ASSERT(QQuickItemPrivate::get(referencedItem)->rootNode());
if (!m_itemTextureHash.contains(referencedItem)) {
QQuickShaderEffectTexture *texture = new QQuickShaderEffectTexture(referencedItem);
QSGRenderContext *rc = QQuickWindowPrivate::get(referencedItem->window())->context;
QSGLayer *texture = rc->sceneGraphContext()->createLayer(rc);
texture->setLive(true);
texture->setItem(QQuickItemPrivate::get(referencedItem)->rootNode());
@ -123,7 +124,7 @@ QImage DesignerSupport::renderImageForItem(QQuickItem *referencedItem, const QRe
return QImage();
}
QQuickShaderEffectTexture *renderTexture = m_itemTextureHash.value(referencedItem);
QSGLayer *renderTexture = m_itemTextureHash.value(referencedItem);
Q_ASSERT(renderTexture);
if (renderTexture == 0)

View File

@ -62,7 +62,7 @@
QT_BEGIN_NAMESPACE
class QQuickItem;
class QQuickShaderEffectTexture;
class QSGLayer;
class QImage;
class QTransform;
class QQmlContext;
@ -154,7 +154,7 @@ public:
static void polishItems(QQuickWindow *window);
private:
QHash<QQuickItem*, QQuickShaderEffectTexture*> m_itemTextureHash;
QHash<QQuickItem*, QSGLayer*> m_itemTextureHash;
};
QT_END_NAMESPACE

View File

@ -49,7 +49,7 @@
QT_BEGIN_NAMESPACE
class QQuickCanvasItem;
class QSGDynamicTexture;
class QSGLayer;
class QQuickCanvasContextPrivate;
class QQuickCanvasContext : public QObject

View File

@ -91,7 +91,7 @@ public:
QPointer<QQuickItem> item;
QPointer<QQuickWindow> window;
QQuickShaderEffectTexture *texture;
QSGLayer *texture;
QSizeF itemSize;
QSize textureSize;
};
@ -228,7 +228,8 @@ void QQuickItemGrabResult::setup()
return;
}
d->texture = new QQuickShaderEffectTexture(d->item);
QSGRenderContext *rc = QQuickWindowPrivate::get(d->window.data())->context;
d->texture = rc->sceneGraphContext()->createLayer(rc);
d->texture->setItem(QQuickItemPrivate::get(d->item)->itemNode());
d->itemSize = QSizeF(d->item->width(), d->item->height());
}

View File

@ -418,7 +418,7 @@ void QQuickShaderEffectMaterial::updateTextures() const
{
for (int i = 0; i < textureProviders.size(); ++i) {
if (QSGTextureProvider *provider = textureProviders.at(i)) {
if (QSGDynamicTexture *texture = qobject_cast<QSGDynamicTexture *>(provider->texture()))
if (QSGLayer *texture = qobject_cast<QSGLayer *>(provider->texture()))
texture->updateTexture();
}
}

View File

@ -54,44 +54,6 @@
QT_BEGIN_NAMESPACE
DEFINE_BOOL_CONFIG_OPTION(qmlFboOverlay, QML_FBO_OVERLAY)
DEFINE_BOOL_CONFIG_OPTION(qmlFboFlushBeforeDetach, QML_FBO_FLUSH_BEFORE_DETACH)
namespace
{
class BindableFbo : public QSGBindable
{
public:
BindableFbo(QOpenGLFramebufferObject *fbo, QSGDepthStencilBuffer *depthStencil);
virtual ~BindableFbo();
virtual void bind() const;
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();
}
}
class QQuickShaderEffectSourceTextureProvider : public QSGTextureProvider
{
Q_OBJECT
@ -113,7 +75,7 @@ public:
return sourceTexture;
}
QQuickShaderEffectTexture *sourceTexture;
QSGLayer *sourceTexture;
QSGTexture::Filtering mipmapFiltering;
QSGTexture::Filtering filtering;
@ -122,379 +84,10 @@ public:
};
#include "qquickshadereffectsource.moc"
QQuickShaderEffectTexture::QQuickShaderEffectTexture(QQuickItem *shaderSource)
: QSGDynamicTexture()
, 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(QQuickItemPrivate::get(shaderSource)->sceneGraphRenderContext())
, m_mipmap(false)
, m_live(true)
, m_recursive(false)
, m_dirtyTexture(true)
, m_multisamplingChecked(false)
, m_multisampling(false)
, m_grab(false)
{
}
QQuickShaderEffectTexture::~QQuickShaderEffectTexture()
{
invalidated();
}
void QQuickShaderEffectTexture::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;
}
}
void QQuickShaderEffectTexture::markDirtyTextureLater()
{
QCoreApplication::postEvent(this, new QEvent(QEvent::User));
}
void QQuickShaderEffectTexture::customEvent(QEvent *)
{
markDirtyTexture();
}
int QQuickShaderEffectTexture::textureId() const
{
return m_fbo ? m_fbo->texture() : 0;
}
bool QQuickShaderEffectTexture::hasAlphaChannel() const
{
return m_format != GL_RGB;
}
bool QQuickShaderEffectTexture::hasMipmaps() const
{
return m_mipmap;
}
void QQuickShaderEffectTexture::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 QQuickShaderEffectTexture::updateTexture()
{
bool doGrab = (m_live || m_grab) && m_dirtyTexture;
if (doGrab)
grab();
if (m_grab)
emit scheduledUpdateCompleted();
m_grab = false;
return doGrab;
}
void QQuickShaderEffectTexture::setHasMipmaps(bool mipmap)
{
if (mipmap == m_mipmap)
return;
m_mipmap = mipmap;
if (m_mipmap && m_fbo && !m_fbo->format().mipmap())
markDirtyTexture();
}
void QQuickShaderEffectTexture::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 QQuickShaderEffectTexture::setRect(const QRectF &rect)
{
if (rect == m_rect)
return;
m_rect = rect;
markDirtyTexture();
}
void QQuickShaderEffectTexture::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 QQuickShaderEffectTexture::setFormat(GLenum format)
{
if (format == m_format)
return;
m_format = format;
markDirtyTexture();
}
void QQuickShaderEffectTexture::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 QQuickShaderEffectTexture::scheduleUpdate()
{
if (m_grab)
return;
m_grab = true;
if (m_dirtyTexture) {
emit updateRequested();
if (m_shaderSourceNode)
m_shaderSourceNode->markDirty(QSGNode::DirtyMaterial);
}
}
void QQuickShaderEffectTexture::setRecursive(bool recursive)
{
m_recursive = recursive;
}
void QQuickShaderEffectTexture::markDirtyTexture()
{
m_dirtyTexture = true;
if (m_live || m_grab) {
emit updateRequested();
if (m_shaderSourceNode)
m_shaderSourceNode->markDirty(QSGNode::DirtyMaterial);
}
}
void QQuickShaderEffectTexture::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 QQuickShaderEffectTexture::toImage() const
{
if (m_fbo)
return m_fbo->toImage();
return QImage();
}
class QQuickShaderEffectSourceCleanup : public QRunnable
{
public:
QQuickShaderEffectSourceCleanup(QQuickShaderEffectTexture *t, QQuickShaderEffectSourceTextureProvider *p)
QQuickShaderEffectSourceCleanup(QSGLayer *t, QQuickShaderEffectSourceTextureProvider *p)
: texture(t)
, provider(p)
{}
@ -502,7 +95,7 @@ public:
delete texture;
delete provider;
}
QQuickShaderEffectTexture *texture;
QSGLayer *texture;
QQuickShaderEffectSourceTextureProvider *provider;
};
@ -642,7 +235,8 @@ void QQuickShaderEffectSource::ensureTexture()
"QQuickShaderEffectSource::ensureTexture",
"Cannot be used outside the rendering thread");
m_texture = new QQuickShaderEffectTexture(this);
QSGRenderContext *rc = QQuickItemPrivate::get(this)->sceneGraphRenderContext();
m_texture = rc->sceneGraphContext()->createLayer(rc);
connect(QQuickItemPrivate::get(this)->window, SIGNAL(sceneGraphInvalidated()), m_texture, SLOT(invalidated()), Qt::DirectConnection);
connect(m_texture, SIGNAL(updateRequested()), this, SLOT(update()));
connect(m_texture, SIGNAL(scheduledUpdateCompleted()), this, SIGNAL(scheduledUpdateCompleted()));

View File

@ -53,8 +53,6 @@
#include "qsize.h"
#include "qrect.h"
#define QSG_DEBUG_FBO_OVERLAY
QT_BEGIN_NAMESPACE
class QSGNode;
@ -64,95 +62,6 @@ class QSGSimpleRectNode;
class QQuickShaderEffectSourceTextureProvider;
class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffectTexture : public QSGDynamicTexture
{
Q_OBJECT
public:
QQuickShaderEffectTexture(QQuickItem *shaderSource);
~QQuickShaderEffectTexture();
virtual bool updateTexture();
// 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;
Q_SIGNALS:
void updateRequested();
void scheduledUpdateCompleted();
public Q_SLOTS:
void markDirtyTexture();
void invalidated();
void markDirtyTextureLater();
protected:
virtual void customEvent(QEvent *);
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;
};
class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffectSource : public QQuickItem, public QQuickItemChangeListener
{
Q_OBJECT
@ -244,7 +153,7 @@ private:
void ensureTexture();
QQuickShaderEffectSourceTextureProvider *m_provider;
QQuickShaderEffectTexture *m_texture;
QSGLayer *m_texture;
WrapMode m_wrapMode;
QQuickItem *m_sourceItem;
QRectF m_sourceRect;

View File

@ -328,6 +328,7 @@ void QSGNodeVisitorEx::visitChildren(QSGNode *node)
}
break;
}
case QSGNode::RootNodeType: // fall through
case QSGNode::BasicNodeType: {
visitChildren(child);
break;
@ -339,4 +340,25 @@ void QSGNodeVisitorEx::visitChildren(QSGNode *node)
}
}
void QSGLayer::markDirtyTextureLater()
{
QCoreApplication::postEvent(this, new QEvent(static_cast<QEvent::Type>(markDirtyEventType())));
}
void QSGLayer::customEvent(QEvent *event)
{
if (event->type() == markDirtyEventType())
markDirtyTexture();
else
QObject::customEvent(event);
}
int QSGLayer::markDirtyEventType()
{
static int type = QEvent::None;
if (type == QEvent::None)
type = QEvent::registerEventType();
return type;
}
QT_END_NAMESPACE

View File

@ -158,6 +158,37 @@ public:
virtual void accept(QSGNodeVisitorEx *visitor) { visitor->visit(this); visitor->visitChildren(this); visitor->endVisit(this); }
};
class Q_QUICK_EXPORT QSGLayer : public QSGDynamicTexture
{
Q_OBJECT
public:
virtual void setItem(QSGNode *item) = 0;
virtual void setShaderSourceNode(QSGNode *node) = 0;
virtual void setRect(const QRectF &rect) = 0;
virtual void setSize(const QSize &size) = 0;
virtual void scheduleUpdate() = 0;
virtual QImage toImage() const = 0;
virtual void setLive(bool live) = 0;
virtual void setRecursive(bool recursive) = 0;
virtual void setFormat(GLenum format) = 0;
virtual void setHasMipmaps(bool mipmap) = 0;
virtual void setDevicePixelRatio(qreal ratio) = 0;
Q_SLOT virtual void markDirtyTexture() = 0;
Q_SLOT virtual void invalidated() = 0;
Q_SLOT void markDirtyTextureLater();
Q_SIGNALS:
void updateRequested();
void scheduledUpdateCompleted();
protected:
virtual void customEvent(QEvent *);
private:
int markDirtyEventType();
};
class Q_QUICK_PRIVATE_EXPORT QSGGlyphNode : public QSGVisitableNode
{
public:

View File

@ -51,6 +51,7 @@
#include <QtQuick/private/qsgshareddistancefieldglyphcache_p.h>
#include <QtQuick/private/qsgatlastexture_p.h>
#include <QtQuick/private/qsgrenderloop_p.h>
#include <QtQuick/private/qsgdefaultlayer_p.h>
#include <QtQuick/private/qsgtexture_p.h>
#include <QtQuick/private/qquickpixmapcache_p.h>
@ -321,6 +322,14 @@ QSGNinePatchNode *QSGContext::createNinePatchNode()
return 0;
}
/*!
Factory function for scene graph backends of layers.
*/
QSGLayer *QSGContext::createLayer(QSGRenderContext *renderContext)
{
return new QSGDefaultLayer(renderContext);
}
QSurfaceFormat QSGContext::defaultSurfaceFormat() const
{
QSurfaceFormat format = QSurfaceFormat::defaultFormat();

View File

@ -72,6 +72,7 @@ class QSGTexture;
class QSGMaterial;
class QSGMaterialShader;
class QSGRenderLoop;
class QSGLayer;
class QOpenGLContext;
class QOpenGLFramebufferObject;
@ -177,6 +178,7 @@ public:
virtual QSGImageNode *createImageNode();
virtual QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode);
virtual QSGNinePatchNode *createNinePatchNode();
virtual QSGLayer *createLayer(QSGRenderContext *renderContext);
virtual QAnimationDriver *createAnimationDriver(QObject *parent);
virtual QSize minimumFBOSize() const;

View File

@ -292,7 +292,7 @@ void QSGDefaultImageNode::update()
void QSGDefaultImageNode::preprocess()
{
bool doDirty = false;
QSGDynamicTexture *t = qobject_cast<QSGDynamicTexture *>(m_material.texture());
QSGLayer *t = qobject_cast<QSGLayer *>(m_material.texture());
if (t) {
doDirty = t->updateTexture();
updateGeometry();

View File

@ -0,0 +1,445 @@
/****************************************************************************
**
** 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();
virtual void bind() const;
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();
}

View File

@ -0,0 +1,131 @@
/****************************************************************************
**
** 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();
virtual bool updateTexture();
// 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

View File

@ -81,7 +81,8 @@ HEADERS += \
$$PWD/qsgshareddistancefieldglyphcache_p.h \
$$PWD/qsgrenderloop_p.h \
$$PWD/qsgthreadedrenderloop_p.h \
$$PWD/qsgwindowsrenderloop_p.h
$$PWD/qsgwindowsrenderloop_p.h \
$$PWD/qsgdefaultlayer_p.h
SOURCES += \
$$PWD/qsgadaptationlayer.cpp \
@ -97,7 +98,8 @@ SOURCES += \
$$PWD/qsgshareddistancefieldglyphcache.cpp \
$$PWD/qsgrenderloop.cpp \
$$PWD/qsgthreadedrenderloop.cpp \
$$PWD/qsgwindowsrenderloop.cpp
$$PWD/qsgwindowsrenderloop.cpp \
$$PWD/qsgdefaultlayer.cpp
RESOURCES += \
$$PWD/scenegraph.qrc