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

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