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

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