summaryrefslogtreecommitdiffstats
path: root/src/gui
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/.prev_CMakeLists.txt2
-rw-r--r--src/gui/CMakeLists.txt2
-rw-r--r--src/gui/image/qimage.h3
-rw-r--r--src/gui/kernel/qevent.cpp12
-rw-r--r--src/gui/kernel/qevent.h11
-rw-r--r--src/gui/kernel/qguiapplication.cpp13
-rw-r--r--src/gui/painting/qcolor.cpp1
-rw-r--r--src/gui/painting/qicc.cpp116
-rw-r--r--src/gui/rhi/qrhi.cpp132
-rw-r--r--src/gui/rhi/qrhi_p.h13
-rw-r--r--src/gui/rhi/qrhid3d11.cpp15
-rw-r--r--src/gui/rhi/qrhid3d11_p_p.h1
-rw-r--r--src/gui/rhi/qrhigles2.cpp18
-rw-r--r--src/gui/rhi/qrhigles2_p_p.h5
-rw-r--r--src/gui/rhi/qrhimetal.mm17
-rw-r--r--src/gui/rhi/qrhimetal_p_p.h1
-rw-r--r--src/gui/rhi/qrhinull.cpp4
-rw-r--r--src/gui/rhi/qrhivulkan.cpp17
-rw-r--r--src/gui/rhi/qrhivulkan_p_p.h1
-rw-r--r--src/gui/text/qtextdocument.cpp3
-rw-r--r--src/gui/text/text.pri2
-rw-r--r--src/gui/util/qshadergenerator.cpp106
-rw-r--r--src/gui/util/qshadergraph.cpp64
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(),
+ [&currentEdges, &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;
}