1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qquickshadereffectsource_p.h"
44 #include "qquickitem_p.h"
45 #include "qquickcanvas_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 \qmlclass ShaderEffectSource QQuickShaderEffectSource
463 \ingroup qtquick-effects
464 \brief Renders a QML element into a texture and displays it
466 The ShaderEffectSource element renders \l sourceItem into a texture and
467 displays it in the scene. \l sourceItem is drawn into the texture as though
468 it was a fully opaque root element. Thus \l sourceItem itself can be
469 invisible, but still appear in the texture.
471 ShaderEffectSource can be used as:
473 \li a texture source in a \l ShaderEffect.
474 This allows you to apply custom shader effects to any QML element.
475 \li a cache for a complex element.
476 The complex element can be rendered once into the texture, which can
477 then be animated freely without the need to render the complex element
479 \li an opacity layer.
480 ShaderEffectSource allows you to apply an opacity to elements as a group
481 rather than each element individually.
486 \li \image declarative-shadereffectsource.png
494 GradientStop { position: 0; color: "white" }
495 GradientStop { position: 1; color: "black" }
501 width: 100; height: 100
502 Rectangle { x: 5; y: 5; width: 60; height: 60; color: "red" }
503 Rectangle { x: 20; y: 20; width: 60; height: 60; color: "orange" }
504 Rectangle { x: 35; y: 35; width: 60; height: 60; color: "yellow" }
507 width: 100; height: 100
516 The ShaderEffectSource element does not redirect any mouse or keyboard
517 input to \l sourceItem. If you hide the \l sourceItem by setting
518 \l{Item::visible}{visible} to false or \l{Item::opacity}{opacity} to zero,
519 it will no longer react to input. In cases where the ShaderEffectSource is
520 meant to replace the \l sourceItem, you typically want to hide the
521 \l sourceItem while still handling input. For this, you can use
522 the \l hideSource property.
524 \note If \l sourceItem is a \l Rectangle with border, by default half the
525 border width falls outside the texture. To get the whole border, you can
526 extend the \l sourceRect.
528 \note The ShaderEffectSource relies on FBO multisampling support
529 to antialias edges. If the underlying hardware does not support this,
530 which is the case for most embedded graphics chips, edges rendered
531 inside a ShaderEffectSource will not be antialiased. One way to remedy
532 this is to double the size of the effect source and render it with
533 \c {smooth: true}. This will be equivalent to 4x multisampling, at
534 the cost of lower performance and higher memory use.
536 \warning In most cases, using a ShaderEffectSource will decrease
537 performance, and in all cases, it will increase video memory usage.
538 Rendering through a ShaderEffectSource might also lead to lower quality
539 since some OpenGL implementations support multisampled backbuffer,
540 but not multisampled framebuffer objects.
543 QQuickShaderEffectSource::QQuickShaderEffectSource(QQuickItem *parent)
547 , m_wrapMode(ClampToEdge)
549 , m_textureSize(0, 0)
552 , m_hideSource(false)
557 setFlag(ItemHasContents);
560 QQuickShaderEffectSource::~QQuickShaderEffectSource()
563 m_texture->deleteLater();
566 m_provider->deleteLater();
569 QQuickItemPrivate *sd = QQuickItemPrivate::get(m_sourceItem);
570 sd->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
571 sd->derefFromEffectItem(m_hideSource);
577 void QQuickShaderEffectSource::ensureTexture()
582 Q_ASSERT_X(QQuickItemPrivate::get(this)->canvas
583 && QQuickItemPrivate::get(this)->sceneGraphContext()
584 && QThread::currentThread() == QQuickItemPrivate::get(this)->sceneGraphContext()->thread(),
585 "QQuickShaderEffectSource::ensureTexture",
586 "Cannot be used outside the rendering thread");
588 m_texture = new QQuickShaderEffectTexture(this);
589 connect(m_texture, SIGNAL(updateRequested()), this, SLOT(update()));
590 connect(m_texture, SIGNAL(scheduledUpdateCompleted()), this, SIGNAL(scheduledUpdateCompleted()));
593 QSGTextureProvider *QQuickShaderEffectSource::textureProvider() const
596 // Make sure it gets thread affinity on the rendering thread so deletion works properly..
597 Q_ASSERT_X(QQuickItemPrivate::get(this)->canvas
598 && QQuickItemPrivate::get(this)->sceneGraphContext()
599 && QThread::currentThread() == QQuickItemPrivate::get(this)->sceneGraphContext()->thread(),
600 "QQuickShaderEffectSource::textureProvider",
601 "Cannot be used outside the rendering thread");
602 const_cast<QQuickShaderEffectSource *>(this)->m_provider = new QQuickShaderEffectSourceTextureProvider();
603 const_cast<QQuickShaderEffectSource *>(this)->ensureTexture();
604 connect(m_texture, SIGNAL(updateRequested()), m_provider, SIGNAL(textureChanged()));
605 m_provider->sourceTexture = m_texture;
611 \qmlproperty enumeration ShaderEffectSource::wrapMode
613 This property defines the OpenGL wrap modes associated with the texture.
614 Modifying this property makes most sense when the element is used as a
615 source texture of a \l ShaderEffect.
618 \li ShaderEffectSource.ClampToEdge - GL_CLAMP_TO_EDGE both horizontally and vertically
619 \li ShaderEffectSource.RepeatHorizontally - GL_REPEAT horizontally, GL_CLAMP_TO_EDGE vertically
620 \li ShaderEffectSource.RepeatVertically - GL_CLAMP_TO_EDGE horizontally, GL_REPEAT vertically
621 \li ShaderEffectSource.Repeat - GL_REPEAT both horizontally and vertically
624 \note Some OpenGL ES 2 implementations do not support the GL_REPEAT
625 wrap mode with non-power-of-two textures.
628 QQuickShaderEffectSource::WrapMode QQuickShaderEffectSource::wrapMode() const
633 void QQuickShaderEffectSource::setWrapMode(WrapMode mode)
635 if (mode == m_wrapMode)
639 emit wrapModeChanged();
643 \qmlproperty Item ShaderEffectSource::sourceItem
645 This property holds the element to be rendered into the texture.
646 Setting this to null while \l live is true, will release the texture
650 QQuickItem *QQuickShaderEffectSource::sourceItem() const
655 void QQuickShaderEffectSource::itemGeometryChanged(QQuickItem *item, const QRectF &newRect, const QRectF &oldRect)
657 Q_ASSERT(item == m_sourceItem);
659 if (newRect.size() != oldRect.size())
663 void QQuickShaderEffectSource::setSourceItem(QQuickItem *item)
665 if (item == m_sourceItem)
668 QQuickItemPrivate *d = QQuickItemPrivate::get(m_sourceItem);
669 d->derefFromEffectItem(m_hideSource);
670 d->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
671 disconnect(m_sourceItem, SIGNAL(destroyed(QObject*)), this, SLOT(sourceItemDestroyed(QObject*)));
678 QQuickItemPrivate *d = QQuickItemPrivate::get(item);
679 // 'item' needs a canvas to get a scene graph node. It usually gets one through its
680 // parent, but if the source item is "inline" rather than a reference -- i.e.
681 // "sourceItem: Item { }" instead of "sourceItem: foo" -- it will not get a parent.
682 // In those cases, 'item' should get the canvas from 'this'.
684 d->refCanvas(canvas());
685 d->refFromEffectItem(m_hideSource);
686 d->addItemChangeListener(this, QQuickItemPrivate::Geometry);
687 connect(m_sourceItem, SIGNAL(destroyed(QObject*)), this, SLOT(sourceItemDestroyed(QObject*)));
690 emit sourceItemChanged();
693 void QQuickShaderEffectSource::sourceItemDestroyed(QObject *item)
695 Q_ASSERT(item == m_sourceItem);
698 emit sourceItemChanged();
703 \qmlproperty rect ShaderEffectSource::sourceRect
705 This property defines which rectangular area of the \l sourceItem to
706 render into the texture. The source rectangle can be larger than
707 \l sourceItem itself. If the rectangle is null, which is the default,
708 the whole \l sourceItem is rendered to texture.
711 QRectF QQuickShaderEffectSource::sourceRect() const
716 void QQuickShaderEffectSource::setSourceRect(const QRectF &rect)
718 if (rect == m_sourceRect)
722 emit sourceRectChanged();
726 \qmlproperty size ShaderEffectSource::textureSize
728 This property holds the requested size of the texture. If it is empty,
729 which is the default, the size of the source rectangle is used.
731 \note Some platforms have a limit on how small framebuffer objects can be,
732 which means the actual texture size might be larger than the requested
736 QSize QQuickShaderEffectSource::textureSize() const
738 return m_textureSize;
741 void QQuickShaderEffectSource::setTextureSize(const QSize &size)
743 if (size == m_textureSize)
745 m_textureSize = size;
747 emit textureSizeChanged();
751 \qmlproperty enumeration ShaderEffectSource::format
753 This property defines the internal OpenGL format of the texture.
754 Modifying this property makes most sense when the element is used as a
755 source texture of a \l ShaderEffect. Depending on the OpenGL
756 implementation, this property might allow you to save some texture memory.
759 \li ShaderEffectSource.Alpha - GL_ALPHA
760 \li ShaderEffectSource.RGB - GL_RGB
761 \li ShaderEffectSource.RGBA - GL_RGBA
764 \note Some OpenGL implementations do not support the GL_ALPHA format.
767 QQuickShaderEffectSource::Format QQuickShaderEffectSource::format() const
772 void QQuickShaderEffectSource::setFormat(QQuickShaderEffectSource::Format format)
774 if (format == m_format)
778 emit formatChanged();
782 \qmlproperty bool ShaderEffectSource::live
784 If this property is true, the texture is updated whenever the
785 \l sourceItem updates. Otherwise, it will be a frozen image, even if
786 \l sourceItem is assigned a new element. The property is true by default.
789 bool QQuickShaderEffectSource::live() const
794 void QQuickShaderEffectSource::setLive(bool live)
804 \qmlproperty bool ShaderEffectSource::hideSource
806 If this property is true, the \l sourceItem is hidden, though it will still
807 be rendered into the texture. As opposed to hiding the \l sourceItem by
808 setting \l{Item::visible}{visible} to false, setting this property to true
809 will not prevent mouse or keyboard input from reaching \l sourceItem.
810 The property is useful when the ShaderEffectSource is anchored on top of,
811 and meant to replace the \l sourceItem.
814 bool QQuickShaderEffectSource::hideSource() const
819 void QQuickShaderEffectSource::setHideSource(bool hide)
821 if (hide == m_hideSource)
824 QQuickItemPrivate::get(m_sourceItem)->refFromEffectItem(hide);
825 QQuickItemPrivate::get(m_sourceItem)->derefFromEffectItem(m_hideSource);
829 emit hideSourceChanged();
833 \qmlproperty bool ShaderEffectSource::mipmap
835 If this property is true, mipmaps are generated for the texture.
837 \note Some OpenGL ES 2 implementations do not support mipmapping of
838 non-power-of-two textures.
841 bool QQuickShaderEffectSource::mipmap() const
846 void QQuickShaderEffectSource::setMipmap(bool enabled)
848 if (enabled == m_mipmap)
852 emit mipmapChanged();
856 \qmlproperty bool ShaderEffectSource::recursive
858 Set this property to true if the ShaderEffectSource has a dependency on
859 itself. ShaderEffectSources form a dependency chain, where one
860 ShaderEffectSource can be part of the \l sourceItem of another.
861 If there is a loop in this chain, a ShaderEffectSource could end up trying
862 to render into the same texture it is using as source, which is not allowed
863 by OpenGL. When this property is set to true, an extra texture is allocated
864 so that ShaderEffectSource can keep a copy of the texture from the previous
865 frame. It can then render into one texture and use the texture from the
866 previous frame as source.
868 Setting both this property and \l live to true will cause the scene graph
869 to render continuously. Since the ShaderEffectSource depends on itself,
870 updating it means that it immediately becomes dirty again.
873 bool QQuickShaderEffectSource::recursive() const
878 void QQuickShaderEffectSource::setRecursive(bool enabled)
880 if (enabled == m_recursive)
882 m_recursive = enabled;
883 emit recursiveChanged();
887 \qmlmethod ShaderEffectSource::scheduleUpdate()
889 Schedules a re-rendering of the texture for the next frame.
890 Use this to update the texture when \l live is false.
893 void QQuickShaderEffectSource::scheduleUpdate()
901 static void get_wrap_mode(QQuickShaderEffectSource::WrapMode mode, QSGTexture::WrapMode *hWrap, QSGTexture::WrapMode *vWrap)
904 case QQuickShaderEffectSource::RepeatHorizontally:
905 *hWrap = QSGTexture::Repeat;
906 *vWrap = QSGTexture::ClampToEdge;
908 case QQuickShaderEffectSource::RepeatVertically:
909 *vWrap = QSGTexture::Repeat;
910 *hWrap = QSGTexture::ClampToEdge;
912 case QQuickShaderEffectSource::Repeat:
913 *hWrap = *vWrap = QSGTexture::Repeat;
916 // QQuickShaderEffectSource::ClampToEdge
917 *hWrap = *vWrap = QSGTexture::ClampToEdge;
923 void QQuickShaderEffectSource::releaseResources()
926 m_texture->deleteLater();
930 m_provider->deleteLater();
935 QSGNode *QQuickShaderEffectSource::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
937 if (!m_sourceItem || m_sourceItem->width() == 0 || m_sourceItem->height() == 0) {
939 m_texture->setItem(0);
946 m_texture->setLive(m_live);
947 m_texture->setItem(QQuickItemPrivate::get(m_sourceItem)->itemNode());
948 QRectF sourceRect = m_sourceRect.width() == 0 || m_sourceRect.height() == 0
949 ? QRectF(0, 0, m_sourceItem->width(), m_sourceItem->height())
951 m_texture->setRect(sourceRect);
952 QSize textureSize = m_textureSize.isEmpty()
953 ? QSize(qCeil(qAbs(sourceRect.width())), qCeil(qAbs(sourceRect.height())))
955 Q_ASSERT(!textureSize.isEmpty());
956 QQuickItemPrivate *d = static_cast<QQuickItemPrivate *>(QObjectPrivate::get(this));
957 const QSize minTextureSize = d->sceneGraphContext()->minimumFBOSize();
958 // Keep power-of-two by doubling the size.
959 while (textureSize.width() < minTextureSize.width())
960 textureSize.rwidth() *= 2;
961 while (textureSize.height() < minTextureSize.height())
962 textureSize.rheight() *= 2;
964 m_texture->setSize(textureSize);
965 m_texture->setRecursive(m_recursive);
966 m_texture->setFormat(GLenum(m_format));
967 m_texture->setHasMipmaps(m_mipmap);
970 m_texture->scheduleUpdate();
973 QSGTexture::Filtering filtering = QQuickItemPrivate::get(this)->smooth
975 : QSGTexture::Nearest;
976 QSGTexture::Filtering mmFiltering = m_mipmap ? filtering : QSGTexture::None;
977 QSGTexture::WrapMode hWrap, vWrap;
978 get_wrap_mode(m_wrapMode, &hWrap, &vWrap);
981 m_provider->mipmapFiltering = mmFiltering;
982 m_provider->filtering = filtering;
983 m_provider->horizontalWrap = hWrap;
984 m_provider->verticalWrap = vWrap;
987 // Don't create the paint node if we're not spanning any area
988 if (width() == 0 || height() == 0) {
993 QQuickShaderEffectSourceNode *node = static_cast<QQuickShaderEffectSourceNode *>(oldNode);
995 node = new QQuickShaderEffectSourceNode;
996 node->setTexture(m_texture);
997 connect(m_texture, SIGNAL(updateRequested()), node, SLOT(markDirtyTexture()));
1000 // If live and recursive, update continuously.
1001 if (m_live && m_recursive)
1002 node->markDirty(QSGNode::DirtyMaterial);
1004 node->setMipmapFiltering(mmFiltering);
1005 node->setFiltering(filtering);
1006 node->setHorizontalWrapMode(hWrap);
1007 node->setVerticalWrapMode(vWrap);
1008 node->setTargetRect(QRectF(0, 0, width(), height()));
1009 node->setInnerTargetRect(QRectF(0, 0, width(), height()));
1015 void QQuickShaderEffectSource::itemChange(ItemChange change, const ItemChangeData &value)
1017 if (change == QQuickItem::ItemSceneChange && m_sourceItem) {
1018 // See comment in QQuickShaderEffectSource::setSourceItem().
1020 QQuickItemPrivate::get(m_sourceItem)->refCanvas(value.canvas);
1022 QQuickItemPrivate::get(m_sourceItem)->derefCanvas();
1024 QQuickItem::itemChange(change, value);