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 "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 \qmlclass ShaderEffectSource QQuickShaderEffectSource
463 \ingroup qtquick-effects
464 \brief Renders a QtQuick item into a texture and displays it
466 The ShaderEffectSource type 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 item. 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 QtQuick item.
475 \li a cache for a complex item.
476 The complex item can be rendered once into the texture, which can
477 then be animated freely without the need to render the complex item
479 \li an opacity layer.
480 ShaderEffectSource allows you to apply an opacity to items as a group
481 rather than each item 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
515 The ShaderEffectSource type does not redirect any mouse or keyboard
516 input to \l sourceItem. If you hide the \l sourceItem by setting
517 \l{Item::visible}{visible} to false or \l{Item::opacity}{opacity} to zero,
518 it will no longer react to input. In cases where the ShaderEffectSource is
519 meant to replace the \l sourceItem, you typically want to hide the
520 \l sourceItem while still handling input. For this, you can use
521 the \l hideSource property.
523 \note If \l sourceItem is a \l Rectangle with border, by default half the
524 border width falls outside the texture. To get the whole border, you can
525 extend the \l sourceRect.
527 \note The ShaderEffectSource relies on FBO multisampling support
528 to antialias edges. If the underlying hardware does not support this,
529 which is the case for most embedded graphics chips, edges rendered
530 inside a ShaderEffectSource will not be antialiased. One way to remedy
531 this is to double the size of the effect source and render it with
532 \c {smooth: true}. This will be equivalent to 4x multisampling, at
533 the cost of lower performance and higher memory use.
535 \warning In most cases, using a ShaderEffectSource will decrease
536 performance, and in all cases, it will increase video memory usage.
537 Rendering through a ShaderEffectSource might also lead to lower quality
538 since some OpenGL implementations support multisampled backbuffer,
539 but not multisampled framebuffer objects.
542 QQuickShaderEffectSource::QQuickShaderEffectSource(QQuickItem *parent)
546 , m_wrapMode(ClampToEdge)
548 , m_textureSize(0, 0)
551 , m_hideSource(false)
556 setFlag(ItemHasContents);
559 QQuickShaderEffectSource::~QQuickShaderEffectSource()
562 m_texture->deleteLater();
565 m_provider->deleteLater();
568 QQuickItemPrivate *sd = QQuickItemPrivate::get(m_sourceItem);
569 sd->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
570 sd->derefFromEffectItem(m_hideSource);
576 void QQuickShaderEffectSource::ensureTexture()
581 Q_ASSERT_X(QQuickItemPrivate::get(this)->window
582 && QQuickItemPrivate::get(this)->sceneGraphContext()
583 && QThread::currentThread() == QQuickItemPrivate::get(this)->sceneGraphContext()->thread(),
584 "QQuickShaderEffectSource::ensureTexture",
585 "Cannot be used outside the rendering thread");
587 m_texture = new QQuickShaderEffectTexture(this);
588 connect(m_texture, SIGNAL(updateRequested()), this, SLOT(update()));
589 connect(m_texture, SIGNAL(scheduledUpdateCompleted()), this, SIGNAL(scheduledUpdateCompleted()));
592 QSGTextureProvider *QQuickShaderEffectSource::textureProvider() const
595 // Make sure it gets thread affinity on the rendering thread so deletion works properly..
596 Q_ASSERT_X(QQuickItemPrivate::get(this)->window
597 && QQuickItemPrivate::get(this)->sceneGraphContext()
598 && QThread::currentThread() == QQuickItemPrivate::get(this)->sceneGraphContext()->thread(),
599 "QQuickShaderEffectSource::textureProvider",
600 "Cannot be used outside the rendering thread");
601 const_cast<QQuickShaderEffectSource *>(this)->m_provider = new QQuickShaderEffectSourceTextureProvider();
602 const_cast<QQuickShaderEffectSource *>(this)->ensureTexture();
603 connect(m_texture, SIGNAL(updateRequested()), m_provider, SIGNAL(textureChanged()));
604 m_provider->sourceTexture = m_texture;
610 \qmlproperty enumeration ShaderEffectSource::wrapMode
612 This property defines the OpenGL wrap modes associated with the texture.
613 Modifying this property makes most sense when the item is used as a
614 source texture of a \l ShaderEffect.
617 \li ShaderEffectSource.ClampToEdge - GL_CLAMP_TO_EDGE both horizontally and vertically
618 \li ShaderEffectSource.RepeatHorizontally - GL_REPEAT horizontally, GL_CLAMP_TO_EDGE vertically
619 \li ShaderEffectSource.RepeatVertically - GL_CLAMP_TO_EDGE horizontally, GL_REPEAT vertically
620 \li ShaderEffectSource.Repeat - GL_REPEAT both horizontally and vertically
623 \note Some OpenGL ES 2 implementations do not support the GL_REPEAT
624 wrap mode with non-power-of-two textures.
627 QQuickShaderEffectSource::WrapMode QQuickShaderEffectSource::wrapMode() const
632 void QQuickShaderEffectSource::setWrapMode(WrapMode mode)
634 if (mode == m_wrapMode)
638 emit wrapModeChanged();
642 \qmlproperty Item ShaderEffectSource::sourceItem
644 This property holds the item to be rendered into the texture.
645 Setting this to null while \l live is true, will release the texture
649 QQuickItem *QQuickShaderEffectSource::sourceItem() const
654 void QQuickShaderEffectSource::itemGeometryChanged(QQuickItem *item, const QRectF &newRect, const QRectF &oldRect)
656 Q_ASSERT(item == m_sourceItem);
658 if (newRect.size() != oldRect.size())
662 void QQuickShaderEffectSource::setSourceItem(QQuickItem *item)
664 if (item == m_sourceItem)
667 QQuickItemPrivate *d = QQuickItemPrivate::get(m_sourceItem);
668 d->derefFromEffectItem(m_hideSource);
669 d->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
670 disconnect(m_sourceItem, SIGNAL(destroyed(QObject*)), this, SLOT(sourceItemDestroyed(QObject*)));
677 QQuickItemPrivate *d = QQuickItemPrivate::get(item);
678 // 'item' needs a window to get a scene graph node. It usually gets one through its
679 // parent, but if the source item is "inline" rather than a reference -- i.e.
680 // "sourceItem: Item { }" instead of "sourceItem: foo" -- it will not get a parent.
681 // In those cases, 'item' should get the window from 'this'.
683 d->refWindow(window());
684 d->refFromEffectItem(m_hideSource);
685 d->addItemChangeListener(this, QQuickItemPrivate::Geometry);
686 connect(m_sourceItem, SIGNAL(destroyed(QObject*)), this, SLOT(sourceItemDestroyed(QObject*)));
689 emit sourceItemChanged();
692 void QQuickShaderEffectSource::sourceItemDestroyed(QObject *item)
694 Q_ASSERT(item == m_sourceItem);
697 emit sourceItemChanged();
702 \qmlproperty rect ShaderEffectSource::sourceRect
704 This property defines which rectangular area of the \l sourceItem to
705 render into the texture. The source rectangle can be larger than
706 \l sourceItem itself. If the rectangle is null, which is the default,
707 the whole \l sourceItem is rendered to texture.
710 QRectF QQuickShaderEffectSource::sourceRect() const
715 void QQuickShaderEffectSource::setSourceRect(const QRectF &rect)
717 if (rect == m_sourceRect)
721 emit sourceRectChanged();
725 \qmlproperty size ShaderEffectSource::textureSize
727 This property holds the requested size of the texture. If it is empty,
728 which is the default, the size of the source rectangle is used.
730 \note Some platforms have a limit on how small framebuffer objects can be,
731 which means the actual texture size might be larger than the requested
735 QSize QQuickShaderEffectSource::textureSize() const
737 return m_textureSize;
740 void QQuickShaderEffectSource::setTextureSize(const QSize &size)
742 if (size == m_textureSize)
744 m_textureSize = size;
746 emit textureSizeChanged();
750 \qmlproperty enumeration ShaderEffectSource::format
752 This property defines the internal OpenGL format of the texture.
753 Modifying this property makes most sense when the item is used as a
754 source texture of a \l ShaderEffect. Depending on the OpenGL
755 implementation, this property might allow you to save some texture memory.
758 \li ShaderEffectSource.Alpha - GL_ALPHA
759 \li ShaderEffectSource.RGB - GL_RGB
760 \li ShaderEffectSource.RGBA - GL_RGBA
763 \note Some OpenGL implementations do not support the GL_ALPHA format.
766 QQuickShaderEffectSource::Format QQuickShaderEffectSource::format() const
771 void QQuickShaderEffectSource::setFormat(QQuickShaderEffectSource::Format format)
773 if (format == m_format)
777 emit formatChanged();
781 \qmlproperty bool ShaderEffectSource::live
783 If this property is true, the texture is updated whenever the
784 \l sourceItem updates. Otherwise, it will be a frozen image, even if
785 \l sourceItem is assigned a new item. The property is true by default.
788 bool QQuickShaderEffectSource::live() const
793 void QQuickShaderEffectSource::setLive(bool live)
803 \qmlproperty bool ShaderEffectSource::hideSource
805 If this property is true, the \l sourceItem is hidden, though it will still
806 be rendered into the texture. As opposed to hiding the \l sourceItem by
807 setting \l{Item::visible}{visible} to false, setting this property to true
808 will not prevent mouse or keyboard input from reaching \l sourceItem.
809 The property is useful when the ShaderEffectSource is anchored on top of,
810 and meant to replace the \l sourceItem.
813 bool QQuickShaderEffectSource::hideSource() const
818 void QQuickShaderEffectSource::setHideSource(bool hide)
820 if (hide == m_hideSource)
823 QQuickItemPrivate::get(m_sourceItem)->refFromEffectItem(hide);
824 QQuickItemPrivate::get(m_sourceItem)->derefFromEffectItem(m_hideSource);
828 emit hideSourceChanged();
832 \qmlproperty bool ShaderEffectSource::mipmap
834 If this property is true, mipmaps are generated for the texture.
836 \note Some OpenGL ES 2 implementations do not support mipmapping of
837 non-power-of-two textures.
840 bool QQuickShaderEffectSource::mipmap() const
845 void QQuickShaderEffectSource::setMipmap(bool enabled)
847 if (enabled == m_mipmap)
851 emit mipmapChanged();
855 \qmlproperty bool ShaderEffectSource::recursive
857 Set this property to true if the ShaderEffectSource has a dependency on
858 itself. ShaderEffectSources form a dependency chain, where one
859 ShaderEffectSource can be part of the \l sourceItem of another.
860 If there is a loop in this chain, a ShaderEffectSource could end up trying
861 to render into the same texture it is using as source, which is not allowed
862 by OpenGL. When this property is set to true, an extra texture is allocated
863 so that ShaderEffectSource can keep a copy of the texture from the previous
864 frame. It can then render into one texture and use the texture from the
865 previous frame as source.
867 Setting both this property and \l live to true will cause the scene graph
868 to render continuously. Since the ShaderEffectSource depends on itself,
869 updating it means that it immediately becomes dirty again.
872 bool QQuickShaderEffectSource::recursive() const
877 void QQuickShaderEffectSource::setRecursive(bool enabled)
879 if (enabled == m_recursive)
881 m_recursive = enabled;
882 emit recursiveChanged();
886 \qmlmethod ShaderEffectSource::scheduleUpdate()
888 Schedules a re-rendering of the texture for the next frame.
889 Use this to update the texture when \l live is false.
892 void QQuickShaderEffectSource::scheduleUpdate()
900 static void get_wrap_mode(QQuickShaderEffectSource::WrapMode mode, QSGTexture::WrapMode *hWrap, QSGTexture::WrapMode *vWrap)
903 case QQuickShaderEffectSource::RepeatHorizontally:
904 *hWrap = QSGTexture::Repeat;
905 *vWrap = QSGTexture::ClampToEdge;
907 case QQuickShaderEffectSource::RepeatVertically:
908 *vWrap = QSGTexture::Repeat;
909 *hWrap = QSGTexture::ClampToEdge;
911 case QQuickShaderEffectSource::Repeat:
912 *hWrap = *vWrap = QSGTexture::Repeat;
915 // QQuickShaderEffectSource::ClampToEdge
916 *hWrap = *vWrap = QSGTexture::ClampToEdge;
922 void QQuickShaderEffectSource::releaseResources()
925 m_texture->deleteLater();
929 m_provider->deleteLater();
934 QSGNode *QQuickShaderEffectSource::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
936 if (!m_sourceItem || m_sourceItem->width() == 0 || m_sourceItem->height() == 0) {
938 m_texture->setItem(0);
945 m_texture->setLive(m_live);
946 m_texture->setItem(QQuickItemPrivate::get(m_sourceItem)->itemNode());
947 QRectF sourceRect = m_sourceRect.width() == 0 || m_sourceRect.height() == 0
948 ? QRectF(0, 0, m_sourceItem->width(), m_sourceItem->height())
950 m_texture->setRect(sourceRect);
951 QSize textureSize = m_textureSize.isEmpty()
952 ? QSize(qCeil(qAbs(sourceRect.width())), qCeil(qAbs(sourceRect.height())))
954 Q_ASSERT(!textureSize.isEmpty());
955 QQuickItemPrivate *d = static_cast<QQuickItemPrivate *>(QObjectPrivate::get(this));
956 const QSize minTextureSize = d->sceneGraphContext()->minimumFBOSize();
957 // Keep power-of-two by doubling the size.
958 while (textureSize.width() < minTextureSize.width())
959 textureSize.rwidth() *= 2;
960 while (textureSize.height() < minTextureSize.height())
961 textureSize.rheight() *= 2;
963 m_texture->setSize(textureSize);
964 m_texture->setRecursive(m_recursive);
965 m_texture->setFormat(GLenum(m_format));
966 m_texture->setHasMipmaps(m_mipmap);
969 m_texture->scheduleUpdate();
972 QSGTexture::Filtering filtering = QQuickItemPrivate::get(this)->smooth
974 : QSGTexture::Nearest;
975 QSGTexture::Filtering mmFiltering = m_mipmap ? filtering : QSGTexture::None;
976 QSGTexture::WrapMode hWrap, vWrap;
977 get_wrap_mode(m_wrapMode, &hWrap, &vWrap);
980 m_provider->mipmapFiltering = mmFiltering;
981 m_provider->filtering = filtering;
982 m_provider->horizontalWrap = hWrap;
983 m_provider->verticalWrap = vWrap;
986 // Don't create the paint node if we're not spanning any area
987 if (width() == 0 || height() == 0) {
992 QQuickShaderEffectSourceNode *node = static_cast<QQuickShaderEffectSourceNode *>(oldNode);
994 node = new QQuickShaderEffectSourceNode;
995 node->setTexture(m_texture);
996 connect(m_texture, SIGNAL(updateRequested()), node, SLOT(markDirtyTexture()));
999 // If live and recursive, update continuously.
1000 if (m_live && m_recursive)
1001 node->markDirty(QSGNode::DirtyMaterial);
1003 node->setMipmapFiltering(mmFiltering);
1004 node->setFiltering(filtering);
1005 node->setHorizontalWrapMode(hWrap);
1006 node->setVerticalWrapMode(vWrap);
1007 node->setTargetRect(QRectF(0, 0, width(), height()));
1008 node->setInnerTargetRect(QRectF(0, 0, width(), height()));
1014 void QQuickShaderEffectSource::itemChange(ItemChange change, const ItemChangeData &value)
1016 if (change == QQuickItem::ItemSceneChange && m_sourceItem) {
1017 // See comment in QQuickShaderEffectSource::setSourceItem().
1019 QQuickItemPrivate::get(m_sourceItem)->refWindow(value.window);
1021 QQuickItemPrivate::get(m_sourceItem)->derefWindow();
1023 QQuickItem::itemChange(change, value);