diff options
author | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2020-02-26 01:00:25 +0100 |
---|---|---|
committer | Edward Welbourne <edward.welbourne@qt.io> | 2020-02-26 18:39:21 +0100 |
commit | 75c0ffaf6d2b92cdf26092e01acdd5af4afeac97 (patch) | |
tree | bb9e85c21248790ec99b3665928872e39b14db64 /src/gui | |
parent | 4753d69d8934258de7fb64550e50a5cbb9b5603f (diff) | |
parent | 462c2745a5168a5b57381d05779b5d16aebe018e (diff) | |
download | qtbase-75c0ffaf6d2b92cdf26092e01acdd5af4afeac97.tar.gz |
Merge remote-tracking branch 'origin/5.15' into dev
Conflicts:
examples/network/bearermonitor/CMakeLists.txt
examples/network/CMakeLists.txt
src/corelib/tools/qlinkedlist.h
src/sql/kernel/qsqldriver_p.h
src/sql/kernel/qsqlresult_p.h
src/widgets/kernel/qwidget.cpp
src/widgets/kernel/qwidget_p.h
tests/auto/network/socket/platformsocketengine/tst_platformsocketengine.cpp
tests/auto/network/socket/qtcpsocket/tst_qtcpsocket.cpp
tests/auto/tools/moc/allmocs_baseline_in.json
Change-Id: I21a3c34570ae79ea9d30107fae71759d7eac17d9
Diffstat (limited to 'src/gui')
-rw-r--r-- | src/gui/.prev_CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/gui/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/gui/image/qimage.h | 3 | ||||
-rw-r--r-- | src/gui/kernel/qevent.cpp | 12 | ||||
-rw-r--r-- | src/gui/kernel/qevent.h | 11 | ||||
-rw-r--r-- | src/gui/kernel/qguiapplication.cpp | 13 | ||||
-rw-r--r-- | src/gui/painting/qcolor.cpp | 1 | ||||
-rw-r--r-- | src/gui/painting/qicc.cpp | 116 | ||||
-rw-r--r-- | src/gui/rhi/qrhi.cpp | 132 | ||||
-rw-r--r-- | src/gui/rhi/qrhi_p.h | 13 | ||||
-rw-r--r-- | src/gui/rhi/qrhid3d11.cpp | 15 | ||||
-rw-r--r-- | src/gui/rhi/qrhid3d11_p_p.h | 1 | ||||
-rw-r--r-- | src/gui/rhi/qrhigles2.cpp | 18 | ||||
-rw-r--r-- | src/gui/rhi/qrhigles2_p_p.h | 5 | ||||
-rw-r--r-- | src/gui/rhi/qrhimetal.mm | 17 | ||||
-rw-r--r-- | src/gui/rhi/qrhimetal_p_p.h | 1 | ||||
-rw-r--r-- | src/gui/rhi/qrhinull.cpp | 4 | ||||
-rw-r--r-- | src/gui/rhi/qrhivulkan.cpp | 17 | ||||
-rw-r--r-- | src/gui/rhi/qrhivulkan_p_p.h | 1 | ||||
-rw-r--r-- | src/gui/text/qtextdocument.cpp | 3 | ||||
-rw-r--r-- | src/gui/text/text.pri | 2 | ||||
-rw-r--r-- | src/gui/util/qshadergenerator.cpp | 106 | ||||
-rw-r--r-- | src/gui/util/qshadergraph.cpp | 64 |
23 files changed, 463 insertions, 96 deletions
diff --git a/src/gui/.prev_CMakeLists.txt b/src/gui/.prev_CMakeLists.txt index 953dc8b2da..36e53a0edc 100644 --- a/src/gui/.prev_CMakeLists.txt +++ b/src/gui/.prev_CMakeLists.txt @@ -494,7 +494,7 @@ qt_extend_target(Gui CONDITION QT_FEATURE_textmarkdownreader ) qt_extend_target(Gui CONDITION QT_FEATURE_system_textmarkdownreader AND QT_FEATURE_textmarkdownreader - PUBLIC_LIBRARIES + LIBRARIES libmd4c ) diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 14859069a5..eb3b07fdae 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -599,7 +599,7 @@ qt_extend_target(Gui CONDITION QT_FEATURE_textmarkdownreader ) qt_extend_target(Gui CONDITION QT_FEATURE_system_textmarkdownreader AND QT_FEATURE_textmarkdownreader - PUBLIC_LIBRARIES + LIBRARIES libmd4c ) diff --git a/src/gui/image/qimage.h b/src/gui/image/qimage.h index a9c0fbbe3e..5a1524b419 100644 --- a/src/gui/image/qimage.h +++ b/src/gui/image/qimage.h @@ -231,7 +231,8 @@ public: bool hasAlphaChannel() const; void setAlphaChannel(const QImage &alphaChannel); #if QT_DEPRECATED_SINCE(5, 15) - QT_DEPRECATED QImage alphaChannel() const; + QT_DEPRECATED_X("Use convertToFormat(QImage::Format_Alpha8)") + QImage alphaChannel() const; #endif QImage createAlphaMask(Qt::ImageConversionFlags flags = Qt::AutoColor) const; #ifndef QT_NO_IMAGE_HEURISTIC_MASK diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index c2dac71e0d..5ad37ae640 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -2368,6 +2368,7 @@ QTabletEvent::QTabletEvent(Type type, const QPointF &pos, const QPointF &globalP { } +#if QT_DEPRECATED_SINCE(5, 15) /*! Construct a tablet event of the given \a type. @@ -2411,6 +2412,7 @@ QTabletEvent::QTabletEvent(Type type, const QPointF &pos, const QPointF &globalP tangentialPressure, rotation, z, keyState, uniqueID, Qt::NoButton, Qt::NoButton) { } +#endif /*! \internal @@ -2451,6 +2453,12 @@ Qt::MouseButtons QTabletEvent::buttons() const /*! \fn TabletDevices QTabletEvent::device() const + \deprecated Use deviceType(). +*/ + +/*! + \fn TabletDevices QTabletEvent::deviceType() const + Returns the type of device that generated the event. \sa TabletDevice @@ -2615,12 +2623,16 @@ Qt::MouseButtons QTabletEvent::buttons() const \fn qreal &QTabletEvent::hiResGlobalX() const The high precision x position of the tablet device. + + \obsolete use globalPosF() */ /*! \fn qreal &QTabletEvent::hiResGlobalY() const The high precision y position of the tablet device. + + \obsolete use globalPosF() */ /*! diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h index 6f3215652b..87ec8e7bee 100644 --- a/src/gui/kernel/qevent.h +++ b/src/gui/kernel/qevent.h @@ -280,10 +280,15 @@ public: Q_ENUM(TabletDevice) enum PointerType { UnknownPointer, Pen, Cursor, Eraser }; Q_ENUM(PointerType) + +#if QT_DEPRECATED_SINCE(5, 15) + // Actually deprecated since 5.4, in docs + QT_DEPRECATED_VERSION_X_5_15("Use the other QTabletEvent constructor") QTabletEvent(Type t, const QPointF &pos, const QPointF &globalPos, int device, int pointerType, qreal pressure, int xTilt, int yTilt, qreal tangentialPressure, qreal rotation, int z, Qt::KeyboardModifiers keyState, qint64 uniqueID); // ### remove in Qt 6 +#endif QTabletEvent(Type t, const QPointF &pos, const QPointF &globalPos, int device, int pointerType, qreal pressure, int xTilt, int yTilt, qreal tangentialPressure, qreal rotation, int z, @@ -304,9 +309,15 @@ public: inline int y() const { return qRound(mPos.y()); } inline int globalX() const { return qRound(mGPos.x()); } inline int globalY() const { return qRound(mGPos.y()); } +#if QT_DEPRECATED_SINCE(5, 15) + QT_DEPRECATED_VERSION_X_5_15("use globalPosF().x()") inline qreal hiResGlobalX() const { return mGPos.x(); } + QT_DEPRECATED_VERSION_X_5_15("use globalPosF().y()") inline qreal hiResGlobalY() const { return mGPos.y(); } + QT_DEPRECATED_VERSION_X_5_15("Use deviceType()") inline TabletDevice device() const { return TabletDevice(mDev); } +#endif + inline TabletDevice deviceType() const { return TabletDevice(mDev); } inline PointerType pointerType() const { return PointerType(mPointerType); } inline qint64 uniqueId() const { return mUnique; } inline qreal pressure() const { return mPress; } diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index 1f8d8d21e9..5a9274e4f3 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -610,7 +610,7 @@ static QWindowGeometrySpecification windowGeometrySpecification = Q_WINDOW_GEOME \li \c {dialogs=[xp|none]}, \c xp uses XP-style native dialogs and \c none disables them. - \li \c {dpiawareness=[0|1|2} Sets the DPI awareness of the process + \li \c {dpiawareness=[0|1|2]} Sets the DPI awareness of the process (see \l{High DPI Displays}, since Qt 5.4). \li \c {fontengine=freetype}, uses the FreeType font engine. \li \c {fontengine=directwrite}, uses the experimental DirectWrite @@ -1738,6 +1738,8 @@ QGuiApplicationPrivate::~QGuiApplicationPrivate() window_list.clear(); screen_list.clear(); + + self = nullptr; } #if 0 @@ -1891,6 +1893,10 @@ bool QGuiApplication::event(QEvent *e) { if(e->type() == QEvent::LanguageChange) { setLayoutDirection(qt_detectRTLLanguage()?Qt::RightToLeft:Qt::LeftToRight); + for (auto *topLevelWindow : QGuiApplication::topLevelWindows()) { + if (topLevelWindow->flags() != Qt::Desktop) + postEvent(topLevelWindow, new QEvent(QEvent::LanguageChange)); + } } else if (e->type() == QEvent::Quit) { // Close open windows. This is done in order to deliver de-expose // events while the event loop is still running. @@ -3412,8 +3418,11 @@ void QGuiApplicationPrivate::applyWindowGeometrySpecificationTo(QWindow *window) */ QFont QGuiApplication::font() { - Q_ASSERT_X(QGuiApplicationPrivate::self, "QGuiApplication::font()", "no QGuiApplication instance"); const auto locker = qt_scoped_lock(applicationFontMutex); + if (!QGuiApplicationPrivate::self && !QGuiApplicationPrivate::app_font) { + qWarning("QGuiApplication::font(): no QGuiApplication instance and no application font set."); + return QFont(); // in effect: QFont((QFontPrivate*)nullptr), so no recursion + } initFontUnlocked(); return *QGuiApplicationPrivate::app_font; } diff --git a/src/gui/painting/qcolor.cpp b/src/gui/painting/qcolor.cpp index e544fce70e..eac2a50c8c 100644 --- a/src/gui/painting/qcolor.cpp +++ b/src/gui/painting/qcolor.cpp @@ -3224,6 +3224,7 @@ const uint qt_inv_premul_factor[256] = { /*! \namespace QColorConstants \inmodule QtGui + \since 5.14 \brief The QColorConstants namespace contains QColor predefined constants. diff --git a/src/gui/painting/qicc.cpp b/src/gui/painting/qicc.cpp index 18f212f8e9..2b5cd58fb1 100644 --- a/src/gui/painting/qicc.cpp +++ b/src/gui/painting/qicc.cpp @@ -87,6 +87,11 @@ constexpr quint32 IccTag(uchar a, uchar b, uchar c, uchar d) return (a << 24) | (b << 16) | (c << 8) | d; } +enum class ColorSpaceType : quint32 { + Rgb = IccTag('R', 'G', 'B', ' '), + Gray = IccTag('G', 'R', 'A', 'Y'), +}; + enum class ProfileClass : quint32 { Input = IccTag('s', 'c', 'r', 'n'), Display = IccTag('m', 'n', 't', 'r'), @@ -105,6 +110,7 @@ enum class Tag : quint32 { rTRC = IccTag('r', 'T', 'R', 'C'), gTRC = IccTag('g', 'T', 'R', 'C'), bTRC = IccTag('b', 'T', 'R', 'C'), + kTRC = IccTag('k', 'T', 'R', 'C'), A2B0 = IccTag('A', '2', 'B', '0'), A2B1 = IccTag('A', '2', 'B', '1'), B2A0 = IccTag('B', '2', 'A', '0'), @@ -219,8 +225,10 @@ static bool isValidIccProfile(const ICCProfileHeader &header) } // Don't overflow 32bit integers: - if (header.tagCount >= INT32_MAX / sizeof(TagTableEntry)) + if (header.tagCount >= INT32_MAX / sizeof(TagTableEntry)) { + qCWarning(lcIcc, "Failed tag count sanity"); return false; + } if (header.profileSize - sizeof(ICCProfileHeader) < header.tagCount * sizeof(TagTableEntry)) { qCWarning(lcIcc, "Failed basic size sanity"); return false; @@ -231,7 +239,8 @@ static bool isValidIccProfile(const ICCProfileHeader &header) qCWarning(lcIcc, "Unsupported ICC profile class %x", quint32(header.profileClass)); return false; } - if (header.inputColorSpace != 0x52474220 /* 'RGB '*/) { + if (header.inputColorSpace != uint(ColorSpaceType::Rgb) + && header.inputColorSpace != uint(ColorSpaceType::Gray)) { qCWarning(lcIcc, "Unsupported ICC input color space %x", quint32(header.inputColorSpace)); return false; } @@ -610,10 +619,8 @@ bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace) return false; } const ICCProfileHeader *header = (const ICCProfileHeader *)data.constData(); - if (!isValidIccProfile(*header)) { - qCWarning(lcIcc) << "fromIccProfile: failed general sanity check"; - return false; - } + if (!isValidIccProfile(*header)) + return false; // if failed we already printing a warning if (qsizetype(header->profileSize) > data.size()) { qCWarning(lcIcc) << "fromIccProfile: failed size sanity 2"; return false; @@ -658,39 +665,74 @@ bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace) } // Check the profile is three-component matrix based (what we currently support): - if (!tagIndex.contains(Tag::rXYZ) || !tagIndex.contains(Tag::gXYZ) || !tagIndex.contains(Tag::bXYZ) || - !tagIndex.contains(Tag::rTRC) || !tagIndex.contains(Tag::gTRC) || !tagIndex.contains(Tag::bTRC) || - !tagIndex.contains(Tag::wtpt)) { - qCWarning(lcIcc) << "fromIccProfile: Unsupported ICC profile - not three component matrix based"; - return false; + if (header->inputColorSpace == uint(ColorSpaceType::Rgb)) { + if (!tagIndex.contains(Tag::rXYZ) || !tagIndex.contains(Tag::gXYZ) || !tagIndex.contains(Tag::bXYZ) || + !tagIndex.contains(Tag::rTRC) || !tagIndex.contains(Tag::gTRC) || !tagIndex.contains(Tag::bTRC) || + !tagIndex.contains(Tag::wtpt)) { + qCWarning(lcIcc) << "fromIccProfile: Unsupported ICC profile - not three component matrix based"; + return false; + } + } else { + Q_ASSERT(header->inputColorSpace == uint(ColorSpaceType::Gray)); + if (!tagIndex.contains(Tag::kTRC) || !tagIndex.contains(Tag::wtpt)) { + qCWarning(lcIcc) << "fromIccProfile: Invalid ICC profile - not valid gray scale based"; + return false; + } } QColorSpacePrivate *colorspaceDPtr = QColorSpacePrivate::getWritable(*colorSpace); - // Parse XYZ tags - if (!parseXyzData(data, tagIndex[Tag::rXYZ], colorspaceDPtr->toXyz.r)) - return false; - if (!parseXyzData(data, tagIndex[Tag::gXYZ], colorspaceDPtr->toXyz.g)) - return false; - if (!parseXyzData(data, tagIndex[Tag::bXYZ], colorspaceDPtr->toXyz.b)) - return false; - if (!parseXyzData(data, tagIndex[Tag::wtpt], colorspaceDPtr->whitePoint)) - return false; + if (header->inputColorSpace == uint(ColorSpaceType::Rgb)) { + // Parse XYZ tags + if (!parseXyzData(data, tagIndex[Tag::rXYZ], colorspaceDPtr->toXyz.r)) + return false; + if (!parseXyzData(data, tagIndex[Tag::gXYZ], colorspaceDPtr->toXyz.g)) + return false; + if (!parseXyzData(data, tagIndex[Tag::bXYZ], colorspaceDPtr->toXyz.b)) + return false; + if (!parseXyzData(data, tagIndex[Tag::wtpt], colorspaceDPtr->whitePoint)) + return false; - colorspaceDPtr->primaries = QColorSpace::Primaries::Custom; - if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromSRgb()) { - qCDebug(lcIcc) << "fromIccProfile: sRGB primaries detected"; - colorspaceDPtr->primaries = QColorSpace::Primaries::SRgb; - } else if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromAdobeRgb()) { - qCDebug(lcIcc) << "fromIccProfile: Adobe RGB primaries detected"; - colorspaceDPtr->primaries = QColorSpace::Primaries::AdobeRgb; - } else if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromDciP3D65()) { - qCDebug(lcIcc) << "fromIccProfile: DCI-P3 D65 primaries detected"; - colorspaceDPtr->primaries = QColorSpace::Primaries::DciP3D65; - } - if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromProPhotoRgb()) { - qCDebug(lcIcc) << "fromIccProfile: ProPhoto RGB primaries detected"; - colorspaceDPtr->primaries = QColorSpace::Primaries::ProPhotoRgb; + colorspaceDPtr->primaries = QColorSpace::Primaries::Custom; + if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromSRgb()) { + qCDebug(lcIcc) << "fromIccProfile: sRGB primaries detected"; + colorspaceDPtr->primaries = QColorSpace::Primaries::SRgb; + } else if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromAdobeRgb()) { + qCDebug(lcIcc) << "fromIccProfile: Adobe RGB primaries detected"; + colorspaceDPtr->primaries = QColorSpace::Primaries::AdobeRgb; + } else if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromDciP3D65()) { + qCDebug(lcIcc) << "fromIccProfile: DCI-P3 D65 primaries detected"; + colorspaceDPtr->primaries = QColorSpace::Primaries::DciP3D65; + } + if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromProPhotoRgb()) { + qCDebug(lcIcc) << "fromIccProfile: ProPhoto RGB primaries detected"; + colorspaceDPtr->primaries = QColorSpace::Primaries::ProPhotoRgb; + } + } else { + // We will use sRGB primaries and fit to match the given white-point if + // it doesn't match sRGB's. + QColorVector whitePoint; + if (!parseXyzData(data, tagIndex[Tag::wtpt], whitePoint)) + return false; + if (!qFuzzyCompare(whitePoint.y, 1.0f) || (1.0f + whitePoint.z - whitePoint.x) == 0.0f) { + qCWarning(lcIcc) << "fromIccProfile: Invalid ICC profile - gray white-point not normalized"; + return false; + } + if (whitePoint == QColorVector::D65()) { + colorspaceDPtr->primaries = QColorSpace::Primaries::SRgb; + } else { + colorspaceDPtr->primaries = QColorSpace::Primaries::Custom; + // Calculate chromaticity from xyz (assuming y == 1.0f). + float y = 1.0f / (1.0f + whitePoint.z - whitePoint.x); + float x = whitePoint.x * y; + QColorSpacePrimaries primaries(QColorSpace::Primaries::SRgb); + primaries.whitePoint = QPointF(x,y); + if (!primaries.areValid()) { + qCWarning(lcIcc) << "fromIccProfile: Invalid ICC profile - invalid white-point"; + return false; + } + colorspaceDPtr->toXyz = primaries.toXyzMatrix(); + } } // Reset the matrix to our canonical values: if (colorspaceDPtr->primaries != QColorSpace::Primaries::Custom) @@ -700,7 +742,11 @@ bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace) TagEntry rTrc; TagEntry gTrc; TagEntry bTrc; - if (tagIndex.contains(Tag::aarg) && tagIndex.contains(Tag::aagg) && tagIndex.contains(Tag::aabg)) { + if (header->inputColorSpace == uint(ColorSpaceType::Gray)) { + rTrc = tagIndex[Tag::kTRC]; + gTrc = tagIndex[Tag::kTRC]; + bTrc = tagIndex[Tag::kTRC]; + } else if (tagIndex.contains(Tag::aarg) && tagIndex.contains(Tag::aagg) && tagIndex.contains(Tag::aabg)) { // Apple extension for parametric version of TRCs in ICCv2: rTrc = tagIndex[Tag::aarg]; gTrc = tagIndex[Tag::aagg]; diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp index f0bf19bd91..7a0d53e1e4 100644 --- a/src/gui/rhi/qrhi.cpp +++ b/src/gui/rhi/qrhi.cpp @@ -577,6 +577,11 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general") specifying a non-zero level in QRhiReadbackDescription leads to returning an all-zero image. In practice this feature will be unsupported with OpenGL ES 2.0, while it will likely be supported everywhere else. + + \value TexelFetch Indicates that texelFetch() is available in shaders. In + practice this will be reported as unsupported with OpenGL ES 2.0 and OpenGL + 2.x contexts, because GLSL 100 es and versions before 130 do not support + this function. */ /*! @@ -622,18 +627,27 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general") is what some OpenGL ES implementations provide. \value FramesInFlight The number of frames the backend may keep "in - flight". The value has no relevance, and is unspecified, with backends like - OpenGL and Direct3D 11. With backends like Vulkan or Metal, it is the - responsibility of QRhi to block whenever starting a new frame and finding - the CPU is already \c{N - 1} frames ahead of the GPU (because the command - buffer submitted in frame no. \c{current} - \c{N} has not yet completed). - The value N is what is returned from here, and is typically 2. This can be - relevant to applications that integrate rendering done directly with the - graphics API, as such rendering code may want to perform double (if the - value is 2) buffering for resources, such as, buffers, similarly to the - QRhi backends themselves. The current frame slot index (a value running 0, - 1, .., N-1, then wrapping around) is retrievable from - QRhi::currentFrameSlot(). + flight": with backends like Vulkan or Metal, it is the responsibility of + QRhi to block whenever starting a new frame and finding the CPU is already + \c{N - 1} frames ahead of the GPU (because the command buffer submitted in + frame no. \c{current} - \c{N} has not yet completed). The value N is what + is returned from here, and is typically 2. This can be relevant to + applications that integrate rendering done directly with the graphics API, + as such rendering code may want to perform double (if the value is 2) + buffering for resources, such as, buffers, similarly to the QRhi backends + themselves. The current frame slot index (a value running 0, 1, .., N-1, + then wrapping around) is retrievable from QRhi::currentFrameSlot(). The + value is 1 for backends where the graphics API offers no such low level + control over the command submission process. Note that pipelining may still + happen even when this value is 1 (some backends, such as D3D11, are + designed to attempt to enable this, for instance, by using an update + strategy for uniform buffers that does not stall the pipeline), but that is + then not controlled by QRhi and so not reflected here in the API. + + \value MaxAsyncReadbackFrames The number of \l{QRhi::endFrame()}{submitted} + frames (including the one that contains the readback) after which an + asynchronous texture or buffer readback is guaranteed to complete upon + \l{QRhi::beginFrame()}{starting a new frame}. */ /*! @@ -1946,6 +1960,40 @@ quint64 QRhiResource::globalResourceId() const */ /*! + \class QRhiBuffer::NativeBuffer + \brief Contains information about the underlying native resources of a buffer. + */ + +/*! + \variable QRhiBuffer::NativeBuffer::objects + \brief an array with pointers to the native object handles. + + With OpenGL, the native handle is a GLuint value, so the elements in the \c + objects array are pointers to a GLuint. With Vulkan, the native handle is a + VkBuffer, so the elements of the array are pointers to a VkBuffer. With + Direct3D 11 and Metal the elements are pointers to a ID3D11Buffer or + MTLBuffer pointer, respectively. + + \note Pay attention to the fact that the elements are always pointers to + the native buffer handle type, even if the native type itself is a pointer. + */ + +/*! + \variable QRhiBuffer::NativeBuffer::slotCount + \brief Specifies the number of valid elements in the objects array. + + The value can be 0, 1, 2, or 3 in practice. 0 indicates that the QRhiBuffer + is not backed by any native buffer objects. This can happen with + QRhiBuffers with the usage UniformBuffer when the underlying API does not + support (or the backend chooses not to use) native uniform buffers. 1 is + commonly used for Immutable and Static types (but some backends may + differ). 2 or 3 is typical when the type is Dynamic (but some backends may + differ). + + \sa QRhi::currentFrameSlot(), QRhi::FramesInFlight + */ + +/*! \internal */ QRhiBuffer::QRhiBuffer(QRhiImplementation *rhi, Type type_, UsageFlags usage_, int size_) @@ -1974,6 +2022,46 @@ QRhiResource::Type QRhiBuffer::resourceType() const */ /*! + \return the underlying native resources for this buffer. The returned value + will be empty if exposing the underlying native resources is not supported by + the backend. + + A QRhiBuffer may be backed by multiple native buffer objects, depending on + the type() and the QRhi backend in use. When this is the case, all of them + are returned in the objects array in the returned struct, with slotCount + specifying the number of native buffer objects. While + \l{QRhi::beginFrame()}{recording a frame}, QRhi::currentFrameSlot() can be + used to determine which of the native buffers QRhi is using for operations + that read or write from this QRhiBuffer within the frame being recorded. + + In some cases a QRhiBuffer will not be backed by a native buffer object at + all. In this case slotCount will be set to 0 and no valid native objects + are returned. This is not an error, and is perfectly valid when a given + backend does not use native buffers for QRhiBuffers with certain types or + usages. + + \note Be aware that QRhi backends may employ various buffer update + strategies. Unlike textures, where uploading image data always means + recording a buffer-to-image (or similar) copy command on the command + buffer, buffers, in particular Dynamic and UniformBuffer ones, can operate + in many different ways. For example, a QRhiBuffer with usage type + UniformBuffer may not even be backed by a native buffer object at all if + uniform buffers are not used or supported by a given backend and graphics + API. There are also differences to how data is written to the buffer and + the type of backing memory used, and, if host visible memory is involved, + when memory writes become available and visible. Therefore, in general it + is recommended to limit native buffer object access to vertex and index + buffers with types Static or Immutable, because these operate in a + relatively uniform manner with all backends. + + \sa QRhi::currentFrameSlot(), QRhi::FramesInFlight + */ +QRhiBuffer::NativeBuffer QRhiBuffer::nativeBuffer() +{ + return {}; +} + +/*! \class QRhiRenderBuffer \internal \inmodule QtGui @@ -4334,7 +4422,15 @@ void QRhiResourceUpdateBatch::uploadStaticBuffer(QRhiBuffer *buf, const void *da is supported only when the QRhi::ReadBackNonUniformBuffer feature is reported as supported. - \a readBackTexture(), QRhi::isFeatureSupported() + \note The asynchronous readback is guaranteed to have completed when one of + the following conditions is met: \l{QRhi::finish()}{finish()} has been + called; or, at least \c N frames have been \l{QRhi::endFrame()}{submitted}, + including the frame that issued the readback operation, and the + \l{QRhi::beginFrame()}{recording of a new frame} has been started, where \c + N is the \l{QRhi::resourceLimit()}{resource limit value} returned for + QRhi::MaxAsyncReadbackFrames. + + \sa readBackTexture(), QRhi::isFeatureSupported(), QRhi::resourceLimit() */ void QRhiResourceUpdateBatch::readBackBuffer(QRhiBuffer *buf, int offset, int size, QRhiBufferReadbackResult *result) { @@ -4425,6 +4521,16 @@ void QRhiResourceUpdateBatch::copyTexture(QRhiTexture *dst, QRhiTexture *src, co happens with a byte ordered format. A \l{QRhiTexture::RGBA8}{RGBA8} texture maps therefore to byte ordered QImage formats, such as, QImage::Format_RGBA8888. + + \note The asynchronous readback is guaranteed to have completed when one of + the following conditions is met: \l{QRhi::finish()}{finish()} has been + called; or, at least \c N frames have been \l{QRhi::endFrame()}{submitted}, + including the frame that issued the readback operation, and the + \l{QRhi::beginFrame()}{recording of a new frame} has been started, where \c + N is the \l{QRhi::resourceLimit()}{resource limit value} returned for + QRhi::MaxAsyncReadbackFrames. + + \sa readBackBuffer(), QRhi::resourceLimit() */ void QRhiResourceUpdateBatch::readBackTexture(const QRhiReadbackDescription &rb, QRhiReadbackResult *result) { diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h index d17112a241..8f53808d34 100644 --- a/src/gui/rhi/qrhi_p.h +++ b/src/gui/rhi/qrhi_p.h @@ -681,6 +681,11 @@ public: }; Q_DECLARE_FLAGS(UsageFlags, UsageFlag) + struct NativeBuffer { + const void *objects[3]; + int slotCount; + }; + QRhiResource::Type resourceType() const override; Type type() const { return m_type; } @@ -694,6 +699,8 @@ public: virtual bool build() = 0; + virtual NativeBuffer nativeBuffer(); + protected: QRhiBuffer(QRhiImplementation *rhi, Type type_, UsageFlags usage_, int size_); Type m_type; @@ -1430,7 +1437,8 @@ public: BaseInstance, TriangleFanTopology, ReadBackNonUniformBuffer, - ReadBackNonBaseMipLevel + ReadBackNonBaseMipLevel, + TexelFetch }; enum BeginFrameFlag { @@ -1447,7 +1455,8 @@ public: TextureSizeMin = 1, TextureSizeMax, MaxColorAttachments, - FramesInFlight + FramesInFlight, + MaxAsyncReadbackFrames }; ~QRhi(); diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index 75b90b6995..f7c7f4a9f2 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -464,6 +464,8 @@ bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const return true; case QRhi::ReadBackNonBaseMipLevel: return true; + case QRhi::TexelFetch: + return true; default: Q_UNREACHABLE(); return false; @@ -480,7 +482,13 @@ int QRhiD3D11::resourceLimit(QRhi::ResourceLimit limit) const case QRhi::MaxColorAttachments: return 8; case QRhi::FramesInFlight: - return 2; // dummy + // From our perspective. What D3D does internally is another question + // (there could be pipelining, helped f.ex. by our MAP_DISCARD based + // uniform buffer update strategy), but that's out of our hands and + // does not concern us here. + return 1; + case QRhi::MaxAsyncReadbackFrames: + return 1; default: Q_UNREACHABLE(); return 0; @@ -2378,6 +2386,11 @@ bool QD3D11Buffer::build() return true; } +QRhiBuffer::NativeBuffer QD3D11Buffer::nativeBuffer() +{ + return { { &buffer }, 1 }; +} + ID3D11UnorderedAccessView *QD3D11Buffer::unorderedAccessView() { if (uav) diff --git a/src/gui/rhi/qrhid3d11_p_p.h b/src/gui/rhi/qrhid3d11_p_p.h index c3a4021241..04751397f7 100644 --- a/src/gui/rhi/qrhid3d11_p_p.h +++ b/src/gui/rhi/qrhid3d11_p_p.h @@ -64,6 +64,7 @@ struct QD3D11Buffer : public QRhiBuffer ~QD3D11Buffer(); void release() override; bool build() override; + QRhiBuffer::NativeBuffer nativeBuffer() override; ID3D11UnorderedAccessView *unorderedAccessView(); diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp index 3b6c022399..4a442bc582 100644 --- a/src/gui/rhi/qrhigles2.cpp +++ b/src/gui/rhi/qrhigles2.cpp @@ -512,6 +512,8 @@ bool QRhiGles2::create(QRhi::Flags flags) else caps.nonBaseLevelFramebufferTexture = true; + caps.texelFetch = caps.ctxMajor >= 3; // 3.0 or ES 3.0 + if (!caps.gles) { f->glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); f->glEnable(GL_POINT_SPRITE); @@ -765,6 +767,8 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const return !caps.gles || caps.properMapBuffer; case QRhi::ReadBackNonBaseMipLevel: return caps.nonBaseLevelFramebufferTexture; + case QRhi::TexelFetch: + return caps.texelFetch; default: Q_UNREACHABLE(); return false; @@ -781,7 +785,11 @@ int QRhiGles2::resourceLimit(QRhi::ResourceLimit limit) const case QRhi::MaxColorAttachments: return caps.maxDrawBuffers; case QRhi::FramesInFlight: - return 2; // dummy + // From our perspective. What the GL impl does internally is another + // question, but that's out of our hands and does not concern us here. + return 1; + case QRhi::MaxAsyncReadbackFrames: + return 1; default: Q_UNREACHABLE(); return 0; @@ -3293,6 +3301,14 @@ bool QGles2Buffer::build() return true; } +QRhiBuffer::NativeBuffer QGles2Buffer::nativeBuffer() +{ + if (m_usage.testFlag(QRhiBuffer::UniformBuffer)) + return { {}, 0 }; + + return { { &buffer }, 1 }; +} + QGles2RenderBuffer::QGles2RenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize, int sampleCount, QRhiRenderBuffer::Flags flags) : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags) diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h index 00caf40118..8b7d01532a 100644 --- a/src/gui/rhi/qrhigles2_p_p.h +++ b/src/gui/rhi/qrhigles2_p_p.h @@ -64,6 +64,7 @@ struct QGles2Buffer : public QRhiBuffer ~QGles2Buffer(); void release() override; bool build() override; + QRhiBuffer::NativeBuffer nativeBuffer() override; GLuint buffer = 0; GLenum targetForDataOps; @@ -778,7 +779,8 @@ public: compute(false), textureCompareMode(false), properMapBuffer(false), - nonBaseLevelFramebufferTexture(false) + nonBaseLevelFramebufferTexture(false), + texelFetch(false) { } int ctxMajor; int ctxMinor; @@ -811,6 +813,7 @@ public: uint textureCompareMode : 1; uint properMapBuffer : 1; uint nonBaseLevelFramebufferTexture : 1; + uint texelFetch : 1; } caps; QGles2SwapChain *currentSwapChain = nullptr; QVector<GLint> supportedCompressedFormats; diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm index 9e8f1ac096..48a562ef1d 100644 --- a/src/gui/rhi/qrhimetal.mm +++ b/src/gui/rhi/qrhimetal.mm @@ -562,6 +562,8 @@ bool QRhiMetal::isFeatureSupported(QRhi::Feature feature) const return true; case QRhi::ReadBackNonBaseMipLevel: return true; + case QRhi::TexelFetch: + return true; default: Q_UNREACHABLE(); return false; @@ -579,6 +581,8 @@ int QRhiMetal::resourceLimit(QRhi::ResourceLimit limit) const return 8; case QRhi::FramesInFlight: return QMTL_FRAMES_IN_FLIGHT; + case QRhi::MaxAsyncReadbackFrames: + return QMTL_FRAMES_IN_FLIGHT; default: Q_UNREACHABLE(); return 0; @@ -2196,6 +2200,19 @@ bool QMetalBuffer::build() return true; } +QRhiBuffer::NativeBuffer QMetalBuffer::nativeBuffer() +{ + if (d->slotted) { + NativeBuffer b; + Q_ASSERT(sizeof(b.objects) / sizeof(b.objects[0]) >= size_t(QMTL_FRAMES_IN_FLIGHT)); + for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) + b.objects[i] = &d->buf[i]; + b.slotCount = QMTL_FRAMES_IN_FLIGHT; + return b; + } + return { { &d->buf[0] }, 1 }; +} + QMetalRenderBuffer::QMetalRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize, int sampleCount, QRhiRenderBuffer::Flags flags) : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags), diff --git a/src/gui/rhi/qrhimetal_p_p.h b/src/gui/rhi/qrhimetal_p_p.h index 58e93e2cdb..212b731b71 100644 --- a/src/gui/rhi/qrhimetal_p_p.h +++ b/src/gui/rhi/qrhimetal_p_p.h @@ -65,6 +65,7 @@ struct QMetalBuffer : public QRhiBuffer ~QMetalBuffer(); void release() override; bool build() override; + QRhiBuffer::NativeBuffer nativeBuffer() override; QMetalBufferData *d; uint generation = 0; diff --git a/src/gui/rhi/qrhinull.cpp b/src/gui/rhi/qrhinull.cpp index 4c59900aa6..8c07e09b32 100644 --- a/src/gui/rhi/qrhinull.cpp +++ b/src/gui/rhi/qrhinull.cpp @@ -146,7 +146,9 @@ int QRhiNull::resourceLimit(QRhi::ResourceLimit limit) const case QRhi::MaxColorAttachments: return 8; case QRhi::FramesInFlight: - return 2; // dummy + return 1; + case QRhi::MaxAsyncReadbackFrames: + return 1; default: Q_UNREACHABLE(); return 0; diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp index 84ca835392..d378e2a4ad 100644 --- a/src/gui/rhi/qrhivulkan.cpp +++ b/src/gui/rhi/qrhivulkan.cpp @@ -3990,6 +3990,8 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const return true; case QRhi::ReadBackNonBaseMipLevel: return true; + case QRhi::TexelFetch: + return true; default: Q_UNREACHABLE(); return false; @@ -4007,6 +4009,8 @@ int QRhiVulkan::resourceLimit(QRhi::ResourceLimit limit) const return int(physDevProperties.limits.maxColorAttachments); case QRhi::FramesInFlight: return QVK_FRAMES_IN_FLIGHT; + case QRhi::MaxAsyncReadbackFrames: + return QVK_FRAMES_IN_FLIGHT; default: Q_UNREACHABLE(); return 0; @@ -5181,6 +5185,19 @@ bool QVkBuffer::build() return true; } +QRhiBuffer::NativeBuffer QVkBuffer::nativeBuffer() +{ + if (m_type == Dynamic) { + NativeBuffer b; + Q_ASSERT(sizeof(b.objects) / sizeof(b.objects[0]) >= size_t(QVK_FRAMES_IN_FLIGHT)); + for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) + b.objects[i] = &buffers[i]; + b.slotCount = QVK_FRAMES_IN_FLIGHT; + return b; + } + return { { &buffers[0] }, 1 }; +} + QVkRenderBuffer::QVkRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize, int sampleCount, Flags flags) : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags) diff --git a/src/gui/rhi/qrhivulkan_p_p.h b/src/gui/rhi/qrhivulkan_p_p.h index d42b83b882..6322882569 100644 --- a/src/gui/rhi/qrhivulkan_p_p.h +++ b/src/gui/rhi/qrhivulkan_p_p.h @@ -76,6 +76,7 @@ struct QVkBuffer : public QRhiBuffer ~QVkBuffer(); void release() override; bool build() override; + QRhiBuffer::NativeBuffer nativeBuffer() override; VkBuffer buffers[QVK_FRAMES_IN_FLIGHT]; QVkAlloc allocations[QVK_FRAMES_IN_FLIGHT]; diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp index 46cfc79643..64ba01d4e5 100644 --- a/src/gui/text/qtextdocument.cpp +++ b/src/gui/text/qtextdocument.cpp @@ -904,6 +904,9 @@ int QTextDocument::lineCount() const Returns the number of characters of this document. + \note As a QTextDocument always contains at least one + QChar::ParagraphSeparator, this method will return at least 1. + \sa blockCount(), characterAt() */ int QTextDocument::characterCount() const diff --git a/src/gui/text/text.pri b/src/gui/text/text.pri index 5e97b312f1..464ff3953b 100644 --- a/src/gui/text/text.pri +++ b/src/gui/text/text.pri @@ -99,7 +99,7 @@ qtConfig(textodfwriter) { qtConfig(textmarkdownreader) { qtConfig(system-textmarkdownreader) { - QMAKE_USE += libmd4c + QMAKE_USE_PRIVATE += libmd4c } else { include($$PWD/../../3rdparty/md4c.pri) } diff --git a/src/gui/util/qshadergenerator.cpp b/src/gui/util/qshadergenerator.cpp index 4beed8ed25..1ec25ccd7b 100644 --- a/src/gui/util/qshadergenerator.cpp +++ b/src/gui/util/qshadergenerator.cpp @@ -42,6 +42,8 @@ #include "qshaderlanguage_p.h" #include <QRegularExpression> +#include <cctype> + QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(ShaderGenerator, "ShaderGenerator", QtWarningMsg) @@ -344,10 +346,9 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) code << QByteArrayLiteral("void main()"); code << QByteArrayLiteral("{"); - const QRegularExpression localToGlobalRegExp(QStringLiteral("^.*\\s+(\\w+)\\s*=\\s*((?:\\w+\\(.*\\))|(?:\\w+)).*;$")); - const QRegularExpression temporaryVariableToAssignmentRegExp(QStringLiteral("^(.*\\s+(v\\d+))\\s*=\\s*(.*);$")); + const QRegularExpression temporaryVariableToAssignmentRegExp(QStringLiteral("([^;]*\\s+(v\\d+))\\s*=\\s*([^;]*);")); const QRegularExpression temporaryVariableInAssignmentRegExp(QStringLiteral("\\W*(v\\d+)\\W*")); - const QRegularExpression outputToTemporaryAssignmentRegExp(QStringLiteral("^\\s*(\\w+)\\s*=\\s*(.*);$")); + const QRegularExpression statementRegExp(QStringLiteral("\\s*(\\w+)\\s*=\\s*([^;]*);")); struct Variable; @@ -457,6 +458,13 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) QByteArray line = node.rule(format).substitution; const QVector<QShaderNodePort> ports = node.ports(); + struct VariableReplacement { + QByteArray placeholder; + QByteArray variable; + }; + + QVector<VariableReplacement> variableReplacements; + // Generate temporary variable names vN for (const QShaderNodePort &port : ports) { const QString portName = port.name; @@ -472,23 +480,65 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) if (variableIndex < 0) continue; - const auto placeholder = QByteArray(QByteArrayLiteral("$") + portName.toUtf8()); - const auto variable = QByteArray(QByteArrayLiteral("v") + QByteArray::number(variableIndex)); + VariableReplacement replacement; + replacement.placeholder = QByteArrayLiteral("$") + portName.toUtf8(); + replacement.variable = QByteArrayLiteral("v") + QByteArray::number(variableIndex); + + variableReplacements.append(std::move(replacement)); + } + + int begin = 0; + while ((begin = line.indexOf('$', begin)) != -1) { + int end = begin + 1; + char endChar = line.at(end); + const int size = line.size(); + while (end < size && (std::isalnum(endChar) || endChar == '_')) { + ++end; + endChar = line.at(end); + } + + const int placeholderLength = end - begin; + + const QByteArray variableName = line.mid(begin, placeholderLength); + const auto replacementIt = std::find_if(variableReplacements.cbegin(), variableReplacements.cend(), + [&variableName](const VariableReplacement &replacement) { + return variableName == replacement.placeholder; + }); - line.replace(placeholder, variable); + if (replacementIt != variableReplacements.cend()) { + line.replace(begin, placeholderLength, replacementIt->variable); + begin += replacementIt->variable.length(); + } else { + begin = end; + } } // Substitute variable names by generated vN variable names const QByteArray substitutionedLine = replaceParameters(line, node, format); - Variable *v = nullptr; + QRegularExpressionMatchIterator matches; switch (node.type()) { - // Record name of temporary variable that possibly references a global input - // We will replace the temporary variables by the matching global variables later - case QShaderNode::Input: { - const QRegularExpressionMatch match = localToGlobalRegExp.match(QString::fromUtf8(substitutionedLine)); - if (match.hasMatch()) { + case QShaderNode::Input: + case QShaderNode::Output: + matches = statementRegExp.globalMatch(QString::fromUtf8(substitutionedLine)); + break; + case QShaderNode::Function: + matches = temporaryVariableToAssignmentRegExp.globalMatch(QString::fromUtf8(substitutionedLine)); + break; + case QShaderNode::Invalid: + break; + } + + while (matches.hasNext()) { + QRegularExpressionMatch match = matches.next(); + + Variable *v = nullptr; + + switch (node.type()) { + // Record name of temporary variable that possibly references a global input + // We will replace the temporary variables by the matching global variables later + case QShaderNode::Input: { const QString localVariable = match.captured(1); const QString globalVariable = match.captured(2); @@ -499,13 +549,10 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) Assignment assignment; assignment.expression = globalVariable; v->assignment = assignment; + break; } - break; - } - case QShaderNode::Function: { - const QRegularExpressionMatch match = temporaryVariableToAssignmentRegExp.match(QString::fromUtf8(substitutionedLine)); - if (match.hasMatch()) { + case QShaderNode::Function: { const QString localVariableDeclaration = match.captured(1); const QString localVariableName = match.captured(2); const QString assignmentContent = match.captured(3); @@ -518,13 +565,10 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) // Find variables that may be referenced in the assignment gatherTemporaryVariablesFromAssignment(v, assignmentContent); + break; } - break; - } - case QShaderNode::Output: { - const QRegularExpressionMatch match = outputToTemporaryAssignmentRegExp.match(QString::fromUtf8(substitutionedLine)); - if (match.hasMatch()) { + case QShaderNode::Output: { const QString outputDeclaration = match.captured(1); const QString assignmentContent = match.captured(2); @@ -539,17 +583,17 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) // Find variables that may be referenced in the assignment gatherTemporaryVariablesFromAssignment(v, assignmentContent); + break; + } + case QShaderNode::Invalid: + break; } - break; - } - case QShaderNode::Invalid: - break; - } - LineContent lineContent; - lineContent.rawContent = QByteArray(QByteArrayLiteral(" ") + substitutionedLine); - lineContent.var = v; - lines << lineContent; + LineContent lineContent; + lineContent.rawContent = QByteArray(QByteArrayLiteral(" ") + substitutionedLine); + lineContent.var = v; + lines << lineContent; + } } // Go through all lines diff --git a/src/gui/util/qshadergraph.cpp b/src/gui/util/qshadergraph.cpp index b05b710713..611bb4b938 100644 --- a/src/gui/util/qshadergraph.cpp +++ b/src/gui/util/qshadergraph.cpp @@ -44,13 +44,20 @@ QT_BEGIN_NAMESPACE namespace { - QVector<QShaderNode> copyOutputNodes(const QVector<QShaderNode> &nodes) + QVector<QShaderNode> copyOutputNodes(const QVector<QShaderNode> &nodes, const QVector<QShaderGraph::Edge> &edges) { auto res = QVector<QShaderNode>(); std::copy_if(nodes.cbegin(), nodes.cend(), std::back_inserter(res), - [] (const QShaderNode &node) { - return node.type() == QShaderNode::Output; + [&edges] (const QShaderNode &node) { + return node.type() == QShaderNode::Output || + (node.type() == QShaderNode::Function && + !std::any_of(edges.cbegin(), + edges.cend(), + [&node] (const QShaderGraph::Edge &edge) { + return edge.sourceNodeUuid == + node.uuid(); + })); }); return res; } @@ -116,6 +123,50 @@ namespace } return targetStatement; } + + void removeNodesWithUnboundInputs(QVector<QShaderGraph::Statement> &statements, + const QVector<QShaderGraph::Edge> &allEdges) + { + // A node is invalid if any of its input ports is disconected + // or connected to the output port of another invalid node. + + // Keeps track of the edges from the nodes we know to be valid + // to unvisited nodes + auto currentEdges = QVector<QShaderGraph::Edge>(); + + statements.erase(std::remove_if(statements.begin(), + statements.end(), + [¤tEdges, &allEdges] (const QShaderGraph::Statement &statement) { + const QShaderNode &node = statement.node; + const QVector<QShaderGraph::Edge> outgoing = outgoingEdges(currentEdges, node.uuid()); + const QVector<QShaderNodePort> ports = node.ports(); + + bool allInputsConnected = true; + for (const QShaderNodePort &port : node.ports()) { + if (port.direction == QShaderNodePort::Output) + continue; + + const auto edgeIt = std::find_if(outgoing.cbegin(), + outgoing.cend(), + [&port] (const QShaderGraph::Edge &edge) { + return edge.targetPortName == port.name; + }); + + if (edgeIt != outgoing.cend()) + currentEdges.removeAll(*edgeIt); + else + allInputsConnected = false; + } + + if (allInputsConnected) { + const QVector<QShaderGraph::Edge> incoming = incomingEdges(allEdges, node.uuid()); + currentEdges.append(incoming); + } + + return !allInputsConnected; + }), + statements.end()); + } } QUuid QShaderGraph::Statement::uuid() const noexcept @@ -210,8 +261,8 @@ QVector<QShaderGraph::Statement> QShaderGraph::createStatements(const QStringLis auto result = QVector<Statement>(); QVector<Edge> currentEdges = enabledEdges; - QVector<QUuid> currentUuids = [enabledNodes] { - const QVector<QShaderNode> inputs = copyOutputNodes(enabledNodes); + QVector<QUuid> currentUuids = [enabledNodes, enabledEdges] { + const QVector<QShaderNode> inputs = copyOutputNodes(enabledNodes, enabledEdges); auto res = QVector<QUuid>(); std::transform(inputs.cbegin(), inputs.cend(), std::back_inserter(res), @@ -241,6 +292,9 @@ QVector<QShaderGraph::Statement> QShaderGraph::createStatements(const QStringLis } std::reverse(result.begin(), result.end()); + + removeNodesWithUnboundInputs(result, enabledEdges); + return result; } |