summaryrefslogtreecommitdiffstats
path: root/src/gui
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/configure.json1
-rw-r--r--src/gui/image/qimage.cpp4
-rw-r--r--src/gui/image/qimagereader.cpp29
-rw-r--r--src/gui/image/qmovie.cpp13
-rw-r--r--src/gui/image/qpixmapcache.cpp33
-rw-r--r--src/gui/image/qpnghandler.cpp56
-rw-r--r--src/gui/kernel/qevent.cpp218
-rw-r--r--src/gui/kernel/qevent.h30
-rw-r--r--src/gui/kernel/qguiapplication.cpp5
-rw-r--r--src/gui/kernel/qopenglcontext.cpp2
-rw-r--r--src/gui/kernel/qstylehints.cpp44
-rw-r--r--src/gui/kernel/qstylehints.h4
-rw-r--r--src/gui/opengl/qopenglprogrambinarycache.cpp37
-rw-r--r--src/gui/opengl/qopenglprogrambinarycache_p.h6
-rw-r--r--src/gui/opengl/qopenglshaderprogram.cpp8
-rw-r--r--src/gui/opengl/qopengltextureglyphcache.cpp4
-rw-r--r--src/gui/opengl/qopengltextureglyphcache_p.h2
-rw-r--r--src/gui/painting/qbackingstore.cpp2
-rw-r--r--src/gui/painting/qcolorspace.cpp279
-rw-r--r--src/gui/painting/qcolorspace.h30
-rw-r--r--src/gui/painting/qcolorspace_p.h17
-rw-r--r--src/gui/painting/qcolortransform.cpp27
-rw-r--r--src/gui/painting/qcolortransform.h29
-rw-r--r--src/gui/painting/qcolortransform_p.h4
-rw-r--r--src/gui/painting/qdrawhelper.cpp27
-rw-r--r--src/gui/painting/qicc.cpp245
-rw-r--r--src/gui/painting/qpagedpaintdevice.cpp3
-rw-r--r--src/gui/painting/qpagedpaintdevice.h1
-rw-r--r--src/gui/painting/qpaintengine_raster.cpp6
-rw-r--r--src/gui/painting/qpainterpath.cpp9
-rw-r--r--src/gui/painting/qpainterpath_p.h2
-rw-r--r--src/gui/painting/qpdf.cpp12
-rw-r--r--src/gui/painting/qpdf_p.h1
-rw-r--r--src/gui/painting/qpdfwriter.cpp8
-rw-r--r--src/gui/painting/qtextureglyphcache.cpp2
-rw-r--r--src/gui/painting/qtextureglyphcache_p.h8
-rw-r--r--src/gui/rhi/qrhi.cpp71
-rw-r--r--src/gui/rhi/qrhi_p_p.h6
-rw-r--r--src/gui/rhi/qrhid3d11.cpp4
-rw-r--r--src/gui/rhi/qrhigles2.cpp919
-rw-r--r--src/gui/rhi/qrhigles2_p_p.h148
-rw-r--r--src/gui/rhi/qrhimetal.mm15
-rw-r--r--src/gui/rhi/qrhivulkan.cpp65
-rw-r--r--src/gui/text/qdistancefield.cpp3
-rw-r--r--src/gui/text/qdistancefield_p.h1
-rw-r--r--src/gui/text/qfontengine.cpp12
-rw-r--r--src/gui/text/qfontengine_p.h4
-rw-r--r--src/gui/text/qfontengineglyphcache_p.h7
-rw-r--r--src/gui/text/qtextobject.cpp2
-rw-r--r--src/gui/util/qdesktopservices.cpp2
50 files changed, 1725 insertions, 742 deletions
diff --git a/src/gui/configure.json b/src/gui/configure.json
index 764e92a729..5aac1f221a 100644
--- a/src/gui/configure.json
+++ b/src/gui/configure.json
@@ -1550,6 +1550,7 @@
},
"xcb-native-painting": {
"label": "Native painting (experimental)",
+ "autoDetect": false,
"emitIf": "features.xcb",
"condition": "features.xcb-xlib && features.fontconfig && libs.xrender",
"output": [ "privateFeature" ]
diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp
index 61d32b0dec..cd2fe5bc10 100644
--- a/src/gui/image/qimage.cpp
+++ b/src/gui/image/qimage.cpp
@@ -5067,12 +5067,12 @@ void QImage::applyColorTransform(const QColorTransform &transform)
if (depth() > 32) {
for (int i = 0; i < height(); ++i) {
QRgba64 *scanline = reinterpret_cast<QRgba64 *>(scanLine(i));
- transform.d_func()->apply(scanline, scanline, width(), flags);
+ transform.d->apply(scanline, scanline, width(), flags);
}
} else {
for (int i = 0; i < height(); ++i) {
QRgb *scanline = reinterpret_cast<QRgb *>(scanLine(i));
- transform.d_func()->apply(scanline, scanline, width(), flags);
+ transform.d->apply(scanline, scanline, width(), flags);
}
}
diff --git a/src/gui/image/qimagereader.cpp b/src/gui/image/qimagereader.cpp
index 2762b702b2..6a0763e696 100644
--- a/src/gui/image/qimagereader.cpp
+++ b/src/gui/image/qimagereader.cpp
@@ -197,7 +197,7 @@ static QImageIOHandler *createReadHandlerHelper(QIODevice *device,
#ifdef QIMAGEREADER_DEBUG
qDebug() << "QImageReader::createReadHandler( device =" << (void *)device << ", format =" << format << "),"
- << keyMap.size() << "plugins available: " << keyMap.values();
+ << keyMap.uniqueKeys().size() << "plugins available: " << keyMap;
#endif
int suffixPluginIndex = -1;
@@ -325,6 +325,29 @@ static QImageIOHandler *createReadHandlerHelper(QIODevice *device,
#endif
}
+ if (handler && device && !suffix.isEmpty()) {
+ Q_ASSERT(qobject_cast<QFile *>(device));
+ // We have a file claiming to be of a recognized format. Now confirm that
+ // the handler also recognizes the file contents.
+ const qint64 pos = device->pos();
+ handler->setDevice(device);
+ if (!form.isEmpty())
+ handler->setFormat(form);
+ bool canRead = handler->canRead();
+ device->seek(pos);
+ if (canRead) {
+ // ok, we're done.
+ return handler;
+ }
+#ifdef QIMAGEREADER_DEBUG
+ qDebug() << "QImageReader::createReadHandler: the" << suffix << "handler can not read this file";
+#endif
+ // File may still be valid, just with wrong suffix, so fall back to
+ // finding a handler based on contents, below.
+ delete handler;
+ handler = nullptr;
+ }
+
#ifndef QT_NO_IMAGEFORMATPLUGIN
if (!handler && (autoDetectImageFormat || ignoresFormatAndExtension)) {
// check if any of our plugins recognize the file from its contents.
@@ -336,7 +359,7 @@ static QImageIOHandler *createReadHandlerHelper(QIODevice *device,
if (plugin && plugin->capabilities(device, QByteArray()) & QImageIOPlugin::CanRead) {
handler = plugin->create(device, testFormat);
#ifdef QIMAGEREADER_DEBUG
- qDebug() << "QImageReader::createReadHandler: the" << keyMap.keys().at(i) << "plugin can read this data";
+ qDebug() << "QImageReader::createReadHandler: the" << keyMap.value(i) << "plugin can read this data";
#endif
break;
}
@@ -1074,7 +1097,7 @@ QList<QByteArray> QImageReader::supportedSubTypes() const
\since 5.5
Returns the transformation metadata of the image, including image orientation. If the format
- does not support transformation metadata \c QImageIOHandler::Transformation_None is returned.
+ does not support transformation metadata, QImageIOHandler::TransformationNone is returned.
\sa setAutoTransform(), autoTransform()
*/
diff --git a/src/gui/image/qmovie.cpp b/src/gui/image/qmovie.cpp
index 4b588527ae..3e975115ab 100644
--- a/src/gui/image/qmovie.cpp
+++ b/src/gui/image/qmovie.cpp
@@ -207,8 +207,8 @@ public:
: pixmap(QPixmap()), delay(QMOVIE_INVALID_DELAY), endMark(false)
{ }
- inline QFrameInfo(const QPixmap &pixmap, int delay)
- : pixmap(pixmap), delay(delay), endMark(false)
+ inline QFrameInfo(QPixmap &&pixmap, int delay)
+ : pixmap(std::move(pixmap)), delay(delay), endMark(false)
{ }
inline bool isValid()
@@ -222,6 +222,7 @@ public:
static inline QFrameInfo endMarker()
{ return QFrameInfo(true); }
};
+Q_DECLARE_TYPEINFO(QFrameInfo, Q_MOVABLE_TYPE);
class QMoviePrivate : public QObjectPrivate
{
@@ -380,9 +381,7 @@ QFrameInfo QMoviePrivate::infoForFrame(int frameNumber)
}
if (frameNumber > greatestFrameNumber)
greatestFrameNumber = frameNumber;
- QPixmap aPixmap = QPixmap::fromImage(std::move(anImage));
- int aDelay = reader->nextImageDelay();
- return QFrameInfo(aPixmap, aDelay);
+ return QFrameInfo(QPixmap::fromImage(std::move(anImage)), reader->nextImageDelay());
} else if (frameNumber != 0) {
// We've read all frames now. Return an end marker
haveReadAll = true;
@@ -406,9 +405,7 @@ QFrameInfo QMoviePrivate::infoForFrame(int frameNumber)
return QFrameInfo(); // Invalid
}
greatestFrameNumber = i;
- QPixmap aPixmap = QPixmap::fromImage(std::move(anImage));
- int aDelay = reader->nextImageDelay();
- QFrameInfo info(aPixmap, aDelay);
+ QFrameInfo info(QPixmap::fromImage(std::move(anImage)), reader->nextImageDelay());
// Cache it!
frameMap.insert(i, info);
if (i == frameNumber) {
diff --git a/src/gui/image/qpixmapcache.cpp b/src/gui/image/qpixmapcache.cpp
index a41ec8f35c..483d6d79a2 100644
--- a/src/gui/image/qpixmapcache.cpp
+++ b/src/gui/image/qpixmapcache.cpp
@@ -42,6 +42,8 @@
#include "qobject.h"
#include "qdebug.h"
#include "qpixmapcache_p.h"
+#include "qthread.h"
+#include "qcoreapplication.h"
QT_BEGIN_NAMESPACE
@@ -83,6 +85,9 @@ QT_BEGIN_NAMESPACE
with QPixmapCache} explains how to use QPixmapCache to speed up
applications by caching the results of painting.
+ \note QPixmapCache is only usable from the application's main thread.
+ Access from other threads will be ignored and return failure.
+
\sa QCache, QPixmap
*/
@@ -98,6 +103,14 @@ static inline int cost(const QPixmap &pixmap)
return static_cast<int>(qBound(1LL, costKb, costMax));
}
+static inline bool qt_pixmapcache_thread_test()
+{
+ if (Q_LIKELY(QCoreApplication::instance() && QThread::currentThread() == QCoreApplication::instance()->thread()))
+ return true;
+
+ return false;
+}
+
/*!
\class QPixmapCache::Key
\brief The QPixmapCache::Key class can be used for efficient access
@@ -490,6 +503,8 @@ QPixmapCacheEntry::~QPixmapCacheEntry()
QPixmap *QPixmapCache::find(const QString &key)
{
+ if (!qt_pixmapcache_thread_test())
+ return nullptr;
return pm_cache()->object(key);
}
@@ -519,6 +534,8 @@ bool QPixmapCache::find(const QString &key, QPixmap &pixmap)
bool QPixmapCache::find(const QString &key, QPixmap *pixmap)
{
+ if (!qt_pixmapcache_thread_test())
+ return false;
QPixmap *ptr = pm_cache()->object(key);
if (ptr && pixmap)
*pixmap = *ptr;
@@ -536,6 +553,8 @@ bool QPixmapCache::find(const QString &key, QPixmap *pixmap)
*/
bool QPixmapCache::find(const Key &key, QPixmap *pixmap)
{
+ if (!qt_pixmapcache_thread_test())
+ return false;
//The key is not valid anymore, a flush happened before probably
if (!key.d || !key.d->isValid)
return false;
@@ -567,6 +586,8 @@ bool QPixmapCache::find(const Key &key, QPixmap *pixmap)
bool QPixmapCache::insert(const QString &key, const QPixmap &pixmap)
{
+ if (!qt_pixmapcache_thread_test())
+ return false;
return pm_cache()->insert(key, pixmap, cost(pixmap));
}
@@ -587,6 +608,8 @@ bool QPixmapCache::insert(const QString &key, const QPixmap &pixmap)
*/
QPixmapCache::Key QPixmapCache::insert(const QPixmap &pixmap)
{
+ if (!qt_pixmapcache_thread_test())
+ return QPixmapCache::Key();
return pm_cache()->insert(pixmap, cost(pixmap));
}
@@ -601,6 +624,8 @@ QPixmapCache::Key QPixmapCache::insert(const QPixmap &pixmap)
*/
bool QPixmapCache::replace(const Key &key, const QPixmap &pixmap)
{
+ if (!qt_pixmapcache_thread_test())
+ return false;
//The key is not valid anymore, a flush happened before probably
if (!key.d || !key.d->isValid)
return false;
@@ -630,6 +655,8 @@ int QPixmapCache::cacheLimit()
void QPixmapCache::setCacheLimit(int n)
{
+ if (!qt_pixmapcache_thread_test())
+ return;
pm_cache()->setMaxCost(n);
}
@@ -638,6 +665,8 @@ void QPixmapCache::setCacheLimit(int n)
*/
void QPixmapCache::remove(const QString &key)
{
+ if (!qt_pixmapcache_thread_test())
+ return;
pm_cache()->remove(key);
}
@@ -649,6 +678,8 @@ void QPixmapCache::remove(const QString &key)
*/
void QPixmapCache::remove(const Key &key)
{
+ if (!qt_pixmapcache_thread_test())
+ return;
//The key is not valid anymore, a flush happened before probably
if (!key.d || !key.d->isValid)
return;
@@ -661,6 +692,8 @@ void QPixmapCache::remove(const Key &key)
void QPixmapCache::clear()
{
+ if (!QCoreApplication::closingDown() && !qt_pixmapcache_thread_test())
+ return;
QT_TRY {
if (pm_cache.exists())
pm_cache->clear();
diff --git a/src/gui/image/qpnghandler.cpp b/src/gui/image/qpnghandler.cpp
index 864a944fcb..16d6c25b8b 100644
--- a/src/gui/image/qpnghandler.cpp
+++ b/src/gui/image/qpnghandler.cpp
@@ -53,7 +53,6 @@
#include <qcolorspace.h>
#include <private/qcolorspace_p.h>
-#include <private/qicc_p.h>
#include <png.h>
#include <pngconf.h>
@@ -607,9 +606,13 @@ bool QPngHandlerPrivate::readPngHeader()
#endif
png_uint_32 profLen;
png_get_iCCP(png_ptr, info_ptr, &name, &compressionType, &profileData, &profLen);
- if (!QIcc::fromIccProfile(QByteArray::fromRawData((const char *)profileData, profLen), &colorSpace)) {
+ colorSpace = QColorSpace::fromIccProfile(QByteArray::fromRawData((const char *)profileData, profLen));
+ if (!colorSpace.isValid()) {
qWarning() << "QPngHandler: Failed to parse ICC profile";
} else {
+ QColorSpacePrivate *csD = QColorSpacePrivate::getWritable(colorSpace);
+ if (csD->description.isEmpty())
+ csD->description = QString::fromLatin1((const char *)name);
colorSpaceState = Icc;
}
}
@@ -628,11 +631,25 @@ bool QPngHandlerPrivate::readPngHeader()
png_get_gAMA(png_ptr, info_ptr, &file_gamma);
fileGamma = file_gamma;
if (fileGamma > 0.0f && colorSpaceState <= GammaChrm) {
- QColorSpacePrivate *csPrivate = colorSpace.d_func();
- csPrivate->gamut = QColorSpace::Gamut::SRgb;
- csPrivate->transferFunction = QColorSpace::TransferFunction::Gamma;
- csPrivate->gamma = fileGamma;
- csPrivate->initialize();
+ QColorSpacePrimaries primaries;
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_cHRM)) {
+ double white_x, white_y, red_x, red_y;
+ double green_x, green_y, blue_x, blue_y;
+ png_get_cHRM(png_ptr, info_ptr,
+ &white_x, &white_y, &red_x, &red_y,
+ &green_x, &green_y, &blue_x, &blue_y);
+ primaries.whitePoint = QPointF(white_x, white_y);
+ primaries.redPoint = QPointF(red_x, red_y);
+ primaries.greenPoint = QPointF(green_x, green_y);
+ primaries.bluePoint = QPointF(blue_x, blue_y);
+ }
+ if (primaries.areValid()) {
+ colorSpace = QColorSpace(primaries.whitePoint, primaries.redPoint, primaries.greenPoint, primaries.bluePoint,
+ QColorSpace::TransferFunction::Gamma, fileGamma);
+ } else {
+ colorSpace = QColorSpace(QColorSpace::Primaries::SRgb,
+ QColorSpace::TransferFunction::Gamma, fileGamma);
+ }
colorSpaceState = GammaChrm;
}
}
@@ -663,10 +680,7 @@ bool QPngHandlerPrivate::readPngImage(QImage *outImage)
// This configuration forces gamma correction and
// thus changes the output colorspace
png_set_gamma(png_ptr, 1.0f / gamma, fileGamma);
- QColorSpacePrivate *csPrivate = colorSpace.d_func();
- csPrivate->transferFunction = QColorSpace::TransferFunction::Gamma;
- csPrivate->gamma = gamma;
- csPrivate->initialize();
+ colorSpace = colorSpace.withTransferFunction(QColorSpace::TransferFunction::Gamma, 1.0f / gamma);
colorSpaceState = GammaChrm;
}
@@ -962,6 +976,26 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int compression_i
bpc, // per channel
color_type, 0, 0, 0); // sets #channels
+#ifdef PNG_iCCP_SUPPORTED
+ if (image.colorSpace().isValid()) {
+ QColorSpace cs = image.colorSpace();
+ // Support the old gamma making it override transferfunction.
+ if (gamma != 0.0 && !qFuzzyCompare(cs.gamma(), 1.0f / gamma))
+ cs = cs.withTransferFunction(QColorSpace::TransferFunction::Gamma, 1.0f / gamma);
+ QByteArray iccProfileName = QColorSpacePrivate::get(cs)->description.toLatin1();
+ if (iccProfileName.isEmpty())
+ iccProfileName = QByteArrayLiteral("Custom");
+ QByteArray iccProfile = cs.iccProfile();
+ png_set_iCCP(png_ptr, info_ptr,
+ #if PNG_LIBPNG_VER < 10500
+ iccProfileName.data(), PNG_COMPRESSION_TYPE_BASE, iccProfile.data(),
+ #else
+ iccProfileName.constData(), PNG_COMPRESSION_TYPE_BASE,
+ (png_const_bytep)iccProfile.constData(),
+ #endif
+ iccProfile.length());
+ } else
+#endif
if (gamma != 0.0) {
png_set_gAMA(png_ptr, info_ptr, 1.0/gamma);
}
diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp
index e1d685b1b0..ec52791010 100644
--- a/src/gui/kernel/qevent.cpp
+++ b/src/gui/kernel/qevent.cpp
@@ -754,31 +754,15 @@ QHoverEvent::~QHoverEvent()
\fn Qt::Orientation QWheelEvent::orientation() const
\obsolete
- Returns the wheel's orientation.
-
Use angleDelta() instead.
*/
+#if QT_CONFIG(wheelevent)
+#if QT_DEPRECATED_SINCE(5, 15)
/*!
\obsolete
- Constructs a wheel event object.
-
- Use the constructor taking \e angleDelta and \e pixelDelta QPoints instead.
-
- The position, \a pos, is the location of the mouse cursor within
- the widget. The globalPos() is initialized to QCursor::pos()
- which is usually, but not always, correct.
- Use the other constructor if you need to specify the global
- position explicitly.
-
- The \a buttons describe the state of the mouse buttons at the time
- of the event, \a delta contains the rotation distance,
- \a modifiers holds the keyboard modifier flags at the time of the
- event, and \a orient holds the wheel's orientation.
-
- \sa pos(), pixelDelta(), angleDelta()
+ This constructor has been deprecated.
*/
-#if QT_CONFIG(wheelevent)
QWheelEvent::QWheelEvent(const QPointF &pos, int delta,
Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers,
Qt::Orientation orient)
@@ -793,26 +777,8 @@ QWheelEvent::QWheelEvent(const QPointF &pos, int delta,
}
/*!
- \internal
-*/
-QWheelEvent::~QWheelEvent()
-{
-}
-
-/*!
\obsolete
- Constructs a wheel event object.
-
- Use the constructor taking \e angleDelta and \e pixelDelta QPoints instead.
-
- The \a pos provides the location of the mouse cursor
- within the widget. The position in global coordinates is specified
- by \a globalPos. \a delta contains the rotation distance, \a modifiers
- holds the keyboard modifier flags at the time of the event, and
- \a orient holds the wheel's orientation.
-
-
- \sa pos(), pixelDelta(), angleDelta()
+ This constructor has been deprecated.
*/
QWheelEvent::QWheelEvent(const QPointF &pos, const QPointF& globalPos, int delta,
Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers,
@@ -827,27 +793,8 @@ QWheelEvent::QWheelEvent(const QPointF &pos, const QPointF& globalPos, int delta
}
/*!
- Constructs a wheel event object.
-
- The \a pos provides the location of the mouse cursor
- within the window. The position in global coordinates is specified
- by \a globalPos.
-
- \a pixelDelta contains the scrolling distance in pixels on screen, while
- \a angleDelta contains the wheel rotation distance. \a pixelDelta is
- optional and can be null.
-
- The mouse and keyboard states at the time of the event are specified by
- \a buttons and \a modifiers.
-
- For backwards compatibility, the event can also hold monodirectional wheel
- event data: \a qt4Delta specifies the rotation, and \a qt4Orientation the
- direction.
-
- The phase() is initialized to Qt::ScrollUpdate. Use the other constructor
- to specify the phase explicitly.
-
- \sa posF(), globalPosF(), angleDelta(), pixelDelta()
+ \obsolete
+ This constructor has been deprecated.
*/
QWheelEvent::QWheelEvent(const QPointF &pos, const QPointF& globalPos,
@@ -858,26 +805,8 @@ QWheelEvent::QWheelEvent(const QPointF &pos, const QPointF& globalPos,
{}
/*!
- Constructs a wheel event object.
-
- The \a pos provides the location of the mouse cursor
- within the window. The position in global coordinates is specified
- by \a globalPos.
-
- \a pixelDelta contains the scrolling distance in pixels on screen, while
- \a angleDelta contains the wheel rotation distance. \a pixelDelta is
- optional and can be null.
-
- The mouse and keyboard states at the time of the event are specified by
- \a buttons and \a modifiers.
-
- For backwards compatibility, the event can also hold monodirectional wheel
- event data: \a qt4Delta specifies the rotation, and \a qt4Orientation the
- direction.
-
- The scrolling phase of the event is specified by \a phase.
-
- \sa posF(), globalPosF(), angleDelta(), pixelDelta(), phase()
+ \obsolete
+ This constructor has been deprecated.
*/
QWheelEvent::QWheelEvent(const QPointF &pos, const QPointF& globalPos,
@@ -888,31 +817,8 @@ QWheelEvent::QWheelEvent(const QPointF &pos, const QPointF& globalPos,
{}
/*!
- Constructs a wheel event object.
-
- The \a pos provides the location of the mouse cursor within the window. The
- position in global coordinates is specified by \a globalPos.
-
- \a pixelDelta contains the scrolling distance in pixels on screen, while
- \a angleDelta contains the wheel rotation distance. \a pixelDelta is
- optional and can be null.
-
- The mouse and keyboard states at the time of the event are specified by
- \a buttons and \a modifiers.
-
- For backwards compatibility, the event can also hold monodirectional wheel
- event data: \a qt4Delta specifies the rotation, and \a qt4Orientation the
- direction.
-
- The scrolling phase of the event is specified by \a phase.
-
- If the wheel event comes from a physical mouse wheel, \a source is set to
- Qt::MouseEventNotSynthesized. If it comes from a gesture detected by the
- operating system, or from a non-mouse hardware device, such that \a pixelDelta is
- directly related to finger movement, \a source is set to Qt::MouseEventSynthesizedBySystem.
- If it comes from Qt, source would be set to Qt::MouseEventSynthesizedByQt.
-
- \sa posF(), globalPosF(), angleDelta(), pixelDelta(), phase()
+ \obsolete
+ This constructor has been deprecated.
*/
QWheelEvent::QWheelEvent(const QPointF &pos, const QPointF& globalPos,
@@ -923,37 +829,8 @@ QWheelEvent::QWheelEvent(const QPointF &pos, const QPointF& globalPos,
{}
/*!
- Constructs a wheel event object.
-
- The \a pos provides the location of the mouse cursor
- within the window. The position in global coordinates is specified
- by \a globalPos.
-
- \a pixelDelta contains the scrolling distance in pixels on screen, while
- \a angleDelta contains the wheel rotation distance. \a pixelDelta is
- optional and can be null.
-
- The mouse and keyboard states at the time of the event are specified by
- \a buttons and \a modifiers.
-
- For backwards compatibility, the event can also hold monodirectional wheel
- event data: \a qt4Delta specifies the rotation, and \a qt4Orientation the
- direction.
-
- The scrolling phase of the event is specified by \a phase.
-
- If the wheel event comes from a physical mouse wheel, \a source is set to
- Qt::MouseEventNotSynthesized. If it comes from a gesture detected by the
- operating system, or from a non-mouse hardware device, such that \a
- pixelDelta is directly related to finger movement, \a source is set to
- Qt::MouseEventSynthesizedBySystem. If it comes from Qt, source would be set
- to Qt::MouseEventSynthesizedByQt.
-
- If the system is configured to invert the delta values delivered with the
- event (such as natural scrolling of the touchpad on OS X), \a inverted
- should be \c true. Otherwise, \a inverted is \c false
-
- \sa posF(), globalPosF(), angleDelta(), pixelDelta(), phase()
+ \obsolete
+ This constructor has been deprecated.
*/
QWheelEvent::QWheelEvent(const QPointF &pos, const QPointF& globalPos,
QPoint pixelDelta, QPoint angleDelta, int qt4Delta, Qt::Orientation qt4Orientation,
@@ -962,6 +839,7 @@ QWheelEvent::QWheelEvent(const QPointF &pos, const QPointF& globalPos,
angleD(angleDelta), qt4D(qt4Delta), qt4O(qt4Orientation), mouseState(buttons), src(source),
invertedScrolling(inverted), ph(phase)
{}
+#endif // QT_DEPRECATED_SINCE(5, 15)
/*!
Constructs a wheel event object.
@@ -990,7 +868,7 @@ QWheelEvent::QWheelEvent(const QPointF &pos, const QPointF& globalPos,
event (such as natural scrolling of the touchpad on macOS), \a inverted
should be \c true. Otherwise, \a inverted is \c false
- \sa posF(), globalPosF(), angleDelta(), pixelDelta(), phase()
+ \sa position(), globalPosition(), angleDelta(), pixelDelta(), phase(), inverted(), source()
*/
QWheelEvent::QWheelEvent(QPointF pos, QPointF globalPos, QPoint pixelDelta, QPoint angleDelta,
Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, Qt::ScrollPhase phase,
@@ -1002,6 +880,12 @@ QWheelEvent::QWheelEvent(QPointF pos, QPointF globalPos, QPoint pixelDelta, QPoi
qt4D = (qt4O == Qt::Horizontal ? angleDelta.x() : angleDelta.y());
}
+/*!
+ \internal
+*/
+QWheelEvent::~QWheelEvent()
+{
+}
#endif // QT_CONFIG(wheelevent)
/*!
@@ -1061,87 +945,59 @@ QWheelEvent::QWheelEvent(QPointF pos, QPointF globalPos, QPoint pixelDelta, QPoi
/*!
\fn QPoint QWheelEvent::pos() const
+ \obsolete
- Returns the position of the mouse cursor relative to the widget
- that received the event.
-
- If you move your widgets around in response to mouse events,
- use globalPos() instead of this function.
-
- \sa x(), y(), globalPos()
+ This function has been deprecated, use position() instead.
*/
/*!
\fn int QWheelEvent::x() const
+ \obsolete
- Returns the x position of the mouse cursor, relative to the
- widget that received the event.
-
- \sa y(), pos()
+ This function has been deprecated, use position() instead.
*/
/*!
\fn int QWheelEvent::y() const
+ \obsolete
- Returns the y position of the mouse cursor, relative to the
- widget that received the event.
-
- \sa x(), pos()
+ This function has been deprecated, use position() instead.
*/
/*!
\fn QPoint QWheelEvent::globalPos() const
+ \obsolete
- Returns the global position of the mouse pointer \e{at the time
- of the event}. This is important on asynchronous window systems
- such as X11; whenever you move your widgets around in response to
- mouse events, globalPos() can differ a lot from the current
- cursor position returned by QCursor::pos().
-
- \sa globalX(), globalY()
+ This function has been deprecated, use globalPosition() instead.
*/
/*!
\fn int QWheelEvent::globalX() const
+ \obsolete
- Returns the global x position of the mouse cursor at the time of
- the event.
-
- \sa globalY(), globalPos()
+ This function has been deprecated, use globalPosition() instead.
*/
/*!
\fn int QWheelEvent::globalY() const
+ \obsolete
- Returns the global y position of the mouse cursor at the time of
- the event.
-
- \sa globalX(), globalPos()
+ This function has been deprecated, use globalPosition() instead.
*/
/*!
\fn const QPointF &QWheelEvent::posF() const
+ \obsolete
- Returns the position of the mouse cursor relative to the widget
- that received the event.
-
- If you move your widgets around in response to mouse events,
- use globalPosF() instead of this function.
-
- \sa globalPosF()
+ This function has been deprecated, use position() instead.
*/
/*!
\fn const QPointF &QWheelEvent::globalPosF() const
+ \obsolete
- Returns the global position of the mouse pointer \e{at the time
- of the event}. This is important on asynchronous window systems
- such as X11; whenever you move your widgets around in response to
- mouse events, globalPosF() can differ a lot from the current
- cursor position returned by QCursor::pos().
-
- \sa posF()
+ This function has been deprecated, use globalPosition() instead.
*/
/*!
@@ -4074,8 +3930,10 @@ QDebug operator<<(QDebug dbg, const QEvent *e)
dbg << "QWheelEvent(" << we->phase();
if (!we->pixelDelta().isNull() || !we->angleDelta().isNull())
dbg << ", pixelDelta=" << we->pixelDelta() << ", angleDelta=" << we->angleDelta();
+#if QT_DEPRECATED_SINCE(5, 14)
else if (int qt4Delta = we->delta())
dbg << ", delta=" << qt4Delta << ", orientation=" << we->orientation();
+#endif
dbg << ')';
}
break;
diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h
index eb0a6208a9..b73d90529a 100644
--- a/src/gui/kernel/qevent.h
+++ b/src/gui/kernel/qevent.h
@@ -175,24 +175,34 @@ class Q_GUI_EXPORT QWheelEvent : public QInputEvent
public:
enum { DefaultDeltasPerStep = 120 };
+#if QT_DEPRECATED_SINCE(5, 15)
+ // Actually deprecated since 5.0, in docs
+ QT_DEPRECATED_VERSION_X_5_15("Use the last QWheelEvent constructor taking pixelDelta, angleDelta, phase, and inverted")
QWheelEvent(const QPointF &pos, int delta,
Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers,
Qt::Orientation orient = Qt::Vertical);
+ // Actually deprecated since 5.0, in docs
+ QT_DEPRECATED_VERSION_X_5_15("Use the last QWheelEvent constructor taking pixelDelta, angleDelta, phase, and inverted")
QWheelEvent(const QPointF &pos, const QPointF& globalPos, int delta,
Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers,
Qt::Orientation orient = Qt::Vertical);
+ QT_DEPRECATED_VERSION_X_5_15("Use the last QWheelEvent constructor taking pixelDelta, angleDelta, phase, and inverted")
QWheelEvent(const QPointF &pos, const QPointF& globalPos,
QPoint pixelDelta, QPoint angleDelta, int qt4Delta, Qt::Orientation qt4Orientation,
Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers);
+ QT_DEPRECATED_VERSION_X_5_15("Use the last QWheelEvent constructor taking pixelDelta, angleDelta, phase, and inverted")
QWheelEvent(const QPointF &pos, const QPointF& globalPos,
QPoint pixelDelta, QPoint angleDelta, int qt4Delta, Qt::Orientation qt4Orientation,
Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, Qt::ScrollPhase phase);
+ QT_DEPRECATED_VERSION_X_5_15("Use the last QWheelEvent constructor taking pixelDelta, angleDelta, phase, and inverted")
QWheelEvent(const QPointF &pos, const QPointF &globalPos, QPoint pixelDelta, QPoint angleDelta,
int qt4Delta, Qt::Orientation qt4Orientation, Qt::MouseButtons buttons,
Qt::KeyboardModifiers modifiers, Qt::ScrollPhase phase, Qt::MouseEventSource source);
+ QT_DEPRECATED_VERSION_X_5_15("Use the last QWheelEvent constructor taking pixelDelta, angleDelta, phase, and inverted")
QWheelEvent(const QPointF &pos, const QPointF &globalPos, QPoint pixelDelta, QPoint angleDelta,
int qt4Delta, Qt::Orientation qt4Orientation, Qt::MouseButtons buttons,
Qt::KeyboardModifiers modifiers, Qt::ScrollPhase phase, Qt::MouseEventSource source, bool inverted);
+#endif
QWheelEvent(QPointF pos, QPointF globalPos, QPoint pixelDelta, QPoint angleDelta,
Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, Qt::ScrollPhase phase,
@@ -203,19 +213,35 @@ public:
inline QPoint pixelDelta() const { return pixelD; }
inline QPoint angleDelta() const { return angleD; }
+#if QT_DEPRECATED_SINCE(5, 15)
+ // Actually deprecated since 5.0, in docs
+ QT_DEPRECATED_VERSION_X_5_15("Use angleDelta()")
inline int delta() const { return qt4D; }
+ // Actually deprecated since 5.0, in docs
+ QT_DEPRECATED_VERSION_X_5_15("Use angleDelta()")
inline Qt::Orientation orientation() const { return qt4O; }
-
#ifndef QT_NO_INTEGER_EVENT_COORDINATES
+ QT_DEPRECATED_VERSION_X_5_15("Use position()")
inline QPoint pos() const { return p.toPoint(); }
+ QT_DEPRECATED_VERSION_X_5_15("Use globalPosition()")
inline QPoint globalPos() const { return g.toPoint(); }
+ QT_DEPRECATED_VERSION_X_5_15("Use position()")
inline int x() const { return int(p.x()); }
+ QT_DEPRECATED_VERSION_X_5_15("Use position()")
inline int y() const { return int(p.y()); }
+ QT_DEPRECATED_VERSION_X_5_15("Use globalPosition()")
inline int globalX() const { return int(g.x()); }
+ QT_DEPRECATED_VERSION_X_5_15("Use globalPosition()")
inline int globalY() const { return int(g.y()); }
#endif
+ QT_DEPRECATED_VERSION_X_5_15("Use position()")
inline const QPointF &posF() const { return p; }
+ QT_DEPRECATED_VERSION_X_5_15("Use globalPosition()")
inline const QPointF &globalPosF() const { return g; }
+#endif // QT_DEPRECATED_SINCE(5, 15)
+
+ inline QPointF position() const { return p; }
+ inline QPointF globalPosition() const { return g; }
inline Qt::MouseButtons buttons() const { return mouseState; }
@@ -231,7 +257,7 @@ protected:
QPoint angleD;
int qt4D = 0;
Qt::Orientation qt4O = Qt::Vertical;
- Qt::MouseButtons mouseState;
+ Qt::MouseButtons mouseState = Qt::NoButton;
uint _unused_ : 2; // Kept for binary compatibility
uint src: 2;
bool invertedScrolling : 1;
diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp
index ceb5055a9d..426f2aeece 100644
--- a/src/gui/kernel/qguiapplication.cpp
+++ b/src/gui/kernel/qguiapplication.cpp
@@ -2239,8 +2239,13 @@ void QGuiApplicationPrivate::processWheelEvent(QWindowSystemInterfacePrivate::Wh
return;
}
+#if QT_DEPRECATED_SINCE(5, 14)
QWheelEvent ev(localPoint, globalPoint, e->pixelDelta, e->angleDelta, e->qt4Delta, e->qt4Orientation,
mouse_buttons, e->modifiers, e->phase, e->source, e->inverted);
+#else
+ QWheelEvent ev(localPoint, globalPoint, e->pixelDelta, e->angleDelta,
+ mouse_buttons, e->modifiers, e->phase, e->inverted, e->source);
+#endif
ev.setTimestamp(e->timestamp);
QGuiApplication::sendSpontaneousEvent(window, &ev);
#else
diff --git a/src/gui/kernel/qopenglcontext.cpp b/src/gui/kernel/qopenglcontext.cpp
index 326d2823eb..10adee438b 100644
--- a/src/gui/kernel/qopenglcontext.cpp
+++ b/src/gui/kernel/qopenglcontext.cpp
@@ -611,8 +611,8 @@ bool QOpenGLContext::create()
d->platformGLContext = QGuiApplicationPrivate::platformIntegration()->createPlatformOpenGLContext(this);
if (!d->platformGLContext)
return false;
- d->platformGLContext->initialize();
d->platformGLContext->setContext(this);
+ d->platformGLContext->initialize();
if (!d->platformGLContext->isSharing())
d->shareContext = 0;
d->shareGroup = d->shareContext ? d->shareContext->shareGroup() : new QOpenGLContextGroup;
diff --git a/src/gui/kernel/qstylehints.cpp b/src/gui/kernel/qstylehints.cpp
index 9b5b7a6f1e..732ede90d0 100644
--- a/src/gui/kernel/qstylehints.cpp
+++ b/src/gui/kernel/qstylehints.cpp
@@ -65,6 +65,20 @@ static inline QVariant themeableHint(QPlatformTheme::ThemeHint th,
return QGuiApplicationPrivate::platformIntegration()->styleHint(ih);
}
+static inline QVariant themeableHint(QPlatformTheme::ThemeHint th)
+{
+ if (!QCoreApplication::instance()) {
+ qWarning("Must construct a QGuiApplication before accessing a platform theme hint.");
+ return QVariant();
+ }
+ if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) {
+ const QVariant themeHint = theme->themeHint(th);
+ if (themeHint.isValid())
+ return themeHint;
+ }
+ return QPlatformTheme::defaultThemeHint(th);
+}
+
class QStyleHintsPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QStyleHints)
@@ -80,6 +94,8 @@ public:
int m_showShortcutsInContextMenus = -1;
int m_wheelScrollLines = -1;
int m_mouseQuickSelectionThreshold = -1;
+ int m_mouseDoubleClickDistance = -1;
+ int m_touchDoubleTapDistance = -1;
};
/*!
@@ -133,6 +149,34 @@ int QStyleHints::mouseDoubleClickInterval() const
}
/*!
+ \property QStyleHints::mouseDoubleClickDistance
+ \brief the maximum distance, in pixels, that the mouse can be moved between
+ two consecutive mouse clicks and still have it detected as a double-click
+ \since 5.14
+*/
+int QStyleHints::mouseDoubleClickDistance() const
+{
+ Q_D(const QStyleHints);
+ return d->m_mouseDoubleClickDistance >= 0 ?
+ d->m_mouseDoubleClickDistance :
+ themeableHint(QPlatformTheme::MouseDoubleClickDistance).toInt();
+}
+
+/*!
+ \property QStyleHints::touchDoubleTapDistance
+ \brief the maximum distance, in pixels, that a finger can be moved between
+ two consecutive taps and still have it detected as a double-tap
+ \since 5.14
+*/
+int QStyleHints::touchDoubleTapDistance() const
+{
+ Q_D(const QStyleHints);
+ return d->m_touchDoubleTapDistance >= 0 ?
+ d->m_touchDoubleTapDistance :
+ themeableHint(QPlatformTheme::TouchDoubleTapDistance).toInt();
+}
+
+/*!
Sets the \a mousePressAndHoldInterval.
\internal
\sa mousePressAndHoldInterval()
diff --git a/src/gui/kernel/qstylehints.h b/src/gui/kernel/qstylehints.h
index 9091db9624..30d8fdc64d 100644
--- a/src/gui/kernel/qstylehints.h
+++ b/src/gui/kernel/qstylehints.h
@@ -74,10 +74,14 @@ class Q_GUI_EXPORT QStyleHints : public QObject
Q_PROPERTY(bool useHoverEffects READ useHoverEffects WRITE setUseHoverEffects NOTIFY useHoverEffectsChanged FINAL)
Q_PROPERTY(int wheelScrollLines READ wheelScrollLines NOTIFY wheelScrollLinesChanged FINAL)
Q_PROPERTY(int mouseQuickSelectionThreshold READ mouseQuickSelectionThreshold WRITE setMouseQuickSelectionThreshold NOTIFY mouseQuickSelectionThresholdChanged FINAL)
+ Q_PROPERTY(int mouseDoubleClickDistance READ mouseDoubleClickDistance STORED false CONSTANT FINAL)
+ Q_PROPERTY(int touchDoubleTapDistance READ touchDoubleTapDistance STORED false CONSTANT FINAL)
public:
void setMouseDoubleClickInterval(int mouseDoubleClickInterval);
int mouseDoubleClickInterval() const;
+ int mouseDoubleClickDistance() const;
+ int touchDoubleTapDistance() const;
void setMousePressAndHoldInterval(int mousePressAndHoldInterval);
int mousePressAndHoldInterval() const;
void setStartDragDistance(int startDragDistance);
diff --git a/src/gui/opengl/qopenglprogrambinarycache.cpp b/src/gui/opengl/qopenglprogrambinarycache.cpp
index c96021a969..1f1ac1fd80 100644
--- a/src/gui/opengl/qopenglprogrambinarycache.cpp
+++ b/src/gui/opengl/qopenglprogrambinarycache.cpp
@@ -168,12 +168,19 @@ bool QOpenGLProgramBinaryCache::verifyHeader(const QByteArray &buf) const
bool QOpenGLProgramBinaryCache::setProgramBinary(uint programId, uint blobFormat, const void *p, uint blobSize)
{
- QOpenGLExtraFunctions *funcs = QOpenGLContext::currentContext()->extraFunctions();
+ QOpenGLContext *context = QOpenGLContext::currentContext();
+ QOpenGLExtraFunctions *funcs = context->extraFunctions();
while (true) {
GLenum error = funcs->glGetError();
if (error == GL_NO_ERROR || error == GL_CONTEXT_LOST)
break;
}
+#if defined(QT_OPENGL_ES_2)
+ if (context->isOpenGLES() && context->format().majorVersion() < 3) {
+ initializeProgramBinaryOES(context);
+ programBinaryOES(programId, blobFormat, p, blobSize);
+ } else
+#endif
funcs->glProgramBinary(programId, blobFormat, p, blobSize);
GLenum err = funcs->glGetError();
@@ -347,7 +354,8 @@ void QOpenGLProgramBinaryCache::save(const QByteArray &cacheKey, uint programId)
GLEnvInfo info;
- QOpenGLExtraFunctions *funcs = QOpenGLContext::currentContext()->extraFunctions();
+ QOpenGLContext *context = QOpenGLContext::currentContext();
+ QOpenGLExtraFunctions *funcs = context->extraFunctions();
GLint blobSize = 0;
while (true) {
GLenum error = funcs->glGetError();
@@ -390,6 +398,12 @@ void QOpenGLProgramBinaryCache::save(const QByteArray &cacheKey, uint programId)
*p++ = 0;
GLint outSize = 0;
+#if defined(QT_OPENGL_ES_2)
+ if (context->isOpenGLES() && context->format().majorVersion() < 3) {
+ initializeProgramBinaryOES(context);
+ getProgramBinaryOES(programId, blobSize, &outSize, &blobFormat, p);
+ } else
+#endif
funcs->glGetProgramBinary(programId, blobSize, &outSize, &blobFormat, p);
if (blobSize != outSize) {
qCDebug(DBG_SHADER_CACHE, "glGetProgramBinary returned size %d instead of %d", outSize, blobSize);
@@ -398,14 +412,33 @@ void QOpenGLProgramBinaryCache::save(const QByteArray &cacheKey, uint programId)
writeUInt(&blobFormatPtr, blobFormat);
+#if QT_CONFIG(temporaryfile)
QSaveFile f(cacheFileName(cacheKey));
if (f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
f.write(blob);
if (!f.commit())
+#else
+ QFile f(cacheFileName(cacheKey));
+ if (f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
+ if (f.write(blob) < blob.length())
+#endif
qCDebug(DBG_SHADER_CACHE, "Failed to write %s to shader cache", qPrintable(f.fileName()));
} else {
qCDebug(DBG_SHADER_CACHE, "Failed to create %s in shader cache", qPrintable(f.fileName()));
}
}
+#if defined(QT_OPENGL_ES_2)
+void QOpenGLProgramBinaryCache::initializeProgramBinaryOES(QOpenGLContext *context)
+{
+ if (m_programBinaryOESInitialized)
+ return;
+ m_programBinaryOESInitialized = true;
+
+ Q_ASSERT(context);
+ getProgramBinaryOES = (void (QOPENGLF_APIENTRYP)(GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, GLvoid *binary))context->getProcAddress("glGetProgramBinaryOES");
+ programBinaryOES = (void (QOPENGLF_APIENTRYP)(GLuint program, GLenum binaryFormat, const GLvoid *binary, GLint length))context->getProcAddress("glProgramBinaryOES");
+}
+#endif
+
QT_END_NAMESPACE
diff --git a/src/gui/opengl/qopenglprogrambinarycache_p.h b/src/gui/opengl/qopenglprogrambinarycache_p.h
index a0e1f91e25..9fade08e66 100644
--- a/src/gui/opengl/qopenglprogrambinarycache_p.h
+++ b/src/gui/opengl/qopenglprogrambinarycache_p.h
@@ -93,6 +93,12 @@ private:
uint format;
};
QCache<QByteArray, MemCacheEntry> m_memCache;
+#if defined(QT_OPENGL_ES_2)
+ void (QOPENGLF_APIENTRYP programBinaryOES)(GLuint program, GLenum binaryFormat, const GLvoid *binary, GLsizei length);
+ void (QOPENGLF_APIENTRYP getProgramBinaryOES)(GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, GLvoid *binary);
+ void initializeProgramBinaryOES(QOpenGLContext *context);
+ bool m_programBinaryOESInitialized = false;
+#endif
};
QT_END_NAMESPACE
diff --git a/src/gui/opengl/qopenglshaderprogram.cpp b/src/gui/opengl/qopenglshaderprogram.cpp
index f225d5dc75..3c7bd4f90d 100644
--- a/src/gui/opengl/qopenglshaderprogram.cpp
+++ b/src/gui/opengl/qopenglshaderprogram.cpp
@@ -3755,8 +3755,14 @@ QOpenGLProgramBinarySupportCheck::QOpenGLProgramBinarySupportCheck(QOpenGLContex
if (ctx) {
if (ctx->isOpenGLES()) {
qCDebug(DBG_SHADER_CACHE, "OpenGL ES v%d context", ctx->format().majorVersion());
- if (ctx->format().majorVersion() >= 3)
+ if (ctx->format().majorVersion() >= 3) {
m_supported = true;
+ } else {
+ const bool hasExt = ctx->hasExtension("GL_OES_get_program_binary");
+ qCDebug(DBG_SHADER_CACHE, "GL_OES_get_program_binary support = %d", hasExt);
+ if (hasExt)
+ m_supported = true;
+ }
} else {
const bool hasExt = ctx->hasExtension("GL_ARB_get_program_binary");
qCDebug(DBG_SHADER_CACHE, "GL_ARB_get_program_binary support = %d", hasExt);
diff --git a/src/gui/opengl/qopengltextureglyphcache.cpp b/src/gui/opengl/qopengltextureglyphcache.cpp
index e3cbba955d..490dc99749 100644
--- a/src/gui/opengl/qopengltextureglyphcache.cpp
+++ b/src/gui/opengl/qopengltextureglyphcache.cpp
@@ -53,8 +53,8 @@ static int next_qopengltextureglyphcache_serial_number()
return 1 + serial.fetchAndAddRelaxed(1);
}
-QOpenGLTextureGlyphCache::QOpenGLTextureGlyphCache(QFontEngine::GlyphFormat format, const QTransform &matrix)
- : QImageTextureGlyphCache(format, matrix)
+QOpenGLTextureGlyphCache::QOpenGLTextureGlyphCache(QFontEngine::GlyphFormat format, const QTransform &matrix, const QColor &color)
+ : QImageTextureGlyphCache(format, matrix, color)
, m_textureResource(0)
, pex(0)
, m_blitProgram(0)
diff --git a/src/gui/opengl/qopengltextureglyphcache_p.h b/src/gui/opengl/qopengltextureglyphcache_p.h
index aed128cf9e..4bea4a463a 100644
--- a/src/gui/opengl/qopengltextureglyphcache_p.h
+++ b/src/gui/opengl/qopengltextureglyphcache_p.h
@@ -110,7 +110,7 @@ public:
class Q_GUI_EXPORT QOpenGLTextureGlyphCache : public QImageTextureGlyphCache
{
public:
- QOpenGLTextureGlyphCache(QFontEngine::GlyphFormat glyphFormat, const QTransform &matrix);
+ QOpenGLTextureGlyphCache(QFontEngine::GlyphFormat glyphFormat, const QTransform &matrix, const QColor &color = QColor());
~QOpenGLTextureGlyphCache();
virtual void createTextureData(int width, int height) override;
diff --git a/src/gui/painting/qbackingstore.cpp b/src/gui/painting/qbackingstore.cpp
index 3fab903c4d..3240b83451 100644
--- a/src/gui/painting/qbackingstore.cpp
+++ b/src/gui/painting/qbackingstore.cpp
@@ -186,7 +186,7 @@ QPaintDevice *QBackingStore::paintDevice()
void QBackingStore::endPaint()
{
if (paintDevice()->paintingActive())
- qWarning() << "QBackingStore::endPaint() called with active painter on backingstore paint device";
+ qWarning("QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?");
handle()->endPaint();
}
diff --git a/src/gui/painting/qcolorspace.cpp b/src/gui/painting/qcolorspace.cpp
index 8d3bbbe412..043a951521 100644
--- a/src/gui/painting/qcolorspace.cpp
+++ b/src/gui/painting/qcolorspace.cpp
@@ -55,34 +55,34 @@ QT_BEGIN_NAMESPACE
QBasicMutex QColorSpacePrivate::s_lutWriteLock;
-QColorSpacePrimaries::QColorSpacePrimaries(QColorSpace::Gamut gamut)
+QColorSpacePrimaries::QColorSpacePrimaries(QColorSpace::Primaries primaries)
{
- switch (gamut) {
- case QColorSpace::Gamut::SRgb:
+ switch (primaries) {
+ case QColorSpace::Primaries::SRgb:
redPoint = QPointF(0.640, 0.330);
greenPoint = QPointF(0.300, 0.600);
bluePoint = QPointF(0.150, 0.060);
whitePoint = QColorVector::D65Chromaticity();
break;
- case QColorSpace::Gamut::DciP3D65:
+ case QColorSpace::Primaries::DciP3D65:
redPoint = QPointF(0.680, 0.320);
greenPoint = QPointF(0.265, 0.690);
bluePoint = QPointF(0.150, 0.060);
whitePoint = QColorVector::D65Chromaticity();
break;
- case QColorSpace::Gamut::Bt2020:
+ case QColorSpace::Primaries::Bt2020:
redPoint = QPointF(0.708, 0.292);
greenPoint = QPointF(0.190, 0.797);
bluePoint = QPointF(0.131, 0.046);
whitePoint = QColorVector::D65Chromaticity();
break;
- case QColorSpace::Gamut::AdobeRgb:
+ case QColorSpace::Primaries::AdobeRgb:
redPoint = QPointF(0.640, 0.330);
greenPoint = QPointF(0.210, 0.710);
bluePoint = QPointF(0.150, 0.060);
whitePoint = QColorVector::D65Chromaticity();
break;
- case QColorSpace::Gamut::ProPhotoRgb:
+ case QColorSpace::Primaries::ProPhotoRgb:
redPoint = QPointF(0.7347, 0.2653);
greenPoint = QPointF(0.1596, 0.8404);
bluePoint = QPointF(0.0366, 0.0001);
@@ -153,7 +153,7 @@ QColorMatrix QColorSpacePrimaries::toXyzMatrix() const
QColorSpacePrivate::QColorSpacePrivate()
: id(QColorSpace::Unknown)
- , gamut(QColorSpace::Gamut::Custom)
+ , primaries(QColorSpace::Primaries::Custom)
, transferFunction(QColorSpace::TransferFunction::Custom)
, gamma(0.0f)
, whitePoint(QColorVector::null())
@@ -163,48 +163,43 @@ QColorSpacePrivate::QColorSpacePrivate()
QColorSpacePrivate::QColorSpacePrivate(QColorSpace::ColorSpaceId colorSpaceId)
: id(colorSpaceId)
+ , gamma(0.0f)
{
switch (colorSpaceId) {
case QColorSpace::Undefined:
- gamut = QColorSpace::Gamut::Custom;
+ primaries = QColorSpace::Primaries::Custom;
transferFunction = QColorSpace::TransferFunction::Custom;
- gamma = 0.0f;
description = QStringLiteral("Undefined");
break;
case QColorSpace::SRgb:
- gamut = QColorSpace::Gamut::SRgb;
+ primaries = QColorSpace::Primaries::SRgb;
transferFunction = QColorSpace::TransferFunction::SRgb;
- gamma = 2.31f; // ?
description = QStringLiteral("sRGB");
break;
case QColorSpace::SRgbLinear:
- gamut = QColorSpace::Gamut::SRgb;
+ primaries = QColorSpace::Primaries::SRgb;
transferFunction = QColorSpace::TransferFunction::Linear;
- gamma = 1.0f;
description = QStringLiteral("Linear sRGB");
break;
case QColorSpace::AdobeRgb:
- gamut = QColorSpace::Gamut::AdobeRgb;
+ primaries = QColorSpace::Primaries::AdobeRgb;
transferFunction = QColorSpace::TransferFunction::Gamma;
gamma = 2.19921875f; // Not quite 2.2, see https://www.adobe.com/digitalimag/pdfs/AdobeRGB1998.pdf
description = QStringLiteral("Adobe RGB");
break;
case QColorSpace::DisplayP3:
- gamut = QColorSpace::Gamut::DciP3D65;
+ primaries = QColorSpace::Primaries::DciP3D65;
transferFunction = QColorSpace::TransferFunction::SRgb;
- gamma = 2.31f; // ?
description = QStringLiteral("Display P3");
break;
case QColorSpace::ProPhotoRgb:
- gamut = QColorSpace::Gamut::ProPhotoRgb;
+ primaries = QColorSpace::Primaries::ProPhotoRgb;
transferFunction = QColorSpace::TransferFunction::ProPhotoRgb;
- gamma = 1.8f;
description = QStringLiteral("ProPhoto RGB");
break;
case QColorSpace::Bt2020:
- gamut = QColorSpace::Gamut::Bt2020;
+ primaries = QColorSpace::Primaries::Bt2020;
transferFunction = QColorSpace::TransferFunction::Bt2020;
- gamma = 2.1f; // ?
description = QStringLiteral("BT.2020");
break;
case QColorSpace::Unknown:
@@ -216,8 +211,8 @@ QColorSpacePrivate::QColorSpacePrivate(QColorSpace::ColorSpaceId colorSpaceId)
initialize();
}
-QColorSpacePrivate::QColorSpacePrivate(QColorSpace::Gamut gamut, QColorSpace::TransferFunction fun, float gamma)
- : gamut(gamut)
+QColorSpacePrivate::QColorSpacePrivate(QColorSpace::Primaries primaries, QColorSpace::TransferFunction fun, float gamma)
+ : primaries(primaries)
, transferFunction(fun)
, gamma(gamma)
{
@@ -229,74 +224,81 @@ QColorSpacePrivate::QColorSpacePrivate(QColorSpace::Gamut gamut, QColorSpace::Tr
QColorSpacePrivate::QColorSpacePrivate(const QColorSpacePrimaries &primaries,
QColorSpace::TransferFunction fun,
float gamma)
- : gamut(QColorSpace::Gamut::Custom)
+ : primaries(QColorSpace::Primaries::Custom)
, transferFunction(fun)
, gamma(gamma)
{
Q_ASSERT(primaries.areValid());
toXyz = primaries.toXyzMatrix();
whitePoint = QColorVector(primaries.whitePoint);
- if (!identifyColorSpace())
- id = QColorSpace::Unknown;
+ identifyColorSpace();
setTransferFunction();
}
bool QColorSpacePrivate::identifyColorSpace()
{
- switch (gamut) {
- case QColorSpace::Gamut::SRgb:
+ switch (primaries) {
+ case QColorSpace::Primaries::SRgb:
if (transferFunction == QColorSpace::TransferFunction::SRgb) {
id = QColorSpace::SRgb;
- description = QStringLiteral("sRGB");
+ if (description.isEmpty())
+ description = QStringLiteral("sRGB");
return true;
}
if (transferFunction == QColorSpace::TransferFunction::Linear) {
id = QColorSpace::SRgbLinear;
- description = QStringLiteral("Linear sRGB");
+ if (description.isEmpty())
+ description = QStringLiteral("Linear sRGB");
return true;
}
break;
- case QColorSpace::Gamut::AdobeRgb:
+ case QColorSpace::Primaries::AdobeRgb:
if (transferFunction == QColorSpace::TransferFunction::Gamma) {
if (qAbs(gamma - 2.19921875f) < (1/1024.0f)) {
id = QColorSpace::AdobeRgb;
- description = QStringLiteral("Adobe RGB");
+ if (description.isEmpty())
+ description = QStringLiteral("Adobe RGB");
return true;
}
}
break;
- case QColorSpace::Gamut::DciP3D65:
+ case QColorSpace::Primaries::DciP3D65:
if (transferFunction == QColorSpace::TransferFunction::SRgb) {
id = QColorSpace::DisplayP3;
- description = QStringLiteral("Display P3");
+ if (description.isEmpty())
+ description = QStringLiteral("Display P3");
return true;
}
break;
- case QColorSpace::Gamut::ProPhotoRgb:
+ case QColorSpace::Primaries::ProPhotoRgb:
if (transferFunction == QColorSpace::TransferFunction::ProPhotoRgb) {
id = QColorSpace::ProPhotoRgb;
- description = QStringLiteral("ProPhoto RGB");
+ if (description.isEmpty())
+ description = QStringLiteral("ProPhoto RGB");
return true;
}
if (transferFunction == QColorSpace::TransferFunction::Gamma) {
// ProPhoto RGB's curve is effectively gamma 1.8 for 8bit precision.
if (qAbs(gamma - 1.8f) < (1/1024.0f)) {
id = QColorSpace::ProPhotoRgb;
- description = QStringLiteral("ProPhoto RGB");
+ if (description.isEmpty())
+ description = QStringLiteral("ProPhoto RGB");
return true;
}
}
break;
- case QColorSpace::Gamut::Bt2020:
+ case QColorSpace::Primaries::Bt2020:
if (transferFunction == QColorSpace::TransferFunction::Bt2020) {
id = QColorSpace::Bt2020;
- description = QStringLiteral("BT.2020");
+ if (description.isEmpty())
+ description = QStringLiteral("BT.2020");
return true;
}
break;
default:
break;
}
+ id = QColorSpace::Unknown;
return false;
}
@@ -308,14 +310,14 @@ void QColorSpacePrivate::initialize()
void QColorSpacePrivate::setToXyzMatrix()
{
- if (gamut == QColorSpace::Gamut::Custom) {
+ if (primaries == QColorSpace::Primaries::Custom) {
toXyz = QColorMatrix::null();
whitePoint = QColorVector::D50();
return;
}
- QColorSpacePrimaries primaries(gamut);
- toXyz = primaries.toXyzMatrix();
- whitePoint = QColorVector(primaries.whitePoint);
+ QColorSpacePrimaries colorSpacePrimaries(primaries);
+ toXyz = colorSpacePrimaries.toXyzMatrix();
+ whitePoint = QColorVector(colorSpacePrimaries.whitePoint);
}
void QColorSpacePrivate::setTransferFunction()
@@ -324,6 +326,8 @@ void QColorSpacePrivate::setTransferFunction()
case QColorSpace::TransferFunction::Linear:
trc[0].m_type = QColorTrc::Type::Function;
trc[0].m_fun = QColorTransferFunction();
+ if (qFuzzyIsNull(gamma))
+ gamma = 1.0f;
break;
case QColorSpace::TransferFunction::Gamma:
trc[0].m_type = QColorTrc::Type::Function;
@@ -332,14 +336,20 @@ void QColorSpacePrivate::setTransferFunction()
case QColorSpace::TransferFunction::SRgb:
trc[0].m_type = QColorTrc::Type::Function;
trc[0].m_fun = QColorTransferFunction::fromSRgb();
+ if (qFuzzyIsNull(gamma))
+ gamma = 2.31f;
break;
case QColorSpace::TransferFunction::ProPhotoRgb:
trc[0].m_type = QColorTrc::Type::Function;
trc[0].m_fun = QColorTransferFunction::fromProPhotoRgb();
+ if (qFuzzyIsNull(gamma))
+ gamma = 1.8f;
break;
case QColorSpace::TransferFunction::Bt2020:
trc[0].m_type = QColorTrc::Type::Function;
trc[0].m_fun = QColorTransferFunction::fromBt2020();
+ if (qFuzzyIsNull(gamma))
+ gamma = 1.961f;
break;
case QColorSpace::TransferFunction::Custom:
break;
@@ -355,10 +365,12 @@ QColorTransform QColorSpacePrivate::transformationToColorSpace(const QColorSpace
{
Q_ASSERT(out);
QColorTransform combined;
- combined.d_ptr.reset(new QColorTransformPrivate);
- combined.d_ptr->colorSpaceIn = this;
- combined.d_ptr->colorSpaceOut = out;
- combined.d_ptr->colorMatrix = out->toXyz.inverted() * toXyz;
+ auto ptr = new QColorTransformPrivate;
+ combined.d = ptr;
+ combined.d->ref.ref();
+ ptr->colorSpaceIn = this;
+ ptr->colorSpaceOut = out;
+ ptr->colorMatrix = out->toXyz.inverted() * toXyz;
return combined;
}
@@ -381,12 +393,14 @@ QColorTransform QColorSpacePrivate::transformationToColorSpace(const QColorSpace
QColorSpace can also represent color spaces defined by ICC profiles or embedded
in images, that do not otherwise fit the predefined color spaces.
- A color space can generally speaking be conceived as a combination of a transfer
- function and a gamut. The gamut defines which colors the color space can represent.
- A color space that can represent a wider range of colors is also known as a
- wide-gamut color space. The gamut is defined by three primary colors that represent
- exactly how red, green, and blue look in this particular color space, and a white
- color that represents where and how bright pure white is.
+ A color space can generally speaking be conceived as a combination of set of primary
+ colors and a transfer function. The primaries defines the axes of the color space, and
+ the transfer function how values are mapped on the axes.
+ The primaries are defined by three primary colors that represent exactly how red, green,
+ and blue look in this particular color space, and a white color that represents where
+ and how bright pure white is. The range of colors expressable by the primary colors is
+ called the gamut, and a color space that can represent a wider range of colors is also
+ known as a wide-gamut color space.
The transfer function or gamma curve determines how each component in the
color space is encoded. These are used because human perception does not operate
@@ -418,16 +432,16 @@ QColorTransform QColorSpacePrivate::transformationToColorSpace(const QColorSpace
*/
/*!
- \enum QColorSpace::Gamut
+ \enum QColorSpace::Primaries
- Predefined gamuts, or sets of primary colors.
+ Predefined sets of primary colors.
- \value Custom The gamut is undefined or does not match any predefined sets.
- \value SRgb The sRGB gamut
- \value AdobeRgb The Adobe RGB gamut
- \value DciP3D65 The DCI-P3 gamut with the D65 whitepoint
- \value ProPhotoRgb The ProPhoto RGB gamut with the D50 whitepoint
- \value Bt2020 The BT.2020 gamut
+ \value Custom The primaries are undefined or does not match any predefined sets.
+ \value SRgb The sRGB primaries
+ \value AdobeRgb The Adobe RGB primaries
+ \value DciP3D65 The DCI-P3 primaries with the D65 whitepoint
+ \value ProPhotoRgb The ProPhoto RGB primaries with the D50 whitepoint
+ \value Bt2020 The BT.2020 primaries
*/
/*!
@@ -463,25 +477,25 @@ QColorSpace::QColorSpace(QColorSpace::ColorSpaceId colorSpaceId)
}
/*!
- Creates a custom color space with the gamut \a gamut, using the transfer function \a fun and
+ Creates a custom color space with the primaries \a primaries, using the transfer function \a fun and
optionally \a gamma.
*/
-QColorSpace::QColorSpace(QColorSpace::Gamut gamut, QColorSpace::TransferFunction fun, float gamma)
- : d_ptr(new QColorSpacePrivate(gamut, fun, gamma))
+QColorSpace::QColorSpace(QColorSpace::Primaries primaries, QColorSpace::TransferFunction fun, float gamma)
+ : d_ptr(new QColorSpacePrivate(primaries, fun, gamma))
{
}
/*!
- Creates a custom color space with the gamut \a gamut, using a gamma transfer function of
+ Creates a custom color space with the primaries \a primaries, using a gamma transfer function of
\a gamma.
*/
-QColorSpace::QColorSpace(QColorSpace::Gamut gamut, float gamma)
- : d_ptr(new QColorSpacePrivate(gamut, TransferFunction::Gamma, gamma))
+QColorSpace::QColorSpace(QColorSpace::Primaries primaries, float gamma)
+ : d_ptr(new QColorSpacePrivate(primaries, TransferFunction::Gamma, gamma))
{
}
/*!
- Creates a custom colorspace with a gamut based on the chromaticities of the primary colors \a whitePoint,
+ Creates a custom colorspace with a primaries based on the chromaticities of the primary colors \a whitePoint,
\a redPoint, \a greenPoint and \a bluePoint, and using the transfer function \a fun and optionally \a gamma.
*/
QColorSpace::QColorSpace(const QPointF &whitePoint, const QPointF &redPoint,
@@ -501,7 +515,7 @@ QColorSpace::~QColorSpace()
{
}
-QColorSpace::QColorSpace(QColorSpace &&colorSpace)
+QColorSpace::QColorSpace(QColorSpace &&colorSpace) noexcept
: d_ptr(std::move(colorSpace.d_ptr))
{
}
@@ -511,7 +525,7 @@ QColorSpace::QColorSpace(const QColorSpace &colorSpace)
{
}
-QColorSpace &QColorSpace::operator=(QColorSpace &&colorSpace)
+QColorSpace &QColorSpace::operator=(QColorSpace &&colorSpace) noexcept
{
d_ptr = std::move(colorSpace.d_ptr);
return *this;
@@ -523,6 +537,12 @@ QColorSpace &QColorSpace::operator=(const QColorSpace &colorSpace)
return *this;
}
+/*! \fn void QColorSpace::swap(QColorSpace &other)
+
+ Swaps color space \a other with this color space. This operation is very fast and
+ never fails.
+*/
+
/*!
Returns the id of the predefined color space this object
represents or \c Unknown if it doesn't match any of them.
@@ -533,19 +553,21 @@ QColorSpace::ColorSpaceId QColorSpace::colorSpaceId() const noexcept
}
/*!
- Returns the predefined gamut of the color space
- or \c Gamut::Custom if it doesn't match any of them.
+ Returns the predefined primaries of the color space
+ or \c primaries::Custom if it doesn't match any of them.
*/
-QColorSpace::Gamut QColorSpace::gamut() const noexcept
+QColorSpace::Primaries QColorSpace::primaries() const noexcept
{
- return d_ptr->gamut;
+ if (Q_UNLIKELY(!d_ptr))
+ return QColorSpace::Primaries::Custom;
+ return d_ptr->primaries;
}
/*!
Returns the predefined transfer function of the color space
or \c TransferFunction::Custom if it doesn't match any of them.
- \sa gamma()
+ \sa gamma(), setTransferFunction(), withTransferFunction()
*/
QColorSpace::TransferFunction QColorSpace::transferFunction() const noexcept
{
@@ -565,6 +587,91 @@ float QColorSpace::gamma() const noexcept
}
/*!
+ Sets the transfer function to \a transferFunction and \a gamma.
+
+ \note This also changes colorSpaceId().
+
+ \sa transferFunction(), gamma(), withTransferFunction()
+*/
+void QColorSpace::setTransferFunction(QColorSpace::TransferFunction transferFunction, float gamma)
+{
+ if (!isValid() || transferFunction == QColorSpace::TransferFunction::Custom)
+ return;
+ if (d_ptr->transferFunction == transferFunction && d_ptr->gamma == gamma)
+ return;
+ d_ptr.detach();
+ d_ptr->description.clear();
+ d_ptr->transferFunction = transferFunction;
+ d_ptr->gamma = gamma;
+ d_ptr->identifyColorSpace();
+ d_ptr->setTransferFunction();
+}
+
+/*!
+ Returns a copy of this color space, except using the transfer function
+ \a transferFunction and \a gamma.
+
+ \sa transferFunction(), gamma(), setTransferFunction()
+*/
+QColorSpace QColorSpace::withTransferFunction(QColorSpace::TransferFunction transferFunction, float gamma) const
+{
+ if (!isValid() || transferFunction == QColorSpace::TransferFunction::Custom)
+ return *this;
+ if (d_ptr->transferFunction == transferFunction && d_ptr->gamma == gamma)
+ return *this;
+ QColorSpace out(*this);
+ out.setTransferFunction(transferFunction, gamma);
+ return out;
+}
+
+/*!
+ Sets the primaries to those of the \a primariesId set.
+
+ \note This also changes colorSpaceId().
+
+ \sa primaries()
+*/
+void QColorSpace::setPrimaries(QColorSpace::Primaries primariesId)
+{
+ if (!isValid() || primariesId == QColorSpace::Primaries::Custom)
+ return;
+ if (d_ptr->primaries == primariesId)
+ return;
+ d_ptr.detach();
+ d_ptr->description.clear();
+ d_ptr->primaries = primariesId;
+ d_ptr->identifyColorSpace();
+ d_ptr->setToXyzMatrix();
+}
+
+/*!
+ Set primaries to the chromaticities of \a whitePoint, \a redPoint, \a greenPoint
+ and \a bluePoint.
+
+ \note This also changes colorSpaceId().
+
+ \sa primaries()
+*/
+void QColorSpace::setPrimaries(const QPointF &whitePoint, const QPointF &redPoint,
+ const QPointF &greenPoint, const QPointF &bluePoint)
+{
+ if (!isValid())
+ return;
+ QColorSpacePrimaries primaries(whitePoint, redPoint, greenPoint, bluePoint);
+ if (!primaries.areValid())
+ return;
+ QColorMatrix toXyz = primaries.toXyzMatrix();
+ if (QColorVector(primaries.whitePoint) == d_ptr->whitePoint && toXyz == d_ptr->toXyz)
+ return;
+ d_ptr.detach();
+ d_ptr->description.clear();
+ d_ptr->primaries = QColorSpace::Primaries::Custom;
+ d_ptr->toXyz = toXyz;
+ d_ptr->whitePoint = QColorVector(primaries.whitePoint);
+ d_ptr->identifyColorSpace();
+}
+
+/*!
Returns an ICC profile representing the color space.
If the color space was generated from an ICC profile, that profile
@@ -631,8 +738,8 @@ bool operator==(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2)
if (colorSpace1.colorSpaceId() != QColorSpace::Unknown && colorSpace2.colorSpaceId() != QColorSpace::Unknown)
return colorSpace1.colorSpaceId() == colorSpace2.colorSpaceId();
- if (colorSpace1.gamut() != QColorSpace::Gamut::Custom && colorSpace2.gamut() != QColorSpace::Gamut::Custom) {
- if (colorSpace1.gamut() != colorSpace2.gamut())
+ if (colorSpace1.primaries() != QColorSpace::Primaries::Custom && colorSpace2.primaries() != QColorSpace::Primaries::Custom) {
+ if (colorSpace1.primaries() != colorSpace2.primaries())
return false;
} else {
if (colorSpace1.d_ptr->toXyz != colorSpace2.d_ptr->toXyz)
@@ -644,7 +751,7 @@ bool operator==(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2)
if (colorSpace1.transferFunction() != colorSpace2.transferFunction())
return false;
if (colorSpace1.transferFunction() == QColorSpace::TransferFunction::Gamma)
- return colorSpace1.gamma() == colorSpace2.gamma();
+ return (qAbs(colorSpace1.gamma() - colorSpace2.gamma()) <= (1.0f / 512.0f));
return true;
}
@@ -676,20 +783,6 @@ QColorTransform QColorSpace::transformationToColorSpace(const QColorSpace &color
return d_ptr->transformationToColorSpace(colorspace.d_ptr.constData());
}
-/*!
- \internal
-*/
-QColorSpacePrivate *QColorSpace::d_func()
-{
- d_ptr.detach();
- return d_ptr.data();
-}
-
-/*!
- \fn const QColorSpacePrivate* QColorSpacePrivate::d_func() const
- \internal
-*/
-
/*****************************************************************************
QColorSpace stream functions
*****************************************************************************/
@@ -734,7 +827,7 @@ QDebug operator<<(QDebug dbg, const QColorSpace &colorSpace)
QDebugStateSaver saver(dbg);
dbg.nospace();
dbg << "QColorSpace(";
- dbg << colorSpace.colorSpaceId() << ", " << colorSpace.gamut() << ", " << colorSpace.transferFunction();
+ dbg << colorSpace.colorSpaceId() << ", " << colorSpace.primaries() << ", " << colorSpace.transferFunction();
dbg << ", gamma=" << colorSpace.gamma();
dbg << ')';
return dbg;
diff --git a/src/gui/painting/qcolorspace.h b/src/gui/painting/qcolorspace.h
index 56676826a9..a7c1091911 100644
--- a/src/gui/painting/qcolorspace.h
+++ b/src/gui/painting/qcolorspace.h
@@ -63,7 +63,7 @@ public:
Bt2020,
};
Q_ENUM(ColorSpaceId)
- enum class Gamut {
+ enum class Primaries {
Custom = 0,
SRgb,
AdobeRgb,
@@ -71,7 +71,7 @@ public:
ProPhotoRgb,
Bt2020,
};
- Q_ENUM(Gamut)
+ Q_ENUM(Primaries)
enum class TransferFunction {
Custom = 0,
Linear,
@@ -83,23 +83,33 @@ public:
Q_ENUM(TransferFunction)
QColorSpace(ColorSpaceId colorSpaceId = Undefined);
- QColorSpace(Gamut gamut, TransferFunction fun, float gamma = 0.0f);
- QColorSpace(Gamut gamut, float gamma);
+ QColorSpace(Primaries primaries, TransferFunction fun, float gamma = 0.0f);
+ QColorSpace(Primaries primaries, float gamma);
QColorSpace(const QPointF &whitePoint, const QPointF &redPoint,
const QPointF &greenPoint, const QPointF &bluePoint,
TransferFunction fun, float gamma = 0.0f);
~QColorSpace();
- QColorSpace(QColorSpace &&colorSpace);
+ QColorSpace(QColorSpace &&colorSpace) noexcept;
QColorSpace(const QColorSpace &colorSpace);
- QColorSpace &operator=(QColorSpace &&colorSpace);
+ QColorSpace &operator=(QColorSpace &&colorSpace) noexcept;
QColorSpace &operator=(const QColorSpace &colorSpace);
+ void swap(QColorSpace &colorSpace) noexcept
+ { qSwap(d_ptr, colorSpace.d_ptr); }
+
ColorSpaceId colorSpaceId() const noexcept;
- Gamut gamut() const noexcept;
+ Primaries primaries() const noexcept;
TransferFunction transferFunction() const noexcept;
float gamma() const noexcept;
+ void setTransferFunction(TransferFunction transferFunction, float gamma = 0.0f);
+ QColorSpace withTransferFunction(TransferFunction transferFunction, float gamma = 0.0f) const;
+
+ void setPrimaries(Primaries primariesId);
+ void setPrimaries(const QPointF &whitePoint, const QPointF &redPoint,
+ const QPointF &greenPoint, const QPointF &bluePoint);
+
bool isValid() const noexcept;
friend Q_GUI_EXPORT bool operator==(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2);
@@ -110,11 +120,9 @@ public:
QColorTransform transformationToColorSpace(const QColorSpace &colorspace) const;
- QColorSpacePrivate *d_func();
- inline const QColorSpacePrivate *d_func() const { return d_ptr.constData(); }
private:
- friend class QColorSpacePrivate;
+ Q_DECLARE_PRIVATE(QColorSpace)
QExplicitlySharedDataPointer<QColorSpacePrivate> d_ptr;
};
@@ -124,6 +132,8 @@ inline bool operator!=(const QColorSpace &colorSpace1, const QColorSpace &colorS
return !(colorSpace1 == colorSpace2);
}
+Q_DECLARE_SHARED(QColorSpace)
+
// QColorSpace stream functions
#if !defined(QT_NO_DATASTREAM)
Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QColorSpace &);
diff --git a/src/gui/painting/qcolorspace_p.h b/src/gui/painting/qcolorspace_p.h
index 95e0655d0c..2a40a0cfd8 100644
--- a/src/gui/painting/qcolorspace_p.h
+++ b/src/gui/painting/qcolorspace_p.h
@@ -66,7 +66,7 @@ class Q_GUI_EXPORT QColorSpacePrimaries
{
public:
QColorSpacePrimaries() = default;
- QColorSpacePrimaries(QColorSpace::Gamut gamut);
+ QColorSpacePrimaries(QColorSpace::Primaries primaries);
QColorSpacePrimaries(QPointF whitePoint,
QPointF redPoint,
QPointF greenPoint,
@@ -91,10 +91,21 @@ class QColorSpacePrivate : public QSharedData
public:
QColorSpacePrivate();
QColorSpacePrivate(QColorSpace::ColorSpaceId colorSpaceId);
- QColorSpacePrivate(QColorSpace::Gamut gamut, QColorSpace::TransferFunction fun, float gamma);
+ QColorSpacePrivate(QColorSpace::Primaries primaries, QColorSpace::TransferFunction fun, float gamma);
QColorSpacePrivate(const QColorSpacePrimaries &primaries, QColorSpace::TransferFunction fun, float gamma);
QColorSpacePrivate(const QColorSpacePrivate &other) = default;
+ static QColorSpacePrivate *getWritable(QColorSpace &colorSpace)
+ {
+ colorSpace.d_ptr.detach();
+ return colorSpace.d_ptr.data();
+ }
+
+ static const QColorSpacePrivate *get(const QColorSpace &colorSpace)
+ {
+ return colorSpace.d_ptr.data();
+ }
+
void initialize();
void setToXyzMatrix();
void setTransferFunction();
@@ -102,7 +113,7 @@ public:
QColorTransform transformationToColorSpace(const QColorSpacePrivate *out) const;
QColorSpace::ColorSpaceId id;
- QColorSpace::Gamut gamut;
+ QColorSpace::Primaries primaries;
QColorSpace::TransferFunction transferFunction;
float gamma;
QColorVector whitePoint;
diff --git a/src/gui/painting/qcolortransform.cpp b/src/gui/painting/qcolortransform.cpp
index de08bf4221..53fd1dfbaa 100644
--- a/src/gui/painting/qcolortransform.cpp
+++ b/src/gui/painting/qcolortransform.cpp
@@ -134,8 +134,18 @@ void QColorTransformPrivate::updateLutsOut() const
*/
-QColorTransform::~QColorTransform() noexcept
+QColorTransform::QColorTransform(const QColorTransform &colorTransform) noexcept
+ : d(colorTransform.d)
{
+ if (d)
+ d->ref.ref();
+}
+
+
+QColorTransform::~QColorTransform()
+{
+ if (d && !d->ref.deref())
+ delete d;
}
/*!
@@ -143,11 +153,10 @@ QColorTransform::~QColorTransform() noexcept
The input should be opaque or unpremultiplied.
*/
-QRgb QColorTransform::map(const QRgb &argb) const
+QRgb QColorTransform::map(QRgb argb) const
{
- if (!d_ptr)
+ if (!d)
return argb;
- Q_D(const QColorTransform);
constexpr float f = 1.0f / 255.0f;
QColorVector c = { qRed(argb) * f, qGreen(argb) * f, qBlue(argb) * f };
c.x = d->colorSpaceIn->trc[0].apply(c.x);
@@ -175,11 +184,10 @@ QRgb QColorTransform::map(const QRgb &argb) const
The input should be opaque or unpremultiplied.
*/
-QRgba64 QColorTransform::map(const QRgba64 &rgba64) const
+QRgba64 QColorTransform::map(QRgba64 rgba64) const
{
- if (!d_ptr)
+ if (!d)
return rgba64;
- Q_D(const QColorTransform);
constexpr float f = 1.0f / 65535.0f;
QColorVector c = { rgba64.red() * f, rgba64.green() * f, rgba64.blue() * f };
c.x = d->colorSpaceIn->trc[0].apply(c.x);
@@ -208,9 +216,8 @@ QRgba64 QColorTransform::map(const QRgba64 &rgba64) const
*/
QColor QColorTransform::map(const QColor &color) const
{
- if (!d_ptr)
+ if (!d)
return color;
- Q_D(const QColorTransform);
QColor clr = color;
if (color.spec() != QColor::ExtendedRgb || color.spec() != QColor::Rgb)
clr = clr.toRgb();
@@ -228,7 +235,7 @@ QColor QColorTransform::map(const QColor &color) const
c = d->colorMatrix.map(c);
bool inGamut = c.x >= 0.0f && c.x <= 1.0f && c.y >= 0.0f && c.y <= 1.0f && c.z >= 0.0f && c.z <= 1.0f;
if (inGamut) {
- if (d_ptr->colorSpaceOut->lut.generated.loadAcquire()) {
+ if (d->colorSpaceOut->lut.generated.loadAcquire()) {
c.x = d->colorSpaceOut->lut[0]->fromLinear(c.x);
c.y = d->colorSpaceOut->lut[1]->fromLinear(c.y);
c.z = d->colorSpaceOut->lut[2]->fromLinear(c.z);
diff --git a/src/gui/painting/qcolortransform.h b/src/gui/painting/qcolortransform.h
index 9274387b97..5fb51739a7 100644
--- a/src/gui/painting/qcolortransform.h
+++ b/src/gui/painting/qcolortransform.h
@@ -51,43 +51,42 @@ class QRgba64;
class QColorSpacePrivate;
class QColorTransformPrivate;
-class Q_GUI_EXPORT QColorTransform
+class QColorTransform
{
public:
- QColorTransform() noexcept : d_ptr(nullptr) { }
- ~QColorTransform() noexcept;
- QColorTransform(const QColorTransform &colorTransform) noexcept
- : d_ptr(colorTransform.d_ptr)
- { }
+ QColorTransform() noexcept : d(nullptr) { }
+ Q_GUI_EXPORT ~QColorTransform();
+ Q_GUI_EXPORT QColorTransform(const QColorTransform &colorTransform) noexcept;
QColorTransform(QColorTransform &&colorTransform) noexcept
- : d_ptr(std::move(colorTransform.d_ptr))
+ : d{qExchange(colorTransform.d, nullptr)}
{ }
QColorTransform &operator=(const QColorTransform &other) noexcept
{
- d_ptr = other.d_ptr;
+ QColorTransform{other}.swap(*this);
return *this;
}
QColorTransform &operator=(QColorTransform &&other) noexcept
{
- d_ptr = std::move(other.d_ptr);
+ QColorTransform{std::move(other)}.swap(*this);
return *this;
}
- bool isNull() const { return d_ptr.isNull(); }
+ void swap(QColorTransform &other) noexcept { qSwap(d, other.d); }
- QRgb map(const QRgb &argb) const;
- QRgba64 map(const QRgba64 &rgba64) const;
- QColor map(const QColor &color) const;
+ Q_GUI_EXPORT QRgb map(QRgb argb) const;
+ Q_GUI_EXPORT QRgba64 map(QRgba64 rgba64) const;
+ Q_GUI_EXPORT QColor map(const QColor &color) const;
private:
friend class QColorSpace;
friend class QColorSpacePrivate;
friend class QImage;
- Q_DECLARE_PRIVATE(QColorTransform)
- QSharedPointer<QColorTransformPrivate> d_ptr;
+ const QColorTransformPrivate *d;
};
+Q_DECLARE_SHARED(QColorTransform)
+
QT_END_NAMESPACE
#endif // QCOLORTRANSFORM_H
diff --git a/src/gui/painting/qcolortransform_p.h b/src/gui/painting/qcolortransform_p.h
index 74a1e7fe0a..5d7116248d 100644
--- a/src/gui/painting/qcolortransform_p.h
+++ b/src/gui/painting/qcolortransform_p.h
@@ -54,9 +54,11 @@
#include "qcolormatrix_p.h"
#include "qcolorspace_p.h"
+#include <QtCore/qshareddata.h>
+
QT_BEGIN_NAMESPACE
-class QColorTransformPrivate
+class QColorTransformPrivate : public QSharedData
{
public:
QColorMatrix colorMatrix;
diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp
index 006befea22..674f52678d 100644
--- a/src/gui/painting/qdrawhelper.cpp
+++ b/src/gui/painting/qdrawhelper.cpp
@@ -2053,6 +2053,23 @@ inline void fetchTransformed_pixelBounds(int max, int l1, int l2, int &v)
}
}
+static inline bool canUseFastMatrixPath(const qreal cx, const qreal cy, const qsizetype length, const QSpanData *data)
+{
+ if (Q_UNLIKELY(!data->fast_matrix))
+ return false;
+
+ qreal fx = (data->m21 * cy + data->m11 * cx + data->dx) * fixed_scale;
+ qreal fy = (data->m22 * cy + data->m12 * cx + data->dy) * fixed_scale;
+ qreal minc = std::min(fx, fy);
+ qreal maxc = std::max(fx, fy);
+ fx += std::trunc(data->m11 * fixed_scale) * length;
+ fy += std::trunc(data->m12 * fixed_scale) * length;
+ minc = std::min(minc, std::min(fx, fy));
+ maxc = std::max(maxc, std::max(fx, fy));
+
+ return minc >= std::numeric_limits<int>::min() && maxc <= std::numeric_limits<int>::max();
+}
+
template<TextureBlendType blendType, QPixelLayout::BPP bpp, typename T>
static void QT_FASTCALL fetchTransformed_fetcher(T *buffer, const QSpanData *data,
int y, int x, int length)
@@ -2070,7 +2087,7 @@ static void QT_FASTCALL fetchTransformed_fetcher(T *buffer, const QSpanData *dat
// When templated 'fetch' should be inlined at compile time:
const FetchPixelFunc fetch = (bpp == QPixelLayout::BPPNone) ? qFetchPixel[layout->bpp] : FetchPixelFunc(fetchPixel<bpp>);
- if (data->fast_matrix) {
+ if (canUseFastMatrixPath(cx, cy, length, data)) {
// The increment pr x in the scanline
int fdx = (int)(data->m11 * fixed_scale);
int fdy = (int)(data->m12 * fixed_scale);
@@ -3026,7 +3043,7 @@ static const uint * QT_FASTCALL fetchTransformedBilinearARGB32PM(uint *buffer, c
uint *end = buffer + length;
uint *b = buffer;
- if (data->fast_matrix) {
+ if (canUseFastMatrixPath(cx, cy, length, data)) {
// The increment pr x in the scanline
int fdx = (int)(data->m11 * fixed_scale);
int fdy = (int)(data->m12 * fixed_scale);
@@ -3383,7 +3400,7 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper
const qreal cx = x + qreal(0.5);
const qreal cy = y + qreal(0.5);
- if (data->fast_matrix) {
+ if (canUseFastMatrixPath(cx, cy, length, data)) {
// The increment pr x in the scanline
int fdx = (int)(data->m11 * fixed_scale);
int fdy = (int)(data->m12 * fixed_scale);
@@ -3570,7 +3587,7 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64_uint32(QRgba64 *buf
QRgba64 *end = buffer + length;
QRgba64 *b = buffer;
- if (data->fast_matrix) {
+ if (canUseFastMatrixPath(cx, cy, length, data)) {
// The increment pr x in the scanline
const int fdx = (int)(data->m11 * fixed_scale);
const int fdy = (int)(data->m12 * fixed_scale);
@@ -3728,7 +3745,7 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64_uint64(QRgba64 *buf
QRgba64 *end = buffer + length;
QRgba64 *b = buffer;
- if (data->fast_matrix) {
+ if (canUseFastMatrixPath(cx, cy, length, data)) {
// The increment pr x in the scanline
const int fdx = (int)(data->m11 * fixed_scale);
const int fdy = (int)(data->m12 * fixed_scale);
diff --git a/src/gui/painting/qicc.cpp b/src/gui/painting/qicc.cpp
index d88b005782..6cb7b57493 100644
--- a/src/gui/painting/qicc.cpp
+++ b/src/gui/painting/qicc.cpp
@@ -42,8 +42,9 @@
#include <qbuffer.h>
#include <qbytearray.h>
#include <qdatastream.h>
-#include <qloggingcategory.h>
#include <qendian.h>
+#include <qloggingcategory.h>
+#include <qstring.h>
#include "qcolorspace_p.h"
#include "qcolortrc_p.h"
@@ -117,6 +118,7 @@ enum class Tag : quint32 {
bkpt = IccTag('b', 'k', 'p', 't'),
mft1 = IccTag('m', 'f', 't', '1'),
mft2 = IccTag('m', 'f', 't', '2'),
+ mluc = IccTag('m', 'l', 'u', 'c'),
mAB_ = IccTag('m', 'A', 'B', ' '),
mBA_ = IccTag('m', 'B', 'A', ' '),
chad = IccTag('c', 'h', 'a', 'd'),
@@ -164,6 +166,25 @@ struct ParaTagData : GenericTagData {
quint32_be parameter[1];
};
+struct DescTagData : GenericTagData {
+ quint32_be asciiDescriptionLength;
+ char asciiDescription[1];
+ // .. we ignore the rest
+};
+
+struct MlucTagRecord {
+ quint16_be languageCode;
+ quint16_be countryCode;
+ quint32_be size;
+ quint32_be offset;
+};
+
+struct MlucTagData : GenericTagData {
+ quint32_be recordCount;
+ quint32_be recordSize; // = sizeof(MlucTagRecord)
+ MlucTagRecord records[1];
+};
+
// For both mAB and mBA
struct mABTagData : GenericTagData {
quint8 inputChannels;
@@ -190,23 +211,17 @@ static float fromFixedS1516(int x)
return x * (1.0f / 65536.0f);
}
-QColorVector fromXyzData(const XYZTagData *xyz)
-{
- const float x = fromFixedS1516(xyz->fixedX);
- const float y = fromFixedS1516(xyz->fixedY);
- const float z = fromFixedS1516(xyz->fixedZ);
- qCDebug(lcIcc) << "XYZ_ " << x << y << z;
-
- return QColorVector(x, y, z);
-}
-
static bool isValidIccProfile(const ICCProfileHeader &header)
{
if (header.signature != uint(Tag::acsp)) {
qCWarning(lcIcc, "Failed ICC signature test");
return false;
}
- if (header.profileSize < (sizeof(ICCProfileHeader) + header.tagCount * sizeof(TagTableEntry))) {
+
+ // Don't overflow 32bit integers:
+ if (header.tagCount >= INT32_MAX / sizeof(TagTableEntry))
+ return false;
+ if (header.profileSize - sizeof(ICCProfileHeader) < header.tagCount * sizeof(TagTableEntry)) {
qCWarning(lcIcc, "Failed basic size sanity");
return false;
}
@@ -288,7 +303,7 @@ QByteArray toIccProfile(const QColorSpace &space)
if (!space.isValid())
return QByteArray();
- const QColorSpacePrivate *spaceDPtr = space.d_func();
+ const QColorSpacePrivate *spaceDPtr = QColorSpacePrivate::get(space);
constexpr int tagCount = 9;
constexpr uint profileDataOffset = 128 + 4 + 12 * tagCount;
@@ -413,17 +428,44 @@ QByteArray toIccProfile(const QColorSpace &space)
return iccProfile;
}
-bool parseTRC(const GenericTagData *trcData, QColorTrc &gamma)
+struct TagEntry {
+ quint32 offset;
+ quint32 size;
+};
+
+bool parseXyzData(const QByteArray &data, const TagEntry &tagEntry, QColorVector &colorVector)
+{
+ if (tagEntry.size < sizeof(XYZTagData)) {
+ qCWarning(lcIcc) << "Undersized XYZ tag";
+ return false;
+ }
+ const XYZTagData *xyz = reinterpret_cast<const XYZTagData *>(data.constData() + tagEntry.offset);
+ if (xyz->type != quint32(Tag::XYZ_)) {
+ qCWarning(lcIcc) << "Bad XYZ content type";
+ return false;
+ }
+ const float x = fromFixedS1516(xyz->fixedX);
+ const float y = fromFixedS1516(xyz->fixedY);
+ const float z = fromFixedS1516(xyz->fixedZ);
+
+ colorVector = QColorVector(x, y, z);
+ return true;
+}
+
+bool parseTRC(const QByteArray &data, const TagEntry &tagEntry, QColorTrc &gamma)
{
+ const GenericTagData *trcData = reinterpret_cast<const GenericTagData *>(data.constData() + tagEntry.offset);
if (trcData->type == quint32(Tag::curv)) {
- const CurvTagData *curv = reinterpret_cast<const CurvTagData *>(trcData);
- qCDebug(lcIcc) << "curv" << uint(curv->valueCount);
+ const CurvTagData *curv = static_cast<const CurvTagData *>(trcData);
+ if (curv->valueCount > (1 << 16))
+ return false;
+ if (tagEntry.size - 12 < 2 * curv->valueCount)
+ return false;
if (curv->valueCount == 0) {
gamma.m_type = QColorTrc::Type::Function;
gamma.m_fun = QColorTransferFunction(); // Linear
} else if (curv->valueCount == 1) {
float g = curv->value[0] * (1.0f / 256.0f);
- qCDebug(lcIcc) << g;
gamma.m_type = QColorTrc::Type::Function;
gamma.m_fun = QColorTransferFunction::fromGamma(g);
} else {
@@ -445,49 +487,54 @@ bool parseTRC(const GenericTagData *trcData, QColorTrc &gamma)
return true;
}
if (trcData->type == quint32(Tag::para)) {
- const ParaTagData *para = reinterpret_cast<const ParaTagData *>(trcData);
- qCDebug(lcIcc) << "para" << uint(para->curveType);
+ if (tagEntry.size < sizeof(ParaTagData))
+ return false;
+ const ParaTagData *para = static_cast<const ParaTagData *>(trcData);
switch (para->curveType) {
case 0: {
float g = fromFixedS1516(para->parameter[0]);
- qCDebug(lcIcc) << g;
gamma.m_type = QColorTrc::Type::Function;
gamma.m_fun = QColorTransferFunction::fromGamma(g);
break;
}
case 1: {
+ if (tagEntry.size < sizeof(ParaTagData) + 2 * 4)
+ return false;
float g = fromFixedS1516(para->parameter[0]);
float a = fromFixedS1516(para->parameter[1]);
float b = fromFixedS1516(para->parameter[2]);
float d = -b / a;
- qCDebug(lcIcc) << g << a << b;
gamma.m_type = QColorTrc::Type::Function;
gamma.m_fun = QColorTransferFunction(a, b, 0.0f, d, 0.0f, 0.0f, g);
break;
}
case 2: {
+ if (tagEntry.size < sizeof(ParaTagData) + 3 * 4)
+ return false;
float g = fromFixedS1516(para->parameter[0]);
float a = fromFixedS1516(para->parameter[1]);
float b = fromFixedS1516(para->parameter[2]);
float c = fromFixedS1516(para->parameter[3]);
float d = -b / a;
- qCDebug(lcIcc) << g << a << b << c;
gamma.m_type = QColorTrc::Type::Function;
gamma.m_fun = QColorTransferFunction(a, b, 0.0f, d, c, c, g);
break;
}
case 3: {
+ if (tagEntry.size < sizeof(ParaTagData) + 4 * 4)
+ return false;
float g = fromFixedS1516(para->parameter[0]);
float a = fromFixedS1516(para->parameter[1]);
float b = fromFixedS1516(para->parameter[2]);
float c = fromFixedS1516(para->parameter[3]);
float d = fromFixedS1516(para->parameter[4]);
- qCDebug(lcIcc) << g << a << b << c << d;
gamma.m_type = QColorTrc::Type::Function;
gamma.m_fun = QColorTransferFunction(a, b, c, d, 0.0f, 0.0f, g);
break;
}
case 4: {
+ if (tagEntry.size < sizeof(ParaTagData) + 6 * 4)
+ return false;
float g = fromFixedS1516(para->parameter[0]);
float a = fromFixedS1516(para->parameter[1]);
float b = fromFixedS1516(para->parameter[2]);
@@ -495,7 +542,6 @@ bool parseTRC(const GenericTagData *trcData, QColorTrc &gamma)
float d = fromFixedS1516(para->parameter[4]);
float e = fromFixedS1516(para->parameter[5]);
float f = fromFixedS1516(para->parameter[6]);
- qCDebug(lcIcc) << g << a << b << c << d << e << f;
gamma.m_type = QColorTrc::Type::Function;
gamma.m_fun = QColorTransferFunction(a, b, c, d, e, f, g);
break;
@@ -510,6 +556,53 @@ bool parseTRC(const GenericTagData *trcData, QColorTrc &gamma)
return false;
}
+bool parseDesc(const QByteArray &data, const TagEntry &tagEntry, QString &descName)
+{
+ const GenericTagData *tag = (const GenericTagData *)(data.constData() + tagEntry.offset);
+
+ // Either 'desc' (ICCv2) or 'mluc' (ICCv4)
+ if (tag->type == quint32(Tag::desc)) {
+ if (tagEntry.size < sizeof(DescTagData))
+ return false;
+ const DescTagData *desc = (const DescTagData *)(data.constData() + tagEntry.offset);
+ const quint32 len = desc->asciiDescriptionLength;
+ if (len < 1)
+ return false;
+ if (tagEntry.size - 12 < len)
+ return false;
+ if (desc->asciiDescription[len - 1] != '\0')
+ return false;
+ descName = QString::fromLatin1(desc->asciiDescription, len - 1);
+ return true;
+ }
+ if (tag->type != quint32(Tag::mluc))
+ return false;
+
+ if (tagEntry.size < sizeof(MlucTagData))
+ return false;
+ const MlucTagData *mluc = (const MlucTagData *)(data.constData() + tagEntry.offset);
+ if (mluc->recordCount < 1)
+ return false;
+ if (mluc->recordSize < 12)
+ return false;
+ // We just use the primary record regardless of language or country.
+ const quint32 stringOffset = mluc->records[0].offset;
+ const quint32 stringSize = mluc->records[0].size;
+ if (tagEntry.size < stringOffset || tagEntry.size - stringOffset < stringSize )
+ return false;
+ if ((stringSize | stringOffset) & 1)
+ return false;
+ quint32 stringLen = stringSize / 2;
+ const ushort *unicodeString = (const ushort *)(data.constData() + tagEntry.offset + stringOffset);
+ // The given length shouldn't include 0-termination, but might.
+ if (stringLen > 1 && unicodeString[stringLen - 1] == 0)
+ --stringLen;
+ QVarLengthArray<quint16> utf16hostendian(stringLen);
+ qFromBigEndian<ushort>(unicodeString, stringLen, utf16hostendian.data());
+ descName = QString::fromUtf16(utf16hostendian.data(), stringLen);
+ return true;
+}
+
bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace)
{
if (data.size() < qsizetype(sizeof(ICCProfileHeader))) {
@@ -529,8 +622,12 @@ bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace)
// Read tag index
const TagTableEntry *tagTable = (const TagTableEntry *)(data.constData() + sizeof(ICCProfileHeader));
const qsizetype offsetToData = sizeof(ICCProfileHeader) + header->tagCount * sizeof(TagTableEntry);
+ if (offsetToData > data.size()) {
+ qCWarning(lcIcc) << "fromIccProfile: failed index size sanity";
+ return false;
+ }
- QHash<Tag, quint32> tagIndex;
+ QHash<Tag, TagEntry> tagIndex;
for (uint i = 0; i < header->tagCount; ++i) {
// Sanity check tag sizes and offsets:
if (qsizetype(tagTable[i].offset) < offsetToData) {
@@ -542,15 +639,24 @@ bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace)
qCWarning(lcIcc) << "fromIccProfile: failed tag offset sanity 2";
return false;
}
- if ((tagTable[i].offset + tagTable[i].size) > header->profileSize) {
+ if (tagTable[i].size < 12) {
+ qCWarning(lcIcc) << "fromIccProfile: failed minimal tag size sanity";
+ return false;
+ }
+ if (tagTable[i].size > header->profileSize - tagTable[i].offset) {
qCWarning(lcIcc) << "fromIccProfile: failed tag offset + size sanity";
return false;
}
+ if (tagTable[i].offset & 0x03) {
+ qCWarning(lcIcc) << "fromIccProfile: invalid tag offset alignment";
+ return false;
+ }
// printf("'%4s' %d %d\n", (const char *)&tagTable[i].signature,
// quint32(tagTable[i].offset),
// quint32(tagTable[i].size));
- tagIndex.insert(Tag(quint32(tagTable[i].signature)), tagTable[i].offset);
+ tagIndex.insert(Tag(quint32(tagTable[i].signature)), { tagTable[i].offset, tagTable[i].size });
}
+
// 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) ||
@@ -559,70 +665,70 @@ bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace)
return false;
}
+ QColorSpacePrivate *colorspaceDPtr = QColorSpacePrivate::getWritable(*colorSpace);
+
// Parse XYZ tags
- const XYZTagData *rXyz = (const XYZTagData *)(data.constData() + tagIndex[Tag::rXYZ]);
- const XYZTagData *gXyz = (const XYZTagData *)(data.constData() + tagIndex[Tag::gXYZ]);
- const XYZTagData *bXyz = (const XYZTagData *)(data.constData() + tagIndex[Tag::bXYZ]);
- const XYZTagData *wXyz = (const XYZTagData *)(data.constData() + tagIndex[Tag::wtpt]);
- if (rXyz->type != quint32(Tag::XYZ_) || gXyz->type != quint32(Tag::XYZ_) ||
- wXyz->type != quint32(Tag::XYZ_) || wXyz->type != quint32(Tag::XYZ_)) {
- qCWarning(lcIcc) << "fromIccProfile: Bad XYZ data type";
+ 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;
- }
- QColorSpacePrivate *colorspaceDPtr = colorSpace->d_func();
-
- colorspaceDPtr->toXyz.r = fromXyzData(rXyz);
- colorspaceDPtr->toXyz.g = fromXyzData(gXyz);
- colorspaceDPtr->toXyz.b = fromXyzData(bXyz);
- QColorVector whitePoint = fromXyzData(wXyz);
- colorspaceDPtr->whitePoint = whitePoint;
- colorspaceDPtr->gamut = QColorSpace::Gamut::Custom;
+ colorspaceDPtr->primaries = QColorSpace::Primaries::Custom;
if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromSRgb()) {
- qCDebug(lcIcc) << "fromIccProfile: sRGB gamut detected";
- colorspaceDPtr->gamut = QColorSpace::Gamut::SRgb;
+ qCDebug(lcIcc) << "fromIccProfile: sRGB primaries detected";
+ colorspaceDPtr->primaries = QColorSpace::Primaries::SRgb;
} else if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromAdobeRgb()) {
- qCDebug(lcIcc) << "fromIccProfile: Adobe RGB gamut detected";
- colorspaceDPtr->gamut = QColorSpace::Gamut::AdobeRgb;
+ qCDebug(lcIcc) << "fromIccProfile: Adobe RGB primaries detected";
+ colorspaceDPtr->primaries = QColorSpace::Primaries::AdobeRgb;
} else if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromDciP3D65()) {
- qCDebug(lcIcc) << "fromIccProfile: DCI-P3 D65 gamut detected";
- colorspaceDPtr->gamut = QColorSpace::Gamut::DciP3D65;
+ qCDebug(lcIcc) << "fromIccProfile: DCI-P3 D65 primaries detected";
+ colorspaceDPtr->primaries = QColorSpace::Primaries::DciP3D65;
} else if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromBt2020()) {
- qCDebug(lcIcc) << "fromIccProfile: BT.2020 gamut detected";
- colorspaceDPtr->gamut = QColorSpace::Gamut::Bt2020;
+ qCDebug(lcIcc) << "fromIccProfile: BT.2020 primaries detected";
+ colorspaceDPtr->primaries = QColorSpace::Primaries::Bt2020;
}
if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromProPhotoRgb()) {
- qCDebug(lcIcc) << "fromIccProfile: ProPhoto RGB gamut detected";
- colorspaceDPtr->gamut = QColorSpace::Gamut::ProPhotoRgb;
+ qCDebug(lcIcc) << "fromIccProfile: ProPhoto RGB primaries detected";
+ colorspaceDPtr->primaries = QColorSpace::Primaries::ProPhotoRgb;
}
// Reset the matrix to our canonical values:
- if (colorspaceDPtr->gamut != QColorSpace::Gamut::Custom)
+ if (colorspaceDPtr->primaries != QColorSpace::Primaries::Custom)
colorspaceDPtr->setToXyzMatrix();
// Parse TRC tags
- const GenericTagData *rTrc;
- const GenericTagData *gTrc;
- const GenericTagData *bTrc;
+ TagEntry rTrc;
+ TagEntry gTrc;
+ TagEntry bTrc;
if (tagIndex.contains(Tag::aarg) && tagIndex.contains(Tag::aagg) && tagIndex.contains(Tag::aabg)) {
// Apple extension for parametric version of TRCs in ICCv2:
- rTrc = (const GenericTagData *)(data.constData() + tagIndex[Tag::aarg]);
- gTrc = (const GenericTagData *)(data.constData() + tagIndex[Tag::aagg]);
- bTrc = (const GenericTagData *)(data.constData() + tagIndex[Tag::aabg]);
+ rTrc = tagIndex[Tag::aarg];
+ gTrc = tagIndex[Tag::aagg];
+ bTrc = tagIndex[Tag::aabg];
} else {
- rTrc = (const GenericTagData *)(data.constData() + tagIndex[Tag::rTRC]);
- gTrc = (const GenericTagData *)(data.constData() + tagIndex[Tag::gTRC]);
- bTrc = (const GenericTagData *)(data.constData() + tagIndex[Tag::bTRC]);
+ rTrc = tagIndex[Tag::rTRC];
+ gTrc = tagIndex[Tag::gTRC];
+ bTrc = tagIndex[Tag::bTRC];
}
QColorTrc rCurve;
QColorTrc gCurve;
QColorTrc bCurve;
- if (!parseTRC(rTrc, rCurve))
+ if (!parseTRC(data, rTrc, rCurve)) {
+ qCWarning(lcIcc) << "fromIccProfile: Invalid rTRC";
return false;
- if (!parseTRC(gTrc, gCurve))
+ }
+ if (!parseTRC(data, gTrc, gCurve)) {
+ qCWarning(lcIcc) << "fromIccProfile: Invalid gTRC";
return false;
- if (!parseTRC(bTrc, bCurve))
+ }
+ if (!parseTRC(data, bTrc, bCurve)) {
+ qCWarning(lcIcc) << "fromIccProfile: Invalid bTRC";
return false;
+ }
if (rCurve == gCurve && gCurve == bCurve && rCurve.m_type == QColorTrc::Type::Function) {
if (rCurve.m_fun.isLinear()) {
qCDebug(lcIcc) << "fromIccProfile: Linear gamma detected";
@@ -652,7 +758,12 @@ bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace)
colorspaceDPtr->transferFunction = QColorSpace::TransferFunction::Custom;
}
- // FIXME: try to parse the description..
+ if (tagIndex.contains(Tag::desc)) {
+ if (!parseDesc(data, tagIndex[Tag::desc], colorspaceDPtr->description))
+ qCWarning(lcIcc) << "fromIccProfile: Failed to parse description";
+ else
+ qCDebug(lcIcc) << "fromIccProfile: Description" << colorspaceDPtr->description;
+ }
if (!colorspaceDPtr->identifyColorSpace())
colorspaceDPtr->id = QColorSpace::Unknown;
diff --git a/src/gui/painting/qpagedpaintdevice.cpp b/src/gui/painting/qpagedpaintdevice.cpp
index 72b2834470..3fdd0206b7 100644
--- a/src/gui/painting/qpagedpaintdevice.cpp
+++ b/src/gui/painting/qpagedpaintdevice.cpp
@@ -42,6 +42,7 @@
QT_BEGIN_NAMESPACE
+// ### Qt 6: remove when the deprecated constructor is removed
class QDummyPagedPaintDevicePrivate : public QPagedPaintDevicePrivate
{
bool setPageLayout(const QPageLayout &newPageLayout) override
@@ -85,7 +86,7 @@ QPagedPaintDevicePrivate::~QPagedPaintDevicePrivate()
\class QPagedPaintDevice
\inmodule QtGui
- \brief The QPagedPaintDevice class is a represents a paintdevice that supports
+ \brief The QPagedPaintDevice class represents a paint device that supports
multiple pages.
\ingroup painting
diff --git a/src/gui/painting/qpagedpaintdevice.h b/src/gui/painting/qpagedpaintdevice.h
index 1c37c17fa3..21e23e0eb4 100644
--- a/src/gui/painting/qpagedpaintdevice.h
+++ b/src/gui/painting/qpagedpaintdevice.h
@@ -213,6 +213,7 @@ public:
Envelope10 = Comm10E
};
+ // keep in sync with QPdfEngine::PdfVersion!
enum PdfVersion { PdfVersion_1_4, PdfVersion_A1b, PdfVersion_1_6 };
// ### Qt6 Make these virtual
diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp
index 461ad51200..0a440e5b75 100644
--- a/src/gui/painting/qpaintengine_raster.cpp
+++ b/src/gui/painting/qpaintengine_raster.cpp
@@ -2902,9 +2902,9 @@ bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs,
QFontEngine::GlyphFormat glyphFormat = fontEngine->glyphFormat != QFontEngine::Format_None ? fontEngine->glyphFormat : d->glyphCacheFormat;
QImageTextureGlyphCache *cache =
- static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(0, glyphFormat, s->matrix));
+ static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(0, glyphFormat, s->matrix, QColor(s->penData.solidColor)));
if (!cache) {
- cache = new QImageTextureGlyphCache(glyphFormat, s->matrix);
+ cache = new QImageTextureGlyphCache(glyphFormat, s->matrix, QColor(s->penData.solidColor));
fontEngine->setGlyphCache(0, cache);
}
@@ -4295,7 +4295,7 @@ protected:
QSharedPointer<const CacheInfo> addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
if (cache.size() == maxCacheSize()) {
// may remove more than 1, but OK
- cache.erase(cache.begin() + QRandomGenerator::global()->bounded(maxCacheSize()));
+ cache.erase(std::next(cache.begin(), QRandomGenerator::global()->bounded(maxCacheSize())));
}
auto cache_entry = QSharedPointer<CacheInfo>::create(gradient.stops(), opacity, gradient.interpolationMode());
generateGradientColorTable(gradient, cache_entry->buffer64, paletteSize(), opacity);
diff --git a/src/gui/painting/qpainterpath.cpp b/src/gui/painting/qpainterpath.cpp
index 8801e66b0f..b1d1f30800 100644
--- a/src/gui/painting/qpainterpath.cpp
+++ b/src/gui/painting/qpainterpath.cpp
@@ -661,6 +661,7 @@ void QPainterPath::clear()
detach();
d_func()->clear();
+ d_func()->elements.append( {0, 0, MoveToElement} );
}
/*!
@@ -2337,12 +2338,12 @@ bool QPainterPath::operator==(const QPainterPath &path) const
{
QPainterPathData *d = reinterpret_cast<QPainterPathData *>(d_func());
QPainterPathData *other_d = path.d_func();
- if (other_d == d)
+ if (other_d == d) {
return true;
- else if (!d || !other_d) {
- if (!d && other_d->elements.empty() && other_d->fillRule == Qt::OddEvenFill)
+ } else if (!d || !other_d) {
+ if (!other_d && isEmpty() && elementAt(0) == QPointF() && d->fillRule == Qt::OddEvenFill)
return true;
- if (!other_d && d && d->elements.empty() && d->fillRule == Qt::OddEvenFill)
+ if (!d && path.isEmpty() && path.elementAt(0) == QPointF() && other_d->fillRule == Qt::OddEvenFill)
return true;
return false;
}
diff --git a/src/gui/painting/qpainterpath_p.h b/src/gui/painting/qpainterpath_p.h
index 22bdbde2a9..a420e0b3d9 100644
--- a/src/gui/painting/qpainterpath_p.h
+++ b/src/gui/painting/qpainterpath_p.h
@@ -313,7 +313,7 @@ inline void QPainterPathData::clear()
elements.clear();
cStart = 0;
-
+ fillRule = Qt::OddEvenFill;
bounds = {};
controlBounds = {};
diff --git a/src/gui/painting/qpdf.cpp b/src/gui/painting/qpdf.cpp
index 25d488961c..f560e1f0f0 100644
--- a/src/gui/painting/qpdf.cpp
+++ b/src/gui/painting/qpdf.cpp
@@ -1570,12 +1570,14 @@ void QPdfEnginePrivate::writeHeader()
{
addXrefEntry(0,false);
- static const QHash<QPdfEngine::PdfVersion, const char *> mapping {
- {QPdfEngine::Version_1_4, "1.4"},
- {QPdfEngine::Version_A1b, "1.4"},
- {QPdfEngine::Version_1_6, "1.6"}
+ // Keep in sync with QPdfEngine::PdfVersion!
+ static const char mapping[][4] = {
+ "1.4", // Version_1_4
+ "1.4", // Version_A1b
+ "1.6", // Version_1_6
};
- const char *verStr = mapping.value(pdfVersion, "1.4");
+ static const size_t numMappings = sizeof mapping / sizeof *mapping;
+ const char *verStr = mapping[size_t(pdfVersion) < numMappings ? pdfVersion : 0];
xprintf("%%PDF-%s\n", verStr);
xprintf("%%\303\242\303\243\n");
diff --git a/src/gui/painting/qpdf_p.h b/src/gui/painting/qpdf_p.h
index e337c61f64..89e549614a 100644
--- a/src/gui/painting/qpdf_p.h
+++ b/src/gui/painting/qpdf_p.h
@@ -168,6 +168,7 @@ class Q_GUI_EXPORT QPdfEngine : public QPaintEngine
Q_DECLARE_PRIVATE(QPdfEngine)
friend class QPdfWriter;
public:
+ // keep in sync with QPagedPaintDevice::PdfVersion and QPdfEnginePrivate::writeHeader()::mapping!
enum PdfVersion
{
Version_1_4,
diff --git a/src/gui/painting/qpdfwriter.cpp b/src/gui/painting/qpdfwriter.cpp
index 7f18ce42be..bf7e2d3dca 100644
--- a/src/gui/painting/qpdfwriter.cpp
+++ b/src/gui/painting/qpdfwriter.cpp
@@ -170,17 +170,11 @@ void QPdfWriter::setPdfVersion(PdfVersion version)
{
Q_D(QPdfWriter);
- static const QHash<QPdfWriter::PdfVersion, QPdfEngine::PdfVersion> engineMapping {
- {QPdfWriter::PdfVersion_1_4, QPdfEngine::Version_1_4},
- {QPdfWriter::PdfVersion_A1b, QPdfEngine::Version_A1b},
- {QPdfWriter::PdfVersion_1_6, QPdfEngine::Version_1_6}
- };
-
if (d->pdfVersion == version)
return;
d->pdfVersion = version;
- d->engine->setPdfVersion(engineMapping.value(version, QPdfEngine::Version_1_4));
+ d->engine->setPdfVersion(static_cast<QPdfEngine::PdfVersion>(static_cast<int>(version)));
}
/*!
diff --git a/src/gui/painting/qtextureglyphcache.cpp b/src/gui/painting/qtextureglyphcache.cpp
index e8c47df21c..7a3dd04965 100644
--- a/src/gui/painting/qtextureglyphcache.cpp
+++ b/src/gui/painting/qtextureglyphcache.cpp
@@ -267,7 +267,7 @@ QImage QTextureGlyphCache::textureMapForGlyph(glyph_t g, QFixed subPixelPosition
case QFontEngine::Format_A32:
return m_current_fontengine->alphaRGBMapForGlyph(g, subPixelPosition, m_transform);
case QFontEngine::Format_ARGB:
- return m_current_fontengine->bitmapForGlyph(g, subPixelPosition, m_transform);
+ return m_current_fontengine->bitmapForGlyph(g, subPixelPosition, m_transform, color());
default:
return m_current_fontengine->alphaMapForGlyph(g, subPixelPosition, m_transform);
}
diff --git a/src/gui/painting/qtextureglyphcache_p.h b/src/gui/painting/qtextureglyphcache_p.h
index 1e83ab46d1..b6fc7230a8 100644
--- a/src/gui/painting/qtextureglyphcache_p.h
+++ b/src/gui/painting/qtextureglyphcache_p.h
@@ -74,8 +74,8 @@ class QTextItemInt;
class Q_GUI_EXPORT QTextureGlyphCache : public QFontEngineGlyphCache
{
public:
- QTextureGlyphCache(QFontEngine::GlyphFormat format, const QTransform &matrix)
- : QFontEngineGlyphCache(format, matrix), m_current_fontengine(nullptr),
+ QTextureGlyphCache(QFontEngine::GlyphFormat format, const QTransform &matrix, const QColor &color = QColor())
+ : QFontEngineGlyphCache(format, matrix, color), m_current_fontengine(nullptr),
m_w(0), m_h(0), m_cx(0), m_cy(0), m_currentRowHeight(0)
{ }
@@ -165,8 +165,8 @@ inline uint qHash(const QTextureGlyphCache::GlyphAndSubPixelPosition &g)
class Q_GUI_EXPORT QImageTextureGlyphCache : public QTextureGlyphCache
{
public:
- QImageTextureGlyphCache(QFontEngine::GlyphFormat format, const QTransform &matrix)
- : QTextureGlyphCache(format, matrix) { }
+ QImageTextureGlyphCache(QFontEngine::GlyphFormat format, const QTransform &matrix, const QColor &color = QColor())
+ : QTextureGlyphCache(format, matrix, color) { }
~QImageTextureGlyphCache();
virtual void createTextureData(int width, int height) override;
diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp
index d7c1607e57..7443c0a04f 100644
--- a/src/gui/rhi/qrhi.cpp
+++ b/src/gui/rhi/qrhi.cpp
@@ -36,6 +36,7 @@
#include "qrhi_p_p.h"
#include <qmath.h>
+#include <QLoggingCategory>
#include "qrhinull_p_p.h"
#ifndef QT_NO_OPENGL
@@ -54,6 +55,8 @@
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
+
/*!
\class QRhi
\inmodule QtRhi
@@ -260,6 +263,12 @@ QT_BEGIN_NAMESPACE
tied to those concepts, however, and is thus a topic that is currently out
of scope, but may be introduced in the future.
+ \note The Metal backend requires that an autorelease pool is available on
+ the rendering thread, ideally wrapping each iteration of the render loop.
+ This needs no action from the users of QRhi when rendering on the main
+ (gui) thread, but becomes important when a separate, dedicated render
+ thread is used.
+
\section3 Resource synchronization
QRhi does not expose APIs for resource barriers or image layout
@@ -383,6 +392,22 @@ QT_BEGIN_NAMESPACE
texture object is "exported" via QRhi::nativeHandles() or
QRhiTexture::nativeHandles(). Most importantly, passing pointers in structs
and via setters does not transfer ownership.
+
+ \section2 Troubleshooting
+
+ Errors are printed to the output via qWarning(). Additional debug messages
+ can be enabled via the following logging categories. Messages from these
+ categories are not printed by default unless explicitly enabled via
+ QRhi::EnableProfiling or the facilities of QLoggingCategory (such as, the
+ \c QT_LOGGING_RULES environment variable).
+
+ \list
+ \li \c{qt.rhi.general}
+ \endlist
+
+ It is strongly advised to inspect the output with the logging categories
+ (\c{qt.rhi.*}) enabled whenever a QRhi-based application is not behaving as
+ expected.
*/
/*!
@@ -403,7 +428,8 @@ QT_BEGIN_NAMESPACE
\value EnableProfiling Enables gathering timing (CPU, GPU) and resource
(QRhiBuffer, QRhiTexture, etc.) information and additional metadata. See
QRhiProfiler. Avoid enabling in production builds as it may involve a
- performance penalty.
+ performance penalty. Also enables debug messages from the \c{qt.rhi.*}
+ logging categories.
\value EnableDebugMarkers Enables debug marker groups. Without this frame
debugging features like making debug groups and custom resource name
@@ -3867,11 +3893,21 @@ QRhi *QRhi::create(Implementation impl, QRhiInitParams *params, Flags flags, QRh
if (r->d) {
r->d->q = r.data();
+
if (flags.testFlag(EnableProfiling)) {
QRhiProfilerPrivate *profD = QRhiProfilerPrivate::get(&r->d->profiler);
profD->rhiDWhenEnabled = r->d;
+ const_cast<QLoggingCategory &>(QRHI_LOG_INFO()).setEnabled(QtDebugMsg, true);
}
+
+ // Play nice with QSG_INFO since that is still the most commonly used
+ // way to get graphics info printed from Qt Quick apps, and the Quick
+ // scenegraph is our primary user.
+ if (qEnvironmentVariableIsSet("QSG_INFO"))
+ const_cast<QLoggingCategory &>(QRHI_LOG_INFO()).setEnabled(QtDebugMsg, true);
+
r->d->debugMarkers = flags.testFlag(EnableDebugMarkers);
+
if (r->d->create(flags)) {
r->d->implType = impl;
r->d->implThread = QThread::currentThread();
@@ -4911,6 +4947,11 @@ QRhiShaderResourceBindings *QRhi::newShaderResourceBindings()
backends. See \l{QRhiBuffer::UsageFlag}{UsageFlags} and
\l{QRhi::NonDynamicUniformBuffers}{the feature flags}.
+ \note Backends may choose to allocate buffers bigger than \a size. This is
+ done transparently to applications, so there are no special restrictions on
+ the value of \a size. QRhiBuffer::size() will always report back the value
+ that was requested in \a size.
+
\sa QRhiResource::release()
*/
QRhiBuffer *QRhi::newBuffer(QRhiBuffer::Type type,
@@ -5331,4 +5372,32 @@ void QRhiPassResourceTracker::registerTexture(QRhiTexture *tex, TextureAccess *a
m_textures.append(t);
}
+QRhiPassResourceTracker::BufferStage QRhiPassResourceTracker::toPassTrackerBufferStage(QRhiShaderResourceBinding::StageFlags stages)
+{
+ // pick the earlier stage (as this is going to be dstAccessMask)
+ if (stages.testFlag(QRhiShaderResourceBinding::VertexStage))
+ return QRhiPassResourceTracker::BufVertexStage;
+ if (stages.testFlag(QRhiShaderResourceBinding::FragmentStage))
+ return QRhiPassResourceTracker::BufFragmentStage;
+ if (stages.testFlag(QRhiShaderResourceBinding::ComputeStage))
+ return QRhiPassResourceTracker::BufComputeStage;
+
+ Q_UNREACHABLE();
+ return QRhiPassResourceTracker::BufVertexStage;
+}
+
+QRhiPassResourceTracker::TextureStage QRhiPassResourceTracker::toPassTrackerTextureStage(QRhiShaderResourceBinding::StageFlags stages)
+{
+ // pick the earlier stage (as this is going to be dstAccessMask)
+ if (stages.testFlag(QRhiShaderResourceBinding::VertexStage))
+ return QRhiPassResourceTracker::TexVertexStage;
+ if (stages.testFlag(QRhiShaderResourceBinding::FragmentStage))
+ return QRhiPassResourceTracker::TexFragmentStage;
+ if (stages.testFlag(QRhiShaderResourceBinding::ComputeStage))
+ return QRhiPassResourceTracker::TexComputeStage;
+
+ Q_UNREACHABLE();
+ return QRhiPassResourceTracker::TexVertexStage;
+}
+
QT_END_NAMESPACE
diff --git a/src/gui/rhi/qrhi_p_p.h b/src/gui/rhi/qrhi_p_p.h
index 83d521f441..b592fe82f2 100644
--- a/src/gui/rhi/qrhi_p_p.h
+++ b/src/gui/rhi/qrhi_p_p.h
@@ -52,6 +52,7 @@
#include "qrhiprofiler_p_p.h"
#include <QBitArray>
#include <QAtomicInt>
+#include <QLoggingCategory>
QT_BEGIN_NAMESPACE
@@ -60,6 +61,8 @@ QT_BEGIN_NAMESPACE
#define QRHI_PROF QRhiProfilerPrivate *rhiP = m_rhi->profilerPrivateOrNull()
#define QRHI_PROF_F(f) for (bool qrhip_enabled = rhiP != nullptr; qrhip_enabled; qrhip_enabled = false) rhiP->f
+Q_DECLARE_LOGGING_CATEGORY(QRHI_LOG_INFO)
+
class QRhiImplementation
{
public:
@@ -553,6 +556,9 @@ public:
};
const QVector<Texture> *textures() const { return &m_textures; }
+ static BufferStage toPassTrackerBufferStage(QRhiShaderResourceBinding::StageFlags stages);
+ static TextureStage toPassTrackerTextureStage(QRhiShaderResourceBinding::StageFlags stages);
+
private:
QVector<Buffer> m_buffers;
QVector<Texture> m_textures;
diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp
index a8a490eb5c..0c5df600a0 100644
--- a/src/gui/rhi/qrhid3d11.cpp
+++ b/src/gui/rhi/qrhid3d11.cpp
@@ -189,10 +189,10 @@ bool QRhiD3D11::create(QRhi::Flags flags)
DXGI_ADAPTER_DESC1 desc;
adapter->GetDesc1(&desc);
const QString name = QString::fromUtf16((char16_t *) desc.Description);
- qDebug("Adapter %d: '%s' (flags 0x%x)", adapterIndex, qPrintable(name), desc.Flags);
+ qCDebug(QRHI_LOG_INFO, "Adapter %d: '%s' (flags 0x%x)", adapterIndex, qPrintable(name), desc.Flags);
if (!adapterToUse && (requestedAdapterIndex < 0 || requestedAdapterIndex == adapterIndex)) {
adapterToUse = adapter;
- qDebug(" using this adapter");
+ qCDebug(QRHI_LOG_INFO, " using this adapter");
} else {
adapter->Release();
}
diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp
index 22cb030c27..379801efbd 100644
--- a/src/gui/rhi/qrhigles2.cpp
+++ b/src/gui/rhi/qrhigles2.cpp
@@ -47,12 +47,13 @@ QT_BEGIN_NAMESPACE
OpenGL backend. Binding vertex attribute locations and decomposing uniform
buffers into uniforms are handled transparently to the application via the
reflection data (QShaderDescription). Real uniform buffers are never used,
- regardless of the GLSL version. Textures and buffers feature no special logic,
- it's all just glTexSubImage2D and glBufferSubData (with "dynamic" buffers set
- to GL_DYNAMIC_DRAW). The swapchain and the associated renderbuffer for
- depth-stencil will be dummies since we have no control over the underlying
- buffers here. While we try to keep this backend clean GLES 2.0, some GL(ES)
- 3.0 features like multisample renderbuffers and blits are used when available.
+ regardless of the GLSL version. Textures and buffers feature no special
+ logic, it's all just glTexSubImage2D and glBufferSubData (with "dynamic"
+ buffers set to GL_DYNAMIC_DRAW). The swapchain and the associated
+ renderbuffer for depth-stencil will be dummies since we have no control over
+ the underlying buffers here. While the baseline here is plain GLES 2.0, some
+ modern GL(ES) features like multisample renderbuffers, blits, and compute are
+ used when available. Also functional with core profile contexts.
*/
/*!
@@ -239,6 +240,34 @@ QT_BEGIN_NAMESPACE
#define GL_MAX_SAMPLES 0x8D57
#endif
+#ifndef GL_SHADER_STORAGE_BUFFER
+#define GL_SHADER_STORAGE_BUFFER 0x90D2
+#endif
+
+#ifndef GL_READ_ONLY
+#define GL_READ_ONLY 0x88B8
+#endif
+
+#ifndef GL_WRITE_ONLY
+#define GL_WRITE_ONLY 0x88B9
+#endif
+
+#ifndef GL_READ_WRITE
+#define GL_READ_WRITE 0x88BA
+#endif
+
+#ifndef GL_COMPUTE_SHADER
+#define GL_COMPUTE_SHADER 0x91B9
+#endif
+
+#ifndef GL_ALL_BARRIER_BITS
+#define GL_ALL_BARRIER_BITS 0xFFFFFFFF
+#endif
+
+#ifndef GL_VERTEX_PROGRAM_POINT_SIZE
+#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642
+#endif
+
/*!
Constructs a new QRhiGles2InitParams.
@@ -356,7 +385,7 @@ bool QRhiGles2::create(QRhi::Flags flags)
ctx = nullptr;
return false;
}
- qDebug() << "Created OpenGL context" << ctx->format();
+ qCDebug(QRHI_LOG_INFO) << "Created OpenGL context" << ctx->format();
}
if (!ensureContext(maybeWindow ? maybeWindow : fallbackSurface)) // see 'window' discussion in QRhiGles2InitParams comments
@@ -368,7 +397,7 @@ bool QRhiGles2::create(QRhi::Flags flags)
const char *renderer = reinterpret_cast<const char *>(f->glGetString(GL_RENDERER));
const char *version = reinterpret_cast<const char *>(f->glGetString(GL_VERSION));
if (vendor && renderer && version)
- qDebug("OpenGL VENDOR: %s RENDERER: %s VERSION: %s", vendor, renderer, version);
+ qCDebug(QRHI_LOG_INFO, "OpenGL VENDOR: %s RENDERER: %s VERSION: %s", vendor, renderer, version);
const QSurfaceFormat actualFormat = ctx->format();
@@ -400,9 +429,9 @@ bool QRhiGles2::create(QRhi::Flags flags)
caps.gles = actualFormat.renderableType() == QSurfaceFormat::OpenGLES;
if (caps.gles)
- caps.fixedIndexPrimitiveRestart = caps.ctxMajor >= 3;
+ caps.fixedIndexPrimitiveRestart = caps.ctxMajor >= 3; // ES 3.0
else
- caps.fixedIndexPrimitiveRestart = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 3);
+ caps.fixedIndexPrimitiveRestart = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 3); // 4.3
if (caps.fixedIndexPrimitiveRestart)
f->glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
@@ -411,8 +440,8 @@ bool QRhiGles2::create(QRhi::Flags flags)
caps.bgraInternalFormat = caps.bgraExternalFormat && caps.gles;
caps.r8Format = f->hasOpenGLFeature(QOpenGLFunctions::TextureRGFormats);
caps.r16Format = f->hasOpenGLExtension(QOpenGLExtensions::Sized16Formats);
- caps.floatFormats = caps.ctxMajor >= 3;
- caps.depthTexture = caps.ctxMajor >= 3;
+ caps.floatFormats = caps.ctxMajor >= 3; // 3.0 or ES 3.0
+ caps.depthTexture = caps.ctxMajor >= 3; // 3.0 or ES 3.0
caps.packedDepthStencil = f->hasOpenGLExtension(QOpenGLExtensions::PackedDepthStencil);
#ifdef Q_OS_WASM
caps.needsDepthStencilCombinedAttach = true;
@@ -421,12 +450,31 @@ bool QRhiGles2::create(QRhi::Flags flags)
#endif
caps.srgbCapableDefaultFramebuffer = f->hasOpenGLExtension(QOpenGLExtensions::SRGBFrameBuffer);
caps.coreProfile = actualFormat.profile() == QSurfaceFormat::CoreProfile;
- caps.uniformBuffers = caps.ctxMajor >= 3 && (caps.gles || caps.ctxMinor >= 1);
+
+ if (caps.gles)
+ caps.uniformBuffers = caps.ctxMajor >= 3; // ES 3.0
+ else
+ caps.uniformBuffers = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 1); // 3.1
+
caps.elementIndexUint = f->hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint);
caps.depth24 = f->hasOpenGLExtension(QOpenGLExtensions::Depth24);
caps.rgba8Format = f->hasOpenGLExtension(QOpenGLExtensions::Sized8Formats);
- caps.instancing = caps.ctxMajor >= 3 && (caps.gles || caps.ctxMinor >= 3);
- caps.baseVertex = caps.ctxMajor >= 3 && caps.ctxMinor >= 2;
+
+ if (caps.gles)
+ caps.instancing = caps.ctxMajor >= 3; // ES 3.0
+ else
+ caps.instancing = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 3); // 3.3
+
+ caps.baseVertex = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2); // 3.2 or ES 3.2
+
+ if (caps.gles)
+ caps.compute = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 1); // ES 3.1
+ else
+ caps.compute = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 3); // 4.3
+
+ if (!caps.gles)
+ f->glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
+ // else (with gles) this is always on
nativeHandlesStruct.context = ctx;
@@ -683,7 +731,7 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const
case QRhi::ElementIndexUint:
return caps.elementIndexUint;
case QRhi::Compute:
- return false;
+ return caps.compute;
case QRhi::WideLines:
return true;
case QRhi::VertexShaderPointSize:
@@ -778,10 +826,11 @@ void QRhiGles2::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline
QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass);
QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, ps);
- const bool pipelineChanged = cbD->currentPipeline != ps || cbD->currentPipelineGeneration != psD->generation;
+ const bool pipelineChanged = cbD->currentGraphicsPipeline != ps || cbD->currentPipelineGeneration != psD->generation;
if (pipelineChanged) {
- cbD->currentPipeline = ps;
+ cbD->currentGraphicsPipeline = ps;
+ cbD->currentComputePipeline = nullptr;
cbD->currentPipelineGeneration = psD->generation;
QGles2CommandBuffer::Command cmd;
@@ -796,35 +845,92 @@ void QRhiGles2::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind
const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
{
QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
- Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass);
- Q_ASSERT(cbD->currentPipeline);
+ Q_ASSERT(cbD->recordingPass != QGles2CommandBuffer::NoPass);
+ QGles2GraphicsPipeline *gfxPsD = QRHI_RES(QGles2GraphicsPipeline, cbD->currentGraphicsPipeline);
+ QGles2ComputePipeline *compPsD = QRHI_RES(QGles2ComputePipeline, cbD->currentComputePipeline);
- if (!srb)
- srb = QRHI_RES(QGles2GraphicsPipeline, cbD->currentPipeline)->m_shaderResourceBindings;
+ if (!srb) {
+ if (gfxPsD)
+ srb = gfxPsD->m_shaderResourceBindings;
+ else
+ srb = compPsD->m_shaderResourceBindings;
+ }
+ QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
QGles2ShaderResourceBindings *srbD = QRHI_RES(QGles2ShaderResourceBindings, srb);
bool hasDynamicOffsetInSrb = false;
for (int i = 0, ie = srbD->m_bindings.count(); i != ie; ++i) {
const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&srbD->m_bindings[i]);
switch (b->type) {
case QRhiShaderResourceBinding::UniformBuffer:
+ // no BufUniformRead / AccessUniform because no real uniform buffers are used
if (b->u.ubuf.hasDynamicOffset)
hasDynamicOffsetInSrb = true;
break;
+ case QRhiShaderResourceBinding::SampledTexture:
+ trackedRegisterTexture(&passResTracker,
+ QRHI_RES(QGles2Texture, b->u.stex.tex),
+ QRhiPassResourceTracker::TexSample,
+ QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage));
+ break;
+ case QRhiShaderResourceBinding::ImageLoad:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::ImageStore:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::ImageLoadStore:
+ {
+ QGles2Texture *texD = QRHI_RES(QGles2Texture, b->u.simage.tex);
+ QRhiPassResourceTracker::TextureAccess access;
+ if (b->type == QRhiShaderResourceBinding::ImageLoad)
+ access = QRhiPassResourceTracker::TexStorageLoad;
+ else if (b->type == QRhiShaderResourceBinding::ImageStore)
+ access = QRhiPassResourceTracker::TexStorageStore;
+ else
+ access = QRhiPassResourceTracker::TexStorageLoadStore;
+ trackedRegisterTexture(&passResTracker, texD, access,
+ QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage));
+ }
+ break;
+ case QRhiShaderResourceBinding::BufferLoad:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::BufferStore:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::BufferLoadStore:
+ {
+ QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, b->u.sbuf.buf);
+ QRhiPassResourceTracker::BufferAccess access;
+ if (b->type == QRhiShaderResourceBinding::BufferLoad)
+ access = QRhiPassResourceTracker::BufStorageLoad;
+ else if (b->type == QRhiShaderResourceBinding::BufferStore)
+ access = QRhiPassResourceTracker::BufStorageStore;
+ else
+ access = QRhiPassResourceTracker::BufStorageLoadStore;
+ trackedRegisterBuffer(&passResTracker, bufD, access,
+ QRhiPassResourceTracker::toPassTrackerBufferStage(b->stage));
+ }
+ break;
default:
break;
}
}
- const bool srbChanged = cbD->currentSrb != srb || cbD->currentSrbGeneration != srbD->generation;
+ const bool srbChanged = gfxPsD ? (cbD->currentGraphicsSrb != srb) : (cbD->currentComputeSrb != srb);
+ const bool srbRebuilt = cbD->currentSrbGeneration != srbD->generation;
- if (srbChanged || hasDynamicOffsetInSrb) {
- cbD->currentSrb = srb;
+ if (srbChanged || srbRebuilt || hasDynamicOffsetInSrb) {
+ if (gfxPsD) {
+ cbD->currentGraphicsSrb = srb;
+ cbD->currentComputeSrb = nullptr;
+ } else {
+ cbD->currentGraphicsSrb = nullptr;
+ cbD->currentComputeSrb = srb;
+ }
cbD->currentSrbGeneration = srbD->generation;
QGles2CommandBuffer::Command cmd;
cmd.cmd = QGles2CommandBuffer::Command::BindShaderResources;
- cmd.args.bindShaderResources.ps = cbD->currentPipeline;
+ cmd.args.bindShaderResources.maybeGraphicsPs = gfxPsD;
+ cmd.args.bindShaderResources.maybeComputePs = compPsD;
cmd.args.bindShaderResources.srb = srb;
cmd.args.bindShaderResources.dynamicOffsetCount = 0;
if (hasDynamicOffsetInSrb) {
@@ -851,30 +957,39 @@ void QRhiGles2::setVertexInput(QRhiCommandBuffer *cb,
{
QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass);
+ QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
for (int i = 0; i < bindingCount; ++i) {
QRhiBuffer *buf = bindings[i].first;
quint32 ofs = bindings[i].second;
QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, buf);
Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::VertexBuffer));
+
QGles2CommandBuffer::Command cmd;
cmd.cmd = QGles2CommandBuffer::Command::BindVertexBuffer;
- cmd.args.bindVertexBuffer.ps = cbD->currentPipeline;
+ cmd.args.bindVertexBuffer.ps = cbD->currentGraphicsPipeline;
cmd.args.bindVertexBuffer.buffer = bufD->buffer;
cmd.args.bindVertexBuffer.offset = ofs;
cmd.args.bindVertexBuffer.binding = startBinding + i;
cbD->commands.append(cmd);
+
+ trackedRegisterBuffer(&passResTracker, bufD, QRhiPassResourceTracker::BufVertexInput,
+ QRhiPassResourceTracker::BufVertexInputStage);
}
if (indexBuf) {
QGles2Buffer *ibufD = QRHI_RES(QGles2Buffer, indexBuf);
Q_ASSERT(ibufD->m_usage.testFlag(QRhiBuffer::IndexBuffer));
+
QGles2CommandBuffer::Command cmd;
cmd.cmd = QGles2CommandBuffer::Command::BindIndexBuffer;
cmd.args.bindIndexBuffer.buffer = ibufD->buffer;
cmd.args.bindIndexBuffer.offset = indexOffset;
cmd.args.bindIndexBuffer.type = indexFormat == QRhiCommandBuffer::IndexUInt16 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
cbD->commands.append(cmd);
+
+ trackedRegisterBuffer(&passResTracker, ibufD, QRhiPassResourceTracker::BufIndexRead,
+ QRhiPassResourceTracker::BufVertexInputStage);
}
}
@@ -932,7 +1047,7 @@ void QRhiGles2::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
QGles2CommandBuffer::Command cmd;
cmd.cmd = QGles2CommandBuffer::Command::StencilRef;
cmd.args.stencilRef.ref = refValue;
- cmd.args.stencilRef.ps = cbD->currentPipeline;
+ cmd.args.stencilRef.ps = cbD->currentGraphicsPipeline;
cbD->commands.append(cmd);
}
@@ -944,7 +1059,7 @@ void QRhiGles2::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
QGles2CommandBuffer::Command cmd;
cmd.cmd = QGles2CommandBuffer::Command::Draw;
- cmd.args.draw.ps = cbD->currentPipeline;
+ cmd.args.draw.ps = cbD->currentGraphicsPipeline;
cmd.args.draw.vertexCount = vertexCount;
cmd.args.draw.firstVertex = firstVertex;
cmd.args.draw.instanceCount = instanceCount;
@@ -960,7 +1075,7 @@ void QRhiGles2::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
QGles2CommandBuffer::Command cmd;
cmd.cmd = QGles2CommandBuffer::Command::DrawIndexed;
- cmd.args.drawIndexed.ps = cbD->currentPipeline;
+ cmd.args.drawIndexed.ps = cbD->currentGraphicsPipeline;
cmd.args.drawIndexed.indexCount = indexCount;
cmd.args.drawIndexed.firstIndex = firstIndex;
cmd.args.drawIndexed.instanceCount = instanceCount;
@@ -1126,9 +1241,48 @@ QRhi::FrameOpResult QRhiGles2::flushCommandBuffer()
return QRhi::FrameOpSuccess;
}
+void QRhiGles2::trackedBufferBarrier(QGles2CommandBuffer *cbD, QGles2Buffer *bufD, QGles2Buffer::Access access)
+{
+ Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::NoPass); // this is for resource updates only
+ const QGles2Buffer::Access prevAccess = bufD->usageState.access;
+ if (access == prevAccess)
+ return;
+
+ if (prevAccess == QGles2Buffer::AccessStorageWrite || prevAccess == QGles2Buffer::AccessStorageReadWrite) {
+ // Generating the minimal barrier set is way too complicated to do
+ // correctly (prevAccess is overwritten so we won't have proper
+ // tracking across multiple passes) so setting all barrier bits will do
+ // for now.
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::Barrier;
+ cmd.args.barrier.barriers = GL_ALL_BARRIER_BITS;
+ cbD->commands.append(cmd);
+ }
+
+ bufD->usageState.access = access;
+}
+
+void QRhiGles2::trackedImageBarrier(QGles2CommandBuffer *cbD, QGles2Texture *texD, QGles2Texture::Access access)
+{
+ Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::NoPass); // this is for resource updates only
+ const QGles2Texture::Access prevAccess = texD->usageState.access;
+ if (access == prevAccess)
+ return;
+
+ if (prevAccess == QGles2Texture::AccessStorageWrite || prevAccess == QGles2Texture::AccessStorageReadWrite) {
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::Barrier;
+ cmd.args.barrier.barriers = GL_ALL_BARRIER_BITS;
+ cbD->commands.append(cmd);
+ }
+
+ texD->usageState.access = access;
+}
+
void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cbD,
int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc)
{
+ trackedImageBarrier(cbD, texD, QGles2Texture::AccessUpdate);
const bool isCompressed = isCompressedFormat(texD->m_format);
const bool isCubeMap = texD->m_flags.testFlag(QRhiTexture::CubeMap);
const GLenum faceTargetBase = isCubeMap ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
@@ -1229,9 +1383,10 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) {
memcpy(bufD->ubuf.data() + u.offset, u.data.constData(), u.data.size());
} else {
+ trackedBufferBarrier(cbD, bufD, QGles2Buffer::AccessUpdate);
QGles2CommandBuffer::Command cmd;
cmd.cmd = QGles2CommandBuffer::Command::BufferSubData;
- cmd.args.bufferSubData.target = bufD->target;
+ cmd.args.bufferSubData.target = bufD->targetForDataOps;
cmd.args.bufferSubData.buffer = bufD->buffer;
cmd.args.bufferSubData.offset = u.offset;
cmd.args.bufferSubData.size = u.data.size();
@@ -1247,9 +1402,10 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) {
memcpy(bufD->ubuf.data() + u.offset, u.data.constData(), u.data.size());
} else {
+ trackedBufferBarrier(cbD, bufD, QGles2Buffer::AccessUpdate);
QGles2CommandBuffer::Command cmd;
cmd.cmd = QGles2CommandBuffer::Command::BufferSubData;
- cmd.args.bufferSubData.target = bufD->target;
+ cmd.args.bufferSubData.target = bufD->targetForDataOps;
cmd.args.bufferSubData.buffer = bufD->buffer;
cmd.args.bufferSubData.offset = u.offset;
cmd.args.bufferSubData.size = u.data.size();
@@ -1273,6 +1429,9 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
QGles2Texture *srcD = QRHI_RES(QGles2Texture, u.copy.src);
QGles2Texture *dstD = QRHI_RES(QGles2Texture, u.copy.dst);
+ trackedImageBarrier(cbD, srcD, QGles2Texture::AccessRead);
+ trackedImageBarrier(cbD, dstD, QGles2Texture::AccessUpdate);
+
const QSize size = u.copy.desc.pixelSize().isEmpty() ? srcD->m_pixelSize : u.copy.desc.pixelSize();
// do not translate coordinates, even if sp is bottom-left from gl's pov
const QPoint sp = u.copy.desc.sourceTopLeft();
@@ -1308,6 +1467,7 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
cmd.cmd = QGles2CommandBuffer::Command::ReadPixels;
cmd.args.readPixels.result = u.read.result;
QGles2Texture *texD = QRHI_RES(QGles2Texture, u.read.rb.texture());
+ trackedImageBarrier(cbD, texD, QGles2Texture::AccessRead);
cmd.args.readPixels.texture = texD ? texD->texture : 0;
if (texD) {
cmd.args.readPixels.w = texD->m_pixelSize.width();
@@ -1320,9 +1480,10 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
}
cbD->commands.append(cmd);
} else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::MipGen) {
+ QGles2Texture *texD = QRHI_RES(QGles2Texture, u.mipgen.tex);
+ trackedImageBarrier(cbD, texD, QGles2Texture::AccessFramebuffer);
QGles2CommandBuffer::Command cmd;
cmd.cmd = QGles2CommandBuffer::Command::GenMip;
- QGles2Texture *texD = QRHI_RES(QGles2Texture, u.mipgen.tex);
cmd.args.genMip.target = texD->target;
cmd.args.genMip.texture = texD->texture;
cbD->commands.append(cmd);
@@ -1571,6 +1732,88 @@ static inline GLenum toGlTextureCompareFunc(QRhiSampler::CompareOp op)
}
}
+static inline QGles2Buffer::Access toGlAccess(QRhiPassResourceTracker::BufferAccess access)
+{
+ switch (access) {
+ case QRhiPassResourceTracker::BufVertexInput:
+ return QGles2Buffer::AccessVertex;
+ case QRhiPassResourceTracker::BufIndexRead:
+ return QGles2Buffer::AccessIndex;
+ case QRhiPassResourceTracker::BufUniformRead:
+ return QGles2Buffer::AccessUniform;
+ case QRhiPassResourceTracker::BufStorageLoad:
+ return QGles2Buffer::AccessStorageRead;
+ case QRhiPassResourceTracker::BufStorageStore:
+ return QGles2Buffer::AccessStorageWrite;
+ case QRhiPassResourceTracker::BufStorageLoadStore:
+ return QGles2Buffer::AccessStorageReadWrite;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ return QGles2Buffer::AccessNone;
+}
+
+static inline QRhiPassResourceTracker::UsageState toPassTrackerUsageState(const QGles2Buffer::UsageState &bufUsage)
+{
+ QRhiPassResourceTracker::UsageState u;
+ u.layout = 0; // N/A
+ u.access = bufUsage.access;
+ u.stage = 0; // N/A
+ return u;
+}
+
+static inline QGles2Texture::Access toGlAccess(QRhiPassResourceTracker::TextureAccess access)
+{
+ switch (access) {
+ case QRhiPassResourceTracker::TexSample:
+ return QGles2Texture::AccessSample;
+ case QRhiPassResourceTracker::TexColorOutput:
+ return QGles2Texture::AccessFramebuffer;
+ case QRhiPassResourceTracker::TexDepthOutput:
+ return QGles2Texture::AccessFramebuffer;
+ case QRhiPassResourceTracker::TexStorageLoad:
+ return QGles2Texture::AccessStorageRead;
+ case QRhiPassResourceTracker::TexStorageStore:
+ return QGles2Texture::AccessStorageWrite;
+ case QRhiPassResourceTracker::TexStorageLoadStore:
+ return QGles2Texture::AccessStorageReadWrite;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ return QGles2Texture::AccessNone;
+}
+
+static inline QRhiPassResourceTracker::UsageState toPassTrackerUsageState(const QGles2Texture::UsageState &texUsage)
+{
+ QRhiPassResourceTracker::UsageState u;
+ u.layout = 0; // N/A
+ u.access = texUsage.access;
+ u.stage = 0; // N/A
+ return u;
+}
+
+void QRhiGles2::trackedRegisterBuffer(QRhiPassResourceTracker *passResTracker,
+ QGles2Buffer *bufD,
+ QRhiPassResourceTracker::BufferAccess access,
+ QRhiPassResourceTracker::BufferStage stage)
+{
+ QGles2Buffer::UsageState &u(bufD->usageState);
+ passResTracker->registerBuffer(bufD, 0, &access, &stage, toPassTrackerUsageState(u));
+ u.access = toGlAccess(access);
+}
+
+void QRhiGles2::trackedRegisterTexture(QRhiPassResourceTracker *passResTracker,
+ QGles2Texture *texD,
+ QRhiPassResourceTracker::TextureAccess access,
+ QRhiPassResourceTracker::TextureStage stage)
+{
+ QGles2Texture::UsageState &u(texD->usageState);
+ passResTracker->registerTexture(texD, &access, &stage, toPassTrackerUsageState(u));
+ u.access = toGlAccess(access);
+}
+
void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
{
QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
@@ -1747,7 +1990,8 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
executeBindGraphicsPipeline(cmd.args.bindGraphicsPipeline.ps);
break;
case QGles2CommandBuffer::Command::BindShaderResources:
- bindShaderResources(cmd.args.bindShaderResources.ps,
+ bindShaderResources(cmd.args.bindShaderResources.maybeGraphicsPs,
+ cmd.args.bindShaderResources.maybeComputePs,
cmd.args.bindShaderResources.srb,
cmd.args.bindShaderResources.dynamicOffsetPairs,
cmd.args.bindShaderResources.dynamicOffsetCount);
@@ -1890,6 +2134,51 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
f->glBindTexture(cmd.args.genMip.target, cmd.args.genMip.texture);
f->glGenerateMipmap(cmd.args.genMip.target);
break;
+ case QGles2CommandBuffer::Command::BindComputePipeline:
+ {
+ QGles2ComputePipeline *psD = QRHI_RES(QGles2ComputePipeline, cmd.args.bindComputePipeline.ps);
+ f->glUseProgram(psD->program);
+ }
+ break;
+ case QGles2CommandBuffer::Command::Dispatch:
+ f->glDispatchCompute(cmd.args.dispatch.x, cmd.args.dispatch.y, cmd.args.dispatch.z);
+ break;
+ case QGles2CommandBuffer::Command::BarriersForPass:
+ {
+ GLbitfield barriers = 0;
+ QRhiPassResourceTracker &tracker(cbD->passResTrackers[cmd.args.barriersForPass.trackerIndex]);
+ const QVector<QRhiPassResourceTracker::Buffer> *buffers = tracker.buffers();
+ // we only care about after-write, not any other accesses, and
+ // cannot tell if something was written in a shader several passes
+ // ago: now the previously written resource may be used with an
+ // access that was not in the previous passes, result in a missing
+ // barrier in theory. Hence setting all barrier bits whenever
+ // something previously written is used for the first time in a
+ // subsequent pass.
+ for (const QRhiPassResourceTracker::Buffer &b : *buffers) {
+ QGles2Buffer::Access accessBeforePass = QGles2Buffer::Access(b.stateAtPassBegin.access);
+ if (accessBeforePass == QGles2Buffer::AccessStorageWrite
+ || accessBeforePass == QGles2Buffer::AccessStorageReadWrite)
+ {
+ barriers |= GL_ALL_BARRIER_BITS;
+ }
+ }
+ const QVector<QRhiPassResourceTracker::Texture> *textures = tracker.textures();
+ for (const QRhiPassResourceTracker::Texture &t : *textures) {
+ QGles2Texture::Access accessBeforePass = QGles2Texture::Access(t.stateAtPassBegin.access);
+ if (accessBeforePass == QGles2Texture::AccessStorageWrite
+ || accessBeforePass == QGles2Texture::AccessStorageReadWrite)
+ {
+ barriers |= GL_ALL_BARRIER_BITS;
+ }
+ }
+ if (barriers)
+ f->glMemoryBarrier(barriers);
+ }
+ break;
+ case QGles2CommandBuffer::Command::Barrier:
+ f->glMemoryBarrier(cmd.args.barrier.barriers);
+ break;
default:
break;
}
@@ -1969,10 +2258,10 @@ void QRhiGles2::executeBindGraphicsPipeline(QRhiGraphicsPipeline *ps)
f->glUseProgram(psD->program);
}
-void QRhiGles2::bindShaderResources(QRhiGraphicsPipeline *ps, QRhiShaderResourceBindings *srb,
+void QRhiGles2::bindShaderResources(QRhiGraphicsPipeline *maybeGraphicsPs, QRhiComputePipeline *maybeComputePs,
+ QRhiShaderResourceBindings *srb,
const uint *dynOfsPairs, int dynOfsCount)
{
- QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, ps);
QGles2ShaderResourceBindings *srbD = QRHI_RES(QGles2ShaderResourceBindings, srb);
int texUnit = 0;
@@ -1994,7 +2283,9 @@ void QRhiGles2::bindShaderResources(QRhiGraphicsPipeline *ps, QRhiShaderResource
QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, b->u.ubuf.buf);
const QByteArray bufView = QByteArray::fromRawData(bufD->ubuf.constData() + viewOffset,
b->u.ubuf.maybeSize ? b->u.ubuf.maybeSize : bufD->m_size);
- for (QGles2GraphicsPipeline::Uniform &uniform : psD->uniforms) {
+ QVector<QGles2UniformDescription> &uniforms(maybeGraphicsPs ? QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->uniforms
+ : QRHI_RES(QGles2ComputePipeline, maybeComputePs)->uniforms);
+ for (QGles2UniformDescription &uniform : uniforms) {
if (uniform.binding == b->binding) {
// in a uniform buffer everything is at least 4 byte aligned
// so this should not cause unaligned reads
@@ -2034,6 +2325,30 @@ void QRhiGles2::bindShaderResources(QRhiGraphicsPipeline *ps, QRhiShaderResource
case QShaderDescription::Int4:
f->glUniform4iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
break;
+ case QShaderDescription::Uint:
+ f->glUniform1ui(uniform.glslLocation, *reinterpret_cast<const quint32 *>(src));
+ break;
+ case QShaderDescription::Uint2:
+ f->glUniform2uiv(uniform.glslLocation, 1, reinterpret_cast<const quint32 *>(src));
+ break;
+ case QShaderDescription::Uint3:
+ f->glUniform3uiv(uniform.glslLocation, 1, reinterpret_cast<const quint32 *>(src));
+ break;
+ case QShaderDescription::Uint4:
+ f->glUniform4uiv(uniform.glslLocation, 1, reinterpret_cast<const quint32 *>(src));
+ break;
+ case QShaderDescription::Bool: // a glsl bool is 4 bytes, like (u)int
+ f->glUniform1i(uniform.glslLocation, *reinterpret_cast<const qint32 *>(src));
+ break;
+ case QShaderDescription::Bool2:
+ f->glUniform2iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
+ break;
+ case QShaderDescription::Bool3:
+ f->glUniform3iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
+ break;
+ case QShaderDescription::Bool4:
+ f->glUniform4iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
+ break;
// ### more types
default:
break;
@@ -2046,8 +2361,10 @@ void QRhiGles2::bindShaderResources(QRhiGraphicsPipeline *ps, QRhiShaderResource
{
QGles2Texture *texD = QRHI_RES(QGles2Texture, b->u.stex.tex);
QGles2Sampler *samplerD = QRHI_RES(QGles2Sampler, b->u.stex.sampler);
+ QVector<QGles2SamplerDescription> &samplers(maybeGraphicsPs ? QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->samplers
+ : QRHI_RES(QGles2ComputePipeline, maybeComputePs)->samplers);
- for (QGles2GraphicsPipeline::Sampler &sampler : psD->samplers) {
+ for (QGles2SamplerDescription &sampler : samplers) {
if (sampler.binding == b->binding) {
f->glActiveTexture(GL_TEXTURE0 + texUnit);
f->glBindTexture(texD->target, texD->texture);
@@ -2074,6 +2391,38 @@ void QRhiGles2::bindShaderResources(QRhiGraphicsPipeline *ps, QRhiShaderResource
}
}
break;
+ case QRhiShaderResourceBinding::ImageLoad:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::ImageStore:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::ImageLoadStore:
+ {
+ QGles2Texture *texD = QRHI_RES(QGles2Texture, b->u.simage.tex);
+ const bool layered = texD->m_flags.testFlag(QRhiTexture::CubeMap);
+ GLenum access = GL_READ_WRITE;
+ if (b->type == QRhiShaderResourceBinding::ImageLoad)
+ access = GL_READ_ONLY;
+ else if (b->type == QRhiShaderResourceBinding::ImageStore)
+ access = GL_WRITE_ONLY;
+ f->glBindImageTexture(b->binding, texD->texture,
+ b->u.simage.level, layered, 0,
+ access, texD->glsizedintformat);
+ }
+ break;
+ case QRhiShaderResourceBinding::BufferLoad:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::BufferStore:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::BufferLoadStore:
+ {
+ QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, b->u.sbuf.buf);
+ if (b->u.sbuf.offset == 0 && b->u.sbuf.maybeSize == 0)
+ f->glBindBufferBase(GL_SHADER_STORAGE_BUFFER, b->binding, bufD->buffer);
+ else
+ f->glBindBufferRange(GL_SHADER_STORAGE_BUFFER, b->binding, bufD->buffer,
+ b->u.sbuf.offset, b->u.sbuf.maybeSize ? b->u.sbuf.maybeSize : bufD->m_size);
+ }
+ break;
default:
Q_UNREACHABLE();
break;
@@ -2095,8 +2444,11 @@ QGles2RenderTargetData *QRhiGles2::enqueueBindFramebuffer(QRhiRenderTarget *rt,
bool *wantsColorClear, bool *wantsDsClear)
{
QGles2RenderTargetData *rtD = nullptr;
+ QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
+
QGles2CommandBuffer::Command fbCmd;
fbCmd.cmd = QGles2CommandBuffer::Command::BindFramebuffer;
+
switch (rt->resourceType()) {
case QRhiResource::RenderTarget:
rtD = &QRHI_RES(QGles2ReferenceRenderTarget, rt)->d;
@@ -2117,14 +2469,39 @@ QGles2RenderTargetData *QRhiGles2::enqueueBindFramebuffer(QRhiRenderTarget *rt,
*wantsDsClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents);
fbCmd.args.bindFramebuffer.fbo = rtTex->framebuffer;
fbCmd.args.bindFramebuffer.colorAttCount = rtD->colorAttCount;
+
+ const QVector<QRhiColorAttachment> colorAttachments = rtTex->m_desc.colorAttachments();
+ for (const QRhiColorAttachment &colorAttachment : colorAttachments) {
+ QGles2Texture *texD = QRHI_RES(QGles2Texture, colorAttachment.texture());
+ QGles2Texture *resolveTexD = QRHI_RES(QGles2Texture, colorAttachment.resolveTexture());
+ if (texD) {
+ trackedRegisterTexture(&passResTracker, texD,
+ QRhiPassResourceTracker::TexColorOutput,
+ QRhiPassResourceTracker::TexColorOutputStage);
+ }
+ if (resolveTexD) {
+ trackedRegisterTexture(&passResTracker, resolveTexD,
+ QRhiPassResourceTracker::TexColorOutput,
+ QRhiPassResourceTracker::TexColorOutputStage);
+ }
+ // renderbuffers cannot be written in shaders (no image store) so
+ // they do not matter here
+ }
+ if (rtTex->m_desc.depthTexture()) {
+ trackedRegisterTexture(&passResTracker, QRHI_RES(QGles2Texture, rtTex->m_desc.depthTexture()),
+ QRhiPassResourceTracker::TexDepthOutput,
+ QRhiPassResourceTracker::TexDepthOutputStage);
+ }
}
break;
default:
Q_UNREACHABLE();
break;
}
+
fbCmd.args.bindFramebuffer.srgb = rtD->srgbUpdateAndBlend;
cbD->commands.append(fbCmd);
+
return rtD;
}
@@ -2140,6 +2517,15 @@ void QRhiGles2::beginPass(QRhiCommandBuffer *cb,
if (resourceUpdates)
enqueueResourceUpdates(cb, resourceUpdates);
+ // Get a new resource tracker. Then add a command that will generate
+ // glMemoryBarrier() calls based on that tracker when submitted.
+ cbD->passResTrackers.append(QRhiPassResourceTracker());
+ cbD->currentPassResTrackerIndex = cbD->passResTrackers.count() - 1;
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::BarriersForPass;
+ cmd.args.barriersForPass.trackerIndex = cbD->currentPassResTrackerIndex;
+ cbD->commands.append(cmd);
+
bool wantsColorClear, wantsDsClear;
QGles2RenderTargetData *rtD = enqueueBindFramebuffer(rt, cbD, &wantsColorClear, &wantsDsClear);
@@ -2160,6 +2546,8 @@ void QRhiGles2::beginPass(QRhiCommandBuffer *cb,
cbD->recordingPass = QGles2CommandBuffer::RenderPass;
cbD->currentTarget = rt;
+
+ cbD->resetCachedState();
}
void QRhiGles2::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
@@ -2212,7 +2600,16 @@ void QRhiGles2::beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch
if (resourceUpdates)
enqueueResourceUpdates(cb, resourceUpdates);
+ cbD->passResTrackers.append(QRhiPassResourceTracker());
+ cbD->currentPassResTrackerIndex = cbD->passResTrackers.count() - 1;
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::BarriersForPass;
+ cmd.args.barriersForPass.trackerIndex = cbD->currentPassResTrackerIndex;
+ cbD->commands.append(cmd);
+
cbD->recordingPass = QGles2CommandBuffer::ComputePass;
+
+ cbD->resetCachedState();
}
void QRhiGles2::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
@@ -2228,16 +2625,189 @@ void QRhiGles2::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *r
void QRhiGles2::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps)
{
- Q_UNUSED(cb);
- Q_UNUSED(ps);
+ QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::ComputePass);
+ QGles2ComputePipeline *psD = QRHI_RES(QGles2ComputePipeline, ps);
+ const bool pipelineChanged = cbD->currentComputePipeline != ps || cbD->currentPipelineGeneration != psD->generation;
+
+ if (pipelineChanged) {
+ cbD->currentGraphicsPipeline = nullptr;
+ cbD->currentComputePipeline = ps;
+ cbD->currentPipelineGeneration = psD->generation;
+
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::BindComputePipeline;
+ cmd.args.bindComputePipeline.ps = ps;
+ cbD->commands.append(cmd);
+ }
}
void QRhiGles2::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
{
- Q_UNUSED(cb);
- Q_UNUSED(x);
- Q_UNUSED(y);
- Q_UNUSED(z);
+ QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::ComputePass);
+
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::Dispatch;
+ cmd.args.dispatch.x = x;
+ cmd.args.dispatch.y = y;
+ cmd.args.dispatch.z = z;
+ cbD->commands.append(cmd);
+}
+
+static inline GLenum toGlShaderType(QRhiShaderStage::Type type)
+{
+ switch (type) {
+ case QRhiShaderStage::Vertex:
+ return GL_VERTEX_SHADER;
+ case QRhiShaderStage::Fragment:
+ return GL_FRAGMENT_SHADER;
+ case QRhiShaderStage::Compute:
+ return GL_COMPUTE_SHADER;
+ default:
+ Q_UNREACHABLE();
+ return GL_VERTEX_SHADER;
+ }
+}
+
+bool QRhiGles2::compileShader(GLuint program, const QRhiShaderStage &shaderStage,
+ QShaderDescription *desc, int *glslVersionUsed)
+{
+ GLuint shader = f->glCreateShader(toGlShaderType(shaderStage.type()));
+ const QShader bakedShader = shaderStage.shader();
+ QVector<int> versionsToTry;
+ QByteArray source;
+ if (caps.gles) {
+ if (caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2)) {
+ versionsToTry << 320 << 310 << 300 << 100;
+ } else if (caps.ctxMajor == 3 && caps.ctxMinor == 1) {
+ versionsToTry << 310 << 300 << 100;
+ } else if (caps.ctxMajor == 3 && caps.ctxMinor == 0) {
+ versionsToTry << 300 << 100;
+ } else {
+ versionsToTry << 100;
+ }
+ for (int v : versionsToTry) {
+ QShaderVersion ver(v, QShaderVersion::GlslEs);
+ source = bakedShader.shader({ QShader::GlslShader, ver, shaderStage.shaderVariant() }).shader();
+ if (!source.isEmpty()) {
+ if (glslVersionUsed)
+ *glslVersionUsed = v;
+ break;
+ }
+ }
+ } else {
+ if (caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 6)) {
+ versionsToTry << 460 << 450 << 440 << 430 << 420 << 410 << 400 << 330 << 150;
+ } else if (caps.ctxMajor == 4 && caps.ctxMinor == 5) {
+ versionsToTry << 450 << 440 << 430 << 420 << 410 << 400 << 330 << 150;
+ } else if (caps.ctxMajor == 4 && caps.ctxMinor == 4) {
+ versionsToTry << 440 << 430 << 420 << 410 << 400 << 330 << 150;
+ } else if (caps.ctxMajor == 4 && caps.ctxMinor == 3) {
+ versionsToTry << 430 << 420 << 410 << 400 << 330 << 150;
+ } else if (caps.ctxMajor == 4 && caps.ctxMinor == 2) {
+ versionsToTry << 420 << 410 << 400 << 330 << 150;
+ } else if (caps.ctxMajor == 4 && caps.ctxMinor == 1) {
+ versionsToTry << 410 << 400 << 330 << 150;
+ } else if (caps.ctxMajor == 4 && caps.ctxMinor == 0) {
+ versionsToTry << 400 << 330 << 150;
+ } else if (caps.ctxMajor == 3 && caps.ctxMinor == 3) {
+ versionsToTry << 330 << 150;
+ } else if (caps.ctxMajor == 3 && caps.ctxMinor == 2) {
+ versionsToTry << 150;
+ }
+ if (!caps.coreProfile)
+ versionsToTry << 120;
+ for (int v : versionsToTry) {
+ source = bakedShader.shader({ QShader::GlslShader, v, shaderStage.shaderVariant() }).shader();
+ if (!source.isEmpty()) {
+ if (glslVersionUsed)
+ *glslVersionUsed = v;
+ break;
+ }
+ }
+ }
+ if (source.isEmpty()) {
+ qWarning() << "No GLSL shader code found (versions tried: " << versionsToTry
+ << ") in baked shader" << bakedShader;
+ return false;
+ }
+
+ const char *srcStr = source.constData();
+ const GLint srcLength = source.count();
+ f->glShaderSource(shader, 1, &srcStr, &srcLength);
+ f->glCompileShader(shader);
+ GLint compiled = 0;
+ f->glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+ if (!compiled) {
+ GLint infoLogLength = 0;
+ f->glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength);
+ QByteArray log;
+ if (infoLogLength > 1) {
+ GLsizei length = 0;
+ log.resize(infoLogLength);
+ f->glGetShaderInfoLog(shader, infoLogLength, &length, log.data());
+ }
+ qWarning("Failed to compile shader: %s\nSource was:\n%s", log.constData(), source.constData());
+ return false;
+ }
+
+ f->glAttachShader(program, shader);
+ f->glDeleteShader(shader);
+
+ *desc = bakedShader.description();
+ return true;
+}
+
+bool QRhiGles2::linkProgram(GLuint program)
+{
+ f->glLinkProgram(program);
+ GLint linked = 0;
+ f->glGetProgramiv(program, GL_LINK_STATUS, &linked);
+ if (!linked) {
+ GLint infoLogLength = 0;
+ f->glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength);
+ QByteArray log;
+ if (infoLogLength > 1) {
+ GLsizei length = 0;
+ log.resize(infoLogLength);
+ f->glGetProgramInfoLog(program, infoLogLength, &length, log.data());
+ }
+ qWarning("Failed to link shader program: %s", log.constData());
+ return false;
+ }
+ return true;
+}
+
+void QRhiGles2::gatherUniforms(GLuint program, const QShaderDescription::UniformBlock &ub,
+ QVector<QGles2UniformDescription> *dst)
+{
+ const QByteArray prefix = ub.structName.toUtf8() + '.';
+ for (const QShaderDescription::BlockVariable &blockMember : ub.members) {
+ // ### no array support for now
+ QGles2UniformDescription uniform;
+ uniform.type = blockMember.type;
+ const QByteArray name = prefix + blockMember.name.toUtf8();
+ uniform.glslLocation = f->glGetUniformLocation(program, name.constData());
+ if (uniform.glslLocation >= 0) {
+ uniform.binding = ub.binding;
+ uniform.offset = blockMember.offset;
+ uniform.size = blockMember.size;
+ dst->append(uniform);
+ }
+ }
+}
+
+void QRhiGles2::gatherSamplers(GLuint program, const QShaderDescription::InOutVariable &v,
+ QVector<QGles2SamplerDescription> *dst)
+{
+ QGles2SamplerDescription sampler;
+ const QByteArray name = v.name.toUtf8();
+ sampler.glslLocation = f->glGetUniformLocation(program, name.constData());
+ if (sampler.glslLocation >= 0) {
+ sampler.binding = v.binding;
+ dst->append(sampler);
+ }
}
QGles2Buffer::QGles2Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size)
@@ -2277,34 +2847,34 @@ bool QGles2Buffer::build()
QRHI_RES_RHI(QRhiGles2);
QRHI_PROF;
+ const int nonZeroSize = m_size <= 0 ? 256 : m_size;
+
if (m_usage.testFlag(QRhiBuffer::UniformBuffer)) {
- if (m_usage.testFlag(QRhiBuffer::VertexBuffer) || m_usage.testFlag(QRhiBuffer::IndexBuffer)) {
+ if (int(m_usage) != QRhiBuffer::UniformBuffer) {
qWarning("Uniform buffer: multiple usages specified, this is not supported by the OpenGL backend");
return false;
}
- ubuf.resize(m_size);
- QRHI_PROF_F(newBuffer(this, m_size, 0, 1));
+ ubuf.resize(nonZeroSize);
+ QRHI_PROF_F(newBuffer(this, nonZeroSize, 0, 1));
return true;
}
if (!rhiD->ensureContext())
return false;
- if (m_usage.testFlag(QRhiBuffer::VertexBuffer)) {
- if (m_usage.testFlag(QRhiBuffer::IndexBuffer)) {
- qWarning("Vertex buffer: multiple usages specified, this is not supported by the OpenGL backend");
- return false;
- }
- target = GL_ARRAY_BUFFER;
- }
+ targetForDataOps = GL_ARRAY_BUFFER;
if (m_usage.testFlag(QRhiBuffer::IndexBuffer))
- target = GL_ELEMENT_ARRAY_BUFFER;
+ targetForDataOps = GL_ELEMENT_ARRAY_BUFFER;
+ else if (m_usage.testFlag(QRhiBuffer::StorageBuffer))
+ targetForDataOps = GL_SHADER_STORAGE_BUFFER;
rhiD->f->glGenBuffers(1, &buffer);
- rhiD->f->glBindBuffer(target, buffer);
- rhiD->f->glBufferData(target, m_size, nullptr, m_type == Dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
+ rhiD->f->glBindBuffer(targetForDataOps, buffer);
+ rhiD->f->glBufferData(targetForDataOps, nonZeroSize, nullptr, m_type == Dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
+
+ usageState.access = AccessNone;
- QRHI_PROF_F(newBuffer(this, m_size, 1, 0));
+ QRHI_PROF_F(newBuffer(this, nonZeroSize, 1, 0));
rhiD->registerResource(this);
return true;
}
@@ -2473,58 +3043,73 @@ bool QGles2Texture::prepareBuild(QSize *adjustedSize)
gltype = GL_UNSIGNED_BYTE;
if (isCompressed) {
+ if (m_flags.testFlag(UsedWithLoadStore)) {
+ qWarning("Compressed texture cannot be used with image load/store");
+ return false;
+ }
glintformat = toGlCompressedTextureFormat(m_format, m_flags);
if (!glintformat) {
qWarning("Compressed format %d not mappable to GL compressed format", m_format);
return false;
}
+ glsizedintformat = glintformat;
glformat = GL_RGBA;
} else {
switch (m_format) {
case QRhiTexture::RGBA8:
glintformat = GL_RGBA;
+ glsizedintformat = rhiD->caps.rgba8Format ? GL_RGBA8 : GL_RGBA;
glformat = GL_RGBA;
break;
case QRhiTexture::BGRA8:
glintformat = rhiD->caps.bgraInternalFormat ? GL_BGRA : GL_RGBA;
+ glsizedintformat = rhiD->caps.rgba8Format ? GL_RGBA8 : GL_RGBA;
glformat = GL_BGRA;
break;
case QRhiTexture::R16:
glintformat = GL_R16;
+ glsizedintformat = glintformat;
glformat = GL_RED;
gltype = GL_UNSIGNED_SHORT;
break;
case QRhiTexture::R8:
glintformat = GL_R8;
+ glsizedintformat = glintformat;
glformat = GL_RED;
break;
case QRhiTexture::RED_OR_ALPHA8:
glintformat = rhiD->caps.coreProfile ? GL_R8 : GL_ALPHA;
+ glsizedintformat = glintformat;
glformat = rhiD->caps.coreProfile ? GL_RED : GL_ALPHA;
break;
case QRhiTexture::RGBA16F:
glintformat = GL_RGBA16F;
+ glsizedintformat = glintformat;
glformat = GL_RGBA;
gltype = GL_HALF_FLOAT;
break;
case QRhiTexture::RGBA32F:
glintformat = GL_RGBA32F;
+ glsizedintformat = glintformat;
glformat = GL_RGBA;
gltype = GL_FLOAT;
break;
case QRhiTexture::D16:
glintformat = GL_DEPTH_COMPONENT16;
+ glsizedintformat = glintformat;
glformat = GL_DEPTH_COMPONENT;
gltype = GL_UNSIGNED_SHORT;
break;
case QRhiTexture::D32F:
glintformat = GL_DEPTH_COMPONENT32F;
+ glsizedintformat = glintformat;
glformat = GL_DEPTH_COMPONENT;
gltype = GL_FLOAT;
break;
default:
Q_UNREACHABLE();
glintformat = GL_RGBA;
+ glsizedintformat = rhiD->caps.rgba8Format ? GL_RGBA8 : GL_RGBA;
glformat = GL_RGBA;
break;
}
@@ -2532,6 +3117,8 @@ bool QGles2Texture::prepareBuild(QSize *adjustedSize)
samplerState = QGles2SamplerData();
+ usageState.access = AccessNone;
+
if (adjustedSize)
*adjustedSize = size;
@@ -2552,18 +3139,25 @@ bool QGles2Texture::build()
const bool isCompressed = rhiD->isCompressedFormat(m_format);
if (!isCompressed) {
rhiD->f->glBindTexture(target, texture);
- if (hasMipMaps || isCube) {
- const GLenum faceTargetBase = isCube ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : target;
- for (int layer = 0, layerCount = isCube ? 6 : 1; layer != layerCount; ++layer) {
- for (int level = 0; level != mipLevelCount; ++level) {
- const QSize mipSize = rhiD->q->sizeForMipLevel(level, size);
- rhiD->f->glTexImage2D(faceTargetBase + layer, level, glintformat,
- mipSize.width(), mipSize.height(), 0,
- glformat, gltype, nullptr);
+ if (!m_flags.testFlag(UsedWithLoadStore)) {
+ if (hasMipMaps || isCube) {
+ const GLenum faceTargetBase = isCube ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : target;
+ for (int layer = 0, layerCount = isCube ? 6 : 1; layer != layerCount; ++layer) {
+ for (int level = 0; level != mipLevelCount; ++level) {
+ const QSize mipSize = rhiD->q->sizeForMipLevel(level, size);
+ rhiD->f->glTexImage2D(faceTargetBase + layer, level, glsizedintformat,
+ mipSize.width(), mipSize.height(), 0,
+ glformat, gltype, nullptr);
+ }
}
+ } else {
+ rhiD->f->glTexImage2D(target, 0, glsizedintformat, size.width(), size.height(),
+ 0, glformat, gltype, nullptr);
}
} else {
- rhiD->f->glTexImage2D(target, 0, glintformat, size.width(), size.height(), 0, glformat, gltype, nullptr);
+ // Must be specified with immutable storage functions otherwise
+ // bindImageTexture may fail.
+ rhiD->f->glTexStorage2D(target, mipLevelCount, glsizedintformat, size.width(), size.height());
}
specified = true;
} else {
@@ -2900,161 +3494,39 @@ bool QGles2GraphicsPipeline::build()
program = rhiD->f->glCreateProgram();
- int sourceVer = 0;
+ QShaderDescription vsDesc;
+ QShaderDescription fsDesc;
for (const QRhiShaderStage &shaderStage : qAsConst(m_shaderStages)) {
const bool isVertex = shaderStage.type() == QRhiShaderStage::Vertex;
const bool isFragment = shaderStage.type() == QRhiShaderStage::Fragment;
- if (!isVertex && !isFragment)
- continue;
-
- GLuint shader = rhiD->f->glCreateShader(isVertex ? GL_VERTEX_SHADER : GL_FRAGMENT_SHADER);
- const QShader bakedShader = shaderStage.shader();
- QVector<int> versionsToTry;
- QByteArray source;
- if (rhiD->caps.gles) {
- if (rhiD->caps.ctxMajor > 3 || (rhiD->caps.ctxMajor == 3 && rhiD->caps.ctxMinor >= 2)) {
- versionsToTry << 320 << 310 << 300 << 100;
- } else if (rhiD->caps.ctxMajor == 3 && rhiD->caps.ctxMinor == 1) {
- versionsToTry << 310 << 300 << 100;
- } else if (rhiD->caps.ctxMajor == 3 && rhiD->caps.ctxMinor == 0) {
- versionsToTry << 300 << 100;
- } else {
- versionsToTry << 100;
- }
- for (int v : versionsToTry) {
- QShaderVersion ver(v, QShaderVersion::GlslEs);
- source = bakedShader.shader({ QShader::GlslShader, ver, shaderStage.shaderVariant() }).shader();
- if (!source.isEmpty()) {
- sourceVer = v;
- break;
- }
- }
- } else {
- if (rhiD->caps.ctxMajor > 4 || (rhiD->caps.ctxMajor == 4 && rhiD->caps.ctxMinor >= 6)) {
- versionsToTry << 460 << 450 << 440 << 430 << 420 << 410 << 400 << 330 << 150;
- } else if (rhiD->caps.ctxMajor == 4 && rhiD->caps.ctxMinor == 5) {
- versionsToTry << 450 << 440 << 430 << 420 << 410 << 400 << 330 << 150;
- } else if (rhiD->caps.ctxMajor == 4 && rhiD->caps.ctxMinor == 4) {
- versionsToTry << 440 << 430 << 420 << 410 << 400 << 330 << 150;
- } else if (rhiD->caps.ctxMajor == 4 && rhiD->caps.ctxMinor == 3) {
- versionsToTry << 430 << 420 << 410 << 400 << 330 << 150;
- } else if (rhiD->caps.ctxMajor == 4 && rhiD->caps.ctxMinor == 2) {
- versionsToTry << 420 << 410 << 400 << 330 << 150;
- } else if (rhiD->caps.ctxMajor == 4 && rhiD->caps.ctxMinor == 1) {
- versionsToTry << 410 << 400 << 330 << 150;
- } else if (rhiD->caps.ctxMajor == 4 && rhiD->caps.ctxMinor == 0) {
- versionsToTry << 400 << 330 << 150;
- } else if (rhiD->caps.ctxMajor == 3 && rhiD->caps.ctxMinor == 3) {
- versionsToTry << 330 << 150;
- } else if (rhiD->caps.ctxMajor == 3 && rhiD->caps.ctxMinor == 2) {
- versionsToTry << 150;
- }
- if (!rhiD->caps.coreProfile)
- versionsToTry << 120;
- for (int v : versionsToTry) {
- source = bakedShader.shader({ QShader::GlslShader, v, shaderStage.shaderVariant() }).shader();
- if (!source.isEmpty()) {
- sourceVer = v;
- break;
- }
- }
- }
- if (source.isEmpty()) {
- qWarning() << "No GLSL shader code found (versions tried: " << versionsToTry
- << ") in baked shader" << bakedShader;
- return false;
- }
-
- const char *srcStr = source.constData();
- const GLint srcLength = source.count();
- rhiD->f->glShaderSource(shader, 1, &srcStr, &srcLength);
- rhiD->f->glCompileShader(shader);
- GLint compiled = 0;
- rhiD->f->glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
- if (!compiled) {
- GLint infoLogLength = 0;
- rhiD->f->glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength);
- QByteArray log;
- if (infoLogLength > 1) {
- GLsizei length = 0;
- log.resize(infoLogLength);
- rhiD->f->glGetShaderInfoLog(shader, infoLogLength, &length, log.data());
- }
- qWarning("Failed to compile shader: %s\nSource was:\n%s", log.constData(), source.constData());
- return false;
+ if (isVertex) {
+ if (!rhiD->compileShader(program, shaderStage, &vsDesc, nullptr))
+ return false;
+ } else if (isFragment) {
+ if (!rhiD->compileShader(program, shaderStage, &fsDesc, nullptr))
+ return false;
}
-
- rhiD->f->glAttachShader(program, shader);
- rhiD->f->glDeleteShader(shader);
-
- if (isVertex)
- vsDesc = bakedShader.description();
- else
- fsDesc = bakedShader.description();
}
- // not very useful atm since we assume the qsb-generated GLSL shaders never use uniform blocks
- canUseUniformBuffers = rhiD->caps.uniformBuffers && sourceVer >= 140;
-
for (auto inVar : vsDesc.inputVariables()) {
const QByteArray name = inVar.name.toUtf8();
rhiD->f->glBindAttribLocation(program, inVar.location, name.constData());
}
- rhiD->f->glLinkProgram(program);
- GLint linked = 0;
- rhiD->f->glGetProgramiv(program, GL_LINK_STATUS, &linked);
- if (!linked) {
- GLint infoLogLength = 0;
- rhiD->f->glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength);
- QByteArray log;
- if (infoLogLength > 1) {
- GLsizei length = 0;
- log.resize(infoLogLength);
- rhiD->f->glGetProgramInfoLog(program, infoLogLength, &length, log.data());
- }
- qWarning("Failed to link shader program: %s", log.constData());
+ if (!rhiD->linkProgram(program))
return false;
- }
-
- auto lookupUniforms = [this, rhiD](const QShaderDescription::UniformBlock &ub) {
- const QByteArray prefix = ub.structName.toUtf8() + '.';
- for (const QShaderDescription::BlockVariable &blockMember : ub.members) {
- // ### no array support for now
- Uniform uniform;
- uniform.type = blockMember.type;
- const QByteArray name = prefix + blockMember.name.toUtf8();
- uniform.glslLocation = rhiD->f->glGetUniformLocation(program, name.constData());
- if (uniform.glslLocation >= 0) {
- uniform.binding = ub.binding;
- uniform.offset = blockMember.offset;
- uniform.size = blockMember.size;
- uniforms.append(uniform);
- }
- }
- };
for (const QShaderDescription::UniformBlock &ub : vsDesc.uniformBlocks())
- lookupUniforms(ub);
+ rhiD->gatherUniforms(program, ub, &uniforms);
for (const QShaderDescription::UniformBlock &ub : fsDesc.uniformBlocks())
- lookupUniforms(ub);
-
- auto lookupSamplers = [this, rhiD](const QShaderDescription::InOutVariable &v) {
- Sampler sampler;
- const QByteArray name = v.name.toUtf8();
- sampler.glslLocation = rhiD->f->glGetUniformLocation(program, name.constData());
- if (sampler.glslLocation >= 0) {
- sampler.binding = v.binding;
- samplers.append(sampler);
- }
- };
+ rhiD->gatherUniforms(program, ub, &uniforms);
for (const QShaderDescription::InOutVariable &v : vsDesc.combinedImageSamplers())
- lookupSamplers(v);
+ rhiD->gatherSamplers(program, v, &samplers);
for (const QShaderDescription::InOutVariable &v : fsDesc.combinedImageSamplers())
- lookupSamplers(v);
+ rhiD->gatherSamplers(program, v, &samplers);
generation += 1;
rhiD->registerResource(this);
@@ -3073,11 +3545,52 @@ QGles2ComputePipeline::~QGles2ComputePipeline()
void QGles2ComputePipeline::release()
{
+ if (!program)
+ return;
+
+ QRhiGles2::DeferredReleaseEntry e;
+ e.type = QRhiGles2::DeferredReleaseEntry::Pipeline;
+
+ e.pipeline.program = program;
+
+ program = 0;
+ uniforms.clear();
+ samplers.clear();
+
+ QRHI_RES_RHI(QRhiGles2);
+ rhiD->releaseQueue.append(e);
+
+ rhiD->unregisterResource(this);
}
bool QGles2ComputePipeline::build()
{
- return false;
+ QRHI_RES_RHI(QRhiGles2);
+
+ if (program)
+ release();
+
+ if (!rhiD->ensureContext())
+ return false;
+
+ program = rhiD->f->glCreateProgram();
+ QShaderDescription csDesc;
+
+ if (!rhiD->compileShader(program, m_shaderStage, &csDesc, nullptr))
+ return false;
+ if (!rhiD->linkProgram(program))
+ return false;
+
+ for (const QShaderDescription::UniformBlock &ub : csDesc.uniformBlocks())
+ rhiD->gatherUniforms(program, ub, &uniforms);
+ for (const QShaderDescription::InOutVariable &v : csDesc.combinedImageSamplers())
+ rhiD->gatherSamplers(program, v, &samplers);
+
+ // storage images and buffers need no special steps here
+
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
}
QGles2CommandBuffer::QGles2CommandBuffer(QRhiImplementation *rhi)
diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h
index d6682977ff..88a6144b42 100644
--- a/src/gui/rhi/qrhigles2_p_p.h
+++ b/src/gui/rhi/qrhigles2_p_p.h
@@ -66,8 +66,22 @@ struct QGles2Buffer : public QRhiBuffer
bool build() override;
GLuint buffer = 0;
- GLenum target;
+ GLenum targetForDataOps;
QByteArray ubuf;
+ enum Access {
+ AccessNone,
+ AccessVertex,
+ AccessIndex,
+ AccessUniform,
+ AccessStorageRead,
+ AccessStorageWrite,
+ AccessStorageReadWrite,
+ AccessUpdate
+ };
+ struct UsageState {
+ Access access;
+ };
+ UsageState usageState;
friend class QRhiGles2;
};
@@ -127,12 +141,27 @@ struct QGles2Texture : public QRhiTexture
bool owns = true;
GLenum target;
GLenum glintformat;
+ GLenum glsizedintformat;
GLenum glformat;
GLenum gltype;
QGles2SamplerData samplerState;
bool specified = false;
int mipLevelCount = 0;
QRhiGles2TextureNativeHandles nativeHandlesStruct;
+ enum Access {
+ AccessNone,
+ AccessSample,
+ AccessFramebuffer,
+ AccessStorageRead,
+ AccessStorageWrite,
+ AccessStorageReadWrite,
+ AccessUpdate,
+ AccessRead
+ };
+ struct UsageState {
+ Access access;
+ };
+ UsageState usageState;
uint generation = 0;
friend class QRhiGles2;
@@ -213,6 +242,25 @@ struct QGles2ShaderResourceBindings : public QRhiShaderResourceBindings
friend class QRhiGles2;
};
+struct QGles2UniformDescription
+{
+ QShaderDescription::VariableType type;
+ int glslLocation;
+ int binding;
+ uint offset;
+ int size;
+};
+
+Q_DECLARE_TYPEINFO(QGles2UniformDescription, Q_MOVABLE_TYPE);
+
+struct QGles2SamplerDescription
+{
+ int glslLocation;
+ int binding;
+};
+
+Q_DECLARE_TYPEINFO(QGles2SamplerDescription, Q_MOVABLE_TYPE);
+
struct QGles2GraphicsPipeline : public QRhiGraphicsPipeline
{
QGles2GraphicsPipeline(QRhiImplementation *rhi);
@@ -222,38 +270,24 @@ struct QGles2GraphicsPipeline : public QRhiGraphicsPipeline
GLuint program = 0;
GLenum drawMode = GL_TRIANGLES;
- QShaderDescription vsDesc;
- QShaderDescription fsDesc;
- bool canUseUniformBuffers = false;
-
- struct Uniform {
- QShaderDescription::VariableType type;
- int glslLocation;
- int binding;
- uint offset;
- int size;
- };
- QVector<Uniform> uniforms;
-
- struct Sampler {
- int glslLocation;
- int binding;
- };
- QVector<Sampler> samplers;
-
+ QVector<QGles2UniformDescription> uniforms;
+ QVector<QGles2SamplerDescription> samplers;
uint generation = 0;
friend class QRhiGles2;
};
-Q_DECLARE_TYPEINFO(QGles2GraphicsPipeline::Uniform, Q_MOVABLE_TYPE);
-Q_DECLARE_TYPEINFO(QGles2GraphicsPipeline::Sampler, Q_MOVABLE_TYPE);
-
struct QGles2ComputePipeline : public QRhiComputePipeline
{
QGles2ComputePipeline(QRhiImplementation *rhi);
~QGles2ComputePipeline();
void release() override;
bool build() override;
+
+ GLuint program = 0;
+ QVector<QGles2UniformDescription> uniforms;
+ QVector<QGles2SamplerDescription> samplers;
+ uint generation = 0;
+ friend class QRhiGles2;
};
struct QGles2CommandBuffer : public QRhiCommandBuffer
@@ -286,7 +320,11 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer
CompressedImage,
CompressedSubImage,
BlitFromRenderbuffer,
- GenMip
+ GenMip,
+ BindComputePipeline,
+ Dispatch,
+ BarriersForPass,
+ Barrier
};
Cmd cmd;
@@ -339,7 +377,8 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer
QRhiGraphicsPipeline *ps;
} bindGraphicsPipeline;
struct {
- QRhiGraphicsPipeline *ps;
+ QRhiGraphicsPipeline *maybeGraphicsPs;
+ QRhiComputePipeline *maybeComputePs;
QRhiShaderResourceBindings *srb;
int dynamicOffsetCount;
uint dynamicOffsetPairs[MAX_UBUF_BINDINGS * 2]; // binding, offsetInConstants
@@ -436,6 +475,20 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer
GLenum target;
GLuint texture;
} genMip;
+ struct {
+ QRhiComputePipeline *ps;
+ } bindComputePipeline;
+ struct {
+ GLuint x;
+ GLuint y;
+ GLuint z;
+ } dispatch;
+ struct {
+ int trackerIndex;
+ } barriersForPass;
+ struct {
+ GLbitfield barriers;
+ } barrier;
} args;
};
@@ -446,11 +499,16 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer
};
QVector<Command> commands;
+ QVarLengthArray<QRhiPassResourceTracker, 8> passResTrackers;
+ int currentPassResTrackerIndex;
+
PassType recordingPass;
QRhiRenderTarget *currentTarget;
- QRhiGraphicsPipeline *currentPipeline;
+ QRhiGraphicsPipeline *currentGraphicsPipeline;
+ QRhiComputePipeline *currentComputePipeline;
uint currentPipelineGeneration;
- QRhiShaderResourceBindings *currentSrb;
+ QRhiShaderResourceBindings *currentGraphicsSrb;
+ QRhiShaderResourceBindings *currentComputeSrb;
uint currentSrbGeneration;
QVector<QByteArray> dataRetainPool;
@@ -467,6 +525,12 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer
}
void resetCommands() {
commands.clear();
+ // beginExternal() can lead to calling resetCommands() inside a pass,
+ // hence the condition
+ if (recordingPass == NoPass) {
+ passResTrackers.clear();
+ currentPassResTrackerIndex = -1;
+ }
dataRetainPool.clear();
imageRetainPool.clear();
}
@@ -477,9 +541,11 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer
resetCachedState();
}
void resetCachedState() {
- currentPipeline = nullptr;
+ currentGraphicsPipeline = nullptr;
+ currentComputePipeline = nullptr;
currentPipelineGeneration = 0;
- currentSrb = nullptr;
+ currentGraphicsSrb = nullptr;
+ currentComputeSrb = nullptr;
currentSrbGeneration = 0;
}
};
@@ -606,17 +672,35 @@ public:
bool ensureContext(QSurface *surface = nullptr) const;
void executeDeferredReleases();
QRhi::FrameOpResult flushCommandBuffer();
+ void trackedBufferBarrier(QGles2CommandBuffer *cbD, QGles2Buffer *bufD, QGles2Buffer::Access access);
+ void trackedImageBarrier(QGles2CommandBuffer *cbD, QGles2Texture *texD, QGles2Texture::Access access);
void enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cbD,
int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc);
void enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates);
+ void trackedRegisterBuffer(QRhiPassResourceTracker *passResTracker,
+ QGles2Buffer *bufD,
+ QRhiPassResourceTracker::BufferAccess access,
+ QRhiPassResourceTracker::BufferStage stage);
+ void trackedRegisterTexture(QRhiPassResourceTracker *passResTracker,
+ QGles2Texture *texD,
+ QRhiPassResourceTracker::TextureAccess access,
+ QRhiPassResourceTracker::TextureStage stage);
void executeCommandBuffer(QRhiCommandBuffer *cb);
void executeBindGraphicsPipeline(QRhiGraphicsPipeline *ps);
- void bindShaderResources(QRhiGraphicsPipeline *ps, QRhiShaderResourceBindings *srb,
+ void bindShaderResources(QRhiGraphicsPipeline *maybeGraphicsPs, QRhiComputePipeline *maybeComputePs,
+ QRhiShaderResourceBindings *srb,
const uint *dynOfsPairs, int dynOfsCount);
QGles2RenderTargetData *enqueueBindFramebuffer(QRhiRenderTarget *rt, QGles2CommandBuffer *cbD,
bool *wantsColorClear = nullptr, bool *wantsDsClear = nullptr);
int effectiveSampleCount(int sampleCount) const;
QSize safeTextureSize(const QSize &size) const;
+ bool compileShader(GLuint program, const QRhiShaderStage &shaderStage,
+ QShaderDescription *desc, int *glslVersionUsed);
+ bool linkProgram(GLuint program);
+ void gatherUniforms(GLuint program, const QShaderDescription::UniformBlock &ub,
+ QVector<QGles2UniformDescription> *dst);
+ void gatherSamplers(GLuint program, const QShaderDescription::InOutVariable &v,
+ QVector<QGles2SamplerDescription> *dst);
QOpenGLContext *ctx = nullptr;
bool importedContext = false;
@@ -652,7 +736,8 @@ public:
depth24(false),
rgba8Format(false),
instancing(false),
- baseVertex(false)
+ baseVertex(false),
+ compute(false)
{ }
int ctxMajor;
int ctxMinor;
@@ -682,6 +767,7 @@ public:
uint rgba8Format : 1;
uint instancing : 1;
uint baseVertex : 1;
+ uint compute : 1;
} caps;
QGles2SwapChain *currentSwapChain = nullptr;
QVector<GLint> supportedCompressedFormats;
diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm
index fa537a504b..ffb2283ae7 100644
--- a/src/gui/rhi/qrhimetal.mm
+++ b/src/gui/rhi/qrhimetal.mm
@@ -57,7 +57,8 @@ QT_BEGIN_NAMESPACE
Textures are Private (device local) and a host visible staging buffer is
used to upload data to them. Does not rely on strong objects refs from
command buffers but does rely on the automatic resource tracking of the
- command encoders.
+ command encoders. Assumes that an autorelease pool (ideally per frame) is
+ available on the thread on which QRhi is used.
*/
#if __has_feature(objc_arc)
@@ -352,7 +353,7 @@ bool QRhiMetal::create(QRhi::Flags flags)
else
d->dev = MTLCreateSystemDefaultDevice();
- qDebug("Metal device: %s", qPrintable(QString::fromNSString([d->dev name])));
+ qCDebug(QRHI_LOG_INFO, "Metal device: %s", qPrintable(QString::fromNSString([d->dev name])));
if (importedCmdQueue)
[d->cmdQueue retain];
@@ -1214,10 +1215,12 @@ QRhi::FrameOpResult QRhiMetal::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame
Q_ASSERT(currentSwapChain == swapChainD);
const bool needsPresent = !flags.testFlag(QRhi::SkipPresent);
- if (needsPresent) {
+ if (needsPresent)
[swapChainD->cbWrapper.d->cb presentDrawable: swapChainD->d->curDrawable];
- swapChainD->d->curDrawable = nil;
- }
+
+ // Must not hold on to the drawable, regardless of needsPresent.
+ // (internally it is autoreleased or something, it seems)
+ swapChainD->d->curDrawable = nil;
__block int thisFrameSlot = currentFrameSlot;
[swapChainD->cbWrapper.d->cb addCompletedHandler: ^(id<MTLCommandBuffer>) {
@@ -3535,7 +3538,7 @@ bool QMetalSwapChain::buildOrResize()
rtWrapper.d->colorAttCount = 1;
rtWrapper.d->dsAttCount = ds ? 1 : 0;
- qDebug("got CAMetalLayer, size %dx%d", pixelSize.width(), pixelSize.height());
+ qCDebug(QRHI_LOG_INFO, "got CAMetalLayer, size %dx%d", pixelSize.width(), pixelSize.height());
if (samples > 1) {
MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp
index 7c4eeaf226..61a1595a50 100644
--- a/src/gui/rhi/qrhivulkan.cpp
+++ b/src/gui/rhi/qrhivulkan.cpp
@@ -360,14 +360,14 @@ bool QRhiVulkan::create(QRhi::Flags flags)
requestedPhysDevIndex = qEnvironmentVariableIntValue("QT_VK_PHYSICAL_DEVICE_INDEX");
for (uint32_t i = 0; i < physDevCount; ++i) {
f->vkGetPhysicalDeviceProperties(physDevs[i], &physDevProperties);
- qDebug("Physical device %d: '%s' %d.%d.%d", i,
- physDevProperties.deviceName,
- VK_VERSION_MAJOR(physDevProperties.driverVersion),
- VK_VERSION_MINOR(physDevProperties.driverVersion),
- VK_VERSION_PATCH(physDevProperties.driverVersion));
+ qCDebug(QRHI_LOG_INFO, "Physical device %d: '%s' %d.%d.%d", i,
+ physDevProperties.deviceName,
+ VK_VERSION_MAJOR(physDevProperties.driverVersion),
+ VK_VERSION_MINOR(physDevProperties.driverVersion),
+ VK_VERSION_PATCH(physDevProperties.driverVersion));
if (physDevIndex < 0 && (requestedPhysDevIndex < 0 || requestedPhysDevIndex == int(i))) {
physDevIndex = i;
- qDebug(" using this physical device");
+ qCDebug(QRHI_LOG_INFO, " using this physical device");
}
}
if (physDevIndex < 0) {
@@ -386,7 +386,8 @@ bool QRhiVulkan::create(QRhi::Flags flags)
gfxQueueFamilyIdx = -1;
int computelessGfxQueueCandidateIdx = -1;
for (int i = 0; i < queueFamilyProps.count(); ++i) {
- qDebug("queue family %d: flags=0x%x count=%d", i, queueFamilyProps[i].queueFlags, queueFamilyProps[i].queueCount);
+ qCDebug(QRHI_LOG_INFO, "queue family %d: flags=0x%x count=%d",
+ i, queueFamilyProps[i].queueFlags, queueFamilyProps[i].queueCount);
if (gfxQueueFamilyIdx == -1
&& (queueFamilyProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
&& (!maybeWindow || inst->supportsPresent(physDev, i, maybeWindow)))
@@ -422,7 +423,7 @@ bool QRhiVulkan::create(QRhi::Flags flags)
f->vkEnumerateDeviceExtensionProperties(physDev, nullptr, &devExtCount, nullptr);
QVector<VkExtensionProperties> devExts(devExtCount);
f->vkEnumerateDeviceExtensionProperties(physDev, nullptr, &devExtCount, devExts.data());
- qDebug("%d device extensions available", devExts.count());
+ qCDebug(QRHI_LOG_INFO, "%d device extensions available", devExts.count());
QVector<const char *> requestedDevExts;
requestedDevExts.append("VK_KHR_swapchain");
@@ -1244,9 +1245,9 @@ bool QRhiVulkan::recreateSwapChain(QRhiSwapChain *swapChain)
// with VK_ERROR_NATIVE_WINDOW_IN_USE_KHR if the old swapchain is provided)
const bool reuseExisting = swapChainD->sc && swapChainD->lastConnectedSurface == swapChainD->surface;
- qDebug("Creating %s swapchain of %u buffers, size %dx%d, presentation mode %d",
- reuseExisting ? "recycled" : "new",
- reqBufferCount, swapChainD->pixelSize.width(), swapChainD->pixelSize.height(), presentMode);
+ qCDebug(QRHI_LOG_INFO, "Creating %s swapchain of %u buffers, size %dx%d, presentation mode %d",
+ reuseExisting ? "recycled" : "new",
+ reqBufferCount, swapChainD->pixelSize.width(), swapChainD->pixelSize.height(), presentMode);
VkSwapchainCreateInfoKHR swapChainInfo;
memset(&swapChainInfo, 0, sizeof(swapChainInfo));
@@ -1290,7 +1291,7 @@ bool QRhiVulkan::recreateSwapChain(QRhiSwapChain *swapChain)
return false;
}
if (actualSwapChainBufferCount != reqBufferCount)
- qDebug("Actual swapchain buffer count is %u", actualSwapChainBufferCount);
+ qCDebug(QRHI_LOG_INFO, "Actual swapchain buffer count is %u", actualSwapChainBufferCount);
swapChainD->bufferCount = actualSwapChainBufferCount;
VkImage swapChainImages[QVkSwapChain::MAX_BUFFER_COUNT];
@@ -1661,6 +1662,10 @@ QRhi::FrameOpResult QRhiVulkan::endFrame(QRhiSwapChain *swapChain, QRhi::EndFram
}
}
+ // Do platform-specific WM notification. F.ex. essential on X11 in
+ // order to prevent glitches on resizing the window.
+ inst->presentQueued(swapChainD->window);
+
// mark the current swapchain buffer as unused from our side
frame.imageAcquired = false;
// and move on to the next buffer
@@ -3660,34 +3665,6 @@ void QRhiVulkan::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline
psD->lastActiveFrameSlot = currentFrameSlot;
}
-QRhiPassResourceTracker::BufferStage toPassTrackerBufferStage(QRhiShaderResourceBinding::StageFlags stages)
-{
- // pick the earlier stage (as this is going to be dstAccessMask)
- if (stages.testFlag(QRhiShaderResourceBinding::VertexStage))
- return QRhiPassResourceTracker::BufVertexStage;
- if (stages.testFlag(QRhiShaderResourceBinding::FragmentStage))
- return QRhiPassResourceTracker::BufFragmentStage;
- if (stages.testFlag(QRhiShaderResourceBinding::ComputeStage))
- return QRhiPassResourceTracker::BufComputeStage;
-
- Q_UNREACHABLE();
- return QRhiPassResourceTracker::BufVertexStage;
-}
-
-QRhiPassResourceTracker::TextureStage toPassTrackerTextureStage(QRhiShaderResourceBinding::StageFlags stages)
-{
- // pick the earlier stage (as this is going to be dstAccessMask)
- if (stages.testFlag(QRhiShaderResourceBinding::VertexStage))
- return QRhiPassResourceTracker::TexVertexStage;
- if (stages.testFlag(QRhiShaderResourceBinding::FragmentStage))
- return QRhiPassResourceTracker::TexFragmentStage;
- if (stages.testFlag(QRhiShaderResourceBinding::ComputeStage))
- return QRhiPassResourceTracker::TexComputeStage;
-
- Q_UNREACHABLE();
- return QRhiPassResourceTracker::TexVertexStage;
-}
-
void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb,
int dynamicOffsetCount,
const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
@@ -3743,7 +3720,7 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin
bufD->lastActiveFrameSlot = currentFrameSlot;
trackedRegisterBuffer(&passResTracker, bufD, bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0,
QRhiPassResourceTracker::BufUniformRead,
- toPassTrackerBufferStage(b->stage));
+ QRhiPassResourceTracker::toPassTrackerBufferStage(b->stage));
// Check both the "local" id (the generation counter) and the
// global id. The latter is relevant when a newly allocated
@@ -3764,7 +3741,7 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin
samplerD->lastActiveFrameSlot = currentFrameSlot;
trackedRegisterTexture(&passResTracker, texD,
QRhiPassResourceTracker::TexSample,
- toPassTrackerTextureStage(b->stage));
+ QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage));
if (texD->generation != bd.stex.texGeneration
|| texD->m_id != bd.stex.texId
@@ -3797,7 +3774,7 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin
access = QRhiPassResourceTracker::TexStorageLoadStore;
trackedRegisterTexture(&passResTracker, texD,
access,
- toPassTrackerTextureStage(b->stage));
+ QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage));
if (texD->generation != bd.simage.generation || texD->m_id != bd.simage.id) {
rewriteDescSet = true;
@@ -3828,7 +3805,7 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin
access = QRhiPassResourceTracker::BufStorageLoadStore;
trackedRegisterBuffer(&passResTracker, bufD, bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0,
access,
- toPassTrackerBufferStage(b->stage));
+ QRhiPassResourceTracker::toPassTrackerBufferStage(b->stage));
if (bufD->generation != bd.sbuf.generation || bufD->m_id != bd.sbuf.id) {
rewriteDescSet = true;
diff --git a/src/gui/text/qdistancefield.cpp b/src/gui/text/qdistancefield.cpp
index 17824a5ba1..d8a971c7b7 100644
--- a/src/gui/text/qdistancefield.cpp
+++ b/src/gui/text/qdistancefield.cpp
@@ -899,9 +899,6 @@ QDistanceField::QDistanceField(int width, int height)
{
}
-QDistanceField &QDistanceField::operator=(const QDistanceField &)
- = default;
-
QDistanceField::QDistanceField(const QRawFont &font, glyph_t glyph, bool doubleResolution)
{
setGlyph(font, glyph, doubleResolution);
diff --git a/src/gui/text/qdistancefield_p.h b/src/gui/text/qdistancefield_p.h
index d6d8edd85d..823bfaf1c6 100644
--- a/src/gui/text/qdistancefield_p.h
+++ b/src/gui/text/qdistancefield_p.h
@@ -94,7 +94,6 @@ public:
QDistanceField(const QRawFont &font, glyph_t glyph, bool doubleResolution = false);
QDistanceField(QFontEngine *fontEngine, glyph_t glyph, bool doubleResolution = false);
QDistanceField(const QPainterPath &path, glyph_t glyph, bool doubleResolution = false);
- QDistanceField &operator=(const QDistanceField &other);
bool isNull() const;
diff --git a/src/gui/text/qfontengine.cpp b/src/gui/text/qfontengine.cpp
index 537d4bcefd..4198df6e43 100644
--- a/src/gui/text/qfontengine.cpp
+++ b/src/gui/text/qfontengine.cpp
@@ -899,7 +899,7 @@ QImage QFontEngine::alphaRGBMapForGlyph(glyph_t glyph, QFixed /*subPixelPosition
return rgbMask;
}
-QImage QFontEngine::bitmapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform&)
+QImage QFontEngine::bitmapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform&, const QColor &)
{
Q_UNUSED(subPixelPosition);
@@ -1075,7 +1075,10 @@ void QFontEngine::setGlyphCache(const void *context, QFontEngineGlyphCache *cach
}
-QFontEngineGlyphCache *QFontEngine::glyphCache(const void *context, GlyphFormat format, const QTransform &transform) const
+QFontEngineGlyphCache *QFontEngine::glyphCache(const void *context,
+ GlyphFormat format,
+ const QTransform &transform,
+ const QColor &color) const
{
const QHash<const void*, GlyphCaches>::const_iterator caches = m_glyphCaches.constFind(context);
if (caches == m_glyphCaches.cend())
@@ -1083,8 +1086,11 @@ QFontEngineGlyphCache *QFontEngine::glyphCache(const void *context, GlyphFormat
for (auto &e : *caches) {
QFontEngineGlyphCache *cache = e.cache.data();
- if (format == cache->glyphFormat() && qtransform_equals_no_translate(cache->m_transform, transform))
+ if (format == cache->glyphFormat()
+ && (format != Format_ARGB || color == cache->color())
+ && qtransform_equals_no_translate(cache->m_transform, transform)) {
return cache;
+ }
}
return nullptr;
diff --git a/src/gui/text/qfontengine_p.h b/src/gui/text/qfontengine_p.h
index 8dcfd7d66c..e20b52cb65 100644
--- a/src/gui/text/qfontengine_p.h
+++ b/src/gui/text/qfontengine_p.h
@@ -189,7 +189,7 @@ public:
virtual QImage alphaMapForGlyph(glyph_t, const QTransform &t);
virtual QImage alphaMapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform &t);
virtual QImage alphaRGBMapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform &t);
- virtual QImage bitmapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform &t);
+ virtual QImage bitmapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform &t, const QColor &color = QColor());
virtual QImage *lockedAlphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition,
GlyphFormat neededFormat,
const QTransform &t = QTransform(),
@@ -251,7 +251,7 @@ public:
void clearGlyphCache(const void *key);
void setGlyphCache(const void *key, QFontEngineGlyphCache *data);
- QFontEngineGlyphCache *glyphCache(const void *key, GlyphFormat format, const QTransform &transform) const;
+ QFontEngineGlyphCache *glyphCache(const void *key, GlyphFormat format, const QTransform &transform, const QColor &color = QColor()) const;
static const uchar *getCMap(const uchar *table, uint tableSize, bool *isSymbolFont, int *cmapSize);
static quint32 getTrueTypeGlyphIndex(const uchar *cmap, int cmapSize, uint unicode);
diff --git a/src/gui/text/qfontengineglyphcache_p.h b/src/gui/text/qfontengineglyphcache_p.h
index fd5db1ecf5..532be10a83 100644
--- a/src/gui/text/qfontengineglyphcache_p.h
+++ b/src/gui/text/qfontengineglyphcache_p.h
@@ -65,7 +65,10 @@ QT_BEGIN_NAMESPACE
class Q_GUI_EXPORT QFontEngineGlyphCache: public QSharedData
{
public:
- QFontEngineGlyphCache(QFontEngine::GlyphFormat format, const QTransform &matrix) : m_format(format), m_transform(matrix)
+ QFontEngineGlyphCache(QFontEngine::GlyphFormat format, const QTransform &matrix, const QColor &color = QColor())
+ : m_format(format)
+ , m_transform(matrix)
+ , m_color(color)
{
Q_ASSERT(m_format != QFontEngine::Format_None);
}
@@ -74,9 +77,11 @@ public:
QFontEngine::GlyphFormat glyphFormat() const { return m_format; }
const QTransform &transform() const { return m_transform; }
+ const QColor &color() const { return m_color; }
QFontEngine::GlyphFormat m_format;
QTransform m_transform;
+ QColor m_color;
};
typedef QHash<void *, QList<QFontEngineGlyphCache *> > GlyphPointerHash;
typedef QHash<int, QList<QFontEngineGlyphCache *> > GlyphIntHash;
diff --git a/src/gui/text/qtextobject.cpp b/src/gui/text/qtextobject.cpp
index 1302bd66bb..b845889c3d 100644
--- a/src/gui/text/qtextobject.cpp
+++ b/src/gui/text/qtextobject.cpp
@@ -1322,7 +1322,7 @@ QTextList *QTextBlock::textList() const
QTextBlockUserData *QTextBlock::userData() const
{
if (!p || !n)
- return 0;
+ return nullptr;
const QTextBlockData *b = p->blockMap().fragment(n);
return b->userData;
diff --git a/src/gui/util/qdesktopservices.cpp b/src/gui/util/qdesktopservices.cpp
index ee0ff4c6ef..99214c4960 100644
--- a/src/gui/util/qdesktopservices.cpp
+++ b/src/gui/util/qdesktopservices.cpp
@@ -290,6 +290,7 @@ void QDesktopServices::unsetUrlHandler(const QString &scheme)
setUrlHandler(scheme, 0, 0);
}
+#if QT_DEPRECATED_SINCE(5, 0)
/*!
\enum QDesktopServices::StandardLocation
\since 4.4
@@ -344,6 +345,7 @@ void QDesktopServices::unsetUrlHandler(const QString &scheme)
\obsolete
Use QStandardPaths::displayName()
*/
+#endif
extern Q_CORE_EXPORT QString qt_applicationName_noFallback();