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)
57 class QQuickShaderEffectSourceTextureProvider : public QSGTextureProvider
61 QQuickShaderEffectSourceTextureProvider()
66 QSGTexture *texture() const {
67 sourceTexture->setMipmapFiltering(mipmapFiltering);
68 sourceTexture->setFiltering(filtering);
69 sourceTexture->setHorizontalWrapMode(horizontalWrap);
70 sourceTexture->setVerticalWrapMode(verticalWrap);
74 QQuickShaderEffectTexture *sourceTexture;
76 QSGTexture::Filtering mipmapFiltering;
77 QSGTexture::Filtering filtering;
78 QSGTexture::WrapMode horizontalWrap;
79 QSGTexture::WrapMode verticalWrap;
81 #include "qquickshadereffectsource.moc"
84 QQuickShaderEffectSourceNode::QQuickShaderEffectSourceNode()
86 setFlag(UsePreprocess, true);
89 void QQuickShaderEffectSourceNode::markDirtyTexture()
91 markDirty(DirtyMaterial);
95 QQuickShaderEffectTexture::QQuickShaderEffectTexture(QQuickItem *shaderSource)
99 , m_shaderSource(shaderSource)
103 #ifdef QSG_DEBUG_FBO_OVERLAY
106 , m_context(QQuickItemPrivate::get(shaderSource)->sceneGraphContext())
110 , m_dirtyTexture(true)
111 , m_multisamplingSupportChecked(false)
112 , m_multisampling(false)
117 QQuickShaderEffectTexture::~QQuickShaderEffectTexture()
120 disconnect(m_renderer, SIGNAL(sceneGraphChanged()), this, SLOT(markDirtyTexture()));
123 delete m_secondaryFbo;
124 #ifdef QSG_DEBUG_FBO_OVERLAY
125 delete m_debugOverlay;
129 int QQuickShaderEffectTexture::textureId() const
131 return m_fbo ? m_fbo->texture() : 0;
134 bool QQuickShaderEffectTexture::hasAlphaChannel() const
136 return m_format != GL_RGB;
139 bool QQuickShaderEffectTexture::hasMipmaps() const
145 void QQuickShaderEffectTexture::bind()
148 if (!m_recursive && m_fbo && ((m_multisampling && m_secondaryFbo->isBound()) || m_fbo->isBound()))
149 qWarning("ShaderEffectSource: \'recursive\' must be set to true when rendering recursively.");
151 glBindTexture(GL_TEXTURE_2D, m_fbo ? m_fbo->texture() : 0);
155 bool QQuickShaderEffectTexture::updateTexture()
157 if ((m_live || m_grab) && m_dirtyTexture) {
165 void QQuickShaderEffectTexture::setHasMipmaps(bool mipmap)
167 if (mipmap == m_mipmap)
170 if (m_mipmap && m_fbo && !m_fbo->format().mipmap())
175 void QQuickShaderEffectTexture::setItem(QSGNode *item)
183 void QQuickShaderEffectTexture::setRect(const QRectF &rect)
191 void QQuickShaderEffectTexture::setSize(const QSize &size)
199 void QQuickShaderEffectTexture::setFormat(GLenum format)
201 if (format == m_format)
207 void QQuickShaderEffectTexture::setLive(bool live)
215 void QQuickShaderEffectTexture::scheduleUpdate()
221 emit updateRequested();
224 void QQuickShaderEffectTexture::setRecursive(bool recursive)
226 m_recursive = recursive;
229 void QQuickShaderEffectTexture::markDirtyTexture()
231 m_dirtyTexture = true;
232 if (m_live || m_grab)
233 emit updateRequested();
236 void QQuickShaderEffectTexture::grab()
238 if (!m_item || m_size.isNull()) {
240 delete m_secondaryFbo;
241 m_fbo = m_secondaryFbo = 0;
242 m_dirtyTexture = false;
244 emit scheduledUpdateCompleted();
247 QSGNode *root = m_item;
248 while (root->firstChild() && root->type() != QSGNode::RootNodeType)
249 root = root->firstChild();
250 if (root->type() != QSGNode::RootNodeType)
254 m_renderer = m_context->createRenderer();
255 connect(m_renderer, SIGNAL(sceneGraphChanged()), this, SLOT(markDirtyTexture()));
257 m_renderer->setRootNode(static_cast<QSGRootNode *>(root));
259 bool deleteFboLater = false;
260 if (!m_fbo || m_fbo->size() != m_size || m_fbo->format().internalTextureFormat() != m_format
261 || (!m_fbo->format().mipmap() && m_mipmap))
263 if (!m_multisamplingSupportChecked) {
264 QList<QByteArray> extensions = QByteArray((const char *)glGetString(GL_EXTENSIONS)).split(' ');
265 m_multisampling = extensions.contains("GL_EXT_framebuffer_multisample")
266 && extensions.contains("GL_EXT_framebuffer_blit");
267 m_multisamplingSupportChecked = true;
269 if (m_multisampling) {
270 // Don't delete the FBO right away in case it is used recursively.
271 deleteFboLater = true;
272 delete m_secondaryFbo;
273 QOpenGLFramebufferObjectFormat format;
275 format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
276 format.setInternalTextureFormat(m_format);
277 format.setSamples(8);
278 m_secondaryFbo = new QOpenGLFramebufferObject(m_size, format);
280 QOpenGLFramebufferObjectFormat format;
281 format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
282 format.setInternalTextureFormat(m_format);
283 format.setMipmap(m_mipmap);
285 deleteFboLater = true;
286 delete m_secondaryFbo;
287 m_secondaryFbo = new QOpenGLFramebufferObject(m_size, format);
288 glBindTexture(GL_TEXTURE_2D, m_secondaryFbo->texture());
289 updateBindOptions(true);
292 delete m_secondaryFbo;
293 m_fbo = new QOpenGLFramebufferObject(m_size, format);
295 glBindTexture(GL_TEXTURE_2D, m_fbo->texture());
296 updateBindOptions(true);
301 if (m_recursive && !m_secondaryFbo) {
302 // m_fbo already created, m_recursive was just set.
304 Q_ASSERT(!m_multisampling);
306 m_secondaryFbo = new QOpenGLFramebufferObject(m_size, m_fbo->format());
307 glBindTexture(GL_TEXTURE_2D, m_secondaryFbo->texture());
308 updateBindOptions(true);
312 root->markDirty(QSGNode::DirtyForceUpdate); // Force matrix, clip and opacity update.
313 m_renderer->nodeChanged(root, QSGNode::DirtyForceUpdate); // Force render list update.
315 #ifdef QSG_DEBUG_FBO_OVERLAY
316 if (qmlFboOverlay()) {
318 m_debugOverlay = m_context->createRectangleNode();
319 m_debugOverlay->setRect(QRectF(0, 0, m_size.width(), m_size.height()));
320 m_debugOverlay->setColor(QColor(0xff, 0x00, 0x80, 0x40));
321 m_debugOverlay->setPenColor(QColor());
322 m_debugOverlay->setPenWidth(0);
323 m_debugOverlay->setRadius(0);
324 m_debugOverlay->update();
325 root->appendChildNode(m_debugOverlay);
329 m_dirtyTexture = false;
331 QOpenGLContext *ctx = m_context->glContext();
332 m_renderer->setDeviceRect(m_size);
333 m_renderer->setViewportRect(m_size);
334 QRectF mirrored(m_rect.left(), m_rect.bottom(), m_rect.width(), -m_rect.height());
335 m_renderer->setProjectionMatrixToRect(mirrored);
336 m_renderer->setClearColor(Qt::transparent);
338 if (m_multisampling) {
339 m_renderer->renderScene(QSGBindableFbo(m_secondaryFbo));
341 if (deleteFboLater) {
343 QOpenGLFramebufferObjectFormat format;
344 format.setInternalTextureFormat(m_format);
345 format.setAttachment(QOpenGLFramebufferObject::NoAttachment);
346 format.setMipmap(m_mipmap);
347 format.setSamples(0);
348 m_fbo = new QOpenGLFramebufferObject(m_size, format);
349 glBindTexture(GL_TEXTURE_2D, m_fbo->texture());
350 updateBindOptions(true);
353 QRect r(QPoint(), m_size);
354 QOpenGLFramebufferObject::blitFramebuffer(m_fbo, r, m_secondaryFbo, r);
357 m_renderer->renderScene(QSGBindableFbo(m_secondaryFbo));
359 if (deleteFboLater) {
361 QOpenGLFramebufferObjectFormat format;
362 format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
363 format.setInternalTextureFormat(m_format);
364 format.setMipmap(m_mipmap);
365 m_fbo = new QOpenGLFramebufferObject(m_size, format);
366 glBindTexture(GL_TEXTURE_2D, m_fbo->texture());
367 updateBindOptions(true);
369 qSwap(m_fbo, m_secondaryFbo);
371 m_renderer->renderScene(QSGBindableFbo(m_fbo));
376 glBindTexture(GL_TEXTURE_2D, textureId());
377 ctx->functions()->glGenerateMipmap(GL_TEXTURE_2D);
380 root->markDirty(QSGNode::DirtyForceUpdate); // Force matrix, clip, opacity and render list update.
382 #ifdef QSG_DEBUG_FBO_OVERLAY
384 root->removeChildNode(m_debugOverlay);
387 markDirtyTexture(); // Continuously update if 'live' and 'recursive'.
390 emit scheduledUpdateCompleted();
393 QImage QQuickShaderEffectTexture::toImage() const
396 return m_fbo->toImage();
402 \qmlclass ShaderEffectSource QQuickShaderEffectSource
404 \ingroup qml-basic-visual-elements
405 \brief The ShaderEffectSource element renders a QML element into a texture
409 The ShaderEffectSource element renders \l sourceItem into a texture and
410 displays it in the scene. \l sourceItem is drawn into the texture as though
411 it was a fully opaque root element. Thus \l sourceItem itself can be
412 invisible, but still appear in the texture.
414 ShaderEffectSource can be used as:
416 \li a texture source in a \l ShaderEffect.
417 This allows you to apply custom shader effects to any QML element.
418 \li a cache for a complex element.
419 The complex element can be rendered once into the texture, which can
420 then be animated freely without the need to render the complex element
422 \li an opacity layer.
423 ShaderEffectSource allows you to apply an opacity to elements as a group
424 rather than each element individually.
429 \li \image declarative-shadereffectsource.png
437 GradientStop { position: 0; color: "white" }
438 GradientStop { position: 1; color: "black" }
444 width: 100; height: 100
445 Rectangle { x: 5; y: 5; width: 60; height: 60; color: "red" }
446 Rectangle { x: 20; y: 20; width: 60; height: 60; color: "orange" }
447 Rectangle { x: 35; y: 35; width: 60; height: 60; color: "yellow" }
450 width: 100; height: 100
459 The ShaderEffectSource element does not redirect any mouse or keyboard
460 input to \l sourceItem. If you hide the \l sourceItem by setting
461 \l{Item::visible}{visible} to false or \l{Item::opacity}{opacity} to zero,
462 it will no longer react to input. In cases where the ShaderEffectSource is
463 meant to replace the \l sourceItem, you typically want to hide the
464 \l sourceItem while still handling input. For this, you can use
465 the \l hideSource property.
467 \note If \l sourceItem is a \l Rectangle with border, by default half the
468 border width falls outside the texture. To get the whole border, you can
469 extend the \l sourceRect.
471 \warning In most cases, using a ShaderEffectSource will decrease
472 performance, and in all cases, it will increase video memory usage.
473 Rendering through a ShaderEffectSource might also lead to lower quality
474 since some OpenGL implementations support multisampled backbuffer,
475 but not multisampled framebuffer objects.
478 QQuickShaderEffectSource::QQuickShaderEffectSource(QQuickItem *parent)
482 , m_wrapMode(ClampToEdge)
484 , m_textureSize(0, 0)
487 , m_hideSource(false)
492 setFlag(ItemHasContents);
495 QQuickShaderEffectSource::~QQuickShaderEffectSource()
498 m_texture->deleteLater();
501 m_provider->deleteLater();
504 QQuickItemPrivate *sd = QQuickItemPrivate::get(m_sourceItem);
505 sd->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
506 sd->derefFromEffectItem(m_hideSource);
510 void QQuickShaderEffectSource::ensureTexture()
515 Q_ASSERT_X(QQuickItemPrivate::get(this)->canvas
516 && QQuickItemPrivate::get(this)->sceneGraphContext()
517 && QThread::currentThread() == QQuickItemPrivate::get(this)->sceneGraphContext()->thread(),
518 "QQuickShaderEffectSource::ensureTexture",
519 "Cannot be used outside the rendering thread");
521 m_texture = new QQuickShaderEffectTexture(this);
522 connect(m_texture, SIGNAL(updateRequested()), this, SLOT(update()));
523 connect(m_texture, SIGNAL(scheduledUpdateCompleted()), this, SIGNAL(scheduledUpdateCompleted()));
526 QSGTextureProvider *QQuickShaderEffectSource::textureProvider() const
529 // Make sure it gets thread affinity on the rendering thread so deletion works properly..
530 Q_ASSERT_X(QQuickItemPrivate::get(this)->canvas
531 && QQuickItemPrivate::get(this)->sceneGraphContext()
532 && QThread::currentThread() == QQuickItemPrivate::get(this)->sceneGraphContext()->thread(),
533 "QQuickShaderEffectSource::textureProvider",
534 "Cannot be used outside the rendering thread");
535 const_cast<QQuickShaderEffectSource *>(this)->m_provider = new QQuickShaderEffectSourceTextureProvider();
536 const_cast<QQuickShaderEffectSource *>(this)->ensureTexture();
537 connect(m_texture, SIGNAL(updateRequested()), m_provider, SIGNAL(textureChanged()));
538 m_provider->sourceTexture = m_texture;
544 \qmlproperty enumeration ShaderEffectSource::wrapMode
546 This property defines the OpenGL wrap modes associated with the texture.
547 Modifying this property makes most sense when the element is used as a
548 source texture of a \l ShaderEffect.
551 \li ShaderEffectSource.ClampToEdge - GL_CLAMP_TO_EDGE both horizontally and vertically
552 \li ShaderEffectSource.RepeatHorizontally - GL_REPEAT horizontally, GL_CLAMP_TO_EDGE vertically
553 \li ShaderEffectSource.RepeatVertically - GL_CLAMP_TO_EDGE horizontally, GL_REPEAT vertically
554 \li ShaderEffectSource.Repeat - GL_REPEAT both horizontally and vertically
557 \note Some OpenGL ES 2 implementations do not support the GL_REPEAT
558 wrap mode with non-power-of-two textures.
561 QQuickShaderEffectSource::WrapMode QQuickShaderEffectSource::wrapMode() const
566 void QQuickShaderEffectSource::setWrapMode(WrapMode mode)
568 if (mode == m_wrapMode)
572 emit wrapModeChanged();
576 \qmlproperty Item ShaderEffectSource::sourceItem
578 This property holds the element to be rendered into the texture.
581 QQuickItem *QQuickShaderEffectSource::sourceItem() const
586 void QQuickShaderEffectSource::itemGeometryChanged(QQuickItem *item, const QRectF &newRect, const QRectF &oldRect)
588 Q_ASSERT(item == m_sourceItem);
590 if (newRect.size() != oldRect.size())
594 void QQuickShaderEffectSource::setSourceItem(QQuickItem *item)
596 if (item == m_sourceItem)
599 QQuickItemPrivate *d = QQuickItemPrivate::get(m_sourceItem);
600 d->derefFromEffectItem(m_hideSource);
601 d->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
606 QQuickItemPrivate *d = QQuickItemPrivate::get(item);
607 // 'item' needs a canvas to get a scene graph node. It usually gets one through its
608 // parent, but if the source item is "inline" rather than a reference -- i.e.
609 // "sourceItem: Item { }" instead of "sourceItem: foo" -- it will not get a parent.
610 // In those cases, 'item' should get the canvas from 'this'.
611 if (!d->parentItem && canvas() && !d->canvas) {
612 QQuickItemPrivate::InitializationState initState;
614 d->initCanvas(&initState, canvas());
616 d->refFromEffectItem(m_hideSource);
617 d->addItemChangeListener(this, QQuickItemPrivate::Geometry);
620 emit sourceItemChanged();
624 \qmlproperty rect ShaderEffectSource::sourceRect
626 This property defines which rectangular area of the \l sourceItem to
627 render into the texture. The source rectangle can be larger than
628 \l sourceItem itself. If the rectangle is null, which is the default,
629 the whole \l sourceItem is rendered to texture.
632 QRectF QQuickShaderEffectSource::sourceRect() const
637 void QQuickShaderEffectSource::setSourceRect(const QRectF &rect)
639 if (rect == m_sourceRect)
643 emit sourceRectChanged();
647 \qmlproperty size ShaderEffectSource::textureSize
649 This property holds the requested size of the texture. If it is empty,
650 which is the default, the size of the source rectangle is used.
652 \note Some platforms have a limit on how small framebuffer objects can be,
653 which means the actual texture size might be larger than the requested
657 QSize QQuickShaderEffectSource::textureSize() const
659 return m_textureSize;
662 void QQuickShaderEffectSource::setTextureSize(const QSize &size)
664 if (size == m_textureSize)
666 m_textureSize = size;
668 emit textureSizeChanged();
672 \qmlproperty enumeration ShaderEffectSource::format
674 This property defines the internal OpenGL format of the texture.
675 Modifying this property makes most sense when the element is used as a
676 source texture of a \l ShaderEffect. Depending on the OpenGL
677 implementation, this property might allow you to save some texture memory.
680 \li ShaderEffectSource.Alpha - GL_ALPHA
681 \li ShaderEffectSource.RGB - GL_RGB
682 \li ShaderEffectSource.RGBA - GL_RGBA
685 \note Some OpenGL implementations do not support the GL_ALPHA format.
688 QQuickShaderEffectSource::Format QQuickShaderEffectSource::format() const
693 void QQuickShaderEffectSource::setFormat(QQuickShaderEffectSource::Format format)
695 if (format == m_format)
699 emit formatChanged();
703 \qmlproperty bool ShaderEffectSource::live
705 If this property is true, the texture is updated whenever the
706 \l sourceItem changes. Otherwise, it will be a frozen image of the
707 \l sourceItem. The property is true by default.
710 bool QQuickShaderEffectSource::live() const
715 void QQuickShaderEffectSource::setLive(bool live)
725 \qmlproperty bool ShaderEffectSource::hideSource
727 If this property is true, the \l sourceItem is hidden, though it will still
728 be rendered into the texture. As opposed to hiding the \l sourceItem by
729 setting \l{Item::visible}{visible} to false, setting this property to true
730 will not prevent mouse or keyboard input from reaching \l sourceItem.
731 The property is useful when the ShaderEffectSource is anchored on top of,
732 and meant to replace the \l sourceItem.
735 bool QQuickShaderEffectSource::hideSource() const
740 void QQuickShaderEffectSource::setHideSource(bool hide)
742 if (hide == m_hideSource)
745 QQuickItemPrivate::get(m_sourceItem)->refFromEffectItem(hide);
746 QQuickItemPrivate::get(m_sourceItem)->derefFromEffectItem(m_hideSource);
750 emit hideSourceChanged();
754 \qmlproperty bool ShaderEffectSource::mipmap
756 If this property is true, mipmaps are generated for the texture.
758 \note Some OpenGL ES 2 implementations do not support mipmapping of
759 non-power-of-two textures.
762 bool QQuickShaderEffectSource::mipmap() const
767 void QQuickShaderEffectSource::setMipmap(bool enabled)
769 if (enabled == m_mipmap)
773 emit mipmapChanged();
777 \qmlproperty bool ShaderEffectSource::recursive
779 Set this property to true if the ShaderEffectSource has a dependency on
780 itself. ShaderEffectSources form a dependency chain, where one
781 ShaderEffectSource can be part of the \l sourceItem of another.
782 If there is a loop in this chain, a ShaderEffectSource could end up trying
783 to render into the same texture it is using as source, which is not allowed
784 by OpenGL. When this property is set to true, an extra texture is allocated
785 so that ShaderEffectSource can keep a copy of the texture from the previous
786 frame. It can then render into one texture and use the texture from the
787 previous frame as source.
789 Setting both this property and \l live to true will cause the scene graph
790 to render continuously. Since the ShaderEffectSource depends on itself,
791 updating it means that it immediately becomes dirty again.
794 bool QQuickShaderEffectSource::recursive() const
799 void QQuickShaderEffectSource::setRecursive(bool enabled)
801 if (enabled == m_recursive)
803 m_recursive = enabled;
804 emit recursiveChanged();
808 \qmlmethod ShaderEffectSource::scheduleUpdate()
810 Schedules a re-rendering of the texture for the next frame.
811 Use this to update the texture when \l live is false.
814 void QQuickShaderEffectSource::scheduleUpdate()
822 static void get_wrap_mode(QQuickShaderEffectSource::WrapMode mode, QSGTexture::WrapMode *hWrap, QSGTexture::WrapMode *vWrap)
825 case QQuickShaderEffectSource::RepeatHorizontally:
826 *hWrap = QSGTexture::Repeat;
827 *vWrap = QSGTexture::ClampToEdge;
829 case QQuickShaderEffectSource::RepeatVertically:
830 *vWrap = QSGTexture::Repeat;
831 *hWrap = QSGTexture::ClampToEdge;
833 case QQuickShaderEffectSource::Repeat:
834 *hWrap = *vWrap = QSGTexture::Repeat;
837 // QQuickShaderEffectSource::ClampToEdge
838 *hWrap = *vWrap = QSGTexture::ClampToEdge;
844 QSGNode *QQuickShaderEffectSource::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
846 if (!m_sourceItem || m_sourceItem->width() == 0 || m_sourceItem->height() == 0) {
853 QQuickShaderEffectTexture *tex = qobject_cast<QQuickShaderEffectTexture *>(m_texture);
854 tex->setLive(m_live);
855 tex->setItem(QQuickItemPrivate::get(m_sourceItem)->itemNode());
856 QRectF sourceRect = m_sourceRect.width() == 0 || m_sourceRect.height() == 0
857 ? QRectF(0, 0, m_sourceItem->width(), m_sourceItem->height())
859 tex->setRect(sourceRect);
860 QSize textureSize = m_textureSize.isEmpty()
861 ? QSize(qCeil(qAbs(sourceRect.width())), qCeil(qAbs(sourceRect.height())))
863 Q_ASSERT(!textureSize.isEmpty());
864 QQuickItemPrivate *d = static_cast<QQuickItemPrivate *>(QObjectPrivate::get(this));
865 const QSize minTextureSize = d->sceneGraphContext()->minimumFBOSize();
866 // Keep power-of-two by doubling the size.
867 while (textureSize.width() < minTextureSize.width())
868 textureSize.rwidth() *= 2;
869 while (textureSize.height() < minTextureSize.height())
870 textureSize.rheight() *= 2;
872 tex->setSize(textureSize);
873 tex->setRecursive(m_recursive);
874 tex->setFormat(GLenum(m_format));
875 tex->setHasMipmaps(m_mipmap);
878 tex->scheduleUpdate();
881 QSGTexture::Filtering filtering = QQuickItemPrivate::get(this)->smooth
883 : QSGTexture::Nearest;
884 QSGTexture::Filtering mmFiltering = m_mipmap ? filtering : QSGTexture::None;
885 QSGTexture::WrapMode hWrap, vWrap;
886 get_wrap_mode(m_wrapMode, &hWrap, &vWrap);
889 m_provider->mipmapFiltering = mmFiltering;
890 m_provider->filtering = filtering;
891 m_provider->horizontalWrap = hWrap;
892 m_provider->verticalWrap = vWrap;
895 // Don't create the paint node if we're not spanning any area
896 if (width() == 0 || height() == 0) {
901 QQuickShaderEffectSourceNode *node = static_cast<QQuickShaderEffectSourceNode *>(oldNode);
903 node = new QQuickShaderEffectSourceNode;
904 node->setTexture(m_texture);
905 connect(m_texture, SIGNAL(updateRequested()), node, SLOT(markDirtyTexture()));
908 // If live and recursive, update continuously.
909 if (m_live && m_recursive)
910 node->markDirty(QSGNode::DirtyMaterial);
912 node->setMipmapFiltering(mmFiltering);
913 node->setFiltering(filtering);
914 node->setHorizontalWrapMode(hWrap);
915 node->setVerticalWrapMode(vWrap);
916 node->setTargetRect(QRectF(0, 0, width(), height()));
917 node->setSourceRect(QRectF(0, 0, 1, 1));
923 void QQuickShaderEffectSource::itemChange(ItemChange change, const ItemChangeData &value)
925 if (change == QQuickItem::ItemSceneChange && m_sourceItem) {
926 // See comment in QQuickShaderEffectSource::setSourceItem().
927 QQuickItemPrivate *d = QQuickItemPrivate::get(m_sourceItem);
928 if (!d->parentItem && value.canvas != d->canvas) {
929 QQuickItemPrivate::InitializationState initState;
931 d->initCanvas(&initState, value.canvas);
934 QQuickItem::itemChange(change, value);