1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQuick module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qquickshadereffectsource_p.h"
41
42#include "qquickitem_p.h"
43#include "qquickwindow_p.h"
44#include <private/qsgadaptationlayer_p.h>
45#include <QtQuick/private/qsgrenderer_p.h>
46#include <qsgsimplerectnode.h>
47
48#include "qmath.h"
49#include <QtQuick/private/qsgtexture_p.h>
50#include <QtCore/QRunnable>
51
52QT_BEGIN_NAMESPACE
53
54class QQuickShaderEffectSourceTextureProvider : public QSGTextureProvider
55{
56 Q_OBJECT
57public:
58 QQuickShaderEffectSourceTextureProvider()
59 : sourceTexture(nullptr)
60 , mipmapFiltering(QSGTexture::None)
61 , filtering(QSGTexture::Nearest)
62 , horizontalWrap(QSGTexture::ClampToEdge)
63 , verticalWrap(QSGTexture::ClampToEdge)
64 {
65 }
66
67 QSGTexture *texture() const override {
68 sourceTexture->setMipmapFiltering(mipmapFiltering);
69 sourceTexture->setFiltering(filtering);
70 sourceTexture->setHorizontalWrapMode(horizontalWrap);
71 sourceTexture->setVerticalWrapMode(verticalWrap);
72 return sourceTexture;
73 }
74
75 QSGLayer *sourceTexture;
76
77 QSGTexture::Filtering mipmapFiltering;
78 QSGTexture::Filtering filtering;
79 QSGTexture::WrapMode horizontalWrap;
80 QSGTexture::WrapMode verticalWrap;
81};
82
83class QQuickShaderEffectSourceCleanup : public QRunnable
84{
85public:
86 QQuickShaderEffectSourceCleanup(QSGLayer *t, QQuickShaderEffectSourceTextureProvider *p)
87 : texture(t)
88 , provider(p)
89 {}
90 void run() override {
91 delete texture;
92 delete provider;
93 }
94 QSGLayer *texture;
95 QQuickShaderEffectSourceTextureProvider *provider;
96};
97
98/*!
99 \qmltype ShaderEffectSource
100 \instantiates QQuickShaderEffectSource
101 \inqmlmodule QtQuick
102 \since 5.0
103 \inherits Item
104 \ingroup qtquick-effects
105 \brief Renders a \l {Qt Quick} item into a texture and displays it.
106
107 The ShaderEffectSource type renders \l sourceItem into a texture and
108 displays it in the scene. \l sourceItem is drawn into the texture as though
109 it was a fully opaque root item. Thus \l sourceItem itself can be
110 invisible, but still appear in the texture.
111
112 ShaderEffectSource can be used as:
113 \list
114 \li a texture source in a \l ShaderEffect.
115 This allows you to apply custom shader effects to any \l {Qt Quick} item.
116 \li a cache for a complex item.
117 The complex item can be rendered once into the texture, which can
118 then be animated freely without the need to render the complex item
119 again every frame.
120 \li an opacity layer.
121 ShaderEffectSource allows you to apply an opacity to items as a group
122 rather than each item individually.
123 \endlist
124
125 \table
126 \row
127 \li \image declarative-shadereffectsource.png
128 \li \qml
129 import QtQuick 2.0
130
131 Rectangle {
132 width: 200
133 height: 100
134 gradient: Gradient {
135 GradientStop { position: 0; color: "white" }
136 GradientStop { position: 1; color: "black" }
137 }
138 Row {
139 opacity: 0.5
140 Item {
141 id: foo
142 width: 100; height: 100
143 Rectangle { x: 5; y: 5; width: 60; height: 60; color: "red" }
144 Rectangle { x: 20; y: 20; width: 60; height: 60; color: "orange" }
145 Rectangle { x: 35; y: 35; width: 60; height: 60; color: "yellow" }
146 }
147 ShaderEffectSource {
148 width: 100; height: 100
149 sourceItem: foo
150 }
151 }
152 }
153 \endqml
154 \endtable
155
156 The ShaderEffectSource type does not redirect any mouse or keyboard
157 input to \l sourceItem. If you hide the \l sourceItem by setting
158 \l{Item::visible}{visible} to false or \l{Item::opacity}{opacity} to zero,
159 it will no longer react to input. In cases where the ShaderEffectSource is
160 meant to replace the \l sourceItem, you typically want to hide the
161 \l sourceItem while still handling input. For this, you can use
162 the \l hideSource property.
163
164 \note The ShaderEffectSource relies on FBO multisampling support
165 to antialias edges. If the underlying hardware does not support this,
166 which is the case for most embedded graphics chips, edges rendered
167 inside a ShaderEffectSource will not be antialiased. One way to remedy
168 this is to double the size of the effect source and render it with
169 \c {smooth: true} (this is the default value of smooth).
170 This will be equivalent to 4x multisampling, at the cost of lower performance
171 and higher memory use.
172
173 \warning In most cases, using a ShaderEffectSource will decrease
174 performance, and in all cases, it will increase video memory usage.
175 Rendering through a ShaderEffectSource might also lead to lower quality
176 since some OpenGL implementations support multisampled backbuffer,
177 but not multisampled framebuffer objects.
178*/
179
180QQuickShaderEffectSource::QQuickShaderEffectSource(QQuickItem *parent)
181 : QQuickItem(parent)
182 , m_provider(nullptr)
183 , m_texture(nullptr)
184 , m_wrapMode(ClampToEdge)
185 , m_sourceItem(nullptr)
186 , m_textureSize(0, 0)
187 , m_format(RGBA)
188 , m_samples(0)
189 , m_live(true)
190 , m_hideSource(false)
191 , m_mipmap(false)
192 , m_recursive(false)
193 , m_grab(true)
194 , m_textureMirroring(MirrorVertically)
195{
196 setFlag(flag: ItemHasContents);
197}
198
199QQuickShaderEffectSource::~QQuickShaderEffectSource()
200{
201 if (window()) {
202 window()->scheduleRenderJob(job: new QQuickShaderEffectSourceCleanup(m_texture, m_provider),
203 schedule: QQuickWindow::AfterSynchronizingStage);
204 } else {
205 // If we don't have a window, these should already have been
206 // released in invalidateSG or in releaseResrouces()
207 Q_ASSERT(!m_texture);
208 Q_ASSERT(!m_provider);
209 }
210
211 if (m_sourceItem) {
212 QQuickItemPrivate *sd = QQuickItemPrivate::get(item: m_sourceItem);
213 sd->removeItemChangeListener(this, types: QQuickItemPrivate::Geometry);
214 sd->derefFromEffectItem(unhide: m_hideSource);
215 if (window())
216 sd->derefWindow();
217 }
218}
219
220void QQuickShaderEffectSource::ensureTexture()
221{
222 if (m_texture)
223 return;
224
225 Q_ASSERT_X(QQuickItemPrivate::get(this)->window
226 && QQuickItemPrivate::get(this)->sceneGraphRenderContext()
227 && QThread::currentThread() == QQuickItemPrivate::get(this)->sceneGraphRenderContext()->thread(),
228 "QQuickShaderEffectSource::ensureTexture",
229 "Cannot be used outside the rendering thread");
230
231 QSGRenderContext *rc = QQuickItemPrivate::get(item: this)->sceneGraphRenderContext();
232 m_texture = rc->sceneGraphContext()->createLayer(renderContext: rc);
233 connect(sender: QQuickItemPrivate::get(item: this)->window, SIGNAL(sceneGraphInvalidated()), receiver: m_texture, SLOT(invalidated()), Qt::DirectConnection);
234 connect(sender: m_texture, SIGNAL(updateRequested()), receiver: this, SLOT(update()));
235 connect(sender: m_texture, SIGNAL(scheduledUpdateCompleted()), receiver: this, SIGNAL(scheduledUpdateCompleted()));
236}
237
238static void get_wrap_mode(QQuickShaderEffectSource::WrapMode mode, QSGTexture::WrapMode *hWrap, QSGTexture::WrapMode *vWrap);
239
240QSGTextureProvider *QQuickShaderEffectSource::textureProvider() const
241{
242 const QQuickItemPrivate *d = QQuickItemPrivate::get(item: this);
243 if (!d->window || !d->sceneGraphRenderContext() || QThread::currentThread() != d->sceneGraphRenderContext()->thread()) {
244 qWarning(msg: "QQuickShaderEffectSource::textureProvider: can only be queried on the rendering thread of an exposed window");
245 return nullptr;
246 }
247
248 if (!m_provider) {
249 const_cast<QQuickShaderEffectSource *>(this)->m_provider = new QQuickShaderEffectSourceTextureProvider();
250 const_cast<QQuickShaderEffectSource *>(this)->ensureTexture();
251 connect(sender: m_texture, SIGNAL(updateRequested()), receiver: m_provider, SIGNAL(textureChanged()));
252
253 get_wrap_mode(mode: m_wrapMode, hWrap: &m_provider->horizontalWrap, vWrap: &m_provider->verticalWrap);
254 m_provider->mipmapFiltering = mipmap() ? QSGTexture::Linear : QSGTexture::None;
255 m_provider->filtering = smooth() ? QSGTexture::Linear : QSGTexture::Nearest;
256 m_provider->sourceTexture = m_texture;
257 }
258 return m_provider;
259}
260
261/*!
262 \qmlproperty enumeration QtQuick::ShaderEffectSource::wrapMode
263
264 This property defines the OpenGL wrap modes associated with the texture.
265 Modifying this property makes most sense when the item is used as a
266 source texture of a \l ShaderEffect.
267
268 The default value is \c{ShaderEffectSource.ClampToEdge}.
269
270 \list
271 \li ShaderEffectSource.ClampToEdge - GL_CLAMP_TO_EDGE both horizontally and vertically
272 \li ShaderEffectSource.RepeatHorizontally - GL_REPEAT horizontally, GL_CLAMP_TO_EDGE vertically
273 \li ShaderEffectSource.RepeatVertically - GL_CLAMP_TO_EDGE horizontally, GL_REPEAT vertically
274 \li ShaderEffectSource.Repeat - GL_REPEAT both horizontally and vertically
275 \endlist
276
277 \note Some OpenGL ES 2 implementations do not support the GL_REPEAT
278 wrap mode with non-power-of-two textures.
279*/
280
281QQuickShaderEffectSource::WrapMode QQuickShaderEffectSource::wrapMode() const
282{
283 return m_wrapMode;
284}
285
286void QQuickShaderEffectSource::setWrapMode(WrapMode mode)
287{
288 if (mode == m_wrapMode)
289 return;
290 m_wrapMode = mode;
291 update();
292 emit wrapModeChanged();
293}
294
295/*!
296 \qmlproperty Item QtQuick::ShaderEffectSource::sourceItem
297
298 This property holds the item to be rendered into the texture.
299 Setting this to null while \l live is true, will release the texture
300 resources.
301*/
302
303QQuickItem *QQuickShaderEffectSource::sourceItem() const
304{
305 return m_sourceItem;
306}
307
308void QQuickShaderEffectSource::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &)
309{
310 Q_ASSERT(item == m_sourceItem);
311 Q_UNUSED(item);
312 if (change.sizeChange())
313 update();
314}
315
316void QQuickShaderEffectSource::setSourceItem(QQuickItem *item)
317{
318 if (item == m_sourceItem)
319 return;
320 if (m_sourceItem) {
321 QQuickItemPrivate *d = QQuickItemPrivate::get(item: m_sourceItem);
322 d->derefFromEffectItem(unhide: m_hideSource);
323 d->removeItemChangeListener(this, types: QQuickItemPrivate::Geometry);
324 disconnect(sender: m_sourceItem, SIGNAL(destroyed(QObject*)), receiver: this, SLOT(sourceItemDestroyed(QObject*)));
325 if (window())
326 d->derefWindow();
327 }
328
329 m_sourceItem = item;
330
331 if (m_sourceItem) {
332 if (window() == m_sourceItem->window()
333 || (window() == nullptr && m_sourceItem->window())
334 || (m_sourceItem->window() == nullptr && window())) {
335 QQuickItemPrivate *d = QQuickItemPrivate::get(item);
336 // 'item' needs a window to get a scene graph node. It usually gets one through its
337 // parent, but if the source item is "inline" rather than a reference -- i.e.
338 // "sourceItem: Item { }" instead of "sourceItem: foo" -- it will not get a parent.
339 // In those cases, 'item' should get the window from 'this'.
340 if (window())
341 d->refWindow(window());
342 else if (m_sourceItem->window())
343 d->refWindow(m_sourceItem->window());
344 d->refFromEffectItem(hide: m_hideSource);
345 d->addItemChangeListener(listener: this, types: QQuickItemPrivate::Geometry);
346 connect(sender: m_sourceItem, SIGNAL(destroyed(QObject*)), receiver: this, SLOT(sourceItemDestroyed(QObject*)));
347 } else {
348 qWarning(msg: "ShaderEffectSource: sourceItem and ShaderEffectSource must both be children of the same window.");
349 m_sourceItem = nullptr;
350 }
351 }
352 update();
353 emit sourceItemChanged();
354}
355
356void QQuickShaderEffectSource::sourceItemDestroyed(QObject *item)
357{
358 Q_ASSERT(item == m_sourceItem);
359 Q_UNUSED(item);
360 m_sourceItem = nullptr;
361 update();
362 emit sourceItemChanged();
363}
364
365
366/*!
367 \qmlproperty rect QtQuick::ShaderEffectSource::sourceRect
368
369 This property defines which rectangular area of the \l sourceItem to
370 render into the texture. The source rectangle can be larger than
371 \l sourceItem itself. If the rectangle is null, which is the default,
372 the whole \l sourceItem is rendered to texture.
373*/
374
375QRectF QQuickShaderEffectSource::sourceRect() const
376{
377 return m_sourceRect;
378}
379
380void QQuickShaderEffectSource::setSourceRect(const QRectF &rect)
381{
382 if (rect == m_sourceRect)
383 return;
384 m_sourceRect = rect;
385 update();
386 emit sourceRectChanged();
387}
388
389/*!
390 \qmlproperty size QtQuick::ShaderEffectSource::textureSize
391
392 This property holds the requested size of the texture. If it is empty,
393 which is the default, the size of the source rectangle is used.
394
395 \note Some platforms have a limit on how small framebuffer objects can be,
396 which means the actual texture size might be larger than the requested
397 size.
398*/
399
400QSize QQuickShaderEffectSource::textureSize() const
401{
402 return m_textureSize;
403}
404
405void QQuickShaderEffectSource::setTextureSize(const QSize &size)
406{
407 if (size == m_textureSize)
408 return;
409 m_textureSize = size;
410 update();
411 emit textureSizeChanged();
412}
413
414/*!
415 \qmlproperty enumeration QtQuick::ShaderEffectSource::format
416
417 This property defines the internal OpenGL format of the texture.
418 Modifying this property makes most sense when the item is used as a
419 source texture of a \l ShaderEffect. Depending on the OpenGL
420 implementation, this property might allow you to save some texture memory.
421
422 \list
423 \li ShaderEffectSource.Alpha - GL_ALPHA
424 \li ShaderEffectSource.RGB - GL_RGB
425 \li ShaderEffectSource.RGBA - GL_RGBA
426 \endlist
427
428 \note Some OpenGL implementations do not support the GL_ALPHA format.
429*/
430
431QQuickShaderEffectSource::Format QQuickShaderEffectSource::format() const
432{
433 return m_format;
434}
435
436void QQuickShaderEffectSource::setFormat(QQuickShaderEffectSource::Format format)
437{
438 if (format == m_format)
439 return;
440 m_format = format;
441 update();
442 emit formatChanged();
443}
444
445/*!
446 \qmlproperty bool QtQuick::ShaderEffectSource::live
447
448 If this property is true, the texture is updated whenever the
449 \l sourceItem updates. Otherwise, it will be a frozen image, even if
450 \l sourceItem is assigned a new item. The property is true by default.
451*/
452
453bool QQuickShaderEffectSource::live() const
454{
455 return m_live;
456}
457
458void QQuickShaderEffectSource::setLive(bool live)
459{
460 if (live == m_live)
461 return;
462 m_live = live;
463 update();
464 emit liveChanged();
465}
466
467/*!
468 \qmlproperty bool QtQuick::ShaderEffectSource::hideSource
469
470 If this property is true, the \l sourceItem is hidden, though it will still
471 be rendered into the texture. As opposed to hiding the \l sourceItem by
472 setting \l{Item::visible}{visible} to false, setting this property to true
473 will not prevent mouse or keyboard input from reaching \l sourceItem.
474 The property is useful when the ShaderEffectSource is anchored on top of,
475 and meant to replace the \l sourceItem.
476*/
477
478bool QQuickShaderEffectSource::hideSource() const
479{
480 return m_hideSource;
481}
482
483void QQuickShaderEffectSource::setHideSource(bool hide)
484{
485 if (hide == m_hideSource)
486 return;
487 if (m_sourceItem) {
488 QQuickItemPrivate::get(item: m_sourceItem)->refFromEffectItem(hide);
489 QQuickItemPrivate::get(item: m_sourceItem)->derefFromEffectItem(unhide: m_hideSource);
490 }
491 m_hideSource = hide;
492 update();
493 emit hideSourceChanged();
494}
495
496/*!
497 \qmlproperty bool QtQuick::ShaderEffectSource::mipmap
498
499 If this property is true, mipmaps are generated for the texture.
500
501 \note Some OpenGL ES 2 implementations do not support mipmapping of
502 non-power-of-two textures.
503*/
504
505bool QQuickShaderEffectSource::mipmap() const
506{
507 return m_mipmap;
508}
509
510void QQuickShaderEffectSource::setMipmap(bool enabled)
511{
512 if (enabled == m_mipmap)
513 return;
514 m_mipmap = enabled;
515 update();
516 emit mipmapChanged();
517}
518
519/*!
520 \qmlproperty bool QtQuick::ShaderEffectSource::recursive
521
522 Set this property to true if the ShaderEffectSource has a dependency on
523 itself. ShaderEffectSources form a dependency chain, where one
524 ShaderEffectSource can be part of the \l sourceItem of another.
525 If there is a loop in this chain, a ShaderEffectSource could end up trying
526 to render into the same texture it is using as source, which is not allowed
527 by OpenGL. When this property is set to true, an extra texture is allocated
528 so that ShaderEffectSource can keep a copy of the texture from the previous
529 frame. It can then render into one texture and use the texture from the
530 previous frame as source.
531
532 Setting both this property and \l live to true will cause the scene graph
533 to render continuously. Since the ShaderEffectSource depends on itself,
534 updating it means that it immediately becomes dirty again.
535*/
536
537bool QQuickShaderEffectSource::recursive() const
538{
539 return m_recursive;
540}
541
542void QQuickShaderEffectSource::setRecursive(bool enabled)
543{
544 if (enabled == m_recursive)
545 return;
546 m_recursive = enabled;
547 emit recursiveChanged();
548}
549
550/*!
551 \qmlproperty enumeration QtQuick::ShaderEffectSource::textureMirroring
552 \since 5.6
553
554 This property defines how the generated OpenGL texture should be mirrored.
555 The default value is \c{ShaderEffectSource.MirrorVertically}.
556 Custom mirroring can be useful if the generated texture is directly accessed by custom shaders,
557 such as those specified by ShaderEffect. Mirroring has no effect on the UI representation of
558 the ShaderEffectSource item itself.
559
560 \list
561 \li ShaderEffectSource.NoMirroring - No mirroring
562 \li ShaderEffectSource.MirrorHorizontally - The generated texture is flipped along X-axis.
563 \li ShaderEffectSource.MirrorVertically - The generated texture is flipped along Y-axis.
564 \endlist
565*/
566
567QQuickShaderEffectSource::TextureMirroring QQuickShaderEffectSource::textureMirroring() const
568{
569 return QQuickShaderEffectSource::TextureMirroring(m_textureMirroring);
570}
571
572void QQuickShaderEffectSource::setTextureMirroring(TextureMirroring mirroring)
573{
574 if (mirroring == QQuickShaderEffectSource::TextureMirroring(m_textureMirroring))
575 return;
576 m_textureMirroring = mirroring;
577 update();
578 emit textureMirroringChanged();
579}
580
581/*!
582 \qmlproperty int QtQuick::ShaderEffectSource::samples
583 \since 5.10
584
585 This property allows requesting multisampled rendering.
586
587 By default multisampling is enabled whenever multisampling is enabled for
588 the entire window, assuming the scenegraph renderer in use and the
589 underlying graphics API supports this.
590
591 By setting the value to 2, 4, etc. multisampled rendering can be requested
592 for a part of the scene without enabling multisampling for the entire
593 scene. This way multisampling is applied only to a given subtree, which can
594 lead to significant performance gains since multisampling is not applied to
595 other parts of the scene.
596
597 \note Enabling multisampling can be potentially expensive regardless of the
598 layer's size, as it incurs a hardware and driver dependent performance and
599 memory cost.
600
601 \note This property is only functional when support for multisample
602 renderbuffers and framebuffer blits is available. Otherwise the value is
603 silently ignored.
604 */
605int QQuickShaderEffectSource::samples() const
606{
607 return m_samples;
608}
609
610void QQuickShaderEffectSource::setSamples(int count)
611{
612 if (count == m_samples)
613 return;
614 m_samples = count;
615 update();
616 emit samplesChanged();
617}
618
619/*!
620 \qmlmethod QtQuick::ShaderEffectSource::scheduleUpdate()
621
622 Schedules a re-rendering of the texture for the next frame.
623 Use this to update the texture when \l live is false.
624*/
625
626void QQuickShaderEffectSource::scheduleUpdate()
627{
628 if (m_grab)
629 return;
630 m_grab = true;
631 update();
632}
633
634static void get_wrap_mode(QQuickShaderEffectSource::WrapMode mode, QSGTexture::WrapMode *hWrap, QSGTexture::WrapMode *vWrap)
635{
636 switch (mode) {
637 case QQuickShaderEffectSource::RepeatHorizontally:
638 *hWrap = QSGTexture::Repeat;
639 *vWrap = QSGTexture::ClampToEdge;
640 break;
641 case QQuickShaderEffectSource::RepeatVertically:
642 *vWrap = QSGTexture::Repeat;
643 *hWrap = QSGTexture::ClampToEdge;
644 break;
645 case QQuickShaderEffectSource::Repeat:
646 *hWrap = *vWrap = QSGTexture::Repeat;
647 break;
648 default:
649 // QQuickShaderEffectSource::ClampToEdge
650 *hWrap = *vWrap = QSGTexture::ClampToEdge;
651 break;
652 }
653}
654
655
656void QQuickShaderEffectSource::releaseResources()
657{
658 if (m_texture || m_provider) {
659 window()->scheduleRenderJob(job: new QQuickShaderEffectSourceCleanup(m_texture, m_provider),
660 schedule: QQuickWindow::AfterSynchronizingStage);
661 m_texture = nullptr;
662 m_provider = nullptr;
663 }
664}
665
666class QQuickShaderSourceAttachedNode : public QObject, public QSGNode
667{
668 Q_OBJECT
669public:
670 Q_SLOT void markTextureDirty() {
671 QSGNode *pn = QSGNode::parent();
672 if (pn) {
673 Q_ASSERT(pn->type() == QSGNode::GeometryNodeType);
674 pn->markDirty(bits: DirtyMaterial);
675 }
676 }
677};
678
679QSGNode *QQuickShaderEffectSource::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
680{
681 if (!m_sourceItem || m_sourceItem->width() <= 0 || m_sourceItem->height() <= 0) {
682 if (m_texture)
683 m_texture->setItem(nullptr);
684 delete oldNode;
685 return nullptr;
686 }
687
688 ensureTexture();
689
690 m_texture->setLive(m_live);
691 m_texture->setItem(QQuickItemPrivate::get(item: m_sourceItem)->itemNode());
692 QRectF sourceRect = m_sourceRect.width() == 0 || m_sourceRect.height() == 0
693 ? QRectF(0, 0, m_sourceItem->width(), m_sourceItem->height())
694 : m_sourceRect;
695 m_texture->setRect(sourceRect);
696 QSize textureSize = m_textureSize.isEmpty()
697 ? QSize(qCeil(v: qAbs(t: sourceRect.width())), qCeil(v: qAbs(t: sourceRect.height())))
698 : m_textureSize;
699 Q_ASSERT(!textureSize.isEmpty());
700
701 QQuickItemPrivate *d = static_cast<QQuickItemPrivate *>(QObjectPrivate::get(o: this));
702
703 // Crate large textures on high-dpi displays.
704 if (sourceItem())
705 textureSize *= d->window->effectiveDevicePixelRatio();
706
707 const QSize minTextureSize = d->sceneGraphContext()->minimumFBOSize();
708 // Keep power-of-two by doubling the size.
709 while (textureSize.width() < minTextureSize.width())
710 textureSize.rwidth() *= 2;
711 while (textureSize.height() < minTextureSize.height())
712 textureSize.rheight() *= 2;
713
714 m_texture->setDevicePixelRatio(d->window->effectiveDevicePixelRatio());
715 m_texture->setSize(textureSize);
716 m_texture->setRecursive(m_recursive);
717 m_texture->setFormat(m_format);
718 m_texture->setHasMipmaps(m_mipmap);
719 m_texture->setMirrorHorizontal(m_textureMirroring & MirrorHorizontally);
720 m_texture->setMirrorVertical(m_textureMirroring & MirrorVertically);
721 m_texture->setSamples(m_samples);
722
723 if (m_grab)
724 m_texture->scheduleUpdate();
725 m_grab = false;
726
727 QSGTexture::Filtering filtering = QQuickItemPrivate::get(item: this)->smooth
728 ? QSGTexture::Linear
729 : QSGTexture::Nearest;
730 QSGTexture::Filtering mmFiltering = m_mipmap ? filtering : QSGTexture::None;
731 QSGTexture::WrapMode hWrap, vWrap;
732 get_wrap_mode(mode: m_wrapMode, hWrap: &hWrap, vWrap: &vWrap);
733
734 if (m_provider) {
735 m_provider->mipmapFiltering = mmFiltering;
736 m_provider->filtering = filtering;
737 m_provider->horizontalWrap = hWrap;
738 m_provider->verticalWrap = vWrap;
739 }
740
741 // Don't create the paint node if we're not spanning any area
742 if (width() <= 0 || height() <= 0) {
743 delete oldNode;
744 return nullptr;
745 }
746
747 QSGInternalImageNode *node = static_cast<QSGInternalImageNode *>(oldNode);
748 if (!node) {
749 node = d->sceneGraphContext()->createInternalImageNode(renderContext: d->sceneGraphRenderContext());
750 node->setFlag(QSGNode::UsePreprocess);
751 node->setTexture(m_texture);
752 QQuickShaderSourceAttachedNode *attached = new QQuickShaderSourceAttachedNode;
753 node->appendChildNode(node: attached);
754 connect(sender: m_texture, SIGNAL(updateRequested()), receiver: attached, SLOT(markTextureDirty()));
755 }
756
757 // If live and recursive, update continuously.
758 if (m_live && m_recursive)
759 node->markDirty(bits: QSGNode::DirtyMaterial);
760
761 node->setMipmapFiltering(mmFiltering);
762 node->setFiltering(filtering);
763 node->setHorizontalWrapMode(hWrap);
764 node->setVerticalWrapMode(vWrap);
765 node->setTargetRect(QRectF(0, 0, width(), height()));
766 node->setInnerTargetRect(QRectF(0, 0, width(), height()));
767 node->update();
768
769 return node;
770}
771
772void QQuickShaderEffectSource::invalidateSceneGraph()
773{
774 if (m_texture)
775 delete m_texture;
776 if (m_provider)
777 delete m_provider;
778 m_texture = nullptr;
779 m_provider = nullptr;
780}
781
782void QQuickShaderEffectSource::itemChange(ItemChange change, const ItemChangeData &value)
783{
784 if (change == QQuickItem::ItemSceneChange && m_sourceItem) {
785 // See comment in QQuickShaderEffectSource::setSourceItem().
786 if (value.window)
787 QQuickItemPrivate::get(item: m_sourceItem)->refWindow(value.window);
788 else
789 QQuickItemPrivate::get(item: m_sourceItem)->derefWindow();
790 }
791 QQuickItem::itemChange(change, value);
792}
793
794#include "qquickshadereffectsource.moc"
795#include "moc_qquickshadereffectsource_p.cpp"
796
797QT_END_NAMESPACE
798

source code of qtdeclarative/src/quick/items/qquickshadereffectsource.cpp