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_multisamplingSupportChecked(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 if ((m_live || m_grab) && m_dirtyTexture) {
197 void QQuickShaderEffectTexture::setHasMipmaps(bool mipmap)
199 if (mipmap == m_mipmap)
202 if (m_mipmap && m_fbo && !m_fbo->format().mipmap())
207 void QQuickShaderEffectTexture::setItem(QSGNode *item)
213 if (m_live && !m_item) {
215 delete m_secondaryFbo;
216 m_fbo = m_secondaryFbo = 0;
217 m_depthStencilBuffer.clear();
223 void QQuickShaderEffectTexture::setRect(const QRectF &rect)
231 void QQuickShaderEffectTexture::setSize(const QSize &size)
237 if (m_live && m_size.isNull()) {
239 delete m_secondaryFbo;
240 m_fbo = m_secondaryFbo = 0;
241 m_depthStencilBuffer.clear();
247 void QQuickShaderEffectTexture::setFormat(GLenum format)
249 if (format == m_format)
255 void QQuickShaderEffectTexture::setLive(bool live)
261 if (m_live && (!m_item || m_size.isNull())) {
263 delete m_secondaryFbo;
264 m_fbo = m_secondaryFbo = 0;
265 m_depthStencilBuffer.clear();
271 void QQuickShaderEffectTexture::scheduleUpdate()
277 emit updateRequested();
280 void QQuickShaderEffectTexture::setRecursive(bool recursive)
282 m_recursive = recursive;
285 void QQuickShaderEffectTexture::markDirtyTexture()
287 m_dirtyTexture = true;
288 if (m_live || m_grab)
289 emit updateRequested();
292 void QQuickShaderEffectTexture::grab()
294 if (!m_item || m_size.isNull()) {
296 delete m_secondaryFbo;
297 m_fbo = m_secondaryFbo = 0;
298 m_depthStencilBuffer.clear();
299 m_dirtyTexture = false;
301 emit scheduledUpdateCompleted();
304 QSGNode *root = m_item;
305 while (root->firstChild() && root->type() != QSGNode::RootNodeType)
306 root = root->firstChild();
307 if (root->type() != QSGNode::RootNodeType)
311 m_renderer = m_context->createRenderer();
312 connect(m_renderer, SIGNAL(sceneGraphChanged()), this, SLOT(markDirtyTexture()));
314 m_renderer->setRootNode(static_cast<QSGRootNode *>(root));
316 bool deleteFboLater = false;
317 if (!m_fbo || m_fbo->size() != m_size || m_fbo->format().internalTextureFormat() != m_format
318 || (!m_fbo->format().mipmap() && m_mipmap))
320 if (!m_multisamplingSupportChecked) {
321 QList<QByteArray> extensions = QByteArray((const char *)glGetString(GL_EXTENSIONS)).split(' ');
322 m_multisampling = extensions.contains("GL_EXT_framebuffer_multisample")
323 && extensions.contains("GL_EXT_framebuffer_blit");
324 m_multisamplingSupportChecked = true;
326 if (m_multisampling) {
327 // Don't delete the FBO right away in case it is used recursively.
328 deleteFboLater = true;
329 delete m_secondaryFbo;
330 QOpenGLFramebufferObjectFormat format;
332 format.setInternalTextureFormat(m_format);
333 format.setSamples(8);
334 m_secondaryFbo = new QOpenGLFramebufferObject(m_size, format);
335 m_depthStencilBuffer = m_context->depthStencilBufferForFbo(m_secondaryFbo);
337 QOpenGLFramebufferObjectFormat format;
338 format.setInternalTextureFormat(m_format);
339 format.setMipmap(m_mipmap);
341 deleteFboLater = true;
342 delete m_secondaryFbo;
343 m_secondaryFbo = new QOpenGLFramebufferObject(m_size, format);
344 glBindTexture(GL_TEXTURE_2D, m_secondaryFbo->texture());
345 updateBindOptions(true);
346 m_depthStencilBuffer = m_context->depthStencilBufferForFbo(m_secondaryFbo);
349 delete m_secondaryFbo;
350 m_fbo = new QOpenGLFramebufferObject(m_size, format);
352 glBindTexture(GL_TEXTURE_2D, m_fbo->texture());
353 updateBindOptions(true);
354 m_depthStencilBuffer = m_context->depthStencilBufferForFbo(m_fbo);
359 if (m_recursive && !m_secondaryFbo) {
360 // m_fbo already created, m_recursive was just set.
362 Q_ASSERT(!m_multisampling);
364 m_secondaryFbo = new QOpenGLFramebufferObject(m_size, m_fbo->format());
365 glBindTexture(GL_TEXTURE_2D, m_secondaryFbo->texture());
366 updateBindOptions(true);
370 root->markDirty(QSGNode::DirtyForceUpdate); // Force matrix, clip and opacity update.
371 m_renderer->nodeChanged(root, QSGNode::DirtyForceUpdate); // Force render list update.
373 #ifdef QSG_DEBUG_FBO_OVERLAY
374 if (qmlFboOverlay()) {
376 m_debugOverlay = m_context->createRectangleNode();
377 m_debugOverlay->setRect(QRectF(0, 0, m_size.width(), m_size.height()));
378 m_debugOverlay->setColor(QColor(0xff, 0x00, 0x80, 0x40));
379 m_debugOverlay->setPenColor(QColor());
380 m_debugOverlay->setPenWidth(0);
381 m_debugOverlay->setRadius(0);
382 m_debugOverlay->update();
383 root->appendChildNode(m_debugOverlay);
387 m_dirtyTexture = false;
389 QOpenGLContext *ctx = m_context->glContext();
390 m_renderer->setDeviceRect(m_size);
391 m_renderer->setViewportRect(m_size);
392 QRectF mirrored(m_rect.left(), m_rect.bottom(), m_rect.width(), -m_rect.height());
393 m_renderer->setProjectionMatrixToRect(mirrored);
394 m_renderer->setClearColor(Qt::transparent);
396 if (m_multisampling) {
397 m_renderer->renderScene(BindableFbo(m_secondaryFbo, m_depthStencilBuffer.data()));
399 if (deleteFboLater) {
401 QOpenGLFramebufferObjectFormat format;
402 format.setInternalTextureFormat(m_format);
403 format.setAttachment(QOpenGLFramebufferObject::NoAttachment);
404 format.setMipmap(m_mipmap);
405 format.setSamples(0);
406 m_fbo = new QOpenGLFramebufferObject(m_size, format);
407 glBindTexture(GL_TEXTURE_2D, m_fbo->texture());
408 updateBindOptions(true);
411 QRect r(QPoint(), m_size);
412 QOpenGLFramebufferObject::blitFramebuffer(m_fbo, r, m_secondaryFbo, r);
415 m_renderer->renderScene(BindableFbo(m_secondaryFbo, m_depthStencilBuffer.data()));
417 if (deleteFboLater) {
419 QOpenGLFramebufferObjectFormat format;
420 format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
421 format.setInternalTextureFormat(m_format);
422 format.setMipmap(m_mipmap);
423 m_fbo = new QOpenGLFramebufferObject(m_size, format);
424 glBindTexture(GL_TEXTURE_2D, m_fbo->texture());
425 updateBindOptions(true);
427 qSwap(m_fbo, m_secondaryFbo);
429 m_renderer->renderScene(BindableFbo(m_fbo, m_depthStencilBuffer.data()));
434 glBindTexture(GL_TEXTURE_2D, textureId());
435 ctx->functions()->glGenerateMipmap(GL_TEXTURE_2D);
438 root->markDirty(QSGNode::DirtyForceUpdate); // Force matrix, clip, opacity and render list update.
440 #ifdef QSG_DEBUG_FBO_OVERLAY
442 root->removeChildNode(m_debugOverlay);
445 markDirtyTexture(); // Continuously update if 'live' and 'recursive'.
448 emit scheduledUpdateCompleted();
451 QImage QQuickShaderEffectTexture::toImage() const
454 return m_fbo->toImage();
460 \qmlclass ShaderEffectSource QQuickShaderEffectSource
462 \ingroup qml-basic-visual-elements
463 \brief The ShaderEffectSource element renders a QML element into a texture
467 The ShaderEffectSource element renders \l sourceItem into a texture and
468 displays it in the scene. \l sourceItem is drawn into the texture as though
469 it was a fully opaque root element. Thus \l sourceItem itself can be
470 invisible, but still appear in the texture.
472 ShaderEffectSource can be used as:
474 \li a texture source in a \l ShaderEffect.
475 This allows you to apply custom shader effects to any QML element.
476 \li a cache for a complex element.
477 The complex element can be rendered once into the texture, which can
478 then be animated freely without the need to render the complex element
480 \li an opacity layer.
481 ShaderEffectSource allows you to apply an opacity to elements as a group
482 rather than each element individually.
487 \li \image declarative-shadereffectsource.png
495 GradientStop { position: 0; color: "white" }
496 GradientStop { position: 1; color: "black" }
502 width: 100; height: 100
503 Rectangle { x: 5; y: 5; width: 60; height: 60; color: "red" }
504 Rectangle { x: 20; y: 20; width: 60; height: 60; color: "orange" }
505 Rectangle { x: 35; y: 35; width: 60; height: 60; color: "yellow" }
508 width: 100; height: 100
517 The ShaderEffectSource element 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 will be equivalent to 4x multisampling, at
535 the cost of lower performance and higher memory use.
537 \warning In most cases, using a ShaderEffectSource will decrease
538 performance, and in all cases, it will increase video memory usage.
539 Rendering through a ShaderEffectSource might also lead to lower quality
540 since some OpenGL implementations support multisampled backbuffer,
541 but not multisampled framebuffer objects.
544 QQuickShaderEffectSource::QQuickShaderEffectSource(QQuickItem *parent)
548 , m_wrapMode(ClampToEdge)
550 , m_textureSize(0, 0)
553 , m_hideSource(false)
558 setFlag(ItemHasContents);
561 QQuickShaderEffectSource::~QQuickShaderEffectSource()
564 m_texture->deleteLater();
567 m_provider->deleteLater();
570 QQuickItemPrivate *sd = QQuickItemPrivate::get(m_sourceItem);
571 sd->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
572 sd->derefFromEffectItem(m_hideSource);
578 void QQuickShaderEffectSource::ensureTexture()
583 Q_ASSERT_X(QQuickItemPrivate::get(this)->canvas
584 && QQuickItemPrivate::get(this)->sceneGraphContext()
585 && QThread::currentThread() == QQuickItemPrivate::get(this)->sceneGraphContext()->thread(),
586 "QQuickShaderEffectSource::ensureTexture",
587 "Cannot be used outside the rendering thread");
589 m_texture = new QQuickShaderEffectTexture(this);
590 connect(m_texture, SIGNAL(updateRequested()), this, SLOT(update()));
591 connect(m_texture, SIGNAL(scheduledUpdateCompleted()), this, SIGNAL(scheduledUpdateCompleted()));
594 QSGTextureProvider *QQuickShaderEffectSource::textureProvider() const
597 // Make sure it gets thread affinity on the rendering thread so deletion works properly..
598 Q_ASSERT_X(QQuickItemPrivate::get(this)->canvas
599 && QQuickItemPrivate::get(this)->sceneGraphContext()
600 && QThread::currentThread() == QQuickItemPrivate::get(this)->sceneGraphContext()->thread(),
601 "QQuickShaderEffectSource::textureProvider",
602 "Cannot be used outside the rendering thread");
603 const_cast<QQuickShaderEffectSource *>(this)->m_provider = new QQuickShaderEffectSourceTextureProvider();
604 const_cast<QQuickShaderEffectSource *>(this)->ensureTexture();
605 connect(m_texture, SIGNAL(updateRequested()), m_provider, SIGNAL(textureChanged()));
606 m_provider->sourceTexture = m_texture;
612 \qmlproperty enumeration ShaderEffectSource::wrapMode
614 This property defines the OpenGL wrap modes associated with the texture.
615 Modifying this property makes most sense when the element is used as a
616 source texture of a \l ShaderEffect.
619 \li ShaderEffectSource.ClampToEdge - GL_CLAMP_TO_EDGE both horizontally and vertically
620 \li ShaderEffectSource.RepeatHorizontally - GL_REPEAT horizontally, GL_CLAMP_TO_EDGE vertically
621 \li ShaderEffectSource.RepeatVertically - GL_CLAMP_TO_EDGE horizontally, GL_REPEAT vertically
622 \li ShaderEffectSource.Repeat - GL_REPEAT both horizontally and vertically
625 \note Some OpenGL ES 2 implementations do not support the GL_REPEAT
626 wrap mode with non-power-of-two textures.
629 QQuickShaderEffectSource::WrapMode QQuickShaderEffectSource::wrapMode() const
634 void QQuickShaderEffectSource::setWrapMode(WrapMode mode)
636 if (mode == m_wrapMode)
640 emit wrapModeChanged();
644 \qmlproperty Item ShaderEffectSource::sourceItem
646 This property holds the element to be rendered into the texture.
647 Setting this to null while \l live is true, will release the texture
651 QQuickItem *QQuickShaderEffectSource::sourceItem() const
656 void QQuickShaderEffectSource::itemGeometryChanged(QQuickItem *item, const QRectF &newRect, const QRectF &oldRect)
658 Q_ASSERT(item == m_sourceItem);
660 if (newRect.size() != oldRect.size())
664 void QQuickShaderEffectSource::setSourceItem(QQuickItem *item)
666 if (item == m_sourceItem)
669 QQuickItemPrivate *d = QQuickItemPrivate::get(m_sourceItem);
670 d->derefFromEffectItem(m_hideSource);
671 d->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
672 disconnect(m_sourceItem, SIGNAL(destroyed(QObject*)), this, SLOT(sourceItemDestroyed(QObject*)));
679 QQuickItemPrivate *d = QQuickItemPrivate::get(item);
680 // 'item' needs a canvas to get a scene graph node. It usually gets one through its
681 // parent, but if the source item is "inline" rather than a reference -- i.e.
682 // "sourceItem: Item { }" instead of "sourceItem: foo" -- it will not get a parent.
683 // In those cases, 'item' should get the canvas from 'this'.
685 d->refCanvas(canvas());
686 d->refFromEffectItem(m_hideSource);
687 d->addItemChangeListener(this, QQuickItemPrivate::Geometry);
688 connect(m_sourceItem, SIGNAL(destroyed(QObject*)), this, SLOT(sourceItemDestroyed(QObject*)));
691 emit sourceItemChanged();
694 void QQuickShaderEffectSource::sourceItemDestroyed(QObject *item)
696 Q_ASSERT(item == m_sourceItem);
699 emit sourceItemChanged();
704 \qmlproperty rect ShaderEffectSource::sourceRect
706 This property defines which rectangular area of the \l sourceItem to
707 render into the texture. The source rectangle can be larger than
708 \l sourceItem itself. If the rectangle is null, which is the default,
709 the whole \l sourceItem is rendered to texture.
712 QRectF QQuickShaderEffectSource::sourceRect() const
717 void QQuickShaderEffectSource::setSourceRect(const QRectF &rect)
719 if (rect == m_sourceRect)
723 emit sourceRectChanged();
727 \qmlproperty size ShaderEffectSource::textureSize
729 This property holds the requested size of the texture. If it is empty,
730 which is the default, the size of the source rectangle is used.
732 \note Some platforms have a limit on how small framebuffer objects can be,
733 which means the actual texture size might be larger than the requested
737 QSize QQuickShaderEffectSource::textureSize() const
739 return m_textureSize;
742 void QQuickShaderEffectSource::setTextureSize(const QSize &size)
744 if (size == m_textureSize)
746 m_textureSize = size;
748 emit textureSizeChanged();
752 \qmlproperty enumeration ShaderEffectSource::format
754 This property defines the internal OpenGL format of the texture.
755 Modifying this property makes most sense when the element is used as a
756 source texture of a \l ShaderEffect. Depending on the OpenGL
757 implementation, this property might allow you to save some texture memory.
760 \li ShaderEffectSource.Alpha - GL_ALPHA
761 \li ShaderEffectSource.RGB - GL_RGB
762 \li ShaderEffectSource.RGBA - GL_RGBA
765 \note Some OpenGL implementations do not support the GL_ALPHA format.
768 QQuickShaderEffectSource::Format QQuickShaderEffectSource::format() const
773 void QQuickShaderEffectSource::setFormat(QQuickShaderEffectSource::Format format)
775 if (format == m_format)
779 emit formatChanged();
783 \qmlproperty bool ShaderEffectSource::live
785 If this property is true, the texture is updated whenever the
786 \l sourceItem updates. Otherwise, it will be a frozen image, even if
787 \l sourceItem is assigned a new element. The property is true by default.
790 bool QQuickShaderEffectSource::live() const
795 void QQuickShaderEffectSource::setLive(bool live)
805 \qmlproperty bool ShaderEffectSource::hideSource
807 If this property is true, the \l sourceItem is hidden, though it will still
808 be rendered into the texture. As opposed to hiding the \l sourceItem by
809 setting \l{Item::visible}{visible} to false, setting this property to true
810 will not prevent mouse or keyboard input from reaching \l sourceItem.
811 The property is useful when the ShaderEffectSource is anchored on top of,
812 and meant to replace the \l sourceItem.
815 bool QQuickShaderEffectSource::hideSource() const
820 void QQuickShaderEffectSource::setHideSource(bool hide)
822 if (hide == m_hideSource)
825 QQuickItemPrivate::get(m_sourceItem)->refFromEffectItem(hide);
826 QQuickItemPrivate::get(m_sourceItem)->derefFromEffectItem(m_hideSource);
830 emit hideSourceChanged();
834 \qmlproperty bool ShaderEffectSource::mipmap
836 If this property is true, mipmaps are generated for the texture.
838 \note Some OpenGL ES 2 implementations do not support mipmapping of
839 non-power-of-two textures.
842 bool QQuickShaderEffectSource::mipmap() const
847 void QQuickShaderEffectSource::setMipmap(bool enabled)
849 if (enabled == m_mipmap)
853 emit mipmapChanged();
857 \qmlproperty bool ShaderEffectSource::recursive
859 Set this property to true if the ShaderEffectSource has a dependency on
860 itself. ShaderEffectSources form a dependency chain, where one
861 ShaderEffectSource can be part of the \l sourceItem of another.
862 If there is a loop in this chain, a ShaderEffectSource could end up trying
863 to render into the same texture it is using as source, which is not allowed
864 by OpenGL. When this property is set to true, an extra texture is allocated
865 so that ShaderEffectSource can keep a copy of the texture from the previous
866 frame. It can then render into one texture and use the texture from the
867 previous frame as source.
869 Setting both this property and \l live to true will cause the scene graph
870 to render continuously. Since the ShaderEffectSource depends on itself,
871 updating it means that it immediately becomes dirty again.
874 bool QQuickShaderEffectSource::recursive() const
879 void QQuickShaderEffectSource::setRecursive(bool enabled)
881 if (enabled == m_recursive)
883 m_recursive = enabled;
884 emit recursiveChanged();
888 \qmlmethod ShaderEffectSource::scheduleUpdate()
890 Schedules a re-rendering of the texture for the next frame.
891 Use this to update the texture when \l live is false.
894 void QQuickShaderEffectSource::scheduleUpdate()
902 static void get_wrap_mode(QQuickShaderEffectSource::WrapMode mode, QSGTexture::WrapMode *hWrap, QSGTexture::WrapMode *vWrap)
905 case QQuickShaderEffectSource::RepeatHorizontally:
906 *hWrap = QSGTexture::Repeat;
907 *vWrap = QSGTexture::ClampToEdge;
909 case QQuickShaderEffectSource::RepeatVertically:
910 *vWrap = QSGTexture::Repeat;
911 *hWrap = QSGTexture::ClampToEdge;
913 case QQuickShaderEffectSource::Repeat:
914 *hWrap = *vWrap = QSGTexture::Repeat;
917 // QQuickShaderEffectSource::ClampToEdge
918 *hWrap = *vWrap = QSGTexture::ClampToEdge;
924 void QQuickShaderEffectSource::releaseResources()
927 m_texture->deleteLater();
931 m_provider->deleteLater();
936 QSGNode *QQuickShaderEffectSource::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
938 if (!m_sourceItem || m_sourceItem->width() == 0 || m_sourceItem->height() == 0) {
940 m_texture->setItem(0);
947 m_texture->setLive(m_live);
948 m_texture->setItem(QQuickItemPrivate::get(m_sourceItem)->itemNode());
949 QRectF sourceRect = m_sourceRect.width() == 0 || m_sourceRect.height() == 0
950 ? QRectF(0, 0, m_sourceItem->width(), m_sourceItem->height())
952 m_texture->setRect(sourceRect);
953 QSize textureSize = m_textureSize.isEmpty()
954 ? QSize(qCeil(qAbs(sourceRect.width())), qCeil(qAbs(sourceRect.height())))
956 Q_ASSERT(!textureSize.isEmpty());
957 QQuickItemPrivate *d = static_cast<QQuickItemPrivate *>(QObjectPrivate::get(this));
958 const QSize minTextureSize = d->sceneGraphContext()->minimumFBOSize();
959 // Keep power-of-two by doubling the size.
960 while (textureSize.width() < minTextureSize.width())
961 textureSize.rwidth() *= 2;
962 while (textureSize.height() < minTextureSize.height())
963 textureSize.rheight() *= 2;
965 m_texture->setSize(textureSize);
966 m_texture->setRecursive(m_recursive);
967 m_texture->setFormat(GLenum(m_format));
968 m_texture->setHasMipmaps(m_mipmap);
971 m_texture->scheduleUpdate();
974 QSGTexture::Filtering filtering = QQuickItemPrivate::get(this)->smooth
976 : QSGTexture::Nearest;
977 QSGTexture::Filtering mmFiltering = m_mipmap ? filtering : QSGTexture::None;
978 QSGTexture::WrapMode hWrap, vWrap;
979 get_wrap_mode(m_wrapMode, &hWrap, &vWrap);
982 m_provider->mipmapFiltering = mmFiltering;
983 m_provider->filtering = filtering;
984 m_provider->horizontalWrap = hWrap;
985 m_provider->verticalWrap = vWrap;
988 // Don't create the paint node if we're not spanning any area
989 if (width() == 0 || height() == 0) {
994 QQuickShaderEffectSourceNode *node = static_cast<QQuickShaderEffectSourceNode *>(oldNode);
996 node = new QQuickShaderEffectSourceNode;
997 node->setTexture(m_texture);
998 connect(m_texture, SIGNAL(updateRequested()), node, SLOT(markDirtyTexture()));
1001 // If live and recursive, update continuously.
1002 if (m_live && m_recursive)
1003 node->markDirty(QSGNode::DirtyMaterial);
1005 node->setMipmapFiltering(mmFiltering);
1006 node->setFiltering(filtering);
1007 node->setHorizontalWrapMode(hWrap);
1008 node->setVerticalWrapMode(vWrap);
1009 node->setTargetRect(QRectF(0, 0, width(), height()));
1010 node->setSourceRect(QRectF(0, 0, 1, 1));
1016 void QQuickShaderEffectSource::itemChange(ItemChange change, const ItemChangeData &value)
1018 if (change == QQuickItem::ItemSceneChange && m_sourceItem) {
1019 // See comment in QQuickShaderEffectSource::setSourceItem().
1021 QQuickItemPrivate::get(m_sourceItem)->refCanvas(value.canvas);
1023 QQuickItemPrivate::get(m_sourceItem)->derefCanvas();
1025 QQuickItem::itemChange(change, value);