Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qquickshadereffect.cpp
Go to the documentation of this file.
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 <private/qquickshadereffect_p_p.h>
5#include <private/qsgcontextplugin_p.h>
6#include <private/qsgrhisupport_p.h>
7#include <private/qquickwindow_p.h>
8
10
495namespace QtPrivate {
497{
498public:
499 typedef std::function<void()> PropChangedFunc;
500
502 : QSlotObjectBase(&impl), _signalIndex(-1), func(func)
503 { ref(); }
504
505 void setSignalIndex(int idx) { _signalIndex = idx; }
506 int signalIndex() const { return _signalIndex; }
507
508private:
509 int _signalIndex;
511
512 static void impl(int which, QSlotObjectBase *this_, QObject *, void **a, bool *ret)
513 {
514 auto thiz = static_cast<EffectSlotMapper*>(this_);
515 switch (which) {
516 case Destroy:
517 delete thiz;
518 break;
519 case Call:
520 thiz->func();
521 break;
522 case Compare:
523 *ret = thiz == reinterpret_cast<EffectSlotMapper *>(a[0]);
524 break;
525 case NumOperations: ;
526 }
527 }
528};
529} // namespace QtPrivate
530
533{
535}
536
538{
540 d->inDestructor = true;
541}
542
557{
558 Q_D(const QQuickShaderEffect);
559 return d->fragmentShader();
560}
561
563{
565 d->setFragmentShader(fileUrl);
566}
567
582{
583 Q_D(const QQuickShaderEffect);
584 return d->vertexShader();
585}
586
588{
590 d->setVertexShader(fileUrl);
591}
592
603{
604 Q_D(const QQuickShaderEffect);
605 return d->blending();
606}
607
609{
611 d->setBlending(enable);
612}
613
628{
629 Q_D(const QQuickShaderEffect);
630 return d->mesh();
631}
632
634{
636 d->setMesh(mesh);
637}
638
652{
653 Q_D(const QQuickShaderEffect);
654 return d->cullMode();
655}
656
658{
660 return d->setCullMode(face);
661}
662
685{
686 Q_D(const QQuickShaderEffect);
687 return d->supportsAtlasTextures();
688}
689
691{
693 d->setSupportsAtlasTextures(supports);
694}
695
738{
739 Q_D(const QQuickShaderEffect);
740 return d->log();
741}
742
744{
745 Q_D(const QQuickShaderEffect);
746 return d->status();
747}
748
750{
752 d->handleEvent(e);
753 return QQuickItem::event(e);
754}
755
756void QQuickShaderEffect::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
757{
759 d->handleGeometryChanged(newGeometry, oldGeometry);
760 QQuickItem::geometryChange(newGeometry, oldGeometry);
761}
762
764{
766 return d->handleUpdatePaintNode(oldNode, updatePaintNodeData);
767}
768
770{
772 d->maybeUpdateShaders();
774}
775
777{
779 d->handleItemChange(change, value);
781}
782
784{
786}
787
789{
790 auto node = static_cast<QSGShaderEffectNode *>(QQuickItemPrivate::get(this)->paintNode);
791 if (!node)
792 return false;
793
795 return d->updateUniformValue(name, value, node);
796}
797
799{
801 if (!qmlEngine(q))
802 return;
804}
805
806constexpr int indexToMappedId(const int shaderType, const int idx)
807{
808 return idx | (shaderType << 16);
809}
810
811constexpr int mappedIdToIndex(const int mappedId)
812{
813 return mappedId & 0xFFFF;
814}
815
816constexpr int mappedIdToShaderType(const int mappedId)
817{
818 return mappedId >> 16;
819}
820
822 : m_meshResolution(1, 1)
823 , m_mesh(nullptr)
824 , m_cullMode(QQuickShaderEffect::NoCulling)
825 , m_blending(true)
826 , m_supportsAtlasTextures(false)
827 , m_mgr(nullptr)
828 , m_fragNeedsUpdate(true)
829 , m_vertNeedsUpdate(true)
830{
831 qRegisterMetaType<QSGGuiThreadShaderEffectManager::ShaderInfo::Type>("ShaderInfo::Type");
832 for (int i = 0; i < NShader; ++i)
833 m_inProgress[i] = nullptr;
834}
835
837{
838 for (int i = 0; i < NShader; ++i) {
839 disconnectSignals(Shader(i));
840 clearMappers(Shader(i));
841 }
842
843 delete m_mgr;
844}
845
847{
849 if (m_fragShader == fileUrl)
850 return;
851
852 m_fragShader = fileUrl;
853
854 m_fragNeedsUpdate = true;
855 if (q->isComponentComplete())
857
858 emit q->fragmentShaderChanged();
859}
860
862{
864 if (m_vertShader == fileUrl)
865 return;
866
867 m_vertShader = fileUrl;
868
869 m_vertNeedsUpdate = true;
870 if (q->isComponentComplete())
872
873 emit q->vertexShaderChanged();
874}
875
877{
879 if (m_blending == enable)
880 return;
881
882 m_blending = enable;
883 q->update();
884 emit q->blendingChanged();
885}
886
888{
889 return m_mesh ? QVariant::fromValue(static_cast<QObject *>(m_mesh))
890 : QVariant::fromValue(m_meshResolution);
891}
892
894{
896 QQuickShaderEffectMesh *newMesh = qobject_cast<QQuickShaderEffectMesh *>(qvariant_cast<QObject *>(mesh));
897 if (newMesh && newMesh == m_mesh)
898 return;
899
900 if (m_mesh)
901 QObject::disconnect(m_meshConnection);
902
903 m_mesh = newMesh;
904
905 if (m_mesh) {
907 [this] { markGeometryDirtyAndUpdate(); });
908 } else {
909 if (mesh.canConvert<QSize>()) {
910 m_meshResolution = mesh.toSize();
911 } else {
913 bool ok = res.size() == 2;
914 if (ok) {
915 int w = res.at(0).toInt(&ok);
916 if (ok) {
917 int h = res.at(1).toInt(&ok);
918 if (ok)
919 m_meshResolution = QSize(w, h);
920 }
921 }
922 if (!ok)
923 qWarning("ShaderEffect: mesh property must be a size or an object deriving from QQuickShaderEffectMesh");
924 }
925 m_defaultMesh.setResolution(m_meshResolution);
926 }
927
929 q->update();
930
931 emit q->meshChanged();
932}
933
935{
937 if (m_cullMode == face)
938 return;
939
940 m_cullMode = face;
941 q->update();
942 emit q->cullModeChanged();
943}
944
946{
948 if (m_supportsAtlasTextures == supports)
949 return;
950
951 m_supportsAtlasTextures = supports;
953 emit q->supportsAtlasTexturesChanged();
954}
955
957{
959 return log();
960}
961
963{
964 QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
965 if (!mgr)
966 return QString();
967
968 return mgr->log();
969}
970
972{
973 QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
974 if (!mgr)
976
977 return QQuickShaderEffect::Status(mgr->status());
978}
979
981{
982 if (event->type() == QEvent::DynamicPropertyChange) {
983 const auto propertyName = static_cast<QDynamicPropertyChangeEvent *>(event)->propertyName();
984 const auto mappedId = findMappedShaderVariableId(propertyName);
985 if (mappedId)
986 propertyChanged(*mappedId);
987 }
988}
989
991{
993}
994
996{
998 QSGShaderEffectNode *node = static_cast<QSGShaderEffectNode *>(oldNode);
999
1000 if (q->width() <= 0 || q->height() <= 0) {
1001 delete node;
1002 return nullptr;
1003 }
1004
1005 // Do not change anything while a new shader is being reflected or compiled.
1006 if (m_inProgress[Vertex] || m_inProgress[Fragment])
1007 return node;
1008
1009 // The manager should be already created on the gui thread. Just take that instance.
1010 QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
1011 if (!mgr) {
1012 delete node;
1013 return nullptr;
1014 }
1015
1016 if (!node) {
1018 node = rc->sceneGraphContext()->createShaderEffectNode(rc);
1019 if (!node) {
1020 qWarning("No shader effect node");
1021 return nullptr;
1022 }
1025 }
1026
1028 sd.dirty = m_dirty;
1029 sd.cullMode = QSGShaderEffectNode::CullMode(m_cullMode);
1030 sd.blending = m_blending;
1031 sd.vertex.shader = &m_shaders[Vertex];
1032 sd.vertex.dirtyConstants = &m_dirtyConstants[Vertex];
1033 sd.vertex.dirtyTextures = &m_dirtyTextures[Vertex];
1034 sd.fragment.shader = &m_shaders[Fragment];
1035 sd.fragment.dirtyConstants = &m_dirtyConstants[Fragment];
1036 sd.fragment.dirtyTextures = &m_dirtyTextures[Fragment];
1037 sd.materialTypeCacheKey = q->window();
1038
1039 node->syncMaterial(&sd);
1040
1042 node->setGeometry(nullptr);
1043 m_dirty &= ~QSGShaderEffectNode::DirtyShaderMesh;
1045 }
1046
1048 const QRectF rect(0, 0, q->width(), q->height());
1049 QQuickShaderEffectMesh *mesh = m_mesh ? m_mesh : &m_defaultMesh;
1050 QSGGeometry *geometry = node->geometry();
1051
1052 const QRectF srcRect = node->updateNormalizedTextureSubRect(m_supportsAtlasTextures);
1053 geometry = mesh->updateGeometry(geometry, 2, 0, srcRect, rect);
1054
1055 node->setFlag(QSGNode::OwnsGeometry, false);
1056 node->setGeometry(geometry);
1057 node->setFlag(QSGNode::OwnsGeometry, true);
1058
1059 m_dirty &= ~QSGShaderEffectNode::DirtyShaderGeometry;
1060 }
1061
1062 m_dirty = {};
1063 for (int i = 0; i < NShader; ++i) {
1064 m_dirtyConstants[i].clear();
1065 m_dirtyTextures[i].clear();
1066 }
1067
1068 return node;
1069}
1070
1072{
1073 Q_Q(QQuickShaderEffect);
1074 if (m_vertNeedsUpdate)
1075 m_vertNeedsUpdate = !updateShader(Vertex, m_vertShader);
1076 if (m_fragNeedsUpdate)
1077 m_fragNeedsUpdate = !updateShader(Fragment, m_fragShader);
1078 if (m_vertNeedsUpdate || m_fragNeedsUpdate) {
1079 // This function is invoked either from componentComplete or in a
1080 // response to a previous invocation's polish() request. If this is
1081 // case #1 then updateShader can fail due to not having a window or
1082 // scenegraph ready. Schedule the polish to try again later. In case #2
1083 // the backend probably does not have shadereffect support so there is
1084 // nothing to do for us here.
1085 if (!q->window() || !q->window()->isSceneGraphInitialized())
1086 q->polish();
1087 }
1088}
1089
1091 QSGShaderEffectNode *node)
1092{
1093 Q_Q(QQuickShaderEffect);
1094 const auto mappedId = findMappedShaderVariableId(name);
1095 if (!mappedId)
1096 return false;
1097
1098 const Shader type = Shader(mappedIdToShaderType(*mappedId));
1099 const int idx = mappedIdToIndex(*mappedId);
1100
1101 // Update value
1102 m_shaders[type].varData[idx].value = value;
1103
1104 // Insert dirty uniform
1105 QSet<int> dirtyConstants[NShader];
1106 dirtyConstants[type].insert(idx);
1107
1108 // Sync material change
1111 sd.cullMode = QSGShaderEffectNode::CullMode(m_cullMode);
1112 sd.blending = m_blending;
1113 sd.vertex.shader = &m_shaders[Vertex];
1114 sd.vertex.dirtyConstants = &dirtyConstants[Vertex];
1115 sd.vertex.dirtyTextures = {};
1116 sd.fragment.shader = &m_shaders[Fragment];
1117 sd.fragment.dirtyConstants = &dirtyConstants[Fragment];
1118 sd.fragment.dirtyTextures = {};
1119 sd.materialTypeCacheKey = q->window();
1120
1121 node->syncMaterial(&sd);
1122
1123 return true;
1124}
1125
1127{
1128 if (inDestructor)
1129 return;
1130
1131 // Move the window ref.
1132 if (change == QQuickItem::ItemSceneChange) {
1133 for (int shaderType = 0; shaderType < NShader; ++shaderType) {
1134 for (const auto &vd : std::as_const(m_shaders[shaderType].varData)) {
1135 if (vd.specialType == QSGShaderEffectNode::VariableData::Source) {
1136 QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(vd.value));
1137 if (source) {
1138 if (value.window)
1140 else
1142 }
1143 }
1144 }
1145 }
1146 }
1147}
1148
1149QSGGuiThreadShaderEffectManager *QQuickShaderEffectPrivate::shaderEffectManager() const
1150{
1151 Q_Q(const QQuickShaderEffect);
1152 if (!m_mgr) {
1153 // return null if this is not the gui thread and not already created
1154 if (QThread::currentThread() != q->thread())
1155 return m_mgr;
1156 QQuickWindow *w = q->window();
1157 if (w) { // note: just the window, don't care about isSceneGraphInitialized() here
1159 if (m_mgr) {
1165 { const_cast<QQuickShaderEffectPrivate *>(this)->shaderCodePrepared(ok, typeHint, loadUrl, result); });
1166 }
1167 }
1168 }
1169 return m_mgr;
1170}
1171
1172void QQuickShaderEffectPrivate::disconnectSignals(Shader shaderType)
1173{
1174 Q_Q(QQuickShaderEffect);
1175 for (auto *mapper : m_mappers[shaderType]) {
1176 void *a = mapper;
1177 if (mapper)
1178 QObjectPrivate::disconnect(q, mapper->signalIndex(), &a);
1179 }
1180 for (const auto &vd : std::as_const(m_shaders[shaderType].varData)) {
1181 if (vd.specialType == QSGShaderEffectNode::VariableData::Source) {
1182 QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(vd.value));
1183 if (source) {
1184 if (q->window())
1186 auto it = m_destroyedConnections.constFind(source);
1187 if (it != m_destroyedConnections.constEnd()) {
1189 m_destroyedConnections.erase(it);
1190 }
1191 }
1192 }
1193 }
1194}
1195
1196void QQuickShaderEffectPrivate::clearMappers(QQuickShaderEffectPrivate::Shader shaderType)
1197{
1198 for (auto *mapper : std::as_const(m_mappers[shaderType])) {
1199 if (mapper)
1200 mapper->destroyIfLastRef();
1201 }
1202 m_mappers[shaderType].clear();
1203}
1204
1205static inline QVariant getValueFromProperty(QObject *item, const QMetaObject *itemMetaObject,
1206 const QByteArray &name, int propertyIndex)
1207{
1209 if (propertyIndex == -1) {
1210 value = item->property(name);
1211 } else {
1212 value = itemMetaObject->property(propertyIndex).read(item);
1213 }
1214 return value;
1215}
1216
1218Q_GLOBAL_STATIC(QQuickShaderInfoCache, shaderInfoCache)
1219
1221{
1222 shaderInfoCache()->clear();
1223}
1224
1225bool QQuickShaderEffectPrivate::updateShader(Shader shaderType, const QUrl &fileUrl)
1226{
1227 Q_Q(QQuickShaderEffect);
1228 QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
1229 if (!mgr)
1230 return false;
1231
1232 const bool texturesSeparate = mgr->hasSeparateSamplerAndTextureObjects();
1233
1234 disconnectSignals(shaderType);
1235
1236 m_shaders[shaderType].shaderInfo.variables.clear();
1237 m_shaders[shaderType].varData.clear();
1238
1239 if (!fileUrl.isEmpty()) {
1240 const QQmlContext *context = qmlContext(q);
1241 const QUrl loadUrl = context ? context->resolvedUrl(fileUrl) : fileUrl;
1242 auto it = shaderInfoCache()->constFind(loadUrl);
1243 if (it != shaderInfoCache()->cend()) {
1244 m_shaders[shaderType].shaderInfo = *it;
1245 m_shaders[shaderType].hasShaderCode = true;
1246 } else {
1247 // Each prepareShaderCode call needs its own work area, hence the
1248 // dynamic alloc. If there are calls in progress, let those run to
1249 // finish, their results can then simply be ignored because
1250 // m_inProgress indicates what we care about.
1251 m_inProgress[shaderType] = new QSGGuiThreadShaderEffectManager::ShaderInfo;
1255 // Figure out what input parameters and variables are used in the
1256 // shader. This is where the data is pulled in from the file.
1257 // (however, if there is compilation involved, that happens at a
1258 // later stage, up to the QRhi backend)
1259 mgr->prepareShaderCode(typeHint, loadUrl, m_inProgress[shaderType]);
1260 // the rest is handled in shaderCodePrepared()
1261 return true;
1262 }
1263 } else {
1264 m_shaders[shaderType].hasShaderCode = false;
1265 if (shaderType == Fragment) {
1266 // With built-in shaders hasShaderCode is set to false and all
1267 // metadata is empty, as it is left up to the node to provide a
1268 // built-in default shader and its metadata. However, in case of
1269 // the built-in fragment shader the value for 'source' has to be
1270 // provided and monitored like with an application-provided shader.
1272 v.name = QByteArrayLiteral("source");
1273 v.bindPoint = 1; // fake, must match the default source bindPoint in qquickshadereffectnode.cpp
1276 m_shaders[shaderType].shaderInfo.variables.append(v);
1277 }
1278 }
1279
1280 updateShaderVars(shaderType);
1282 q->update();
1283 return true;
1284}
1285
1288{
1289 Q_Q(QQuickShaderEffect);
1290 const Shader shaderType = typeHint == QSGGuiThreadShaderEffectManager::ShaderInfo::TypeVertex ? Vertex : Fragment;
1291
1292 // If another call was made to updateShader() for the same shader type in
1293 // the meantime then our results are useless, just drop them.
1294 if (result != m_inProgress[shaderType]) {
1295 delete result;
1296 return;
1297 }
1298
1299 m_shaders[shaderType].shaderInfo = *result;
1300 delete result;
1301 m_inProgress[shaderType] = nullptr;
1302
1303 if (!ok) {
1304 qWarning("ShaderEffect: shader preparation failed for %s\n%s\n",
1305 qPrintable(loadUrl.toString()), qPrintable(log()));
1306 m_shaders[shaderType].hasShaderCode = false;
1307 return;
1308 }
1309
1310 m_shaders[shaderType].hasShaderCode = true;
1311 shaderInfoCache()->insert(loadUrl, m_shaders[shaderType].shaderInfo);
1312 updateShaderVars(shaderType);
1314 q->update();
1315}
1316
1317void QQuickShaderEffectPrivate::updateShaderVars(Shader shaderType)
1318{
1319 Q_Q(QQuickShaderEffect);
1320 QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
1321 if (!mgr)
1322 return;
1323
1324 const bool texturesSeparate = mgr->hasSeparateSamplerAndTextureObjects();
1325
1326 const int varCount = m_shaders[shaderType].shaderInfo.variables.size();
1327 m_shaders[shaderType].varData.resize(varCount);
1328
1329 // Recreate signal mappers when the shader has changed.
1330 clearMappers(shaderType);
1331
1333
1334 if (!m_itemMetaObject)
1335 m_itemMetaObject = q->metaObject();
1336
1337 // Hook up the signals to get notified about changes for properties that
1338 // correspond to variables in the shader. Store also the values.
1339 for (int i = 0; i < varCount; ++i) {
1340 const auto &v(m_shaders[shaderType].shaderInfo.variables.at(i));
1341 QSGShaderEffectNode::VariableData &vd(m_shaders[shaderType].varData[i]);
1342 const bool isSpecial = v.name.startsWith("qt_"); // special names not mapped to properties
1343 if (isSpecial) {
1344 if (v.name == "qt_Opacity")
1346 else if (v.name == "qt_Matrix")
1348 else if (v.name.startsWith("qt_SubRect_"))
1350 continue;
1351 }
1352
1353 // The value of a property corresponding to a sampler is the source
1354 // item ref, unless there are separate texture objects in which case
1355 // the sampler is ignored (here).
1357 if (texturesSeparate) {
1359 continue;
1360 } else {
1362 }
1364 Q_ASSERT(texturesSeparate);
1366 } else {
1368 }
1369
1370 // Find the property on the ShaderEffect item.
1371 int propIdx = -1;
1372 const QQmlPropertyData *pd = nullptr;
1373 if (propCache) {
1374 pd = propCache->property(QLatin1String(v.name), nullptr, nullptr);
1375 if (pd) {
1376 if (!pd->isFunction())
1377 propIdx = pd->coreIndex();
1378 }
1379 }
1380 if (propIdx >= 0) {
1381 if (pd && !pd->isFunction()) {
1382 if (pd->notifyIndex() == -1) {
1383 qWarning("QQuickShaderEffect: property '%s' does not have notification method!", v.name.constData());
1384 } else {
1385 const int mappedId = indexToMappedId(shaderType, i);
1386 auto mapper = new QtPrivate::EffectSlotMapper([this, mappedId](){
1387 this->propertyChanged(mappedId);
1388 });
1389 m_mappers[shaderType].append(mapper);
1390 mapper->setSignalIndex(m_itemMetaObject->property(propIdx).notifySignal().methodIndex());
1391 Q_ASSERT(q->metaObject() == m_itemMetaObject);
1392 bool ok = QObjectPrivate::connectImpl(q, pd->notifyIndex(), q, nullptr, mapper,
1393 Qt::AutoConnection, nullptr, m_itemMetaObject);
1394 if (!ok)
1395 qWarning() << "Failed to connect to property" << m_itemMetaObject->property(propIdx).name()
1396 << "(" << propIdx << ", signal index" << pd->notifyIndex()
1397 << ") of item" << q;
1398 }
1399 }
1400 } else {
1401 // Do not warn for dynamic properties.
1402 if (!q->property(v.name.constData()).isValid())
1403 qWarning("ShaderEffect: '%s' does not have a matching property", v.name.constData());
1404 }
1405
1406
1407 vd.propertyIndex = propIdx;
1408 vd.value = getValueFromProperty(q, m_itemMetaObject, v.name, vd.propertyIndex);
1409 if (vd.specialType == QSGShaderEffectNode::VariableData::Source) {
1410 QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(vd.value));
1411 if (source) {
1412 if (q->window())
1414
1415 // Cannot just pass q as the 'context' for the connect(). The
1416 // order of destruction is...complicated. Having an inline
1417 // source (e.g. source: ShaderEffectSource { ... } in QML would
1418 // emit destroyed() after the connection was already gone. To
1419 // work that around, store the Connection and manually
1420 // disconnect instead.
1421 if (!m_destroyedConnections.contains(source))
1422 m_destroyedConnections.insert(source, QObject::connect(source, &QObject::destroyed, [this](QObject *obj) { sourceDestroyed(obj); }));
1423 }
1424 }
1425 }
1426}
1427
1428std::optional<int> QQuickShaderEffectPrivate::findMappedShaderVariableId(const QByteArray &name) const
1429{
1430 for (int shaderType = 0; shaderType < NShader; ++shaderType) {
1431 const auto &vars = m_shaders[shaderType].shaderInfo.variables;
1432 for (int idx = 0; idx < vars.size(); ++idx) {
1433 if (vars[idx].name == name)
1434 return indexToMappedId(shaderType, idx);
1435 }
1436 }
1437
1438 return {};
1439}
1440
1441bool QQuickShaderEffectPrivate::sourceIsUnique(QQuickItem *source, Shader typeToSkip, int indexToSkip) const
1442{
1443 for (int shaderType = 0; shaderType < NShader; ++shaderType) {
1444 for (int idx = 0; idx < m_shaders[shaderType].varData.size(); ++idx) {
1445 if (shaderType != typeToSkip || idx != indexToSkip) {
1446 const auto &vd(m_shaders[shaderType].varData[idx]);
1447 if (vd.specialType == QSGShaderEffectNode::VariableData::Source && qvariant_cast<QObject *>(vd.value) == source)
1448 return false;
1449 }
1450 }
1451 }
1452 return true;
1453}
1454
1456{
1457 Q_Q(QQuickShaderEffect);
1458 const Shader type = Shader(mappedIdToShaderType(mappedId));
1459 const int idx = mappedIdToIndex(mappedId);
1460 const auto &v(m_shaders[type].shaderInfo.variables[idx]);
1461 auto &vd(m_shaders[type].varData[idx]);
1462
1463 QVariant oldValue = vd.value;
1464 vd.value = getValueFromProperty(q, m_itemMetaObject, v.name, vd.propertyIndex);
1465
1466 if (vd.specialType == QSGShaderEffectNode::VariableData::Source) {
1467 QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(oldValue));
1468 if (source) {
1469 if (q->window())
1471 // If the same source has been attached to two separate
1472 // textures/samplers, then changing one of them would trigger both
1473 // to be disconnected. So check first.
1474 if (sourceIsUnique(source, type, idx)) {
1475 auto it = m_destroyedConnections.constFind(source);
1476 if (it != m_destroyedConnections.constEnd()) {
1478 m_destroyedConnections.erase(it);
1479 }
1480 }
1481 }
1482
1483 source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(vd.value));
1484 if (source) {
1485 // 'source' needs a window to get a scene graph node. It usually gets one through its
1486 // parent, but if the source item is "inline" rather than a reference -- i.e.
1487 // "property variant source: Image { }" instead of "property variant source: foo" -- it
1488 // will not get a parent. In those cases, 'source' should get the window from 'item'.
1489 if (q->window())
1491 if (!m_destroyedConnections.contains(source))
1492 m_destroyedConnections.insert(source, QObject::connect(source, &QObject::destroyed, [this](QObject *obj) { sourceDestroyed(obj); }));
1493 }
1494
1496 m_dirtyTextures[type].insert(idx);
1497
1498 } else {
1500 m_dirtyConstants[type].insert(idx);
1501 }
1502
1503 q->update();
1504}
1505
1507{
1508 for (int shaderType = 0; shaderType < NShader; ++shaderType) {
1509 for (auto &vd : m_shaders[shaderType].varData) {
1510 if (vd.specialType == QSGShaderEffectNode::VariableData::Source && vd.value.canConvert<QObject *>()) {
1511 if (qvariant_cast<QObject *>(vd.value) == object)
1512 vd.value = QVariant();
1513 }
1514 }
1515 }
1516}
1517
1519{
1520 Q_Q(QQuickShaderEffect);
1522 q->update();
1523}
1524
1526{
1527 if (m_supportsAtlasTextures)
1529}
1530
1532
1533#include "moc_qquickshadereffect_p.cpp"
\inmodule QtCore
Definition qbytearray.h:57
QList< QByteArray > split(char sep) const
Splits the byte array into subarrays wherever sep occurs, and returns the list of those arrays.
\inmodule QtCore
Definition qcoreevent.h:45
@ DynamicPropertyChange
Definition qcoreevent.h:207
\inmodule QtCore
Definition qhash.h:818
const_iterator constFind(const Key &key) const noexcept
Definition qhash.h:1279
const_iterator constEnd() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the ...
Definition qhash.h:1209
iterator erase(const_iterator it)
Definition qhash.h:1223
bool contains(const Key &key) const noexcept
Returns true if the hash contains an item with the key; otherwise returns false.
Definition qhash.h:991
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1283
Definition qlist.h:74
int methodIndex() const
QVariant read(const QObject *obj) const
Reads the property's value from the given object.
QMetaMethod notifySignal() const
const char * name() const
Returns this property's name.
static QMetaObject::Connection connectImpl(const QObject *sender, int signal_index, const QObject *receiver, void **slot, QtPrivate::QSlotObjectBase *slotObj, int type, const int *types, const QMetaObject *senderMetaObject)
Definition qobject.cpp:5090
static bool disconnect(const typename QtPrivate::FunctionPointer< Func1 >::Object *sender, Func1 signal, const typename QtPrivate::FunctionPointer< Func2 >::Object *receiverPrivate, Func2 slot)
Definition qobject_p.h:327
\inmodule QtCore
Definition qobject.h:90
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
Definition qobject.cpp:3099
QThread * thread() const
Returns the thread in which the object lives.
Definition qobject.cpp:1561
void destroyed(QObject *=nullptr)
This signal is emitted immediately before the object obj is destroyed, after any instances of QPointe...
The QQmlContext class defines a context within a QML engine.
Definition qqmlcontext.h:25
static QQmlPropertyCache::ConstPtr ensurePropertyCache(QObject *object)
Definition qqmldata_p.h:252
void setResolution(const QSize &res)
\qmlproperty size QtQuick::GridMesh::resolution
void refWindow(QQuickWindow *)
static QQuickItemPrivate * get(QQuickItem *item)
The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
Definition qquickitem.h:64
bool event(QEvent *) override
\reimp
void setFlag(Flag flag, bool enabled=true)
Enables the specified flag for this item if enabled is true; if enabled is false, the flag is disable...
virtual void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
void componentComplete() override
\reimp Derived classes should call the base class method before adding their own actions to perform a...
virtual void itemChange(ItemChange, const ItemChangeData &)
Called when change occurs for this item.
bool isComponentComplete() const
Returns true if construction of the QML component is complete; otherwise returns false.
ItemChange
Used in conjunction with QQuickItem::itemChange() to notify the item about certain types of changes.
Definition qquickitem.h:143
void setVertexShader(const QUrl &fileUrl)
void shaderCodePrepared(bool ok, QSGGuiThreadShaderEffectManager::ShaderInfo::Type typeHint, const QUrl &loadUrl, QSGGuiThreadShaderEffectManager::ShaderInfo *result)
QSGNode * handleUpdatePaintNode(QSGNode *, QQuickItem::UpdatePaintNodeData *)
void setMesh(const QVariant &mesh)
void setSupportsAtlasTextures(bool supports)
bool updateUniformValue(const QByteArray &name, const QVariant &value, QSGShaderEffectNode *node)
QQuickShaderEffect::Status status() const
void setFragmentShader(const QUrl &fileUrl)
void setCullMode(QQuickShaderEffect::CullMode face)
void handleGeometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
void sourceDestroyed(QObject *object)
void handleItemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
void setBlending(bool enable)
void setFragmentShader(const QUrl &fileUrl)
void componentComplete() override
\reimp Derived classes should call the base class method before adding their own actions to perform a...
void setCullMode(CullMode face)
void setVertexShader(const QUrl &fileUrl)
bool event(QEvent *e) override
\reimp
void setMesh(const QVariant &mesh)
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override
QQuickShaderEffect(QQuickItem *parent=nullptr)
bool updateUniformValue(const QByteArray &name, const QVariant &value)
void setSupportsAtlasTextures(bool supports)
QSGNode * updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData) override
Called on the render thread when it is time to sync the state of the item with the scene graph.
void itemChange(ItemChange change, const ItemChangeData &value) override
Called when change occurs for this item.
static QQuickWindowPrivate * get(QQuickWindow *c)
QSGRenderContext * context
\qmltype Window \instantiates QQuickWindow \inqmlmodule QtQuick
\inmodule QtCore\reentrant
Definition qrect.h:483
const QSGGeometry * geometry() const
Returns this node's geometry.
Definition qsgnode.h:160
void setGeometry(QSGGeometry *geometry)
Sets the geometry of this node to geometry.
Definition qsgnode.cpp:762
virtual QSGShaderEffectNode * createShaderEffectNode(QSGRenderContext *renderContext)
Creates a new shader effect node.
virtual QSGGuiThreadShaderEffectManager * createGuiThreadShaderEffectManager()
Creates a new shader effect helper instance.
The QSGGeometry class provides low-level storage for graphics primitives in the \l{Qt Quick Scene Gra...
Definition qsggeometry.h:15
virtual Status status() const =0
void shaderCodePrepared(bool ok, ShaderInfo::Type typeHint, const QUrl &src, ShaderInfo *result)
virtual bool hasSeparateSamplerAndTextureObjects() const =0
virtual QString log() const =0
virtual void prepareShaderCode(ShaderInfo::Type typeHint, const QUrl &src, ShaderInfo *result)=0
\group qtquick-scenegraph-nodes \title Qt Quick Scene Graph Node classes
Definition qsgnode.h:37
@ OwnsGeometry
Definition qsgnode.h:57
void setFlag(Flag, bool=true)
Sets the flag f on this node if enabled is true; otherwise clears the flag.
Definition qsgnode.cpp:584
QSGContext * sceneGraphContext() const
virtual QRectF updateNormalizedTextureSubRect(bool supportsAtlasTextures)=0
virtual void syncMaterial(SyncData *syncData)=0
Definition qset.h:18
void clear()
Definition qset.h:61
const_iterator constFind(const T &value) const
Definition qset.h:161
iterator insert(const T &value)
Definition qset.h:155
\inmodule QtCore
Definition qsize.h:25
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
static QThread * currentThread()
Definition qthread.cpp:966
\inmodule QtCore
Definition qurl.h:94
bool isEmpty() const
Returns true if the URL has no data; otherwise returns false.
Definition qurl.cpp:1888
QString toString(FormattingOptions options=FormattingOptions(PrettyDecoded)) const
Returns a string representation of the URL.
Definition qurl.cpp:2828
\inmodule QtCore
Definition qvariant.h:64
T value() const &
Definition qvariant.h:511
QSize toSize() const
Returns the variant as a QSize if the variant has userType() \l QMetaType::QSize; otherwise returns a...
bool canConvert(QMetaType targetType) const
Definition qvariant.h:342
static auto fromValue(T &&value) noexcept(std::is_nothrow_copy_constructible_v< T > &&Private::CanUseInternalSpace< T >) -> std::enable_if_t< std::conjunction_v< std::is_copy_constructible< T >, std::is_destructible< T > >, QVariant >
Definition qvariant.h:531
QByteArray toByteArray() const
Returns the variant as a QByteArray if the variant has userType() \l QMetaType::QByteArray or \l QMet...
std::function< void()> PropChangedFunc
EffectSlotMapper(PropChangedFunc func)
double e
QSet< QString >::iterator it
rect
[4]
const PluginKeyMapConstIterator cend
Combined button and popup list for selecting options.
\macro QT_NAMESPACE
@ AutoConnection
static void * context
#define QByteArrayLiteral(str)
Definition qbytearray.h:52
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
#define qWarning
Definition qlogging.h:162
return ret
GLsizei const GLfloat * v
[13]
GLfloat GLfloat GLfloat w
[0]
GLboolean GLboolean GLboolean GLboolean a
[7]
GLuint object
[3]
GLenum face
GLenum type
GLboolean enable
GLuint name
GLfloat GLfloat GLfloat GLfloat h
GLsizei GLsizei GLchar * source
struct _cl_event * event
GLhandleARB obj
[2]
GLenum func
Definition qopenglext.h:663
GLuint res
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLuint64EXT * result
[6]
QQmlEngine * qmlEngine(const QObject *obj)
Definition qqml.cpp:76
QQmlContext * qmlContext(const QObject *obj)
Definition qqml.cpp:71
QQuickItem * qobject_cast< QQuickItem * >(QObject *o)
Definition qquickitem.h:483
constexpr int mappedIdToShaderType(const int mappedId)
constexpr int mappedIdToIndex(const int mappedId)
static QVariant getValueFromProperty(QObject *item, const QMetaObject *itemMetaObject, const QByteArray &name, int propertyIndex)
void qtquick_shadereffect_purge_gui_thread_shader_cache()
constexpr int indexToMappedId(const int shaderType, const int idx)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define qPrintable(string)
Definition qstring.h:1391
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define emit
QObject::connect nullptr
QGraphicsItem * item
QDataWidgetMapper * mapper
[0]
\inmodule QtCore
QMetaProperty property(int index) const
Returns the meta-data for the property with the given index.
QSGGuiThreadShaderEffectManager::ShaderInfo shaderInfo
\qmltype MapCircle \instantiates QDeclarativeCircleMapItem \inqmlmodule QtLocation
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent
\inmodule QtQuick
Definition qquickitem.h:158