Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
504namespace QtPrivate {
506{
507public:
508 typedef std::function<void()> PropChangedFunc;
509
511 : QSlotObjectBase(&impl), _signalIndex(-1), func(func)
512 { ref(); }
513
514 void setSignalIndex(int idx) { _signalIndex = idx; }
515 int signalIndex() const { return _signalIndex; }
516
517private:
518 int _signalIndex;
520
521 static void impl(int which, QSlotObjectBase *this_, QObject *, void **a, bool *ret)
522 {
523 auto thiz = static_cast<EffectSlotMapper*>(this_);
524 switch (which) {
525 case Destroy:
526 delete thiz;
527 break;
528 case Call:
529 thiz->func();
530 break;
531 case Compare:
532 *ret = thiz == reinterpret_cast<EffectSlotMapper *>(a[0]);
533 break;
534 case NumOperations: ;
535 }
536 }
537};
538} // namespace QtPrivate
539
545
547{
549 d->inDestructor = true;
550}
551
566{
567 Q_D(const QQuickShaderEffect);
568 return d->fragmentShader();
569}
570
572{
574 d->setFragmentShader(fileUrl);
575}
576
591{
592 Q_D(const QQuickShaderEffect);
593 return d->vertexShader();
594}
595
597{
599 d->setVertexShader(fileUrl);
600}
601
612{
613 Q_D(const QQuickShaderEffect);
614 return d->blending();
615}
616
618{
620 d->setBlending(enable);
621}
622
637{
638 Q_D(const QQuickShaderEffect);
639 return d->mesh();
640}
641
643{
645 d->setMesh(mesh);
646}
647
661{
662 Q_D(const QQuickShaderEffect);
663 return d->cullMode();
664}
665
667{
669 return d->setCullMode(face);
670}
671
694{
695 Q_D(const QQuickShaderEffect);
696 return d->supportsAtlasTextures();
697}
698
700{
702 d->setSupportsAtlasTextures(supports);
703}
704
747{
748 Q_D(const QQuickShaderEffect);
749 return d->log();
750}
751
753{
754 Q_D(const QQuickShaderEffect);
755 return d->status();
756}
757
759{
761 d->handleEvent(e);
762 return QQuickItem::event(e);
763}
764
765void QQuickShaderEffect::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
766{
768 d->handleGeometryChanged(newGeometry, oldGeometry);
769 QQuickItem::geometryChange(newGeometry, oldGeometry);
770}
771
773{
775 return d->handleUpdatePaintNode(oldNode, updatePaintNodeData);
776}
777
779{
781 d->maybeUpdateShaders();
783}
784
786{
788 d->handleItemChange(change, value);
790}
791
796
798{
799 auto node = static_cast<QSGShaderEffectNode *>(QQuickItemPrivate::get(this)->paintNode);
800 if (!node)
801 return false;
802
804 return d->updateUniformValue(name, value, node);
805}
806
808{
810 if (!qmlEngine(q))
811 return;
813}
814
815constexpr int indexToMappedId(const int shaderType, const int idx)
816{
817 return idx | (shaderType << 16);
818}
819
820constexpr int mappedIdToIndex(const int mappedId)
821{
822 return mappedId & 0xFFFF;
823}
824
825constexpr int mappedIdToShaderType(const int mappedId)
826{
827 return mappedId >> 16;
828}
829
831 : m_meshResolution(1, 1)
832 , m_mesh(nullptr)
833 , m_cullMode(QQuickShaderEffect::NoCulling)
834 , m_blending(true)
835 , m_supportsAtlasTextures(false)
836 , m_mgr(nullptr)
837 , m_fragNeedsUpdate(true)
838 , m_vertNeedsUpdate(true)
839{
840 qRegisterMetaType<QSGGuiThreadShaderEffectManager::ShaderInfo::Type>("ShaderInfo::Type");
841 for (int i = 0; i < NShader; ++i)
842 m_inProgress[i] = nullptr;
843}
844
846{
847 for (int i = 0; i < NShader; ++i) {
848 disconnectSignals(Shader(i));
849 clearMappers(Shader(i));
850 }
851
852 delete m_mgr;
853}
854
856{
858 if (m_fragShader == fileUrl)
859 return;
860
861 m_fragShader = fileUrl;
862
863 m_fragNeedsUpdate = true;
864 if (q->isComponentComplete())
866
867 emit q->fragmentShaderChanged();
868}
869
871{
873 if (m_vertShader == fileUrl)
874 return;
875
876 m_vertShader = fileUrl;
877
878 m_vertNeedsUpdate = true;
879 if (q->isComponentComplete())
881
882 emit q->vertexShaderChanged();
883}
884
886{
888 if (m_blending == enable)
889 return;
890
891 m_blending = enable;
892 q->update();
893 emit q->blendingChanged();
894}
895
897{
898 return m_mesh ? QVariant::fromValue(static_cast<QObject *>(m_mesh))
899 : QVariant::fromValue(m_meshResolution);
900}
901
903{
905 QQuickShaderEffectMesh *newMesh = qobject_cast<QQuickShaderEffectMesh *>(qvariant_cast<QObject *>(mesh));
906 if (newMesh && newMesh == m_mesh)
907 return;
908
909 if (m_mesh)
910 QObject::disconnect(m_meshConnection);
911
912 m_mesh = newMesh;
913
914 if (m_mesh) {
916 [this] { markGeometryDirtyAndUpdate(); });
917 } else {
918 if (mesh.canConvert<QSize>()) {
919 m_meshResolution = mesh.toSize();
920 } else {
921 QList<QByteArray> res = mesh.toByteArray().split('x');
922 bool ok = res.size() == 2;
923 if (ok) {
924 int w = res.at(0).toInt(&ok);
925 if (ok) {
926 int h = res.at(1).toInt(&ok);
927 if (ok)
928 m_meshResolution = QSize(w, h);
929 }
930 }
931 if (!ok)
932 qWarning("ShaderEffect: mesh property must be a size or an object deriving from QQuickShaderEffectMesh");
933 }
934 m_defaultMesh.setResolution(m_meshResolution);
935 }
936
938 q->update();
939
940 emit q->meshChanged();
941}
942
944{
946 if (m_cullMode == face)
947 return;
948
949 m_cullMode = face;
950 q->update();
951 emit q->cullModeChanged();
952}
953
955{
957 if (m_supportsAtlasTextures == supports)
958 return;
959
960 m_supportsAtlasTextures = supports;
962 emit q->supportsAtlasTexturesChanged();
963}
964
970
972{
973 QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
974 if (!mgr)
975 return QString();
976
977 return mgr->log();
978}
979
981{
982 QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
983 if (!mgr)
985
986 return QQuickShaderEffect::Status(mgr->status());
987}
988
990{
991 if (event->type() == QEvent::DynamicPropertyChange) {
992 const auto propertyName = static_cast<QDynamicPropertyChangeEvent *>(event)->propertyName();
993 for (int i = 0; i < NShader; ++i) {
994 const auto mappedId = findMappedShaderVariableId(propertyName, Shader(i));
995 if (mappedId)
996 propertyChanged(*mappedId);
997 }
998 }
999}
1000
1005
1007{
1008 Q_Q(QQuickShaderEffect);
1009 QSGShaderEffectNode *node = static_cast<QSGShaderEffectNode *>(oldNode);
1010
1011 if (q->width() <= 0 || q->height() <= 0) {
1012 delete node;
1013 return nullptr;
1014 }
1015
1016 // Do not change anything while a new shader is being reflected or compiled.
1017 if (m_inProgress[Vertex] || m_inProgress[Fragment])
1018 return node;
1019
1020 // The manager should be already created on the gui thread. Just take that instance.
1021 QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
1022 if (!mgr) {
1023 delete node;
1024 return nullptr;
1025 }
1026
1027 if (!node) {
1028 QSGRenderContext *rc = QQuickWindowPrivate::get(q->window())->context;
1029 node = rc->sceneGraphContext()->createShaderEffectNode(rc);
1030 if (!node) {
1031 qWarning("No shader effect node");
1032 return nullptr;
1033 }
1036 }
1037
1039 sd.dirty = m_dirty;
1040 sd.cullMode = QSGShaderEffectNode::CullMode(m_cullMode);
1041 sd.blending = m_blending;
1042 sd.vertex.shader = &m_shaders[Vertex];
1043 sd.vertex.dirtyConstants = &m_dirtyConstants[Vertex];
1044 sd.vertex.dirtyTextures = &m_dirtyTextures[Vertex];
1045 sd.fragment.shader = &m_shaders[Fragment];
1046 sd.fragment.dirtyConstants = &m_dirtyConstants[Fragment];
1047 sd.fragment.dirtyTextures = &m_dirtyTextures[Fragment];
1048 sd.materialTypeCacheKey = q->window();
1049 sd.viewCount = QQuickWindowPrivate::get(q->window())->multiViewCount();
1050
1051 node->syncMaterial(&sd);
1052
1054 node->setGeometry(nullptr);
1055 m_dirty &= ~QSGShaderEffectNode::DirtyShaderMesh;
1057 }
1058
1060 const QRectF rect(0, 0, q->width(), q->height());
1061 QQuickShaderEffectMesh *mesh = m_mesh ? m_mesh : &m_defaultMesh;
1062 QSGGeometry *geometry = node->geometry();
1063
1064 const QRectF srcRect = node->updateNormalizedTextureSubRect(m_supportsAtlasTextures);
1065 geometry = mesh->updateGeometry(geometry, 2, 0, srcRect, rect);
1066
1067 node->setFlag(QSGNode::OwnsGeometry, false);
1068 node->setGeometry(geometry);
1069 node->setFlag(QSGNode::OwnsGeometry, true);
1070
1071 m_dirty &= ~QSGShaderEffectNode::DirtyShaderGeometry;
1072 }
1073
1074 m_dirty = {};
1075 for (int i = 0; i < NShader; ++i) {
1076 m_dirtyConstants[i].clear();
1077 m_dirtyTextures[i].clear();
1078 }
1079
1080 return node;
1081}
1082
1084{
1085 Q_Q(QQuickShaderEffect);
1086 if (m_vertNeedsUpdate)
1087 m_vertNeedsUpdate = !updateShader(Vertex, m_vertShader);
1088 if (m_fragNeedsUpdate)
1089 m_fragNeedsUpdate = !updateShader(Fragment, m_fragShader);
1090 if (m_vertNeedsUpdate || m_fragNeedsUpdate) {
1091 // This function is invoked either from componentComplete or in a
1092 // response to a previous invocation's polish() request. If this is
1093 // case #1 then updateShader can fail due to not having a window or
1094 // scenegraph ready. Schedule the polish to try again later. In case #2
1095 // the backend probably does not have shadereffect support so there is
1096 // nothing to do for us here.
1097 if (!q->window() || !q->window()->isSceneGraphInitialized())
1098 q->polish();
1099 }
1100}
1101
1103 QSGShaderEffectNode *node)
1104{
1105 Q_Q(QQuickShaderEffect);
1106 const auto mappedId = findMappedShaderVariableId(name);
1107 if (!mappedId)
1108 return false;
1109
1110 const Shader type = Shader(mappedIdToShaderType(*mappedId));
1111 const int idx = mappedIdToIndex(*mappedId);
1112
1113 // Update value
1114 m_shaders[type].varData[idx].value = value;
1115
1116 // Insert dirty uniform
1117 QSet<int> dirtyConstants[NShader];
1118 dirtyConstants[type].insert(idx);
1119
1120 // Sync material change
1123 sd.cullMode = QSGShaderEffectNode::CullMode(m_cullMode);
1124 sd.blending = m_blending;
1125 sd.vertex.shader = &m_shaders[Vertex];
1126 sd.vertex.dirtyConstants = &dirtyConstants[Vertex];
1127 sd.vertex.dirtyTextures = {};
1128 sd.fragment.shader = &m_shaders[Fragment];
1129 sd.fragment.dirtyConstants = &dirtyConstants[Fragment];
1130 sd.fragment.dirtyTextures = {};
1131 sd.materialTypeCacheKey = q->window();
1132 sd.viewCount = QQuickWindowPrivate::get(q->window())->multiViewCount();
1133
1134 node->syncMaterial(&sd);
1135
1136 return true;
1137}
1138
1140{
1141 if (inDestructor)
1142 return;
1143
1144 // Move the window ref.
1145 if (change == QQuickItem::ItemSceneChange) {
1146 for (int shaderType = 0; shaderType < NShader; ++shaderType) {
1147 for (const auto &vd : std::as_const(m_shaders[shaderType].varData)) {
1148 if (vd.specialType == QSGShaderEffectNode::VariableData::Source) {
1149 QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(vd.value));
1150 if (source) {
1151 if (value.window)
1152 QQuickItemPrivate::get(source)->refWindow(value.window);
1153 else
1154 QQuickItemPrivate::get(source)->derefWindow();
1155 }
1156 }
1157 }
1158 }
1159 }
1160}
1161
1162QSGGuiThreadShaderEffectManager *QQuickShaderEffectPrivate::shaderEffectManager() const
1163{
1164 Q_Q(const QQuickShaderEffect);
1165 if (!m_mgr) {
1166 // return null if this is not the gui thread and not already created
1167 if (QThread::currentThread() != q->thread())
1168 return m_mgr;
1169 QQuickWindow *w = q->window();
1170 if (w) { // note: just the window, don't care about isSceneGraphInitialized() here
1171 m_mgr = QQuickWindowPrivate::get(w)->context->sceneGraphContext()->createGuiThreadShaderEffectManager();
1172 if (m_mgr) {
1178 { const_cast<QQuickShaderEffectPrivate *>(this)->shaderCodePrepared(ok, typeHint, loadUrl, result); });
1179 }
1180 }
1181 }
1182 return m_mgr;
1183}
1184
1185void QQuickShaderEffectPrivate::disconnectSignals(Shader shaderType)
1186{
1187 Q_Q(QQuickShaderEffect);
1188 for (auto *mapper : m_mappers[shaderType]) {
1189 void *a = mapper;
1190 if (mapper)
1191 QObjectPrivate::disconnect(q, mapper->signalIndex(), &a);
1192 }
1193 for (const auto &vd : std::as_const(m_shaders[shaderType].varData)) {
1194 if (vd.specialType == QSGShaderEffectNode::VariableData::Source) {
1195 QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(vd.value));
1196 if (source) {
1197 if (q->window())
1198 QQuickItemPrivate::get(source)->derefWindow();
1199 auto it = m_destroyedConnections.constFind(source);
1200 if (it != m_destroyedConnections.constEnd()) {
1202 m_destroyedConnections.erase(it);
1203 }
1204 }
1205 }
1206 }
1207}
1208
1209void QQuickShaderEffectPrivate::clearMappers(QQuickShaderEffectPrivate::Shader shaderType)
1210{
1211 for (auto *mapper : std::as_const(m_mappers[shaderType])) {
1212 if (mapper)
1213 mapper->destroyIfLastRef();
1214 }
1215 m_mappers[shaderType].clear();
1216}
1217
1218static inline QVariant getValueFromProperty(QObject *item, const QMetaObject *itemMetaObject,
1219 const QByteArray &name, int propertyIndex)
1220{
1222 if (propertyIndex == -1) {
1223 value = item->property(name);
1224 } else {
1225 value = itemMetaObject->property(propertyIndex).read(item);
1226 }
1227 return value;
1228}
1229
1230using QQuickShaderInfoCache = QHash<QUrl, QSGGuiThreadShaderEffectManager::ShaderInfo>;
1231Q_GLOBAL_STATIC(QQuickShaderInfoCache, shaderInfoCache)
1232
1234{
1235 shaderInfoCache()->clear();
1236}
1237
1238bool QQuickShaderEffectPrivate::updateShader(Shader shaderType, const QUrl &fileUrl)
1239{
1240 Q_Q(QQuickShaderEffect);
1241 QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
1242 if (!mgr)
1243 return false;
1244
1245 const bool texturesSeparate = mgr->hasSeparateSamplerAndTextureObjects();
1246
1247 disconnectSignals(shaderType);
1248
1249 m_shaders[shaderType].shaderInfo.variables.clear();
1250 m_shaders[shaderType].varData.clear();
1251
1252 if (!fileUrl.isEmpty()) {
1253 const QQmlContext *context = qmlContext(q);
1254 const QUrl loadUrl = context ? context->resolvedUrl(fileUrl) : fileUrl;
1255 auto it = shaderInfoCache()->constFind(loadUrl);
1256 if (it != shaderInfoCache()->cend()) {
1257 m_shaders[shaderType].shaderInfo = *it;
1258 m_shaders[shaderType].hasShaderCode = true;
1259 } else {
1260 // Each prepareShaderCode call needs its own work area, hence the
1261 // dynamic alloc. If there are calls in progress, let those run to
1262 // finish, their results can then simply be ignored because
1263 // m_inProgress indicates what we care about.
1264 m_inProgress[shaderType] = new QSGGuiThreadShaderEffectManager::ShaderInfo;
1268 // Figure out what input parameters and variables are used in the
1269 // shader. This is where the data is pulled in from the file.
1270 // (however, if there is compilation involved, that happens at a
1271 // later stage, up to the QRhi backend)
1272 mgr->prepareShaderCode(typeHint, loadUrl, m_inProgress[shaderType]);
1273 // the rest is handled in shaderCodePrepared()
1274 return true;
1275 }
1276 } else {
1277 m_shaders[shaderType].hasShaderCode = false;
1278 if (shaderType == Fragment) {
1279 // With built-in shaders hasShaderCode is set to false and all
1280 // metadata is empty, as it is left up to the node to provide a
1281 // built-in default shader and its metadata. However, in case of
1282 // the built-in fragment shader the value for 'source' has to be
1283 // provided and monitored like with an application-provided shader.
1285 v.name = QByteArrayLiteral("source");
1286 v.bindPoint = 1; // fake, must match the default source bindPoint in qquickshadereffectnode.cpp
1289 m_shaders[shaderType].shaderInfo.variables.append(v);
1290 }
1291 }
1292
1293 updateShaderVars(shaderType);
1295 q->update();
1296 return true;
1297}
1298
1301{
1302 Q_Q(QQuickShaderEffect);
1303 const Shader shaderType = typeHint == QSGGuiThreadShaderEffectManager::ShaderInfo::TypeVertex ? Vertex : Fragment;
1304
1305 // If another call was made to updateShader() for the same shader type in
1306 // the meantime then our results are useless, just drop them.
1307 if (result != m_inProgress[shaderType]) {
1308 delete result;
1309 return;
1310 }
1311
1312 m_shaders[shaderType].shaderInfo = *result;
1313 delete result;
1314 m_inProgress[shaderType] = nullptr;
1315
1316 if (!ok) {
1317 qWarning("ShaderEffect: shader preparation failed for %s\n%s\n",
1318 qPrintable(loadUrl.toString()), qPrintable(log()));
1319 m_shaders[shaderType].hasShaderCode = false;
1320 return;
1321 }
1322
1323 m_shaders[shaderType].hasShaderCode = true;
1324 shaderInfoCache()->insert(loadUrl, m_shaders[shaderType].shaderInfo);
1325 updateShaderVars(shaderType);
1327 q->update();
1328}
1329
1330void QQuickShaderEffectPrivate::updateShaderVars(Shader shaderType)
1331{
1332 Q_Q(QQuickShaderEffect);
1333 QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
1334 if (!mgr)
1335 return;
1336
1337 const bool texturesSeparate = mgr->hasSeparateSamplerAndTextureObjects();
1338
1339 const int varCount = m_shaders[shaderType].shaderInfo.variables.size();
1340 m_shaders[shaderType].varData.resize(varCount);
1341
1342 // Recreate signal mappers when the shader has changed.
1343 clearMappers(shaderType);
1344
1346
1347 if (!m_itemMetaObject)
1348 m_itemMetaObject = q->metaObject();
1349
1350 // Hook up the signals to get notified about changes for properties that
1351 // correspond to variables in the shader. Store also the values.
1352 for (int i = 0; i < varCount; ++i) {
1353 const auto &v(m_shaders[shaderType].shaderInfo.variables.at(i));
1354 QSGShaderEffectNode::VariableData &vd(m_shaders[shaderType].varData[i]);
1355 const bool isSpecial = v.name.startsWith("qt_"); // special names not mapped to properties
1356 if (isSpecial) {
1357 if (v.name == "qt_Opacity")
1359 else if (v.name == "qt_Matrix")
1361 else if (v.name.startsWith("qt_SubRect_"))
1363 continue;
1364 }
1365
1366 // The value of a property corresponding to a sampler is the source
1367 // item ref, unless there are separate texture objects in which case
1368 // the sampler is ignored (here).
1370 if (texturesSeparate) {
1372 continue;
1373 } else {
1375 }
1377 Q_ASSERT(texturesSeparate);
1379 } else {
1381 }
1382
1383 // Find the property on the ShaderEffect item.
1384 int propIdx = -1;
1385 const QQmlPropertyData *pd = nullptr;
1386 if (propCache) {
1387 pd = propCache->property(QLatin1String(v.name), nullptr, nullptr);
1388 if (pd) {
1389 if (!pd->isFunction())
1390 propIdx = pd->coreIndex();
1391 }
1392 }
1393 if (propIdx >= 0) {
1394 if (pd && !pd->isFunction()) {
1395 if (pd->notifyIndex() == -1) {
1396 qWarning("QQuickShaderEffect: property '%s' does not have notification method!", v.name.constData());
1397 } else {
1398 const int mappedId = indexToMappedId(shaderType, i);
1399 auto mapper = new QtPrivate::EffectSlotMapper([this, mappedId](){
1400 this->propertyChanged(mappedId);
1401 });
1402 m_mappers[shaderType].append(mapper);
1403 mapper->setSignalIndex(m_itemMetaObject->property(propIdx).notifySignal().methodIndex());
1404 Q_ASSERT(q->metaObject() == m_itemMetaObject);
1405 bool ok = QObjectPrivate::connectImpl(q, pd->notifyIndex(), q, nullptr, mapper,
1406 Qt::AutoConnection, nullptr, m_itemMetaObject);
1407 if (!ok)
1408 qWarning() << "Failed to connect to property" << m_itemMetaObject->property(propIdx).name()
1409 << "(" << propIdx << ", signal index" << pd->notifyIndex()
1410 << ") of item" << q;
1411 }
1412 }
1413 } else {
1414 // Do not warn for dynamic properties.
1415 if (!q->property(v.name.constData()).isValid())
1416 qWarning("ShaderEffect: '%s' does not have a matching property", v.name.constData());
1417 }
1418
1419
1420 vd.propertyIndex = propIdx;
1421 vd.value = getValueFromProperty(q, m_itemMetaObject, v.name, vd.propertyIndex);
1422 if (vd.specialType == QSGShaderEffectNode::VariableData::Source) {
1423 QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(vd.value));
1424 if (source) {
1425 if (q->window())
1426 QQuickItemPrivate::get(source)->refWindow(q->window());
1427
1428 // Cannot just pass q as the 'context' for the connect(). The
1429 // order of destruction is...complicated. Having an inline
1430 // source (e.g. source: ShaderEffectSource { ... } in QML would
1431 // emit destroyed() after the connection was already gone. To
1432 // work that around, store the Connection and manually
1433 // disconnect instead.
1434 if (!m_destroyedConnections.contains(source))
1435 m_destroyedConnections.insert(source, QObject::connect(source, &QObject::destroyed, [this](QObject *obj) { sourceDestroyed(obj); }));
1436 }
1437 }
1438 }
1439}
1440
1441std::optional<int> QQuickShaderEffectPrivate::findMappedShaderVariableId(const QByteArray &name) const
1442{
1443 for (int shaderType = 0; shaderType < NShader; ++shaderType) {
1444 const auto &vars = m_shaders[shaderType].shaderInfo.variables;
1445 for (int idx = 0; idx < vars.size(); ++idx) {
1446 if (vars[idx].name == name)
1447 return indexToMappedId(shaderType, idx);
1448 }
1449 }
1450
1451 return {};
1452}
1453
1454std::optional<int> QQuickShaderEffectPrivate::findMappedShaderVariableId(const QByteArray &name, Shader shaderType) const
1455{
1456 const auto &vars = m_shaders[shaderType].shaderInfo.variables;
1457 for (int idx = 0; idx < vars.size(); ++idx) {
1458 if (vars[idx].name == name)
1459 return indexToMappedId(shaderType, idx);
1460 }
1461
1462 return {};
1463}
1464
1465bool QQuickShaderEffectPrivate::sourceIsUnique(QQuickItem *source, Shader typeToSkip, int indexToSkip) const
1466{
1467 for (int shaderType = 0; shaderType < NShader; ++shaderType) {
1468 for (int idx = 0; idx < m_shaders[shaderType].varData.size(); ++idx) {
1469 if (shaderType != typeToSkip || idx != indexToSkip) {
1470 const auto &vd(m_shaders[shaderType].varData[idx]);
1471 if (vd.specialType == QSGShaderEffectNode::VariableData::Source && qvariant_cast<QObject *>(vd.value) == source)
1472 return false;
1473 }
1474 }
1475 }
1476 return true;
1477}
1478
1480{
1481 Q_Q(QQuickShaderEffect);
1482 const Shader type = Shader(mappedIdToShaderType(mappedId));
1483 const int idx = mappedIdToIndex(mappedId);
1484 const auto &v(m_shaders[type].shaderInfo.variables[idx]);
1485 auto &vd(m_shaders[type].varData[idx]);
1486
1487 QVariant oldValue = vd.value;
1488 vd.value = getValueFromProperty(q, m_itemMetaObject, v.name, vd.propertyIndex);
1489
1490 if (vd.specialType == QSGShaderEffectNode::VariableData::Source) {
1491 QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(oldValue));
1492 if (source) {
1493 if (q->window())
1494 QQuickItemPrivate::get(source)->derefWindow();
1495 // If the same source has been attached to two separate
1496 // textures/samplers, then changing one of them would trigger both
1497 // to be disconnected. So check first.
1498 if (sourceIsUnique(source, type, idx)) {
1499 auto it = m_destroyedConnections.constFind(source);
1500 if (it != m_destroyedConnections.constEnd()) {
1502 m_destroyedConnections.erase(it);
1503 }
1504 }
1505 }
1506
1507 source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(vd.value));
1508 if (source) {
1509 // 'source' needs a window to get a scene graph node. It usually gets one through its
1510 // parent, but if the source item is "inline" rather than a reference -- i.e.
1511 // "property variant source: Image { }" instead of "property variant source: foo" -- it
1512 // will not get a parent. In those cases, 'source' should get the window from 'item'.
1513 if (q->window())
1514 QQuickItemPrivate::get(source)->refWindow(q->window());
1515 if (!m_destroyedConnections.contains(source))
1516 m_destroyedConnections.insert(source, QObject::connect(source, &QObject::destroyed, [this](QObject *obj) { sourceDestroyed(obj); }));
1517 }
1518
1520 m_dirtyTextures[type].insert(idx);
1521
1522 } else {
1524 m_dirtyConstants[type].insert(idx);
1525 }
1526
1527 q->update();
1528}
1529
1531{
1532 for (int shaderType = 0; shaderType < NShader; ++shaderType) {
1533 for (auto &vd : m_shaders[shaderType].varData) {
1534 if (vd.specialType == QSGShaderEffectNode::VariableData::Source && vd.value.canConvert<QObject *>()) {
1535 if (qvariant_cast<QObject *>(vd.value) == object)
1536 vd.value = QVariant();
1537 }
1538 }
1539 }
1540}
1541
1548
1554
1556
1557#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
const_iterator constFind(const Key &key) const noexcept
Definition qhash.h:1299
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:1219
iterator erase(const_iterator it)
Definition qhash.h:1233
bool contains(const Key &key) const noexcept
Returns true if the hash contains an item with the key; otherwise returns false.
Definition qhash.h:1007
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1303
qsizetype size() const noexcept
Definition qlist.h:397
int methodIndex() const
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:5241
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:328
\inmodule QtCore
Definition qobject.h:103
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
Definition qobject.cpp:3236
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
static QQuickItemPrivate * get(QQuickItem *item)
The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
Definition qquickitem.h:63
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:144
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)
\qmltype Window \instantiates QQuickWindow \inqmlmodule QtQuick
\inmodule QtCore\reentrant
Definition qrect.h:484
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:764
virtual QSGShaderEffectNode * createShaderEffectNode(QSGRenderContext *renderContext)
Creates a new shader effect node.
The QSGGeometry class provides low-level storage for graphics primitives in the \l{Qt Quick Scene Gra...
Definition qsggeometry.h:15
void shaderCodePrepared(bool ok, ShaderInfo::Type typeHint, const QUrl &src, ShaderInfo *result)
\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:586
QSGContext * sceneGraphContext() const
virtual QRectF updateNormalizedTextureSubRect(bool supportsAtlasTextures)=0
virtual void syncMaterial(SyncData *syncData)=0
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:129
static QThread * currentThread()
Definition qthread.cpp:1039
\inmodule QtCore
Definition qurl.h:94
bool isEmpty() const
Returns true if the URL has no data; otherwise returns false.
Definition qurl.cpp:1896
QString toString(FormattingOptions options=FormattingOptions(PrettyDecoded)) const
Returns a string representation of the URL.
Definition qurl.cpp:2831
\inmodule QtCore
Definition qvariant.h:65
T value() const &
Definition qvariant.h:516
QSize toSize() const
Returns the variant as a QSize if the variant has userType() \l QMetaType::QSize; otherwise returns a...
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:536
bool canConvert(QMetaType targetType) const
Definition qvariant.h:345
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)
QSet< QString >::iterator it
rect
[4]
const PluginKeyMapConstIterator cend
Combined button and popup list for selecting options.
\macro QT_NO_KEYWORDS >
@ 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:166
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:80
QQmlContext * qmlContext(const QObject *obj)
Definition qqml.cpp:75
QQuickItem * qobject_cast< QQuickItem * >(QObject *o)
Definition qquickitem.h:492
constexpr int mappedIdToShaderType(const int mappedId)
QHash< QUrl, QSGGuiThreadShaderEffectManager::ShaderInfo > QQuickShaderInfoCache
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:1531
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
\inmodule QtQuick
Definition qquickitem.h:159