1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
42 #include "qquickshadereffectsource_p.h"
44 #include "qquickitem_p.h"
45 #include "qquickwindow_p.h"
46 #include <private/qsgadaptationlayer_p.h>
47 #include <QtQuick/private/qsgrenderer_p.h>
49 #include "qopenglframebufferobject.h"
51 #include <QtQuick/private/qsgtexture_p.h>
55 DEFINE_BOOL_CONFIG_OPTION(qmlFboOverlay, QML_FBO_OVERLAY)
59 class BindableFbo : public QSGBindable
62 BindableFbo(QOpenGLFramebufferObject *fbo, QSGDepthStencilBuffer *depthStencil);
63 virtual ~BindableFbo();
64 virtual void bind() const;
66 QOpenGLFramebufferObject *m_fbo;
67 QSGDepthStencilBuffer *m_depthStencil;
70 BindableFbo::BindableFbo(QOpenGLFramebufferObject *fbo, QSGDepthStencilBuffer *depthStencil)
72 , m_depthStencil(depthStencil)
76 BindableFbo::~BindableFbo()
79 m_depthStencil->detach();
82 void BindableFbo::bind() const
86 m_depthStencil->attach();
90 class QQuickShaderEffectSourceTextureProvider : public QSGTextureProvider
94 QQuickShaderEffectSourceTextureProvider()
99 QSGTexture *texture() const {
100 sourceTexture->setMipmapFiltering(mipmapFiltering);
101 sourceTexture->setFiltering(filtering);
102 sourceTexture->setHorizontalWrapMode(horizontalWrap);
103 sourceTexture->setVerticalWrapMode(verticalWrap);
104 return sourceTexture;
107 QQuickShaderEffectTexture *sourceTexture;
109 QSGTexture::Filtering mipmapFiltering;
110 QSGTexture::Filtering filtering;
111 QSGTexture::WrapMode horizontalWrap;
112 QSGTexture::WrapMode verticalWrap;
114 #include "qquickshadereffectsource.moc"
117 QQuickShaderEffectSourceNode::QQuickShaderEffectSourceNode()
119 setFlag(UsePreprocess, true);
122 void QQuickShaderEffectSourceNode::markDirtyTexture()
124 markDirty(DirtyMaterial);
128 QQuickShaderEffectTexture::QQuickShaderEffectTexture(QQuickItem *shaderSource)
129 : QSGDynamicTexture()
135 #ifdef QSG_DEBUG_FBO_OVERLAY
138 , m_context(QQuickItemPrivate::get(shaderSource)->sceneGraphContext())
142 , m_dirtyTexture(true)
143 , m_multisamplingChecked(false)
144 , m_multisampling(false)
149 QQuickShaderEffectTexture::~QQuickShaderEffectTexture()
152 disconnect(m_renderer, SIGNAL(sceneGraphChanged()), this, SLOT(markDirtyTexture()));
155 delete m_secondaryFbo;
156 #ifdef QSG_DEBUG_FBO_OVERLAY
157 delete m_debugOverlay;
161 int QQuickShaderEffectTexture::textureId() const
163 return m_fbo ? m_fbo->texture() : 0;
166 bool QQuickShaderEffectTexture::hasAlphaChannel() const
168 return m_format != GL_RGB;
171 bool QQuickShaderEffectTexture::hasMipmaps() const
177 void QQuickShaderEffectTexture::bind()
180 if (!m_recursive && m_fbo && ((m_multisampling && m_secondaryFbo->isBound()) || m_fbo->isBound()))
181 qWarning("ShaderEffectSource: \'recursive\' must be set to true when rendering recursively.");
183 glBindTexture(GL_TEXTURE_2D, m_fbo ? m_fbo->texture() : 0);
187 bool QQuickShaderEffectTexture::updateTexture()
189 bool doGrab = (m_live || m_grab) && m_dirtyTexture;
193 emit scheduledUpdateCompleted();
198 void QQuickShaderEffectTexture::setHasMipmaps(bool mipmap)
200 if (mipmap == m_mipmap)
203 if (m_mipmap && m_fbo && !m_fbo->format().mipmap())
208 void QQuickShaderEffectTexture::setItem(QSGNode *item)
214 if (m_live && !m_item) {
216 delete m_secondaryFbo;
217 m_fbo = m_secondaryFbo = 0;
218 m_depthStencilBuffer.clear();
224 void QQuickShaderEffectTexture::setRect(const QRectF &rect)
232 void QQuickShaderEffectTexture::setSize(const QSize &size)
238 if (m_live && m_size.isNull()) {
240 delete m_secondaryFbo;
241 m_fbo = m_secondaryFbo = 0;
242 m_depthStencilBuffer.clear();
248 void QQuickShaderEffectTexture::setFormat(GLenum format)
250 if (format == m_format)
256 void QQuickShaderEffectTexture::setLive(bool live)
262 if (m_live && (!m_item || m_size.isNull())) {
264 delete m_secondaryFbo;
265 m_fbo = m_secondaryFbo = 0;
266 m_depthStencilBuffer.clear();
272 void QQuickShaderEffectTexture::scheduleUpdate()
278 emit updateRequested();
281 void QQuickShaderEffectTexture::setRecursive(bool recursive)
283 m_recursive = recursive;
286 void QQuickShaderEffectTexture::markDirtyTexture()
288 m_dirtyTexture = true;
289 if (m_live || m_grab)
290 emit updateRequested();
293 void QQuickShaderEffectTexture::grab()
295 if (!m_item || m_size.isNull()) {
297 delete m_secondaryFbo;
298 m_fbo = m_secondaryFbo = 0;
299 m_depthStencilBuffer.clear();
300 m_dirtyTexture = false;
303 QSGNode *root = m_item;
304 while (root->firstChild() && root->type() != QSGNode::RootNodeType)
305 root = root->firstChild();
306 if (root->type() != QSGNode::RootNodeType)
310 m_renderer = m_context->createRenderer();
311 connect(m_renderer, SIGNAL(sceneGraphChanged()), this, SLOT(markDirtyTexture()));
313 m_renderer->setRootNode(static_cast<QSGRootNode *>(root));
315 bool deleteFboLater = false;
316 if (!m_fbo || m_fbo->size() != m_size || m_fbo->format().internalTextureFormat() != m_format
317 || (!m_fbo->format().mipmap() && m_mipmap))
319 if (!m_multisamplingChecked) {
320 if (m_context->glContext()->format().samples() <= 1) {
321 m_multisampling = false;
323 QList<QByteArray> extensions = QByteArray((const char *)glGetString(GL_EXTENSIONS)).split(' ');
324 m_multisampling = extensions.contains("GL_EXT_framebuffer_multisample")
325 && extensions.contains("GL_EXT_framebuffer_blit");
327 m_multisamplingChecked = true;
329 if (m_multisampling) {
330 // Don't delete the FBO right away in case it is used recursively.
331 deleteFboLater = true;
332 delete m_secondaryFbo;
333 QOpenGLFramebufferObjectFormat format;
335 format.setInternalTextureFormat(m_format);
336 format.setSamples(m_context->glContext()->format().samples());
337 m_secondaryFbo = new QOpenGLFramebufferObject(m_size, format);
338 m_depthStencilBuffer = m_context->depthStencilBufferForFbo(m_secondaryFbo);
340 QOpenGLFramebufferObjectFormat format;
341 format.setInternalTextureFormat(m_format);
342 format.setMipmap(m_mipmap);
344 deleteFboLater = true;
345 delete m_secondaryFbo;
346 m_secondaryFbo = new QOpenGLFramebufferObject(m_size, format);
347 glBindTexture(GL_TEXTURE_2D, m_secondaryFbo->texture());
348 updateBindOptions(true);
349 m_depthStencilBuffer = m_context->depthStencilBufferForFbo(m_secondaryFbo);
352 delete m_secondaryFbo;
353 m_fbo = new QOpenGLFramebufferObject(m_size, format);
355 glBindTexture(GL_TEXTURE_2D, m_fbo->texture());
356 updateBindOptions(true);
357 m_depthStencilBuffer = m_context->depthStencilBufferForFbo(m_fbo);
362 if (m_recursive && !m_secondaryFbo) {
363 // m_fbo already created, m_recursive was just set.
365 Q_ASSERT(!m_multisampling);
367 m_secondaryFbo = new QOpenGLFramebufferObject(m_size, m_fbo->format());
368 glBindTexture(GL_TEXTURE_2D, m_secondaryFbo->texture());
369 updateBindOptions(true);
373 root->markDirty(QSGNode::DirtyForceUpdate); // Force matrix, clip and opacity update.
374 m_renderer->nodeChanged(root, QSGNode::DirtyForceUpdate); // Force render list update.
376 #ifdef QSG_DEBUG_FBO_OVERLAY
377 if (qmlFboOverlay()) {
379 m_debugOverlay = m_context->createRectangleNode();
380 m_debugOverlay->setRect(QRectF(0, 0, m_size.width(), m_size.height()));
381 m_debugOverlay->setColor(QColor(0xff, 0x00, 0x80, 0x40));
382 m_debugOverlay->setPenColor(QColor());
383 m_debugOverlay->setPenWidth(0);
384 m_debugOverlay->setRadius(0);
385 m_debugOverlay->update();
386 root->appendChildNode(m_debugOverlay);
390 m_dirtyTexture = false;
392 QOpenGLContext *ctx = m_context->glContext();
393 m_renderer->setDeviceRect(m_size);
394 m_renderer->setViewportRect(m_size);
395 QRectF mirrored(m_rect.left(), m_rect.bottom(), m_rect.width(), -m_rect.height());
396 m_renderer->setProjectionMatrixToRect(mirrored);
397 m_renderer->setClearColor(Qt::transparent);
399 if (m_multisampling) {
400 m_renderer->renderScene(BindableFbo(m_secondaryFbo, m_depthStencilBuffer.data()));
402 if (deleteFboLater) {
404 QOpenGLFramebufferObjectFormat format;
405 format.setInternalTextureFormat(m_format);
406 format.setAttachment(QOpenGLFramebufferObject::NoAttachment);
407 format.setMipmap(m_mipmap);
408 format.setSamples(0);
409 m_fbo = new QOpenGLFramebufferObject(m_size, format);
410 glBindTexture(GL_TEXTURE_2D, m_fbo->texture());
411 updateBindOptions(true);
414 QRect r(QPoint(), m_size);
415 QOpenGLFramebufferObject::blitFramebuffer(m_fbo, r, m_secondaryFbo, r);
418 m_renderer->renderScene(BindableFbo(m_secondaryFbo, m_depthStencilBuffer.data()));
420 if (deleteFboLater) {
422 QOpenGLFramebufferObjectFormat format;
423 format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
424 format.setInternalTextureFormat(m_format);
425 format.setMipmap(m_mipmap);
426 m_fbo = new QOpenGLFramebufferObject(m_size, format);
427 glBindTexture(GL_TEXTURE_2D, m_fbo->texture());
428 updateBindOptions(true);
430 qSwap(m_fbo, m_secondaryFbo);
432 m_renderer->renderScene(BindableFbo(m_fbo, m_depthStencilBuffer.data()));
437 glBindTexture(GL_TEXTURE_2D, textureId());
438 ctx->functions()->glGenerateMipmap(GL_TEXTURE_2D);
441 root->markDirty(QSGNode::DirtyForceUpdate); // Force matrix, clip, opacity and render list update.
443 #ifdef QSG_DEBUG_FBO_OVERLAY
445 root->removeChildNode(m_debugOverlay);
448 markDirtyTexture(); // Continuously update if 'live' and 'recursive'.
451 QImage QQuickShaderEffectTexture::toImage() const
454 return m_fbo->toImage();
460 \qmltype ShaderEffectSource
461 \instantiates QQuickShaderEffectSource
462 \inqmlmodule QtQuick 2
465 \ingroup qtquick-effects
466 \brief Renders a QtQuick item into a texture and displays it
468 The ShaderEffectSource type renders \l sourceItem into a texture and
469 displays it in the scene. \l sourceItem is drawn into the texture as though
470 it was a fully opaque root item. Thus \l sourceItem itself can be
471 invisible, but still appear in the texture.
473 ShaderEffectSource can be used as:
475 \li a texture source in a \l ShaderEffect.
476 This allows you to apply custom shader effects to any QtQuick item.
477 \li a cache for a complex item.
478 The complex item can be rendered once into the texture, which can
479 then be animated freely without the need to render the complex item
481 \li an opacity layer.
482 ShaderEffectSource allows you to apply an opacity to items as a group
483 rather than each item individually.
488 \li \image declarative-shadereffectsource.png
496 GradientStop { position: 0; color: "white" }
497 GradientStop { position: 1; color: "black" }
503 width: 100; height: 100
504 Rectangle { x: 5; y: 5; width: 60; height: 60; color: "red" }
505 Rectangle { x: 20; y: 20; width: 60; height: 60; color: "orange" }
506 Rectangle { x: 35; y: 35; width: 60; height: 60; color: "yellow" }
509 width: 100; height: 100
517 The ShaderEffectSource type does not redirect any mouse or keyboard
518 input to \l sourceItem. If you hide the \l sourceItem by setting
519 \l{Item::visible}{visible} to false or \l{Item::opacity}{opacity} to zero,
520 it will no longer react to input. In cases where the ShaderEffectSource is
521 meant to replace the \l sourceItem, you typically want to hide the
522 \l sourceItem while still handling input. For this, you can use
523 the \l hideSource property.
525 \note If \l sourceItem is a \l Rectangle with border, by default half the
526 border width falls outside the texture. To get the whole border, you can
527 extend the \l sourceRect.
529 \note The ShaderEffectSource relies on FBO multisampling support
530 to antialias edges. If the underlying hardware does not support this,
531 which is the case for most embedded graphics chips, edges rendered
532 inside a ShaderEffectSource will not be antialiased. One way to remedy
533 this is to double the size of the effect source and render it with
534 \c {smooth: true} (this is the default value of smooth).
535 This will be equivalent to 4x multisampling, at the cost of lower performance
536 and higher memory use.
538 \warning In most cases, using a ShaderEffectSource will decrease
539 performance, and in all cases, it will increase video memory usage.
540 Rendering through a ShaderEffectSource might also lead to lower quality
541 since some OpenGL implementations support multisampled backbuffer,
542 but not multisampled framebuffer objects.
545 QQuickShaderEffectSource::QQuickShaderEffectSource(QQuickItem *parent)
549 , m_wrapMode(ClampToEdge)
551 , m_textureSize(0, 0)
554 , m_hideSource(false)
559 setFlag(ItemHasContents);
562 QQuickShaderEffectSource::~QQuickShaderEffectSource()
565 m_texture->deleteLater();
568 m_provider->deleteLater();
571 QQuickItemPrivate *sd = QQuickItemPrivate::get(m_sourceItem);
572 sd->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
573 sd->derefFromEffectItem(m_hideSource);
579 void QQuickShaderEffectSource::ensureTexture()
584 Q_ASSERT_X(QQuickItemPrivate::get(this)->window
585 && QQuickItemPrivate::get(this)->sceneGraphContext()
586 && QThread::currentThread() == QQuickItemPrivate::get(this)->sceneGraphContext()->thread(),
587 "QQuickShaderEffectSource::ensureTexture",
588 "Cannot be used outside the rendering thread");
590 m_texture = new QQuickShaderEffectTexture(this);
591 connect(m_texture, SIGNAL(updateRequested()), this, SLOT(update()));
592 connect(m_texture, SIGNAL(scheduledUpdateCompleted()), this, SIGNAL(scheduledUpdateCompleted()));
595 QSGTextureProvider *QQuickShaderEffectSource::textureProvider() const
598 // Make sure it gets thread affinity on the rendering thread so deletion works properly..
599 Q_ASSERT_X(QQuickItemPrivate::get(this)->window
600 && QQuickItemPrivate::get(this)->sceneGraphContext()
601 && QThread::currentThread() == QQuickItemPrivate::get(this)->sceneGraphContext()->thread(),
602 "QQuickShaderEffectSource::textureProvider",
603 "Cannot be used outside the rendering thread");
604 const_cast<QQuickShaderEffectSource *>(this)->m_provider = new QQuickShaderEffectSourceTextureProvider();
605 const_cast<QQuickShaderEffectSource *>(this)->ensureTexture();
606 connect(m_texture, SIGNAL(updateRequested()), m_provider, SIGNAL(textureChanged()));
607 m_provider->sourceTexture = m_texture;
613 \qmlproperty enumeration QtQuick2::ShaderEffectSource::wrapMode
615 This property defines the OpenGL wrap modes associated with the texture.
616 Modifying this property makes most sense when the item is used as a
617 source texture of a \l ShaderEffect.
620 \li ShaderEffectSource.ClampToEdge - GL_CLAMP_TO_EDGE both horizontally and vertically
621 \li ShaderEffectSource.RepeatHorizontally - GL_REPEAT horizontally, GL_CLAMP_TO_EDGE vertically
622 \li ShaderEffectSource.RepeatVertically - GL_CLAMP_TO_EDGE horizontally, GL_REPEAT vertically
623 \li ShaderEffectSource.Repeat - GL_REPEAT both horizontally and vertically
626 \note Some OpenGL ES 2 implementations do not support the GL_REPEAT
627 wrap mode with non-power-of-two textures.
630 QQuickShaderEffectSource::WrapMode QQuickShaderEffectSource::wrapMode() const
635 void QQuickShaderEffectSource::setWrapMode(WrapMode mode)
637 if (mode == m_wrapMode)
641 emit wrapModeChanged();
645 \qmlproperty Item QtQuick2::ShaderEffectSource::sourceItem
647 This property holds the item to be rendered into the texture.
648 Setting this to null while \l live is true, will release the texture
652 QQuickItem *QQuickShaderEffectSource::sourceItem() const
657 void QQuickShaderEffectSource::itemGeometryChanged(QQuickItem *item, const QRectF &newRect, const QRectF &oldRect)
659 Q_ASSERT(item == m_sourceItem);
661 if (newRect.size() != oldRect.size())
665 void QQuickShaderEffectSource::setSourceItem(QQuickItem *item)
667 if (item == m_sourceItem)
670 QQuickItemPrivate *d = QQuickItemPrivate::get(m_sourceItem);
671 d->derefFromEffectItem(m_hideSource);
672 d->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
673 disconnect(m_sourceItem, SIGNAL(destroyed(QObject*)), this, SLOT(sourceItemDestroyed(QObject*)));
680 QQuickItemPrivate *d = QQuickItemPrivate::get(item);
681 // 'item' needs a window to get a scene graph node. It usually gets one through its
682 // parent, but if the source item is "inline" rather than a reference -- i.e.
683 // "sourceItem: Item { }" instead of "sourceItem: foo" -- it will not get a parent.
684 // In those cases, 'item' should get the window from 'this'.
686 d->refWindow(window());
687 d->refFromEffectItem(m_hideSource);
688 d->addItemChangeListener(this, QQuickItemPrivate::Geometry);
689 connect(m_sourceItem, SIGNAL(destroyed(QObject*)), this, SLOT(sourceItemDestroyed(QObject*)));
692 emit sourceItemChanged();
695 void QQuickShaderEffectSource::sourceItemDestroyed(QObject *item)
697 Q_ASSERT(item == m_sourceItem);
700 emit sourceItemChanged();
705 \qmlproperty rect QtQuick2::ShaderEffectSource::sourceRect
707 This property defines which rectangular area of the \l sourceItem to
708 render into the texture. The source rectangle can be larger than
709 \l sourceItem itself. If the rectangle is null, which is the default,
710 the whole \l sourceItem is rendered to texture.
713 QRectF QQuickShaderEffectSource::sourceRect() const
718 void QQuickShaderEffectSource::setSourceRect(const QRectF &rect)
720 if (rect == m_sourceRect)
724 emit sourceRectChanged();
728 \qmlproperty size QtQuick2::ShaderEffectSource::textureSize
730 This property holds the requested size of the texture. If it is empty,
731 which is the default, the size of the source rectangle is used.
733 \note Some platforms have a limit on how small framebuffer objects can be,
734 which means the actual texture size might be larger than the requested
738 QSize QQuickShaderEffectSource::textureSize() const
740 return m_textureSize;
743 void QQuickShaderEffectSource::setTextureSize(const QSize &size)
745 if (size == m_textureSize)
747 m_textureSize = size;
749 emit textureSizeChanged();
753 \qmlproperty enumeration QtQuick2::ShaderEffectSource::format
755 This property defines the internal OpenGL format of the texture.
756 Modifying this property makes most sense when the item is used as a
757 source texture of a \l ShaderEffect. Depending on the OpenGL
758 implementation, this property might allow you to save some texture memory.
761 \li ShaderEffectSource.Alpha - GL_ALPHA
762 \li ShaderEffectSource.RGB - GL_RGB
763 \li ShaderEffectSource.RGBA - GL_RGBA
766 \note Some OpenGL implementations do not support the GL_ALPHA format.
769 QQuickShaderEffectSource::Format QQuickShaderEffectSource::format() const
774 void QQuickShaderEffectSource::setFormat(QQuickShaderEffectSource::Format format)
776 if (format == m_format)
780 emit formatChanged();
784 \qmlproperty bool QtQuick2::ShaderEffectSource::live
786 If this property is true, the texture is updated whenever the
787 \l sourceItem updates. Otherwise, it will be a frozen image, even if
788 \l sourceItem is assigned a new item. The property is true by default.
791 bool QQuickShaderEffectSource::live() const
796 void QQuickShaderEffectSource::setLive(bool live)
806 \qmlproperty bool QtQuick2::ShaderEffectSource::hideSource
808 If this property is true, the \l sourceItem is hidden, though it will still
809 be rendered into the texture. As opposed to hiding the \l sourceItem by
810 setting \l{Item::visible}{visible} to false, setting this property to true
811 will not prevent mouse or keyboard input from reaching \l sourceItem.
812 The property is useful when the ShaderEffectSource is anchored on top of,
813 and meant to replace the \l sourceItem.
816 bool QQuickShaderEffectSource::hideSource() const
821 void QQuickShaderEffectSource::setHideSource(bool hide)
823 if (hide == m_hideSource)
826 QQuickItemPrivate::get(m_sourceItem)->refFromEffectItem(hide);
827 QQuickItemPrivate::get(m_sourceItem)->derefFromEffectItem(m_hideSource);
831 emit hideSourceChanged();
835 \qmlproperty bool QtQuick2::ShaderEffectSource::mipmap
837 If this property is true, mipmaps are generated for the texture.
839 \note Some OpenGL ES 2 implementations do not support mipmapping of
840 non-power-of-two textures.
843 bool QQuickShaderEffectSource::mipmap() const
848 void QQuickShaderEffectSource::setMipmap(bool enabled)
850 if (enabled == m_mipmap)
854 emit mipmapChanged();
858 \qmlproperty bool QtQuick2::ShaderEffectSource::recursive
860 Set this property to true if the ShaderEffectSource has a dependency on
861 itself. ShaderEffectSources form a dependency chain, where one
862 ShaderEffectSource can be part of the \l sourceItem of another.
863 If there is a loop in this chain, a ShaderEffectSource could end up trying
864 to render into the same texture it is using as source, which is not allowed
865 by OpenGL. When this property is set to true, an extra texture is allocated
866 so that ShaderEffectSource can keep a copy of the texture from the previous
867 frame. It can then render into one texture and use the texture from the
868 previous frame as source.
870 Setting both this property and \l live to true will cause the scene graph
871 to render continuously. Since the ShaderEffectSource depends on itself,
872 updating it means that it immediately becomes dirty again.
875 bool QQuickShaderEffectSource::recursive() const
880 void QQuickShaderEffectSource::setRecursive(bool enabled)
882 if (enabled == m_recursive)
884 m_recursive = enabled;
885 emit recursiveChanged();
889 \qmlmethod QtQuick2::ShaderEffectSource::scheduleUpdate()
891 Schedules a re-rendering of the texture for the next frame.
892 Use this to update the texture when \l live is false.
895 void QQuickShaderEffectSource::scheduleUpdate()
903 static void get_wrap_mode(QQuickShaderEffectSource::WrapMode mode, QSGTexture::WrapMode *hWrap, QSGTexture::WrapMode *vWrap)
906 case QQuickShaderEffectSource::RepeatHorizontally:
907 *hWrap = QSGTexture::Repeat;
908 *vWrap = QSGTexture::ClampToEdge;
910 case QQuickShaderEffectSource::RepeatVertically:
911 *vWrap = QSGTexture::Repeat;
912 *hWrap = QSGTexture::ClampToEdge;
914 case QQuickShaderEffectSource::Repeat:
915 *hWrap = *vWrap = QSGTexture::Repeat;
918 // QQuickShaderEffectSource::ClampToEdge
919 *hWrap = *vWrap = QSGTexture::ClampToEdge;
925 void QQuickShaderEffectSource::releaseResources()
928 m_texture->deleteLater();
932 m_provider->deleteLater();
937 QSGNode *QQuickShaderEffectSource::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
939 if (!m_sourceItem || m_sourceItem->width() == 0 || m_sourceItem->height() == 0) {
941 m_texture->setItem(0);
948 m_texture->setLive(m_live);
949 m_texture->setItem(QQuickItemPrivate::get(m_sourceItem)->itemNode());
950 QRectF sourceRect = m_sourceRect.width() == 0 || m_sourceRect.height() == 0
951 ? QRectF(0, 0, m_sourceItem->width(), m_sourceItem->height())
953 m_texture->setRect(sourceRect);
954 QSize textureSize = m_textureSize.isEmpty()
955 ? QSize(qCeil(qAbs(sourceRect.width())), qCeil(qAbs(sourceRect.height())))
957 Q_ASSERT(!textureSize.isEmpty());
958 QQuickItemPrivate *d = static_cast<QQuickItemPrivate *>(QObjectPrivate::get(this));
959 const QSize minTextureSize = d->sceneGraphContext()->minimumFBOSize();
960 // Keep power-of-two by doubling the size.
961 while (textureSize.width() < minTextureSize.width())
962 textureSize.rwidth() *= 2;
963 while (textureSize.height() < minTextureSize.height())
964 textureSize.rheight() *= 2;
966 m_texture->setSize(textureSize);
967 m_texture->setRecursive(m_recursive);
968 m_texture->setFormat(GLenum(m_format));
969 m_texture->setHasMipmaps(m_mipmap);
972 m_texture->scheduleUpdate();
975 QSGTexture::Filtering filtering = QQuickItemPrivate::get(this)->smooth
977 : QSGTexture::Nearest;
978 QSGTexture::Filtering mmFiltering = m_mipmap ? filtering : QSGTexture::None;
979 QSGTexture::WrapMode hWrap, vWrap;
980 get_wrap_mode(m_wrapMode, &hWrap, &vWrap);
983 m_provider->mipmapFiltering = mmFiltering;
984 m_provider->filtering = filtering;
985 m_provider->horizontalWrap = hWrap;
986 m_provider->verticalWrap = vWrap;
989 // Don't create the paint node if we're not spanning any area
990 if (width() == 0 || height() == 0) {
995 QQuickShaderEffectSourceNode *node = static_cast<QQuickShaderEffectSourceNode *>(oldNode);
997 node = new QQuickShaderEffectSourceNode;
998 node->setTexture(m_texture);
999 connect(m_texture, SIGNAL(updateRequested()), node, SLOT(markDirtyTexture()));
1002 // If live and recursive, update continuously.
1003 if (m_live && m_recursive)
1004 node->markDirty(QSGNode::DirtyMaterial);
1006 node->setMipmapFiltering(mmFiltering);
1007 node->setFiltering(filtering);
1008 node->setHorizontalWrapMode(hWrap);
1009 node->setVerticalWrapMode(vWrap);
1010 node->setTargetRect(QRectF(0, 0, width(), height()));
1011 node->setInnerTargetRect(QRectF(0, 0, width(), height()));
1017 void QQuickShaderEffectSource::itemChange(ItemChange change, const ItemChangeData &value)
1019 if (change == QQuickItem::ItemSceneChange && m_sourceItem) {
1020 // See comment in QQuickShaderEffectSource::setSourceItem().
1022 QQuickItemPrivate::get(m_sourceItem)->refWindow(value.window);
1024 QQuickItemPrivate::get(m_sourceItem)->derefWindow();
1026 QQuickItem::itemChange(change, value);