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 <private/qquickgenericshadereffect_p.h>
41#include <private/qquickwindow_p.h>
42#include <private/qquickitem_p.h>
43
44QT_BEGIN_NAMESPACE
45
46namespace {
47class IntSignalMapper : public QObject
48{
49 Q_OBJECT
50
51 int value;
52public:
53 explicit IntSignalMapper(int v)
54 : QObject(nullptr), value(v) {}
55
56public Q_SLOTS:
57 void map() { emit mapped(value); }
58
59Q_SIGNALS:
60 void mapped(int);
61};
62} // unnamed namespace
63
64// The generic shader effect is used whenever on the RHI code path, or when the
65// scenegraph backend indicates SupportsShaderEffectNode. This, unlike the
66// monolithic and interconnected (e.g. with particles) OpenGL variant, passes
67// most of the work to a scenegraph node created via the adaptation layer, thus
68// allowing different implementation in the backends.
69
70QQuickGenericShaderEffect::QQuickGenericShaderEffect(QQuickShaderEffect *item, QObject *parent)
71 : QObject(parent)
72 , m_item(item)
73 , m_meshResolution(1, 1)
74 , m_mesh(nullptr)
75 , m_cullMode(QQuickShaderEffect::NoCulling)
76 , m_blending(true)
77 , m_supportsAtlasTextures(false)
78 , m_mgr(nullptr)
79 , m_fragNeedsUpdate(true)
80 , m_vertNeedsUpdate(true)
81{
82 qRegisterMetaType<QSGGuiThreadShaderEffectManager::ShaderInfo::Type>(typeName: "ShaderInfo::Type");
83 for (int i = 0; i < NShader; ++i)
84 m_inProgress[i] = nullptr;
85}
86
87QQuickGenericShaderEffect::~QQuickGenericShaderEffect()
88{
89 for (int i = 0; i < NShader; ++i) {
90 disconnectSignals(shaderType: Shader(i));
91 for (const auto &sm : qAsConst(t&: m_signalMappers[i]))
92 delete sm.mapper;
93 }
94
95 delete m_mgr;
96}
97
98void QQuickGenericShaderEffect::setFragmentShader(const QByteArray &src)
99{
100 // Compare the actual values since they are often just filenames.
101 // Optimizing by comparing constData() is a bad idea since seemingly static
102 // strings in QML may in fact have different addresses when a binding
103 // triggers assigning the "same" value to the property.
104 if (m_fragShader == src)
105 return;
106
107 m_fragShader = src;
108
109 m_fragNeedsUpdate = true;
110 if (m_item->isComponentComplete())
111 maybeUpdateShaders();
112
113 emit m_item->fragmentShaderChanged();
114}
115
116void QQuickGenericShaderEffect::setVertexShader(const QByteArray &src)
117{
118 if (m_vertShader == src)
119 return;
120
121 m_vertShader = src;
122
123 m_vertNeedsUpdate = true;
124 if (m_item->isComponentComplete())
125 maybeUpdateShaders();
126
127 emit m_item->vertexShaderChanged();
128}
129
130void QQuickGenericShaderEffect::setBlending(bool enable)
131{
132 if (m_blending == enable)
133 return;
134
135 m_blending = enable;
136 m_item->update();
137 emit m_item->blendingChanged();
138}
139
140QVariant QQuickGenericShaderEffect::mesh() const
141{
142 return m_mesh ? QVariant::fromValue(value: static_cast<QObject *>(m_mesh))
143 : QVariant::fromValue(value: m_meshResolution);
144}
145
146void QQuickGenericShaderEffect::setMesh(const QVariant &mesh)
147{
148 QQuickShaderEffectMesh *newMesh = qobject_cast<QQuickShaderEffectMesh *>(object: qvariant_cast<QObject *>(v: mesh));
149 if (newMesh && newMesh == m_mesh)
150 return;
151
152 if (m_mesh)
153 disconnect(sender: m_mesh, SIGNAL(geometryChanged()), receiver: this, member: nullptr);
154
155 m_mesh = newMesh;
156
157 if (m_mesh) {
158 connect(sender: m_mesh, SIGNAL(geometryChanged()), receiver: this, SLOT(markGeometryDirtyAndUpdate()));
159 } else {
160 if (mesh.canConvert<QSize>()) {
161 m_meshResolution = mesh.toSize();
162 } else {
163 QList<QByteArray> res = mesh.toByteArray().split(sep: 'x');
164 bool ok = res.size() == 2;
165 if (ok) {
166 int w = res.at(i: 0).toInt(ok: &ok);
167 if (ok) {
168 int h = res.at(i: 1).toInt(ok: &ok);
169 if (ok)
170 m_meshResolution = QSize(w, h);
171 }
172 }
173 if (!ok)
174 qWarning(msg: "ShaderEffect: mesh property must be a size or an object deriving from QQuickShaderEffectMesh");
175 }
176 m_defaultMesh.setResolution(m_meshResolution);
177 }
178
179 m_dirty |= QSGShaderEffectNode::DirtyShaderMesh;
180 m_item->update();
181
182 emit m_item->meshChanged();
183}
184
185void QQuickGenericShaderEffect::setCullMode(QQuickShaderEffect::CullMode face)
186{
187 if (m_cullMode == face)
188 return;
189
190 m_cullMode = face;
191 m_item->update();
192 emit m_item->cullModeChanged();
193}
194
195void QQuickGenericShaderEffect::setSupportsAtlasTextures(bool supports)
196{
197 if (m_supportsAtlasTextures == supports)
198 return;
199
200 m_supportsAtlasTextures = supports;
201 markGeometryDirtyAndUpdate();
202 emit m_item->supportsAtlasTexturesChanged();
203}
204
205QString QQuickGenericShaderEffect::parseLog()
206{
207 maybeUpdateShaders();
208 return log();
209}
210
211QString QQuickGenericShaderEffect::log() const
212{
213 QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
214 if (!mgr)
215 return QString();
216
217 return mgr->log();
218}
219
220QQuickShaderEffect::Status QQuickGenericShaderEffect::status() const
221{
222 QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
223 if (!mgr)
224 return QQuickShaderEffect::Uncompiled;
225
226 return QQuickShaderEffect::Status(mgr->status());
227}
228
229void QQuickGenericShaderEffect::handleEvent(QEvent *event)
230{
231 if (event->type() == QEvent::DynamicPropertyChange) {
232 QDynamicPropertyChangeEvent *e = static_cast<QDynamicPropertyChangeEvent *>(event);
233 for (int shaderType = 0; shaderType < NShader; ++shaderType) {
234 const auto &vars(m_shaders[shaderType].shaderInfo.variables);
235 for (int idx = 0; idx < vars.count(); ++idx) {
236 if (vars[idx].name == e->propertyName()) {
237 propertyChanged(mappedId: (shaderType << 16) | idx);
238 break;
239 }
240 }
241 }
242 }
243}
244
245void QQuickGenericShaderEffect::handleGeometryChanged(const QRectF &, const QRectF &)
246{
247 m_dirty |= QSGShaderEffectNode::DirtyShaderGeometry;
248}
249
250QSGNode *QQuickGenericShaderEffect::handleUpdatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
251{
252 QSGShaderEffectNode *node = static_cast<QSGShaderEffectNode *>(oldNode);
253
254 if (m_item->width() <= 0 || m_item->height() <= 0) {
255 delete node;
256 return nullptr;
257 }
258
259 // Do not change anything while a new shader is being reflected or compiled.
260 if (m_inProgress[Vertex] || m_inProgress[Fragment])
261 return node;
262
263 // The manager should be already created on the gui thread. Just take that instance.
264 QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
265 if (!mgr) {
266 delete node;
267 return nullptr;
268 }
269
270 if (!node) {
271 QSGRenderContext *rc = QQuickWindowPrivate::get(c: m_item->window())->context;
272 node = rc->sceneGraphContext()->createShaderEffectNode(renderContext: rc, mgr);
273 if (!node) {
274 qWarning(msg: "No shader effect node");
275 return nullptr;
276 }
277 m_dirty = QSGShaderEffectNode::DirtyShaderAll;
278 }
279
280 QSGShaderEffectNode::SyncData sd;
281 sd.dirty = m_dirty;
282 sd.cullMode = QSGShaderEffectNode::CullMode(m_cullMode);
283 sd.blending = m_blending;
284 sd.vertex.shader = &m_shaders[Vertex];
285 sd.vertex.dirtyConstants = &m_dirtyConstants[Vertex];
286 sd.vertex.dirtyTextures = &m_dirtyTextures[Vertex];
287 sd.fragment.shader = &m_shaders[Fragment];
288 sd.fragment.dirtyConstants = &m_dirtyConstants[Fragment];
289 sd.fragment.dirtyTextures = &m_dirtyTextures[Fragment];
290 node->syncMaterial(syncData: &sd);
291
292 if (m_dirty & QSGShaderEffectNode::DirtyShaderMesh) {
293 node->setGeometry(nullptr);
294 m_dirty &= ~QSGShaderEffectNode::DirtyShaderMesh;
295 m_dirty |= QSGShaderEffectNode::DirtyShaderGeometry;
296 }
297
298 if (m_dirty & QSGShaderEffectNode::DirtyShaderGeometry) {
299 const QRectF rect(0, 0, m_item->width(), m_item->height());
300 QQuickShaderEffectMesh *mesh = m_mesh ? m_mesh : &m_defaultMesh;
301 QSGGeometry *geometry = node->geometry();
302
303 const QRectF srcRect = node->updateNormalizedTextureSubRect(supportsAtlasTextures: m_supportsAtlasTextures);
304 geometry = mesh->updateGeometry(geometry, attrCount: 2, posIndex: 0, srcRect, rect);
305
306 node->setFlag(QSGNode::OwnsGeometry, false);
307 node->setGeometry(geometry);
308 node->setFlag(QSGNode::OwnsGeometry, true);
309
310 m_dirty &= ~QSGShaderEffectNode::DirtyShaderGeometry;
311 }
312
313 m_dirty = {};
314 for (int i = 0; i < NShader; ++i) {
315 m_dirtyConstants[i].clear();
316 m_dirtyTextures[i].clear();
317 }
318
319 return node;
320}
321
322void QQuickGenericShaderEffect::maybeUpdateShaders()
323{
324 if (m_vertNeedsUpdate)
325 m_vertNeedsUpdate = !updateShader(shaderType: Vertex, src: m_vertShader);
326 if (m_fragNeedsUpdate)
327 m_fragNeedsUpdate = !updateShader(shaderType: Fragment, src: m_fragShader);
328 if (m_vertNeedsUpdate || m_fragNeedsUpdate) {
329 // This function is invoked either from componentComplete or in a
330 // response to a previous invocation's polish() request. If this is
331 // case #1 then updateShader can fail due to not having a window or
332 // scenegraph ready. Schedule the polish to try again later. In case #2
333 // the backend probably does not have shadereffect support so there is
334 // nothing to do for us here.
335 if (!m_item->window() || !m_item->window()->isSceneGraphInitialized())
336 m_item->polish();
337 }
338}
339
340void QQuickGenericShaderEffect::handleItemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
341{
342 // Move the window ref.
343 if (change == QQuickItem::ItemSceneChange) {
344 for (int shaderType = 0; shaderType < NShader; ++shaderType) {
345 for (const auto &vd : qAsConst(t&: m_shaders[shaderType].varData)) {
346 if (vd.specialType == QSGShaderEffectNode::VariableData::Source) {
347 QQuickItem *source = qobject_cast<QQuickItem *>(object: qvariant_cast<QObject *>(v: vd.value));
348 if (source) {
349 if (value.window)
350 QQuickItemPrivate::get(item: source)->refWindow(value.window);
351 else
352 QQuickItemPrivate::get(item: source)->derefWindow();
353 }
354 }
355 }
356 }
357 }
358}
359
360QSGGuiThreadShaderEffectManager *QQuickGenericShaderEffect::shaderEffectManager() const
361{
362 if (!m_mgr) {
363 // return null if this is not the gui thread and not already created
364 if (QThread::currentThread() != m_item->thread())
365 return m_mgr;
366 QQuickWindow *w = m_item->window();
367 if (w) { // note: just the window, don't care about isSceneGraphInitialized() here
368 m_mgr = QQuickWindowPrivate::get(c: w)->context->sceneGraphContext()->createGuiThreadShaderEffectManager();
369 if (m_mgr) {
370 connect(sender: m_mgr, SIGNAL(logAndStatusChanged()), receiver: m_item, SIGNAL(logChanged()));
371 connect(sender: m_mgr, SIGNAL(logAndStatusChanged()), receiver: m_item, SIGNAL(statusChanged()));
372 connect(sender: m_mgr, SIGNAL(textureChanged()), receiver: this, SLOT(markGeometryDirtyAndUpdateIfSupportsAtlas()));
373 connect(sender: m_mgr, signal: &QSGGuiThreadShaderEffectManager::shaderCodePrepared, receiver: this, slot: &QQuickGenericShaderEffect::shaderCodePrepared);
374 }
375 }
376 }
377
378 return m_mgr;
379}
380
381void QQuickGenericShaderEffect::disconnectSignals(Shader shaderType)
382{
383 for (auto &sm : m_signalMappers[shaderType]) {
384 if (sm.active) {
385 sm.active = false;
386 QObject::disconnect(sender: m_item, signal: nullptr, receiver: sm.mapper, SLOT(map()));
387 QObject::disconnect(sender: sm.mapper, SIGNAL(mapped(int)), receiver: this, SLOT(propertyChanged(int)));
388 }
389 }
390 for (const auto &vd : qAsConst(t&: m_shaders[shaderType].varData)) {
391 if (vd.specialType == QSGShaderEffectNode::VariableData::Source) {
392 QQuickItem *source = qobject_cast<QQuickItem *>(object: qvariant_cast<QObject *>(v: vd.value));
393 if (source) {
394 if (m_item->window())
395 QQuickItemPrivate::get(item: source)->derefWindow();
396 QObject::disconnect(sender: source, SIGNAL(destroyed(QObject*)), receiver: this, SLOT(sourceDestroyed(QObject*)));
397 }
398 }
399 }
400}
401
402struct ShaderInfoCache
403{
404 bool contains(const QByteArray &key) const
405 {
406 return m_shaderInfoCache.contains(akey: key);
407 }
408
409 QSGGuiThreadShaderEffectManager::ShaderInfo value(const QByteArray &key) const
410 {
411 return m_shaderInfoCache.value(akey: key);
412 }
413
414 void insert(const QByteArray &key, const QSGGuiThreadShaderEffectManager::ShaderInfo &value)
415 {
416 m_shaderInfoCache.insert(akey: key, avalue: value);
417 }
418
419 QHash<QByteArray, QSGGuiThreadShaderEffectManager::ShaderInfo> m_shaderInfoCache;
420};
421
422Q_GLOBAL_STATIC(ShaderInfoCache, shaderInfoCache)
423
424bool QQuickGenericShaderEffect::updateShader(Shader shaderType, const QByteArray &src)
425{
426 QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
427 if (!mgr)
428 return false;
429
430 const bool texturesSeparate = mgr->hasSeparateSamplerAndTextureObjects();
431
432 disconnectSignals(shaderType);
433
434 m_shaders[shaderType].shaderInfo = QSGGuiThreadShaderEffectManager::ShaderInfo();
435 m_shaders[shaderType].varData.clear();
436
437 if (!src.isEmpty()) {
438 if (shaderInfoCache()->contains(key: src)) {
439 m_shaders[shaderType].shaderInfo = shaderInfoCache()->value(key: src);
440 m_shaders[shaderType].hasShaderCode = true;
441 } else {
442 // Each prepareShaderCode call needs its own work area, hence the
443 // dynamic alloc. If there are calls in progress, let those run to
444 // finish, their results can then simply be ignored because
445 // m_inProgress indicates what we care about.
446 m_inProgress[shaderType] = new QSGGuiThreadShaderEffectManager::ShaderInfo;
447 const QSGGuiThreadShaderEffectManager::ShaderInfo::Type typeHint =
448 shaderType == Vertex ? QSGGuiThreadShaderEffectManager::ShaderInfo::TypeVertex
449 : QSGGuiThreadShaderEffectManager::ShaderInfo::TypeFragment;
450 // Figure out what input parameters and variables are used in the
451 // shader. For file-based shader source/bytecode this is where the data
452 // is pulled in from the file. Some backends may choose to do
453 // source->bytecode compilation as well in this step.
454 mgr->prepareShaderCode(typeHint, src, result: m_inProgress[shaderType]);
455 // the rest is handled in shaderCodePrepared()
456 return true;
457 }
458 } else {
459 m_shaders[shaderType].hasShaderCode = false;
460 if (shaderType == Fragment) {
461 // With built-in shaders hasShaderCode is set to false and all
462 // metadata is empty, as it is left up to the node to provide a
463 // built-in default shader and its metadata. However, in case of
464 // the built-in fragment shader the value for 'source' has to be
465 // provided and monitored like with an application-provided shader.
466 QSGGuiThreadShaderEffectManager::ShaderInfo::Variable v;
467 v.name = QByteArrayLiteral("source");
468 v.bindPoint = 0; // fake
469 v.type = texturesSeparate ? QSGGuiThreadShaderEffectManager::ShaderInfo::Texture
470 : QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler;
471 m_shaders[shaderType].shaderInfo.variables.append(t: v);
472 }
473 }
474
475 updateShaderVars(shaderType);
476 m_dirty |= QSGShaderEffectNode::DirtyShaders;
477 m_item->update();
478 return true;
479}
480
481void QQuickGenericShaderEffect::shaderCodePrepared(bool ok, QSGGuiThreadShaderEffectManager::ShaderInfo::Type typeHint,
482 const QByteArray &src, QSGGuiThreadShaderEffectManager::ShaderInfo *result)
483{
484 const Shader shaderType = typeHint == QSGGuiThreadShaderEffectManager::ShaderInfo::TypeVertex ? Vertex : Fragment;
485
486 // If another call was made to updateShader() for the same shader type in
487 // the meantime then our results are useless, just drop them.
488 if (result != m_inProgress[shaderType]) {
489 delete result;
490 return;
491 }
492
493 m_shaders[shaderType].shaderInfo = *result;
494 delete result;
495 m_inProgress[shaderType] = nullptr;
496
497 if (!ok) {
498 qWarning(msg: "ShaderEffect: shader preparation failed for %s\n%s\n", src.constData(), qPrintable(log()));
499 m_shaders[shaderType].hasShaderCode = false;
500 return;
501 }
502
503 m_shaders[shaderType].hasShaderCode = true;
504 shaderInfoCache()->insert(key: src, value: m_shaders[shaderType].shaderInfo);
505 updateShaderVars(shaderType);
506 m_dirty |= QSGShaderEffectNode::DirtyShaders;
507 m_item->update();
508}
509
510void QQuickGenericShaderEffect::updateShaderVars(Shader shaderType)
511{
512 QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
513 if (!mgr)
514 return;
515
516 const bool texturesSeparate = mgr->hasSeparateSamplerAndTextureObjects();
517
518 const int varCount = m_shaders[shaderType].shaderInfo.variables.count();
519 m_shaders[shaderType].varData.resize(asize: varCount);
520
521 // Reuse signal mappers as much as possible since the mapping is based on
522 // the index and shader type which are both constant.
523 if (m_signalMappers[shaderType].count() < varCount)
524 m_signalMappers[shaderType].resize(asize: varCount);
525
526 // Hook up the signals to get notified about changes for properties that
527 // correspond to variables in the shader. Store also the values.
528 for (int i = 0; i < varCount; ++i) {
529 const auto &v(m_shaders[shaderType].shaderInfo.variables.at(i));
530 QSGShaderEffectNode::VariableData &vd(m_shaders[shaderType].varData[i]);
531 const bool isSpecial = v.name.startsWith(c: "qt_"); // special names not mapped to properties
532 if (isSpecial) {
533 if (v.name == "qt_Opacity")
534 vd.specialType = QSGShaderEffectNode::VariableData::Opacity;
535 else if (v.name == "qt_Matrix")
536 vd.specialType = QSGShaderEffectNode::VariableData::Matrix;
537 else if (v.name.startsWith(c: "qt_SubRect_"))
538 vd.specialType = QSGShaderEffectNode::VariableData::SubRect;
539 continue;
540 }
541
542 // The value of a property corresponding to a sampler is the source
543 // item ref, unless there are separate texture objects in which case
544 // the sampler is ignored (here).
545 if (v.type == QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler) {
546 if (texturesSeparate) {
547 vd.specialType = QSGShaderEffectNode::VariableData::Unused;
548 continue;
549 } else {
550 vd.specialType = QSGShaderEffectNode::VariableData::Source;
551 }
552 } else if (v.type == QSGGuiThreadShaderEffectManager::ShaderInfo::Texture) {
553 Q_ASSERT(texturesSeparate);
554 vd.specialType = QSGShaderEffectNode::VariableData::Source;
555 } else {
556 vd.specialType = QSGShaderEffectNode::VariableData::None;
557 }
558
559 // Find the property on the ShaderEffect item.
560 const int propIdx = m_item->metaObject()->indexOfProperty(name: v.name.constData());
561 if (propIdx >= 0) {
562 QMetaProperty mp = m_item->metaObject()->property(index: propIdx);
563 if (!mp.hasNotifySignal())
564 qWarning(msg: "ShaderEffect: property '%s' does not have notification method", v.name.constData());
565
566 // Have a IntSignalMapper that emits mapped() with an index+type on each property change notify signal.
567 auto &sm(m_signalMappers[shaderType][i]);
568 if (!sm.mapper)
569 sm.mapper = new IntSignalMapper(i | (shaderType << 16));
570 sm.active = true;
571 const QByteArray signalName = '2' + mp.notifySignal().methodSignature();
572 QObject::connect(sender: m_item, signal: signalName, receiver: sm.mapper, SLOT(map()));
573 QObject::connect(sender: sm.mapper, SIGNAL(mapped(int)), receiver: this, SLOT(propertyChanged(int)));
574 } else {
575 // Do not warn for dynamic properties.
576 if (!m_item->property(name: v.name.constData()).isValid())
577 qWarning(msg: "ShaderEffect: '%s' does not have a matching property", v.name.constData());
578 }
579
580 vd.value = m_item->property(name: v.name.constData());
581
582 if (vd.specialType == QSGShaderEffectNode::VariableData::Source) {
583 QQuickItem *source = qobject_cast<QQuickItem *>(object: qvariant_cast<QObject *>(v: vd.value));
584 if (source) {
585 if (m_item->window())
586 QQuickItemPrivate::get(item: source)->refWindow(m_item->window());
587 QObject::connect(sender: source, SIGNAL(destroyed(QObject*)), receiver: this, SLOT(sourceDestroyed(QObject*)));
588 }
589 }
590 }
591}
592
593bool QQuickGenericShaderEffect::sourceIsUnique(QQuickItem *source, Shader typeToSkip, int indexToSkip) const
594{
595 for (int shaderType = 0; shaderType < NShader; ++shaderType) {
596 for (int idx = 0; idx < m_shaders[shaderType].varData.count(); ++idx) {
597 if (shaderType != typeToSkip || idx != indexToSkip) {
598 const auto &vd(m_shaders[shaderType].varData[idx]);
599 if (vd.specialType == QSGShaderEffectNode::VariableData::Source && qvariant_cast<QObject *>(v: vd.value) == source)
600 return false;
601 }
602 }
603 }
604 return true;
605}
606
607void QQuickGenericShaderEffect::propertyChanged(int mappedId)
608{
609 const Shader type = Shader(mappedId >> 16);
610 const int idx = mappedId & 0xFFFF;
611 const auto &v(m_shaders[type].shaderInfo.variables[idx]);
612 auto &vd(m_shaders[type].varData[idx]);
613
614 if (vd.specialType == QSGShaderEffectNode::VariableData::Source) {
615 QQuickItem *source = qobject_cast<QQuickItem *>(object: qvariant_cast<QObject *>(v: vd.value));
616 if (source) {
617 if (m_item->window())
618 QQuickItemPrivate::get(item: source)->derefWindow();
619 // QObject::disconnect() will disconnect all matching connections.
620 // If the same source has been attached to two separate
621 // textures/samplers, then changing one of them would trigger both
622 // to be disconnected. So check first.
623 if (sourceIsUnique(source, typeToSkip: type, indexToSkip: idx))
624 QObject::disconnect(sender: source, SIGNAL(destroyed(QObject*)), receiver: this, SLOT(sourceDestroyed(QObject*)));
625 }
626
627 vd.value = m_item->property(name: v.name.constData());
628
629 source = qobject_cast<QQuickItem *>(object: qvariant_cast<QObject *>(v: vd.value));
630 if (source) {
631 // 'source' needs a window to get a scene graph node. It usually gets one through its
632 // parent, but if the source item is "inline" rather than a reference -- i.e.
633 // "property variant source: Image { }" instead of "property variant source: foo" -- it
634 // will not get a parent. In those cases, 'source' should get the window from 'item'.
635 if (m_item->window())
636 QQuickItemPrivate::get(item: source)->refWindow(m_item->window());
637 QObject::connect(sender: source, SIGNAL(destroyed(QObject*)), receiver: this, SLOT(sourceDestroyed(QObject*)));
638 }
639
640 m_dirty |= QSGShaderEffectNode::DirtyShaderTexture;
641 m_dirtyTextures[type].insert(value: idx);
642
643 } else {
644 vd.value = m_item->property(name: v.name.constData());
645 m_dirty |= QSGShaderEffectNode::DirtyShaderConstant;
646 m_dirtyConstants[type].insert(value: idx);
647 }
648
649 m_item->update();
650}
651
652void QQuickGenericShaderEffect::sourceDestroyed(QObject *object)
653{
654 for (int shaderType = 0; shaderType < NShader; ++shaderType) {
655 for (auto &vd : m_shaders[shaderType].varData) {
656 if (vd.specialType == QSGShaderEffectNode::VariableData::Source && vd.value.canConvert<QObject *>()) {
657 if (qvariant_cast<QObject *>(v: vd.value) == object)
658 vd.value = QVariant();
659 }
660 }
661 }
662}
663
664void QQuickGenericShaderEffect::markGeometryDirtyAndUpdate()
665{
666 m_dirty |= QSGShaderEffectNode::DirtyShaderGeometry;
667 m_item->update();
668}
669
670void QQuickGenericShaderEffect::markGeometryDirtyAndUpdateIfSupportsAtlas()
671{
672 if (m_supportsAtlasTextures)
673 markGeometryDirtyAndUpdate();
674}
675
676QT_END_NAMESPACE
677
678#include "moc_qquickgenericshadereffect_p.cpp"
679#include "qquickgenericshadereffect.moc"
680

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