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
qquick3dtexture.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qquick3dtexture_p.h"
5#include <QtQuick3DRuntimeRender/private/qssgrenderimage_p.h>
6#include <QtQuick3DRuntimeRender/private/qssgrendertexturedata_p.h>
7#include <QtQuick3DRuntimeRender/private/qssgrenderloadedtexture_p.h>
8#include <QtQml/QQmlFile>
9#include <QtQuick/QQuickItem>
10#include <QtQuick/private/qquickitem_p.h>
11#include <QtCore/qmath.h>
12
13#include <ssg/qssgrenderextensions.h>
14
15#include "qquick3dobject_p.h"
17#include "qquick3dutils_p.h"
19#include "qquick3dviewport_p.h"
20
22
98
100 : QQuick3DObject(dd, parent)
101{
102 const QMetaObject *mo = metaObject();
103 const int updateSlotIdx = mo->indexOfSlot("update()");
104 if (updateSlotIdx >= 0)
105 m_updateSlot = mo->method(updateSlotIdx);
106 if (!m_updateSlot.isValid())
107 qWarning("QQuick3DTexture: Failed to find update() slot");
108}
109
111{
112 if (m_layer) {
113 if (m_sceneManagerForLayer)
114 m_sceneManagerForLayer->qsgDynamicTextures.removeAll(m_layer);
115 m_layer->deleteLater(); // uhh...
116 }
117
118 if (m_sourceItem) {
119 QQuickItemPrivate *sourcePrivate = QQuickItemPrivate::get(m_sourceItem);
120 sourcePrivate->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
121 }
122}
123
161{
162 return m_source;
163}
164
210{
211 return m_sourceItem;
212}
213
235{
236 return m_scaleU;
237}
238
260{
261 return m_scaleV;
262}
263
287{
288 return m_mappingMode;
289}
290
305{
306 return m_tilingModeHorizontal;
307}
308
324{
325 return m_tilingModeVertical;
326}
327
340QQuick3DTexture::TilingMode QQuick3DTexture::depthTiling() const
341{
342 return m_tilingModeDepth;
343}
344
363{
364 return m_rotationUV;
365}
366
384{
385 return m_positionU;
386}
387
409{
410 return m_positionV;
411}
412
431{
432 return m_pivotU;
433}
434
453{
454 return m_pivotV;
455}
456
474{
475 return m_flipU;
476}
477
495{
496 return m_flipV;
497}
498
509{
510 return m_indexUV;
511}
512
529{
530 return m_magFilter;
531}
532
549{
550 return m_minFilter;
551}
552
570{
571 return m_mipFilter;
572}
573
588{
589 return m_textureData;
590}
591
614{
615 return m_generateMipmaps;
616}
617
650{
651 return m_autoOrientation;
652}
653
669{
670 return m_renderExtension;
671}
672
674{
675 if (m_source == source)
676 return;
677
678 m_source = source;
679 m_dirtyFlags.setFlag(DirtyFlag::SourceDirty);
680 m_dirtyFlags.setFlag(DirtyFlag::SourceItemDirty);
681 m_dirtyFlags.setFlag(DirtyFlag::TextureDataDirty);
683 update();
684}
685
686void QQuick3DTexture::trySetSourceParent()
687{
688 if (m_sourceItem->parentItem() && m_sourceItemRefed)
689 return;
690
691 auto *sourcePrivate = QQuickItemPrivate::get(m_sourceItem);
692
693 if (!m_sourceItem->parentItem()) {
694 if (const auto &manager = QQuick3DObjectPrivate::get(this)->sceneManager) {
695 if (auto *window = manager->window()) {
696 if (m_sourceItemRefed) {
697 // Item was already refed but probably with hide set to false...
698 // so we need to deref before we ref again below.
699 const bool hide = m_sourceItemReparented;
700 sourcePrivate->derefFromEffectItem(hide);
701 m_sourceItemRefed = false;
702 }
703
704 m_sourceItem->setParentItem(window->contentItem());
705 m_sourceItemReparented = true;
706 update();
707 }
708 }
709 }
710
711 if (!m_sourceItemRefed) {
712 const bool hide = m_sourceItemReparented;
713 sourcePrivate->refFromEffectItem(hide);
714 }
715}
716
718{
719 if (m_sourceItem == sourceItem)
720 return;
721
722 disconnect(m_textureProviderConnection);
723 disconnect(m_textureUpdateConnection);
724
725 if (m_sourceItem) {
726 QQuickItemPrivate *sourcePrivate = QQuickItemPrivate::get(m_sourceItem);
727
728 const bool hide = m_sourceItemReparented;
729 sourcePrivate->derefFromEffectItem(hide);
730 m_sourceItemRefed = false;
731
732 sourcePrivate->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
733 disconnect(m_sourceItem, SIGNAL(destroyed(QObject*)), this, SLOT(sourceItemDestroyed(QObject*)));
734 if (m_sourceItemReparented) {
735 m_sourceItem->setParentItem(nullptr);
736 m_sourceItemReparented = false;
737 }
738 }
739
740 m_sourceItem = sourceItem;
741
742 if (sourceItem) {
743 trySetSourceParent();
744 QQuickItemPrivate *sourcePrivate = QQuickItemPrivate::get(m_sourceItem);
745 sourcePrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
746 connect(m_sourceItem, SIGNAL(destroyed(QObject*)), this, SLOT(sourceItemDestroyed(QObject*)));
747 sourcePrivate->ensureSubsceneDeliveryAgent();
748 }
749
750 if (m_layer) {
751 const auto &manager = QQuick3DObjectPrivate::get(this)->sceneManager;
752 manager->qsgDynamicTextures.removeAll(m_layer);
753 m_sceneManagerForLayer = nullptr;
754 // cannot touch m_layer here
755 }
756 m_initializedSourceItem = nullptr;
757 m_initializedSourceItemSize = QSize();
758
759 m_dirtyFlags.setFlag(DirtyFlag::SourceDirty);
760 m_dirtyFlags.setFlag(DirtyFlag::SourceItemDirty);
761 m_dirtyFlags.setFlag(DirtyFlag::TextureDataDirty);
763 update();
764}
765
767{
768 if (qFuzzyCompare(m_scaleU, scaleU))
769 return;
770
771 m_scaleU = scaleU;
772 m_dirtyFlags.setFlag(DirtyFlag::TransformDirty);
774 update();
775}
776
778{
779 if (qFuzzyCompare(m_scaleV, scaleV))
780 return;
781
782 m_scaleV = scaleV;
783 m_dirtyFlags.setFlag(DirtyFlag::TransformDirty);
785 update();
786}
787
789{
790 if (m_mappingMode == mappingMode)
791 return;
792
793 m_mappingMode = mappingMode;
795 update();
796}
797
799{
800 if (m_tilingModeHorizontal == tilingModeHorizontal)
801 return;
802
803 m_tilingModeHorizontal = tilingModeHorizontal;
805 update();
806}
807
809{
810 if (m_tilingModeVertical == tilingModeVertical)
811 return;
812
813 m_tilingModeVertical = tilingModeVertical;
815 update();
816}
817
818void QQuick3DTexture::setDepthTiling(QQuick3DTexture::TilingMode tilingModeDepth)
819{
820 if (m_tilingModeDepth == tilingModeDepth)
821 return;
822 m_tilingModeDepth = tilingModeDepth;
823 emit depthTilingChanged();
824 update();
825}
826
827void QQuick3DTexture::setRotationUV(float rotationUV)
828{
829 if (qFuzzyCompare(m_rotationUV, rotationUV))
830 return;
831
832 m_rotationUV = rotationUV;
833 m_dirtyFlags.setFlag(DirtyFlag::TransformDirty);
835 update();
836}
837
838void QQuick3DTexture::setPositionU(float positionU)
839{
840 if (qFuzzyCompare(m_positionU, positionU))
841 return;
842
843 m_positionU = positionU;
844 m_dirtyFlags.setFlag(DirtyFlag::TransformDirty);
846 update();
847}
848
849void QQuick3DTexture::setPositionV(float positionV)
850{
851 if (qFuzzyCompare(m_positionV, positionV))
852 return;
853
854 m_positionV = positionV;
855 m_dirtyFlags.setFlag(DirtyFlag::TransformDirty);
857 update();
858}
859
861{
862 if (qFuzzyCompare(m_pivotU, pivotU))
863 return;
864
865 m_pivotU = pivotU;
866 m_dirtyFlags.setFlag(DirtyFlag::TransformDirty);
868 update();
869}
870
872{
873 if (qFuzzyCompare(m_pivotV, pivotV))
874 return;
875
876 m_pivotV = pivotV;
877 m_dirtyFlags.setFlag(DirtyFlag::TransformDirty);
879 update();
880}
881
883{
884 if (m_flipU == flipU)
885 return;
886
887 m_flipU = flipU;
888 m_dirtyFlags.setFlag(DirtyFlag::TransformDirty);
890 update();
891}
892
894{
895 if (m_flipV == flipV)
896 return;
897
898 m_flipV = flipV;
899 m_dirtyFlags.setFlag(DirtyFlag::FlipVDirty);
901 update();
902}
903
905{
906 if (m_indexUV == indexUV)
907 return;
908
909 if (indexUV < 0)
910 m_indexUV = 0;
911 else if (indexUV > 1)
912 m_indexUV = 1;
913 else
914 m_indexUV = indexUV;
915
916 m_dirtyFlags.setFlag(DirtyFlag::IndexUVDirty);
918 update();
919}
920
922{
923 if (m_textureData == textureData)
924 return;
925
926 // Make sure to disconnect if the geometry gets deleted out from under us
928
929 if (m_textureData)
930 QObject::disconnect(m_textureDataConnection);
931 m_textureData = textureData;
932
933 if (m_textureData) {
934 m_textureDataConnection
935 = QObject::connect(m_textureData, &QQuick3DTextureData::textureDataNodeDirty, this, [this]() {
936 markDirty(DirtyFlag::TextureDataDirty);
937 });
938 }
939
940 m_dirtyFlags.setFlag(DirtyFlag::SourceDirty);
941 m_dirtyFlags.setFlag(DirtyFlag::SourceItemDirty);
942 m_dirtyFlags.setFlag(DirtyFlag::TextureDataDirty);
944 update();
945}
946
947void QQuick3DTexture::setGenerateMipmaps(bool generateMipmaps)
948{
949 if (m_generateMipmaps == generateMipmaps)
950 return;
951
952 m_generateMipmaps = generateMipmaps;
953 m_dirtyFlags.setFlag(DirtyFlag::SamplerDirty);
955 update();
956}
957
958void QQuick3DTexture::setAutoOrientation(bool autoOrientation)
959{
960 if (m_autoOrientation == autoOrientation)
961 return;
962
963 m_autoOrientation = autoOrientation;
964 m_dirtyFlags.setFlag(DirtyFlag::FlipVDirty);
966 update();
967}
968
970{
971 if (m_magFilter == magFilter)
972 return;
973
974 m_magFilter = magFilter;
975 m_dirtyFlags.setFlag(DirtyFlag::SamplerDirty);
977 update();
978}
979
981{
982 if (m_minFilter == minFilter)
983 return;
984
985 m_minFilter = minFilter;
986 m_dirtyFlags.setFlag(DirtyFlag::SamplerDirty);
988 update();
989}
990
992{
993 if (m_mipFilter == mipFilter)
994 return;
995
996 m_mipFilter = mipFilter;
997 m_dirtyFlags.setFlag(DirtyFlag::SamplerDirty);
999 update();
1000}
1001
1002// this function may involve file system access and hence can be expensive
1003bool QQuick3DTexture::effectiveFlipV(const QSSGRenderImage &imageNode) const
1004{
1005 // No magic when autoOrientation is false.
1006 if (!m_autoOrientation)
1007 return m_flipV;
1008
1009 // Keep the same order as in QSSGBufferManager: sourceItem > textureData > source
1010
1011 // Using sourceItem implies inverting (the effective, internal) flipV,
1012 // transparently to the user. Otherwise two #Rectangle models textured with
1013 // two Textures where one has its content loaded from an image file via
1014 // QImage while the other is generated by Qt Quick rendering into the
1015 // texture would appear upside-down relative to each other, and that
1016 // discrepancy is not ideal. (that said, this won't help CustomMaterial, as
1017 // documented for flipV and co.)
1018
1019 if (m_sourceItem)
1020 return !m_flipV;
1021
1022 // With textureData and renderExtension we assume the application knows what it is doing,
1023 // because there the application is controlling the content itself.
1024
1025 if (m_textureData || m_renderExtension)
1026 return m_flipV;
1027
1028 // Compressed textures (or any texture that is coming from the associated
1029 // container formats, such as KTX, i.e. not via QImage but through
1030 // QTextureFileReader) get the implicit flip, like sourceItem. This is done
1031 // mainly for parity with Qt Quick's Image, see QTBUG-93972.
1032
1033 if (!m_source.isEmpty()) {
1034 const QString filePath = imageNode.m_imagePath.path();
1035 if (!filePath.isEmpty()) {
1037 if (QSSGInputUtil::getStreamForTextureFile(filePath, true, nullptr, &fileType)) {
1039 return !m_flipV;
1040 }
1041 }
1042 }
1043
1044 return m_flipV;
1045}
1046
1048{
1049 if (context && url.isRelative()) {
1050 QString path = url.path();
1051 QChar separator = QChar::fromLatin1(';');
1052 if (path.contains(separator)) {
1053 QString resolvedPath;
1054 const QStringList paths = path.split(separator);
1055 bool first = true;
1056 for (auto &s : paths) {
1057 auto mapped = QQmlFile::urlToLocalFileOrQrc(context->resolvedUrl(s));
1058 if (!first)
1059 resolvedPath.append(separator);
1060 resolvedPath.append(mapped);
1061 first = false;
1062 }
1063 return QSSGRenderPath(resolvedPath);
1064 }
1065 }
1067}
1068
1070{
1071 if (!node) {
1072 markAllDirty();
1074 }
1076 auto imageNode = static_cast<QSSGRenderImage *>(node);
1077
1078 if (m_dirtyFlags.testFlag(DirtyFlag::TransformDirty)) {
1079 m_dirtyFlags.setFlag(DirtyFlag::TransformDirty, false);
1080
1081 // flipV and indexUV have their own dirty flags, handled separately below
1082 imageNode->m_flipU = m_flipU;
1083 imageNode->m_scale = QVector2D(m_scaleU, m_scaleV);
1084 imageNode->m_pivot = QVector2D(m_pivotU, m_pivotV);
1085 imageNode->m_rotation = m_rotationUV;
1086 imageNode->m_position = QVector2D(m_positionU, m_positionV);
1087
1089 }
1090
1091 bool nodeChanged = false;
1092 if (m_dirtyFlags.testFlag(DirtyFlag::SourceDirty)) {
1093 m_dirtyFlags.setFlag(DirtyFlag::SourceDirty, false);
1094 m_dirtyFlags.setFlag(DirtyFlag::FlipVDirty, true);
1095 if (!m_source.isEmpty()) {
1096 const QQmlContext *context = qmlContext(this);
1097 imageNode->m_imagePath = resolveImagePath(m_source, context);
1098 } else {
1099 imageNode->m_imagePath = QSSGRenderPath();
1100 }
1101 nodeChanged = true;
1102 }
1103 if (m_dirtyFlags.testFlag(DirtyFlag::IndexUVDirty)) {
1104 m_dirtyFlags.setFlag(DirtyFlag::IndexUVDirty, false);
1105 imageNode->m_indexUV = m_indexUV;
1106 }
1107 nodeChanged |= qUpdateIfNeeded(imageNode->m_mappingMode,
1108 QSSGRenderImage::MappingModes(m_mappingMode));
1109 nodeChanged |= qUpdateIfNeeded(imageNode->m_horizontalTilingMode,
1110 QSSGRenderTextureCoordOp(m_tilingModeHorizontal));
1111 nodeChanged |= qUpdateIfNeeded(imageNode->m_verticalTilingMode,
1112 QSSGRenderTextureCoordOp(m_tilingModeVertical));
1113 nodeChanged |= qUpdateIfNeeded(imageNode->m_depthTilingMode,
1114 QSSGRenderTextureCoordOp(m_tilingModeDepth));
1115
1116 if (m_dirtyFlags.testFlag(DirtyFlag::SamplerDirty)) {
1117 m_dirtyFlags.setFlag(DirtyFlag::SamplerDirty, false);
1118 nodeChanged |= qUpdateIfNeeded(imageNode->m_minFilterType,
1119 QSSGRenderTextureFilterOp(m_minFilter));
1120 nodeChanged |= qUpdateIfNeeded(imageNode->m_magFilterType,
1121 QSSGRenderTextureFilterOp(m_magFilter));
1122 nodeChanged |= qUpdateIfNeeded(imageNode->m_mipFilterType,
1123 QSSGRenderTextureFilterOp(m_mipFilter));
1124 nodeChanged |= qUpdateIfNeeded(imageNode->m_generateMipmaps,
1125 m_generateMipmaps);
1126 }
1127
1128 if (m_dirtyFlags.testFlag(DirtyFlag::TextureDataDirty)) {
1129 m_dirtyFlags.setFlag(DirtyFlag::TextureDataDirty, false);
1130 m_dirtyFlags.setFlag(DirtyFlag::FlipVDirty, true);
1131 if (m_textureData)
1132 imageNode->m_rawTextureData = static_cast<QSSGRenderTextureData *>(QQuick3DObjectPrivate::get(m_textureData)->spatialNode);
1133 else
1134 imageNode->m_rawTextureData = nullptr;
1135 nodeChanged = true;
1136 }
1137
1138 if (m_dirtyFlags.testFlag(DirtyFlag::ExtensionDirty)) {
1139 bool extDirty = false;
1140 if (m_renderExtension) {
1141 auto *sn = QQuick3DObjectPrivate::get(m_renderExtension)->spatialNode;
1142 // NOTE: We don't clear if we haven't gotten the spatial node yet, as
1143 // we'll be called once _again_ when the extensions have been processed.
1144 extDirty = (sn == nullptr);
1145 if (sn && QSSG_GUARD(sn->type == QSSGRenderGraphObject::Type::RenderExtension))
1146 imageNode->m_extensionsSource = static_cast<QSSGRenderExtension *>(sn);
1147 }
1148
1149 m_dirtyFlags.setFlag(DirtyFlag::ExtensionDirty, extDirty);
1150 m_dirtyFlags.setFlag(DirtyFlag::FlipVDirty, true);
1151
1152 nodeChanged = true;
1153 }
1154
1155 if (m_dirtyFlags.testFlag(DirtyFlag::SourceItemDirty)) {
1156 m_dirtyFlags.setFlag(DirtyFlag::SourceItemDirty, false);
1157 m_dirtyFlags.setFlag(DirtyFlag::FlipVDirty, true);
1158 if (m_sourceItem) {
1159 QQuickWindow *window = m_sourceItem->window();
1160 // If it was an inline declared item (very common, e.g. Texture {
1161 // sourceItem: Rectangle { ... } } then it is likely it won't be
1162 // associated with a window (Qt Quick scene) unless we help it to
1163 // one via refWindow. However, this here is only the last resort,
1164 // ideally there is a refWindow upon ItemSceneChange already.
1165 if (!window) {
1166 window = QQuick3DObjectPrivate::get(this)->sceneManager->window();
1167 if (window)
1168 QQuickItemPrivate::get(m_sourceItem)->refWindow(window);
1169 else
1170 qWarning("Unable to get window, this will probably not work");
1171 }
1172
1173 // Workaround: Due to limitation in how the texture provider works in View3D we can't use it in the
1174 // special case when a non-visible View3D is used as a sourceItem, at least not in its current incarnation.
1175 // There's also a drawback here, since we now have an extra indirection by rendering to the layer texture first..
1176 const bool isHiddenView3D = !m_sourceItem->isVisible() && (qobject_cast<QQuick3DViewport *>(m_sourceItem) != nullptr);
1177
1178 // This assumes that the QSGTextureProvider returned never changes,
1179 // which is hopefully the case for both Image and Item layers.
1180 if (QSGTextureProvider *provider = m_sourceItem->textureProvider(); provider != nullptr && !isHiddenView3D) {
1181 imageNode->m_qsgTexture = provider->texture();
1182
1183 disconnect(m_textureProviderConnection);
1184 m_textureProviderConnection = connect(provider, &QSGTextureProvider::textureChanged, this, [this, provider] () {
1185 // called on the render thread, if there is one; the gui
1186 // thread may or may not be blocked (e.g. if the source is
1187 // a View3D, that emits textureChanged() from preprocess,
1188 // so after sync, whereas an Image emits in
1189 // updatePaintNode() where gui is blocked)
1190 auto imageNode = static_cast<QSSGRenderImage *>(QQuick3DObjectPrivate::get(this)->spatialNode);
1191 if (!imageNode)
1192 return;
1193
1194 imageNode->m_qsgTexture = provider->texture();
1195 // the QSGTexture may be different now, go through loadRenderImage() again
1196 imageNode->m_flags.setFlag(QSSGRenderImage::Flag::Dirty);
1197 // Call update() on the main thread - otherwise we could
1198 // end up in a situation where the 3D scene does not update
1199 // due to nothing else changing, even though the source
1200 // texture is now different.
1201 m_updateSlot.invoke(this, Qt::AutoConnection);
1203
1204 disconnect(m_textureUpdateConnection);
1205 auto *sourcePrivate = QQuickItemPrivate::get(m_sourceItem);
1206 if (sourcePrivate->window) {
1207 QQuickItem *sourceItem = m_sourceItem; // for capturing, recognizing in the lambda that m_sourceItem has changed is essential
1208
1209 // Why after, not beforeSynchronizing? Consider the case of an Item layer:
1210 // if the View3D gets to sync (updatePaintNode) first, doing an
1211 // updateTexture() is futile, the QSGLayer is not yet initialized (not
1212 // associated with an Item, has no size, etc.). That happens only once the
1213 // underlying QQuickShaderEffectSource hits its updatePaintNode. And that
1214 // may well happen happen only after the View3D has finished with its sync
1215 // step. By connecting to afterSynchronizing, we still get a chance to
1216 // trigger a layer texture update and so have a QSGTexture with real
1217 // content ready by the time the View3D prepares/renders the 3D scene upon
1218 // the scenegraph's preprocess step (Offscreen) or before/after the
1219 // scenegraph rendering (if Underlay/Overlay).
1220 //
1221 // This eliminates, or in the worst case reduces, the ugly effects of not
1222 // having a texture ready when rendering the 3D scene.
1223
1224 m_textureUpdateConnection = connect(sourcePrivate->window, &QQuickWindow::afterSynchronizing, this, [this, sourceItem]() {
1225 // Called on the render thread with gui blocked (if there is a render thread, that is).
1226 if (m_sourceItem != sourceItem) {
1227 disconnect(m_textureProviderConnection);
1228 disconnect(m_textureUpdateConnection);
1229 return;
1230 }
1231 auto imageNode = static_cast<QSSGRenderImage *>(QQuick3DObjectPrivate::get(this)->spatialNode);
1232 if (!imageNode)
1233 return;
1234
1235 if (QSGDynamicTexture *t = qobject_cast<QSGDynamicTexture *>(imageNode->m_qsgTexture)) {
1236 if (t->updateTexture())
1237 update(); // safe because the gui thread is blocked
1238 }
1240 } else {
1241 qWarning("No window for item, texture updates are doomed");
1242 }
1243
1244 if (m_layer) {
1245 delete m_layer;
1246 m_layer = nullptr;
1247 }
1248 } else {
1249 // Not a texture provider, so not an Image or an Item with
1250 // layer.enabled: true, create our own QSGLayer.
1251 if (m_initializedSourceItem != m_sourceItem || m_initializedSourceItemSize != m_sourceItem->size()) {
1252 // If there was a previous sourceItem and m_layer is valid
1253 // then set its content to null until we get to
1254 // afterSynchronizing, otherwise things can blow up.
1255 if (m_layer)
1256 m_layer->setItem(nullptr);
1257
1258 m_initializedSourceItem = m_sourceItem;
1259 m_initializedSourceItemSize = m_sourceItem->size();
1260
1261 // The earliest next point where we can do anything is
1262 // after the scenegraph's QQuickItem sync round has completed.
1263 connect(window, &QQuickWindow::afterSynchronizing, this, [this, window]() {
1264 auto imageNode = static_cast<QSSGRenderImage *>(QQuick3DObjectPrivate::get(this)->spatialNode);
1265 if (!imageNode)
1266 return;
1267
1268 // Called on the render thread with gui blocked (if there is a render thread, that is).
1269 disconnect(window, &QQuickWindow::afterSynchronizing, this, nullptr);
1270 if (m_layer) {
1271 const auto &manager = QQuick3DObjectPrivate::get(this)->sceneManager;
1272 manager->qsgDynamicTextures.removeAll(m_layer);
1273 delete m_layer;
1274 m_layer = nullptr;
1275 }
1276
1277 QQuickItemPrivate *sourcePrivate = QQuickItemPrivate::get(m_sourceItem);
1278 QSGRenderContext *rc = sourcePrivate->sceneGraphRenderContext();
1279 Q_ASSERT(QThread::currentThread() == rc->thread()); // must be on the render thread
1281 connect(sourcePrivate->window, SIGNAL(sceneGraphInvalidated()), layer, SLOT(invalidated()), Qt::DirectConnection);
1282
1284 manager->qsgDynamicTextures << layer;
1285 m_sceneManagerForLayer = manager;
1286
1288 {
1289 // this is on the render thread so all borked threading-wise (all data here is gui thread stuff...) but will survive
1290 manager->qsgDynamicTextures.removeAll(layer);
1292
1293 QQuickItem *sourceItem = m_sourceItem; // for capturing, recognizing in the lambda that m_sourceItem has changed is essential
1294 connect(layer, &QObject::destroyed, this, [this, sourceItem]()
1295 {
1296 // just as dubious as the previous connection
1297 if (m_initializedSourceItem == sourceItem) {
1298 m_sceneManagerForLayer = nullptr;
1299 m_initializedSourceItem = nullptr;
1300 }
1302
1303 // With every frame try to update the texture. Use
1304 // afterSynchronizing like in the other branch. (why
1305 // after: a property changing something in the 2D
1306 // subtree leading to updates in the content will only
1307 // be "visible" after the (2D item) sync, not before)
1308 //
1309 // If updateTexture() returns false, content hasn't
1310 // changed. This complements qsgDynamicTextures and
1311 // QQuick3DViewport::updateDynamicTextures().
1312 m_textureUpdateConnection = connect(sourcePrivate->window, &QQuickWindow::afterSynchronizing,
1313 this, [this, sourceItem]()
1314 {
1315 // Called on the render thread with gui blocked (if there is a render thread, that is).
1316 if (!m_layer)
1317 return;
1318 if (m_sourceItem != sourceItem) {
1319 disconnect(m_textureUpdateConnection);
1320 return;
1321 }
1322 if (m_layer->updateTexture())
1323 update();
1325
1326 m_layer = layer;
1327 m_layer->setItem(QQuickItemPrivate::get(m_sourceItem)->itemNode());
1328
1329 QRectF sourceRect = QRectF(0, 0, m_sourceItem->width(), m_sourceItem->height());
1330 if (qFuzzyIsNull(sourceRect.width()))
1331 sourceRect.setWidth(256);
1332 if (qFuzzyIsNull(sourceRect.height()))
1333 sourceRect.setHeight(256);
1334 m_layer->setRect(sourceRect);
1335
1336 QSize textureSize(qCeil(qAbs(sourceRect.width())), qCeil(qAbs(sourceRect.height())));
1337 const QSize minTextureSize = sourcePrivate->sceneGraphContext()->minimumFBOSize();
1338 while (textureSize.width() < minTextureSize.width())
1339 textureSize.rwidth() *= 2;
1340 while (textureSize.height() < minTextureSize.height())
1341 textureSize.rheight() *= 2;
1342 m_layer->setSize(textureSize);
1343
1344 // now that the layer has an item and a size, it can render into the texture
1345 m_layer->updateTexture();
1346
1347 imageNode->m_qsgTexture = m_layer;
1348 imageNode->m_flags.setFlag(QSSGRenderImage::Flag::Dirty);
1350 }
1351 }
1352 } else {
1353 if (m_layer) {
1354 m_layer->setItem(nullptr);
1355 delete m_layer;
1356 m_layer = nullptr;
1357 }
1358 imageNode->m_qsgTexture = nullptr;
1359 }
1360 nodeChanged = true;
1361 }
1362
1363 if (m_dirtyFlags.testFlag(DirtyFlag::FlipVDirty)) {
1364 m_dirtyFlags.setFlag(DirtyFlag::FlipVDirty, false);
1365 imageNode->m_flipV = effectiveFlipV(*imageNode);
1367 }
1368
1369 if (nodeChanged)
1370 imageNode->m_flags.setFlag(QSSGRenderImage::Flag::Dirty);
1371
1372 return imageNode;
1373}
1374
1375void QQuick3DTexture::itemChange(QQuick3DObject::ItemChange change, const QQuick3DObject::ItemChangeData &value)
1376{
1378 if (change == QQuick3DObject::ItemChange::ItemSceneChange) {
1379 // Source item
1380 if (m_sourceItem) {
1381 disconnect(m_sceneManagerWindowChangeConnection);
1382
1383 if (m_sceneManagerForLayer) {
1384 m_sceneManagerForLayer->qsgDynamicTextures.removeOne(m_layer);
1385 m_sceneManagerForLayer = nullptr;
1386 }
1387 trySetSourceParent();
1388 const auto &sceneManager = value.sceneManager;
1389 Q_ASSERT(QQuick3DObjectPrivate::get(this)->sceneManager == sceneManager);
1390 if (m_layer) {
1391 if (sceneManager)
1392 sceneManager->qsgDynamicTextures << m_layer;
1393 m_sceneManagerForLayer = sceneManager;
1394 }
1395
1396 // If m_sourceItem was an inline declared item (very common, e.g.
1397 // Texture { sourceItem: Rectangle { ... } } then it is highly
1398 // likely it won't be associated with a window (Qt Quick scene)
1399 // yet. Associate with one as soon as possible, do not leave it to
1400 // updateSpatialNode, because that, while safe, would defer
1401 // rendering into the texture to a future frame (adding a 2 frame
1402 // lag for the first rendering of the mesh textured with the 2D
1403 // item content), since a refWindow needs to be followed by a
1404 // scenegraph sync round to get QSGNodes created (updatePaintNode),
1405 // whereas updateSpatialNode is in the middle of a sync round, so
1406 // would need to wait for another one, etc.
1407 if (sceneManager && m_sourceItem && !m_sourceItem->window()) {
1408 if (sceneManager->window()) {
1409 QQuickItemPrivate::get(m_sourceItem)->refWindow(sceneManager->window());
1410 } else {
1411 m_sceneManagerWindowChangeConnection = connect(sceneManager, &QQuick3DSceneManager::windowChanged, this,
1412 [this, sceneManager]
1413 {
1414 if (m_sourceItem && !m_sourceItem->window() && sceneManager->window())
1415 QQuickItemPrivate::get(m_sourceItem)->refWindow(sceneManager->window());
1416 });
1417 }
1418 }
1419 }
1420 // TextureData
1421 if (m_textureData) {
1422 const auto &sceneManager = value.sceneManager;
1423 if (sceneManager)
1424 QQuick3DObjectPrivate::refSceneManager(m_textureData, *sceneManager);
1425 else
1427 }
1428 }
1429}
1430
1432{
1433 Q_ASSERT(item == m_sourceItem);
1434 Q_UNUSED(item);
1435 Q_UNUSED(geometry);
1436 if (change.sizeChange()) {
1437 m_dirtyFlags.setFlag(DirtyFlag::SourceItemDirty);
1438 update();
1439 }
1440}
1441
1442void QQuick3DTexture::sourceItemDestroyed(QObject *item)
1443{
1444 Q_ASSERT(item == m_sourceItem);
1445 Q_UNUSED(item);
1446
1447 m_sourceItem = nullptr;
1448
1449 m_dirtyFlags.setFlag(DirtyFlag::SourceDirty);
1450 m_dirtyFlags.setFlag(DirtyFlag::SourceItemDirty);
1451 m_dirtyFlags.setFlag(DirtyFlag::TextureDataDirty);
1453 update();
1454}
1455
1456void QQuick3DTexture::markDirty(QQuick3DTexture::DirtyFlag type)
1457{
1458 if (!m_dirtyFlags.testFlag(type)) {
1459 m_dirtyFlags.setFlag(type, true);
1460 update();
1461 }
1462}
1463
1465{
1467 return static_cast<QSSGRenderImage *>(p->spatialNode);
1468}
1469
1471{
1472 m_dirtyFlags = DirtyFlags(0xFFFF);
1474}
1475
1476void QQuick3DTexture::setTextureProvider(QQuick3DRenderExtension *textureProvider)
1477{
1478 if (m_renderExtension == textureProvider)
1479 return;
1480
1481 QQuick3DObjectPrivate::attachWatcher(this, &QQuick3DTexture::setTextureProvider, textureProvider, m_renderExtension);
1482
1483 m_renderExtension = textureProvider;
1484
1485 m_dirtyFlags.setFlag(DirtyFlag::SourceDirty);
1486 m_dirtyFlags.setFlag(DirtyFlag::SourceItemDirty);
1487 m_dirtyFlags.setFlag(DirtyFlag::TextureDataDirty);
1488 m_dirtyFlags.setFlag(DirtyFlag::ExtensionDirty);
1489
1490 emit textureProviderChanged();
1491 update();
1492}
1493
\inmodule QtCore
bool invoke(QObject *object, Qt::ConnectionType connectionType, QGenericReturnArgument returnValue, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument()) const
\obsolete [6.5] Please use the variadic overload of this function
bool isValid() const
\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
QThread * thread() const
Returns the thread in which the object lives.
Definition qobject.cpp:1598
void destroyed(QObject *=nullptr)
This signal is emitted immediately before the object obj is destroyed, after any instances of QPointe...
void deleteLater()
\threadsafe
Definition qobject.cpp:2435
The QQmlContext class defines a context within a QML engine.
Definition qqmlcontext.h:25
static QString urlToLocalFileOrQrc(const QString &)
If url is a local file returns a path suitable for passing to \l{QFile}.
Definition qqmlfile.cpp:742
void refSceneManager(QQuick3DSceneManager &)
static QQuick3DObjectPrivate * get(QQuick3DObject *item)
static void attachWatcher(Context *context, Setter setter, Object3D *newO, Object3D *oldO)
Attach a object-destroyed-watcher to an object that's not owned.
\qmltype Object3D \inqmlmodule QtQuick3D \instantiates QQuick3DObject \inherits QtObject
virtual QSSGRenderGraphObject * updateSpatialNode(QSSGRenderGraphObject *node)
virtual void itemChange(ItemChange, const ItemChangeData &)
virtual void markAllDirty()
QVector< QSGDynamicTexture * > qsgDynamicTextures
\qmltype TextureData \inherits Object3D \inqmlmodule QtQuick3D \instantiates QQuick3DTextureData
void setPivotV(float pivotV)
void setScaleV(float scaleV)
void setHorizontalTiling(QQuick3DTexture::TilingMode tilingModeHorizontal)
void setAutoOrientation(bool autoOrientation)
void minFilterChanged()
TilingMode tilingModeVertical
void setPivotU(float pivotU)
void sourceItemChanged()
Q_REVISION(6, 7) void setDepthTiling(QQuick3DTexture void setRotationUV(float rotationUV)
void autoOrientationChanged()
void setTextureData(QQuick3DTextureData *textureData)
void setMipFilter(QQuick3DTexture::Filter mipFilter)
MappingMode mappingMode
TilingMode tilingModeHorizontal
void positionUChanged()
void setSourceItem(QQuickItem *sourceItem)
void itemChange(ItemChange change, const ItemChangeData &value) override
void horizontalTilingChanged()
void setVerticalTiling(QQuick3DTexture::TilingMode tilingModeVertical)
void verticalTilingChanged()
void magFilterChanged()
void markAllDirty() override
void setGenerateMipmaps(bool generateMipmaps)
void scaleUChanged()
QQuick3DTextureData * textureData
void pivotVChanged()
void generateMipmapsChanged()
void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &geometry) override
void mipFilterChanged()
void setIndexUV(int indexUV)
void rotationUVChanged()
void setPositionU(float positionU)
QQuick3DRenderExtension * textureProvider
\qmlproperty RenderExtension QtQuick3D::Texture::textureProvider
void mappingModeChanged()
TilingMode tilingModeDepth
void setPositionV(float positionV)
~QQuick3DTexture() override
void setSource(const QUrl &source)
void setFlipV(bool flipV)
void indexUVChanged()
void scaleVChanged()
QQuick3DTexture(QQuick3DObject *parent=nullptr)
\qmltype Texture \inherits Object3D \inqmlmodule QtQuick3D
QSSGRenderGraphObject * updateSpatialNode(QSSGRenderGraphObject *node) override
void setMagFilter(QQuick3DTexture::Filter magFilter)
void pivotUChanged()
QQuickItem * sourceItem
void setMappingMode(QQuick3DTexture::MappingMode mappingMode)
QSSGRenderImage * getRenderImage()
void setMinFilter(QQuick3DTexture::Filter minFilter)
TilingMode verticalTiling() const
\qmlproperty enumeration QtQuick3D::Texture::tilingModeVertical
void setFlipU(bool flipU)
void setScaleU(float scaleU)
void positionVChanged()
void textureDataChanged()
void sourceChanged()
TilingMode horizontalTiling() const
\qmlproperty enumeration QtQuick3D::Texture::tilingModeHorizontal
static QQuickItemPrivate * get(QQuickItem *item)
The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
Definition qquickitem.h:63
void setParentItem(QQuickItem *parent)
bool isVisible() const
virtual QSGTextureProvider * textureProvider() const
Returns the texture provider for an item.
QSizeF size() const
QQuickWindow * window() const
Returns the window in which this item is rendered.
qreal width
This property holds the width of this item.
Definition qquickitem.h:75
QQuickItem * parentItem() const
qreal height
This property holds the height of this item.
Definition qquickitem.h:76
\qmltype Window \instantiates QQuickWindow \inqmlmodule QtQuick
\inmodule QtCore\reentrant
Definition qrect.h:484
constexpr qreal height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:732
constexpr qreal width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:729
constexpr void setWidth(qreal w) noexcept
Sets the width of the rectangle to the given finite width.
Definition qrect.h:818
constexpr void setHeight(qreal h) noexcept
Sets the height of the rectangle to the given finite height.
Definition qrect.h:821
virtual QSGLayer * createLayer(QSGRenderContext *renderContext)=0
The QSGDynamicTexture class serves as a baseclass for dynamically changing textures,...
Definition qsgtexture.h:100
virtual bool updateTexture()=0
Call this function to explicitly update the dynamic texture.
virtual void setItem(QSGNode *item)=0
QSGContext * sceneGraphContext() const
The QSGTextureProvider class encapsulates texture based entities in QML.
void textureChanged()
This signal is emitted when the texture changes.
\inmodule QtQuick3D
QString path() const
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:133
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:130
constexpr int & rheight() noexcept
Returns a reference to the height.
Definition qsize.h:157
constexpr int & rwidth() noexcept
Returns a reference to the width.
Definition qsize.h:154
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
static QThread * currentThread()
Definition qthread.cpp:1039
\inmodule QtCore
Definition qurl.h:94
bool isRelative() const
Returns true if the URL is relative; otherwise returns false.
Definition qurl.cpp:2800
bool isEmpty() const
Returns true if the URL has no data; otherwise returns false.
Definition qurl.cpp:1896
QString path(ComponentFormattingOptions options=FullyDecoded) const
Returns the path of the URL.
Definition qurl.cpp:2468
The QVector2D class represents a vector or vertex in 2D space.
Definition qvectornd.h:31
auto mo
[7]
Combined button and popup list for selecting options.
@ AutoConnection
@ DirectConnection
static void * context
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
EGLOutputLayerEXT layer
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:333
bool qFuzzyIsNull(qfloat16 f) noexcept
Definition qfloat16.h:349
#define qWarning
Definition qlogging.h:166
int qCeil(T v)
Definition qmath.h:36
constexpr T qAbs(const T &t)
Definition qnumeric.h:328
#define SLOT(a)
Definition qobjectdefs.h:52
#define SIGNAL(a)
Definition qobjectdefs.h:53
GLenum type
GLsizei const GLuint * paths
GLint first
GLsizei GLsizei GLchar * source
GLdouble s
[6]
Definition qopenglext.h:235
GLdouble GLdouble t
Definition qopenglext.h:243
GLsizei const GLchar *const * path
GLfloat GLfloat p
[1]
QQmlContext * qmlContext(const QObject *obj)
Definition qqml.cpp:75
static QSSGRenderPath resolveImagePath(const QUrl &url, const QQmlContext *context)
QT_BEGIN_NAMESPACE bool qUpdateIfNeeded(T &orig, T updated)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
static const QSize minTextureSize
#define QSSG_GUARD(cond)
QSSGRenderTextureCoordOp
QSSGRenderTextureFilterOp
static FileType fileType(const QFileInfo &fi)
#define emit
#define Q_UNUSED(x)
if(qFloatDistance(a, b)<(1<< 7))
[0]
QUrl url("example.com")
[constructor-url-reference]
obj metaObject() -> className()
myObject disconnect()
[26]
QGraphicsItem * item
edit hide()
aWidget window() -> setWindowTitle("New Window Title")
[2]
QNetworkAccessManager manager
\inmodule QtCore
static QSharedPointer< QIODevice > getStreamForTextureFile(const QString &inPath, bool inQuiet=false, QString *outPath=nullptr, FileType *outFileType=nullptr)
QSSGRenderTextureFilterOp m_mipFilterType
MappingModes m_mappingMode
QSSGRenderPath m_imagePath
QSSGRenderTextureCoordOp m_depthTilingMode
QSSGRenderTextureCoordOp m_horizontalTilingMode
QSSGRenderTextureFilterOp m_minFilterType
QSSGRenderExtension * m_extensionsSource
QSSGRenderTextureCoordOp m_verticalTilingMode
QSSGRenderTextureData * m_rawTextureData
QSSGRenderTextureFilterOp m_magFilterType
QSGTexture * m_qsgTexture
Definition moc.h:23