From 97891aa19765591ed9c85952e8eac53459167315 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 7 Jun 2019 16:27:44 +0200 Subject: Fix Vulkan yes OpenGL no builds The format conversion relies on the QOpenGLTexture header that has its body ifdefed out with -no-opengl. However, Qt, in a very forward looking manner, allows having Vulkan support even when OpenGL is disabled. Assuming that the format conversion will not be used in -no-opengl builds, put the entire function inside a #if QT_CONFIG(opengl). Change-Id: I772e12129729d69b81159669d239ea3945a42e5a Reviewed-by: Johan Helsing --- src/platformsupport/vkconvenience/qvkconvenience.cpp | 2 ++ src/platformsupport/vkconvenience/qvkconvenience_p.h | 2 ++ 2 files changed, 4 insertions(+) (limited to 'src') diff --git a/src/platformsupport/vkconvenience/qvkconvenience.cpp b/src/platformsupport/vkconvenience/qvkconvenience.cpp index 462cdc9e0d..acde1d1bda 100644 --- a/src/platformsupport/vkconvenience/qvkconvenience.cpp +++ b/src/platformsupport/vkconvenience/qvkconvenience.cpp @@ -51,6 +51,7 @@ QT_BEGIN_NAMESPACE \ingroup qpa */ +#if QT_CONFIG(opengl) VkFormat QVkConvenience::vkFormatFromGlFormat(uint glFormat) { using GlFormat = QOpenGLTexture::TextureFormat; @@ -211,5 +212,6 @@ VkFormat QVkConvenience::vkFormatFromGlFormat(uint glFormat) default: return VK_FORMAT_UNDEFINED; } } +#endif QT_END_NAMESPACE diff --git a/src/platformsupport/vkconvenience/qvkconvenience_p.h b/src/platformsupport/vkconvenience/qvkconvenience_p.h index 1dd1dfc4a7..580271b593 100644 --- a/src/platformsupport/vkconvenience/qvkconvenience_p.h +++ b/src/platformsupport/vkconvenience/qvkconvenience_p.h @@ -59,7 +59,9 @@ QT_BEGIN_NAMESPACE class QVkConvenience { public: +#if QT_CONFIG(opengl) static VkFormat vkFormatFromGlFormat(uint glFormat); +#endif }; QT_END_NAMESPACE -- cgit v1.2.3 From 45373c19243aea335897ba0f371a1dd53ae8f079 Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Mon, 10 Jun 2019 00:10:53 +0200 Subject: Deprecate QLatin1Literal It's an undocumented typedef for QLatin1String. [ChangeLog][QtCore][QLatin1Literal] The undocumented QLatin1Literal type alias for QLatin1String is now deprecated. Use QLatin1String instead. Change-Id: I05eba8b857454e59b9b9d7b07c42fe6fc9c77fec Reviewed-by: Thiago Macieira --- src/corelib/tools/qstring.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index da8260a999..a885ad1412 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -214,7 +214,9 @@ private: Q_DECLARE_TYPEINFO(QLatin1String, Q_MOVABLE_TYPE); // Qt 4.x compatibility -typedef QLatin1String QLatin1Literal; +#if QT_DEPRECATED_SINCE(5, 14) +QT_DEPRECATED_X("Use QLatin1String") typedef QLatin1String QLatin1Literal; +#endif // // QLatin1String inline implementations -- cgit v1.2.3 From 972a0402be4d7aed0ebbd0ff56b5018cda01599d Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 3 Dec 2018 12:38:45 +0100 Subject: Use insert instead of insertMulti when operating on a QMultiHash Change-Id: I96ebf6954d0a137f9f6f9632cdad6e18750519fe Reviewed-by: Mikhail Svetkin Reviewed-by: Marc Mutz --- src/gui/text/qtextdocumentlayout.cpp | 2 +- src/widgets/widgets/qwidgettextcontrol.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/gui/text/qtextdocumentlayout.cpp b/src/gui/text/qtextdocumentlayout.cpp index e5cfa7f46e..638dd5a5a8 100644 --- a/src/gui/text/qtextdocumentlayout.cpp +++ b/src/gui/text/qtextdocumentlayout.cpp @@ -1614,7 +1614,7 @@ QRectF QTextDocumentLayoutPrivate::layoutTable(QTextTable *table, int layoutFrom for (int i = 0; i < children.count(); ++i) { QTextFrame *frame = children.at(i); QTextTableCell cell = table->cellAt(frame->firstPosition()); - td->childFrameMap.insertMulti(cell.row() + cell.column() * rows, frame); + td->childFrameMap.insert(cell.row() + cell.column() * rows, frame); } } diff --git a/src/widgets/widgets/qwidgettextcontrol.cpp b/src/widgets/widgets/qwidgettextcontrol.cpp index af3b03cd9e..081acb0998 100644 --- a/src/widgets/widgets/qwidgettextcontrol.cpp +++ b/src/widgets/widgets/qwidgettextcontrol.cpp @@ -2479,7 +2479,7 @@ void QWidgetTextControl::setExtraSelections(const QList hash; for (int i = 0; i < d->extraSelections.count(); ++i) { const QAbstractTextDocumentLayout::Selection &esel = d->extraSelections.at(i); - hash.insertMulti(esel.cursor.anchor(), i); + hash.insert(esel.cursor.anchor(), i); } for (int i = 0; i < selections.count(); ++i) { -- cgit v1.2.3 From c66c4a844f7fcdc071fb5b57474a3d1cd54047c0 Mon Sep 17 00:00:00 2001 From: Mikhail Svetkin Date: Tue, 20 Mar 2018 14:41:48 +0100 Subject: rtems: Fix detection bad file descriptor RTEMS does not support poll/select and etc for files and fcntl(fd, F_GETFD) can not say that. Change-Id: If5ad160cd81e347fac72d2bafcb5b5bb815ed059 Reviewed-by: Ryan Chu Reviewed-by: Volker Hilsheimer --- src/corelib/kernel/qpoll.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src') diff --git a/src/corelib/kernel/qpoll.cpp b/src/corelib/kernel/qpoll.cpp index 8e93c4d363..8b87bfe79d 100644 --- a/src/corelib/kernel/qpoll.cpp +++ b/src/corelib/kernel/qpoll.cpp @@ -39,6 +39,10 @@ #include "qcore_unix_p.h" +#ifdef Q_OS_RTEMS +#include +#endif + QT_BEGIN_NAMESPACE #define QT_POLL_READ_MASK (POLLIN | POLLRDNORM) @@ -135,6 +139,11 @@ static inline int qt_poll_sweep(struct pollfd *fds, nfds_t nfds, static inline bool qt_poll_is_bad_fd(int fd) { +#ifdef Q_OS_RTEMS + if (!rtems_bsdnet_fdToSocket(fd)) + return true; +#endif + int ret; EINTR_LOOP(ret, fcntl(fd, F_GETFD)); return (ret == -1 && errno == EBADF); -- cgit v1.2.3 From 910a0aedbb68249e3c9175e09b3d8857460015ea Mon Sep 17 00:00:00 2001 From: Mikhail Svetkin Date: Tue, 11 Jun 2019 15:24:36 +0200 Subject: qtlite: Fix build with -no-feature-texthtmlparser Change-Id: I3ffd4612884f57c2d0ff8e9c9c10d0805dd72f6f Reviewed-by: Shawn Rutledge --- src/gui/text/qtextmarkdownimporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/gui/text/qtextmarkdownimporter.cpp b/src/gui/text/qtextmarkdownimporter.cpp index 8a9bb3953c..b96263f5fc 100644 --- a/src/gui/text/qtextmarkdownimporter.cpp +++ b/src/gui/text/qtextmarkdownimporter.cpp @@ -440,7 +440,7 @@ int QTextMarkdownImporter::cbText(int textType, const char *text, unsigned size) #endif case MD_TEXT_HTML: // count how many tags are opened and how many are closed -#if QT_CONFIG(regularexpression) +#if QT_CONFIG(regularexpression) && QT_CONFIG(texthtmlparser) { int startIdx = 0; while ((startIdx = s.indexOf(openingBracket, startIdx)) >= 0) { -- cgit v1.2.3 From b4ead572501179244aa036e7a590fa7536929f2b Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Tue, 14 May 2019 15:04:18 +0200 Subject: Move away from using 0 as a pointer constant Cleans up most of corelib to use nullptr or default enums where appropriate. Change-Id: Ifcaac14ecdaaee730f87f10941db3ce407d71ef9 Reviewed-by: Thiago Macieira --- src/corelib/codecs/qlatincodec.cpp | 4 +- src/corelib/codecs/qtextcodec.cpp | 24 +-- src/corelib/global/qglobal.cpp | 2 +- src/corelib/global/qlogging.cpp | 14 +- src/corelib/global/qmalloc.cpp | 8 +- src/corelib/io/qabstractfileengine.cpp | 10 +- src/corelib/io/qbuffer.cpp | 2 +- src/corelib/io/qdir.cpp | 4 +- src/corelib/io/qfile.cpp | 6 +- src/corelib/io/qfiledevice.cpp | 4 +- src/corelib/io/qfileinfo.cpp | 16 +- src/corelib/io/qfilesystemengine.cpp | 4 +- src/corelib/io/qfilesystemengine_unix.cpp | 4 +- src/corelib/io/qfilesystemiterator_unix.cpp | 6 +- src/corelib/io/qfsfileengine.cpp | 16 +- src/corelib/io/qfsfileengine_unix.cpp | 20 +-- src/corelib/io/qiodevice.cpp | 2 +- src/corelib/io/qipaddress.cpp | 8 +- src/corelib/io/qloggingcategory.cpp | 8 +- src/corelib/io/qloggingregistry.cpp | 4 +- src/corelib/io/qresource.cpp | 18 ++- src/corelib/io/qsavefile.cpp | 12 +- src/corelib/io/qtemporaryfile.cpp | 6 +- src/corelib/io/qurl.cpp | 34 ++--- src/corelib/io/qurlidna.cpp | 6 +- src/corelib/io/qurlquery.cpp | 10 +- src/corelib/io/qurlrecode.cpp | 2 +- src/corelib/kernel/qcoreapplication.cpp | 22 +-- src/corelib/kernel/qcoreglobaldata.cpp | 4 +- src/corelib/kernel/qmetatype.cpp | 66 +++++---- src/corelib/kernel/qmetatype_p.h | 16 +- src/corelib/kernel/qmetatypeswitcher_p.h | 2 +- src/corelib/kernel/qsystemerror.cpp | 2 +- src/corelib/kernel/qvariant.cpp | 44 +++--- src/corelib/serialization/qdatastream.cpp | 14 +- src/corelib/serialization/qjsonarray.cpp | 6 +- src/corelib/serialization/qjsondocument.cpp | 10 +- src/corelib/serialization/qjsonobject.cpp | 6 +- src/corelib/serialization/qjsonparser.cpp | 4 +- src/corelib/serialization/qjsonvalue.cpp | 16 +- src/corelib/serialization/qtextstream.cpp | 26 ++-- src/corelib/serialization/qxmlstream.cpp | 26 ++-- src/corelib/serialization/qxmlstream.g | 2 +- src/corelib/time/qdatetime.cpp | 30 ++-- src/corelib/tools/qarraydata.cpp | 2 +- src/corelib/tools/qbytearray.cpp | 6 +- src/corelib/tools/qbytearraymatcher.cpp | 10 +- src/corelib/tools/qchar.cpp | 2 +- src/corelib/tools/qhash.cpp | 6 +- src/corelib/tools/qlist.cpp | 2 +- src/corelib/tools/qlocale.cpp | 8 +- src/corelib/tools/qlocale_p.h | 4 +- src/corelib/tools/qlocale_tools.cpp | 4 +- src/corelib/tools/qmap.cpp | 30 ++-- src/corelib/tools/qregexp.cpp | 20 +-- src/corelib/tools/qringbuffer.cpp | 2 +- src/corelib/tools/qstring.cpp | 10 +- src/corelib/tools/qstringmatcher.cpp | 6 +- src/xml/dom/qdom.cpp | 220 ++++++++++++++-------------- src/xml/sax/qxml.cpp | 110 +++++++------- 60 files changed, 502 insertions(+), 490 deletions(-) (limited to 'src') diff --git a/src/corelib/codecs/qlatincodec.cpp b/src/corelib/codecs/qlatincodec.cpp index 1b53d26ef4..463c5a56ae 100644 --- a/src/corelib/codecs/qlatincodec.cpp +++ b/src/corelib/codecs/qlatincodec.cpp @@ -48,7 +48,7 @@ QLatin1Codec::~QLatin1Codec() QString QLatin1Codec::convertToUnicode(const char *chars, int len, ConverterState *) const { - if (chars == 0) + if (chars == nullptr) return QString(); return QString::fromLatin1(chars, len); @@ -104,7 +104,7 @@ QLatin15Codec::~QLatin15Codec() QString QLatin15Codec::convertToUnicode(const char* chars, int len, ConverterState *) const { - if (chars == 0) + if (chars == nullptr) return QString(); QString str = QString::fromLatin1(chars, len); diff --git a/src/corelib/codecs/qtextcodec.cpp b/src/corelib/codecs/qtextcodec.cpp index a86db142f9..85cfcdbf48 100644 --- a/src/corelib/codecs/qtextcodec.cpp +++ b/src/corelib/codecs/qtextcodec.cpp @@ -159,7 +159,7 @@ static QTextCodec *setupLocaleMapper() { QCoreGlobalData *globalData = QCoreGlobalData::instance(); - QTextCodec *locale = 0; + QTextCodec *locale = nullptr; { QMutexLocker locker(textCodecsMutex()); @@ -208,7 +208,7 @@ static QTextCodec *setupLocaleMapper() // First part is getting that locale name. First try setlocale() which // definitely knows it, but since we cannot fully trust it, get ready // to fall back to environment variables. - const QByteArray ctype = setlocale(LC_CTYPE, 0); + const QByteArray ctype = setlocale(LC_CTYPE, nullptr); // Get the first nonempty value from $LC_ALL, $LC_CTYPE, and $LANG // environment variables. @@ -532,13 +532,13 @@ QTextCodec::~QTextCodec() QTextCodec *QTextCodec::codecForName(const QByteArray &name) { if (name.isEmpty()) - return 0; + return nullptr; QMutexLocker locker(textCodecsMutex()); QCoreGlobalData *globalData = QCoreGlobalData::instance(); if (!globalData) - return 0; + return nullptr; setup(); #if !QT_CONFIG(icu) @@ -567,7 +567,7 @@ QTextCodec *QTextCodec::codecForName(const QByteArray &name) } } - return 0; + return nullptr; #else return QIcuCodec::codecForNameUnlocked(name); #endif @@ -585,7 +585,7 @@ QTextCodec* QTextCodec::codecForMib(int mib) QCoreGlobalData *globalData = QCoreGlobalData::instance(); if (!globalData) - return 0; + return nullptr; if (globalData->allCodecs.isEmpty()) setup(); @@ -611,7 +611,7 @@ QTextCodec* QTextCodec::codecForMib(int mib) #if QT_CONFIG(icu) return QIcuCodec::codecForMibUnlocked(mib); #else - return 0; + return nullptr; #endif } @@ -704,7 +704,7 @@ QTextCodec* QTextCodec::codecForLocale() { QCoreGlobalData *globalData = QCoreGlobalData::instance(); if (!globalData) - return 0; + return nullptr; QTextCodec *codec = globalData->codecForLocale.loadAcquire(); if (!codec) { @@ -830,7 +830,7 @@ QTextEncoder* QTextCodec::makeEncoder(QTextCodec::ConversionFlags flags) const */ QByteArray QTextCodec::fromUnicode(const QString& str) const { - return convertFromUnicode(str.constData(), str.length(), 0); + return convertFromUnicode(str.constData(), str.length(), nullptr); } #endif @@ -863,7 +863,7 @@ QByteArray QTextCodec::fromUnicode(QStringView str) const */ QString QTextCodec::toUnicode(const QByteArray& a) const { - return convertToUnicode(a.constData(), a.length(), 0); + return convertToUnicode(a.constData(), a.length(), nullptr); } /*! @@ -915,7 +915,7 @@ bool QTextCodec::canEncode(QStringView s) const QString QTextCodec::toUnicode(const char *chars) const { int len = qstrlen(chars); - return convertToUnicode(chars, len, 0); + return convertToUnicode(chars, len, nullptr); } @@ -1110,7 +1110,7 @@ QString QTextDecoder::toUnicode(const QByteArray &ba) QTextCodec *QTextCodec::codecForHtml(const QByteArray &ba, QTextCodec *defaultCodec) { // determine charset - QTextCodec *c = QTextCodec::codecForUtfText(ba, 0); + QTextCodec *c = QTextCodec::codecForUtfText(ba, nullptr); if (!c) { static Q_RELAXED_CONSTEXPR auto matcher = qMakeStaticByteArrayMatcher("meta "); QByteArray header = ba.left(1024).toLower(); diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp index be01e5a605..897f92a4ea 100644 --- a/src/corelib/global/qglobal.cpp +++ b/src/corelib/global/qglobal.cpp @@ -3612,7 +3612,7 @@ bool qEnvironmentVariableIsSet(const char *varName) noexcept (void)getenv_s(&requiredSize, 0, 0, varName); return requiredSize != 0; #else - return ::getenv(varName) != 0; + return ::getenv(varName) != nullptr; #endif } diff --git a/src/corelib/global/qlogging.cpp b/src/corelib/global/qlogging.cpp index 292a459e5d..49411306c2 100644 --- a/src/corelib/global/qlogging.cpp +++ b/src/corelib/global/qlogging.cpp @@ -1100,8 +1100,8 @@ Q_DECLARE_TYPEINFO(QMessagePattern::BacktraceParams, Q_MOVABLE_TYPE); QBasicMutex QMessagePattern::mutex; QMessagePattern::QMessagePattern() - : literals(0) - , tokens(0) + : literals(nullptr) + , tokens(nullptr) , fromEnvironment(false) { #ifndef QT_BOOTSTRAPPED @@ -1121,9 +1121,9 @@ QMessagePattern::~QMessagePattern() for (int i = 0; literals[i]; ++i) delete [] literals[i]; delete [] literals; - literals = 0; + literals = nullptr; delete [] tokens; - tokens = 0; + tokens = nullptr; } void QMessagePattern::setPattern(const QString &pattern) @@ -1173,7 +1173,7 @@ void QMessagePattern::setPattern(const QString &pattern) // tokenizer QVarLengthArray literalsVar; tokens = new const char*[lexemes.size() + 1]; - tokens[lexemes.size()] = 0; + tokens[lexemes.size()] = nullptr; bool nestedIfError = false; bool inIf = false; @@ -1280,7 +1280,7 @@ void QMessagePattern::setPattern(const QString &pattern) qt_message_print(error); literals = new const char*[literalsVar.size() + 1]; - literals[literalsVar.size()] = 0; + literals[literalsVar.size()] = nullptr; memcpy(literals, literalsVar.constData(), literalsVar.size() * sizeof(const char*)); } @@ -1406,7 +1406,7 @@ QString qFormatLogMessage(QtMsgType type, const QMessageLogContext &context, con #endif // we do not convert file, function, line literals to local encoding due to overhead - for (int i = 0; pattern->tokens[i] != 0; ++i) { + for (int i = 0; pattern->tokens[i]; ++i) { const char *token = pattern->tokens[i]; if (token == endifTokenC) { skip = false; diff --git a/src/corelib/global/qmalloc.cpp b/src/corelib/global/qmalloc.cpp index 05676a0da2..b071c1df62 100644 --- a/src/corelib/global/qmalloc.cpp +++ b/src/corelib/global/qmalloc.cpp @@ -74,18 +74,18 @@ void *qRealloc(void *ptr, size_t size) void *qMallocAligned(size_t size, size_t alignment) { - return qReallocAligned(0, size, 0, alignment); + return qReallocAligned(nullptr, size, 0, alignment); } void *qReallocAligned(void *oldptr, size_t newsize, size_t oldsize, size_t alignment) { // fake an aligned allocation - void *actualptr = oldptr ? static_cast(oldptr)[-1] : 0; + void *actualptr = oldptr ? static_cast(oldptr)[-1] : nullptr; if (alignment <= sizeof(void*)) { // special, fast case void **newptr = static_cast(realloc(actualptr, newsize + sizeof(void*))); if (!newptr) - return 0; + return nullptr; if (newptr == actualptr) { // realloc succeeded without reallocating return oldptr; @@ -105,7 +105,7 @@ void *qReallocAligned(void *oldptr, size_t newsize, size_t oldsize, size_t align void *real = realloc(actualptr, newsize + alignment); if (!real) - return 0; + return nullptr; quintptr faked = reinterpret_cast(real) + alignment; faked &= ~(alignment - 1); diff --git a/src/corelib/io/qabstractfileengine.cpp b/src/corelib/io/qabstractfileengine.cpp index f2a895bbb8..8a1679c5af 100644 --- a/src/corelib/io/qabstractfileengine.cpp +++ b/src/corelib/io/qabstractfileengine.cpp @@ -159,7 +159,7 @@ QAbstractFileEngineHandler::~QAbstractFileEngineHandler() */ QAbstractFileEngine *qt_custom_file_engine_handler_create(const QString &path) { - QAbstractFileEngine *engine = 0; + QAbstractFileEngine *engine = nullptr; if (qt_file_engine_handlers_in_use) { QReadLocker locker(fileEngineHandlerMutex()); @@ -658,7 +658,7 @@ QStringList QAbstractFileEngine::entryList(QDir::Filters filters, const QStringL QAbstractFileEngine::FileFlags QAbstractFileEngine::fileFlags(FileFlags type) const { Q_UNUSED(type); - return 0; + return nullptr; } /*! @@ -838,7 +838,7 @@ uchar *QAbstractFileEngine::map(qint64 offset, qint64 size, QFile::MemoryMapFlag option.flags = flags; MapExtensionReturn r; if (!extension(MapExtension, &option, &r)) - return 0; + return nullptr; return r.address; } @@ -1118,7 +1118,7 @@ QAbstractFileEngine::Iterator *QAbstractFileEngine::beginEntryList(QDir::Filters { Q_UNUSED(filters); Q_UNUSED(filterNames); - return 0; + return nullptr; } /*! @@ -1126,7 +1126,7 @@ QAbstractFileEngine::Iterator *QAbstractFileEngine::beginEntryList(QDir::Filters */ QAbstractFileEngine::Iterator *QAbstractFileEngine::endEntryList() { - return 0; + return nullptr; } /*! diff --git a/src/corelib/io/qbuffer.cpp b/src/corelib/io/qbuffer.cpp index 7b3fa2ccad..8e980733de 100644 --- a/src/corelib/io/qbuffer.cpp +++ b/src/corelib/io/qbuffer.cpp @@ -50,7 +50,7 @@ class QBufferPrivate : public QIODevicePrivate public: QBufferPrivate() - : buf(0) + : buf(nullptr) #ifndef QT_NO_QOBJECT , writtenSinceLastEmit(0), signalConnectionCount(0), signalsEmitted(false) #endif diff --git a/src/corelib/io/qdir.cpp b/src/corelib/io/qdir.cpp index 671913e92f..62dae3577c 100644 --- a/src/corelib/io/qdir.cpp +++ b/src/corelib/io/qdir.cpp @@ -1431,7 +1431,7 @@ QStringList QDir::entryList(const QStringList &nameFilters, Filters filters, l.append(it.fileInfo()); } QStringList ret; - d->sortFileList(sort, l, &ret, 0); + d->sortFileList(sort, l, &ret, nullptr); return ret; } @@ -1473,7 +1473,7 @@ QFileInfoList QDir::entryInfoList(const QStringList &nameFilters, Filters filter l.append(it.fileInfo()); } QFileInfoList ret; - d->sortFileList(sort, l, 0, &ret); + d->sortFileList(sort, l, nullptr, &ret); return ret; } diff --git a/src/corelib/io/qfile.cpp b/src/corelib/io/qfile.cpp index 8d871904bc..6959c7a40b 100644 --- a/src/corelib/io/qfile.cpp +++ b/src/corelib/io/qfile.cpp @@ -86,7 +86,7 @@ QFilePrivate::openExternalFile(int flags, int fd, QFile::FileHandleFlags handleF return false; #else delete fileEngine; - fileEngine = 0; + fileEngine = nullptr; QFSFileEngine *fe = new QFSFileEngine; fileEngine = fe; return fe->open(QIODevice::OpenMode(flags), fd, handleFlags); @@ -102,7 +102,7 @@ QFilePrivate::openExternalFile(int flags, FILE *fh, QFile::FileHandleFlags handl return false; #else delete fileEngine; - fileEngine = 0; + fileEngine = nullptr; QFSFileEngine *fe = new QFSFileEngine; fileEngine = fe; return fe->open(QIODevice::OpenMode(flags), fh, handleFlags); @@ -336,7 +336,7 @@ QFile::setFileName(const QString &name) } if(d->fileEngine) { //get a new file engine later delete d->fileEngine; - d->fileEngine = 0; + d->fileEngine = nullptr; } d->fileName = name; } diff --git a/src/corelib/io/qfiledevice.cpp b/src/corelib/io/qfiledevice.cpp index 0689118f3e..2f74547054 100644 --- a/src/corelib/io/qfiledevice.cpp +++ b/src/corelib/io/qfiledevice.cpp @@ -53,7 +53,7 @@ QT_BEGIN_NAMESPACE #endif QFileDevicePrivate::QFileDevicePrivate() - : fileEngine(0), + : fileEngine(nullptr), cachedSize(0), error(QFile::NoError), lastWasWrite(false) { @@ -63,7 +63,7 @@ QFileDevicePrivate::QFileDevicePrivate() QFileDevicePrivate::~QFileDevicePrivate() { delete fileEngine; - fileEngine = 0; + fileEngine = nullptr; } QAbstractFileEngine * QFileDevicePrivate::engine() const diff --git a/src/corelib/io/qfileinfo.cpp b/src/corelib/io/qfileinfo.cpp index 998382021d..f5b398feae 100644 --- a/src/corelib/io/qfileinfo.cpp +++ b/src/corelib/io/qfileinfo.cpp @@ -52,7 +52,7 @@ QString QFileInfoPrivate::getFileName(QAbstractFileEngine::FileName name) const return fileNames[(int)name]; QString ret; - if (fileEngine == 0) { // local file; use the QFileSystemEngine directly + if (fileEngine == nullptr) { // local file; use the QFileSystemEngine directly switch (name) { case QAbstractFileEngine::CanonicalName: case QAbstractFileEngine::CanonicalPathName: { @@ -103,7 +103,7 @@ QString QFileInfoPrivate::getFileOwner(QAbstractFileEngine::FileOwner own) const if (cache_enabled && !fileOwners[(int)own].isNull()) return fileOwners[(int)own]; QString ret; - if (fileEngine == 0) { + if (fileEngine == nullptr) { switch (own) { case QAbstractFileEngine::OwnerUser: ret = QFileSystemEngine::resolveUserName(fileEntry, metaData); @@ -134,7 +134,7 @@ uint QFileInfoPrivate::getFileFlags(QAbstractFileEngine::FileFlags request) cons // extra syscall. Bundle detecton on Mac can be slow, expecially on network // paths, so we separate out that as well. - QAbstractFileEngine::FileFlags req = 0; + QAbstractFileEngine::FileFlags req = nullptr; uint cachedFlags = 0; if (request & (QAbstractFileEngine::FlagsMask | QAbstractFileEngine::TypesMask)) { @@ -434,7 +434,7 @@ bool QFileInfo::operator==(const QFileInfo &fileinfo) const return true; Qt::CaseSensitivity sensitive; - if (d->fileEngine == 0 || fileinfo.d_ptr->fileEngine == 0) { + if (d->fileEngine == nullptr || fileinfo.d_ptr->fileEngine == nullptr) { if (d->fileEngine != fileinfo.d_ptr->fileEngine) // one is native, the other is a custom file-engine return false; @@ -649,7 +649,7 @@ bool QFileInfo::isRelative() const Q_D(const QFileInfo); if (d->isDefaultConstructed) return true; - if (d->fileEngine == 0) + if (d->fileEngine == nullptr) return d->fileEntry.isRelative(); return d->fileEngine->isRelativePath(); } @@ -682,7 +682,7 @@ bool QFileInfo::exists() const Q_D(const QFileInfo); if (d->isDefaultConstructed) return false; - if (d->fileEngine == 0) { + if (d->fileEngine == nullptr) { if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::ExistsAttribute)) QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::ExistsAttribute); return d->metaData.exists(); @@ -982,7 +982,7 @@ bool QFileInfo::isNativePath() const Q_D(const QFileInfo); if (d->isDefaultConstructed) return false; - if (d->fileEngine == 0) + if (d->fileEngine == nullptr) return true; return d->getFileFlags(QAbstractFileEngine::LocalDiskFlag); } @@ -1075,7 +1075,7 @@ bool QFileInfo::isRoot() const Q_D(const QFileInfo); if (d->isDefaultConstructed) return false; - if (d->fileEngine == 0) { + if (d->fileEngine == nullptr) { if (d->fileEntry.isRoot()) { #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) //the path is a drive root, but the drive may not exist diff --git a/src/corelib/io/qfilesystemengine.cpp b/src/corelib/io/qfilesystemengine.cpp index 7abdf90bf5..21847c2a7c 100644 --- a/src/corelib/io/qfilesystemengine.cpp +++ b/src/corelib/io/qfilesystemengine.cpp @@ -124,7 +124,7 @@ static inline bool _q_checkEntry(QAbstractFileEngine *&engine, bool resolvingEnt if (resolvingEntry) { if (!(engine->fileFlags(QAbstractFileEngine::FlagsMask) & QAbstractFileEngine::ExistsFlag)) { delete engine; - engine = 0; + engine = nullptr; return false; } } @@ -191,7 +191,7 @@ static bool _q_resolveEntryAndCreateLegacyEngine_recursive(QFileSystemEntry &ent QAbstractFileEngine *QFileSystemEngine::resolveEntryAndCreateLegacyEngine( QFileSystemEntry &entry, QFileSystemMetaData &data) { QFileSystemEntry copy = entry; - QAbstractFileEngine *engine = 0; + QAbstractFileEngine *engine = nullptr; if (_q_resolveEntryAndCreateLegacyEngine_recursive(copy, data, engine)) // Reset entry to resolved copy. diff --git a/src/corelib/io/qfilesystemengine_unix.cpp b/src/corelib/io/qfilesystemengine_unix.cpp index b78e037865..066a7c3f01 100644 --- a/src/corelib/io/qfilesystemengine_unix.cpp +++ b/src/corelib/io/qfilesystemengine_unix.cpp @@ -813,7 +813,7 @@ QString QFileSystemEngine::resolveUserName(uint userId) #endif #if !defined(Q_OS_INTEGRITY) && !defined(Q_OS_WASM) - struct passwd *pw = 0; + struct passwd *pw = nullptr; #if QT_CONFIG(thread) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) && !defined(Q_OS_VXWORKS) struct passwd entry; getpwuid_r(userId, &entry, buf.data(), buf.size(), &pw); @@ -837,7 +837,7 @@ QString QFileSystemEngine::resolveGroupName(uint groupId) #endif #if !defined(Q_OS_INTEGRITY) && !defined(Q_OS_WASM) - struct group *gr = 0; + struct group *gr = nullptr; #if QT_CONFIG(thread) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) && !defined(Q_OS_VXWORKS) && (!defined(Q_OS_ANDROID) || defined(Q_OS_ANDROID) && (__ANDROID_API__ >= 24)) size_max = sysconf(_SC_GETGR_R_SIZE_MAX); if (size_max == -1) diff --git a/src/corelib/io/qfilesystemiterator_unix.cpp b/src/corelib/io/qfilesystemiterator_unix.cpp index a9acf542d4..5ac7e13a2e 100644 --- a/src/corelib/io/qfilesystemiterator_unix.cpp +++ b/src/corelib/io/qfilesystemiterator_unix.cpp @@ -50,15 +50,15 @@ QT_BEGIN_NAMESPACE QFileSystemIterator::QFileSystemIterator(const QFileSystemEntry &entry, QDir::Filters filters, const QStringList &nameFilters, QDirIterator::IteratorFlags flags) : nativePath(entry.nativeFilePath()) - , dir(0) - , dirEntry(0) + , dir(nullptr) + , dirEntry(nullptr) , lastError(0) { Q_UNUSED(filters) Q_UNUSED(nameFilters) Q_UNUSED(flags) - if ((dir = QT_OPENDIR(nativePath.constData())) == 0) { + if ((dir = QT_OPENDIR(nativePath.constData())) == nullptr) { lastError = errno; } else { if (!nativePath.endsWith('/')) diff --git a/src/corelib/io/qfsfileengine.cpp b/src/corelib/io/qfsfileengine.cpp index 387990ed79..0db27f3e25 100644 --- a/src/corelib/io/qfsfileengine.cpp +++ b/src/corelib/io/qfsfileengine.cpp @@ -127,7 +127,7 @@ void QFSFileEnginePrivate::init() is_link = 0; openMode = QIODevice::NotOpen; fd = -1; - fh = 0; + fh = nullptr; lastIOCommand = IOFlushCommand; lastFlushFailed = false; closeFileHandle = false; @@ -240,7 +240,7 @@ bool QFSFileEngine::open(QIODevice::OpenMode openMode) d->openMode = openMode; d->lastFlushFailed = false; d->tried_stat = 0; - d->fh = 0; + d->fh = nullptr; d->fd = -1; return d->nativeOpen(openMode); @@ -299,7 +299,7 @@ bool QFSFileEnginePrivate::openFh(QIODevice::OpenMode openMode, FILE *fh) QSystemError::stdString()); this->openMode = QIODevice::NotOpen; - this->fh = 0; + this->fh = nullptr; return false; } @@ -328,7 +328,7 @@ bool QFSFileEngine::open(QIODevice::OpenMode openMode, int fd, QFile::FileHandle d->lastFlushFailed = false; d->closeFileHandle = (handleFlags & QFile::AutoCloseHandle); d->fileEntry.clear(); - d->fh = 0; + d->fh = nullptr; d->fd = -1; d->tried_stat = 0; @@ -344,7 +344,7 @@ bool QFSFileEnginePrivate::openFd(QIODevice::OpenMode openMode, int fd) { Q_Q(QFSFileEngine); this->fd = fd; - fh = 0; + fh = nullptr; // Seek to the end when in Append mode. if (openMode & QFile::Append) { @@ -405,7 +405,7 @@ bool QFSFileEnginePrivate::closeFdFh() // We must reset these guys regardless; calling close again after a // failed close causes crashes on some systems. - fh = 0; + fh = nullptr; fd = -1; closed = (ret == 0); } @@ -822,7 +822,7 @@ QAbstractFileEngine::Iterator *QFSFileEngine::beginEntryList(QDir::Filters filte */ QAbstractFileEngine::Iterator *QFSFileEngine::endEntryList() { - return 0; + return nullptr; } #endif // QT_NO_FILESYSTEMITERATOR @@ -870,7 +870,7 @@ bool QFSFileEngine::extension(Extension extension, const ExtensionOption *option const MapExtensionOption *options = (const MapExtensionOption*)(option); MapExtensionReturn *returnValue = static_cast(output); returnValue->address = d->map(options->offset, options->size, options->flags); - return (returnValue->address != 0); + return (returnValue->address != nullptr); } if (extension == UnMapExtension) { const UnMapExtensionOption *options = (const UnMapExtensionOption*)option; diff --git a/src/corelib/io/qfsfileengine_unix.cpp b/src/corelib/io/qfsfileengine_unix.cpp index 90ad0126d6..d4983c72af 100644 --- a/src/corelib/io/qfsfileengine_unix.cpp +++ b/src/corelib/io/qfsfileengine_unix.cpp @@ -153,7 +153,7 @@ bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode) } } - fh = 0; + fh = nullptr; } closeFileHandle = true; @@ -451,14 +451,14 @@ QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(FileFlags type) const if (type & Refresh) d->metaData.clear(); - QAbstractFileEngine::FileFlags ret = 0; + QAbstractFileEngine::FileFlags ret = { }; if (type & FlagsMask) ret |= LocalDiskFlag; bool exists; { - QFileSystemMetaData::MetaDataFlags queryFlags = 0; + QFileSystemMetaData::MetaDataFlags queryFlags = { }; queryFlags |= QFileSystemMetaData::MetaDataFlags(uint(type)) & QFileSystemMetaData::Permissions; @@ -590,9 +590,9 @@ bool QFSFileEngine::setPermissions(uint perms) QSystemError error; bool ok; if (d->fd != -1) - ok = QFileSystemEngine::setPermissions(d->fd, QFile::Permissions(perms), error, 0); + ok = QFileSystemEngine::setPermissions(d->fd, QFile::Permissions(perms), error); else - ok = QFileSystemEngine::setPermissions(d->fileEntry, QFile::Permissions(perms), error, 0); + ok = QFileSystemEngine::setPermissions(d->fileEntry, QFile::Permissions(perms), error); if (!ok) { setError(QFile::PermissionsError, error.toString()); return false; @@ -651,13 +651,13 @@ uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFla Q_Q(QFSFileEngine); if (openMode == QIODevice::NotOpen) { q->setError(QFile::PermissionsError, qt_error_string(int(EACCES))); - return 0; + return nullptr; } if (offset < 0 || offset > maxFileOffset || size < 0 || quint64(size) > quint64(size_t(-1))) { q->setError(QFile::UnspecifiedError, qt_error_string(int(EINVAL))); - return 0; + return nullptr; } // If we know the mapping will extend beyond EOF, fail early to avoid @@ -685,14 +685,14 @@ uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFla if (quint64(size + extra) > quint64((size_t)-1)) { q->setError(QFile::UnspecifiedError, qt_error_string(int(EINVAL))); - return 0; + return nullptr; } size_t realSize = (size_t)size + extra; QT_OFF_T realOffset = QT_OFF_T(offset); realOffset &= ~(QT_OFF_T(pageSize - 1)); - void *mapAddress = QT_MMAP((void*)0, realSize, + void *mapAddress = QT_MMAP((void*)nullptr, realSize, access, sharemode, nativeHandle(), realOffset); if (MAP_FAILED != mapAddress) { uchar *address = extra + static_cast(mapAddress); @@ -714,7 +714,7 @@ uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFla q->setError(QFile::UnspecifiedError, qt_error_string(int(errno))); break; } - return 0; + return nullptr; } bool QFSFileEnginePrivate::unmap(uchar *ptr) diff --git a/src/corelib/io/qiodevice.cpp b/src/corelib/io/qiodevice.cpp index 74df0f71ef..e26508e631 100644 --- a/src/corelib/io/qiodevice.cpp +++ b/src/corelib/io/qiodevice.cpp @@ -169,7 +169,7 @@ QIODevicePrivate::QIODevicePrivate() , baseReadLineDataCalled(false) , accessMode(Unset) #ifdef QT_NO_QOBJECT - , q_ptr(0) + , q_ptr(nullptr) #endif { } diff --git a/src/corelib/io/qipaddress.cpp b/src/corelib/io/qipaddress.cpp index d9f7916dd4..131795330b 100644 --- a/src/corelib/io/qipaddress.cpp +++ b/src/corelib/io/qipaddress.cpp @@ -67,7 +67,7 @@ static const QChar *checkedToAscii(Buffer &buffer, const QChar *begin, const QCh *dst++ = *src++; } *dst = '\0'; - return 0; + return nullptr; } static bool parseIp4Internal(IPv4Address &address, const char *ptr, bool acceptLeadingZero); @@ -175,7 +175,7 @@ const QChar *parseIp6(IPv6Address &address, const QChar *begin, const QChar *end memset(address, 0, sizeof address); if (colonCount == 2 && end - begin == 2) // "::" - return 0; + return nullptr; // if there's a double colon ("::"), this is how many zeroes it means int zeroWordsToFill; @@ -236,7 +236,7 @@ const QChar *parseIp6(IPv6Address &address, const QChar *begin, const QChar *end address[13] = ip4 >> 16; address[14] = ip4 >> 8; address[15] = ip4; - return 0; + return nullptr; } address[pos++] = x >> 8; @@ -248,7 +248,7 @@ const QChar *parseIp6(IPv6Address &address, const QChar *begin, const QChar *end return begin + (endptr - buffer.data()); ptr = endptr + 1; } - return pos == 16 ? 0 : end; + return pos == 16 ? nullptr : end; } static inline QChar toHex(uchar c) diff --git a/src/corelib/io/qloggingcategory.cpp b/src/corelib/io/qloggingcategory.cpp index 33253429a2..91b3396217 100644 --- a/src/corelib/io/qloggingcategory.cpp +++ b/src/corelib/io/qloggingcategory.cpp @@ -214,8 +214,8 @@ static void setBoolLane(QBasicAtomicInt *atomic, bool enable, int shift) Note that \a category must be kept valid during the lifetime of this object. */ QLoggingCategory::QLoggingCategory(const char *category) - : d(0), - name(0) + : d(nullptr), + name(nullptr) { init(category, QtDebugMsg); } @@ -231,8 +231,8 @@ QLoggingCategory::QLoggingCategory(const char *category) \since 5.4 */ QLoggingCategory::QLoggingCategory(const char *category, QtMsgType enableForLevel) - : d(0), - name(0) + : d(nullptr), + name(nullptr) { init(category, enableForLevel); } diff --git a/src/corelib/io/qloggingregistry.cpp b/src/corelib/io/qloggingregistry.cpp index 9792d956cc..7849dfd14c 100644 --- a/src/corelib/io/qloggingregistry.cpp +++ b/src/corelib/io/qloggingregistry.cpp @@ -165,7 +165,7 @@ void QLoggingRule::parse(const QStringRef &pattern) p = QStringRef(p.string(), p.position() + 1, p.length() - 1); } if (p.contains(QLatin1Char('*'))) // '*' only supported at start/end - flags = 0; + flags = PatternFlags(); } category = p.toString(); @@ -415,7 +415,7 @@ QLoggingRegistry::installFilter(QLoggingCategory::CategoryFilter filter) { QMutexLocker locker(®istryMutex); - if (filter == 0) + if (!filter) filter = defaultCategoryFilter; QLoggingCategory::CategoryFilter old = categoryFilter; diff --git a/src/corelib/io/qresource.cpp b/src/corelib/io/qresource.cpp index e7d739b4dc..d143f7fda3 100644 --- a/src/corelib/io/qresource.cpp +++ b/src/corelib/io/qresource.cpp @@ -147,7 +147,7 @@ private: public: mutable QAtomicInt ref; - inline QResourceRoot(): tree(0), names(0), payloads(0), version(0) {} + inline QResourceRoot(): tree(nullptr), names(nullptr), payloads(nullptr), version(0) {} inline QResourceRoot(int version, const uchar *t, const uchar *n, const uchar *d) { setSource(version, t, n, d); } virtual ~QResourceRoot() { } int findNode(const QString &path, const QLocale &locale=QLocale()) const; @@ -165,7 +165,7 @@ public: quint64 lastModified(int node) const; QStringList children(int node) const; virtual QString mappingRoot() const { return QString(); } - bool mappingRootSubdir(const QString &path, QString *match=0) const; + bool mappingRootSubdir(const QString &path, QString *match = nullptr) const; inline bool operator==(const QResourceRoot &other) const { return tree == other.tree && names == other.names && payloads == other.payloads && version == other.version; } inline bool operator!=(const QResourceRoot &other) const @@ -320,7 +320,7 @@ QResourcePrivate::clear() { absoluteFilePath.clear(); compressionAlgo = QResource::NoCompression; - data = 0; + data = nullptr; size = 0; children.clear(); lastModified = 0; @@ -864,7 +864,7 @@ const uchar *QResourceRoot::data(int node, qint64 *size) const { if(node == -1) { *size = 0; - return 0; + return nullptr; } int offset = findOffset(node) + 4; //jump past name @@ -881,7 +881,7 @@ const uchar *QResourceRoot::data(int node, qint64 *size) const return ret; } *size = 0; - return 0; + return nullptr; } quint64 QResourceRoot::lastModified(int node) const @@ -991,7 +991,7 @@ class QDynamicBufferResourceRoot: public QResourceRoot const uchar *buffer; public: - inline QDynamicBufferResourceRoot(const QString &_root) : root(_root), buffer(0) { } + inline QDynamicBufferResourceRoot(const QString &_root) : root(_root), buffer(nullptr) { } inline ~QDynamicBufferResourceRoot() { } inline const uchar *mappingBuffer() const { return buffer; } QString mappingRoot() const override { return root; } @@ -1063,12 +1063,14 @@ class QDynamicFileResourceRoot: public QDynamicBufferResourceRoot qsizetype unmapLength; public: - inline QDynamicFileResourceRoot(const QString &_root) : QDynamicBufferResourceRoot(_root), unmapPointer(0), unmapLength(0) { } + QDynamicFileResourceRoot(const QString &_root) + : QDynamicBufferResourceRoot(_root), unmapPointer(nullptr), unmapLength(0) + { } ~QDynamicFileResourceRoot() { #if defined(QT_USE_MMAP) if (unmapPointer) { munmap((char*)unmapPointer, unmapLength); - unmapPointer = 0; + unmapPointer = nullptr; unmapLength = 0; } else #endif diff --git a/src/corelib/io/qsavefile.cpp b/src/corelib/io/qsavefile.cpp index bde5133cb3..915d0a0a00 100644 --- a/src/corelib/io/qsavefile.cpp +++ b/src/corelib/io/qsavefile.cpp @@ -151,7 +151,7 @@ QSaveFile::~QSaveFile() if (d->fileEngine) { d->fileEngine->remove(); delete d->fileEngine; - d->fileEngine = 0; + d->fileEngine = nullptr; } } @@ -252,7 +252,7 @@ bool QSaveFile::open(OpenMode mode) return true; d->setError(d->fileEngine->error(), d->fileEngine->errorString()); delete d->fileEngine; - d->fileEngine = 0; + d->fileEngine = nullptr; } else { QString msg = QSaveFile::tr("QSaveFile cannot open '%1' without direct write fallback " @@ -285,7 +285,7 @@ bool QSaveFile::open(OpenMode mode) err = QFileDevice::OpenError; d->setError(err, d->fileEngine->errorString()); delete d->fileEngine; - d->fileEngine = 0; + d->fileEngine = nullptr; return false; } @@ -339,7 +339,7 @@ bool QSaveFile::commit() d->fileEngine->remove(); d->writeError = QFileDevice::NoError; delete d->fileEngine; - d->fileEngine = 0; + d->fileEngine = nullptr; return false; } // atomically replace old file with new file @@ -349,12 +349,12 @@ bool QSaveFile::commit() d->setError(d->fileEngine->error(), d->fileEngine->errorString()); d->fileEngine->remove(); delete d->fileEngine; - d->fileEngine = 0; + d->fileEngine = nullptr; return false; } } delete d->fileEngine; - d->fileEngine = 0; + d->fileEngine = nullptr; return true; } diff --git a/src/corelib/io/qtemporaryfile.cpp b/src/corelib/io/qtemporaryfile.cpp index ced08a9a87..c89ad06a1f 100644 --- a/src/corelib/io/qtemporaryfile.cpp +++ b/src/corelib/io/qtemporaryfile.cpp @@ -327,7 +327,7 @@ bool QTemporaryFileEngine::isReallyOpen() const { Q_D(const QFSFileEngine); - if (!((0 == d->fh) && (-1 == d->fd) + if (!((nullptr == d->fh) && (-1 == d->fd) #if defined Q_OS_WIN && (INVALID_HANDLE_VALUE == d->fileHandle) #endif @@ -902,7 +902,7 @@ QTemporaryFile *QTemporaryFile::createNativeFile(QFile &file) { if (QAbstractFileEngine *engine = file.d_func()->engine()) { if(engine->fileFlags(QAbstractFileEngine::FlagsMask) & QAbstractFileEngine::LocalDiskFlag) - return 0; //native already + return nullptr; // native already //cache bool wasOpen = file.isOpen(); qint64 old_off = 0; @@ -934,7 +934,7 @@ QTemporaryFile *QTemporaryFile::createNativeFile(QFile &file) //done return ret; } - return 0; + return nullptr; } /*! diff --git a/src/corelib/io/qurl.cpp b/src/corelib/io/qurl.cpp index 681a0c7ef2..4d0306abde 100644 --- a/src/corelib/io/qurl.cpp +++ b/src/corelib/io/qurl.cpp @@ -523,7 +523,7 @@ public: Error *cloneError() const; void clearError(); void setError(ErrorCode errorCode, const QString &source, int supplement = -1); - ErrorCode validityError(QString *source = 0, int *position = 0) const; + ErrorCode validityError(QString *source = nullptr, int *position = nullptr) const; bool validateComponent(Section section, const QString &input, int begin, int end); bool validateComponent(Section section, const QString &input) { return validateComponent(section, input, 0, uint(input.length())); } @@ -591,7 +591,7 @@ public: inline QUrlPrivate::QUrlPrivate() : ref(1), port(-1), - error(0), + error(nullptr), sectionIsPresent(0), flags(0) { @@ -619,13 +619,13 @@ inline QUrlPrivate::~QUrlPrivate() inline QUrlPrivate::Error *QUrlPrivate::cloneError() const { - return error ? new Error(*error) : 0; + return error ? new Error(*error) : nullptr; } inline void QUrlPrivate::clearError() { delete error; - error = 0; + error = nullptr; } inline void QUrlPrivate::setError(ErrorCode errorCode, const QString &source, int supplement) @@ -826,7 +826,7 @@ recodeFromUser(const QString &input, const ushort *actions, int from, int to) QString output; const QChar *begin = input.constData() + from; const QChar *end = input.constData() + to; - if (qt_urlRecode(output, begin, end, 0, actions)) + if (qt_urlRecode(output, begin, end, nullptr, actions)) return output; return input.mid(from, to - from); @@ -954,7 +954,7 @@ inline void QUrlPrivate::appendFragment(QString &appendTo, QUrl::FormattingOptio { appendToUser(appendTo, fragment, options, options & QUrl::EncodeDelimiters ? fragmentInUrl : - appendingTo == FullUrl ? 0 : fragmentInIsolation); + appendingTo == FullUrl ? nullptr : fragmentInIsolation); } inline void QUrlPrivate::appendQuery(QString &appendTo, QUrl::FormattingOptions options, Section appendingTo) const @@ -1179,7 +1179,7 @@ inline void QUrlPrivate::appendHost(QString &appendTo, QUrl::FormattingOptions o if (host.at(0).unicode() == '[') { // IPv6 addresses might contain a zone-id which needs to be recoded if (options != 0) - if (qt_urlRecode(appendTo, host.constBegin(), host.constEnd(), options, 0)) + if (qt_urlRecode(appendTo, host.constBegin(), host.constEnd(), options, nullptr)) return; appendTo += host; } else { @@ -1221,7 +1221,7 @@ static const QChar *parseIpFuture(QString &host, const QChar *begin, const QChar --end; QString decoded; - if (mode == QUrl::TolerantMode && qt_urlRecode(decoded, begin, end, QUrl::FullyDecoded, 0)) { + if (mode == QUrl::TolerantMode && qt_urlRecode(decoded, begin, end, QUrl::FullyDecoded, nullptr)) { begin = decoded.constBegin(); end = decoded.constEnd(); } @@ -1233,13 +1233,13 @@ static const QChar *parseIpFuture(QString &host, const QChar *begin, const QChar host += *begin; else if (begin->unicode() >= '0' && begin->unicode() <= '9') host += *begin; - else if (begin->unicode() < 0x80 && strchr(acceptable, begin->unicode()) != 0) + else if (begin->unicode() < 0x80 && strchr(acceptable, begin->unicode()) != nullptr) host += *begin; else return decoded.isEmpty() ? begin : &origBegin[2]; } host += QLatin1Char(']'); - return 0; + return nullptr; } return &origBegin[2]; } @@ -1286,7 +1286,7 @@ static const QChar *parseIp6(QString &host, const QChar *begin, const QChar *end host += zoneId; } host += QLatin1Char(']'); - return 0; + return nullptr; } inline bool QUrlPrivate::setHost(const QString &value, int from, int iend, QUrl::ParsingMode mode) @@ -1352,7 +1352,7 @@ inline bool QUrlPrivate::setHost(const QString &value, int from, int iend, QUrl: // check for percent-encoding first QString s; - if (mode == QUrl::TolerantMode && qt_urlRecode(s, begin, end, 0, 0)) { + if (mode == QUrl::TolerantMode && qt_urlRecode(s, begin, end, { }, nullptr)) { // something was decoded // anything encoded left? int pos = s.indexOf(QChar(0x25)); // '%' @@ -1837,7 +1837,7 @@ inline void QUrlPrivate::validate() const \sa setUrl(), fromEncoded(), TolerantMode */ -QUrl::QUrl(const QString &url, ParsingMode parsingMode) : d(0) +QUrl::QUrl(const QString &url, ParsingMode parsingMode) : d(nullptr) { setUrl(url, parsingMode); } @@ -1845,7 +1845,7 @@ QUrl::QUrl(const QString &url, ParsingMode parsingMode) : d(0) /*! Constructs an empty QUrl object. */ -QUrl::QUrl() : d(0) +QUrl::QUrl() : d(nullptr) { } @@ -1879,7 +1879,7 @@ QUrl::~QUrl() bool QUrl::isValid() const { if (isEmpty()) { - // also catches d == 0 + // also catches d == nullptr return false; } return d->validityError() == QUrlPrivate::NoError; @@ -1907,7 +1907,7 @@ void QUrl::clear() { if (d && !d->ref.deref()) delete d; - d = 0; + d = nullptr; } /*! @@ -4187,7 +4187,7 @@ static QUrl adjustFtpPath(QUrl url) static bool isIp6(const QString &text) { QIPAddressUtils::IPv6Address address; - return !text.isEmpty() && QIPAddressUtils::parseIp6(address, text.begin(), text.end()) == 0; + return !text.isEmpty() && QIPAddressUtils::parseIp6(address, text.begin(), text.end()) == nullptr; } /*! diff --git a/src/corelib/io/qurlidna.cpp b/src/corelib/io/qurlidna.cpp index 2305e66407..2f89d22660 100644 --- a/src/corelib/io/qurlidna.cpp +++ b/src/corelib/io/qurlidna.cpp @@ -1444,7 +1444,7 @@ static void mapToLowerCase(QString *str, int from) { int N = sizeof(NameprepCaseFolding) / sizeof(NameprepCaseFolding[0]); - ushort *d = 0; + ushort *d = nullptr; for (int i = from; i < str->size(); ++i) { uint uc = str->at(i).unicode(); if (uc < 0x80) { @@ -1474,7 +1474,7 @@ static void mapToLowerCase(QString *str, int from) else str->replace(--i, 2, reinterpret_cast(&entry->mapping[0]), l); i += l - 1; - d = 0; + d = nullptr; } else { if (!d) d = reinterpret_cast(str->data()); @@ -2404,7 +2404,7 @@ static const char * const idn_whitelist[] = { }; static const size_t idn_whitelist_size = sizeof idn_whitelist / sizeof *idn_whitelist; -static QStringList *user_idn_whitelist = 0; +static QStringList *user_idn_whitelist = nullptr; static bool lessThan(const QChar *a, int l, const char *c) { diff --git a/src/corelib/io/qurlquery.cpp b/src/corelib/io/qurlquery.cpp index d76107abfd..a8f40a3a12 100644 --- a/src/corelib/io/qurlquery.cpp +++ b/src/corelib/io/qurlquery.cpp @@ -262,7 +262,7 @@ inline QString QUrlQueryPrivate::recodeToUser(const QString &input, QUrl::Compon if (!(encoding & QUrl::EncodeDelimiters)) { QString output; if (qt_urlRecode(output, input.constData(), input.constData() + input.length(), - encoding, 0)) + encoding, nullptr)) return output; return input; } @@ -290,7 +290,7 @@ void QUrlQueryPrivate::setQuery(const QString &query) const QChar *const end = pos + query.size(); while (pos != end) { const QChar *begin = pos; - const QChar *delimiter = 0; + const QChar *delimiter = nullptr; while (pos != end) { // scan for the component parts of this pair if (!delimiter && pos->unicode() == valueDelimiter) @@ -345,7 +345,7 @@ QSharedDataPointer::clone() \sa setQuery(), addQueryItem() */ QUrlQuery::QUrlQuery() - : d(0) + : d(nullptr) { } @@ -356,7 +356,7 @@ QUrlQuery::QUrlQuery() set the query with setQuery(). */ QUrlQuery::QUrlQuery(const QString &queryString) - : d(queryString.isEmpty() ? 0 : new QUrlQueryPrivate(queryString)) + : d(queryString.isEmpty() ? nullptr : new QUrlQueryPrivate(queryString)) { } @@ -369,7 +369,7 @@ QUrlQuery::QUrlQuery(const QString &queryString) \sa QUrl::query() */ QUrlQuery::QUrlQuery(const QUrl &url) - : d(0) + : d(nullptr) { // use internals to avoid unnecessary recoding // ### FIXME: actually do it diff --git a/src/corelib/io/qurlrecode.cpp b/src/corelib/io/qurlrecode.cpp index f23480c755..35ede8d078 100644 --- a/src/corelib/io/qurlrecode.cpp +++ b/src/corelib/io/qurlrecode.cpp @@ -377,7 +377,7 @@ static int recode(QString &result, const ushort *begin, const ushort *end, QUrl: { const int origSize = result.size(); const ushort *input = begin; - ushort *output = 0; + ushort *output = nullptr; EncodingAction action = EncodeCharacter; for ( ; input != end; ++input) { diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index 72aaac44a6..a48a7c80f2 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -216,11 +216,11 @@ QString QCoreApplicationPrivate::appVersion() const } #endif -QString *QCoreApplicationPrivate::cachedApplicationFilePath = 0; +QString *QCoreApplicationPrivate::cachedApplicationFilePath = nullptr; bool QCoreApplicationPrivate::checkInstance(const char *function) { - bool b = (QCoreApplication::self != 0); + bool b = (QCoreApplication::self != nullptr); if (!b) qWarning("QApplication::%s: Please instantiate the QApplication object first", function); return b; @@ -257,7 +257,7 @@ void QCoreApplicationPrivate::processCommandLineArguments() } if (j < argc) { - argv[j] = 0; + argv[j] = nullptr; argc = j; } } @@ -367,11 +367,11 @@ Q_CORE_EXPORT uint qGlobalPostedEventsCount() return currentThreadData->postEventList.size() - currentThreadData->postEventList.startOffset; } -QAbstractEventDispatcher *QCoreApplicationPrivate::eventDispatcher = 0; +QAbstractEventDispatcher *QCoreApplicationPrivate::eventDispatcher = nullptr; #endif // QT_NO_QOBJECT -QCoreApplication *QCoreApplication::self = 0; +QCoreApplication *QCoreApplication::self = nullptr; uint QCoreApplicationPrivate::attribs = (1 << Qt::AA_SynthesizeMouseForUnhandledTouchEvents) | (1 << Qt::AA_SynthesizeMouseForUnhandledTabletEvents); @@ -455,12 +455,12 @@ QCoreApplicationPrivate::QCoreApplicationPrivate(int &aargc, char **aargv, uint , aboutToQuitEmitted(false) , threadData_clean(false) #else - , q_ptr(0) + , q_ptr(nullptr) #endif { app_compile_version = flags & 0xffffff; static const char *const empty = ""; - if (argc == 0 || argv == 0) { + if (argc == 0 || argv == nullptr) { argc = 0; argv = const_cast(&empty); } @@ -886,7 +886,7 @@ QCoreApplication::~QCoreApplication() { qt_call_post_routines(); - self = 0; + self = nullptr; #ifndef QT_NO_QOBJECT QCoreApplicationPrivate::is_app_closing = true; QCoreApplicationPrivate::is_app_running = false; @@ -894,7 +894,7 @@ QCoreApplication::~QCoreApplication() #if QT_CONFIG(thread) // Synchronize and stop the global thread pool threads. - QThreadPool *globalThreadPool = 0; + QThreadPool *globalThreadPool = nullptr; QT_TRY { globalThreadPool = QThreadPool::globalInstance(); } QT_CATCH (...) { @@ -905,10 +905,10 @@ QCoreApplication::~QCoreApplication() #endif #ifndef QT_NO_QOBJECT - d_func()->threadData->eventDispatcher = 0; + d_func()->threadData->eventDispatcher = nullptr; if (QCoreApplicationPrivate::eventDispatcher) QCoreApplicationPrivate::eventDispatcher->closingDown(); - QCoreApplicationPrivate::eventDispatcher = 0; + QCoreApplicationPrivate::eventDispatcher = nullptr; #endif #if QT_CONFIG(library) diff --git a/src/corelib/kernel/qcoreglobaldata.cpp b/src/corelib/kernel/qcoreglobaldata.cpp index 104be1b87b..7ff222f170 100644 --- a/src/corelib/kernel/qcoreglobaldata.cpp +++ b/src/corelib/kernel/qcoreglobaldata.cpp @@ -48,7 +48,7 @@ Q_GLOBAL_STATIC(QCoreGlobalData, globalInstance) QCoreGlobalData::QCoreGlobalData() #if QT_CONFIG(textcodec) - : codecForLocale(0) + : codecForLocale(nullptr) #endif { } @@ -56,7 +56,7 @@ QCoreGlobalData::QCoreGlobalData() QCoreGlobalData::~QCoreGlobalData() { #if QT_CONFIG(textcodec) - codecForLocale = 0; + codecForLocale = nullptr; QList tmp = allCodecs; allCodecs.clear(); codecCache.clear(); diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp index 855d36794d..311fb01fd0 100644 --- a/src/corelib/kernel/qmetatype.cpp +++ b/src/corelib/kernel/qmetatype.cpp @@ -517,12 +517,12 @@ static const struct { const char * typeName; int typeNameLength; int type; } typ QT_FOR_EACH_STATIC_TYPE(QT_ADD_STATIC_METATYPE) QT_FOR_EACH_STATIC_ALIAS_TYPE(QT_ADD_STATIC_METATYPE_ALIASES_ITER) QT_FOR_EACH_STATIC_HACKS_TYPE(QT_ADD_STATIC_METATYPE_HACKS_ITER) - {0, 0, QMetaType::UnknownType} + {nullptr, 0, QMetaType::UnknownType} }; -Q_CORE_EXPORT const QMetaTypeInterface *qMetaTypeGuiHelper = 0; -Q_CORE_EXPORT const QMetaTypeInterface *qMetaTypeWidgetsHelper = 0; -Q_CORE_EXPORT const QMetaObject *qMetaObjectWidgetsHelper = 0; +Q_CORE_EXPORT const QMetaTypeInterface *qMetaTypeGuiHelper = nullptr; +Q_CORE_EXPORT const QMetaTypeInterface *qMetaTypeWidgetsHelper = nullptr; +Q_CORE_EXPORT const QMetaObject *qMetaObjectWidgetsHelper = nullptr; class QCustomTypeInfo : public QMetaTypeInterface { @@ -557,7 +557,7 @@ public: { const QWriteLocker locker(&lock); const T* &fun = map[k]; - if (fun != 0) + if (fun) return false; fun = f; return true; @@ -566,7 +566,7 @@ public: const T *function(Key k) const { const QReadLocker locker(&lock); - return map.value(k, 0); + return map.value(k, nullptr); } void remove(int from, int to) @@ -973,7 +973,7 @@ static inline int qMetaTypeStaticType(const char *typeName, int length) The extra \a firstInvalidIndex parameter is an easy way to avoid iterating over customTypes() a second time in registerNormalizedType(). */ -static int qMetaTypeCustomType_unlocked(const char *typeName, int length, int *firstInvalidIndex = 0) +static int qMetaTypeCustomType_unlocked(const char *typeName, int length, int *firstInvalidIndex = nullptr) { const QVector * const ct = customTypes(); if (!ct) @@ -1006,7 +1006,7 @@ int QMetaType::registerType(const char *typeName, Deleter deleter, { return registerType(typeName, deleter, creator, QtMetaTypePrivate::QMetaTypeFunctionHelper::Destruct, - QtMetaTypePrivate::QMetaTypeFunctionHelper::Construct, 0, TypeFlags(), 0); + QtMetaTypePrivate::QMetaTypeFunctionHelper::Construct, 0, TypeFlags(), nullptr); } /*! @@ -1613,7 +1613,7 @@ void *QMetaType::create(int type, const void *copy) QMetaType info(type); if (int size = info.sizeOf()) return info.construct(operator new(size), copy); - return 0; + return nullptr; } /*! @@ -1639,14 +1639,18 @@ class TypeConstructor { static void *Construct(const int type, void *where, const void *copy) { if (QModulesPrivate::QTypeModuleInfo::IsGui) - return Q_LIKELY(qMetaTypeGuiHelper) ? qMetaTypeGuiHelper[type - QMetaType::FirstGuiType].constructor(where, copy) : 0; + return Q_LIKELY(qMetaTypeGuiHelper) + ? qMetaTypeGuiHelper[type - QMetaType::FirstGuiType].constructor(where, copy) + : nullptr; if (QModulesPrivate::QTypeModuleInfo::IsWidget) - return Q_LIKELY(qMetaTypeWidgetsHelper) ? qMetaTypeWidgetsHelper[type - QMetaType::FirstWidgetsType].constructor(where, copy) : 0; + return Q_LIKELY(qMetaTypeWidgetsHelper) + ? qMetaTypeWidgetsHelper[type - QMetaType::FirstWidgetsType].constructor(where, copy) + : nullptr; // This point can be reached only for known types that definition is not available, for example // in bootstrap mode. We have no other choice then ignore it. - return 0; + return nullptr; } }; public: @@ -1670,7 +1674,7 @@ private: { QReadLocker locker(customTypesLock()); if (Q_UNLIKELY(type < QMetaType::User || !ct || ct->count() <= type - QMetaType::User)) - return 0; + return nullptr; const auto &typeInfo = ct->at(type - QMetaType::User); ctor = typeInfo.constructor; tctor = typeInfo.typedConstructor; @@ -1715,7 +1719,7 @@ private: void *QMetaType::construct(int type, void *where, const void *copy) { if (!where) - return 0; + return nullptr; TypeConstructor constructor(type, where); return QMetaTypeSwitcher::switcher(constructor, type, copy); } @@ -1861,7 +1865,7 @@ private: int QMetaType::sizeOf(int type) { SizeOf sizeOf(type); - return QMetaTypeSwitcher::switcher(sizeOf, type, 0); + return QMetaTypeSwitcher::switcher(sizeOf, type); } namespace { @@ -1925,7 +1929,7 @@ private: QMetaType::TypeFlags QMetaType::typeFlags(int type) { Flags flags(type); - return static_cast(QMetaTypeSwitcher::switcher(flags, type, 0)); + return static_cast(QMetaTypeSwitcher::switcher(flags, type)); } #ifndef QT_BOOTSTRAPPED @@ -1948,17 +1952,21 @@ public: { static const QMetaObject *MetaObject(int type) { if (QModulesPrivate::QTypeModuleInfo::IsGui) - return Q_LIKELY(qMetaTypeGuiHelper) ? qMetaTypeGuiHelper[type - QMetaType::FirstGuiType].metaObject : 0; + return Q_LIKELY(qMetaTypeGuiHelper) + ? qMetaTypeGuiHelper[type - QMetaType::FirstGuiType].metaObject + : nullptr; if (QModulesPrivate::QTypeModuleInfo::IsWidget) - return Q_LIKELY(qMetaTypeWidgetsHelper) ? qMetaTypeWidgetsHelper[type - QMetaType::FirstWidgetsType].metaObject : 0; + return Q_LIKELY(qMetaTypeWidgetsHelper) + ? qMetaTypeWidgetsHelper[type - QMetaType::FirstWidgetsType].metaObject + : nullptr; return 0; } }; template const QMetaObject *delegate(const T *) { return MetaObjectImpl::MetaObject(m_type); } - const QMetaObject *delegate(const void*) { return 0; } - const QMetaObject *delegate(const QMetaTypeSwitcher::UnknownType*) { return 0; } + const QMetaObject *delegate(const void*) { return nullptr; } + const QMetaObject *delegate(const QMetaTypeSwitcher::UnknownType*) { return nullptr; } const QMetaObject *delegate(const QMetaTypeSwitcher::NotBuiltinType*) { return customMetaObject(m_type); } private: const int m_type; @@ -1966,10 +1974,10 @@ private: { const QVector * const ct = customTypes(); if (Q_UNLIKELY(!ct || type < QMetaType::User)) - return 0; + return nullptr; QReadLocker locker(customTypesLock()); if (Q_UNLIKELY(ct->count() <= type - QMetaType::User)) - return 0; + return nullptr; return ct->at(type - QMetaType::User).metaObject; } }; @@ -1987,10 +1995,10 @@ const QMetaObject *QMetaType::metaObjectForType(int type) { #ifndef QT_BOOTSTRAPPED MetaObject mo(type); - return QMetaTypeSwitcher::switcher(mo, type, 0); + return QMetaTypeSwitcher::switcher(mo, type); #else Q_UNUSED(type); - return 0; + return nullptr; #endif } @@ -2187,7 +2195,7 @@ private: QMetaType QMetaType::typeInfo(const int type) { TypeInfo typeInfo(type); - QMetaTypeSwitcher::switcher(typeInfo, type, 0); + QMetaTypeSwitcher::switcher(typeInfo, type); return (typeInfo.info.constructor || typeInfo.info.typedConstructor) ? QMetaType(static_cast(QMetaType::CreateEx | QMetaType::DestroyEx | (typeInfo.info.typedConstructor ? QMetaType::ConstructEx | QMetaType::DestructEx : 0)) @@ -2307,7 +2315,7 @@ void QMetaType::dtor() void *QMetaType::createExtended(const void *copy) const { if (m_typeId == QMetaType::UnknownType) - return 0; + return nullptr; if (Q_UNLIKELY(m_typedConstructor && !m_constructor)) return m_typedConstructor(m_typeId, operator new(m_size), copy); return m_constructor(operator new(m_size), copy); @@ -2387,7 +2395,7 @@ uint QMetaType::sizeExtended() const */ QMetaType::TypeFlags QMetaType::flagsExtended() const { - return 0; + return { }; } /*! @@ -2400,7 +2408,7 @@ QMetaType::TypeFlags QMetaType::flagsExtended() const */ const QMetaObject *QMetaType::metaObjectExtended() const { - return 0; + return nullptr; } @@ -2409,7 +2417,7 @@ namespace QtPrivate const QMetaObject *metaObjectForQWidget() { if (!qMetaTypeWidgetsHelper) - return 0; + return nullptr; return qMetaObjectWidgetsHelper; } } diff --git a/src/corelib/kernel/qmetatype_p.h b/src/corelib/kernel/qmetatype_p.h index 0846193e66..97d6001937 100644 --- a/src/corelib/kernel/qmetatype_p.h +++ b/src/corelib/kernel/qmetatype_p.h @@ -140,12 +140,12 @@ public: /*saveOp*/(QtMetaTypePrivate::QMetaTypeFunctionHelper::IsAvailable>::Save), \ /*loadOp*/(QtMetaTypePrivate::QMetaTypeFunctionHelper::IsAvailable>::Load), # define QT_METATYPE_INTERFACE_INIT_EMPTY_DATASTREAM_IMPL(Type) \ - /*saveOp*/ 0, \ - /*loadOp*/ 0, + /*saveOp*/ nullptr, \ + /*loadOp*/ nullptr, #else # define QT_METATYPE_INTERFACE_INIT_EMPTY_DATASTREAM_IMPL(Type) \ - /*saveOp*/ 0, \ - /*loadOp*/ 0, + /*saveOp*/ nullptr, \ + /*loadOp*/ nullptr, # define QT_METATYPE_INTERFACE_INIT_DATASTREAM_IMPL(Type) \ QT_METATYPE_INTERFACE_INIT_EMPTY_DATASTREAM_IMPL(Type) #endif @@ -153,7 +153,7 @@ public: #ifndef QT_BOOTSTRAPPED #define METAOBJECT_DELEGATE(Type) (QtPrivate::MetaObjectForType::value()) #else -#define METAOBJECT_DELEGATE(Type) 0 +#define METAOBJECT_DELEGATE(Type) nullptr #endif #define QT_METATYPE_INTERFACE_INIT_IMPL(Type, DATASTREAM_DELEGATE) \ @@ -184,11 +184,11 @@ public: #define QT_METATYPE_INTERFACE_INIT_EMPTY() \ { \ QT_METATYPE_INTERFACE_INIT_EMPTY_DATASTREAM_IMPL(void) \ - /*constructor*/ 0, \ - /*destructor*/ 0, \ + /*constructor*/ nullptr, \ + /*destructor*/ nullptr, \ /*size*/ 0, \ /*flags*/ 0, \ - /*metaObject*/ 0 , \ + /*metaObject*/ nullptr , \ /*typedConstructor*/ nullptr, \ /*typedDestructor*/ nullptr \ } diff --git a/src/corelib/kernel/qmetatypeswitcher_p.h b/src/corelib/kernel/qmetatypeswitcher_p.h index 154fb8ab7f..dabc70f4b0 100644 --- a/src/corelib/kernel/qmetatypeswitcher_p.h +++ b/src/corelib/kernel/qmetatypeswitcher_p.h @@ -60,7 +60,7 @@ public: class NotBuiltinType; // type is not a built-in type, but it may be a custom type or an unknown type class UnknownType; // type not known to QMetaType system template - static ReturnType switcher(DelegateObject &logic, int type, const void *data); + static ReturnType switcher(DelegateObject &logic, int type, const void *data = nullptr); }; diff --git a/src/corelib/kernel/qsystemerror.cpp b/src/corelib/kernel/qsystemerror.cpp index 9d0394e4a5..a735e12925 100644 --- a/src/corelib/kernel/qsystemerror.cpp +++ b/src/corelib/kernel/qsystemerror.cpp @@ -112,7 +112,7 @@ static QString windowsErrorString(int errorCode) static QString standardLibraryErrorString(int errorCode) { - const char *s = 0; + const char *s = nullptr; QString ret; switch (errorCode) { case 0: diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index 1ac47f3972..b9563b8395 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -119,19 +119,19 @@ namespace { // annonymous used to hide QVariant handlers static void construct(QVariant::Private *x, const void *copy) { QVariantConstructor constructor(x, copy); - QMetaTypeSwitcher::switcher(constructor, x->type, 0); + QMetaTypeSwitcher::switcher(constructor, x->type); } static void clear(QVariant::Private *d) { QVariantDestructor cleaner(d); - QMetaTypeSwitcher::switcher(cleaner, d->type, 0); + QMetaTypeSwitcher::switcher(cleaner, d->type); } static bool isNull(const QVariant::Private *d) { QVariantIsNull isNull(d); - return QMetaTypeSwitcher::switcher(isNull, d->type, 0); + return QMetaTypeSwitcher::switcher(isNull, d->type); } /*! @@ -143,7 +143,7 @@ static bool isNull(const QVariant::Private *d) static bool compare(const QVariant::Private *a, const QVariant::Private *b) { QVariantComparator comparator(a, b); - return QMetaTypeSwitcher::switcher(comparator, a->type, 0); + return QMetaTypeSwitcher::switcher(comparator, a->type); } /*! @@ -1401,7 +1401,7 @@ static void streamDebug(QDebug dbg, const QVariant &v) { QVariant::Private *d = const_cast(&v.data_ptr()); QVariantDebugStream stream(dbg, d); - QMetaTypeSwitcher::switcher(stream, d->type, 0); + QMetaTypeSwitcher::switcher(stream, d->type); } #endif @@ -1410,16 +1410,16 @@ const QVariant::Handler qt_kernel_variant_handler = { clear, isNull, #ifndef QT_NO_DATASTREAM - 0, - 0, + nullptr, + nullptr, #endif compare, convert, - 0, + nullptr, #if !defined(QT_NO_DEBUG_STREAM) streamDebug #else - 0 + nullptr #endif }; @@ -1436,16 +1436,16 @@ const QVariant::Handler qt_dummy_variant_handler = { dummyClear, dummyIsNull, #ifndef QT_NO_DATASTREAM - 0, - 0, + nullptr, + nullptr, #endif dummyCompare, dummyConvert, - 0, + nullptr, #if !defined(QT_NO_DEBUG_STREAM) dummyStreamDebug #else - 0 + nullptr #endif }; @@ -1555,16 +1555,16 @@ const QVariant::Handler qt_custom_variant_handler = { customClear, customIsNull, #ifndef QT_NO_DATASTREAM - 0, - 0, + nullptr, + nullptr, #endif customCompare, customConvert, - 0, + nullptr, #if !defined(QT_NO_DEBUG_STREAM) customStreamDebug #else - 0 + nullptr #endif }; @@ -2125,7 +2125,7 @@ QVariant::QVariant(const char *val) */ QVariant::QVariant(Type type) -{ create(type, 0); } +{ create(type, nullptr); } QVariant::QVariant(int typeId, const void *copy) { create(typeId, copy); d.is_null = false; } @@ -2667,7 +2667,7 @@ inline T qVariantToHelper(const QVariant::Private &d, const HandlersManager &han return ret; } - handlerManager[d.type]->convert(&d, targetType, &ret, 0); + handlerManager[d.type]->convert(&d, targetType, &ret, nullptr); return ret; } @@ -3217,7 +3217,7 @@ bool QVariant::toBool() const return d.data.b; bool res = false; - handlerManager[d.type]->convert(&d, Bool, &res, 0); + handlerManager[d.type]->convert(&d, Bool, &res, nullptr); return res; } @@ -3735,7 +3735,7 @@ bool QVariant::convert(int targetTypeId) if (!oldValue.canConvert(targetTypeId)) return false; - create(targetTypeId, 0); + create(targetTypeId, nullptr); // Fail if the value is not initialized or was forced null by a previous failed convert. if (oldValue.d.is_null && oldValue.d.type != QMetaType::Nullptr) return false; @@ -3760,7 +3760,7 @@ bool QVariant::convert(int targetTypeId) */ bool QVariant::convert(const int type, void *ptr) const { - return handlerManager[type]->convert(&d, type, ptr, 0); + return handlerManager[type]->convert(&d, type, ptr, nullptr); } diff --git a/src/corelib/serialization/qdatastream.cpp b/src/corelib/serialization/qdatastream.cpp index 2c29b81f7c..b3330d6cea 100644 --- a/src/corelib/serialization/qdatastream.cpp +++ b/src/corelib/serialization/qdatastream.cpp @@ -277,7 +277,7 @@ QT_BEGIN_NAMESPACE QDataStream::QDataStream() { - dev = 0; + dev = nullptr; owndev = false; byteorder = BigEndian; ver = Qt_DefaultCompiledVersion; @@ -433,7 +433,7 @@ bool QDataStream::atEnd() const */ QDataStream::FloatingPointPrecision QDataStream::floatingPointPrecision() const { - return d == 0 ? QDataStream::DoublePrecision : d->floatingPointPrecision; + return d ? d->floatingPointPrecision : QDataStream::DoublePrecision; } /*! @@ -458,7 +458,7 @@ QDataStream::FloatingPointPrecision QDataStream::floatingPointPrecision() const */ void QDataStream::setFloatingPointPrecision(QDataStream::FloatingPointPrecision precision) { - if (d == 0) + if (!d) d.reset(new QDataStreamPrivate()); d->floatingPointPrecision = precision; } @@ -639,7 +639,7 @@ void QDataStream::startTransaction() { CHECK_STREAM_PRECOND(Q_VOID) - if (d == 0) + if (!d) d.reset(new QDataStreamPrivate()); if (++d->transactionDepth == 1) { @@ -1043,7 +1043,7 @@ QDataStream &QDataStream::operator>>(char *&s) QDataStream &QDataStream::readBytes(char *&s, uint &l) { - s = 0; + s = nullptr; l = 0; CHECK_STREAM_PRECOND(*this) @@ -1054,8 +1054,8 @@ QDataStream &QDataStream::readBytes(char *&s, uint &l) const quint32 Step = 1024 * 1024; quint32 allocated = 0; - char *prevBuf = 0; - char *curBuf = 0; + char *prevBuf = nullptr; + char *curBuf = nullptr; do { int blockSize = qMin(Step, len - allocated); diff --git a/src/corelib/serialization/qjsonarray.cpp b/src/corelib/serialization/qjsonarray.cpp index 7dfa9b43f0..3a6244f5ac 100644 --- a/src/corelib/serialization/qjsonarray.cpp +++ b/src/corelib/serialization/qjsonarray.cpp @@ -132,7 +132,7 @@ QT_BEGIN_NAMESPACE Creates an empty array. */ QJsonArray::QJsonArray() - : d(0), a(0) + : d(nullptr), a(nullptr) { } @@ -168,8 +168,8 @@ QJsonArray::QJsonArray(QJsonPrivate::Data *data, QJsonPrivate::Array *array) */ void QJsonArray::initialize() { - d = 0; - a = 0; + d = nullptr; + a = nullptr; } /*! diff --git a/src/corelib/serialization/qjsondocument.cpp b/src/corelib/serialization/qjsondocument.cpp index 179a87c699..f8027efb58 100644 --- a/src/corelib/serialization/qjsondocument.cpp +++ b/src/corelib/serialization/qjsondocument.cpp @@ -84,7 +84,7 @@ QT_BEGIN_NAMESPACE * Constructs an empty and invalid document. */ QJsonDocument::QJsonDocument() - : d(0) + : d(nullptr) { } @@ -92,7 +92,7 @@ QJsonDocument::QJsonDocument() * Creates a QJsonDocument from \a object. */ QJsonDocument::QJsonDocument(const QJsonObject &object) - : d(0) + : d(nullptr) { setObject(object); } @@ -101,7 +101,7 @@ QJsonDocument::QJsonDocument(const QJsonObject &object) * Constructs a QJsonDocument from \a array. */ QJsonDocument::QJsonDocument(const QJsonArray &array) - : d(0) + : d(nullptr) { setArray(array); } @@ -236,7 +236,7 @@ const char *QJsonDocument::rawData(int *size) const { if (!d) { *size = 0; - return 0; + return nullptr; } *size = d->alloc; return d->rawData; @@ -635,7 +635,7 @@ bool QJsonDocument::operator==(const QJsonDocument &other) const */ bool QJsonDocument::isNull() const { - return (d == 0); + return (d == nullptr); } #if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY) diff --git a/src/corelib/serialization/qjsonobject.cpp b/src/corelib/serialization/qjsonobject.cpp index a9f25a119c..f92bdd0e80 100644 --- a/src/corelib/serialization/qjsonobject.cpp +++ b/src/corelib/serialization/qjsonobject.cpp @@ -110,7 +110,7 @@ QT_BEGIN_NAMESPACE \sa isEmpty() */ QJsonObject::QJsonObject() - : d(0), o(0) + : d(nullptr), o(nullptr) { } @@ -149,8 +149,8 @@ QJsonObject::QJsonObject(QJsonPrivate::Data *data, QJsonPrivate::Object *object) void QJsonObject::initialize() { - d = 0; - o = 0; + d = nullptr; + o = nullptr; } /*! diff --git a/src/corelib/serialization/qjsonparser.cpp b/src/corelib/serialization/qjsonparser.cpp index f29348d593..cd36bd5a5b 100644 --- a/src/corelib/serialization/qjsonparser.cpp +++ b/src/corelib/serialization/qjsonparser.cpp @@ -198,7 +198,9 @@ QString QJsonParseError::errorString() const using namespace QJsonPrivate; Parser::Parser(const char *json, int length) - : head(json), json(json), data(0), dataLength(0), current(0), nestingLevel(0), lastError(QJsonParseError::NoError) + : head(json), json(json), data(nullptr) + , dataLength(0), current(0), nestingLevel(0) + , lastError(QJsonParseError::NoError) { end = json + length; } diff --git a/src/corelib/serialization/qjsonvalue.cpp b/src/corelib/serialization/qjsonvalue.cpp index 1fc610d7c7..0bd28581f3 100644 --- a/src/corelib/serialization/qjsonvalue.cpp +++ b/src/corelib/serialization/qjsonvalue.cpp @@ -112,7 +112,7 @@ QT_BEGIN_NAMESPACE The default is to create a Null value. */ QJsonValue::QJsonValue(Type type) - : ui(0), d(0), t(type) + : ui(0), d(nullptr), t(type) { } @@ -120,7 +120,7 @@ QJsonValue::QJsonValue(Type type) \internal */ QJsonValue::QJsonValue(QJsonPrivate::Data *data, QJsonPrivate::Base *base, const QJsonPrivate::Value &v) - : d(0) + : d(nullptr) { t = (Type)(uint)v.type; switch (t) { @@ -154,7 +154,7 @@ QJsonValue::QJsonValue(QJsonPrivate::Data *data, QJsonPrivate::Base *base, const Creates a value of type Bool, with value \a b. */ QJsonValue::QJsonValue(bool b) - : d(0), t(Bool) + : d(nullptr), t(Bool) { this->b = b; } @@ -163,7 +163,7 @@ QJsonValue::QJsonValue(bool b) Creates a value of type Double, with value \a n. */ QJsonValue::QJsonValue(double n) - : d(0), t(Double) + : d(nullptr), t(Double) { this->dbl = n; } @@ -173,7 +173,7 @@ QJsonValue::QJsonValue(double n) Creates a value of type Double, with value \a n. */ QJsonValue::QJsonValue(int n) - : d(0), t(Double) + : d(nullptr), t(Double) { this->dbl = n; } @@ -185,7 +185,7 @@ QJsonValue::QJsonValue(int n) If you pass in values outside this range expect a loss of precision to occur. */ QJsonValue::QJsonValue(qint64 n) - : d(0), t(Double) + : d(nullptr), t(Double) { this->dbl = double(n); } @@ -194,7 +194,7 @@ QJsonValue::QJsonValue(qint64 n) Creates a value of type String, with value \a s. */ QJsonValue::QJsonValue(const QString &s) - : d(0), t(String) + : d(nullptr), t(String) { stringDataFromQStringHelper(s); } @@ -221,7 +221,7 @@ void QJsonValue::stringDataFromQStringHelper(const QString &string) Creates a value of type String, with value \a s. */ QJsonValue::QJsonValue(QLatin1String s) - : d(0), t(String) + : d(nullptr), t(String) { // ### FIXME: Avoid creating the temp QString below QString str(s); diff --git a/src/corelib/serialization/qtextstream.cpp b/src/corelib/serialization/qtextstream.cpp index ef2a9c97ee..9d4bc223ab 100644 --- a/src/corelib/serialization/qtextstream.cpp +++ b/src/corelib/serialization/qtextstream.cpp @@ -327,7 +327,7 @@ QT_BEGIN_NAMESPACE QTextStreamPrivate::QTextStreamPrivate(QTextStream *q_ptr) : #if QT_CONFIG(textcodec) - readConverterSavedState(0), + readConverterSavedState(nullptr), #endif readConverterSavedStateOffset(0), locale(QLocale::c()) @@ -381,7 +381,7 @@ void QTextStreamPrivate::Params::reset() padChar = QLatin1Char(' '); fieldAlignment = QTextStream::AlignRight; realNumberNotation = QTextStream::SmartNotation; - numberFlags = 0; + numberFlags = { }; } /*! @@ -391,9 +391,9 @@ void QTextStreamPrivate::reset() { params.reset(); - device = 0; + device = nullptr; deleteDevice = false; - string = 0; + string = nullptr; stringOffset = 0; stringOpenMode = QIODevice::NotOpen; @@ -406,7 +406,7 @@ void QTextStreamPrivate::reset() resetCodecConverterStateHelper(&readConverterState); resetCodecConverterStateHelper(&writeConverterState); delete readConverterSavedState; - readConverterSavedState = 0; + readConverterSavedState = nullptr; writeConverterState.flags |= QTextCodec::IgnoreHeader; autoDetectUnicode = true; #endif @@ -1207,7 +1207,7 @@ bool QTextStream::seek(qint64 pos) resetCodecConverterStateHelper(&d->readConverterState); resetCodecConverterStateHelper(&d->writeConverterState); delete d->readConverterSavedState; - d->readConverterSavedState = 0; + d->readConverterSavedState = nullptr; d->writeConverterState.flags |= QTextCodec::IgnoreHeader; #endif return true; @@ -1295,7 +1295,7 @@ void QTextStream::skipWhiteSpace() { Q_D(QTextStream); CHECK_VALID_STREAM(Q_VOID); - d->scan(0, 0, 0, QTextStreamPrivate::NotSpace); + d->scan(nullptr, nullptr, 0, QTextStreamPrivate::NotSpace); d->consumeLastToken(); } @@ -1751,7 +1751,7 @@ QString QTextStream::read(qint64 maxlen) */ QTextStreamPrivate::NumberParsingStatus QTextStreamPrivate::getNumber(qulonglong *ret) { - scan(0, 0, 0, NotSpace); + scan(nullptr, nullptr, 0, NotSpace); consumeLastToken(); // detect int encoding @@ -1980,7 +1980,7 @@ bool QTextStreamPrivate::getReal(double *f) ParserState state = Init; InputToken input = None; - scan(0, 0, 0, NotSpace); + scan(nullptr, nullptr, 0, NotSpace); consumeLastToken(); const int BufferSize = 128; @@ -2084,7 +2084,7 @@ QTextStream &QTextStream::operator>>(QChar &c) { Q_D(QTextStream); CHECK_VALID_STREAM(*this); - d->scan(0, 0, 0, QTextStreamPrivate::NotSpace); + d->scan(nullptr, nullptr, 0, QTextStreamPrivate::NotSpace); if (!d->getChar(&c)) setStatus(ReadPastEnd); return *this; @@ -2245,7 +2245,7 @@ QTextStream &QTextStream::operator>>(QString &str) CHECK_VALID_STREAM(*this); str.clear(); - d->scan(0, 0, 0, QTextStreamPrivate::NotSpace); + d->scan(nullptr, nullptr, 0, QTextStreamPrivate::NotSpace); d->consumeLastToken(); const QChar *ptr; @@ -2273,7 +2273,7 @@ QTextStream &QTextStream::operator>>(QByteArray &array) CHECK_VALID_STREAM(*this); array.clear(); - d->scan(0, 0, 0, QTextStreamPrivate::NotSpace); + d->scan(nullptr, nullptr, 0, QTextStreamPrivate::NotSpace); d->consumeLastToken(); const QChar *ptr; @@ -2308,7 +2308,7 @@ QTextStream &QTextStream::operator>>(char *c) Q_D(QTextStream); *c = 0; CHECK_VALID_STREAM(*this); - d->scan(0, 0, 0, QTextStreamPrivate::NotSpace); + d->scan(nullptr, nullptr, 0, QTextStreamPrivate::NotSpace); d->consumeLastToken(); const QChar *ptr; diff --git a/src/corelib/serialization/qxmlstream.cpp b/src/corelib/serialization/qxmlstream.cpp index d43d9c4e14..be3a476cb2 100644 --- a/src/corelib/serialization/qxmlstream.cpp +++ b/src/corelib/serialization/qxmlstream.cpp @@ -58,9 +58,9 @@ // case for most bootstrapped applications. #define Q_DECLARE_TR_FUNCTIONS(context) \ public: \ - static inline QString tr(const char *sourceText, const char *comment = 0) \ + static inline QString tr(const char *sourceText, const char *comment = nullptr) \ { Q_UNUSED(comment); return QString::fromLatin1(sourceText); } \ - static inline QString trUtf8(const char *sourceText, const char *comment = 0) \ + static inline QString trUtf8(const char *sourceText, const char *comment = nullptr) \ { Q_UNUSED(comment); return QString::fromLatin1(sourceText); } \ static inline QString tr(const char *sourceText, const char*, int) \ { return QString::fromLatin1(sourceText); } \ @@ -548,7 +548,7 @@ void QXmlStreamReader::clear() if (d->device) { if (d->deleteDevice) delete d->device; - d->device = 0; + d->device = nullptr; } } @@ -792,16 +792,16 @@ QXmlStreamPrivateTagStack::QXmlStreamPrivateTagStack() QXmlStreamReaderPrivate::QXmlStreamReaderPrivate(QXmlStreamReader *q) :q_ptr(q) { - device = 0; + device = nullptr; deleteDevice = false; #if QT_CONFIG(textcodec) - decoder = 0; + decoder = nullptr; #endif stack_size = 64; - sym_stack = 0; - state_stack = 0; + sym_stack = nullptr; + state_stack = nullptr; reallocateStack(); - entityResolver = 0; + entityResolver = nullptr; init(); #define ADD_PREDEFINED(n, v) \ do { \ @@ -843,11 +843,11 @@ void QXmlStreamReaderPrivate::init() #if QT_CONFIG(textcodec) codec = QTextCodec::codecForMib(106); // utf8 delete decoder; - decoder = 0; + decoder = nullptr; #endif attributeStack.clear(); attributeStack.reserve(16); - entityParser = 0; + entityParser = nullptr; hasCheckedStartDocument = false; normalizeLiterals = false; hasSeenTag = false; @@ -3024,8 +3024,8 @@ QXmlStreamWriterPrivate::QXmlStreamWriterPrivate(QXmlStreamWriter *q) :autoFormattingIndent(4, ' ') { q_ptr = q; - device = 0; - stringDevice = 0; + device = nullptr; + stringDevice = nullptr; deleteDevice = false; #if QT_CONFIG(textcodec) codec = QTextCodec::codecForMib(106); // utf8 @@ -3315,7 +3315,7 @@ void QXmlStreamWriter::setDevice(QIODevice *device) Q_D(QXmlStreamWriter); if (device == d->device) return; - d->stringDevice = 0; + d->stringDevice = nullptr; if (d->deleteDevice) { delete d->device; d->deleteDevice = false; diff --git a/src/corelib/serialization/qxmlstream.g b/src/corelib/serialization/qxmlstream.g index e6328a11ac..12ecc9bdb2 100644 --- a/src/corelib/serialization/qxmlstream.g +++ b/src/corelib/serialization/qxmlstream.g @@ -506,7 +506,7 @@ public: int fastScanLiteralContent(); int fastScanSpace(); int fastScanContentCharList(); - int fastScanName(int *prefix = 0); + int fastScanName(int *prefix = nullptr); inline int fastScanNMTOKEN(); diff --git a/src/corelib/time/qdatetime.cpp b/src/corelib/time/qdatetime.cpp index 9220d210f1..92b243f60e 100644 --- a/src/corelib/time/qdatetime.cpp +++ b/src/corelib/time/qdatetime.cpp @@ -615,7 +615,7 @@ int QDate::weekNumber(int *yearNumber) const Q_ASSERT(week == 53 || week == 1); } - if (yearNumber != 0) + if (yearNumber) *yearNumber = year; return week; } @@ -2265,7 +2265,7 @@ QTime QTime::fromString(const QString &string, Qt::DateFormat format) case Qt::ISODateWithMs: case Qt::TextDate: default: - return fromIsoTimeString(QStringRef(&string), format, 0); + return fromIsoTimeString(QStringRef(&string), format, nullptr); } } @@ -2512,7 +2512,7 @@ int QDateTimeParser::startsWithLocalTimeZone(const QStringRef name) // then null date/time will be returned, you should adjust the date first if // you need a guaranteed result. static qint64 qt_mktime(QDate *date, QTime *time, QDateTimePrivate::DaylightStatus *daylightStatus, - QString *abbreviation, bool *ok = 0) + QString *abbreviation, bool *ok = nullptr) { const qint64 msec = time->msec(); int yy, mm, dd; @@ -2601,7 +2601,7 @@ static bool qt_localtime(qint64 msecsSinceEpoch, QDate *localDate, QTime *localT #if QT_CONFIG(thread) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) // Use the reentrant version of localtime() where available // as is thread-safe and doesn't use a shared static data area - tm *res = 0; + tm *res = nullptr; res = localtime_r(&secsSinceEpoch, &local); if (res) valid = true; @@ -2611,7 +2611,7 @@ static bool qt_localtime(qint64 msecsSinceEpoch, QDate *localDate, QTime *localT #else // Returns shared static data which may be overwritten at any time // So copy the result asap - tm *res = 0; + tm *res = nullptr; res = localtime(&secsSinceEpoch); if (res) { local = *res; @@ -2674,7 +2674,7 @@ static qint64 timeToMSecs(const QDate &date, const QTime &time) // Convert an MSecs Since Epoch into Local Time static bool epochMSecsToLocalTime(qint64 msecs, QDate *localDate, QTime *localTime, - QDateTimePrivate::DaylightStatus *daylightStatus = 0) + QDateTimePrivate::DaylightStatus *daylightStatus = nullptr) { if (msecs < 0) { // Docs state any LocalTime before 1970-01-01 will *not* have any Daylight Time applied @@ -2714,8 +2714,8 @@ static bool epochMSecsToLocalTime(qint64 msecs, QDate *localDate, QTime *localTi // values from mktime for the adjusted local date and time. static qint64 localMSecsToEpochMSecs(qint64 localMsecs, QDateTimePrivate::DaylightStatus *daylightStatus, - QDate *localDate = 0, QTime *localTime = 0, - QString *abbreviation = 0) + QDate *localDate = nullptr, QTime *localTime = nullptr, + QString *abbreviation = nullptr) { QDate dt; QTime tm; @@ -3018,7 +3018,7 @@ static void setDateTime(QDateTimeData &d, const QDate &date, const QTime &time) if (!useTime.isValid() && date.isValid()) useTime = QTime::fromMSecsSinceStartOfDay(0); - QDateTimePrivate::StatusFlags newStatus = 0; + QDateTimePrivate::StatusFlags newStatus = { }; // Set date value and status qint64 days = 0; @@ -3096,7 +3096,7 @@ inline QDateTime::Data::Data(Qt::TimeSpec spec) // the structure is too small, we need to detach d = new QDateTimePrivate; d->ref.ref(); - d->m_status = mergeSpec(0, spec); + d->m_status = mergeSpec(nullptr, spec); } } @@ -3550,7 +3550,7 @@ QDate QDateTime::date() const if (!status.testFlag(QDateTimePrivate::ValidDate)) return QDate(); QDate dt; - msecsToTime(getMSecs(d), &dt, 0); + msecsToTime(getMSecs(d), &dt, nullptr); return dt; } @@ -3566,7 +3566,7 @@ QTime QDateTime::time() const if (!status.testFlag(QDateTimePrivate::ValidTime)) return QTime(); QTime tm; - msecsToTime(getMSecs(d), 0, &tm); + msecsToTime(getMSecs(d), nullptr, &tm); return tm; } @@ -3684,7 +3684,7 @@ QString QDateTime::timeZoneAbbreviation() const case Qt::LocalTime: { QString abbrev; auto status = extractDaylightStatus(getStatus(d)); - localMSecsToEpochMSecs(getMSecs(d), &status, 0, 0, &abbrev); + localMSecsToEpochMSecs(getMSecs(d), &status, nullptr, nullptr, &abbrev); return abbrev; } } @@ -4769,14 +4769,14 @@ qint64 QDateTime::currentMSecsSinceEpoch() noexcept // posix compliant system // we have milliseconds struct timeval tv; - gettimeofday(&tv, 0); + gettimeofday(&tv, nullptr); return qint64(tv.tv_sec) * Q_INT64_C(1000) + tv.tv_usec / 1000; } qint64 QDateTime::currentSecsSinceEpoch() noexcept { struct timeval tv; - gettimeofday(&tv, 0); + gettimeofday(&tv, nullptr); return qint64(tv.tv_sec); } #else diff --git a/src/corelib/tools/qarraydata.cpp b/src/corelib/tools/qarraydata.cpp index 88d8b8244d..844cde5563 100644 --- a/src/corelib/tools/qarraydata.cpp +++ b/src/corelib/tools/qarraydata.cpp @@ -215,7 +215,7 @@ QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment, headerSize += (alignment - Q_ALIGNOF(QArrayData)); if (headerSize > size_t(MaxAllocSize)) - return 0; + return nullptr; size_t allocSize = calculateBlockSize(capacity, objectSize, headerSize, options); QArrayData *header = static_cast(::malloc(allocSize)); diff --git a/src/corelib/tools/qbytearray.cpp b/src/corelib/tools/qbytearray.cpp index 9526350126..c25d39461f 100644 --- a/src/corelib/tools/qbytearray.cpp +++ b/src/corelib/tools/qbytearray.cpp @@ -2072,7 +2072,7 @@ static inline QByteArray &qbytearray_insert(QByteArray *ba, { Q_ASSERT(pos >= 0); - if (pos < 0 || len <= 0 || arr == 0) + if (pos < 0 || len <= 0 || arr == nullptr) return *ba; int oldsize = ba->size(); @@ -4775,7 +4775,7 @@ static void q_toPercentEncoding(QByteArray *ba, const char *dontEncode, const ch QByteArray input = *ba; int len = input.count(); const char *inputData = input.constData(); - char *output = 0; + char *output = nullptr; int length = 0; for (int i = 0; i < len; ++i) { @@ -4815,7 +4815,7 @@ void q_toPercentEncoding(QByteArray *ba, const char *exclude, const char *includ void q_normalizePercentEncoding(QByteArray *ba, const char *exclude) { q_fromPercentEncoding(ba, '%'); - q_toPercentEncoding(ba, exclude, 0, '%'); + q_toPercentEncoding(ba, exclude, nullptr, '%'); } /*! diff --git a/src/corelib/tools/qbytearraymatcher.cpp b/src/corelib/tools/qbytearraymatcher.cpp index d2eb4e0e3c..72e09226af 100644 --- a/src/corelib/tools/qbytearraymatcher.cpp +++ b/src/corelib/tools/qbytearraymatcher.cpp @@ -116,9 +116,9 @@ static inline int bm_find(const uchar *cc, int l, int index, const uchar *puc, u Call setPattern() to give it a pattern to match. */ QByteArrayMatcher::QByteArrayMatcher() - : d(0) + : d(nullptr) { - p.p = 0; + p.p = nullptr; p.l = 0; memset(p.q_skiptable, 0, sizeof(p.q_skiptable)); } @@ -129,7 +129,7 @@ QByteArrayMatcher::QByteArrayMatcher() the destructor does not delete \a pattern. */ QByteArrayMatcher::QByteArrayMatcher(const char *pattern, int length) - : d(0) + : d(nullptr) { p.p = reinterpret_cast(pattern); p.l = length; @@ -141,7 +141,7 @@ QByteArrayMatcher::QByteArrayMatcher(const char *pattern, int length) Call indexIn() to perform a search. */ QByteArrayMatcher::QByteArrayMatcher(const QByteArray &pattern) - : d(0), q_pattern(pattern) + : d(nullptr), q_pattern(pattern) { p.p = reinterpret_cast(pattern.constData()); p.l = pattern.size(); @@ -152,7 +152,7 @@ QByteArrayMatcher::QByteArrayMatcher(const QByteArray &pattern) Copies the \a other byte array matcher to this byte array matcher. */ QByteArrayMatcher::QByteArrayMatcher(const QByteArrayMatcher &other) - : d(0) + : d(nullptr) { operator=(other); } diff --git a/src/corelib/tools/qchar.cpp b/src/corelib/tools/qchar.cpp index d6061defc3..e097e4a5fe 100644 --- a/src/corelib/tools/qchar.cpp +++ b/src/corelib/tools/qchar.cpp @@ -1339,7 +1339,7 @@ static const unsigned short * QT_FASTCALL decompositionHelper if (index == 0xffff) { *length = 0; *tag = QChar::NoDecomposition; - return 0; + return nullptr; } const unsigned short *decomposition = uc_decomposition_map+index; diff --git a/src/corelib/tools/qhash.cpp b/src/corelib/tools/qhash.cpp index 85a3456d71..6b003fe739 100644 --- a/src/corelib/tools/qhash.cpp +++ b/src/corelib/tools/qhash.cpp @@ -471,7 +471,7 @@ static int countBits(int hint) const int MinNumBits = 4; const QHashData QHashData::shared_null = { - 0, 0, Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, MinNumBits, 0, 0, 0, true, false, 0 + nullptr, nullptr, Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, MinNumBits, 0, 0, 0, true, false, 0 }; void *QHashData::allocateNode(int nodeAlign) @@ -501,8 +501,8 @@ QHashData *QHashData::detach_helper(void (*node_duplicate)(Node *, void *), if (this == &shared_null) qt_initialize_qhash_seed(); // may throw d = new QHashData; - d->fakeNext = 0; - d->buckets = 0; + d->fakeNext = nullptr; + d->buckets = nullptr; d->ref.initializeOwned(); d->size = size; d->nodeSize = nodeSize; diff --git a/src/corelib/tools/qlist.cpp b/src/corelib/tools/qlist.cpp index 48617f0539..dfebd57e34 100644 --- a/src/corelib/tools/qlist.cpp +++ b/src/corelib/tools/qlist.cpp @@ -75,7 +75,7 @@ template class Q_CORE_EXPORT QVector; the number of elements in the list. */ -const QListData::Data QListData::shared_null = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, { 0 } }; +const QListData::Data QListData::shared_null = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, { nullptr } }; /*! * Detaches the QListData by allocating new memory for a list which will be bigger diff --git a/src/corelib/tools/qlocale.cpp b/src/corelib/tools/qlocale.cpp index c8740e55f3..939f8eb34d 100644 --- a/src/corelib/tools/qlocale.cpp +++ b/src/corelib/tools/qlocale.cpp @@ -301,9 +301,9 @@ QByteArray QLocaleId::name(char separator) const const unsigned char *lang = language_code_list + 3 * language_id; const unsigned char *script = - (script_id != QLocale::AnyScript ? script_code_list + 4 * script_id : 0); + (script_id != QLocale::AnyScript ? script_code_list + 4 * script_id : nullptr); const unsigned char *country = - (country_id != QLocale::AnyCountry ? country_code_list + 3 * country_id : 0); + (country_id != QLocale::AnyCountry ? country_code_list + 3 * country_id : nullptr); char len = (lang[2] != 0 ? 3 : 2) + (script ? 4+1 : 0) + (country ? (country[2] != 0 ? 3 : 2)+1 : 0); QByteArray name(len, Qt::Uninitialized); char *uc = name.data(); @@ -373,7 +373,7 @@ static const QLocaleData *findLocaleDataById(const QLocaleId &localeId) } while (data->m_language_id && data->m_language_id == localeId.language_id); } - return 0; + return nullptr; } const QLocaleData *QLocaleData::findLocaleData(QLocale::Language language, QLocale::Script script, QLocale::Country country) @@ -604,7 +604,7 @@ int qt_repeatCount(QStringView s) return int(j); } -static const QLocaleData *default_data = 0; +static const QLocaleData *default_data = nullptr; static QLocale::NumberOptions default_number_options = QLocale::DefaultNumberOptions; static const QLocaleData *const c_data = locale_data; diff --git a/src/corelib/tools/qlocale_p.h b/src/corelib/tools/qlocale_p.h index 15398ded32..59cc33700d 100644 --- a/src/corelib/tools/qlocale_p.h +++ b/src/corelib/tools/qlocale_p.h @@ -250,14 +250,14 @@ public: if (qIsInf(d)) return float(d); if (std::fabs(d) > std::numeric_limits::max()) { - if (ok != nullptr) + if (ok) *ok = false; const float huge = std::numeric_limits::infinity(); return d < 0 ? -huge : huge; } if (d != 0 && float(d) == 0) { // Values that underflow double already failed. Match them: - if (ok != 0) + if (ok) *ok = false; return 0; } diff --git a/src/corelib/tools/qlocale_tools.cpp b/src/corelib/tools/qlocale_tools.cpp index 53258bec3e..db8c8cd12f 100644 --- a/src/corelib/tools/qlocale_tools.cpp +++ b/src/corelib/tools/qlocale_tools.cpp @@ -398,7 +398,7 @@ qstrtoull(const char * nptr, const char **endptr, int base, bool *ok) *ok = true; errno = 0; - char *endptr2 = 0; + char *endptr2 = nullptr; unsigned long long result = qt_strtoull(nptr, &endptr2, base); if (endptr) *endptr = endptr2; @@ -415,7 +415,7 @@ qstrtoll(const char * nptr, const char **endptr, int base, bool *ok) { *ok = true; errno = 0; - char *endptr2 = 0; + char *endptr2 = nullptr; long long result = qt_strtoll(nptr, &endptr2, base); if (endptr) *endptr = endptr2; diff --git a/src/corelib/tools/qmap.cpp b/src/corelib/tools/qmap.cpp index 5f7275c5f8..a0ec372f9a 100644 --- a/src/corelib/tools/qmap.cpp +++ b/src/corelib/tools/qmap.cpp @@ -48,7 +48,7 @@ QT_BEGIN_NAMESPACE -const QMapDataBase QMapDataBase::shared_null = { Q_REFCOUNT_INITIALIZE_STATIC, 0, { 0, 0, 0 }, 0 }; +const QMapDataBase QMapDataBase::shared_null = { Q_REFCOUNT_INITIALIZE_STATIC, 0, { 0, nullptr, nullptr }, nullptr }; const QMapNodeBase *QMapNodeBase::nextNode() const { @@ -92,7 +92,7 @@ void QMapDataBase::rotateLeft(QMapNodeBase *x) QMapNodeBase *&root = header.left; QMapNodeBase *y = x->right; x->right = y->left; - if (y->left != 0) + if (y->left != nullptr) y->left->setParent(x); y->setParent(x->parent()); if (x == root) @@ -111,7 +111,7 @@ void QMapDataBase::rotateRight(QMapNodeBase *x) QMapNodeBase *&root = header.left; QMapNodeBase *y = x->left; x->left = y->right; - if (y->right != 0) + if (y->right != nullptr) y->right->setParent(x); y->setParent(x->parent()); if (x == root) @@ -173,7 +173,7 @@ void QMapDataBase::freeNodeAndRebalance(QMapNodeBase *z) QMapNodeBase *y = z; QMapNodeBase *x; QMapNodeBase *x_parent; - if (y->left == 0) { + if (y->left == nullptr) { x = y->right; if (y == mostLeftNode) { if (x) @@ -182,11 +182,11 @@ void QMapDataBase::freeNodeAndRebalance(QMapNodeBase *z) mostLeftNode = y->parent(); } } else { - if (y->right == 0) { + if (y->right == nullptr) { x = y->left; } else { y = y->right; - while (y->left != 0) + while (y->left != nullptr) y = y->left; x = y->right; } @@ -228,7 +228,7 @@ void QMapDataBase::freeNodeAndRebalance(QMapNodeBase *z) z->parent()->right = x; } if (y->color() != QMapNodeBase::Red) { - while (x != root && (x == 0 || x->color() == QMapNodeBase::Black)) { + while (x != root && (x == nullptr || x->color() == QMapNodeBase::Black)) { if (x == x_parent->left) { QMapNodeBase *w = x_parent->right; if (w->color() == QMapNodeBase::Red) { @@ -237,13 +237,13 @@ void QMapDataBase::freeNodeAndRebalance(QMapNodeBase *z) rotateLeft(x_parent); w = x_parent->right; } - if ((w->left == 0 || w->left->color() == QMapNodeBase::Black) && - (w->right == 0 || w->right->color() == QMapNodeBase::Black)) { + if ((w->left == nullptr || w->left->color() == QMapNodeBase::Black) && + (w->right == nullptr || w->right->color() == QMapNodeBase::Black)) { w->setColor(QMapNodeBase::Red); x = x_parent; x_parent = x_parent->parent(); } else { - if (w->right == 0 || w->right->color() == QMapNodeBase::Black) { + if (w->right == nullptr || w->right->color() == QMapNodeBase::Black) { if (w->left) w->left->setColor(QMapNodeBase::Black); w->setColor(QMapNodeBase::Red); @@ -265,13 +265,13 @@ void QMapDataBase::freeNodeAndRebalance(QMapNodeBase *z) rotateRight(x_parent); w = x_parent->left; } - if ((w->right == 0 || w->right->color() == QMapNodeBase::Black) && - (w->left == 0 || w->left->color() == QMapNodeBase::Black)) { + if ((w->right == nullptr || w->right->color() == QMapNodeBase::Black) && + (w->left == nullptr|| w->left->color() == QMapNodeBase::Black)) { w->setColor(QMapNodeBase::Red); x = x_parent; x_parent = x_parent->parent(); } else { - if (w->left == 0 || w->left->color() == QMapNodeBase::Black) { + if (w->left == nullptr || w->left->color() == QMapNodeBase::Black) { if (w->right) w->right->setColor(QMapNodeBase::Black); w->setColor(QMapNodeBase::Red); @@ -363,8 +363,8 @@ QMapDataBase *QMapDataBase::createData() d->size = 0; d->header.p = 0; - d->header.left = 0; - d->header.right = 0; + d->header.left = nullptr; + d->header.right = nullptr; d->mostLeftNode = &(d->header); return d; diff --git a/src/corelib/tools/qregexp.cpp b/src/corelib/tools/qregexp.cpp index d970843dea..132ff48846 100644 --- a/src/corelib/tools/qregexp.cpp +++ b/src/corelib/tools/qregexp.cpp @@ -937,10 +937,10 @@ struct QRegExpMatchState const QRegExpEngine *eng; - inline QRegExpMatchState() : bigArray(0), captured(0) {} + inline QRegExpMatchState() : bigArray(nullptr), captured(nullptr) {} inline ~QRegExpMatchState() { free(bigArray); } - void drain() { free(bigArray); bigArray = 0; captured = 0; } // to save memory + void drain() { free(bigArray); bigArray = nullptr; captured = nullptr; } // to save memory void prepareForMatch(QRegExpEngine *eng); void match(const QChar *str, int len, int pos, bool minimal, bool oneTest, int caretIndex); @@ -1428,7 +1428,7 @@ void QRegExpMatchState::match(const QChar *str0, int len0, int pos0, #endif { in = str0; - if (in == 0) + if (in == nullptr) in = &char_null; pos = pos0; caretPos = caretIndex; @@ -2910,7 +2910,7 @@ int QRegExpEngine::getEscape() #ifndef QT_NO_REGEXP_ESCAPE if ((prevCh & ~0xff) == 0) { const char *p = strchr(tab, prevCh); - if (p != 0) + if (p != nullptr) return Tok_Char | backTab[p - tab]; } #endif @@ -3530,7 +3530,7 @@ int QRegExpEngine::parse(const QChar *pattern, int len) #endif box.cat(middleBox); box.cat(rightBox); - yyCharClass.reset(0); + yyCharClass.reset(); #ifndef QT_NO_REGEXP_CAPTURE for (int i = 0; i < nf; ++i) { @@ -3608,7 +3608,7 @@ int QRegExpEngine::parse(const QChar *pattern, int len) void QRegExpEngine::parseAtom(Box *box) { #ifndef QT_NO_REGEXP_LOOKAHEAD - QRegExpEngine *eng = 0; + QRegExpEngine *eng = nullptr; bool neg; int len; #endif @@ -3805,9 +3805,9 @@ struct QRegExpPrivate QRegExpMatchState matchState; inline QRegExpPrivate() - : eng(0), engineKey(QString(), QRegExp::RegExp, Qt::CaseSensitive), minimal(false) { } + : eng(nullptr), engineKey(QString(), QRegExp::RegExp, Qt::CaseSensitive), minimal(false) { } inline QRegExpPrivate(const QRegExpEngineKey &key) - : eng(0), engineKey(key), minimal(false) {} + : eng(nullptr), engineKey(key), minimal(false) {} }; #if !defined(QT_NO_REGEXP_OPTIM) @@ -3886,9 +3886,9 @@ static void prepareEngineForMatch(QRegExpPrivate *priv, const QString &str) static void invalidateEngine(QRegExpPrivate *priv) { - if (priv->eng != 0) { + if (priv->eng) { derefEngine(priv->eng, priv->engineKey); - priv->eng = 0; + priv->eng = nullptr; priv->matchState.drain(); } } diff --git a/src/corelib/tools/qringbuffer.cpp b/src/corelib/tools/qringbuffer.cpp index 67cce57d01..311058a776 100644 --- a/src/corelib/tools/qringbuffer.cpp +++ b/src/corelib/tools/qringbuffer.cpp @@ -105,7 +105,7 @@ const char *QRingBuffer::readPointerAtPosition(qint64 pos, qint64 &length) const } length = 0; - return 0; + return nullptr; } void QRingBuffer::free(qint64 bytes) diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index 963c2a4d34..345a786df4 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -3044,7 +3044,7 @@ void QString::replace_helper(uint *indices, int nIndices, int blen, const QChar { // Copy after if it lies inside our own d->data() area (which we could // possibly invalidate via a realloc or modify by replacement). - QChar *afterBuffer = 0; + QChar *afterBuffer = nullptr; if (pointsIntoRange(after, d->data(), d->size)) // Use copy in place of vulnerable original: after = afterBuffer = textCopy(after, alen); @@ -3129,7 +3129,7 @@ QString &QString::replace(const QChar *before, int blen, return *this; QStringMatcher matcher(before, blen, cs); - QChar *beforeBuffer = 0, *afterBuffer = 0; + QChar *beforeBuffer = nullptr, *afterBuffer = nullptr; int index = 0; while (1) { @@ -5591,7 +5591,7 @@ QString QString::fromUtf16(const ushort *unicode, int size) while (unicode[size] != 0) ++size; } - return QUtf16::convertToUnicode((const char *)unicode, size*2, 0); + return QUtf16::convertToUnicode((const char *)unicode, size*2, nullptr); } /*! @@ -5645,7 +5645,7 @@ QString QString::fromUcs4(const uint *unicode, int size) while (unicode[size] != 0) ++size; } - return QUtf32::convertToUnicode((const char *)unicode, size*4, 0); + return QUtf32::convertToUnicode((const char *)unicode, size*4, nullptr); } @@ -8060,7 +8060,7 @@ void qt_string_normalize(QString *data, QString::NormalizationForm mode, QChar:: version = QChar::currentUnicodeVersion(); } else if (int(version) <= NormalizationCorrectionsVersionMax) { const QString &s = *data; - QChar *d = 0; + QChar *d = nullptr; for (int i = 0; i < NumNormalizationCorrections; ++i) { const NormalizationCorrection &n = uc_normalization_corrections[i]; if (n.version > version) { diff --git a/src/corelib/tools/qstringmatcher.cpp b/src/corelib/tools/qstringmatcher.cpp index 2e2ae18b9a..167a467480 100644 --- a/src/corelib/tools/qstringmatcher.cpp +++ b/src/corelib/tools/qstringmatcher.cpp @@ -149,7 +149,7 @@ static inline qsizetype bm_find(const ushort *uc, qsizetype l, qsizetype index, Call setPattern() to give it a pattern to match. */ QStringMatcher::QStringMatcher() - : d_ptr(0), q_cs(Qt::CaseSensitive) + : d_ptr(nullptr), q_cs(Qt::CaseSensitive) { memset(q_data, 0, sizeof(q_data)); } @@ -161,7 +161,7 @@ QStringMatcher::QStringMatcher() Call indexIn() to perform a search. */ QStringMatcher::QStringMatcher(const QString &pattern, Qt::CaseSensitivity cs) - : d_ptr(0), q_pattern(pattern), q_cs(cs) + : d_ptr(nullptr), q_pattern(pattern), q_cs(cs) { p.uc = pattern.unicode(); p.len = pattern.size(); @@ -200,7 +200,7 @@ QStringMatcher::QStringMatcher(QStringView str, Qt::CaseSensitivity cs) Copies the \a other string matcher to this string matcher. */ QStringMatcher::QStringMatcher(const QStringMatcher &other) - : d_ptr(0) + : d_ptr(nullptr) { operator=(other); } diff --git a/src/xml/dom/qdom.cpp b/src/xml/dom/qdom.cpp index 6498d53b96..25655c09b2 100644 --- a/src/xml/dom/qdom.cpp +++ b/src/xml/dom/qdom.cpp @@ -136,7 +136,7 @@ public: class QDomNodePrivate { public: - QDomNodePrivate(QDomDocumentPrivate*, QDomNodePrivate* parent = 0); + QDomNodePrivate(QDomDocumentPrivate*, QDomNodePrivate* parent = nullptr); QDomNodePrivate(QDomNodePrivate* n, bool deep); virtual ~QDomNodePrivate(); @@ -159,11 +159,11 @@ public: virtual void normalize(); virtual void clear(); - inline QDomNodePrivate* parent() const { return hasParent ? ownerNode : 0; } + inline QDomNodePrivate* parent() const { return hasParent ? ownerNode : nullptr; } inline void setParent(QDomNodePrivate *p) { ownerNode = p; hasParent = true; } void setNoParent() { - ownerNode = hasParent ? (QDomNodePrivate*)ownerDocument() : 0; + ownerNode = hasParent ? (QDomNodePrivate*)ownerDocument() : nullptr; hasParent = false; } @@ -289,7 +289,7 @@ public: class QDomDocumentTypePrivate : public QDomNodePrivate { public: - QDomDocumentTypePrivate(QDomDocumentPrivate*, QDomNodePrivate* parent = 0); + QDomDocumentTypePrivate(QDomDocumentPrivate*, QDomNodePrivate* parent = nullptr); QDomDocumentTypePrivate(QDomDocumentTypePrivate* n, bool deep); ~QDomDocumentTypePrivate(); void init(); @@ -317,7 +317,7 @@ public: class QDomDocumentFragmentPrivate : public QDomNodePrivate { public: - QDomDocumentFragmentPrivate(QDomDocumentPrivate*, QDomNodePrivate* parent = 0); + QDomDocumentFragmentPrivate(QDomDocumentPrivate*, QDomNodePrivate* parent = nullptr); QDomDocumentFragmentPrivate(QDomNodePrivate* n, bool deep); // Reimplemented from QDomNodePrivate @@ -907,7 +907,7 @@ QDomImplementationPrivate* QDomImplementationPrivate::clone() */ QDomImplementation::QDomImplementation() { - impl = 0; + impl = nullptr; } /*! @@ -1036,7 +1036,7 @@ QDomDocumentType QDomImplementation::createDocumentType(const QString& qName, co if (!ok) return QDomDocumentType(); - QDomDocumentTypePrivate *dt = new QDomDocumentTypePrivate(0); + QDomDocumentTypePrivate *dt = new QDomDocumentTypePrivate(nullptr); dt->name = fixedName; if (systemId.isNull()) { dt->publicId.clear(); @@ -1070,7 +1070,7 @@ QDomDocument QDomImplementation::createDocument(const QString& nsURI, const QStr */ bool QDomImplementation::isNull() { - return (impl == 0); + return (impl == nullptr); } /*! @@ -1244,14 +1244,14 @@ void QDomNodeListPrivate::createList() QDomNodePrivate* QDomNodeListPrivate::item(int index) { if (!node_impl) - return 0; + return nullptr; const QDomDocumentPrivate *const doc = node_impl->ownerDocument(); if (!doc || timestamp != doc->nodeListTime) createList(); if (index >= list.size()) - return 0; + return nullptr; return list.at(index); } @@ -1305,13 +1305,13 @@ int QDomNodeListPrivate::length() const Creates an empty node list. */ QDomNodeList::QDomNodeList() + : impl(nullptr) { - impl = 0; } QDomNodeList::QDomNodeList(QDomNodeListPrivate* p) + : impl(p) { - impl = p; } /*! @@ -1443,10 +1443,10 @@ QDomNodePrivate::QDomNodePrivate(QDomDocumentPrivate *doc, QDomNodePrivate *par) setParent(par); else setOwnerDocument(doc); - prev = 0; - next = 0; - first = 0; - last = 0; + prev = nullptr; + next = nullptr; + first = nullptr; + last = nullptr; createdWithDom1Interface = true; lineNumber = -1; columnNumber = -1; @@ -1455,10 +1455,10 @@ QDomNodePrivate::QDomNodePrivate(QDomDocumentPrivate *doc, QDomNodePrivate *par) QDomNodePrivate::QDomNodePrivate(QDomNodePrivate *n, bool deep) : ref(1) { setOwnerDocument(n->ownerDocument()); - prev = 0; - next = 0; - first = 0; - last = 0; + prev = nullptr; + next = nullptr; + first = nullptr; + last = nullptr; name = n->name; value = n->value; @@ -1488,8 +1488,8 @@ QDomNodePrivate::~QDomNodePrivate() p->setNoParent(); p = n; } - first = 0; - last = 0; + first = nullptr; + last = nullptr; } void QDomNodePrivate::clear() @@ -1503,8 +1503,8 @@ void QDomNodePrivate::clear() delete p; p = n; } - first = 0; - last = 0; + first = nullptr; + last = nullptr; } QDomNodePrivate* QDomNodePrivate::namedItem(const QString &n) @@ -1515,7 +1515,7 @@ QDomNodePrivate* QDomNodePrivate::namedItem(const QString &n) return p; p = p->next; } - return 0; + return nullptr; } @@ -1523,15 +1523,15 @@ QDomNodePrivate* QDomNodePrivate::insertBefore(QDomNodePrivate* newChild, QDomNo { // Error check if (!newChild) - return 0; + return nullptr; // Error check if (newChild == refChild) - return 0; + return nullptr; // Error check if (refChild && refChild->parent() != this) - return 0; + return nullptr; // "mark lists as dirty" QDomDocumentPrivate *const doc = ownerDocument(); @@ -1542,7 +1542,7 @@ QDomNodePrivate* QDomNodePrivate::insertBefore(QDomNodePrivate* newChild, QDomNo // all elements of the fragment instead of the fragment itself. if (newChild->isDocumentFragment()) { // Fragment is empty ? - if (newChild->first == 0) + if (newChild->first == nullptr) return newChild; // New parent @@ -1553,7 +1553,7 @@ QDomNodePrivate* QDomNodePrivate::insertBefore(QDomNodePrivate* newChild, QDomNo } // Insert at the beginning ? - if (!refChild || refChild->prev == 0) { + if (!refChild || refChild->prev == nullptr) { if (first) first->prev = newChild->last; newChild->last->next = first; @@ -1572,8 +1572,8 @@ QDomNodePrivate* QDomNodePrivate::insertBefore(QDomNodePrivate* newChild, QDomNo // does not decrease the reference. // Remove the nodes from the fragment - newChild->first = 0; - newChild->last = 0; + newChild->first = nullptr; + newChild->last = nullptr; return newChild; } @@ -1596,7 +1596,7 @@ QDomNodePrivate* QDomNodePrivate::insertBefore(QDomNodePrivate* newChild, QDomNo return newChild; } - if (refChild->prev == 0) { + if (refChild->prev == nullptr) { if (first) first->prev = newChild; newChild->next = first; @@ -1618,15 +1618,15 @@ QDomNodePrivate* QDomNodePrivate::insertAfter(QDomNodePrivate* newChild, QDomNod { // Error check if (!newChild) - return 0; + return nullptr; // Error check if (newChild == refChild) - return 0; + return nullptr; // Error check if (refChild && refChild->parent() != this) - return 0; + return nullptr; // "mark lists as dirty" QDomDocumentPrivate *const doc = ownerDocument(); @@ -1637,7 +1637,7 @@ QDomNodePrivate* QDomNodePrivate::insertAfter(QDomNodePrivate* newChild, QDomNod // all elements of the fragment instead of the fragment itself. if (newChild->isDocumentFragment()) { // Fragment is empty ? - if (newChild->first == 0) + if (newChild->first == nullptr) return newChild; // New parent @@ -1648,7 +1648,7 @@ QDomNodePrivate* QDomNodePrivate::insertAfter(QDomNodePrivate* newChild, QDomNod } // Insert at the end - if (!refChild || refChild->next == 0) { + if (!refChild || refChild->next == nullptr) { if (last) last->next = newChild->first; newChild->first->prev = last; @@ -1666,8 +1666,8 @@ QDomNodePrivate* QDomNodePrivate::insertAfter(QDomNodePrivate* newChild, QDomNod // does not decrease the reference. // Remove the nodes from the fragment - newChild->first = 0; - newChild->last = 0; + newChild->first = nullptr; + newChild->last = nullptr; return newChild; } @@ -1692,7 +1692,7 @@ QDomNodePrivate* QDomNodePrivate::insertAfter(QDomNodePrivate* newChild, QDomNod return newChild; } - if (refChild->next == 0) { + if (refChild->next == nullptr) { if (last) last->next = newChild; newChild->prev = last; @@ -1713,11 +1713,11 @@ QDomNodePrivate* QDomNodePrivate::insertAfter(QDomNodePrivate* newChild, QDomNod QDomNodePrivate* QDomNodePrivate::replaceChild(QDomNodePrivate* newChild, QDomNodePrivate* oldChild) { if (!newChild || !oldChild) - return 0; + return nullptr; if (oldChild->parent() != this) - return 0; + return nullptr; if (newChild == oldChild) - return 0; + return nullptr; // mark lists as dirty QDomDocumentPrivate *const doc = ownerDocument(); @@ -1728,7 +1728,7 @@ QDomNodePrivate* QDomNodePrivate::replaceChild(QDomNodePrivate* newChild, QDomNo // all elements of the fragment instead of the fragment itself. if (newChild->isDocumentFragment()) { // Fragment is empty ? - if (newChild->first == 0) + if (newChild->first == nullptr) return newChild; // New parent @@ -1753,15 +1753,15 @@ QDomNodePrivate* QDomNodePrivate::replaceChild(QDomNodePrivate* newChild, QDomNo last = newChild->last; oldChild->setNoParent(); - oldChild->next = 0; - oldChild->prev = 0; + oldChild->next = nullptr; + oldChild->prev = nullptr; // No need to increase the reference since QDomDocumentFragment // does not decrease the reference. // Remove the nodes from the fragment - newChild->first = 0; - newChild->last = 0; + newChild->first = nullptr; + newChild->last = nullptr; // We are no longer interested in the old node if (oldChild) @@ -1794,8 +1794,8 @@ QDomNodePrivate* QDomNodePrivate::replaceChild(QDomNodePrivate* newChild, QDomNo last = newChild; oldChild->setNoParent(); - oldChild->next = 0; - oldChild->prev = 0; + oldChild->next = nullptr; + oldChild->prev = nullptr; // We are no longer interested in the old node if (oldChild) @@ -1808,7 +1808,7 @@ QDomNodePrivate* QDomNodePrivate::removeChild(QDomNodePrivate* oldChild) { // Error check if (oldChild->parent() != this) - return 0; + return nullptr; // "mark lists as dirty" QDomDocumentPrivate *const doc = ownerDocument(); @@ -1817,8 +1817,8 @@ QDomNodePrivate* QDomNodePrivate::removeChild(QDomNodePrivate* oldChild) // Perhaps oldChild was just created with "createElement" or that. In this case // its parent is QDomDocument but it is not part of the documents child list. - if (oldChild->next == 0 && oldChild->prev == 0 && first != oldChild) - return 0; + if (oldChild->next == nullptr && oldChild->prev == nullptr && first != oldChild) + return nullptr; if (oldChild->next) oldChild->next->prev = oldChild->prev; @@ -1831,8 +1831,8 @@ QDomNodePrivate* QDomNodePrivate::removeChild(QDomNodePrivate* oldChild) first = oldChild->next; oldChild->setNoParent(); - oldChild->next = 0; - oldChild->prev = 0; + oldChild->next = nullptr; + oldChild->prev = nullptr; // We are no longer interested in the old node oldChild->ref.deref(); @@ -1843,7 +1843,7 @@ QDomNodePrivate* QDomNodePrivate::removeChild(QDomNodePrivate* oldChild) QDomNodePrivate* QDomNodePrivate::appendChild(QDomNodePrivate* newChild) { // No reference manipulation needed. Done in insertAfter. - return insertAfter(newChild, 0); + return insertAfter(newChild, nullptr); } QDomDocumentPrivate* QDomNodePrivate::ownerDocument() @@ -1869,7 +1869,7 @@ QDomNodePrivate* QDomNodePrivate::cloneNode(bool deep) static void qNormalizeNode(QDomNodePrivate* n) { QDomNodePrivate* p = n->first; - QDomTextPrivate* t = 0; + QDomTextPrivate* t = nullptr; while (p) { if (p->isText()) { @@ -1884,7 +1884,7 @@ static void qNormalizeNode(QDomNodePrivate* n) } } else { p = p->next; - t = 0; + t = nullptr; } } } @@ -2009,8 +2009,8 @@ void QDomNodePrivate::setLocation(int lineNumber, int columnNumber) Constructs a \l{isNull()}{null} node. */ QDomNode::QDomNode() + : impl(nullptr) { - impl = 0; } /*! @@ -2619,7 +2619,7 @@ bool QDomNode::hasChildNodes() const { if (!impl) return false; - return IMPL->first != 0; + return IMPL->first != nullptr; } /*! @@ -2628,7 +2628,7 @@ bool QDomNode::hasChildNodes() const */ bool QDomNode::isNull() const { - return (impl == 0); + return (impl == nullptr); } /*! @@ -2641,7 +2641,7 @@ void QDomNode::clear() { if (impl && !impl->ref.deref()) delete impl; - impl = 0; + impl = nullptr; } /*! @@ -3094,13 +3094,13 @@ QDomNodePrivate* QDomNamedNodeMapPrivate::namedItemNS(const QString& nsURI, cons return n; } } - return 0; + return nullptr; } QDomNodePrivate* QDomNamedNodeMapPrivate::setNamedItem(QDomNodePrivate* arg) { if (readonly || !arg) - return 0; + return nullptr; if (appendToParent) return parent->appendChild(arg); @@ -3115,7 +3115,7 @@ QDomNodePrivate* QDomNamedNodeMapPrivate::setNamedItem(QDomNodePrivate* arg) QDomNodePrivate* QDomNamedNodeMapPrivate::setNamedItemNS(QDomNodePrivate* arg) { if (readonly || !arg) - return 0; + return nullptr; if (appendToParent) return parent->appendChild(arg); @@ -3136,11 +3136,11 @@ QDomNodePrivate* QDomNamedNodeMapPrivate::setNamedItemNS(QDomNodePrivate* arg) QDomNodePrivate* QDomNamedNodeMapPrivate::removeNamedItem(const QString& name) { if (readonly) - return 0; + return nullptr; QDomNodePrivate* p = namedItem(name); - if (p == 0) - return 0; + if (p == nullptr) + return nullptr; if (appendToParent) return parent->removeChild(p); @@ -3153,7 +3153,7 @@ QDomNodePrivate* QDomNamedNodeMapPrivate::removeNamedItem(const QString& name) QDomNodePrivate* QDomNamedNodeMapPrivate::item(int index) const { if (index >= length() || index < 0) - return 0; + return nullptr; return *(map.constBegin() + index); } @@ -3164,12 +3164,12 @@ int QDomNamedNodeMapPrivate::length() const bool QDomNamedNodeMapPrivate::contains(const QString& name) const { - return map.value(name) != 0; + return map.contains(name); } bool QDomNamedNodeMapPrivate::containsNS(const QString& nsURI, const QString & localName) const { - return namedItemNS(nsURI, localName) != 0; + return namedItemNS(nsURI, localName) != nullptr; } /************************************************************** @@ -3222,8 +3222,8 @@ bool QDomNamedNodeMapPrivate::containsNS(const QString& nsURI, const QString & l Constructs an empty named node map. */ QDomNamedNodeMap::QDomNamedNodeMap() + : impl(nullptr) { - impl = 0; } /*! @@ -3570,7 +3570,7 @@ QDomNodePrivate* QDomDocumentTypePrivate::removeChild(QDomNodePrivate* oldChild) QDomNodePrivate* QDomDocumentTypePrivate::appendChild(QDomNodePrivate* newChild) { - return insertAfter(newChild, 0); + return insertAfter(newChild, nullptr); } static QString quotedValue(const QString &data) @@ -4115,7 +4115,7 @@ QDomAttrPrivate::QDomAttrPrivate(QDomAttrPrivate* n, bool deep) void QDomAttrPrivate::setNodeValue(const QString& v) { value = v; - QDomTextPrivate *t = new QDomTextPrivate(0, this, v); + QDomTextPrivate *t = new QDomTextPrivate(nullptr, this, v); // keep the refcount balanced: appendChild() does a ref anyway. t->ref.deref(); if (first) { @@ -4517,7 +4517,7 @@ QDomAttrPrivate* QDomElementPrivate::setAttributeNode(QDomAttrPrivate* newAttr) QDomAttrPrivate* QDomElementPrivate::setAttributeNodeNS(QDomAttrPrivate* newAttr) { - QDomNodePrivate* n = 0; + QDomNodePrivate* n = nullptr; if (!newAttr->prefix.isNull()) n = m_attr->namedItemNS(newAttr->namespaceURI, newAttr->name); @@ -5184,10 +5184,10 @@ QDomTextPrivate* QDomTextPrivate::splitText(int offset) { if (!parent()) { qWarning("QDomText::splitText The node has no parent. So I cannot split"); - return 0; + return nullptr; } - QDomTextPrivate* t = new QDomTextPrivate(ownerDocument(), 0, value.mid(offset)); + QDomTextPrivate* t = new QDomTextPrivate(ownerDocument(), nullptr, value.mid(offset)); value.truncate(offset); parent()->insertAfter(t, this); @@ -6144,7 +6144,7 @@ void QDomProcessingInstruction::setData(const QString& d) **************************************************************/ QDomDocumentPrivate::QDomDocumentPrivate() - : QDomNodePrivate(0), + : QDomNodePrivate(nullptr), impl(new QDomImplementationPrivate), nodeListTime(1) { @@ -6155,7 +6155,7 @@ QDomDocumentPrivate::QDomDocumentPrivate() } QDomDocumentPrivate::QDomDocumentPrivate(const QString& aname) - : QDomNodePrivate(0), + : QDomNodePrivate(nullptr), impl(new QDomImplementationPrivate), nodeListTime(1) { @@ -6167,11 +6167,11 @@ QDomDocumentPrivate::QDomDocumentPrivate(const QString& aname) } QDomDocumentPrivate::QDomDocumentPrivate(QDomDocumentTypePrivate* dt) - : QDomNodePrivate(0), + : QDomNodePrivate(nullptr), impl(new QDomImplementationPrivate), nodeListTime(1) { - if (dt != 0) { + if (dt != nullptr) { type = dt; } else { type = new QDomDocumentTypePrivate(this, this); @@ -6267,9 +6267,9 @@ QDomElementPrivate* QDomDocumentPrivate::createElement(const QString &tagName) bool ok; QString fixedName = fixedXmlName(tagName, &ok); if (!ok) - return 0; + return nullptr; - QDomElementPrivate *e = new QDomElementPrivate(this, 0, fixedName); + QDomElementPrivate *e = new QDomElementPrivate(this, nullptr, fixedName); e->ref.deref(); return e; } @@ -6279,16 +6279,16 @@ QDomElementPrivate* QDomDocumentPrivate::createElementNS(const QString &nsURI, c bool ok; QString fixedName = fixedXmlName(qName, &ok, true); if (!ok) - return 0; + return nullptr; - QDomElementPrivate *e = new QDomElementPrivate(this, 0, nsURI, fixedName); + QDomElementPrivate *e = new QDomElementPrivate(this, nullptr, nsURI, fixedName); e->ref.deref(); return e; } QDomDocumentFragmentPrivate* QDomDocumentPrivate::createDocumentFragment() { - QDomDocumentFragmentPrivate *f = new QDomDocumentFragmentPrivate(this, (QDomNodePrivate*)0); + QDomDocumentFragmentPrivate *f = new QDomDocumentFragmentPrivate(this, (QDomNodePrivate*)nullptr); f->ref.deref(); return f; } @@ -6298,9 +6298,9 @@ QDomTextPrivate* QDomDocumentPrivate::createTextNode(const QString &data) bool ok; QString fixedData = fixedCharData(data, &ok); if (!ok) - return 0; + return nullptr; - QDomTextPrivate *t = new QDomTextPrivate(this, 0, fixedData); + QDomTextPrivate *t = new QDomTextPrivate(this, nullptr, fixedData); t->ref.deref(); return t; } @@ -6310,9 +6310,9 @@ QDomCommentPrivate* QDomDocumentPrivate::createComment(const QString &data) bool ok; QString fixedData = fixedComment(data, &ok); if (!ok) - return 0; + return nullptr; - QDomCommentPrivate *c = new QDomCommentPrivate(this, 0, fixedData); + QDomCommentPrivate *c = new QDomCommentPrivate(this, nullptr, fixedData); c->ref.deref(); return c; } @@ -6322,9 +6322,9 @@ QDomCDATASectionPrivate* QDomDocumentPrivate::createCDATASection(const QString & bool ok; QString fixedData = fixedCDataSection(data, &ok); if (!ok) - return 0; + return nullptr; - QDomCDATASectionPrivate *c = new QDomCDATASectionPrivate(this, 0, fixedData); + QDomCDATASectionPrivate *c = new QDomCDATASectionPrivate(this, nullptr, fixedData); c->ref.deref(); return c; } @@ -6335,13 +6335,13 @@ QDomProcessingInstructionPrivate* QDomDocumentPrivate::createProcessingInstructi bool ok; QString fixedData = fixedPIData(data, &ok); if (!ok) - return 0; + return nullptr; // [17] PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' | 'l')) QString fixedTarget = fixedXmlName(target, &ok); if (!ok) - return 0; + return nullptr; - QDomProcessingInstructionPrivate *p = new QDomProcessingInstructionPrivate(this, 0, fixedTarget, fixedData); + QDomProcessingInstructionPrivate *p = new QDomProcessingInstructionPrivate(this, nullptr, fixedTarget, fixedData); p->ref.deref(); return p; } @@ -6350,9 +6350,9 @@ QDomAttrPrivate* QDomDocumentPrivate::createAttribute(const QString &aname) bool ok; QString fixedName = fixedXmlName(aname, &ok); if (!ok) - return 0; + return nullptr; - QDomAttrPrivate *a = new QDomAttrPrivate(this, 0, fixedName); + QDomAttrPrivate *a = new QDomAttrPrivate(this, nullptr, fixedName); a->ref.deref(); return a; } @@ -6362,9 +6362,9 @@ QDomAttrPrivate* QDomDocumentPrivate::createAttributeNS(const QString &nsURI, co bool ok; QString fixedName = fixedXmlName(qName, &ok, true); if (!ok) - return 0; + return nullptr; - QDomAttrPrivate *a = new QDomAttrPrivate(this, 0, nsURI, fixedName); + QDomAttrPrivate *a = new QDomAttrPrivate(this, nullptr, nsURI, fixedName); a->ref.deref(); return a; } @@ -6374,16 +6374,16 @@ QDomEntityReferencePrivate* QDomDocumentPrivate::createEntityReference(const QSt bool ok; QString fixedName = fixedXmlName(aname, &ok); if (!ok) - return 0; + return nullptr; - QDomEntityReferencePrivate *e = new QDomEntityReferencePrivate(this, 0, fixedName); + QDomEntityReferencePrivate *e = new QDomEntityReferencePrivate(this, nullptr, fixedName); e->ref.deref(); return e; } QDomNodePrivate* QDomDocumentPrivate::importNode(QDomNodePrivate *importedNode, bool deep) { - QDomNodePrivate *node = 0; + QDomNodePrivate *node = nullptr; switch (importedNode->nodeType()) { case QDomNode::AttributeNode: node = new QDomAttrPrivate((QDomAttrPrivate*)importedNode, true); @@ -6435,7 +6435,7 @@ void QDomDocumentPrivate::saveDocument(QTextStream& s, const int indent, QDomNod #if QT_CONFIG(textcodec) && QT_CONFIG(regularexpression) const QDomNodePrivate* n = first; - QTextCodec *codec = 0; + QTextCodec *codec = nullptr; if (n && n->isProcessingInstruction() && n->nodeName() == QLatin1String("xml")) { // we have an XML declaration @@ -6593,7 +6593,7 @@ void QDomDocumentPrivate::saveDocument(QTextStream& s, const int indent, QDomNod */ QDomDocument::QDomDocument() { - impl = 0; + impl = nullptr; } /*! @@ -6822,7 +6822,7 @@ bool QDomDocument::setContent(QXmlInputSource *source, QXmlReader *reader, QStri { if (!impl) impl = new QDomDocumentPrivate(); - return IMPL->setContent(source, reader, 0, errorMsg, errorLine, errorColumn); + return IMPL->setContent(source, reader, nullptr, errorMsg, errorLine, errorColumn); } /*! @@ -7369,7 +7369,7 @@ QDomComment QDomNode::toComment() const QDomHandler::QDomHandler(QDomDocumentPrivate* adoc, QXmlSimpleReader* areader, bool namespaceProcessing) : errorLine(0), errorColumn(0), doc(adoc), node(adoc), cdata(false), - nsProcessing(namespaceProcessing), locator(0), reader(areader) + nsProcessing(namespaceProcessing), locator(nullptr), reader(areader) { } @@ -7443,7 +7443,7 @@ bool QDomHandler::characters(const QString& ch) if (cdata) { n.reset(doc->createCDATASection(ch)); } else if (!entityName.isEmpty()) { - QScopedPointer e(new QDomEntityPrivate(doc, 0, entityName, + QScopedPointer e(new QDomEntityPrivate(doc, nullptr, entityName, QString(), QString(), QString())); e->value = ch; e->ref.deref(); @@ -7528,7 +7528,7 @@ bool QDomHandler::comment(const QString& ch) bool QDomHandler::unparsedEntityDecl(const QString &name, const QString &publicId, const QString &systemId, const QString ¬ationName) { - QDomEntityPrivate* e = new QDomEntityPrivate(doc, 0, name, + QDomEntityPrivate* e = new QDomEntityPrivate(doc, nullptr, name, publicId, systemId, notationName); // keep the refcount balanced: appendChild() does a ref anyway. e->ref.deref(); @@ -7543,7 +7543,7 @@ bool QDomHandler::externalEntityDecl(const QString &name, const QString &publicI bool QDomHandler::notationDecl(const QString & name, const QString & publicId, const QString & systemId) { - QDomNotationPrivate* n = new QDomNotationPrivate(doc, 0, name, publicId, systemId); + QDomNotationPrivate* n = new QDomNotationPrivate(doc, nullptr, name, publicId, systemId); // keep the refcount balanced: appendChild() does a ref anyway. n->ref.deref(); doc->doctype()->appendChild(n); diff --git a/src/xml/sax/qxml.cpp b/src/xml/sax/qxml.cpp index b2fff5b61f..1c45118fb1 100644 --- a/src/xml/sax/qxml.cpp +++ b/src/xml/sax/qxml.cpp @@ -1079,12 +1079,12 @@ void QXmlInputSource::init() d = new QXmlInputSourcePrivate; QT_TRY { - d->inputDevice = 0; - d->inputStream = 0; + d->inputDevice = nullptr; + d->inputStream = nullptr; setData(QString()); #if QT_CONFIG(textcodec) - d->encMapper = 0; + d->encMapper = nullptr; #endif d->nextReturnedEndOfData = true; // first call to next() will call fetchData() @@ -1357,13 +1357,13 @@ QString QXmlInputSource::fromRawData(const QByteArray &data, bool beginning) return QString(); if (beginning) { delete d->encMapper; - d->encMapper = 0; + d->encMapper = nullptr; } int mib = 106; // UTF-8 // This is the initial UTF codec we will read the encoding declaration with - if (d->encMapper == 0) { + if (d->encMapper == nullptr) { d->encodingDeclBytes.clear(); d->encodingDeclChars.clear(); d->lookingForEncodingDecl = true; @@ -2377,7 +2377,7 @@ bool QXmlDefaultHandler::unparsedEntityDecl(const QString&, const QString&, bool QXmlDefaultHandler::resolveEntity(const QString&, const QString&, QXmlInputSource*& ret) { - ret = 0; + ret = nullptr; return true; } @@ -2520,15 +2520,15 @@ inline void QXmlSimpleReaderPrivate::refClear() QXmlSimpleReaderPrivate::QXmlSimpleReaderPrivate(QXmlSimpleReader *reader) { q_ptr = reader; - parseStack = 0; + parseStack = nullptr; locator.reset(new QXmlSimpleReaderLocator(reader)); - entityRes = 0; - dtdHnd = 0; - contentHnd = 0; - errorHnd = 0; - lexicalHnd = 0; - declHnd = 0; + entityRes = nullptr; + dtdHnd = nullptr; + contentHnd = nullptr; + errorHnd = nullptr; + lexicalHnd = nullptr; + declHnd = nullptr; // default feature settings useNamespaces = true; @@ -2932,7 +2932,7 @@ bool QXmlSimpleReader::feature(const QString& name, bool *ok) const { const QXmlSimpleReaderPrivate *d = d_func(); - if (ok != 0) + if (ok) *ok = true; if (name == QLatin1String("http://xml.org/sax/features/namespaces")) { return d->useNamespaces; @@ -2946,7 +2946,7 @@ bool QXmlSimpleReader::feature(const QString& name, bool *ok) const return d->reportEntities; } else { qWarning("Unknown feature %s", name.toLatin1().data()); - if (ok != 0) + if (ok) *ok = false; } return false; @@ -3023,9 +3023,9 @@ bool QXmlSimpleReader::hasFeature(const QString& name) const */ void* QXmlSimpleReader::property(const QString&, bool *ok) const { - if (ok != 0) + if (ok) *ok = false; - return 0; + return nullptr; } /*! \reimp @@ -3206,7 +3206,7 @@ bool QXmlSimpleReader::parse(const QXmlInputSource *input, bool incremental) d->initIncrementalParsing(); } else { delete d->parseStack; - d->parseStack = 0; + d->parseStack = nullptr; } d->init(input); @@ -3251,7 +3251,7 @@ bool QXmlSimpleReader::parse(const QXmlInputSource *input, bool incremental) bool QXmlSimpleReader::parseContinue() { Q_D(QXmlSimpleReader); - if (d->parseStack == 0 || d->parseStack->isEmpty()) + if (d->parseStack == nullptr || d->parseStack->isEmpty()) return false; d->initData(); int state = d->parseStack->pop().state; @@ -3268,7 +3268,7 @@ bool QXmlSimpleReaderPrivate::parseBeginOrContinue(int state, bool incremental) if (state==0) { if (!parseProlog()) { if (incremental && error.isNull()) { - pushParseState(0, 0); + pushParseState(nullptr, 0); return true; } else { clear(tags); @@ -3280,7 +3280,7 @@ bool QXmlSimpleReaderPrivate::parseBeginOrContinue(int state, bool incremental) if (state==1) { if (!parseElement()) { if (incremental && error.isNull()) { - pushParseState(0, 1); + pushParseState(nullptr, 1); return true; } else { clear(tags); @@ -3293,7 +3293,7 @@ bool QXmlSimpleReaderPrivate::parseBeginOrContinue(int state, bool incremental) while (!atEnd()) { if (!parseMisc()) { if (incremental && error.isNull()) { - pushParseState(0, 2); + pushParseState(nullptr, 2); return true; } else { clear(tags); @@ -3303,7 +3303,7 @@ bool QXmlSimpleReaderPrivate::parseBeginOrContinue(int state, bool incremental) } if (!atEndOrig && incremental) { // we parsed something at all, so be prepared to come back later - pushParseState(0, 2); + pushParseState(nullptr, 2); return true; } // is stack empty? @@ -3315,7 +3315,7 @@ bool QXmlSimpleReaderPrivate::parseBeginOrContinue(int state, bool incremental) // call the handler if (contentHnd) { delete parseStack; - parseStack = 0; + parseStack = nullptr; if (!contentHnd->endDocument()) { reportParseError(contentHnd->errorString()); return false; @@ -3350,7 +3350,7 @@ bool QXmlSimpleReaderPrivate::parseBeginOrContinue(int state, bool incremental) signed char state; signed char input; -(4) if (d->parseStack == 0 || d->parseStack->isEmpty()) { +(4) if (d->parseStack == nullptr || d->parseStack->isEmpty()) { (4a) ... } else { (4b) ... @@ -3440,7 +3440,7 @@ bool QXmlSimpleReaderPrivate::parseProlog() signed char state; signed char input; - if (parseStack == 0 || parseStack->isEmpty()) { + if (parseStack == nullptr|| parseStack->isEmpty()) { xmldecl_possible = true; doctype_read = false; state = Init; @@ -3631,7 +3631,7 @@ bool QXmlSimpleReaderPrivate::parseElement() int state; int input; - if (parseStack == 0 || parseStack->isEmpty()) { + if (parseStack == nullptr|| parseStack->isEmpty()) { state = Init; } else { state = parseStack->pop().state; @@ -4000,7 +4000,7 @@ bool QXmlSimpleReaderPrivate::parseContent() signed char state; signed char input; - if (parseStack == 0 || parseStack->isEmpty()) { + if (parseStack == nullptr || parseStack->isEmpty()) { contentCharDataRead = false; state = Init; } else { @@ -4303,7 +4303,7 @@ bool QXmlSimpleReaderPrivate::parseMisc() signed char state; signed char input; - if (parseStack==0 || parseStack->isEmpty()) { + if (parseStack == nullptr || parseStack->isEmpty()) { state = Init; } else { state = parseStack->pop().state; @@ -4458,7 +4458,7 @@ bool QXmlSimpleReaderPrivate::parsePI() signed char state; signed char input; - if (parseStack==0 || parseStack->isEmpty()) { + if (parseStack == nullptr || parseStack->isEmpty()) { state = Init; } else { state = parseStack->pop().state; @@ -4685,7 +4685,7 @@ bool QXmlSimpleReaderPrivate::parseDoctype() signed char state; signed char input; - if (parseStack==0 || parseStack->isEmpty()) { + if (parseStack == nullptr || parseStack->isEmpty()) { startDTDwasReported = false; systemId.clear(); publicId.clear(); @@ -4896,7 +4896,7 @@ bool QXmlSimpleReaderPrivate::parseExternalID() signed char state; signed char input; - if (parseStack==0 || parseStack->isEmpty()) { + if (parseStack == nullptr || parseStack->isEmpty()) { systemId.clear(); publicId.clear(); state = Init; @@ -5060,7 +5060,7 @@ bool QXmlSimpleReaderPrivate::parseMarkupdecl() signed char state; signed char input; - if (parseStack==0 || parseStack->isEmpty()) { + if (parseStack == nullptr || parseStack->isEmpty()) { state = Init; } else { state = parseStack->pop().state; @@ -5218,7 +5218,7 @@ bool QXmlSimpleReaderPrivate::parsePEReference() signed char state; signed char input; - if (parseStack==0 || parseStack->isEmpty()) { + if (parseStack == nullptr || parseStack->isEmpty()) { state = Init; } else { state = parseStack->pop().state; @@ -5255,7 +5255,7 @@ bool QXmlSimpleReaderPrivate::parsePEReference() } else if (entityRes) { QMap::Iterator it2; it2 = externParameterEntities.find(ref()); - QXmlInputSource *ret = 0; + QXmlInputSource *ret = nullptr; if (it2 != externParameterEntities.end()) { if (!entityRes->resolveEntity((*it2).publicId, (*it2).systemId, ret)) { delete ret; @@ -5396,7 +5396,7 @@ bool QXmlSimpleReaderPrivate::parseAttlistDecl() signed char state; signed char input; - if (parseStack==0 || parseStack->isEmpty()) { + if (parseStack == nullptr || parseStack->isEmpty()) { state = Init; } else { state = parseStack->pop().state; @@ -5612,7 +5612,7 @@ bool QXmlSimpleReaderPrivate::parseAttType() signed char state; signed char input; - if (parseStack==0 || parseStack->isEmpty()) { + if (parseStack == nullptr || parseStack->isEmpty()) { state = Init; } else { state = parseStack->pop().state; @@ -5833,7 +5833,7 @@ bool QXmlSimpleReaderPrivate::parseAttValue() signed char state; signed char input; - if (parseStack==0 || parseStack->isEmpty()) { + if (parseStack == nullptr || parseStack->isEmpty()) { state = Init; } else { state = parseStack->pop().state; @@ -5975,7 +5975,7 @@ bool QXmlSimpleReaderPrivate::parseElementDecl() signed char state; signed char input; - if (parseStack==0 || parseStack->isEmpty()) { + if (parseStack == nullptr || parseStack->isEmpty()) { state = Init; } else { state = parseStack->pop().state; @@ -6184,7 +6184,7 @@ bool QXmlSimpleReaderPrivate::parseNotationDecl() signed char state; signed char input; - if (parseStack==0 || parseStack->isEmpty()) { + if (parseStack == nullptr || parseStack->isEmpty()) { state = Init; } else { state = parseStack->pop().state; @@ -6328,7 +6328,7 @@ bool QXmlSimpleReaderPrivate::parseChoiceSeq() signed char state; signed char input; - if (parseStack==0 || parseStack->isEmpty()) { + if (parseStack == nullptr || parseStack->isEmpty()) { state = Init; } else { state = parseStack->pop().state; @@ -6557,7 +6557,7 @@ bool QXmlSimpleReaderPrivate::parseEntityDecl() signed char state; signed char input; - if (parseStack==0 || parseStack->isEmpty()) { + if (parseStack == nullptr || parseStack->isEmpty()) { state = Init; } else { state = parseStack->pop().state; @@ -6832,7 +6832,7 @@ bool QXmlSimpleReaderPrivate::parseEntityValue() signed char state; signed char input; - if (parseStack==0 || parseStack->isEmpty()) { + if (parseStack == nullptr || parseStack->isEmpty()) { state = Init; } else { state = parseStack->pop().state; @@ -6951,7 +6951,7 @@ bool QXmlSimpleReaderPrivate::parseComment() signed char state; signed char input; - if (parseStack==0 || parseStack->isEmpty()) { + if (parseStack == nullptr || parseStack->isEmpty()) { state = Init; } else { state = parseStack->pop().state; @@ -7063,7 +7063,7 @@ bool QXmlSimpleReaderPrivate::parseAttribute() int state; int input; - if (parseStack==0 || parseStack->isEmpty()) { + if (parseStack == nullptr || parseStack->isEmpty()) { state = Init; } else { state = parseStack->pop().state; @@ -7162,7 +7162,7 @@ bool QXmlSimpleReaderPrivate::parseName() }; int state; - if (parseStack==0 || parseStack->isEmpty()) { + if (parseStack == nullptr || parseStack->isEmpty()) { state = Init; } else { state = parseStack->pop().state; @@ -7248,7 +7248,7 @@ bool QXmlSimpleReaderPrivate::parseNmtoken() signed char state; signed char input; - if (parseStack==0 || parseStack->isEmpty()) { + if (parseStack == nullptr || parseStack->isEmpty()) { state = Init; } else { state = parseStack->pop().state; @@ -7356,7 +7356,7 @@ bool QXmlSimpleReaderPrivate::parseReference() signed char state; signed char input; - if (parseStack==0 || parseStack->isEmpty()) { + if (parseStack == nullptr || parseStack->isEmpty()) { parseReference_charDataRead = false; state = Init; } else { @@ -7582,7 +7582,7 @@ bool QXmlSimpleReaderPrivate::processReference() if (parseReference_context == InContent) { if (contentCharDataRead) { if (reportWhitespaceCharData || !string().simplified().isEmpty()) { - if (contentHnd != 0 && !contentHnd->characters(string())) { + if (contentHnd != nullptr && !contentHnd->characters(string())) { reportParseError(contentHnd->errorString()); return false; } @@ -7610,7 +7610,7 @@ bool QXmlSimpleReaderPrivate::processReference() // Included if validating bool skipIt = true; if (entityRes) { - QXmlInputSource *ret = 0; + QXmlInputSource *ret = nullptr; if (!entityRes->resolveEntity((*itExtern).publicId, (*itExtern).systemId, ret)) { delete ret; reportParseError(entityRes->errorString()); @@ -7696,7 +7696,7 @@ bool QXmlSimpleReaderPrivate::parseString() signed char state; // state in this function is the position in the string s signed char input; - if (parseStack==0 || parseStack->isEmpty()) { + if (parseStack == nullptr || parseStack->isEmpty()) { Done = parseString_s.length(); state = 0; } else { @@ -7800,7 +7800,7 @@ void QXmlSimpleReaderPrivate::next() c = inputSource->next(); // If we are not incremental parsing, we just skip over EndOfData chars to give the // parser an uninterrupted stream of document chars. - if (c == QXmlInputSource::EndOfData && parseStack == 0) + if (c == QXmlInputSource::EndOfData && parseStack == nullptr) c = inputSource->next(); if (uc == '\n') { lineNr++; @@ -7832,7 +7832,7 @@ bool QXmlSimpleReaderPrivate::eat_ws() } next(); } - if (parseStack != 0) { + if (parseStack != nullptr) { unexpectedEof(&QXmlSimpleReaderPrivate::eat_ws, 0); return false; } @@ -7922,7 +7922,7 @@ void QXmlSimpleReaderPrivate::reportParseError(const QString& error) */ void QXmlSimpleReaderPrivate::unexpectedEof(ParseFunction where, int state) { - if (parseStack == 0) { + if (parseStack == nullptr) { reportParseError(QLatin1String(XMLERR_UNEXPECTEDEOF)); } else { if (c == QXmlInputSource::EndOfDocument) { @@ -7942,7 +7942,7 @@ void QXmlSimpleReaderPrivate::unexpectedEof(ParseFunction where, int state) */ void QXmlSimpleReaderPrivate::parseFailed(ParseFunction where, int state) { - if (parseStack!=0 && error.isNull()) { + if (parseStack != nullptr && error.isNull()) { pushParseState(where, state); } } -- cgit v1.2.3 From 6f03867d0288d7a180c2ed775df1f9f922561848 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Wed, 15 May 2019 15:58:03 +0200 Subject: Rearrange date parsing in anticipation of calendar work The calendar APIs shall need fromShortMonthName() to know its year number; so rearrange the date parsing that uses it to ensure the year number is known in time. In the process, pass &ok to toInt() also for the calls that get a day number (where failure's 0 return is an adequate check for failed parse), just to be on the safe side. Change-Id: Id09c40da9f7e70e68be440e9805a3d30a80977c6 Reviewed-by: Thiago Macieira --- src/corelib/time/qdatetime.cpp | 63 +++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 34 deletions(-) (limited to 'src') diff --git a/src/corelib/time/qdatetime.cpp b/src/corelib/time/qdatetime.cpp index 92b243f60e..74b7966fa7 100644 --- a/src/corelib/time/qdatetime.cpp +++ b/src/corelib/time/qdatetime.cpp @@ -1516,19 +1516,17 @@ QDate QDate::fromString(const QString &string, Qt::DateFormat format) if (parts.count() != 4) return QDate(); - QStringRef monthName = parts.at(1); - const int month = fromShortMonthName(monthName); - if (month == -1) { - // Month name matches neither English nor other localised name. - return QDate(); - } - bool ok = false; int year = parts.at(3).toInt(&ok); - if (!ok) + int day = ok ? parts.at(2).toInt(&ok) : 0; + if (!ok || !day) return QDate(); - return QDate(year, month, parts.at(2).toInt()); + const int month = fromShortMonthName(parts.at(1)); + if (month == -1) // Month name matches no English or localised name. + return QDate(); + + return QDate(year, month, day); } #endif // textdate case Qt::ISODate: { @@ -5098,48 +5096,45 @@ QDateTime QDateTime::fromString(const QString &string, Qt::DateFormat format) return QDateTime(); // Accept "Sun Dec 1 13:02:00 1974" and "Sun 1. Dec 13:02:00 1974" + + // Year and time can be in either order. + // Guess which by looking for ':' in the time + int yearPart = 3; + int timePart = 3; + if (parts.at(3).contains(QLatin1Char(':'))) + yearPart = 4; + else if (parts.at(4).contains(QLatin1Char(':'))) + timePart = 4; + else + return QDateTime(); + int month = 0; int day = 0; bool ok = false; - // First try month then day + int year = parts.at(yearPart).toInt(&ok); + if (!ok || year == 0) + return QDateTime(); + + // Next try month then day month = fromShortMonthName(parts.at(1)); if (month) - day = parts.at(2).toInt(); + day = parts.at(2).toInt(&ok); - // If failed try day then month - if (!month || !day) { + // If failed, try day then month + if (!ok || !month || !day) { month = fromShortMonthName(parts.at(2)); if (month) { QStringRef dayStr = parts.at(1); if (dayStr.endsWith(QLatin1Char('.'))) { dayStr = dayStr.left(dayStr.size() - 1); - day = dayStr.toInt(); + day = dayStr.toInt(&ok); } } } // If both failed, give up - if (!month || !day) - return QDateTime(); - - // Year can be before or after time, "Sun Dec 1 1974 13:02:00" or "Sun Dec 1 13:02:00 1974" - // Guess which by looking for ':' in the time - int year = 0; - int yearPart = 0; - int timePart = 0; - if (parts.at(3).contains(QLatin1Char(':'))) { - yearPart = 4; - timePart = 3; - } else if (parts.at(4).contains(QLatin1Char(':'))) { - yearPart = 3; - timePart = 4; - } else { - return QDateTime(); - } - - year = parts.at(yearPart).toInt(&ok); - if (!ok) + if (!ok || !month || !day) return QDateTime(); QDate date(year, month, day); -- cgit v1.2.3 From efe1073e440fd7a179da8d19abc43fb9a745ac2f Mon Sep 17 00:00:00 2001 From: Mikhail Svetkin Date: Thu, 6 Jun 2019 10:16:09 +0200 Subject: rtems: Enable Thread local storage support Change-Id: If2ecf440fda9270688be60273e57d4b765bbdec2 Reviewed-by: Volker Hilsheimer --- src/corelib/thread/qthread_unix.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/corelib/thread/qthread_unix.cpp b/src/corelib/thread/qthread_unix.cpp index 3d4c906dc2..2bcc31e423 100644 --- a/src/corelib/thread/qthread_unix.cpp +++ b/src/corelib/thread/qthread_unix.cpp @@ -119,6 +119,9 @@ enum { ThreadPriorityResetFlag = 0x80000000 }; #if defined(Q_CC_XLC) || defined (Q_CC_SUN) #define HAVE_TLS #endif +#if defined(Q_OS_RTEMS) +#define HAVE_TLS +#endif #ifdef HAVE_TLS static __thread QThreadData *currentThreadData = 0; -- cgit v1.2.3 From 620f12120618ae548575c741a9dc54e405aefed4 Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Thu, 6 Jun 2019 12:27:26 +0200 Subject: QAtomic: introduce loadRelaxed() / storeRelaxed() Plain load() / store() have already relaxed semantics. This can be surprising -- std::atomic::load()/store() are actually sequentially consistent -- and introduce a pain point if someone wants to move from Qt atomics to std:: atomics. So just add a suffix to the functions to clarify what's the memory ordering involved with them. The Ops::load / ::store are temporarily left in, because other modules depends on them. We need to port those modules away, then they can go (it's private API anyhow). Similarly, not deprecating anything yet, except for marking obsolete in the docs; there's a lot of code around using load() / store() that needs to be ported first. [ChangeLog][QtCore][QAtomicInteger] Added loadRelaxed() and storeRelaxed(), to be used as replacements of load() / store(). [ChangeLog][QtCore][QAtomicPointer] Added loadRelaxed() and storeRelaxed(), to be used as replacements of load() / store(). Change-Id: Iab0a78885050379e3740f0b039ba2bef28ce3bd2 Reviewed-by: Marc Mutz Reviewed-by: Thiago Macieira --- src/corelib/thread/qatomic.cpp | 75 ++++++++++++++++++++++++++++++++----- src/corelib/thread/qatomic.h | 4 +- src/corelib/thread/qatomic_cxx11.h | 18 +++++++++ src/corelib/thread/qbasicatomic.h | 14 +++++-- src/corelib/thread/qgenericatomic.h | 22 ++++++++--- 5 files changed, 113 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/corelib/thread/qatomic.cpp b/src/corelib/thread/qatomic.cpp index c161bec537..b1a7edad91 100644 --- a/src/corelib/thread/qatomic.cpp +++ b/src/corelib/thread/qatomic.cpp @@ -258,12 +258,26 @@ /*! \fn template T QAtomicInteger::load() const + \obsolete + + Use loadRelaxed() instead. Atomically loads the value of this QAtomicInteger using relaxed memory ordering. The value is not modified in any way, but note that there's no guarantee that it remains so. - \sa store(), loadAcquire() + \sa storeRelaxed(), loadAcquire() +*/ + +/*! + \fn template T QAtomicInteger::loadRelaxed() const + \since 5.14 + + Atomically loads the value of this QAtomicInteger using relaxed memory + ordering. The value is not modified in any way, but note that there's no + guarantee that it remains so. + + \sa storeRelaxed(), loadAcquire() */ /*! @@ -273,16 +287,29 @@ ordering. The value is not modified in any way, but note that there's no guarantee that it remains so. - \sa store(), load() + \sa storeRelaxed(), loadRelaxed() */ /*! \fn template void QAtomicInteger::store(T newValue) + \obsolete + + Use storeRelaxed() instead. Atomically stores the \a newValue value into this atomic type, using relaxed memory ordering. - \sa storeRelease(), load() + \sa storeRelease(), loadRelaxed() +*/ + +/*! + \fn template void QAtomicInteger::storeRelaxed(T newValue) + \since 5.14 + + Atomically stores the \a newValue value into this atomic type, using + relaxed memory ordering. + + \sa storeRelease(), loadRelaxed() */ /*! @@ -291,7 +318,7 @@ Atomically stores the \a newValue value into this atomic type, using the "Release" memory ordering. - \sa store(), load() + \sa store(), loadAcquire() */ /*! @@ -303,7 +330,7 @@ value is not modified in any way, but note that there's no guarantee that it remains so. - \sa load(), loadAcquire() + \sa loadRelaxed(), loadAcquire() */ /*! @@ -314,7 +341,7 @@ sequentially consistent memory ordering if possible; or "Release" ordering if not. This function returns a reference to this object. - \sa store(), storeRelease() + \sa storeRelaxed(), storeRelease() */ /*! \fn template bool QAtomicInteger::isReferenceCountingNative() @@ -1278,14 +1305,29 @@ /*! \fn template T *QAtomicPointer::load() const + \obsolete + + Use loadRelaxed() instead. Atomically loads the value of this QAtomicPointer using relaxed memory ordering. The value is not modified in any way, but note that there's no guarantee that it remains so. - \sa store(), loadAcquire() + \sa storeRelaxed(), loadAcquire() */ +/*! + \fn template T *QAtomicPointer::loadRelaxed() const + \since 5.14 + + Atomically loads the value of this QAtomicPointer using relaxed memory + ordering. The value is not modified in any way, but note that there's no + guarantee that it remains so. + + \sa storeRelaxed(), loadAcquire() +*/ + + /*! \fn template T *QAtomicPointer::loadAcquire() const @@ -1293,16 +1335,29 @@ ordering. The value is not modified in any way, but note that there's no guarantee that it remains so. - \sa store(), load() + \sa storeRelease(), loadRelaxed() */ /*! \fn template void QAtomicPointer::store(T *newValue) + \obsolete + + Use storeRelaxed() instead. + + Atomically stores the \a newValue value into this atomic type, using + relaxed memory ordering. + + \sa storeRelease(), loadRelaxed() +*/ + +/*! + \fn template void QAtomicPointer::storeRelaxed(T *newValue) + \since 5.14 Atomically stores the \a newValue value into this atomic type, using relaxed memory ordering. - \sa storeRelease(), load() + \sa storeRelease(), loadRelaxed() */ /*! @@ -1311,7 +1366,7 @@ Atomically stores the \a newValue value into this atomic type, using the "Release" memory ordering. - \sa store(), load() + \sa storeRelaxed(), loadRelaxed() */ /*! \fn template bool QAtomicPointer::isTestAndSetNative() diff --git a/src/corelib/thread/qatomic.h b/src/corelib/thread/qatomic.h index 280ce96b76..7990db2fd9 100644 --- a/src/corelib/thread/qatomic.h +++ b/src/corelib/thread/qatomic.h @@ -81,8 +81,10 @@ public: #ifdef Q_CLANG_QDOC T load() const; + T loadRelaxed() const; T loadAcquire() const; void store(T newValue); + void storeRelaxed(T newValue); void storeRelease(T newValue); operator T() const; @@ -172,7 +174,7 @@ public: #else inline QAtomicPointer(T *value = nullptr) noexcept { - this->store(value); + this->storeRelaxed(value); } #endif inline QAtomicPointer(const QAtomicPointer &other) noexcept diff --git a/src/corelib/thread/qatomic_cxx11.h b/src/corelib/thread/qatomic_cxx11.h index 2851bae73e..7386aee126 100644 --- a/src/corelib/thread/qatomic_cxx11.h +++ b/src/corelib/thread/qatomic_cxx11.h @@ -233,6 +233,18 @@ template struct QAtomicOps return _q_value.load(std::memory_order_relaxed); } + template static inline + T loadRelaxed(const std::atomic &_q_value) noexcept + { + return _q_value.load(std::memory_order_relaxed); + } + + template static inline + T loadRelaxed(const volatile std::atomic &_q_value) noexcept + { + return _q_value.load(std::memory_order_relaxed); + } + template static inline T loadAcquire(const std::atomic &_q_value) noexcept { @@ -251,6 +263,12 @@ template struct QAtomicOps _q_value.store(newValue, std::memory_order_relaxed); } + template static inline + void storeRelaxed(std::atomic &_q_value, T newValue) noexcept + { + _q_value.store(newValue, std::memory_order_relaxed); + } + template static inline void storeRelease(std::atomic &_q_value, T newValue) noexcept { diff --git a/src/corelib/thread/qbasicatomic.h b/src/corelib/thread/qbasicatomic.h index 7d2e06a499..dc976819ef 100644 --- a/src/corelib/thread/qbasicatomic.h +++ b/src/corelib/thread/qbasicatomic.h @@ -99,9 +99,11 @@ public: typename Ops::Type _q_value; // Everything below is either implemented in ../arch/qatomic_XXX.h or (as fallback) in qgenericatomic.h + T load() const noexcept { return loadRelaxed(); } + void store(T newValue) noexcept { storeRelaxed(newValue); } - T load() const noexcept { return Ops::load(_q_value); } - void store(T newValue) noexcept { Ops::store(_q_value, newValue); } + T loadRelaxed() const noexcept { return Ops::loadRelaxed(_q_value); } + void storeRelaxed(T newValue) noexcept { Ops::storeRelaxed(_q_value, newValue); } T loadAcquire() const noexcept { return Ops::loadAcquire(_q_value); } void storeRelease(T newValue) noexcept { Ops::storeRelease(_q_value, newValue); } @@ -236,8 +238,12 @@ public: AtomicType _q_value; - Type load() const noexcept { return Ops::load(_q_value); } - void store(Type newValue) noexcept { Ops::store(_q_value, newValue); } + Type load() const noexcept { return loadRelaxed(); } + void store(Type newValue) noexcept { storeRelaxed(newValue); } + + Type loadRelaxed() const noexcept { return Ops::loadRelaxed(_q_value); } + void storeRelaxed(Type newValue) noexcept { Ops::storeRelaxed(_q_value, newValue); } + operator Type() const noexcept { return loadAcquire(); } Type operator=(Type newValue) noexcept { storeRelease(newValue); return newValue; } diff --git a/src/corelib/thread/qgenericatomic.h b/src/corelib/thread/qgenericatomic.h index f8333e7de6..e9e5f3c74b 100644 --- a/src/corelib/thread/qgenericatomic.h +++ b/src/corelib/thread/qgenericatomic.h @@ -96,6 +96,18 @@ template struct QGenericAtomicOps _q_value = newValue; } + template static Q_ALWAYS_INLINE + T loadRelaxed(const T &_q_value) noexcept + { + return _q_value; + } + + template static Q_ALWAYS_INLINE + void storeRelaxed(T &_q_value, X newValue) noexcept + { + _q_value = newValue; + } + template static Q_ALWAYS_INLINE T loadAcquire(const T &_q_value) noexcept { @@ -190,7 +202,7 @@ template struct QGenericAtomicOps { // implement fetchAndStore on top of testAndSet Q_FOREVER { - T tmp = load(_q_value); + T tmp = loadRelaxed(_q_value); if (BaseClass::testAndSetRelaxed(_q_value, tmp, newValue)) return tmp; } @@ -225,7 +237,7 @@ template struct QGenericAtomicOps { // implement fetchAndAdd on top of testAndSet Q_FOREVER { - T tmp = BaseClass::load(_q_value); + T tmp = BaseClass::loadRelaxed(_q_value); if (BaseClass::testAndSetRelaxed(_q_value, tmp, T(tmp + valueToAdd))) return tmp; } @@ -289,7 +301,7 @@ QT_WARNING_POP T fetchAndAndRelaxed(T &_q_value, typename std::enable_if::isIntegral, T>::type operand) noexcept { // implement fetchAndAnd on top of testAndSet - T tmp = BaseClass::load(_q_value); + T tmp = BaseClass::loadRelaxed(_q_value); Q_FOREVER { if (BaseClass::testAndSetRelaxed(_q_value, tmp, T(tmp & operand), &tmp)) return tmp; @@ -322,7 +334,7 @@ QT_WARNING_POP T fetchAndOrRelaxed(T &_q_value, typename std::enable_if::isIntegral, T>::type operand) noexcept { // implement fetchAndOr on top of testAndSet - T tmp = BaseClass::load(_q_value); + T tmp = BaseClass::loadRelaxed(_q_value); Q_FOREVER { if (BaseClass::testAndSetRelaxed(_q_value, tmp, T(tmp | operand), &tmp)) return tmp; @@ -355,7 +367,7 @@ QT_WARNING_POP T fetchAndXorRelaxed(T &_q_value, typename std::enable_if::isIntegral, T>::type operand) noexcept { // implement fetchAndXor on top of testAndSet - T tmp = BaseClass::load(_q_value); + T tmp = BaseClass::loadRelaxed(_q_value); Q_FOREVER { if (BaseClass::testAndSetRelaxed(_q_value, tmp, T(tmp ^ operand), &tmp)) return tmp; -- cgit v1.2.3 From ff88c3bc55acefb3e57c01162d2dc04c5e24a276 Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Wed, 12 Jun 2019 01:30:10 +0200 Subject: Q_ARRAY_LITERAL: fix the checks on the payload's datatype The check was a misnomer -- non-POD types can go in unions since C++11. And we may want them, e.g. types without a trivial default constructor. What we really want is to check for a literal type (so that the array payload can be built entirely at compile time, and put in .rodata). So, amend the check. Also, make the dummy array constexpr, to be sure that we are indeed building the payload using constexpr constructors. That would make the first check redundant, but the fact that we're still using a macro for constexpr makes me think that not all compilers support it, so I'm leaving the first check in... Change-Id: I9f1473aa74dff5b6b6535ae4cd8325451c0b18e6 Reviewed-by: Marc Mutz --- src/corelib/tools/qarraydata.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/corelib/tools/qarraydata.h b/src/corelib/tools/qarraydata.h index 7e3f8c9dbd..074072b987 100644 --- a/src/corelib/tools/qarraydata.h +++ b/src/corelib/tools/qarraydata.h @@ -324,10 +324,10 @@ struct QArrayDataPointerRef /**/ #define Q_ARRAY_LITERAL_IMPL(Type, ...) \ - union { Type type_must_be_POD; } dummy; Q_UNUSED(dummy) \ + Q_STATIC_ASSERT(std::is_literal_type::value); \ \ /* Portable compile-time array size computation */ \ - Type data[] = { __VA_ARGS__ }; Q_UNUSED(data) \ + Q_CONSTEXPR Type data[] = { __VA_ARGS__ }; Q_UNUSED(data); \ enum { Size = sizeof(data) / sizeof(data[0]) }; \ \ static const QStaticArrayData literal = { \ -- cgit v1.2.3 From fafae936a68bac1231b2de69b58e8248ae5dca2f Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Fri, 7 Jun 2019 23:00:53 +0200 Subject: QMetaType: fix an is_pod test that C++11 broke C++11 allows non-POD-types in unions, so the test didn't test anything. Use a static_assert over std::is_pod instead. Change-Id: Ida7ef0551ae6ae07357a987a409294d2a386be2f Reviewed-by: Ville Voutilainen --- src/corelib/kernel/qmetatype.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'src') diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp index 311fb01fd0..754f5a13e4 100644 --- a/src/corelib/kernel/qmetatype.cpp +++ b/src/corelib/kernel/qmetatype.cpp @@ -587,13 +587,7 @@ QMetaTypeComparatorRegistry; typedef QMetaTypeFunctionRegistry QMetaTypeDebugStreamRegistry; -namespace -{ -union CheckThatItIsPod -{ // This should break if QMetaTypeInterface is not a POD type - QMetaTypeInterface iface; -}; -} +Q_STATIC_ASSERT(std::is_pod::value); Q_DECLARE_TYPEINFO(QCustomTypeInfo, Q_MOVABLE_TYPE); Q_GLOBAL_STATIC(QVector, customTypes) -- cgit v1.2.3 From 242bc539ab6d9e76e1f67f2a51918c79970c2788 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Thu, 6 Jun 2019 12:52:15 +0200 Subject: QWeakPointer: use an alternative work-round for internalData() users MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous work-around fails, probably because of cross-dependencies. E.g. we have this in QtScXml: In file included from /home/qt/work/install/include/QtCore/qsharedpointer.h:48:0, from ../../src/scxml/qscxmltabledata_p.h:55, from ../../src/scxml/qscxmltabledata.cpp:40: /home/qt/work/install/include/QtCore/qsharedpointer_impl.h:687:12: error: ‘QPointer’ does not name a type; did you mean ‘pointer’? friend QPointer ^~~~~~~~ pointer /home/qt/work/install/include/QtCore/qsharedpointer_impl.h:689:23: error: ‘QSmartPointerConvertFunctor’ in namespace ‘QtPrivate’ does not name a template type friend QtPrivate::QSmartPointerConvertFunctor; ^~~~~~~~~~~~~~~~~~~~~~~~~~~ To fix, grand friendship only to a non-template class with a templated static method that returns internalData(). This fixes most users, except in qmetatype.h, which does not include qsharedpointer.h. In order to use the non-template class in there, we need to delay its name lookup to instantiation time. We do this by artificially making it a dependent name, by using a class template that inherits from our befrieded class. Change-Id: I12b427f1fe9503df819ea5436d780972d6402e68 Reviewed-by: Ville Voutilainen --- src/corelib/kernel/qmetatype.h | 7 ++++++- src/corelib/kernel/qpointer.h | 3 ++- src/corelib/tools/qsharedpointer_impl.h | 28 +++++++++++++++++----------- 3 files changed, 25 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h index 154ccf62bb..fef25a32c4 100644 --- a/src/corelib/kernel/qmetatype.h +++ b/src/corelib/kernel/qmetatype.h @@ -1715,12 +1715,17 @@ namespace QtPrivate { } }; + // hack to delay name lookup to instantiation time by making + // EnableInternalData a dependent name: + template + struct EnableInternalDataWrap; + template struct QSmartPointerConvertFunctor > { QObject* operator()(const QWeakPointer &p) const { - return p.internalData(); + return QtPrivate::EnableInternalDataWrap::internalData(p); } }; } diff --git a/src/corelib/kernel/qpointer.h b/src/corelib/kernel/qpointer.h index 80faef2990..7052bcf0d4 100644 --- a/src/corelib/kernel/qpointer.h +++ b/src/corelib/kernel/qpointer.h @@ -143,7 +143,8 @@ template QPointer qPointerFromVariant(const QVariant &variant) { - return QPointer(qobject_cast(QtSharedPointer::weakPointerFromVariant_internal(variant).internalData())); + const auto wp = QtSharedPointer::weakPointerFromVariant_internal(variant); + return QPointer{qobject_cast(QtPrivate::EnableInternalData::internalData(wp))}; } template diff --git a/src/corelib/tools/qsharedpointer_impl.h b/src/corelib/tools/qsharedpointer_impl.h index 55f0f17c52..f352e433c5 100644 --- a/src/corelib/tools/qsharedpointer_impl.h +++ b/src/corelib/tools/qsharedpointer_impl.h @@ -107,6 +107,10 @@ template QSharedPointer qSharedPointerObjectCast(const QSharedPointer &ptr); #endif +namespace QtPrivate { +struct EnableInternalData; +} + namespace QtSharedPointer { template class ExternalRefCount; @@ -672,21 +676,12 @@ public: #endif private: - + friend struct QtPrivate::EnableInternalData; #if defined(Q_NO_TEMPLATE_FRIENDS) public: #else template friend class QSharedPointer; template friend class QPointer; -# ifndef QT_NO_QOBJECT - template - friend QWeakPointer::Value, X>::type> - qWeakPointerFromVariant(const QVariant &variant); -# endif - template - friend QPointer - qPointerFromVariant(const QVariant &variant); - friend QtPrivate::QSmartPointerConvertFunctor; #endif template @@ -721,6 +716,17 @@ public: T *value; }; +namespace QtPrivate { +struct EnableInternalData { + template + static T *internalData(const QWeakPointer &p) noexcept { return p.internalData(); } +}; +// hack to delay name lookup to instantiation time by making +// EnableInternalData a dependent name: +template +struct EnableInternalDataWrap : EnableInternalData {}; +} + template class QEnableSharedFromThis { @@ -996,7 +1002,7 @@ template QWeakPointer::Value, T>::type> qWeakPointerFromVariant(const QVariant &variant) { - return QWeakPointer(qobject_cast(QtSharedPointer::weakPointerFromVariant_internal(variant).internalData())); + return QWeakPointer(qobject_cast(QtPrivate::EnableInternalData::internalData(QtSharedPointer::weakPointerFromVariant_internal(variant)))); } template QSharedPointer::Value, T>::type> -- cgit v1.2.3 From 166753d8e00e11d2fa92d3bfbe5667ad4b8f7b9d Mon Sep 17 00:00:00 2001 From: hjk Date: Thu, 7 Mar 2019 16:57:52 +0100 Subject: rcc: Avoid needless use of macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit strlen on literals gets typically optimized out nowadays. Adjust callee side to handle the off-by-one between sizeof(literal) and strlen(). Change-Id: I1551f69a160922681d66024701ba1bd8f6dc03bf Reviewed-by: Jörg Bornemann --- src/tools/rcc/rcc.cpp | 5 +---- src/tools/rcc/rcc.h | 1 + 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/tools/rcc/rcc.cpp b/src/tools/rcc/rcc.cpp index 08fb6fca5f..94f6911010 100644 --- a/src/tools/rcc/rcc.cpp +++ b/src/tools/rcc/rcc.cpp @@ -67,11 +67,8 @@ enum { # define CONSTANT_COMPRESSALGO_DEFAULT RCCResourceLibrary::CompressionAlgorithm::None #endif -#define writeString(s) write(s, sizeof(s)) - void RCCResourceLibrary::write(const char *str, int len) { - --len; // trailing \0 on string literals... int n = m_out.size(); m_out.resize(n + len); memcpy(m_out.data() + n, str, len); @@ -983,7 +980,7 @@ void RCCResourceLibrary::writeDecimal(int value) Q_ASSERT(m_format != RCCResourceLibrary::Binary); char buf[std::numeric_limits::digits10 + 2]; int n = snprintf(buf, sizeof(buf), "%d", value); - write(buf, n + 1); // write() takes a size including terminating NUL + write(buf, n); } static const char hexDigits[] = "0123456789abcdef"; diff --git a/src/tools/rcc/rcc.h b/src/tools/rcc/rcc.h index b301355e4f..190c37a1f6 100644 --- a/src/tools/rcc/rcc.h +++ b/src/tools/rcc/rcc.h @@ -143,6 +143,7 @@ private: void writeChar(char c) { m_out.append(c); } void writeByteArray(const QByteArray &); void write(const char *, int len); + void writeString(const char *s) { write(s, static_cast(strlen(s))); } #if QT_CONFIG(zstd) ZSTD_CCtx *m_zstdCCtx; -- cgit v1.2.3 From de82d239f814cf2a717bea0defeee3732384e271 Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Wed, 12 Jun 2019 01:37:05 +0200 Subject: Make QColor a literal type Extracted from the SVG names patch. It basically just requires adding a constexpr constructor to the inner union, then sprinkling constexpr on the existing ones. Do minor refactorings as drive-by. Change-Id: I60e7a1c9068def3507cb07440450e51673269f84 Reviewed-by: Allan Sandfeld Jensen --- src/gui/painting/qcolor.cpp | 7 ++----- src/gui/painting/qcolor.h | 42 +++++++++++++++++++++++++----------------- 2 files changed, 27 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/gui/painting/qcolor.cpp b/src/gui/painting/qcolor.cpp index 174350d884..6cbc30e79a 100644 --- a/src/gui/painting/qcolor.cpp +++ b/src/gui/painting/qcolor.cpp @@ -1349,7 +1349,7 @@ void QColor::setRgbF(qreal r, qreal g, qreal b, qreal a) */ void QColor::setRgb(int r, int g, int b, int a) { - if ((uint)r > 255 || (uint)g > 255 || (uint)b > 255 || (uint)a > 255) { + if (!isRgbaValid(r, g, b, a)) { qWarning("QColor::setRgb: RGB parameters out of range"); invalidate(); return; @@ -2398,10 +2398,7 @@ QColor QColor::fromRgba(QRgb rgba) noexcept */ QColor QColor::fromRgb(int r, int g, int b, int a) { - if (r < 0 || r > 255 - || g < 0 || g > 255 - || b < 0 || b > 255 - || a < 0 || a > 255) { + if (!isRgbaValid(r, g, b, a)) { qWarning("QColor::fromRgb: RGB parameters out of range"); return QColor(); } diff --git a/src/gui/painting/qcolor.h b/src/gui/painting/qcolor.h index e3c267f97d..723b9fce73 100644 --- a/src/gui/painting/qcolor.h +++ b/src/gui/painting/qcolor.h @@ -67,9 +67,16 @@ public: enum Spec { Invalid, Rgb, Hsv, Cmyk, Hsl, ExtendedRgb }; enum NameFormat { HexRgb, HexArgb }; - inline QColor() noexcept; + Q_DECL_CONSTEXPR QColor() noexcept + : cspec(Invalid), ct(USHRT_MAX, 0, 0, 0, 0) {} QColor(Qt::GlobalColor color) noexcept; - inline QColor(int r, int g, int b, int a = 255); + Q_DECL_CONSTEXPR QColor(int r, int g, int b, int a = 255) noexcept + : cspec(isRgbaValid(r, g, b, a) ? Rgb : Invalid), + ct(cspec == Rgb ? a * 0x0101 : 0, + cspec == Rgb ? r * 0x0101 : 0, + cspec == Rgb ? g * 0x0101 : 0, + cspec == Rgb ? b * 0x0101 : 0, + 0) {} QColor(QRgb rgb) noexcept; QColor(QRgba64 rgba64) noexcept; #if QT_STRINGVIEW_LEVEL < 2 @@ -81,8 +88,11 @@ public: QColor(Spec spec) noexcept; #if QT_VERSION < QT_VERSION_CHECK(6,0,0) - inline QColor(const QColor &color) noexcept; // ### Qt 6: remove all of these, the trivial ones are fine. - QColor(QColor &&other) noexcept : cspec(other.cspec), ct(other.ct) {} + // ### Qt 6: remove all of these, the trivial ones are fine. + Q_DECL_CONSTEXPR QColor(const QColor &color) noexcept + : cspec(color.cspec), ct(color.ct) + {} + Q_DECL_CONSTEXPR QColor(QColor &&other) noexcept : cspec(other.cspec), ct(other.ct) {} QColor &operator=(QColor &&other) noexcept { cspec = other.cspec; ct = other.ct; return *this; } QColor &operator=(const QColor &) noexcept; @@ -244,8 +254,18 @@ private: template bool setColorFromString(String name); + static Q_DECL_CONSTEXPR bool isRgbaValid(int r, int g, int b, int a = 255) noexcept Q_DECL_CONST_FUNCTION + { + return uint(r) <= 255 && uint(g) <= 255 && uint(b) <= 255 && uint(a) <= 255; + } + Spec cspec; - union { + union CT { +#ifdef Q_COMPILER_UNIFORM_INIT + CT() {} // doesn't init anything, thus can't be constexpr + Q_DECL_CONSTEXPR explicit CT(ushort a1, ushort a2, ushort a3, ushort a4, ushort a5) noexcept + : array{a1, a2, a3, a4, a5} {} +#endif struct { ushort alpha; ushort red; @@ -292,12 +312,6 @@ private: }; Q_DECLARE_TYPEINFO(QColor, QT_VERSION >= QT_VERSION_CHECK(6,0,0) ? Q_MOVABLE_TYPE : Q_RELOCATABLE_TYPE); -inline QColor::QColor() noexcept -{ invalidate(); } - -inline QColor::QColor(int r, int g, int b, int a) -{ setRgb(r, g, b, a); } - inline QColor::QColor(QLatin1String aname) { setNamedColor(aname); } @@ -309,12 +323,6 @@ inline QColor::QColor(const QString& aname) { setNamedColor(aname); } #endif -#if QT_VERSION < QT_VERSION_CHECK(6,0,0) -inline QColor::QColor(const QColor &acolor) noexcept - : cspec(acolor.cspec) -{ ct.argb = acolor.ct.argb; } -#endif - inline bool QColor::isValid() const noexcept { return cspec != Invalid; } -- cgit v1.2.3 From 0ad5e1626832d80952ef02bfe0457cf61b2f698e Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Sat, 8 Jun 2019 12:00:05 +0200 Subject: QEvdev: use printf-style qCDebug()/qWarning() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also use qUtf16Printable() and qErrnoWarning (removing explicit errno, where present). Saves 6.6KiB in text size on optimized Linux AMD64 GCC 9.1 build across all .so's that link to QtInputSupport.a. Change-Id: I1def2cfabd2eed65390099cd1d06f8061a9355be Reviewed-by: Allan Sandfeld Jensen Reviewed-by: Mårten Nordheim --- .../input/evdevkeyboard/qevdevkeyboardhandler.cpp | 21 ++++++++++--------- .../input/evdevkeyboard/qevdevkeyboardmanager.cpp | 8 ++++---- .../input/evdevmouse/qevdevmousemanager.cpp | 8 ++++---- .../input/evdevtablet/qevdevtablethandler.cpp | 22 ++++++++++---------- .../input/evdevtablet/qevdevtabletmanager.cpp | 8 ++++---- .../input/evdevtouch/qevdevtouchhandler.cpp | 24 +++++++++++----------- .../input/evdevtouch/qevdevtouchmanager.cpp | 12 +++++------ 7 files changed, 52 insertions(+), 51 deletions(-) (limited to 'src') diff --git a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp index 02d6586fe8..35ee23311d 100644 --- a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp +++ b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp @@ -102,7 +102,8 @@ QEvdevKeyboardHandler *QEvdevKeyboardHandler::create(const QString &device, const QString &specification, const QString &defaultKeymapFile) { - qCDebug(qLcEvdevKey) << "Try to create keyboard handler for" << device << specification; + qCDebug(qLcEvdevKey, "Try to create keyboard handler for \"%ls\" \"%ls\"", + qUtf16Printable(device), qUtf16Printable(specification)); QString keymapFile = defaultKeymapFile; int repeatDelay = 400; @@ -127,7 +128,7 @@ QEvdevKeyboardHandler *QEvdevKeyboardHandler::create(const QString &device, grab = arg.mid(5).toInt(); } - qCDebug(qLcEvdevKey) << "Opening keyboard at" << device; + qCDebug(qLcEvdevKey, "Opening keyboard at %ls", qUtf16Printable(device)); QFdContainer fd(qt_safe_open(device.toLocal8Bit().constData(), O_RDONLY | O_NDELAY, 0)); if (fd.get() >= 0) { @@ -139,14 +140,14 @@ QEvdevKeyboardHandler *QEvdevKeyboardHandler::create(const QString &device, return new QEvdevKeyboardHandler(device, fd, disableZap, enableCompose, keymapFile); } else { - qWarning("Cannot open keyboard input device '%s': %s", qPrintable(device), strerror(errno)); + qErrnoWarning("Cannot open keyboard input device '%ls'", qUtf16Printable(device)); return 0; } } void QEvdevKeyboardHandler::switchLed(int led, bool state) { - qCDebug(qLcEvdevKey) << "switchLed" << led << state; + qCDebug(qLcEvdevKey, "switchLed %d %d", led, int(state)); struct ::input_event led_ie; ::gettimeofday(&led_ie.time, 0); @@ -170,7 +171,7 @@ void QEvdevKeyboardHandler::readKeycode() return; } else if (result < 0) { if (errno != EINTR && errno != EAGAIN) { - qErrnoWarning(errno, "evdevkeyboard: Could not read from input device"); + qErrnoWarning("evdevkeyboard: Could not read from input device"); // If the device got disconnected, stop reading, otherwise we get flooded // by the above error over and over again. if (errno == ENODEV) { @@ -473,7 +474,7 @@ QEvdevKeyboardHandler::KeycodeAction QEvdevKeyboardHandler::processKeycode(quint void QEvdevKeyboardHandler::unloadKeymap() { - qCDebug(qLcEvdevKey) << "Unload current keymap and restore built-in"; + qCDebug(qLcEvdevKey, "Unload current keymap and restore built-in"); if (m_keymap && m_keymap != s_keymap_default) delete [] m_keymap; @@ -517,12 +518,12 @@ void QEvdevKeyboardHandler::unloadKeymap() bool QEvdevKeyboardHandler::loadKeymap(const QString &file) { - qCDebug(qLcEvdevKey) << "Loading keymap" << file; + qCDebug(qLcEvdevKey, "Loading keymap %ls", qUtf16Printable(file)); QFile f(file); if (!f.open(QIODevice::ReadOnly)) { - qWarning("Could not open keymap file '%s'", qPrintable(file)); + qWarning("Could not open keymap file '%ls'", qUtf16Printable(file)); return false; } @@ -541,7 +542,7 @@ bool QEvdevKeyboardHandler::loadKeymap(const QString &file) ds >> qmap_magic >> qmap_version >> qmap_keymap_size >> qmap_keycompose_size; if (ds.status() != QDataStream::Ok || qmap_magic != QEvdevKeyboardMap::FileMagic || qmap_version != 1 || qmap_keymap_size == 0) { - qWarning("'%s' is not a valid .qmap keymap file", qPrintable(file)); + qWarning("'%ls' is not a valid .qmap keymap file", qUtf16Printable(file)); return false; } @@ -557,7 +558,7 @@ bool QEvdevKeyboardHandler::loadKeymap(const QString &file) delete [] qmap_keymap; delete [] qmap_keycompose; - qWarning("Keymap file '%s' cannot be loaded.", qPrintable(file)); + qWarning("Keymap file '%ls' cannot be loaded.", qUtf16Printable(file)); return false; } diff --git a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp index e1659bc0d9..a73d4728a5 100644 --- a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp +++ b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp @@ -80,7 +80,7 @@ QEvdevKeyboardManager::QEvdevKeyboardManager(const QString &key, const QString & addKeyboard(device); if (devices.isEmpty()) { - qCDebug(qLcEvdevKey) << "evdevkeyboard: Using device discovery"; + qCDebug(qLcEvdevKey, "evdevkeyboard: Using device discovery"); m_deviceDiscovery = QDeviceDiscovery::create(QDeviceDiscovery::Device_Keyboard, this); if (m_deviceDiscovery) { // scan and add already connected keyboards @@ -104,7 +104,7 @@ QEvdevKeyboardManager::~QEvdevKeyboardManager() void QEvdevKeyboardManager::addKeyboard(const QString &deviceNode) { - qCDebug(qLcEvdevKey) << "Adding keyboard at" << deviceNode; + qCDebug(qLcEvdevKey, "Adding keyboard at %ls", qUtf16Printable(deviceNode)); QEvdevKeyboardHandler *keyboard; keyboard = QEvdevKeyboardHandler::create(deviceNode, m_spec, m_defaultKeymapFile); if (keyboard) { @@ -112,14 +112,14 @@ void QEvdevKeyboardManager::addKeyboard(const QString &deviceNode) QInputDeviceManagerPrivate::get(QGuiApplicationPrivate::inputDeviceManager())->setDeviceCount( QInputDeviceManager::DeviceTypeKeyboard, m_keyboards.count()); } else { - qWarning("Failed to open keyboard device %s", qPrintable(deviceNode)); + qWarning("Failed to open keyboard device %ls", qUtf16Printable(deviceNode)); } } void QEvdevKeyboardManager::removeKeyboard(const QString &deviceNode) { if (m_keyboards.contains(deviceNode)) { - qCDebug(qLcEvdevKey) << "Removing keyboard at" << deviceNode; + qCDebug(qLcEvdevKey, "Removing keyboard at %ls", qUtf16Printable(deviceNode)); QEvdevKeyboardHandler *keyboard = m_keyboards.value(deviceNode); m_keyboards.remove(deviceNode); QInputDeviceManagerPrivate::get(QGuiApplicationPrivate::inputDeviceManager())->setDeviceCount( diff --git a/src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp b/src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp index ae81bca00f..038ff7db43 100644 --- a/src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp +++ b/src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp @@ -86,7 +86,7 @@ QEvdevMouseManager::QEvdevMouseManager(const QString &key, const QString &specif addMouse(device); if (devices.isEmpty()) { - qCDebug(qLcEvdevMouse) << "evdevmouse: Using device discovery"; + qCDebug(qLcEvdevMouse, "evdevmouse: Using device discovery"); m_deviceDiscovery = QDeviceDiscovery::create(QDeviceDiscovery::Device_Mouse | QDeviceDiscovery::Device_Touchpad, this); if (m_deviceDiscovery) { // scan and add already connected keyboards @@ -159,7 +159,7 @@ void QEvdevMouseManager::handleWheelEvent(QPoint delta) void QEvdevMouseManager::addMouse(const QString &deviceNode) { - qCDebug(qLcEvdevMouse) << "Adding mouse at" << deviceNode; + qCDebug(qLcEvdevMouse, "Adding mouse at %ls", qUtf16Printable(deviceNode)); QEvdevMouseHandler *handler = QEvdevMouseHandler::create(deviceNode, m_spec); if (handler) { connect(handler, &QEvdevMouseHandler::handleMouseEvent, @@ -170,14 +170,14 @@ void QEvdevMouseManager::addMouse(const QString &deviceNode) QInputDeviceManagerPrivate::get(QGuiApplicationPrivate::inputDeviceManager())->setDeviceCount( QInputDeviceManager::DeviceTypePointer, m_mice.count()); } else { - qWarning("evdevmouse: Failed to open mouse device %s", qPrintable(deviceNode)); + qWarning("evdevmouse: Failed to open mouse device %ls", qUtf16Printable(deviceNode)); } } void QEvdevMouseManager::removeMouse(const QString &deviceNode) { if (m_mice.contains(deviceNode)) { - qCDebug(qLcEvdevMouse) << "Removing mouse at" << deviceNode; + qCDebug(qLcEvdevMouse, "Removing mouse at %ls", qUtf16Printable(deviceNode)); QEvdevMouseHandler *handler = m_mice.value(deviceNode); m_mice.remove(deviceNode); QInputDeviceManagerPrivate::get(QGuiApplicationPrivate::inputDeviceManager())->setDeviceCount( diff --git a/src/platformsupport/input/evdevtablet/qevdevtablethandler.cpp b/src/platformsupport/input/evdevtablet/qevdevtablethandler.cpp index b6051aaf3c..c86840b76c 100644 --- a/src/platformsupport/input/evdevtablet/qevdevtablethandler.cpp +++ b/src/platformsupport/input/evdevtablet/qevdevtablethandler.cpp @@ -172,11 +172,11 @@ QEvdevTabletHandler::QEvdevTabletHandler(const QString &device, const QString &s setObjectName(QLatin1String("Evdev Tablet Handler")); - qCDebug(qLcEvdevTablet, "evdevtablet: using %s", qPrintable(device)); + qCDebug(qLcEvdevTablet, "evdevtablet: using %ls", qUtf16Printable(device)); m_fd = QT_OPEN(device.toLocal8Bit().constData(), O_RDONLY | O_NDELAY, 0); if (m_fd < 0) { - qErrnoWarning(errno, "evdevtablet: Cannot open input device %s", qPrintable(device)); + qErrnoWarning("evdevtablet: Cannot open input device %ls", qUtf16Printable(device)); return; } @@ -184,11 +184,11 @@ QEvdevTabletHandler::QEvdevTabletHandler(const QString &device, const QString &s if (grabSuccess) ioctl(m_fd, EVIOCGRAB, (void *) 0); else - qWarning("evdevtablet: %s: The device is grabbed by another process. No events will be read.", qPrintable(device)); + qWarning("evdevtablet: %ls: The device is grabbed by another process. No events will be read.", qUtf16Printable(device)); d = new QEvdevTabletData(this); if (!queryLimits()) - qWarning("evdevtablet: %s: Unset or invalid ABS limits. Behavior will be unspecified.", qPrintable(device)); + qWarning("evdevtablet: %ls: Unset or invalid ABS limits. Behavior will be unspecified.", qUtf16Printable(device)); m_notifier = new QSocketNotifier(m_fd, QSocketNotifier::Read, this); connect(m_notifier, &QSocketNotifier::activated, this, &QEvdevTabletHandler::readData); @@ -216,32 +216,32 @@ bool QEvdevTabletHandler::queryLimits() if (ok) { d->minValues.x = absInfo.minimum; d->maxValues.x = absInfo.maximum; - qCDebug(qLcEvdevTablet, "evdevtablet: %s: min X: %d max X: %d", qPrintable(m_device), + qCDebug(qLcEvdevTablet, "evdevtablet: %ls: min X: %d max X: %d", qUtf16Printable(m_device), d->minValues.x, d->maxValues.x); } ok &= ioctl(m_fd, EVIOCGABS(ABS_Y), &absInfo) >= 0; if (ok) { d->minValues.y = absInfo.minimum; d->maxValues.y = absInfo.maximum; - qCDebug(qLcEvdevTablet, "evdevtablet: %s: min Y: %d max Y: %d", qPrintable(m_device), + qCDebug(qLcEvdevTablet, "evdevtablet: %ls: min Y: %d max Y: %d", qUtf16Printable(m_device), d->minValues.y, d->maxValues.y); } if (ioctl(m_fd, EVIOCGABS(ABS_PRESSURE), &absInfo) >= 0) { d->minValues.p = absInfo.minimum; d->maxValues.p = absInfo.maximum; - qCDebug(qLcEvdevTablet, "evdevtablet: %s: min pressure: %d max pressure: %d", qPrintable(m_device), + qCDebug(qLcEvdevTablet, "evdevtablet: %ls: min pressure: %d max pressure: %d", qUtf16Printable(m_device), d->minValues.p, d->maxValues.p); } if (ioctl(m_fd, EVIOCGABS(ABS_DISTANCE), &absInfo) >= 0) { d->minValues.d = absInfo.minimum; d->maxValues.d = absInfo.maximum; - qCDebug(qLcEvdevTablet, "evdevtablet: %s: min distance: %d max distance: %d", qPrintable(m_device), + qCDebug(qLcEvdevTablet, "evdevtablet: %ls: min distance: %d max distance: %d", qUtf16Printable(m_device), d->minValues.d, d->maxValues.d); } char name[128]; if (ioctl(m_fd, EVIOCGNAME(sizeof(name) - 1), name) >= 0) { d->devName = QString::fromLocal8Bit(name); - qCDebug(qLcEvdevTablet, "evdevtablet: %s: device name: %s", qPrintable(m_device), name); + qCDebug(qLcEvdevTablet, "evdevtablet: %ls: device name: %s", qUtf16Printable(m_device), name); } return ok; } @@ -253,11 +253,11 @@ void QEvdevTabletHandler::readData() for (; ;) { int result = QT_READ(m_fd, reinterpret_cast(buffer) + n, sizeof(buffer) - n); if (!result) { - qWarning("evdevtablet: %s: Got EOF from input device", qPrintable(m_device)); + qWarning("evdevtablet: %ls: Got EOF from input device", qUtf16Printable(m_device)); return; } else if (result < 0) { if (errno != EINTR && errno != EAGAIN) { - qErrnoWarning(errno, "evdevtablet: %s: Could not read from input device", qPrintable(m_device)); + qErrnoWarning("evdevtablet: %ls: Could not read from input device", qUtf16Printable(m_device)); if (errno == ENODEV) { // device got disconnected -> stop reading delete m_notifier; m_notifier = 0; diff --git a/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp b/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp index 90949408ac..da4b6e5172 100644 --- a/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp +++ b/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp @@ -82,7 +82,7 @@ QEvdevTabletManager::QEvdevTabletManager(const QString &key, const QString &spec // when no devices specified, use device discovery to scan and monitor if (devices.isEmpty()) { - qCDebug(qLcEvdevTablet) << "evdevtablet: Using device discovery"; + qCDebug(qLcEvdevTablet, "evdevtablet: Using device discovery"); m_deviceDiscovery = QDeviceDiscovery::create(QDeviceDiscovery::Device_Tablet, this); if (m_deviceDiscovery) { const QStringList devices = m_deviceDiscovery->scanConnectedDevices(); @@ -104,7 +104,7 @@ QEvdevTabletManager::~QEvdevTabletManager() void QEvdevTabletManager::addDevice(const QString &deviceNode) { - qCDebug(qLcEvdevTablet) << "Adding device at" << deviceNode; + qCDebug(qLcEvdevTablet, "Adding device at %ls", qUtf16Printable(deviceNode)); QEvdevTabletHandlerThread *handler; handler = new QEvdevTabletHandlerThread(deviceNode, m_spec); if (handler) { @@ -112,14 +112,14 @@ void QEvdevTabletManager::addDevice(const QString &deviceNode) QInputDeviceManagerPrivate::get(QGuiApplicationPrivate::inputDeviceManager())->setDeviceCount( QInputDeviceManager::DeviceTypeTablet, m_activeDevices.count()); } else { - qWarning("evdevtablet: Failed to open tablet device %s", qPrintable(deviceNode)); + qWarning("evdevtablet: Failed to open tablet device %ls", qUtf16Printable(deviceNode)); } } void QEvdevTabletManager::removeDevice(const QString &deviceNode) { if (m_activeDevices.contains(deviceNode)) { - qCDebug(qLcEvdevTablet) << "Removing device at" << deviceNode; + qCDebug(qLcEvdevTablet, "Removing device at %ls", qUtf16Printable(deviceNode)); QEvdevTabletHandlerThread *handler = m_activeDevices.value(deviceNode); m_activeDevices.remove(deviceNode); QInputDeviceManagerPrivate::get(QGuiApplicationPrivate::inputDeviceManager())->setDeviceCount( diff --git a/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp b/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp index c3a5391255..737d85d5c3 100644 --- a/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp +++ b/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp @@ -228,7 +228,7 @@ QEvdevTouchScreenHandler::QEvdevTouchScreenHandler(const QString &device, const } } - qCDebug(qLcEvdevTouch, "evdevtouch: Using device %s", qPrintable(device)); + qCDebug(qLcEvdevTouch, "evdevtouch: Using device %ls", qUtf16Printable(device)); m_fd = QT_OPEN(device.toLocal8Bit().constData(), O_RDONLY | O_NDELAY, 0); @@ -236,7 +236,7 @@ QEvdevTouchScreenHandler::QEvdevTouchScreenHandler(const QString &device, const m_notify = new QSocketNotifier(m_fd, QSocketNotifier::Read, this); connect(m_notify, &QSocketNotifier::activated, this, &QEvdevTouchScreenHandler::readData); } else { - qErrnoWarning(errno, "evdevtouch: Cannot open input device %s", qPrintable(device)); + qErrnoWarning("evdevtouch: Cannot open input device %ls", qUtf16Printable(device)); return; } @@ -266,8 +266,8 @@ QEvdevTouchScreenHandler::QEvdevTouchScreenHandler(const QString &device, const d->deviceNode = device; qCDebug(qLcEvdevTouch, - "evdevtouch: %s: Protocol type %c %s (%s), filtered=%s", - qPrintable(d->deviceNode), + "evdevtouch: %ls: Protocol type %c %s (%s), filtered=%s", + qUtf16Printable(d->deviceNode), d->m_typeB ? 'B' : 'A', mtdevStr, d->m_singleTouch ? "single" : "multi", d->m_filtered ? "yes" : "no"); @@ -279,7 +279,7 @@ QEvdevTouchScreenHandler::QEvdevTouchScreenHandler(const QString &device, const bool has_x_range = false, has_y_range = false; if (ioctl(m_fd, EVIOCGABS((d->m_singleTouch ? ABS_X : ABS_MT_POSITION_X)), &absInfo) >= 0) { - qCDebug(qLcEvdevTouch, "evdevtouch: %s: min X: %d max X: %d", qPrintable(device), + qCDebug(qLcEvdevTouch, "evdevtouch: %ls: min X: %d max X: %d", qUtf16Printable(device), absInfo.minimum, absInfo.maximum); d->hw_range_x_min = absInfo.minimum; d->hw_range_x_max = absInfo.maximum; @@ -287,7 +287,7 @@ QEvdevTouchScreenHandler::QEvdevTouchScreenHandler(const QString &device, const } if (ioctl(m_fd, EVIOCGABS((d->m_singleTouch ? ABS_Y : ABS_MT_POSITION_Y)), &absInfo) >= 0) { - qCDebug(qLcEvdevTouch, "evdevtouch: %s: min Y: %d max Y: %d", qPrintable(device), + qCDebug(qLcEvdevTouch, "evdevtouch: %ls: min Y: %d max Y: %d", qUtf16Printable(device), absInfo.minimum, absInfo.maximum); d->hw_range_y_min = absInfo.minimum; d->hw_range_y_max = absInfo.maximum; @@ -295,10 +295,10 @@ QEvdevTouchScreenHandler::QEvdevTouchScreenHandler(const QString &device, const } if (!has_x_range || !has_y_range) - qWarning("evdevtouch: %s: Invalid ABS limits, behavior unspecified", qPrintable(device)); + qWarning("evdevtouch: %ls: Invalid ABS limits, behavior unspecified", qUtf16Printable(device)); if (ioctl(m_fd, EVIOCGABS(ABS_PRESSURE), &absInfo) >= 0) { - qCDebug(qLcEvdevTouch, "evdevtouch: %s: min pressure: %d max pressure: %d", qPrintable(device), + qCDebug(qLcEvdevTouch, "evdevtouch: %ls: min pressure: %d max pressure: %d", qUtf16Printable(device), absInfo.minimum, absInfo.maximum); if (absInfo.maximum > absInfo.minimum) { d->hw_pressure_min = absInfo.minimum; @@ -309,7 +309,7 @@ QEvdevTouchScreenHandler::QEvdevTouchScreenHandler(const QString &device, const char name[1024]; if (ioctl(m_fd, EVIOCGNAME(sizeof(name) - 1), name) >= 0) { d->hw_name = QString::fromLocal8Bit(name); - qCDebug(qLcEvdevTouch, "evdevtouch: %s: device name: %s", qPrintable(device), name); + qCDebug(qLcEvdevTouch, "evdevtouch: %ls: device name: %s", qUtf16Printable(device), name); } // Fix up the coordinate ranges for am335x in case the kernel driver does not have them fixed. @@ -345,8 +345,8 @@ QEvdevTouchScreenHandler::QEvdevTouchScreenHandler(const QString &device, const if (mapping.load()) { d->m_screenName = mapping.screenNameForDeviceNode(d->deviceNode); if (!d->m_screenName.isEmpty()) - qCDebug(qLcEvdevTouch, "evdevtouch: Mapping device %s to screen %s", - qPrintable(d->deviceNode), qPrintable(d->m_screenName)); + qCDebug(qLcEvdevTouch, "evdevtouch: Mapping device %ls to screen %ls", + qUtf16Printable(d->deviceNode), qUtf16Printable(d->m_screenName)); } registerTouchDevice(); @@ -427,7 +427,7 @@ err: return; } else if (events < 0) { if (errno != EINTR && errno != EAGAIN) { - qErrnoWarning(errno, "evdevtouch: Could not read from input device"); + qErrnoWarning("evdevtouch: Could not read from input device"); if (errno == ENODEV) { // device got disconnected -> stop reading delete m_notify; m_notify = nullptr; diff --git a/src/platformsupport/input/evdevtouch/qevdevtouchmanager.cpp b/src/platformsupport/input/evdevtouch/qevdevtouchmanager.cpp index 4cacbf03e5..f59d8d5e99 100644 --- a/src/platformsupport/input/evdevtouch/qevdevtouchmanager.cpp +++ b/src/platformsupport/input/evdevtouch/qevdevtouchmanager.cpp @@ -82,7 +82,7 @@ QEvdevTouchManager::QEvdevTouchManager(const QString &key, const QString &specif // when no devices specified, use device discovery to scan and monitor if (devices.isEmpty()) { - qCDebug(qLcEvdevTouch) << "evdevtouch: Using device discovery"; + qCDebug(qLcEvdevTouch, "evdevtouch: Using device discovery"); m_deviceDiscovery = QDeviceDiscovery::create(QDeviceDiscovery::Device_Touchpad | QDeviceDiscovery::Device_Touchscreen, this); if (m_deviceDiscovery) { const QStringList devices = m_deviceDiscovery->scanConnectedDevices(); @@ -104,21 +104,21 @@ QEvdevTouchManager::~QEvdevTouchManager() void QEvdevTouchManager::addDevice(const QString &deviceNode) { - qCDebug(qLcEvdevTouch) << "evdevtouch: Adding device at" << deviceNode; + qCDebug(qLcEvdevTouch, "evdevtouch: Adding device at %ls", qUtf16Printable(deviceNode)); QEvdevTouchScreenHandlerThread *handler; handler = new QEvdevTouchScreenHandlerThread(deviceNode, m_spec); if (handler) { m_activeDevices.insert(deviceNode, handler); connect(handler, &QEvdevTouchScreenHandlerThread::touchDeviceRegistered, this, &QEvdevTouchManager::updateInputDeviceCount); } else { - qWarning("evdevtouch: Failed to open touch device %s", qPrintable(deviceNode)); + qWarning("evdevtouch: Failed to open touch device %ls", qUtf16Printable(deviceNode)); } } void QEvdevTouchManager::removeDevice(const QString &deviceNode) { if (m_activeDevices.contains(deviceNode)) { - qCDebug(qLcEvdevTouch) << "evdevtouch: Removing device at" << deviceNode; + qCDebug(qLcEvdevTouch, "evdevtouch: Removing device at %ls", qUtf16Printable(deviceNode)); QEvdevTouchScreenHandlerThread *handler = m_activeDevices.value(deviceNode); m_activeDevices.remove(deviceNode); delete handler; @@ -135,8 +135,8 @@ void QEvdevTouchManager::updateInputDeviceCount() ++registeredTouchDevices; } - qCDebug(qLcEvdevTouch) << "evdevtouch: Updating QInputDeviceManager device count:" << registeredTouchDevices << " touch devices," - << m_activeDevices.count() - registeredTouchDevices << "pending handler(s)" ; + qCDebug(qLcEvdevTouch, "evdevtouch: Updating QInputDeviceManager device count: %d touch devices, %d pending handler(s)", + registeredTouchDevices, m_activeDevices.count() - registeredTouchDevices); QInputDeviceManagerPrivate::get(QGuiApplicationPrivate::inputDeviceManager())->setDeviceCount( QInputDeviceManager::DeviceTypeTouch, registeredTouchDevices); -- cgit v1.2.3 From ac608b7bd2fc3876d1a521cb3542b6b56133fd0f Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Tue, 4 Jun 2019 12:43:16 +0200 Subject: QDebug: add nothrow move special member functions This requires making QDebugStateSaver hold QDebug::Stream directly, not QDebug by reference, as the referenced object will have been moved from when ~QDebugStateSaver executes. The stream object, however, will still be around. Change-Id: I0ca2eb60cb9b68ea3835d9a9ff5e295d9b1c5fb5 Reviewed-by: Thiago Macieira Reviewed-by: David Faure --- src/corelib/io/qdebug.cpp | 36 +++++++++++++++++------------------- src/corelib/io/qdebug.h | 9 +++++---- 2 files changed, 22 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/corelib/io/qdebug.cpp b/src/corelib/io/qdebug.cpp index 6dc12cd83f..d13e94e096 100644 --- a/src/corelib/io/qdebug.cpp +++ b/src/corelib/io/qdebug.cpp @@ -147,7 +147,7 @@ using QtMiscUtils::fromHex; // Has been defined in the header / inlined before Qt 5.4 QDebug::~QDebug() { - if (!--stream->ref) { + if (stream && !--stream->ref) { if (stream->space && stream->buffer.endsWith(QLatin1Char(' '))) stream->buffer.chop(1); if (stream->message_output) { @@ -843,36 +843,34 @@ QDebug &QDebug::resetFormat() class QDebugStateSaverPrivate { public: - QDebugStateSaverPrivate(QDebug &dbg) - : m_dbg(dbg), - m_spaces(dbg.autoInsertSpaces()), - m_flags(0), - m_streamParams(dbg.stream->ts.d_ptr->params) + QDebugStateSaverPrivate(QDebug::Stream *stream) + : m_stream(stream), + m_spaces(stream->space), + m_flags(stream->context.version > 1 ? stream->flags : 0), + m_streamParams(stream->ts.d_ptr->params) { - if (m_dbg.stream->context.version > 1) - m_flags = m_dbg.stream->flags; } void restoreState() { - const bool currentSpaces = m_dbg.autoInsertSpaces(); + const bool currentSpaces = m_stream->space; if (currentSpaces && !m_spaces) - if (m_dbg.stream->buffer.endsWith(QLatin1Char(' '))) - m_dbg.stream->buffer.chop(1); + if (m_stream->buffer.endsWith(QLatin1Char(' '))) + m_stream->buffer.chop(1); - m_dbg.setAutoInsertSpaces(m_spaces); - m_dbg.stream->ts.d_ptr->params = m_streamParams; - if (m_dbg.stream->context.version > 1) - m_dbg.stream->flags = m_flags; + m_stream->space = m_spaces; + m_stream->ts.d_ptr->params = m_streamParams; + if (m_stream->context.version > 1) + m_stream->flags = m_flags; if (!currentSpaces && m_spaces) - m_dbg.stream->ts << ' '; + m_stream->ts << ' '; } - QDebug &m_dbg; + QDebug::Stream *m_stream; // QDebug state const bool m_spaces; - int m_flags; + const int m_flags; // QTextStream state const QTextStreamPrivate::Params m_streamParams; @@ -886,7 +884,7 @@ public: \sa QDebug::setAutoInsertSpaces(), QDebug::autoInsertSpaces() */ QDebugStateSaver::QDebugStateSaver(QDebug &dbg) - : d(new QDebugStateSaverPrivate(dbg)) + : d(new QDebugStateSaverPrivate(dbg.stream)) { } diff --git a/src/corelib/io/qdebug.h b/src/corelib/io/qdebug.h index 889fb6b571..421c5d933b 100644 --- a/src/corelib/io/qdebug.h +++ b/src/corelib/io/qdebug.h @@ -65,6 +65,7 @@ QT_BEGIN_NAMESPACE class Q_CORE_EXPORT QDebug { friend class QMessageLogger; + friend class QDebugStateSaver; friend class QDebugStateSaverPrivate; struct Stream { enum { VerbosityShift = 29, VerbosityMask = 0x7 }; @@ -114,7 +115,10 @@ public: inline QDebug(QString *string) : stream(new Stream(string)) {} inline QDebug(QtMsgType t) : stream(new Stream(t)) {} inline QDebug(const QDebug &o):stream(o.stream) { ++stream->ref; } + QDebug(QDebug &&other) noexcept : stream{qExchange(other.stream, nullptr)} {} inline QDebug &operator=(const QDebug &other); + QDebug &operator=(QDebug &&other) noexcept + { QDebug{std::move(other)}.swap(*this); return *this; } ~QDebug(); inline void swap(QDebug &other) noexcept { qSwap(stream, other.stream); } @@ -203,10 +207,7 @@ public: inline QDebug &QDebug::operator=(const QDebug &other) { - if (this != &other) { - QDebug copy(other); - qSwap(stream, copy.stream); - } + QDebug{other}.swap(*this); return *this; } -- cgit v1.2.3 From 18e7e82d3f23a2a6954308c7a7c49ec23344b593 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Thu, 23 May 2019 09:38:56 +0200 Subject: Remove the last uses of Java-style iterators in QtCore They are going to be deprecated. Add a strategic break. This was a pre-existing problem: the comment claims that h is invalid (though I personally don't see it), but then goes on to check the loop condition (which, in the mutable Java iterator case, involves calling h.cend()). We now cache the end iterator, so if there ever was a problem, it's probably a lesser one now, but it's still not kosher, and a debug version of QHash would find it, so break out explicitly. Saves ~200b in text size on optimized GCC 9.1 Linux AMD64 builds, ie. ~100b per loop. Change-Id: I7684485b55fb23a8cf882f89621ebb75a0e607b5 Reviewed-by: Lars Knoll --- src/corelib/io/qfilesystemwatcher_polling.cpp | 17 ++++++++--------- src/corelib/io/qfilesystemwatcher_win.cpp | 6 +++--- 2 files changed, 11 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/corelib/io/qfilesystemwatcher_polling.cpp b/src/corelib/io/qfilesystemwatcher_polling.cpp index a95a48cc8f..e07b02f7c2 100644 --- a/src/corelib/io/qfilesystemwatcher_polling.cpp +++ b/src/corelib/io/qfilesystemwatcher_polling.cpp @@ -110,33 +110,32 @@ QStringList QPollingFileSystemWatcherEngine::removePaths(const QStringList &path void QPollingFileSystemWatcherEngine::timeout() { - QMutableHashIterator fit(files); - while (fit.hasNext()) { - QHash::iterator x = fit.next(); + for (auto it = files.begin(), end = files.end(); it != end; /*erasing*/) { + auto x = it++; QString path = x.key(); QFileInfo fi(path); if (!fi.exists()) { - fit.remove(); + files.erase(x); emit fileChanged(path, true); } else if (x.value() != fi) { x.value() = fi; emit fileChanged(path, false); } } - QMutableHashIterator dit(directories); - while (dit.hasNext()) { - QHash::iterator x = dit.next(); + + for (auto it = directories.begin(), end = directories.end(); it != end; /*erasing*/) { + auto x = it++; QString path = x.key(); QFileInfo fi(path); if (!path.endsWith(QLatin1Char('/'))) fi = QFileInfo(path + QLatin1Char('/')); if (!fi.exists()) { - dit.remove(); + directories.erase(x); emit directoryChanged(path, true); } else if (x.value() != fi) { fi.refresh(); if (!fi.exists()) { - dit.remove(); + directories.erase(x); emit directoryChanged(path, true); } else { x.value() = fi; diff --git a/src/corelib/io/qfilesystemwatcher_win.cpp b/src/corelib/io/qfilesystemwatcher_win.cpp index 81ed694d95..c22a003931 100644 --- a/src/corelib/io/qfilesystemwatcher_win.cpp +++ b/src/corelib/io/qfilesystemwatcher_win.cpp @@ -695,9 +695,8 @@ void QWindowsFileSystemWatcherEngineThread::run() qErrnoWarning(error, "%ls", qUtf16Printable(msgFindNextFailed(h))); } - QMutableHashIterator it(h); - while (it.hasNext()) { - QWindowsFileSystemWatcherEngineThread::PathInfoHash::iterator x = it.next(); + for (auto it = h.begin(), end = h.end(); it != end; /*erasing*/ ) { + auto x = it++; QString absolutePath = x.value().absolutePath; QFileInfo fileInfo(x.value().path); DEBUG() << "checking" << x.key(); @@ -723,6 +722,7 @@ void QWindowsFileSystemWatcherEngineThread::run() handleForDir.remove(QFileSystemWatcherPathKey(absolutePath)); // h is now invalid + break; } } else if (x.value().isDir) { DEBUG() << x.key() << "directory changed!"; -- cgit v1.2.3 From dd3229e6724f05c7c61d6ffc92b5ebfa8ece0b23 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 7 Jun 2019 13:02:03 +0200 Subject: Remove a few usages of deprecated API Change-Id: I94bad0b8d3891c6b4a55178836cfff2a4312e330 Reviewed-by: Marc Mutz --- src/corelib/kernel/qbasictimer.cpp | 2 ++ src/corelib/mimetypes/qmimetype.h | 2 +- src/gui/image/qmovie.cpp | 3 ++- src/widgets/widgets/qcombobox.cpp | 4 ++-- 4 files changed, 7 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/corelib/kernel/qbasictimer.cpp b/src/corelib/kernel/qbasictimer.cpp index b16833b34c..ea8f8e2c77 100644 --- a/src/corelib/kernel/qbasictimer.cpp +++ b/src/corelib/kernel/qbasictimer.cpp @@ -105,6 +105,7 @@ QT_BEGIN_NAMESPACE \sa stop(), isActive(), swap() */ +#if QT_DEPRECATED_SINCE(5, 14) #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) /*! \internal @@ -125,6 +126,7 @@ QBasicTimer &QBasicTimer::operator=(const QBasicTimer &other) return *this; } #endif +#endif /*! \fn QBasicTimer::~QBasicTimer() diff --git a/src/corelib/mimetypes/qmimetype.h b/src/corelib/mimetypes/qmimetype.h index 6d6f9eb488..df1b60f2ce 100644 --- a/src/corelib/mimetypes/qmimetype.h +++ b/src/corelib/mimetypes/qmimetype.h @@ -48,11 +48,11 @@ QT_REQUIRE_CONFIG(mimetype); #include #include #include +#include QT_BEGIN_NAMESPACE class QMimeTypePrivate; -class QStringList; class QMimeType; Q_CORE_EXPORT uint qHash(const QMimeType &key, uint seed = 0) noexcept; diff --git a/src/gui/image/qmovie.cpp b/src/gui/image/qmovie.cpp index 79203c7b98..c9344fe2a1 100644 --- a/src/gui/image/qmovie.cpp +++ b/src/gui/image/qmovie.cpp @@ -175,6 +175,7 @@ #include "qmovie.h" #include "qglobal.h" +#include "qelapsedtimer.h" #include "qimage.h" #include "qimagereader.h" #include "qpixmap.h" @@ -437,7 +438,7 @@ QFrameInfo QMoviePrivate::infoForFrame(int frameNumber) */ bool QMoviePrivate::next() { - QTime time; + QElapsedTimer time; time.start(); QFrameInfo info = infoForFrame(nextFrameNumber); if (!info.isValid()) diff --git a/src/widgets/widgets/qcombobox.cpp b/src/widgets/widgets/qcombobox.cpp index 07c55e4db6..d8e72588e6 100644 --- a/src/widgets/widgets/qcombobox.cpp +++ b/src/widgets/widgets/qcombobox.cpp @@ -1881,12 +1881,11 @@ void QComboBox::setLineEdit(QLineEdit *edit) d->updateFocusPolicy(); d->lineEdit->setFocusProxy(this); d->lineEdit->setAttribute(Qt::WA_MacShowFocusRect, false); +#if QT_DEPRECATED_SINCE(5, 13) #if QT_CONFIG(completer) setAutoCompletion(d->autoCompletion); -#endif #ifdef QT_KEYPAD_NAVIGATION -#if QT_CONFIG(completer) if (QApplication::keypadNavigationEnabled()) { // Editable combo boxes will have a completer that is set to UnfilteredPopupCompletion. // This means that when the user enters edit mode they are immediately presented with a @@ -1898,6 +1897,7 @@ void QComboBox::setLineEdit(QLineEdit *edit) } } #endif +#endif #endif setAttribute(Qt::WA_InputMethodEnabled); -- cgit v1.2.3 From c143161608ded130919006f151bf92c44a0991d0 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Thu, 2 May 2019 14:58:12 +0200 Subject: Avoid uninitialized texture data in image glyph cache The problem becomes visible with styled native text materials in Quick, but only in certain cases: the regions not used by glyphs in the QImage are undefined (if they are 0 there's no problem) - but the whole code path is only used when the fbo readback workaround is enabled. When these conditions met, the styled text materials may sample locations with uninitialized data in the texture, showing small artifacts around the glyphs when shifting is involved in the styling. The non-image based GL glyph cache handles this by an explicit upload with all 0's when creating the texture - the QImage code path should do the same then. Change-Id: I818ee19f87c6a147e42cd3ead39645da4d0fef11 Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/gui/painting/qtextureglyphcache.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src') diff --git a/src/gui/painting/qtextureglyphcache.cpp b/src/gui/painting/qtextureglyphcache.cpp index c21f2cdda4..e8c47df21c 100644 --- a/src/gui/painting/qtextureglyphcache.cpp +++ b/src/gui/painting/qtextureglyphcache.cpp @@ -285,6 +285,8 @@ QImageTextureGlyphCache::~QImageTextureGlyphCache() void QImageTextureGlyphCache::resizeTextureData(int width, int height) { m_image = m_image.copy(0, 0, width, height); + // Regions not part of the copy are initialized to 0, and that is just what + // we need. } void QImageTextureGlyphCache::createTextureData(int width, int height) @@ -305,6 +307,12 @@ void QImageTextureGlyphCache::createTextureData(int width, int height) default: Q_UNREACHABLE(); } + + // Regions not touched by the glyphs must be initialized to 0. (such + // locations may in fact be sampled with styled (shifted) text materials) + // When resizing, the QImage copy() does this implicitly but the initial + // contents must be zeroed out explicitly here. + m_image.fill(0); } void QImageTextureGlyphCache::fillTexture(const Coord &c, glyph_t g, QFixed subPixelPosition) -- cgit v1.2.3 From 53599592e09edd215bfa1eaa7e6f3a9f3fc50ae6 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 22 Mar 2019 09:55:03 +0100 Subject: Introduce the Qt graphics abstraction as private QtGui helpers Comes with backends for Vulkan, Metal, Direct3D 11.1, and OpenGL (ES). All APIs are private for now. Shader conditioning (i.e. generating a QRhiShader in memory or on disk from some shader source code) is done via the tools and APIs provided by qt-labs/qtshadertools. The OpenGL support follows the cross-platform tradition of requiring ES 2.0 only, while optionally using some (ES) 3.x features. It can operate in core profile contexts as well. Task-number: QTBUG-70287 Change-Id: I246f2e36d562e404012c05db2aa72487108aa7cc Reviewed-by: Lars Knoll --- src/gui/gui.pro | 1 + src/gui/rhi/qrhi.cpp | 4891 +++++++++++++++++++++++++++++ src/gui/rhi/qrhi_p.h | 1375 ++++++++ src/gui/rhi/qrhi_p_p.h | 544 ++++ src/gui/rhi/qrhid3d11.cpp | 3388 ++++++++++++++++++++ src/gui/rhi/qrhid3d11_p.h | 76 + src/gui/rhi/qrhid3d11_p_p.h | 624 ++++ src/gui/rhi/qrhigles2.cpp | 2975 ++++++++++++++++++ src/gui/rhi/qrhigles2_p.h | 84 + src/gui/rhi/qrhigles2_p_p.h | 695 +++++ src/gui/rhi/qrhimetal.mm | 3249 +++++++++++++++++++ src/gui/rhi/qrhimetal_p.h | 80 + src/gui/rhi/qrhimetal_p_p.h | 410 +++ src/gui/rhi/qrhinull.cpp | 709 +++++ src/gui/rhi/qrhinull_p.h | 69 + src/gui/rhi/qrhinull_p_p.h | 279 ++ src/gui/rhi/qrhiprofiler.cpp | 603 ++++ src/gui/rhi/qrhiprofiler_p.h | 120 + src/gui/rhi/qrhiprofiler_p_p.h | 121 + src/gui/rhi/qrhivulkan.cpp | 5681 ++++++++++++++++++++++++++++++++++ src/gui/rhi/qrhivulkan_p.h | 85 + src/gui/rhi/qrhivulkan_p_p.h | 859 +++++ src/gui/rhi/qrhivulkanext_p.h | 81 + src/gui/rhi/qshader.cpp | 586 ++++ src/gui/rhi/qshader_p.h | 227 ++ src/gui/rhi/qshader_p_p.h | 84 + src/gui/rhi/qshaderdescription.cpp | 1088 +++++++ src/gui/rhi/qshaderdescription_p.h | 278 ++ src/gui/rhi/qshaderdescription_p_p.h | 95 + src/gui/rhi/rhi.pri | 57 + 30 files changed, 29414 insertions(+) create mode 100644 src/gui/rhi/qrhi.cpp create mode 100644 src/gui/rhi/qrhi_p.h create mode 100644 src/gui/rhi/qrhi_p_p.h create mode 100644 src/gui/rhi/qrhid3d11.cpp create mode 100644 src/gui/rhi/qrhid3d11_p.h create mode 100644 src/gui/rhi/qrhid3d11_p_p.h create mode 100644 src/gui/rhi/qrhigles2.cpp create mode 100644 src/gui/rhi/qrhigles2_p.h create mode 100644 src/gui/rhi/qrhigles2_p_p.h create mode 100644 src/gui/rhi/qrhimetal.mm create mode 100644 src/gui/rhi/qrhimetal_p.h create mode 100644 src/gui/rhi/qrhimetal_p_p.h create mode 100644 src/gui/rhi/qrhinull.cpp create mode 100644 src/gui/rhi/qrhinull_p.h create mode 100644 src/gui/rhi/qrhinull_p_p.h create mode 100644 src/gui/rhi/qrhiprofiler.cpp create mode 100644 src/gui/rhi/qrhiprofiler_p.h create mode 100644 src/gui/rhi/qrhiprofiler_p_p.h create mode 100644 src/gui/rhi/qrhivulkan.cpp create mode 100644 src/gui/rhi/qrhivulkan_p.h create mode 100644 src/gui/rhi/qrhivulkan_p_p.h create mode 100644 src/gui/rhi/qrhivulkanext_p.h create mode 100644 src/gui/rhi/qshader.cpp create mode 100644 src/gui/rhi/qshader_p.h create mode 100644 src/gui/rhi/qshader_p_p.h create mode 100644 src/gui/rhi/qshaderdescription.cpp create mode 100644 src/gui/rhi/qshaderdescription_p.h create mode 100644 src/gui/rhi/qshaderdescription_p_p.h create mode 100644 src/gui/rhi/rhi.pri (limited to 'src') diff --git a/src/gui/gui.pro b/src/gui/gui.pro index edf8124081..45c8c05162 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -49,6 +49,7 @@ qtConfig(animation): include(animation/animation.pri) include(itemmodels/itemmodels.pri) include(vulkan/vulkan.pri) include(platform/platform.pri) +include(rhi/rhi.pri) QMAKE_LIBS += $$QMAKE_LIBS_GUI diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp new file mode 100644 index 0000000000..8cd4813723 --- /dev/null +++ b/src/gui/rhi/qrhi.cpp @@ -0,0 +1,4891 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Gui module +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qrhi_p_p.h" +#include + +#include "qrhinull_p_p.h" +#ifndef QT_NO_OPENGL +#include "qrhigles2_p_p.h" +#endif +#if QT_CONFIG(vulkan) +#include "qrhivulkan_p_p.h" +#endif +#ifdef Q_OS_WIN +#include "qrhid3d11_p_p.h" +#endif +//#ifdef Q_OS_DARWIN +#ifdef Q_OS_MACOS +#include "qrhimetal_p_p.h" +#endif + +QT_BEGIN_NAMESPACE + +/*! + \class QRhi + \inmodule QtRhi + + \brief Accelerated 2D/3D graphics API abstraction. + + The Qt Rendering Hardware Interface is an abstraction for hardware accelerated + graphics APIs, such as, \l{https://www.khronos.org/opengl/}{OpenGL}, + \l{https://www.khronos.org/opengles/}{OpenGL ES}, + \l{https://docs.microsoft.com/en-us/windows/desktop/direct3d}{Direct3D}, + \l{https://developer.apple.com/metal/}{Metal}, and + \l{https://www.khronos.org/vulkan/}{Vulkan}. + + Some of the main design goals are: + + \list + + \li Simple, minimal, understandable, extensible. Follow the proven path of the + Qt Quick scenegraph. + + \li Aim to be a product - and in the bigger picture, part of a product (Qt) - + that is usable out of the box both by internal (such as, Qt Quick) and, + eventually, external users. + + \li Not a complete 1:1 wrapper for any of the underlying APIs. The feature set + is tuned towards the needs of Qt's 2D and 3D offering (QPainter, Qt Quick, Qt + 3D Studio). Iterate and evolve in a sustainable manner. + + \li Intrinsically cross-platform, without reinventing: abstracting + cross-platform aspects of certain APIs (such as, OpenGL context creation and + windowing system interfaces, Vulkan instance and surface management) is not in + scope here. These are delegated to the existing QtGui facilities (QWindow, + QOpenGLContext, QVulkanInstance) and its backing QPA architecture. + + \endlist + + Each QRhi instance is backed by a backend for a specific graphics API. The + selection of the backend is a run time choice and is up to the application + or library that creates the QRhi instance. Some backends are available on + multiple platforms (OpenGL, Vulkan, Null), while APIs specific to a given + platform are only available when running on the platform in question (Metal + on macOS/iOS/tvOS, Direct3D on Windows). + + The available backends currently are: + + \list + + \li OpenGL 2.1 or OpenGL ES 2.0 or newer. Some extensions are utilized when + present, for example to enable multisample framebuffers. + + \li Direct3D 11.1 + + \li Metal + + \li Vulkan 1.0, optionally with some extensions that are part of Vulkan 1.1 + + \li Null - A "dummy" backend that issues no graphics calls at all. + + \endlist + + In order to allow shader code to be written once in Qt applications and + libraries, all shaders are expected to be written in a single language + which is then compiled into SPIR-V. Versions for various shading language + are then generated from that, together with reflection information (inputs, + outputs, shader resources). This is then packed into easily and efficiently + serializable QShader instances. The compilers and tools to generate such + shaders are not part of QRhi, but the core classes for using such shaders, + QShader and QShaderDescription, are. + + \section2 Design Fundamentals + + A QRhi cannot be instantiated directly. Instead, use the create() + function. Delete the QRhi instance normally to release the graphics device. + + \section3 Resources + + Instances of classes deriving from QRhiResource, such as, QRhiBuffer, + QRhiTexture, etc., encapsulate zero, one, or more native graphics + resources. Instances of such classes are always created via the \c new + functions of the QRhi, such as, newBuffer(), newTexture(), + newTextureRenderTarget(), newSwapChain(). + + \badcode + vbuf = rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData)); + if (!vbuf->build()) { error } + ... + delete vbuf; + \endcode + + \list + + \li The returned value from both create() and functions like newBuffer() is + owned by the caller. + + \li Just creating a QRhiResource subclass never allocates or initializes any + native resources. That is only done when calling the \c build function of a + subclass, for example, QRhiBuffer::build() or QRhiTexture::build(). + + \li The exception is + QRhiTextureRenderTarget::newCompatibleRenderPassDescriptor() and + QRhiSwapChain::newCompatibleRenderPassDescriptor(). There is no \c build + operation for these and the returned object is immediately active. + + \li The resource objects themselves are treated as immutable: once a + resource is built, changing any parameters via the setters, such as, + QRhiTexture::setPixelSize(), has no effect, unless the underlying native + resource is released and \c build is called again. See more about resource + reuse in the sections below. + + \li The underlying native resources are scheduled for releasing by the + QRhiResource destructor, or by calling QRhiResource::release(). Backends + often queue release requests and defer executing them to an unspecified + time, this is hidden from the applications. This way applications do not + have to worry about releasing native resources that may still be in use by + an in-flight frame. + + \li Note that this does not mean that a QRhiResource can freely be + destroyed or release()'d within a frame (that is, in a + \l{QRhiCommandBuffer::beginFrame()}{beginFrame()} - + \l{QRhiCommandBuffer::endFrame()}{endFrame()} section). As a general rule, + all referenced QRhiResource objects must stay unchanged until the frame is + submitted by calling \l{QRhiCommandBuffer::endFrame()}{endFrame()}. To ease + this, QRhiResource::releaseAndDestroyLater() is provided as a convenience. + + \endlist + + \section3 Command buffers and deferred command execution + + Regardless of the design and capabilities of the underlying graphics API, + all QRhi backends implement some level of command buffers. No + QRhiCommandBuffer function issues any native bind or draw command (such as, + \c glDrawElements) directly. Commands are always recorded in a queue, + either native or provided by the QRhi backend. The command buffer is + submitted, and so execution starts only upon QRhi::endFrame() or + QRhi::finish(). + + The deferred nature has consequences for some types of objects. For example, + writing to a dynamic buffer multiple times within a frame, in case such + buffers are backed by host-visible memory, will result in making the + results of all writes are visible to all draw calls in the command buffer + of the frame, regardless of when the dynamic buffer update was recorded + relative to a draw call. + + Furthermore, instances of QRhiResource subclasses must be treated immutable + within a frame in which they are referenced in any way. Create or rebuild + all resources upfront, before starting to record commands for the next + frame. Reusing a QRhiResource instance within a frame (by rebuilding it and + then referencing it again in the same \c{beginFrame - endFrame} section) + should be avoided as it may lead to unexpected results, depending on the + backend. + + As a general rule, all referenced QRhiResource objects must stay valid and + unmodified until the frame is submitted by calling + \l{QRhiCommandBuffer::endFrame()}{endFrame()}. On the other hand, calling + \l{QRhiResource::release()}{release()} or destroying the QRhiResource are + always safe once the frame is submitted, regardless of the status of the + underlying native resources (which may still be in use by the GPU - but + that is taken care of internally). + + Unlike APIs like OpenGL, upload and copy type of commands cannot be mixed + with draw commands. The typical renderer will involve a sequence similar to + the following: \c{(re)build resources} - \c{begin frame} - \c{record + uploads and copies} - \c{start renderpass} - \c{record draw calls} - \c{end + renderpass} - \c{end frame}. Recording copy type of operations happens via + QRhiResourceUpdateBatch. Such operations are committed typically on + \l{QRhiCommandBuffer::beginPass()}{beginPass()}. + + When working with legacy rendering engines designed for OpenGL, the + migration to QRhi often involves redesigning from having a single \c render + step (that performs copies and uploads, clears buffers, and issues draw + calls, all mixed together) to a clearly separated, two phase \c prepare - + \c render setup where the \c render step only starts a renderpass and + records draw calls, while all resource creation and queuing of updates, + uploads and copies happens beforehand, in the \c prepare step. + + QRhi does not at the moment allow freely creating and submitting command + buffers. This may be lifted in the future to some extent, in particular if + compute support is introduced, but the model of well defined + \c{frame-start} and \c{frame-end} points, combined with a dedicated, + "frame" command buffer, where \c{frame-end} implies presenting, is going to + remain the primary way of operating since this is what fits Qt's various UI + technologies best. + + \section3 Threading + + A QRhi instance and the associated resources can be created and used on any + thread but all usage must be limited to that one single thread. When + rendering to multiple QWindows in an application, having a dedicated thread + and QRhi instance for each window is often advisable, as this can eliminate + issues with unexpected throttling caused by presenting to multiple windows. + Conceptually that is then the same as how Qt Quick scene graph's threaded + render loop operates when working directly with OpenGL: one thread for each + window, one QOpenGLContext for each thread. When moving onto QRhi, + QOpenGLContext is replaced by QRhi, making the migration straightforward. + + When it comes to externally created native objects, such as OpenGL contexts + passed in via QRhiGles2NativeHandles, it is up to the application to ensure + they are not misused by other threads. + + Resources are not shareable between QRhi instances. This is an intentional + choice since QRhi hides most queue, command buffer, and resource + synchronization related tasks, and provides no API for them. Safe and + efficient concurrent use of graphics resources from multiple threads is + tied to those concepts, however, and is thus a topic that is currently out + of scope, but may be introduced in the future. + + \section3 Resource synchronization + + QRhi does not expose APIs for resource barriers or image layout + transitions. Such synchronization is done implicitly by the backends, where + applicable (for example, Vulkan), by tracking resource usage as necessary. + + \section3 Resource reuse + + From the user's point of view a QRhiResource is reusable immediately after + calling QRhiResource::release(). With the exception of swapchains, calling + \c build() on an already built object does an implicit \c release(). This + provides a handy shortcut to reuse a QRhiResource instance with different + parameters, with a new native graphics object underneath. + + The importance of reusing the same object lies in the fact that some + objects reference other objects: for example, a QRhiShaderResourceBindings + can reference QRhiBuffer, QRhiTexture, and QRhiSampler instances. If in a + later frame one of these buffers need to be resized or a sampler parameter + needs changing, destroying and creating a whole new QRhiBuffer or + QRhiSampler would invalidate all references to the old instance. By just + changing the appropriate parameters via QRhiBuffer::setSize() or similar + and then calling QRhiBuffer::build(), everything works as expected and + there is no need to touch the QRhiShaderResourceBindings at all, even + though there is a good chance that under the hood the QRhiBuffer is now + backed by a whole new native buffer. + + \badcode + ubuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 256); + ubuf->build(); + + srb = rhi->newShaderResourceBindings() + srb->setBindings({ + QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, ubuf) + }); + srb->build(); + + ... + + // now in a later frame we need to grow the buffer to a larger size + ubuf->setSize(512); + ubuf->build(); // same as ubuf->release(); ubuf->build(); + + // that's it, srb needs no changes whatsoever + \endcode + + \section3 Pooled objects + + In addition to resources, there are pooled objects as well, such as, + QRhiResourceUpdateBatch. An instance is retrieved via a \c next function, + such as, nextResourceUpdateBatch(). The caller does not own the returned + instance in this case. The only valid way of operating here is calling + functions on the QRhiResourceUpdateBatch and then passing it to + QRhiCommandBuffer::beginPass() or QRhiCommandBuffer::endPass(). These + functions take care of returning the batch to the pool. Alternatively, a + batch can be "canceled" and returned to the pool without processing by + calling QRhiResourceUpdateBatch::release(). + + A typical pattern is thus: + + \badcode + QRhiResourceUpdateBatch *resUpdates = rhi->nextResourceUpdateBatch(); + ... + resUpdates->updateDynamicBuffer(ubuf, 0, 64, mvp.constData()); + if (!image.isNull()) { + resUpdates->uploadTexture(texture, image); + image = QImage(); + } + ... + QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer(); + cb->beginPass(swapchain->currentFrameRenderTarget(), clearCol, clearDs, resUpdates); + \endcode + + \section3 Swapchain specifics + + QRhiSwapChain features some special semantics due to the peculiar nature of + swapchains. + + \list + + \li It has no \c build but rather a QRhiSwapChain::buildOrResize(). + Repeatedly calling this function is \b not the same as calling + QRhiSwapChain::release() followed by QRhiSwapChain::buildOrResize(). This + is because swapchains often have ways to handle the case where buffers need + to be resized in a manner that is more efficient than a brute force + destroying and recreating from scratch. + + \li An active QRhiSwapChain must be released by calling + \l{QRhiSwapChain::release()}{release()}, or by destroying the object, before + the QWindow's underlying QPlatformWindow, and so the associated native + window object, is destroyed. It should not be postponed because releasing + the swapchain may become problematic (and with some APIs, like Vulkan, is + explicitly disallowed) when the native window is not around anymore, for + example because the QPlatformWindow got destroyed upon getting a + QWindow::close(). Therefore, releasing the swapchain must happen whenever + the targeted QWindow sends the + QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed event. If the event does + not arrive before the destruction of the QWindow - this can happen when + using QCoreApplication::quit() -, then check QWindow::handle() after the + event loop exits and invoke the swapchain release when non-null (meaning + the underlying native window is still around). + + \endlist + + \section3 Ownership + + The general rule is no ownership transfer. Creating a QRhi with an already + existing graphics device does not mean the QRhi takes ownership of the + device object. Similarly, ownership is not given away when a device or + texture object is "exported" via QRhi::nativeHandles() or + QRhiTexture::nativeHandles(). Most importantly, passing pointers in structs + and via setters does not transfer ownership. + */ + +/*! + \enum QRhi::Implementation + Describes which graphics API-specific backend gets used by a QRhi instance. + + \value Null + \value Vulkan + \value OpenGLES2 + \value D3D11 + \value Metal + */ + +/*! + \enum QRhi::Flag + Describes what special features to enable. + + \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. + + \value EnableDebugMarkers Enables debug marker groups. Without this frame + debugging features like making debug groups and custom resource name + visible in external GPU debugging tools will not be available and functions + like QRhiCommandBuffer::debugMarkBegin() will become a no-op. Avoid + enabling in production builds as it may involve a performance penalty. + */ + +/*! + \enum QRhi::FrameOpResult + Describes the result of operations that can have a soft failure. + + \value FrameOpSuccess Success + + \value FrameOpError Unspecified error + + \value FrameOpSwapChainOutOfDate The swapchain is in an inconsistent state + internally. This can be recoverable by attempting to repeat the operation + (such as, beginFrame()) later. + + \value FrameOpDeviceLost The graphics device was lost. This can be + recoverable by attempting to repeat the operation (such as, beginFrame()) + and releasing and reinitializing all objects backed by native graphics + resources. + */ + +/*! + \enum QRhi::Feature + Flag values to indicate what features are supported by the backend currently in use. + + \value MultisampleTexture Indicates that textures with a sample count larger + than 1 are supported. + + \value MultisampleRenderBuffer Indicates that renderbuffers with a sample + count larger than 1 are supported. + + \value DebugMarkers Indicates that debug marker groups (and so + QRhiCommandBuffer::debugMarkBegin()) are supported. + + \value Timestamps Indicates that command buffer timestamps are supported. + Relevant for QRhiProfiler::gpuFrameTimes(). + + \value Instancing Indicates that instanced drawing is supported. + + \value CustomInstanceStepRate Indicates that instance step rates other than + 1 are supported. + + \value PrimitiveRestart Indicates that restarting the assembly of + primitives when encountering an index value of 0xFFFF + (\l{QRhiCommandBuffer::IndexUInt16}{IndexUInt16}) or 0xFFFFFFFF + (\l{QRhiCommandBuffer::IndexUInt32}{IndexUInt32}) is enabled, for certain + primitive topologies at least. QRhi will try to enable this with all + backends, but in some cases it will not be supported. Dynamically + controlling primitive restart is not possible since with some APIs + primitive restart with a fixed index is always on. Applications must assume + that whenever this feature is reported as supported, the above mentioned + index values \c may be treated specially, depending on the topology. The + only two topologies where primitive restart is guaranteed to behave + identically across backends, as long as this feature is reported as + supported, are \l{QRhiGraphicsPipeline::LineStrip}{LineStrip} and + \l{QRhiGraphicsPipeline::TriangleStrip}{TriangleStrip}. + + \value NonDynamicUniformBuffers Indicates that creating buffers with the + usage \l{QRhiBuffer::UniformBuffer}{UniformBuffer} and the types + \l{QRhiBuffer::Immutable}{Immutable} or \l{QRhiBuffer::Static}{Static} is + supported. When reported as unsupported, uniform (constant) buffers must be + created as \l{QRhiBuffer::Dynamic}{Dynamic}. (which is recommended + regardless) + + \value NonFourAlignedEffectiveIndexBufferOffset Indicates that effective + index buffer offsets (\c{indexOffset + firstIndex * indexComponentSize}) + that are not 4 byte aligned are supported. When not supported, attempting + to issue a \l{QRhiCommandBuffer::drawIndexed()}{drawIndexed()} with a + non-aligned effective offset may lead to unspecified behavior. + + \value NPOTTextureRepeat Indicates that the \l{QRhiSampler::Repeat}{Repeat} + mode is supported for textures with a non-power-of-two size. + + \value RedOrAlpha8IsRed Indicates that the + \l{QRhiTexture::RED_OR_ALPHA8}{RED_OR_ALPHA8} format maps to a one + component 8-bit \c red format. This is the case for all backends except + OpenGL, where \c{GL_ALPHA}, a one component 8-bit \c alpha format, is used + instead. This is relevant for shader code that samples from the texture. + + \value ElementIndexUint Indicates that 32-bit unsigned integer elements are + supported in the index buffer. In practice this is true everywhere except + when running on plain OpenGL ES 2.0 implementations without the necessary + extension. When false, only 16-bit unsigned elements are supported in the + index buffer. + */ + +/*! + \enum QRhi::BeginFrameFlag + Flag values for QRhi::beginFrame() + */ + +/*! + \enum QRhi::EndFrameFlag + Flag values for QRhi::endFrame() + + \value SkipPresent Specifies that no present command is to be queued or no + swapBuffers call is to be made. This way no image is presented. Generating + multiple frames with all having this flag set is not recommended (except, + for example, for benchmarking purposes - but keep in mind that backends may + behave differently when it comes to waiting for command completion without + presenting so the results are not comparable between them) + */ + +/*! + \enum QRhi::ResourceLimit + Describes the resource limit to query. + + \value TextureSizeMin Minimum texture width and height. This is typically + 1. The minimum texture size is handled gracefully, meaning attempting to + create a texture with an empty size will instead create a texture with the + minimum size. + + \value TextureSizeMax Maximum texture width and height. This depends on the + graphics API and sometimes the platform or implementation as well. + Typically the value is in the range 4096 - 16384. Attempting to create + textures larger than this is expected to fail. + + \value MaxColorAttachments The maximum number of color attachments for a + QRhiTextureRenderTarget, in case multiple render targets are supported. When + MRT is not supported, the value is 1. Otherwise this is typically 8, but + watch out for the fact that OpenGL only mandates 4 as the minimum, and that + is what some OpenGL ES implementations provide. + + \value FramesInFlight The number of frames the backend may keep "in + flight". The value has no relevance, and is unspecified, with backends like + OpenGL and Direct3D 11. With backends like Vulkan or Metal, it is the + responsibility of QRhi to block whenever starting a new frame and finding + the CPU is already \c{N - 1} frames ahead of the GPU (because the command + buffer submitted in frame no. \c{current} - \c{N} has not yet completed). + The value N is what is returned from here, and is typically 2. This can be + relevant to applications that integrate rendering done directly with the + graphics API, as such rendering code may want to perform double (if the + value is 2) buffering for resources, such as, buffers, similarly to the + QRhi backends themselves. The current frame slot index (a value running 0, + 1, .., N-1, then wrapping around) is retrievable from + QRhi::currentFrameSlot(). + */ + +/*! + \class QRhiInitParams + \inmodule QtRhi + \brief Base class for backend-specific initialization parameters. + + Contains fields that are relevant to all backends. + */ + +/*! + \class QRhiDepthStencilClearValue + \inmodule QtRhi + \brief Specifies clear values for a depth or stencil buffer. + */ + +/*! + \fn QRhiDepthStencilClearValue::QRhiDepthStencilClearValue() + + Constructs a depth/stencil clear value with depth clear value 1.0f and + stencil clear value 0. + */ + +/*! + Constructs a depth/stencil clear value with depth clear value \a d and + stencil clear value \a s. + */ +QRhiDepthStencilClearValue::QRhiDepthStencilClearValue(float d, quint32 s) + : m_d(d), + m_s(s) +{ +} + +/*! + \return \c true if the values in the two QRhiDepthStencilClearValue objects + \a a and \a b are equal. + + \relates QRhiDepthStencilClearValue + */ +bool operator==(const QRhiDepthStencilClearValue &a, const QRhiDepthStencilClearValue &b) Q_DECL_NOTHROW +{ + return a.depthClearValue() == b.depthClearValue() + && a.stencilClearValue() == b.stencilClearValue(); +} + +/*! + \return \c false if the values in the two QRhiDepthStencilClearValue + objects \a a and \a b are equal; otherwise returns \c true. + + \relates QRhiDepthStencilClearValue +*/ +bool operator!=(const QRhiDepthStencilClearValue &a, const QRhiDepthStencilClearValue &b) Q_DECL_NOTHROW +{ + return !(a == b); +} + +/*! + \return the hash value for \a v, using \a seed to seed the calculation. + + \relates QRhiDepthStencilClearValue + */ +uint qHash(const QRhiDepthStencilClearValue &v, uint seed) Q_DECL_NOTHROW +{ + return seed * (qFloor(v.depthClearValue() * 100) + v.stencilClearValue()); +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QRhiDepthStencilClearValue &v) +{ + QDebugStateSaver saver(dbg); + dbg.nospace() << "QRhiDepthStencilClearValue(depth-clear=" << v.depthClearValue() + << " stencil-clear=" << v.stencilClearValue() + << ')'; + return dbg; +} +#endif + +/*! + \class QRhiViewport + \inmodule QtRhi + \brief Specifies a viewport rectangle. + + Used with QRhiCommandBuffer::setViewport(). + + \note QRhi assumes OpenGL-style viewport coordinates, meaning x and y are + bottom-left. + + Typical usage is like the following: + + \badcode + const QSize outputSizeInPixels = swapchain->currentPixelSize(); + const QRhiViewport viewport(0, 0, outputSizeInPixels.width(), outputSizeInPixels.height()); + cb->beginPass(swapchain->currentFrameRenderTarget(), { 0, 0, 0, 1 }, { 1, 0 }); + cb->setGraphicsPipeline(ps); + cb->setViewport(viewport); + ... + \endcode + + \sa QRhiCommandBuffer::setViewport(), QRhi::clipSpaceCorrMatrix(), QRhiScissor + */ + +/*! + \fn QRhiViewport::QRhiViewport() + + Constructs a viewport description with an empty rectangle and a depth range + of 0.0f - 1.0f. + + \sa QRhi::clipSpaceCorrMatrix() + */ + +/*! + Constructs a viewport description with the rectangle specified by \a x, \a + y, \a w, \a h and the depth range \a minDepth and \a maxDepth. + + \note x and y are assumed to be the bottom-left position. + + \sa QRhi::clipSpaceCorrMatrix() + */ +QRhiViewport::QRhiViewport(float x, float y, float w, float h, float minDepth, float maxDepth) + : m_rect { { x, y, w, h } }, + m_minDepth(minDepth), + m_maxDepth(maxDepth) +{ +} + +/*! + \return \c true if the values in the two QRhiViewport objects + \a a and \a b are equal. + + \relates QRhiViewport + */ +bool operator==(const QRhiViewport &a, const QRhiViewport &b) Q_DECL_NOTHROW +{ + return a.viewport() == b.viewport() + && a.minDepth() == b.minDepth() + && a.maxDepth() == b.maxDepth(); +} + +/*! + \return \c false if the values in the two QRhiViewport + objects \a a and \a b are equal; otherwise returns \c true. + + \relates QRhiViewport +*/ +bool operator!=(const QRhiViewport &a, const QRhiViewport &b) Q_DECL_NOTHROW +{ + return !(a == b); +} + +/*! + \return the hash value for \a v, using \a seed to seed the calculation. + + \relates QRhiViewport + */ +uint qHash(const QRhiViewport &v, uint seed) Q_DECL_NOTHROW +{ + const std::array r = v.viewport(); + return seed + r[0] + r[1] + r[2] + r[3] + qFloor(v.minDepth() * 100) + qFloor(v.maxDepth() * 100); +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QRhiViewport &v) +{ + QDebugStateSaver saver(dbg); + const std::array r = v.viewport(); + dbg.nospace() << "QRhiViewport(bottom-left-x=" << r[0] + << " bottom-left-y=" << r[1] + << " width=" << r[2] + << " height=" << r[3] + << " minDepth=" << v.minDepth() + << " maxDepth=" << v.maxDepth() + << ')'; + return dbg; +} +#endif + +/*! + \class QRhiScissor + \inmodule QtRhi + \brief Specifies a scissor rectangle. + + Used with QRhiCommandBuffer::setScissor(). Setting a scissor rectangle is + only possible with a QRhiGraphicsPipeline that has + QRhiGraphicsPipeline::UsesScissor set. + + \note QRhi assumes OpenGL-style scissor coordinates, meaning x and y are + bottom-left. + + \sa QRhiCommandBuffer::setScissor(), QRhiViewport + */ + +/*! + \fn QRhiScissor::QRhiScissor() + + Constructs an empty scissor. + */ + +/*! + Constructs a scissor with the rectangle specified by \a x, \a y, \a w, and + \a h. + + \note x and y are assumed to be the bottom-left position. + */ +QRhiScissor::QRhiScissor(int x, int y, int w, int h) + : m_rect { { x, y, w, h } } +{ +} + +/*! + \return \c true if the values in the two QRhiScissor objects + \a a and \a b are equal. + + \relates QRhiScissor + */ +bool operator==(const QRhiScissor &a, const QRhiScissor &b) Q_DECL_NOTHROW +{ + return a.scissor() == b.scissor(); +} + +/*! + \return \c false if the values in the two QRhiScissor + objects \a a and \a b are equal; otherwise returns \c true. + + \relates QRhiScissor +*/ +bool operator!=(const QRhiScissor &a, const QRhiScissor &b) Q_DECL_NOTHROW +{ + return !(a == b); +} + +/*! + \return the hash value for \a v, using \a seed to seed the calculation. + + \relates QRhiScissor + */ +uint qHash(const QRhiScissor &v, uint seed) Q_DECL_NOTHROW +{ + const std::array r = v.scissor(); + return seed + r[0] + r[1] + r[2] + r[3]; +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QRhiScissor &s) +{ + QDebugStateSaver saver(dbg); + const std::array r = s.scissor(); + dbg.nospace() << "QRhiScissor(bottom-left-x=" << r[0] + << " bottom-left-y=" << r[1] + << " width=" << r[2] + << " height=" << r[3] + << ')'; + return dbg; +} +#endif + +/*! + \class QRhiVertexInputBinding + \inmodule QtRhi + \brief Describes a vertex input binding. + + Specifies the stride (in bytes, must be a multiple of 4), the + classification and optionally the instance step rate. + + As an example, assume a vertex shader with the following inputs: + + \badcode + layout(location = 0) in vec4 position; + layout(location = 1) in vec2 texcoord; + \endcode + + Now let's assume also that 3 component vertex positions \c{(x, y, z)} and 2 + component texture coordinates \c{(u, v)} are provided in a non-interleaved + format in a buffer (or separate buffers even). Definining two bindings + could then be done like this: + + \badcode + QRhiVertexInputLayout inputLayout; + inputLayout.setBindings({ + { 3 * sizeof(float) }, + { 2 * sizeof(float) } + }); + \endcode + + Only the stride is interesting here since instancing is not used. The + binding number is given by the index of the QRhiVertexInputBinding + element in the bindings vector of the QRhiVertexInputLayout. + + Once a graphics pipeline with this vertex input layout is bound, the vertex + inputs could be set up like the following for drawing a cube with 36 + vertices, assuming we have a single buffer with first the positions and + then the texture coordinates: + + \badcode + const QRhiCommandBuffer::VertexInput vbufBindings[] = { + { cubeBuf, 0 }, + { cubeBuf, 36 * 3 * sizeof(float) } + }; + cb->setVertexInput(0, 2, vbufBindings); + \endcode + + Note how the index defined by \c {startBinding + i}, where \c i is the + index in the second argument of + \l{QRhiCommandBuffer::setVertexInput()}{setVertexInput()}, matches the + index of the corresponding entry in the \c bindings vector of the + QRhiVertexInputLayout. + + \note the stride must always be a multiple of 4. + + \sa QRhiCommandBuffer::setVertexInput() + */ + +/*! + \enum QRhiVertexInputBinding::Classification + Describes the input data classification. + + \value PerVertex Data is per-vertex + \value PerInstance Data is per-instance + */ + +/*! + \fn QRhiVertexInputBinding::QRhiVertexInputBinding() + + Constructs a default vertex input binding description. + */ + +/*! + Constructs a vertex input binding description with the specified \a stride, + classification \a cls, and instance step rate \a stepRate. + + \note \a stepRate other than 1 is only supported when + QRhi::CustomInstanceStepRate is reported to be supported. + */ +QRhiVertexInputBinding::QRhiVertexInputBinding(quint32 stride, Classification cls, int stepRate) + : m_stride(stride), + m_classification(cls), + m_instanceStepRate(stepRate) +{ +} + +/*! + \return \c true if the values in the two QRhiVertexInputBinding objects + \a a and \a b are equal. + + \relates QRhiVertexInputBinding + */ +bool operator==(const QRhiVertexInputBinding &a, const QRhiVertexInputBinding &b) Q_DECL_NOTHROW +{ + return a.stride() == b.stride() + && a.classification() == b.classification() + && a.instanceStepRate() == b.instanceStepRate(); +} + +/*! + \return \c false if the values in the two QRhiVertexInputBinding + objects \a a and \a b are equal; otherwise returns \c true. + + \relates QRhiVertexInputBinding +*/ +bool operator!=(const QRhiVertexInputBinding &a, const QRhiVertexInputBinding &b) Q_DECL_NOTHROW +{ + return !(a == b); +} + +/*! + \return the hash value for \a v, using \a seed to seed the calculation. + + \relates QRhiVertexInputBinding + */ +uint qHash(const QRhiVertexInputBinding &v, uint seed) Q_DECL_NOTHROW +{ + return seed + v.stride() + v.classification(); +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QRhiVertexInputBinding &b) +{ + QDebugStateSaver saver(dbg); + dbg.nospace() << "QRhiVertexInputBinding(stride=" << b.stride() + << " cls=" << b.classification() + << " step-rate=" << b.instanceStepRate() + << ')'; + return dbg; +} +#endif + +/*! + \class QRhiVertexInputAttribute + \inmodule QtRhi + \brief Describes a single vertex input element. + + The members specify the binding number, location, format, and offset for a + single vertex input element. + + \note For HLSL it is assumed that the vertex shader uses + \c{TEXCOORD} as the semantic for each input. Hence no separate + semantic name and index. + + As an example, assume a vertex shader with the following inputs: + + \badcode + layout(location = 0) in vec4 position; + layout(location = 1) in vec2 texcoord; + \endcode + + Now let's assume that we have 3 component vertex positions \c{(x, y, z)} + and 2 component texture coordinates \c{(u, v)} are provided in a + non-interleaved format in a buffer (or separate buffers even). Once two + bindings are defined, the attributes could be specified as: + + \badcode + QRhiVertexInputLayout inputLayout; + inputLayout.setBindings({ + { 3 * sizeof(float) }, + { 2 * sizeof(float) } + }); + inputLayout.setAttributes({ + { 0, 0, QRhiVertexInputAttribute::Float3, 0 }, + { 1, 1, QRhiVertexInputAttribute::Float2, 0 } + }); + \endcode + + Once a graphics pipeline with this vertex input layout is bound, the vertex + inputs could be set up like the following for drawing a cube with 36 + vertices, assuming we have a single buffer with first the positions and + then the texture coordinates: + + \badcode + const QRhiCommandBuffer::VertexInput vbufBindings[] = { + { cubeBuf, 0 }, + { cubeBuf, 36 * 3 * sizeof(float) } + }; + cb->setVertexInput(0, 2, vbufBindings); + \endcode + + When working with interleaved data, there will typically be just one + binding, with multiple attributes referring to that same buffer binding + point: + + \badcode + QRhiVertexInputLayout inputLayout; + inputLayout.setBindings({ + { 5 * sizeof(float) } + }); + inputLayout.setAttributes({ + { 0, 0, QRhiVertexInputAttribute::Float3, 0 }, + { 0, 1, QRhiVertexInputAttribute::Float2, 3 * sizeof(float) } + }); + \endcode + + and then: + + \badcode + const QRhiCommandBuffer::VertexInput vbufBinding(interleavedCubeBuf, 0); + cb->setVertexInput(0, 1, &vbufBinding); + \endcode + + \sa QRhiCommandBuffer::setVertexInput() + */ + +/*! + \enum QRhiVertexInputAttribute::Format + Specifies the type of the element data. + + \value Float4 Four component float vector + \value Float3 Three component float vector + \value Float2 Two component float vector + \value Float Float + \value UNormByte4 Four component normalized unsigned byte vector + \value UNormByte2 Two component normalized unsigned byte vector + \value UNormByte Normalized unsigned byte + */ + +/*! + \fn QRhiVertexInputAttribute::QRhiVertexInputAttribute() + + Constructs a default vertex input attribute description. + */ + +/*! + Constructs a vertex input attribute description with the specified \a + binding number, \a location, \a format, and \a offset. + */ +QRhiVertexInputAttribute::QRhiVertexInputAttribute(int binding, int location, Format format, quint32 offset) + : m_binding(binding), + m_location(location), + m_format(format), + m_offset(offset) +{ +} + +/*! + \return \c true if the values in the two QRhiVertexInputAttribute objects + \a a and \a b are equal. + + \relates QRhiVertexInputAttribute + */ +bool operator==(const QRhiVertexInputAttribute &a, const QRhiVertexInputAttribute &b) Q_DECL_NOTHROW +{ + return a.binding() == b.binding() + && a.location() == b.location() + && a.format() == b.format() + && a.offset() == b.offset(); +} + +/*! + \return \c false if the values in the two QRhiVertexInputAttribute + objects \a a and \a b are equal; otherwise returns \c true. + + \relates QRhiVertexInputAttribute +*/ +bool operator!=(const QRhiVertexInputAttribute &a, const QRhiVertexInputAttribute &b) Q_DECL_NOTHROW +{ + return !(a == b); +} + +/*! + \return the hash value for \a v, using \a seed to seed the calculation. + + \relates QRhiVertexInputAttribute + */ +uint qHash(const QRhiVertexInputAttribute &v, uint seed) Q_DECL_NOTHROW +{ + return seed + v.binding() + v.location() + v.format() + v.offset(); +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QRhiVertexInputAttribute &a) +{ + QDebugStateSaver saver(dbg); + dbg.nospace() << "QRhiVertexInputAttribute(binding=" << a.binding() + << " location=" << a.location() + << " format=" << a.format() + << " offset=" << a.offset() + << ')'; + return dbg; +} +#endif + +/*! + \class QRhiVertexInputLayout + \inmodule QtRhi + \brief Describes the layout of vertex inputs consumed by a vertex shader. + + The vertex input layout is defined by the collections of + QRhiVertexInputBinding and QRhiVertexInputAttribute. + */ + +/*! + \fn QRhiVertexInputLayout::QRhiVertexInputLayout() + + Constructs an empty vertex input layout description. + */ + +/*! + \return \c true if the values in the two QRhiVertexInputLayout objects + \a a and \a b are equal. + + \relates QRhiVertexInputLayout + */ +bool operator==(const QRhiVertexInputLayout &a, const QRhiVertexInputLayout &b) Q_DECL_NOTHROW +{ + return a.bindings() == b.bindings() + && a.attributes() == b.attributes(); +} + +/*! + \return \c false if the values in the two QRhiVertexInputLayout + objects \a a and \a b are equal; otherwise returns \c true. + + \relates QRhiVertexInputLayout +*/ +bool operator!=(const QRhiVertexInputLayout &a, const QRhiVertexInputLayout &b) Q_DECL_NOTHROW +{ + return !(a == b); +} + +/*! + \return the hash value for \a v, using \a seed to seed the calculation. + + \relates QRhiVertexInputLayout + */ +uint qHash(const QRhiVertexInputLayout &v, uint seed) Q_DECL_NOTHROW +{ + return qHash(v.bindings(), seed) + qHash(v.attributes(), seed); +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QRhiVertexInputLayout &v) +{ + QDebugStateSaver saver(dbg); + dbg.nospace() << "QRhiVertexInputLayout(bindings=" << v.bindings() + << " attributes=" << v.attributes() + << ')'; + return dbg; +} +#endif + +/*! + \class QRhiGraphicsShaderStage + \inmodule QtRhi + \brief Specifies the type and the shader code for a shader stage in the graphics pipeline. + */ + +/*! + \enum QRhiGraphicsShaderStage::Type + Specifies the type of the shader stage. + + \value Vertex Vertex stage + \value Fragment Fragment (pixel) stage + */ + +/*! + \fn QRhiGraphicsShaderStage::QRhiGraphicsShaderStage() + + Constructs a shader stage description for the vertex stage with an empty + QShader. + */ + +/*! + Constructs a shader stage description with the \a type of the stage and the + \a shader. + + The shader variant \a v defaults to QShader::StandardShader. A + QShader contains multiple source and binary versions of a shader. + In addition, it can also contain variants of the shader with slightly + modified code. \a v can then be used to select the desired variant. + */ +QRhiGraphicsShaderStage::QRhiGraphicsShaderStage(Type type, const QShader &shader, QShader::Variant v) + : m_type(type), + m_shader(shader), + m_shaderVariant(v) +{ +} + +/*! + \return \c true if the values in the two QRhiGraphicsShaderStage objects + \a a and \a b are equal. + + \relates QRhiGraphicsShaderStage + */ +bool operator==(const QRhiGraphicsShaderStage &a, const QRhiGraphicsShaderStage &b) Q_DECL_NOTHROW +{ + return a.type() == b.type() + && a.shader() == b.shader() + && a.shaderVariant() == b.shaderVariant(); +} + +/*! + \return \c false if the values in the two QRhiGraphicsShaderStage + objects \a a and \a b are equal; otherwise returns \c true. + + \relates QRhiGraphicsShaderStage +*/ +bool operator!=(const QRhiGraphicsShaderStage &a, const QRhiGraphicsShaderStage &b) Q_DECL_NOTHROW +{ + return !(a == b); +} + +/*! + \return the hash value for \a v, using \a seed to seed the calculation. + + \relates QRhiGraphicsShaderStage + */ +uint qHash(const QRhiGraphicsShaderStage &v, uint seed) Q_DECL_NOTHROW +{ + return v.type() + qHash(v.shader(), seed) + v.shaderVariant(); +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QRhiGraphicsShaderStage &s) +{ + QDebugStateSaver saver(dbg); + dbg.nospace() << "QRhiGraphicsShaderStage(type=" << s.type() + << " shader=" << s.shader() + << " variant=" << s.shaderVariant() + << ')'; + return dbg; +} +#endif + +/*! + \class QRhiColorAttachment + \inmodule QtRhi + \brief Describes the a single color attachment of a render target. + + A color attachment is either a QRhiTexture or a QRhiRenderBuffer. The + former, when texture() is set, is used in most cases. + + \note texture() and renderBuffer() cannot be both set (be non-null at the + same time). + + Setting renderBuffer instead is recommended only when multisampling is + needed. Relying on QRhi::MultisampleRenderBuffer is a better choice than + QRhi::MultisampleTexture in practice since the former is available in more + run time configurations (e.g. when running on OpenGL ES 3.0 which has no + support for multisample textures, but does support multisample + renderbuffers). + + When targeting a non-multisample texture, the layer() and level() + indicate the targeted layer (face index \c{0-5} for cubemaps) and mip + level. + + When texture() or renderBuffer() is multisample, resolveTexture() can be + set optionally. When set, samples are resolved automatically into that + (non-multisample) texture at the end of the render pass. When rendering + into a multisample renderbuffers, this is the only way to get resolved, + non-multisample content out of them. Multisample textures allow sampling in + shaders so for them this is just one option. + + \note when resolving is enabled, the multisample data may not be written + out at all. This means that the multisample texture() must not be used + afterwards with shaders for sampling when resolveTexture() is set. + */ + +/*! + \fn QRhiColorAttachment::QRhiColorAttachment() + + Constructs an empty color attachment description. + */ + +/*! + Constructs a color attachment description that specifies \a texture as the + associated color buffer. + */ +QRhiColorAttachment::QRhiColorAttachment(QRhiTexture *texture) + : m_texture(texture) +{ +} + +/*! + Constructs a color attachment description that specifies \a renderBuffer as + the associated color buffer. + */ +QRhiColorAttachment::QRhiColorAttachment(QRhiRenderBuffer *renderBuffer) + : m_renderBuffer(renderBuffer) +{ +} + +/*! + \class QRhiTextureRenderTargetDescription + \inmodule QtRhi + \brief Describes the color and depth or depth/stencil attachments of a render target. + + A texture render target has zero or more textures as color attachments, + zero or one renderbuffer as combined depth/stencil buffer or zero or one + texture as depth buffer. + + \note depthStencilBuffer() and depthTexture() cannot be both set (cannot be + non-null at the same time). + */ + +/*! + \fn QRhiTextureRenderTargetDescription::QRhiTextureRenderTargetDescription() + + Constructs an empty texture render target description. + */ + +/*! + Constructs a texture render target description with one attachment + described by \a colorAttachment. + */ +QRhiTextureRenderTargetDescription::QRhiTextureRenderTargetDescription(const QRhiColorAttachment &colorAttachment) +{ + m_colorAttachments.append(colorAttachment); +} + +/*! + Constructs a texture render target description with two attachments, a + color attachment described by \a colorAttachment, and a depth/stencil + attachment with \a depthStencilBuffer. + */ +QRhiTextureRenderTargetDescription::QRhiTextureRenderTargetDescription(const QRhiColorAttachment &colorAttachment, + QRhiRenderBuffer *depthStencilBuffer) + : m_depthStencilBuffer(depthStencilBuffer) +{ + m_colorAttachments.append(colorAttachment); +} + +/*! + Constructs a texture render target description with two attachments, a + color attachment described by \a colorAttachment, and a depth attachment + with \a depthTexture. + + \note \a depthTexture must have a suitable format, such as QRhiTexture::D16 + or QRhiTexture::D32F. + */ +QRhiTextureRenderTargetDescription::QRhiTextureRenderTargetDescription(const QRhiColorAttachment &colorAttachment, + QRhiTexture *depthTexture) + : m_depthTexture(depthTexture) +{ + m_colorAttachments.append(colorAttachment); +} + +/*! + \class QRhiTextureSubresourceUploadDescription + \inmodule QtRhi + \brief Describes the source for one mip level in a layer in a texture upload operation. + + The source content is specified either as a QImage or as a raw blob. The + former is only allowed for uncompressed textures with a format that can be + mapped to QImage, while the latter is supported for all formats, including + floating point and compressed. + + \note image() and data() cannot be both set at the same time. + + destinationTopLeft() specifies the top-left corner of the target + rectangle. Defaults to (0, 0). + + An empty sourceSize() (the default) indicates that size is assumed to be + the size of the subresource. With QImage-based uploads this implies that + the size of the source image() must match the subresource. When providing + raw data instead, sufficient number of bytes must be provided in data(). + + \note With compressed textures the first upload must always match the + subresource size due to graphics API limitations with some backends. + + sourceTopLeft() is supported only for QImage-based uploads, and specifies + the top-left corner of the source rectangle. + + \note Setting sourceSize() or sourceTopLeft() may trigger a QImage copy + internally, depending on the format and the backend. + + When providing raw data, the stride (row pitch, row length in bytes) of the + provided data must be equal to \c{width * pixelSize} where \c pixelSize is + the number of bytes used for one pixel, and there must be no additional + padding between rows. There is no row start alignment requirement. + + \note The format of the source data must be compatible with the texture + format. With many graphics APIs the data is copied as-is into a staging + buffer, there is no intermediate format conversion provided by QRhi. This + applies to floating point formats as well, with, for example, RGBA16F + requiring half floats in the source data. + */ + +/*! + \fn QRhiTextureSubresourceUploadDescription::QRhiTextureSubresourceUploadDescription() + + Constructs an empty subresource description. + + \note an empty QRhiTextureSubresourceUploadDescription is not useful on its + own and should not be submitted to a QRhiTextureUploadEntry. At minimum + image or data must be set first. + */ + +/*! + Constructs a mip level description with a \a image. + + The \l{QImage::size()}{size} of \a image must match the size of the mip + level. For level 0 that is the \l{QRhiTexture::pixelSize()}{texture size}. + + The bit depth of \a image must be compatible with the + \l{QRhiTexture::Format}{texture format}. + + To describe a partial upload, call setSourceSize(), setSourceTopLeft(), or + setDestinationTopLeft() afterwards. + */ +QRhiTextureSubresourceUploadDescription::QRhiTextureSubresourceUploadDescription(const QImage &image) + : m_image(image) +{ +} + +/*! + Constructs a mip level description with the image data is specified by \a + data and \a size. This is suitable for floating point and compressed + formats as well. + + \a data can safely be destroyed or changed once this function returns. + */ +QRhiTextureSubresourceUploadDescription::QRhiTextureSubresourceUploadDescription(const void *data, int size) + : m_data(reinterpret_cast(data), size) +{ +} + +/*! + \class QRhiTextureUploadEntry + \inmodule QtRhi + \brief Describes one layer (face for cubemaps) in a texture upload operation. + */ + +/*! + \fn QRhiTextureUploadEntry::QRhiTextureUploadEntry() + + Constructs an empty QRhiTextureUploadEntry targeting layer 0 and level 0. + + \note an empty QRhiTextureUploadEntry should not be submitted without + setting a QRhiTextureSubresourceUploadDescription via setDescription() + first. + */ + +/*! + Constructs a QRhiTextureUploadEntry targeting the given \a layer and mip + \a level, with the subresource contents described by \a desc. + */ +QRhiTextureUploadEntry::QRhiTextureUploadEntry(int layer, int level, + const QRhiTextureSubresourceUploadDescription &desc) + : m_layer(layer), + m_level(level), + m_desc(desc) +{ +} + +/*! + \class QRhiTextureUploadDescription + \inmodule QtRhi + \brief Describes a texture upload operation. + + Used with QRhiResourceUpdateBatch::uploadTexture(). That function has two + variants: one taking a QImage and one taking a + QRhiTextureUploadDescription. The former is a convenience version, + internally creating a QRhiTextureUploadDescription with a single image + targeting level 0 for layer 0. However, when cubemaps, pre-generated mip + images, or compressed textures are involved, applications will have to work + directly with this class instead. + + QRhiTextureUploadDescription also enables specifying batched uploads, which + are useful for example when generating an atlas or glyph cache texture: + multiple, partial uploads for the same subresource (meaning the same layer + and level) are supported, and can be, depending on the backend and the + underlying graphics API, more efficient when batched into the same + QRhiTextureUploadDescription as opposed to issuing individual + \l{QRhiResourceUpdateBatch::uploadTexture()}{uploadTexture()} commands for + each of them. + + \note Cubemaps have one layer for each of the six faces in the order +X, + -X, +Y, -Y, +Z, -Z. + + For example, specifying the faces of a cubemap could look like the following: + + \badcode + QImage faces[6]; + ... + QVector entries; + for (int i = 0; i < 6; ++i) + entries.append(QRhiTextureUploadEntry(i, 0, faces[i])); + QRhiTextureUploadDescription desc(entries); + resourceUpdates->uploadTexture(texture, desc); + \endcode + + Another example that specifies mip images for a compressed texture: + + \badcode + QRhiTextureUploadDescription desc; + const int mipCount = rhi->mipLevelsForSize(compressedTexture->pixelSize()); + for (int level = 0; level < mipCount; ++level) { + const QByteArray compressedDataForLevel = .. + desc.append(QRhiTextureUploadEntry(0, level, compressedDataForLevel)); + } + resourceUpdates->uploadTexture(compressedTexture, desc); + \endcode + + With partial uploads targeting the same subresource, it is recommended to + batch them into a single upload request, whenever possible: + + \badcode + QRhiTextureSubresourceUploadDescription subresDesc(image); + subresDesc.setSourceSize(QSize(10, 10)); + subResDesc.setDestinationTopLeft(QPoint(50, 40)); + QRhiTextureUploadEntry entry(0, 0, subresDesc); // layer 0, level 0 + + QRhiTextureSubresourceUploadDescription subresDesc2(image); + subresDesc2.setSourceSize(QSize(30, 40)); + subResDesc2.setDestinationTopLeft(QPoint(100, 200)); + QRhiTextureUploadEntry entry2(0, 0, subresDesc2); // layer 0, level 0, i.e. same subresource + + QRhiTextureUploadDescription desc({ entry, entry2}); + resourceUpdates->uploadTexture(texture, desc); + \endcode + */ + +/*! + \fn QRhiTextureUploadDescription::QRhiTextureUploadDescription() + + Constructs an empty texture upload description. + */ + +/*! + Constructs a texture upload description with a single subresource upload + described by \a entry. + */ +QRhiTextureUploadDescription::QRhiTextureUploadDescription(const QRhiTextureUploadEntry &entry) +{ + m_entries.append(entry); +} + +/*! + Constructs a texture upload description with the specified list of \a entries. + + \note \a entries can also contain multiple QRhiTextureUploadEntry elements + with the the same layer and level. This makes sense when those uploads are + partial, meaning their subresource description has a source size or image + smaller than the subresource dimensions, and can be more efficient than + issuing separate uploadTexture()'s. + */ +QRhiTextureUploadDescription::QRhiTextureUploadDescription(const QVector &entries) + : m_entries(entries) +{ +} + +/*! + Adds \a entry to the list of subresource uploads. + */ +void QRhiTextureUploadDescription::append(const QRhiTextureUploadEntry &entry) +{ + m_entries.append(entry); +} + +/*! + \class QRhiTextureCopyDescription + \inmodule QtRhi + \brief Describes a texture-to-texture copy operation. + + An empty pixelSize() indicates that the entire subresource is to be copied. + A default constructed copy description therefore leads to copying the + entire subresource at level 0 of layer 0. + + \note The source texture must be created with + QRhiTexture::UsedAsTransferSource. + + \note The source and destination rectangles defined by pixelSize(), + sourceTopLeft(), and destinationTopLeft() must fit the source and + destination textures, respectively. The behavior is undefined otherwise. + */ + +/*! + \fn QRhiTextureCopyDescription::QRhiTextureCopyDescription() + + Constructs an empty texture copy description. + */ + +/*! + \class QRhiReadbackDescription + \inmodule QtRhi + \brief Describes a readback (reading back texture contents from possibly GPU-only memory) operation. + + The source of the readback operation is either a QRhiTexture or the + current backbuffer of the currently targeted QRhiSwapChain. When + texture() is not set, the swapchain is used. Otherwise the specified + QRhiTexture is treated as the source. + + \note Textures used in readbacks must be created with + QRhiTexture::UsedAsTransferSource. + + \note Swapchains used in readbacks must be created with + QRhiSwapChain::UsedAsTransferSource. + + layer() and level() are only applicable when the source is a QRhiTexture. + + \note Multisample textures cannot be read back. Readbacks are supported for + multisample swapchain buffers however. + */ + +/*! + \fn QRhiReadbackDescription::QRhiReadbackDescription() + + Constructs an empty texture readback description. + + \note The source texture is set to null by default, which is still a valid + readback: it specifies that the backbuffer of the current swapchain is to + be read back. (current meaning the frame's target swapchain at the time of + committing the QRhiResourceUpdateBatch with the + \l{QRhiResourceUpdateBatch::readBackTexture()}{texture readback} on it) + */ + +/*! + Constructs an texture readback description that specifies that level 0 of + layer 0 of \a texture is to be read back. + + \note \a texture can also be null in which case this constructor is + identical to the argumentless variant. + */ +QRhiReadbackDescription::QRhiReadbackDescription(QRhiTexture *texture) + : m_texture(texture) +{ +} + +/*! + \class QRhiReadbackResult + \inmodule QtRhi + \brief Describes the results of a potentially asynchronous readback operation. + + When \l completed is set, the function is invoked when the \l data is + available. \l format and \l pixelSize are set upon completion together with + \l data. + */ + +/*! + \class QRhiNativeHandles + \inmodule QtRhi + \brief Base class for classes exposing backend-specific collections of native resource objects. + */ + +/*! + \class QRhiResource + \inmodule QtRhi + \brief Base class for classes encapsulating native resource objects. + */ + +/*! + \fn QRhiResource::Type QRhiResource::resourceType() const + + \return the type of the resource. + */ + +/*! + \internal + */ +QRhiResource::QRhiResource(QRhiImplementation *rhi) + : m_rhi(rhi) +{ + m_id = QRhiGlobalObjectIdGenerator::newId(); +} + +/*! + Destructor. + + Releases (or requests deferred releasing of) the underlying native graphics + resources, if there are any. + + \note Resources referenced by commands for the current frame should not be + released until the frame is submitted by QRhi::endFrame(). + + \sa release() + */ +QRhiResource::~QRhiResource() +{ + // release() cannot be called here, it being virtual; it is up to the + // subclasses to do that. +} + +/*! + \fn void QRhiResource::release() + + Releases (or requests deferred releasing of) the underlying native graphics + resources. Safe to call multiple times, subsequent invocations will be a + no-op then. + + Once release() is called, the QRhiResource instance can be reused, by + calling \c build() again. That will then result in creating new native + graphics resources underneath. + + \note Resources referenced by commands for the current frame should not be + released until the frame is submitted by QRhi::endFrame(). + + The QRhiResource destructor also performs the same task, so calling this + function is not necessary before destroying a QRhiResource. + + \sa releaseAndDestroyLater() + */ + +/*! + When called without a frame being recorded, this function is equivalent to + deleting the object. Between a QRhi::beginFrame() and QRhi::endFrame() + however the behavior is different: the QRhiResource will not be destroyed + until the frame is submitted via QRhi::endFrame(), thus satisfying the QRhi + requirement of not altering QRhiResource objects that are referenced by the + frame being recorded. + + \sa release() + */ +void QRhiResource::releaseAndDestroyLater() +{ + m_rhi->addReleaseAndDestroyLater(this); +} + +/*! + \return the currently set object name. By default the name is empty. + */ +QByteArray QRhiResource::name() const +{ + return m_objectName; +} + +/*! + Sets a \a name for the object. + + This has two uses: to get descriptive names for the native graphics + resources visible in graphics debugging tools, such as + \l{https://renderdoc.org/}{RenderDoc} and + \l{https://developer.apple.com/xcode/}{XCode}, and in the output stream of + QRhiProfiler. + + When it comes to naming native objects by relaying the name via the + appropriate graphics API, note that the name is ignored when + QRhi::DebugMarkers are not supported, and may, depending on the backend, + also be ignored when QRhi::EnableDebugMarkers is not set. + + \note The name may be ignored for objects other than buffers, + renderbuffers, and textures, depending on the backend. + + \note The name may be modified. For slotted resources, such as a QRhiBuffer + backed by multiple native buffers, QRhi will append a suffix to make the + underlying native buffers easily distinguishable from each other. + */ +void QRhiResource::setName(const QByteArray &name) +{ + m_objectName = name; + m_objectName.replace(',', '_'); // cannot contain comma for QRhiProfiler +} + +/*! + \return the global, unique identifier of this QRhiResource. + + User code rarely needs to deal with the value directly. It is used + internally for tracking and bookkeeping purposes. + */ +quint64 QRhiResource::globalResourceId() const +{ + return m_id; +} + +/*! + \class QRhiBuffer + \inmodule QtRhi + \brief Vertex, index, or uniform (constant) buffer resource. + */ + +/*! + \enum QRhiBuffer::Type + Specifies storage type of buffer resource. + + \value Immutable Indicates that the data is not expected to change ever + after the initial upload. Under the hood such buffer resources are + typically placed in device local (GPU) memory (on systems where + applicable). Uploading new data is possible, but may be expensive. The + upload typically happens by copying to a separate, host visible staging + buffer from which a GPU buffer-to-buffer copy is issued into the actual + GPU-only buffer. + + \value Static Indicates that the data is expected to change only + infrequently. Typically placed in device local (GPU) memory, where + applicable. On backends where host visible staging buffers are used for + uploading, the staging buffers are kept around for this type, unlike with + Immutable, so subsequent uploads do not suffer in performance. Frequent + updates, especially updates in consecutive frames, should be avoided. + + \value Dynamic Indicates that the data is expected to change frequently. + Not recommended for large buffers. Typically backed by host visible memory + in 2 copies in order to allow for changing without stalling the graphics + pipeline. The double buffering is managed transparently to the applications + and is not exposed in the API here in any form. This is the recommended, + and, with some backends, the only possible, type for buffers with + UniformBuffer usage. + */ + +/*! + \enum QRhiBuffer::UsageFlag + Flag values to specify how the buffer is going to be used. + + \value VertexBuffer Vertex buffer + \value IndexBuffer Index buffer + \value UniformBuffer Uniform (constant) buffer + */ + +/*! + \fn void QRhiBuffer::setSize(int sz) + + Sets the size of the buffer in bytes. The size is normally specified in + QRhi::newBuffer() so this function is only used when the size has to be + changed. As with other setters, the size only takes effect when calling + build(), and for already built buffers this involves releasing the previous + native resource and creating new ones under the hood. + + Backends may choose to allocate buffers bigger than \a sz in order to + fulfill alignment requirements. This is hidden from the applications and + size() will always report the size requested in \a sz. + */ + +/*! + \internal + */ +QRhiBuffer::QRhiBuffer(QRhiImplementation *rhi, Type type_, UsageFlags usage_, int size_) + : QRhiResource(rhi), + m_type(type_), m_usage(usage_), m_size(size_) +{ +} + +/*! + \return the resource type. + */ +QRhiResource::Type QRhiBuffer::resourceType() const +{ + return Buffer; +} + +/*! + \fn bool QRhiBuffer::build() + + Creates the corresponding native graphics resources. If there are already + resources present due to an earlier build() with no corresponding + release(), then release() is called implicitly first. + + \return \c true when successful, \c false when a graphics operation failed. + Regardless of the return value, calling release() is always safe. + */ + +/*! + \class QRhiRenderBuffer + \inmodule QtRhi + \brief Renderbuffer resource. + + Renderbuffers cannot be sampled or read but have some benefits over + textures in some cases: + + A DepthStencil renderbuffer may be lazily allocated and be backed by + transient memory with some APIs. On some platforms this may mean the + depth/stencil buffer uses no physical backing at all. + + Color renderbuffers are useful since QRhi::MultisampleRenderBuffer may be + supported even when QRhi::MultisampleTexture is not. + + How the renderbuffer is implemented by a backend is not exposed to the + applications. In some cases it may be backed by ordinary textures, while in + others there may be a different kind of native resource used. + */ + +/*! + \enum QRhiRenderBuffer::Type + Specifies the type of the renderbuffer + + \value DepthStencil Combined depth/stencil + \value Color Color + */ + +/*! + \enum QRhiRenderBuffer::Flag + Flag values for flags() and setFlags() + + \value UsedWithSwapChainOnly For DepthStencil renderbuffers this indicates + that the renderbuffer is only used in combination with a QRhiSwapChain and + never in other ways. Relevant with some backends, while others ignore it. + With OpenGL where a separate windowing system interface API is in use (EGL, + GLX, etc.), the flag is important since it avoids creating any actual + resource as there is already a windowing system provided depth/stencil + buffer as requested by QSurfaceFormat. + */ + +/*! + \internal + */ +QRhiRenderBuffer::QRhiRenderBuffer(QRhiImplementation *rhi, Type type_, const QSize &pixelSize_, + int sampleCount_, Flags flags_) + : QRhiResource(rhi), + m_type(type_), m_pixelSize(pixelSize_), m_sampleCount(sampleCount_), m_flags(flags_) +{ +} + +/*! + \return the resource type. + */ +QRhiResource::Type QRhiRenderBuffer::resourceType() const +{ + return RenderBuffer; +} + +/*! + \fn bool QRhiRenderBuffer::build() + + Creates the corresponding native graphics resources. If there are already + resources present due to an earlier build() with no corresponding + release(), then release() is called implicitly first. + + \return \c true when successful, \c false when a graphics operation failed. + Regardless of the return value, calling release() is always safe. + */ + +/*! + \fn QRhiTexture::Format QRhiRenderBuffer::backingFormat() const + + \internal + */ + +/*! + \class QRhiTexture + \inmodule QtRhi + \brief Texture resource. + */ + +/*! + \enum QRhiTexture::Flag + + Flag values to specify how the texture is going to be used. Not honoring + the flags set before build() and attempting to use the texture in ways that + was not declared upfront can lead to unspecified behavior or decreased + performance depending on the backend and the underlying graphics API. + + \value RenderTarget The texture going to be used in combination with + QRhiTextureRenderTarget. + + \value CubeMap The texture is a cubemap. Such textures have 6 layers, one + for each face in the order of +X, -X, +Y, -Y, +Z, -Z. Cubemap textures + cannot be multisample. + + \value MipMapped The texture has mipmaps. The appropriate mip count is + calculated automatically and can also be retrieved via + QRhi::mipLevelsForSize(). The images for the mip levels have to be + provided in the texture uploaded or generated via + QRhiResourceUpdateBatch::generateMips(). Multisample textures cannot have + mipmaps. + + \value sRGB Use an sRGB format. + + \value UsedAsTransferSource The texture is used as the source of a texture + copy or readback, meaning the texture is given as the source in + QRhiResourceUpdateBatch::copyTexture() or + QRhiResourceUpdateBatch::readBackTexture(). + + \value UsedWithGenerateMips The texture is going to be used with + QRhiResourceUpdateBatch::generateMips(). + */ + +/*! + \enum QRhiTexture::Format + + Specifies the texture format. See also QRhi::isTextureFormatSupported() and + note that flags() can modify the format when QRhiTexture::sRGB is set. + + \value UnknownFormat Not a valid format. This cannot be passed to setFormat(). + + \value RGBA8 Four component, unsigned normalized 8 bit per component. Always supported. + + \value BGRA8 Four component, unsigned normalized 8 bit per component. + + \value R8 One component, unsigned normalized 8 bit. + + \value R16 One component, unsigned normalized 16 bit. + + \value RED_OR_ALPHA8 Either same as R8, or is a similar format with the component swizzled to alpha, + depending on \l{QRhi::RedOrAlpha8IsRed}{RedOrAlpha8IsRed}. + + \value RGBA16F Four components, 16-bit float per component. + + \value RGBA32F Four components, 32-bit float per component. + + \value D16 16-bit depth (normalized unsigned integer) + + \value D32F 32-bit depth (32-bit float) + + \value BC1 + \value BC2 + \value BC3 + \value BC4 + \value BC5 + \value BC6H + \value BC7 + + \value ETC2_RGB8 + \value ETC2_RGB8A1 + \value ETC2_RGBA8 + + \value ASTC_4x4 + \value ASTC_5x4 + \value ASTC_5x5 + \value ASTC_6x5 + \value ASTC_6x6 + \value ASTC_8x5 + \value ASTC_8x6 + \value ASTC_8x8 + \value ASTC_10x5 + \value ASTC_10x6 + \value ASTC_10x8 + \value ASTC_10x10 + \value ASTC_12x10 + \value ASTC_12x12 + */ + +/*! + \internal + */ +QRhiTexture::QRhiTexture(QRhiImplementation *rhi, Format format_, const QSize &pixelSize_, + int sampleCount_, Flags flags_) + : QRhiResource(rhi), + m_format(format_), m_pixelSize(pixelSize_), m_sampleCount(sampleCount_), m_flags(flags_) +{ +} + +/*! + \return the resource type. + */ +QRhiResource::Type QRhiTexture::resourceType() const +{ + return Texture; +} + +/*! + \fn bool QRhiTexture::build() + + Creates the corresponding native graphics resources. If there are already + resources present due to an earlier build() with no corresponding + release(), then release() is called implicitly first. + + \return \c true when successful, \c false when a graphics operation failed. + Regardless of the return value, calling release() is always safe. + */ + +/*! + \return a pointer to a backend-specific QRhiNativeHandles subclass, such as + QRhiVulkanTextureNativeHandles. The returned value is null when exposing + the underlying native resources is not supported by the backend. + + \sa QRhiVulkanTextureNativeHandles, QRhiD3D11TextureNativeHandles, + QRhiMetalTextureNativeHandles, QRhiGles2TextureNativeHandles + */ +const QRhiNativeHandles *QRhiTexture::nativeHandles() +{ + return nullptr; +} + +/*! + Similar to build() except that no new native textures are created. Instead, + the texture from \a src is used. + + This allows importing an existing native texture object (which must belong + to the same device or sharing context, depending on the graphics API) from + an external graphics engine. + + \note format(), pixelSize(), sampleCount(), and flags() must still be set + correctly. Passing incorrect sizes and other values to QRhi::newTexture() + and then following it with a buildFrom() expecting that the native texture + object alone is sufficient to deduce such values is \b wrong and will lead + to problems. + + \note QRhiTexture does not take ownership of the texture object. release() + does not free the object or any associated memory. + + The opposite of this operation, exposing a QRhiTexture-created native + texture object to a foreign engine, is possible via nativeHandles(). + + \sa QRhiVulkanTextureNativeHandles, QRhiD3D11TextureNativeHandles, + QRhiMetalTextureNativeHandles, QRhiGles2TextureNativeHandles + */ +bool QRhiTexture::buildFrom(const QRhiNativeHandles *src) +{ + Q_UNUSED(src); + return false; +} + +/*! + \class QRhiSampler + \inmodule QtRhi + \brief Sampler resource. + */ + +/*! + \enum QRhiSampler::Filter + Specifies the minification, magnification, or mipmap filtering + + \value None Applicable only for mipmapMode(), indicates no mipmaps to be used + \value Nearest + \value Linear + */ + +/*! + \enum QRhiSampler::AddressMode + Specifies the addressing mode + + \value Repeat + \value ClampToEdge + \value Border + \value Mirror + \value MirrorOnce + */ + +/*! + \enum QRhiSampler::CompareOp + Specifies the texture comparison function. + + \value Never (default) + \value Less + \value Equal + \value LessOrEqual + \value Greater + \value NotEqual + \value GreaterOrEqual + \value Always + */ + +/*! + \internal + */ +QRhiSampler::QRhiSampler(QRhiImplementation *rhi, + Filter magFilter_, Filter minFilter_, Filter mipmapMode_, + AddressMode u_, AddressMode v_) + : QRhiResource(rhi), + m_magFilter(magFilter_), m_minFilter(minFilter_), m_mipmapMode(mipmapMode_), + m_addressU(u_), m_addressV(v_), + m_addressW(QRhiSampler::ClampToEdge), + m_compareOp(QRhiSampler::Never) +{ +} + +/*! + \return the resource type. + */ +QRhiResource::Type QRhiSampler::resourceType() const +{ + return Sampler; +} + +/*! + \class QRhiRenderPassDescriptor + \inmodule QtRhi + \brief Render pass resource. + */ + +/*! + \internal + */ +QRhiRenderPassDescriptor::QRhiRenderPassDescriptor(QRhiImplementation *rhi) + : QRhiResource(rhi) +{ +} + +/*! + \return the resource type. + */ +QRhiResource::Type QRhiRenderPassDescriptor::resourceType() const +{ + return RenderPassDescriptor; +} + +/*! + \class QRhiRenderTarget + \inmodule QtRhi + \brief Represents an onscreen (swapchain) or offscreen (texture) render target. + */ + +/*! + \internal + */ +QRhiRenderTarget::QRhiRenderTarget(QRhiImplementation *rhi) + : QRhiResource(rhi) +{ +} + +/*! + \return the resource type. + */ +QRhiResource::Type QRhiRenderTarget::resourceType() const +{ + return RenderTarget; +} + +/*! + \fn QSize QRhiRenderTarget::pixelSize() const + + \return the size in pixels. + */ + +/*! + \fn float QRhiRenderTarget::devicePixelRatio() const + + \return the device pixel ratio. For QRhiTextureRenderTarget this is always + 1. For targets retrieved from a QRhiSwapChain the value reflects the + \l{QWindow::devicePixelRatio()}{device pixel ratio} of the targeted + QWindow. + */ + +/*! + \class QRhiTextureRenderTarget + \inmodule QtRhi + \brief Texture render target resource. + + A texture render target allows rendering into one or more textures, + optionally with a depth texture or depth/stencil renderbuffer. + + \note Textures used in combination with QRhiTextureRenderTarget must be + created with the QRhiTexture::RenderTarget flag. + + The simplest example of creating a render target with a texture as its + single color attachment: + + \badcode + texture = rhi->newTexture(QRhiTexture::RGBA8, size, 1, QRhiTexture::RenderTarget); + texture->build(); + rt = rhi->newTextureRenderTarget({ texture }); + rp = rt->newCompatibleRenderPassDescriptor(); + rt->setRenderPassDescriptor(rt); + rt->build(); + // rt can now be used with beginPass() + \endcode + */ + +/*! + \enum QRhiTextureRenderTarget::Flag + + Flag values describing the load/store behavior for the render target. The + load/store behavior may be baked into native resources under the hood, + depending on the backend, and therefore it needs to be known upfront and + cannot be changed without rebuilding (and so releasing and creating new + native resources). + + \value PreserveColorContents Indicates that the contents of the color + attachments is to be loaded when starting a render pass, instead of + clearing. This is potentially more expensive, especially on mobile (tiled) + GPUs, but allows preserving the existing contents between passes. + + \value PreserveDepthStencilContents Indicates that the contents of the + depth texture is to be loaded when starting a render pass, instead + clearing. Only applicable when a texture is used as the depth buffer + (QRhiTextureRenderTargetDescription::depthTexture() is set) because + depth/stencil renderbuffers may not have any physical backing and data may + not be written out in the first place. + */ + +/*! + \internal + */ +QRhiTextureRenderTarget::QRhiTextureRenderTarget(QRhiImplementation *rhi, + const QRhiTextureRenderTargetDescription &desc_, + Flags flags_) + : QRhiRenderTarget(rhi), + m_desc(desc_), + m_flags(flags_) +{ +} + +/*! + \return the resource type. + */ +QRhiResource::Type QRhiTextureRenderTarget::resourceType() const +{ + return TextureRenderTarget; +} + +/*! + \fn QRhiRenderPassDescriptor *QRhiTextureRenderTarget::newCompatibleRenderPassDescriptor() + + \return a new QRhiRenderPassDescriptor that is compatible with this render + target. + + The returned value is used in two ways: it can be passed to + setRenderPassDescriptor() and + QRhiGraphicsPipeline::setRenderPassDescriptor(). A render pass descriptor + describes the attachments (color, depth/stencil) and the load/store + behavior that can be affected by flags(). A QRhiGraphicsPipeline can only + be used in combination with a render target that has the same + QRhiRenderPassDescriptor set. + + Two QRhiTextureRenderTarget instances can share the same render pass + descriptor as long as they have the same number and type of attachments. + The associated QRhiTexture or QRhiRenderBuffer instances are not part of + the render pass descriptor so those can differ in the two + QRhiTextureRenderTarget intances. + + \note resources, such as QRhiTexture instances, referenced in description() + must already be built + + \sa build() + */ + +/*! + \fn bool QRhiTextureRenderTarget::build() + + Creates the corresponding native graphics resources. If there are already + resources present due to an earlier build() with no corresponding + release(), then release() is called implicitly first. + + \note renderPassDescriptor() must be set before calling build(). To obtain + a QRhiRenderPassDescriptor compatible with the render target, call + newCompatibleRenderPassDescriptor() before build() but after setting all + other parameters, such as description() and flags(). To save resources, + reuse the same QRhiRenderPassDescriptor with multiple + QRhiTextureRenderTarget instances, whenever possible. Sharing the same + render pass descriptor is only possible when the render targets have the + same number and type of attachments (the actual textures can differ) and + the same flags. + + \note resources, such as QRhiTexture instances, referenced in description() + must already be built + + \return \c true when successful, \c false when a graphics operation failed. + Regardless of the return value, calling release() is always safe. + */ + +/*! + \class QRhiShaderResourceBindings + \inmodule QtRhi + \brief Encapsulates resources for making buffer, texture, sampler resources visible to shaders. + + A QRhiShaderResourceBindings is a collection of QRhiShaderResourceBinding + objects, each of which describe a single binding. + + Take a fragment shader with the following interface: + + \badcode + layout(std140, binding = 0) uniform buf { + mat4 mvp; + int flip; + } ubuf; + + layout(binding = 1) uniform sampler2D tex; + \endcode + + To make resources visible to the shader, the following + QRhiShaderResourceBindings could be created and then passed to + QRhiGraphicsPipeline::setShaderResourceBindings(): + + \badcode + srb = rhi->newShaderResourceBindings(); + srb->setBindings({ + QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, ubuf), + QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, texture, sampler) + }); + srb->build(); + ... + ps = rhi->newGraphicsPipeline(); + ... + ps->setShaderResourceBindings(srb); + ps->build(); + ... + cb->setGraphicsPipeline(ps); + cb->setShaderResources(); // binds srb + \endcode + + This assumes that \c ubuf is a QRhiBuffer, \c texture is a QRhiTexture, + while \a sampler is a QRhiSampler. The example also assumes that the + uniform block is present in the vertex shader as well so the same buffer is + made visible to the vertex stage too. + + \section3 Advanced usage + + Building on the above example, let's assume that a pass now needs to use + the exact same pipeline and shaders with a different texture. Creating a + whole separate QRhiGraphicsPipeline just for this would be an overkill. + This is why QRhiCommandBuffer::setShaderResources() allows specifying a \a + srb argument. As long as the layouts (so the number of bindings and the + binding points) match between two QRhiShaderResourceBindings, they can both + be used with the same pipeline, assuming the pipeline was built with one of + them in the first place. + + Creating and then using a new \c srb2 that is very similar to \c srb with + the exception of referencing another texture could be implemented like the + following: + + \badcode + srb2 = rhi->newShaderResourceBindings(); + QVector bindings = srb->bindings(); + bindings[1] = QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, anotherTexture, sampler); + srb2->setBindings(bindings); + srb2->build(); + ... + cb->setGraphicsPipeline(ps); + cb->setShaderResources(srb2); // binds srb2 + \endcode + */ + +/*! + \internal + */ +QRhiShaderResourceBindings::QRhiShaderResourceBindings(QRhiImplementation *rhi) + : QRhiResource(rhi) +{ +} + +/*! + \return the resource type. + */ +QRhiResource::Type QRhiShaderResourceBindings::resourceType() const +{ + return ShaderResourceBindings; +} + +/*! + \return \c true if the layout is compatible with \a other. The layout does + not include the actual resource (such as, buffer or texture) and related + parameters (such as, offset or size). It does include the binding point, + pipeline stage, and resource type, however. The number and order of the + bindings must also match in order to be compatible. + + When there is a QRhiGraphicsPipeline created with this + QRhiShaderResourceBindings, and the function returns \c true, \a other can + then safely be passed to QRhiCommandBuffer::setShaderResources(), and so + be used with the pipeline in place of this QRhiShaderResourceBindings. + + This function can be called before build() as well. The bindings must + already be set via setBindings() however. + */ +bool QRhiShaderResourceBindings::isLayoutCompatible(const QRhiShaderResourceBindings *other) const +{ + const int count = m_bindings.count(); + if (count != other->m_bindings.count()) + return false; + + for (int i = 0; i < count; ++i) { + if (!m_bindings[i].isLayoutCompatible(other->m_bindings.at(i))) + return false; + } + + return true; +} + +/*! + \class QRhiShaderResourceBinding + \inmodule QtRhi + \brief Describes the shader resource for a single binding point. + + A QRhiShaderResourceBinding cannot be constructed directly. Instead, use + the static functions uniformBuffer(), sampledTexture() to get an instance. + */ + +/*! + \enum QRhiShaderResourceBinding::Type + Specifies type of the shader resource bound to a binding point + + \value UniformBuffer Uniform buffer + \value SampledTexture Combined image sampler + */ + +/*! + \enum QRhiShaderResourceBinding::StageFlag + Flag values to indicate which stages the shader resource is visible in + + \value VertexStage Vertex stage + \value FragmentStage Fragment (pixel) stage + */ + +/*! + \internal + */ +QRhiShaderResourceBinding::QRhiShaderResourceBinding() + : d(new QRhiShaderResourceBindingPrivate) +{ +} + +/*! + \internal + */ +void QRhiShaderResourceBinding::detach() +{ + qAtomicDetach(d); +} + +/*! + \internal + */ +QRhiShaderResourceBinding::QRhiShaderResourceBinding(const QRhiShaderResourceBinding &other) + : d(other.d) +{ + d->ref.ref(); +} + +/*! + \internal + */ +QRhiShaderResourceBinding &QRhiShaderResourceBinding::operator=(const QRhiShaderResourceBinding &other) +{ + qAtomicAssign(d, other.d); + return *this; +} + +/*! + Destructor. + */ +QRhiShaderResourceBinding::~QRhiShaderResourceBinding() +{ + if (!d->ref.deref()) + delete d; +} + +/*! + \return \c true if the layout is compatible with \a other. The layout does not + include the actual resource (such as, buffer or texture) and related + parameters (such as, offset or size). + + For example, \c a and \c b below are not equal, but are compatible layout-wise: + + \badcode + auto a = QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage, buffer); + auto b = QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage, someOtherBuffer, 256); + \endcode + */ +bool QRhiShaderResourceBinding::isLayoutCompatible(const QRhiShaderResourceBinding &other) const +{ + return (d == other.d) + || (d->binding == other.d->binding && d->stage == other.d->stage && d->type == other.d->type); +} + +/*! + \return a shader resource binding for the given binding number, pipeline + stages, and buffer specified by \a binding, \a stage, and \a buf. + */ +QRhiShaderResourceBinding QRhiShaderResourceBinding::uniformBuffer( + int binding, StageFlags stage, QRhiBuffer *buf) +{ + QRhiShaderResourceBinding b; + QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b); + Q_ASSERT(d->ref.load() == 1); + d->binding = binding; + d->stage = stage; + d->type = UniformBuffer; + d->u.ubuf.buf = buf; + d->u.ubuf.offset = 0; + d->u.ubuf.maybeSize = 0; // entire buffer + d->u.ubuf.hasDynamicOffset = false; + return b; +} + +/*! + \return a shader resource binding for the given binding number, pipeline + stages, and buffer specified by \a binding, \a stage, and \a buf. This + overload binds a region only, as specified by \a offset and \a size. + + \note It is up to the user to ensure the offset is aligned to + QRhi::ubufAlignment(). + + \note \a size must be greater than 0. + */ +QRhiShaderResourceBinding QRhiShaderResourceBinding::uniformBuffer( + int binding, StageFlags stage, QRhiBuffer *buf, int offset, int size) +{ + Q_ASSERT(size > 0); + QRhiShaderResourceBinding b; + QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b); + Q_ASSERT(d->ref.load() == 1); + d->binding = binding; + d->stage = stage; + d->type = UniformBuffer; + d->u.ubuf.buf = buf; + d->u.ubuf.offset = offset; + d->u.ubuf.maybeSize = size; + d->u.ubuf.hasDynamicOffset = false; + return b; +} + +/*! + \return a shader resource binding for the given binding number, pipeline + stages, and buffer specified by \a binding, \a stage, and \a buf. The + uniform buffer is assumed to have dynamic offset. The dynamic offset can be + specified in QRhiCommandBuffer::setShaderResources(), thus allowing using + varying offset values without creating new bindings for the buffer. The + size of the bound region is specified by \a size. Like with non-dynamic + offsets, \c{offset + size} cannot exceed the size of \a buf. + */ +QRhiShaderResourceBinding QRhiShaderResourceBinding::uniformBufferWithDynamicOffset( + int binding, StageFlags stage, QRhiBuffer *buf, int size) +{ + QRhiShaderResourceBinding b; + QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b); + Q_ASSERT(d->ref.load() == 1); + d->binding = binding; + d->stage = stage; + d->type = UniformBuffer; + d->u.ubuf.buf = buf; + d->u.ubuf.offset = 0; + d->u.ubuf.maybeSize = size; + d->u.ubuf.hasDynamicOffset = true; + return b; +} + +/*! + \return a shader resource binding for the given binding number, pipeline + stages, texture, and sampler specified by \a binding, \a stage, \a tex, + \a sampler. + */ +QRhiShaderResourceBinding QRhiShaderResourceBinding::sampledTexture( + int binding, StageFlags stage, QRhiTexture *tex, QRhiSampler *sampler) +{ + QRhiShaderResourceBinding b; + QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b); + Q_ASSERT(d->ref.load() == 1); + d->binding = binding; + d->stage = stage; + d->type = SampledTexture; + d->u.stex.tex = tex; + d->u.stex.sampler = sampler; + return b; +} + +/*! + \return \c true if the contents of the two QRhiShaderResourceBinding + objects \a a and \a b are equal. This includes the resources (buffer, + texture) and related parameters (offset, size) as well. To only compare + layouts (binding point, pipeline stage, resource type), use + \l{QRhiShaderResourceBinding::isLayoutCompatible()}{isLayoutCompatible()} + instead. + + \relates QRhiShaderResourceBinding + */ +bool operator==(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b) Q_DECL_NOTHROW +{ + if (a.d == b.d) + return true; + + if (a.d->binding != b.d->binding + || a.d->stage != b.d->stage + || a.d->type != b.d->type) + { + return false; + } + + switch (a.d->type) { + case QRhiShaderResourceBinding::UniformBuffer: + if (a.d->u.ubuf.buf != b.d->u.ubuf.buf + || a.d->u.ubuf.offset != b.d->u.ubuf.offset + || a.d->u.ubuf.maybeSize != b.d->u.ubuf.maybeSize) + { + return false; + } + break; + case QRhiShaderResourceBinding::SampledTexture: + if (a.d->u.stex.tex != b.d->u.stex.tex + || a.d->u.stex.sampler != b.d->u.stex.sampler) + { + return false; + } + break; + default: + Q_UNREACHABLE(); + return false; + } + + return true; +} + +/*! + \return \c false if all the bindings in the two QRhiShaderResourceBinding + objects \a a and \a b are equal; otherwise returns \c true. + + \relates QRhiShaderResourceBinding + */ +bool operator!=(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b) Q_DECL_NOTHROW +{ + return !(a == b); +} + +/*! + \return the hash value for \a b, using \a seed to seed the calculation. + + \relates QRhiShaderResourceBinding + */ +uint qHash(const QRhiShaderResourceBinding &b, uint seed) Q_DECL_NOTHROW +{ + const char *u = reinterpret_cast(&b.d->u); + return seed + b.d->binding + 10 * b.d->stage + 100 * b.d->type + + qHash(QByteArray::fromRawData(u, sizeof(b.d->u)), seed); +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QRhiShaderResourceBinding &b) +{ + const QRhiShaderResourceBindingPrivate *d = b.d; + QDebugStateSaver saver(dbg); + dbg.nospace() << "QRhiShaderResourceBinding(" + << "binding=" << d->binding + << " stage=" << d->stage + << " type=" << d->type; + switch (d->type) { + case QRhiShaderResourceBinding::UniformBuffer: + dbg.nospace() << " UniformBuffer(" + << "buffer=" << d->u.ubuf.buf + << " offset=" << d->u.ubuf.offset + << " maybeSize=" << d->u.ubuf.maybeSize + << ')'; + break; + case QRhiShaderResourceBinding::SampledTexture: + dbg.nospace() << " SampledTexture(" + << "texture=" << d->u.stex.tex + << " sampler=" << d->u.stex.sampler + << ')'; + break; + default: + Q_UNREACHABLE(); + break; + } + dbg.nospace() << ')'; + return dbg; +} +#endif + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QRhiShaderResourceBindings &srb) +{ + QDebugStateSaver saver(dbg); + dbg.nospace() << "QRhiShaderResourceBindings(" + << srb.m_bindings + << ')'; + return dbg; +} +#endif + +/*! + \class QRhiGraphicsPipeline + \inmodule QtRhi + \brief Graphics pipeline state resource. + + \note Setting the shader resource bindings is mandatory. The referenced + QRhiShaderResourceBindings must already be built by the time build() is + called. + + \note Setting the render pass descriptor is mandatory. To obtain a + QRhiRenderPassDescriptor that can be passed to setRenderPassDescriptor(), + use either QRhiTextureRenderTarget::newCompatibleRenderPassDescriptor() or + QRhiSwapChain::newCompatibleRenderPassDescriptor(). + + \note Setting the vertex input layout is mandatory. + + \note Setting the shader stages is mandatory. + + \note sampleCount() defaults to 1 and must match the sample count of the + render target's color and depth stencil attachments. + + \note The depth test, depth write, and stencil test are disabled by + default. + + \note stencilReadMask() and stencilWriteMask() apply to both faces. They + both default to 0xFF. + */ + +/*! + \fn void QRhiGraphicsPipeline::setTargetBlends(const QVector &blends) + + Sets the blend specification for color attachments. Each element in \a + blends corresponds to a color attachment of the render target. + + By default no blends are set, which is a shortcut to disabling blending and + enabling color write for all four channels. + */ + +/*! + \enum QRhiGraphicsPipeline::Flag + + Flag values for describing the dynamic state of the pipeline. The viewport is always dynamic. + + \value UsesBlendConstants Indicates that a blend color constant will be set + via QRhiCommandBuffer::setBlendConstants() + + \value UsesStencilRef Indicates that a stencil reference value will be set + via QRhiCommandBuffer::setStencilRef() + + \value UsesScissor Indicates that a scissor rectangle will be set via + QRhiCommandBuffer::setScissor() + */ + +/*! + \enum QRhiGraphicsPipeline::Topology + Specifies the primitive topology + + \value Triangles (default) + \value TriangleStrip + \value Lines + \value LineStrip + \value Points + */ + +/*! + \enum QRhiGraphicsPipeline::CullMode + Specifies the culling mode + + \value None No culling (default) + \value Front Cull front faces + \value Back Cull back faces + */ + +/*! + \enum QRhiGraphicsPipeline::FrontFace + Specifies the front face winding order + + \value CCW Counter clockwise (default) + \value CW Clockwise + */ + +/*! + \enum QRhiGraphicsPipeline::ColorMaskComponent + Flag values for specifying the color write mask + + \value R + \value G + \value B + \value A + */ + +/*! + \enum QRhiGraphicsPipeline::BlendFactor + Specifies the blend factor + + \value Zero + \value One + \value SrcColor + \value OneMinusSrcColor + \value DstColor + \value OneMinusDstColor + \value SrcAlpha + \value OneMinusSrcAlpha + \value DstAlpha + \value OneMinusDstAlpha + \value ConstantColor + \value OneMinusConstantColor + \value ConstantAlpha + \value OneMinusConstantAlpha + \value SrcAlphaSaturate + \value Src1Color + \value OneMinusSrc1Color + \value Src1Alpha + \value OneMinusSrc1Alpha + */ + +/*! + \enum QRhiGraphicsPipeline::BlendOp + Specifies the blend operation + + \value Add + \value Subtract + \value ReverseSubtract + \value Min + \value Max + */ + +/*! + \enum QRhiGraphicsPipeline::CompareOp + Specifies the depth or stencil comparison function + + \value Never + \value Less (default for depth) + \value Equal + \value LessOrEqual + \value Greater + \value NotEqual + \value GreaterOrEqual + \value Always (default for stencil) + */ + +/*! + \enum QRhiGraphicsPipeline::StencilOp + Specifies the stencil operation + + \value StencilZero + \value Keep (default) + \value Replace + \value IncrementAndClamp + \value DecrementAndClamp + \value Invert + \value IncrementAndWrap + \value DecrementAndWrap + */ + +/*! + \class QRhiGraphicsPipeline::TargetBlend + \inmodule QtRhi + \brief Describes the blend state for one color attachment. + + Defaults to color write enabled, blending disabled. The blend values are + set up for pre-multiplied alpha (One, OneMinusSrcAlpha, One, + OneMinusSrcAlpha) by default. + */ + +/*! + \class QRhiGraphicsPipeline::StencilOpState + \inmodule QtRhi + \brief Describes the stencil operation state. + */ + +/*! + \internal + */ +QRhiGraphicsPipeline::QRhiGraphicsPipeline(QRhiImplementation *rhi) + : QRhiResource(rhi) +{ +} + +/*! + \return the resource type. + */ +QRhiResource::Type QRhiGraphicsPipeline::resourceType() const +{ + return GraphicsPipeline; +} + +/*! + \fn bool QRhiGraphicsPipeline::build() + + Creates the corresponding native graphics resources. If there are already + resources present due to an earlier build() with no corresponding + release(), then release() is called implicitly first. + + \return \c true when successful, \c false when a graphics operation failed. + Regardless of the return value, calling release() is always safe. + */ + +/*! + \fn void QRhiGraphicsPipeline::setDepthTest(bool enable) + + Enables or disables depth testing. Both depth test and the writing out of + depth data are disabled by default. + + \sa setDepthWrite() + */ + +/*! + \fn void QRhiGraphicsPipeline::setDepthWrite(bool enable) + + Controls the writing out of depth data into the depth buffer. By default + this is disabled. Depth write is typically enabled together with the depth + test. + + \note Enabling depth write without having depth testing enabled may not + lead to the desired result, and should be avoided. + + \sa setDepthTest() + */ + +/*! + \class QRhiSwapChain + \inmodule QtRhi + \brief Swapchain resource. + + A swapchain enables presenting rendering results to a surface. A swapchain + is typically backed by a set of color buffers. Of these, one is displayed + at a time. + + Below is a typical pattern for creating and managing a swapchain and some + associated resources in order to render onto a QWindow: + + \badcode + void init() + { + sc = rhi->newSwapChain(); + ds = rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, + QSize(), // no need to set the size yet + 1, + QRhiRenderBuffer::UsedWithSwapChainOnly); + sc->setWindow(window); + sc->setDepthStencil(ds); + rp = sc->newCompatibleRenderPassDescriptor(); + sc->setRenderPassDescriptor(rp); + resizeSwapChain(); + } + + void resizeSwapChain() + { + const QSize outputSize = sc->surfacePixelSize(); + ds->setPixelSize(outputSize); + ds->build(); + hasSwapChain = sc->buildOrResize(); + } + + void render() + { + if (!hasSwapChain || notExposed) + return; + + if (sc->currentPixelSize() != sc->surfacePixelSize() || newlyExposed) { + resizeSwapChain(); + if (!hasSwapChain) + return; + newlyExposed = false; + } + + rhi->beginFrame(sc); + // ... + rhi->endFrame(sc); + } + \endcode + + Avoid relying on QWindow resize events to resize swapchains, especially + considering that surface sizes may not always fully match the QWindow + reported dimensions. The safe, cross-platform approach is to do the check + via surfacePixelSize() whenever starting a new frame. + + Releasing the swapchain must happen while the QWindow and the underlying + native window is fully up and running. Building on the previous example: + + \badcode + void releaseSwapChain() + { + if (hasSwapChain) { + sc->release(); + hasSwapChain = false; + } + } + + // assuming Window is our QWindow subclass + bool Window::event(QEvent *e) + { + switch (e->type()) { + case QEvent::UpdateRequest: // for QWindow::requestUpdate() + render(); + break; + case QEvent::PlatformSurface: + if (static_cast(e)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) + releaseSwapChain(); + break; + default: + break; + } + return QWindow::event(e); + } + \endcode + + Initializing the swapchain and starting to render the first frame cannot + start at any time. The safe, cross-platform approach is to rely on expose + events. QExposeEvent is a loosely specified event that is sent whenever a + window gets mapped, obscured, and resized, depending on the platform. + + \badcode + void Window::exposeEvent(QExposeEvent *) + { + // initialize and start rendering when the window becomes usable for graphics purposes + if (isExposed() && !running) { + running = true; + init(); + } + + // stop pushing frames when not exposed or size becomes 0 + if ((!isExposed() || (hasSwapChain && sc->surfacePixelSize().isEmpty())) && running) + notExposed = true; + + // continue when exposed again and the surface has a valid size + if (isExposed() && running && notExposed && !sc->surfacePixelSize().isEmpty()) { + notExposed = false; + newlyExposed = true; + } + + if (isExposed() && !sc->surfacePixelSize().isEmpty()) + render(); + } + \endcode + + Once the rendering has started, a simple way to request a new frame is + QWindow::requestUpdate(). While on some platforms this is merely a small + timer, on others it has a specific implementation: for instance on macOS or + iOS it may be backed by + \l{https://developer.apple.com/documentation/corevideo/cvdisplaylink?language=objc}{CVDisplayLink}. + The example above is already prepared for update requests by handling + QEvent::UpdateRequest. + + While acting as a QRhiRenderTarget, QRhiSwapChain also manages a + QRhiCommandBuffer. Calling QRhi::endFrame() submits the recorded commands + and also enqueues a \c present request. The default behavior is to do this + with a swap interval of 1, meaning synchronizing to the display's vertical + refresh is enabled. Thus the rendering thread calling beginFrame() and + endFrame() will get throttled to vsync. On some backends this can be + disabled by passing QRhiSwapChain:NoVSync in flags(). + + Multisampling (MSAA) is handled transparently to the applications when + requested via setSampleCount(). Where applicable, QRhiSwapChain will take + care of creating additional color buffers and issuing a multisample resolve + command at the end of a frame. For OpenGL, it is necessary to request the + appropriate sample count also via QSurfaceFormat, by calling + QSurfaceFormat::setDefaultFormat() before initializing the QRhi. + */ + +/*! + \enum QRhiSwapChain::Flag + Flag values to describe swapchain properties + + \value SurfaceHasPreMulAlpha Indicates that the target surface has + transparency with premultiplied alpha. + + \value SurfaceHasNonPreMulAlpha Indicates the target surface has + transparencyt with non-premultiplied alpha. + + \value sRGB Requests to pick an sRGB format for the swapchain and/or its + render target views, where applicable. Note that this implies that sRGB + framebuffer update and blending will get enabled for all content targeting + this swapchain, and opting out is not possible. For OpenGL, set + \l{QSurfaceFormat::sRGBColorSpace}{sRGBColorSpace} on the QSurfaceFormat of + the QWindow in addition. + + \value UsedAsTransferSource Indicates the the swapchain will be used as the + source of a readback in QRhiResourceUpdateBatch::readBackTexture(). + + \value NoVSync Requests disabling waiting for vertical sync, also avoiding + throttling the rendering thread. The behavior is backend specific and + applicable only where it is possible to control this. Some may ignore the + request altogether. For OpenGL, try instead setting the swap interval to 0 + on the QWindow via QSurfaceFormat::setSwapInterval(). + + \value MinimalBufferCount Requests creating the swapchain with the minimum + number of buffers, which is in practice 2, unless the graphics + implementation has a higher minimum number than that. Only applicable with + backends where such control is available via the graphics API, for example, + Vulkan. By default it is up to the backend to decide what number of buffers + it requests (in practice this is almost always either 2 or 3), and it is + not the applications' concern. However, on Vulkan for instance the backend + will likely prefer the higher number (3), for example to avoid odd + performance issues with some Vulkan implementations on mobile devices. It + could be that on some platforms it can prove to be beneficial to force the + lower buffer count (2), so this flag allows forcing that. Note that all + this has no effect on the number of frames kept in flight, so the CPU + (QRhi) will still prepare frames at most \c{N - 1} frames ahead of the GPU, + even when the swapchain image buffer count larger than \c N. (\c{N} = + QRhi::FramesInFlight and typically 2). + */ + +/*! + \internal + */ +QRhiSwapChain::QRhiSwapChain(QRhiImplementation *rhi) + : QRhiResource(rhi) +{ +} + +/*! + \return the resource type. + */ +QRhiResource::Type QRhiSwapChain::resourceType() const +{ + return SwapChain; +} + +/*! + \fn QSize QRhiSwapChain::currentPixelSize() const + + \return the size with which the swapchain was last successfully built. Use + this to decide if buildOrResize() needs to be called again: if + \c{currentPixelSize() != surfacePixelSize()} then the swapchain needs to be + resized. + + \sa surfacePixelSize() + */ + +/*! + \fn QSize QRhiSwapChain::surfacePixelSize() + + \return The size of the window's associated surface or layer. Do not assume + this is the same as QWindow::size() * QWindow::devicePixelRatio(). + + Can be called before buildOrResize() (but with window() already set), which + allows setting the correct size for the depth-stencil buffer that is then + used together with the swapchain's color buffers. Also used in combination + with currentPixelSize() to detect size changes. + + \sa currentPixelSize() + */ + +/*! + \fn QRhiCommandBuffer *QRhiSwapChain::currentFrameCommandBuffer() + + \return a command buffer on which rendering commands can be recorded. Only + valid within a QRhi::beginFrame() - QRhi::endFrame() block where + beginFrame() was called with this swapchain. + + \note the value must not be cached and reused between frames +*/ + +/*! + \fn QRhiRenderTarget *QRhiSwapChain::currentFrameRenderTarget() + + \return a render target that can used with beginPass() in order to render + the the swapchain's current backbuffer. Only valid within a + QRhi::beginFrame() - QRhi::endFrame() block where beginFrame() was called + with this swapchain. + + \note the value must not be cached and reused between frames + */ + +/*! + \fn bool QRhiSwapChain::buildOrResize() + + Creates the swapchain if not already done and resizes the swapchain buffers + to match the current size of the targeted surface. Call this whenever the + size of the target surface is different than before. + + \note call release() only when the swapchain needs to be released + completely, typically upon + QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed. To perform resizing, just + call buildOrResize(). + + \return \c true when successful, \c false when a graphics operation failed. + Regardless of the return value, calling release() is always safe. + */ + +/*! + \class QRhiCommandBuffer + \inmodule QtRhi + \brief Command buffer resource. + + Not creatable by applications at the moment. The only ways to obtain a + valid QRhiCommandBuffer are to get it from the targeted swapchain via + QRhiSwapChain::currentFrameCommandBuffer(), or, in case of rendering + completely offscreen, initializing one via QRhi::beginOffscreenFrame(). + */ + +/*! + \enum QRhiCommandBuffer::IndexFormat + Specifies the index data type + + \value IndexUInt16 Unsigned 16-bit (quint16) + \value IndexUInt32 Unsigned 32-bit (quint32) + */ + +/*! + \typedef QRhiCommandBuffer::DynamicOffset + + Synonym for QPair. The first entry is the binding, the second + is the offset in the buffer. +*/ + +/*! + \typedef QRhiCommandBuffer::VertexInput + + Synonym for QPair. The second entry is an offset in + the buffer specified by the first. +*/ + +/*! + \internal + */ +QRhiCommandBuffer::QRhiCommandBuffer(QRhiImplementation *rhi) + : QRhiResource(rhi) +{ +} + +/*! + \return the resource type. + */ +QRhiResource::Type QRhiCommandBuffer::resourceType() const +{ + return CommandBuffer; +} + +QRhiImplementation::~QRhiImplementation() +{ + qDeleteAll(resUpdPool); + + // Be nice and show something about leaked stuff. Though we may not get + // this far with some backends where the allocator or the api may check + // and freak out for unfreed graphics objects in the derived dtor already. +#ifndef QT_NO_DEBUG + if (!resources.isEmpty()) { + qWarning("QRhi %p going down with %d unreleased resources. This is not nice.", + q, resources.count()); + for (QRhiResource *res : qAsConst(resources)) { + qWarning(" Resource %p (%s)", res, res->m_objectName.constData()); + res->m_rhi = nullptr; + } + } +#endif +} + +bool QRhiImplementation::isCompressedFormat(QRhiTexture::Format format) const +{ + return (format >= QRhiTexture::BC1 && format <= QRhiTexture::BC7) + || (format >= QRhiTexture::ETC2_RGB8 && format <= QRhiTexture::ETC2_RGBA8) + || (format >= QRhiTexture::ASTC_4x4 && format <= QRhiTexture::ASTC_12x12); +} + +void QRhiImplementation::compressedFormatInfo(QRhiTexture::Format format, const QSize &size, + quint32 *bpl, quint32 *byteSize, + QSize *blockDim) const +{ + int xdim = 4; + int ydim = 4; + quint32 blockSize = 0; + + switch (format) { + case QRhiTexture::BC1: + blockSize = 8; + break; + case QRhiTexture::BC2: + blockSize = 16; + break; + case QRhiTexture::BC3: + blockSize = 16; + break; + case QRhiTexture::BC4: + blockSize = 8; + break; + case QRhiTexture::BC5: + blockSize = 16; + break; + case QRhiTexture::BC6H: + blockSize = 16; + break; + case QRhiTexture::BC7: + blockSize = 16; + break; + + case QRhiTexture::ETC2_RGB8: + blockSize = 8; + break; + case QRhiTexture::ETC2_RGB8A1: + blockSize = 8; + break; + case QRhiTexture::ETC2_RGBA8: + blockSize = 16; + break; + + case QRhiTexture::ASTC_4x4: + blockSize = 16; + break; + case QRhiTexture::ASTC_5x4: + blockSize = 16; + xdim = 5; + break; + case QRhiTexture::ASTC_5x5: + blockSize = 16; + xdim = ydim = 5; + break; + case QRhiTexture::ASTC_6x5: + blockSize = 16; + xdim = 6; + ydim = 5; + break; + case QRhiTexture::ASTC_6x6: + blockSize = 16; + xdim = ydim = 6; + break; + case QRhiTexture::ASTC_8x5: + blockSize = 16; + xdim = 8; + ydim = 5; + break; + case QRhiTexture::ASTC_8x6: + blockSize = 16; + xdim = 8; + ydim = 6; + break; + case QRhiTexture::ASTC_8x8: + blockSize = 16; + xdim = ydim = 8; + break; + case QRhiTexture::ASTC_10x5: + blockSize = 16; + xdim = 10; + ydim = 5; + break; + case QRhiTexture::ASTC_10x6: + blockSize = 16; + xdim = 10; + ydim = 6; + break; + case QRhiTexture::ASTC_10x8: + blockSize = 16; + xdim = 10; + ydim = 8; + break; + case QRhiTexture::ASTC_10x10: + blockSize = 16; + xdim = ydim = 10; + break; + case QRhiTexture::ASTC_12x10: + blockSize = 16; + xdim = 12; + ydim = 10; + break; + case QRhiTexture::ASTC_12x12: + blockSize = 16; + xdim = ydim = 12; + break; + + default: + Q_UNREACHABLE(); + break; + } + + const quint32 wblocks = (size.width() + xdim - 1) / xdim; + const quint32 hblocks = (size.height() + ydim - 1) / ydim; + + if (bpl) + *bpl = wblocks * blockSize; + if (byteSize) + *byteSize = wblocks * hblocks * blockSize; + if (blockDim) + *blockDim = QSize(xdim, ydim); +} + +void QRhiImplementation::textureFormatInfo(QRhiTexture::Format format, const QSize &size, + quint32 *bpl, quint32 *byteSize) const +{ + if (isCompressedFormat(format)) { + compressedFormatInfo(format, size, bpl, byteSize, nullptr); + return; + } + + quint32 bpc = 0; + switch (format) { + case QRhiTexture::RGBA8: + bpc = 4; + break; + case QRhiTexture::BGRA8: + bpc = 4; + break; + case QRhiTexture::R8: + bpc = 1; + break; + case QRhiTexture::R16: + bpc = 2; + break; + case QRhiTexture::RED_OR_ALPHA8: + bpc = 1; + break; + + case QRhiTexture::RGBA16F: + bpc = 8; + break; + case QRhiTexture::RGBA32F: + bpc = 16; + break; + + case QRhiTexture::D16: + bpc = 2; + break; + case QRhiTexture::D32F: + bpc = 4; + break; + + default: + Q_UNREACHABLE(); + break; + } + + if (bpl) + *bpl = size.width() * bpc; + if (byteSize) + *byteSize = size.width() * size.height() * bpc; +} + +// Approximate because it excludes subresource alignment or multisampling. +quint32 QRhiImplementation::approxByteSizeForTexture(QRhiTexture::Format format, const QSize &baseSize, + int mipCount, int layerCount) +{ + quint32 approxSize = 0; + for (int level = 0; level < mipCount; ++level) { + quint32 byteSize = 0; + const QSize size(qFloor(float(qMax(1, baseSize.width() >> level))), + qFloor(float(qMax(1, baseSize.height() >> level)))); + textureFormatInfo(format, size, nullptr, &byteSize); + approxSize += byteSize; + } + approxSize *= layerCount; + return approxSize; +} + +/*! + \internal + */ +QRhi::QRhi() +{ +} + +/*! + Destructor. Destroys the backend and releases resources. + */ +QRhi::~QRhi() +{ + if (!d) + return; + + qDeleteAll(d->pendingReleaseAndDestroyResources); + d->pendingReleaseAndDestroyResources.clear(); + + runCleanup(); + + d->destroy(); + delete d; +} + +/*! + \return a new QRhi instance with a backend for the graphics API specified by \a impl. + + \a params must point to an instance of one of the backend-specific + subclasses of QRhiInitParams, such as, QRhiVulkanInitParams, + QRhiMetalInitParams, QRhiD3D11InitParams, QRhiGles2InitParams. See these + classes for examples on creating a QRhi. + + \a flags is optional. It is used to enable profile and debug related + features that are potentially expensive and should only be used during + development. + */ +QRhi *QRhi::create(Implementation impl, QRhiInitParams *params, Flags flags, QRhiNativeHandles *importDevice) +{ + QScopedPointer r(new QRhi); + + switch (impl) { + case Null: + r->d = new QRhiNull(static_cast(params)); + break; + case Vulkan: +#if QT_CONFIG(vulkan) + r->d = new QRhiVulkan(static_cast(params), + static_cast(importDevice)); + break; +#else + qWarning("This build of Qt has no Vulkan support"); + break; +#endif + case OpenGLES2: +#ifndef QT_NO_OPENGL + r->d = new QRhiGles2(static_cast(params), + static_cast(importDevice)); + break; +#else + qWarning("This build of Qt has no OpenGL support"); + break; +#endif + case D3D11: +#ifdef Q_OS_WIN + r->d = new QRhiD3D11(static_cast(params), + static_cast(importDevice)); + break; +#else + qWarning("This platform has no Direct3D 11 support"); + break; +#endif + case Metal: +//#ifdef Q_OS_DARWIN +#ifdef Q_OS_MACOS + r->d = new QRhiMetal(static_cast(params), + static_cast(importDevice)); + break; +#else + qWarning("This platform has no Metal support"); + break; +#endif + default: + break; + } + + if (r->d) { + r->d->q = r.data(); + if (flags.testFlag(EnableProfiling)) { + QRhiProfilerPrivate *profD = QRhiProfilerPrivate::get(&r->d->profiler); + profD->rhiDWhenEnabled = r->d; + } + r->d->debugMarkers = flags.testFlag(EnableDebugMarkers); + if (r->d->create(flags)) { + r->d->implType = impl; + r->d->implThread = QThread::currentThread(); + return r.take(); + } + } + + return nullptr; +} + +/*! + \return the backend type for this QRhi. + */ +QRhi::Implementation QRhi::backend() const +{ + return d->implType; +} + +/*! + \return the thread on which the QRhi was \l{QRhi::create()}{initialized}. + */ +QThread *QRhi::thread() const +{ + return d->implThread; +} + +/*! + Registers a \a callback that is invoked either when the QRhi is destroyed, + or when runCleanup() is called. + + The callback will run with the graphics resource still available, so this + provides an opportunity for the application to cleanly release QRhiResource + instances belonging to the QRhi. This is particularly useful for managing + the lifetime of resources stored in \c cache type of objects, where the + cache holds QRhiResources or objects containing QRhiResources. + + \sa runCleanup(), ~QRhi() + */ +void QRhi::addCleanupCallback(const CleanupCallback &callback) +{ + d->addCleanupCallback(callback); +} + +/*! + Invokes all registered cleanup functions. The list of cleanup callbacks it + then cleared. Normally destroying the QRhi does this automatically, but + sometimes it can be useful to trigger cleanup in order to release all + cached, non-essential resources. + + \sa addCleanupCallback() + */ +void QRhi::runCleanup() +{ + for (const CleanupCallback &f : qAsConst(d->cleanupCallbacks)) + f(this); + + d->cleanupCallbacks.clear(); +} + +/*! + \class QRhiResourceUpdateBatch + \inmodule QtRhi + \brief Records upload and copy type of operations. + + With QRhi it is no longer possible to perform copy type of operations at + arbitrary times. Instead, all such operations are recorded into batches + that are then passed, most commonly, to QRhiCommandBuffer::beginPass(). + What then happens under the hood is hidden from the application: the + underlying implementations can defer and implement these operations in + various different ways. + + A resource update batch owns no graphics resources and does not perform any + actual operations on its own. It should rather be viewed as a command + buffer for update, upload, and copy type of commands. + + To get an available, empty batch from the pool, call + QRhi::nextResourceUpdateBatch(). + */ + +/*! + \internal + */ +QRhiResourceUpdateBatch::QRhiResourceUpdateBatch(QRhiImplementation *rhi) + : d(new QRhiResourceUpdateBatchPrivate) +{ + d->q = this; + d->rhi = rhi; +} + +QRhiResourceUpdateBatch::~QRhiResourceUpdateBatch() +{ + delete d; +} + +/*! + \return the batch to the pool. This should only be used when the batch is + not passed to one of QRhiCommandBuffer::beginPass(), + QRhiCommandBuffer::endPass(), or QRhiCommandBuffer::resourceUpdate() + because these implicitly call release(). + + \note QRhiResourceUpdateBatch instances must never by \c deleted by + applications. + */ +void QRhiResourceUpdateBatch::release() +{ + d->free(); +} + +/*! + Copies all queued operations from the \a other batch into this one. + + \note \a other is not changed in any way, typically it will still need a + release() + + This allows for a convenient pattern where resource updates that are + already known during the initialization step are collected into a batch + that is then merged into another when starting to first render pass later + on: + + \badcode + void init() + { + ... + initialUpdates = rhi->nextResourceUpdateBatch(); + initialUpdates->uploadStaticBuffer(vbuf, vertexData); + initialUpdates->uploadStaticBuffer(ibuf, indexData); + ... + } + + void render() + { + ... + QRhiResourceUpdateBatch *resUpdates = rhi->nextResourceUpdateBatch(); + if (initialUpdates) { + resUpdates->merge(initialUpdates); + initialUpdates->release(); + initialUpdates = nullptr; + } + resUpdates->updateDynamicBuffer(...); + ... + cb->beginPass(rt, clearCol, clearDs, resUpdates); + } + \endcode + */ +void QRhiResourceUpdateBatch::merge(QRhiResourceUpdateBatch *other) +{ + d->merge(other->d); +} + +/*! + Enqueues updating a region of a QRhiBuffer \a buf created with the type + QRhiBuffer::Dynamic. + + The region is specified \a offset and \a size. The actual bytes to write + are specified by \a data which must have at least \a size bytes available. + \a data can safely be destroyed or changed once this function returns. + + \note If host writes are involved, which is the case with + updateDynamicBuffer() typically as such buffers are backed by host visible + memory with most backends, they may accumulate within a frame. Thus pass 1 + reading a region changed by a batch passed to pass 2 may see the changes + specified in pass 2's update batch. + + \note QRhi transparently manages double buffering in order to prevent + stalling the graphics pipeline. The fact that a QRhiBuffer may have + multiple native underneath can be safely ignored when using the QRhi and + QRhiResourceUpdateBatch. + */ +void QRhiResourceUpdateBatch::updateDynamicBuffer(QRhiBuffer *buf, int offset, int size, const void *data) +{ + if (size > 0) + d->dynamicBufferUpdates.append({ buf, offset, size, data }); +} + +/*! + Enqueues updating a region of a QRhiBuffer \a buf created with the type + QRhiBuffer::Immutable or QRhiBuffer::Static. + + The region is specified \a offset and \a size. The actual bytes to write + are specified by \a data which must have at least \a size bytes available. + \a data can safely be destroyed or changed once this function returns. + */ +void QRhiResourceUpdateBatch::uploadStaticBuffer(QRhiBuffer *buf, int offset, int size, const void *data) +{ + if (size > 0) + d->staticBufferUploads.append({ buf, offset, size, data }); +} + +/*! + Enqueues updating the entire QRhiBuffer \a buf created with the type + QRhiBuffer::Immutable or QRhiBuffer::Static. + */ +void QRhiResourceUpdateBatch::uploadStaticBuffer(QRhiBuffer *buf, const void *data) +{ + if (buf->size() > 0) + d->staticBufferUploads.append({ buf, 0, 0, data }); +} + +/*! + Enqueues uploading the image data for one or more mip levels in one or more + layers of the texture \a tex. + + The details of the copy (source QImage or compressed texture data, regions, + target layers and levels) are described in \a desc. + */ +void QRhiResourceUpdateBatch::uploadTexture(QRhiTexture *tex, const QRhiTextureUploadDescription &desc) +{ + if (!desc.entries().isEmpty()) + d->textureOps.append(QRhiResourceUpdateBatchPrivate::TextureOp::textureUpload(tex, desc)); +} + +/*! + Enqueues uploading the image data for mip level 0 of layer 0 of the texture + \a tex. + + \a tex must have an uncompressed format. Its format must also be compatible + with the QImage::format() of \a image. The source data is given in \a + image. + */ +void QRhiResourceUpdateBatch::uploadTexture(QRhiTexture *tex, const QImage &image) +{ + uploadTexture(tex, QRhiTextureUploadEntry(0, 0, image)); +} + +/*! + Enqueues a texture-to-texture copy operation from \a src into \a dst as + described by \a desc. + + \note The source texture \a src must be created with + QRhiTexture::UsedAsTransferSource. + */ +void QRhiResourceUpdateBatch::copyTexture(QRhiTexture *dst, QRhiTexture *src, const QRhiTextureCopyDescription &desc) +{ + d->textureOps.append(QRhiResourceUpdateBatchPrivate::TextureOp::textureCopy(dst, src, desc)); +} + +/*! + Enqueues a texture-to-host copy operation as described by \a rb. + + Normally \a rb will specify a QRhiTexture as the source. However, when the + swapchain in the current frame was created with + QRhiSwapChain::UsedAsTransferSource, it can also be the source of the + readback. For this, leave the texture set to null in \a rb. + + Unlike other operations, the results here need to be processed by the + application. Therefore, \a result provides not just the data but also a + callback as operations on the batch are asynchronous by nature: + + \badcode + beginFrame(sc); + beginPass + ... + QRhiReadbackResult *rbResult = new QRhiReadbackResult; + rbResult->completed = [rbResult] { + { + const QImage::Format fmt = QImage::Format_RGBA8888_Premultiplied; // fits QRhiTexture::RGBA8 + const uchar *p = reinterpret_cast(rbResult->data.constData()); + QImage image(p, rbResult->pixelSize.width(), rbResult->pixelSize.height(), fmt); + image.save("result.png"); + } + delete rbResult; + }; + u = nextResourceUpdateBatch(); + QRhiReadbackDescription rb; // no texture -> uses the current backbuffer of sc + u->readBackTexture(rb, rbResult); + endPass(u); + endFrame(sc); + \endcode + + \note The texture must be created with QRhiTexture::UsedAsTransferSource. + + \note Multisample textures cannot be read back. + + \note The readback returns raw byte data, in order to allow the applications + to interpret it in any way they see fit. Be aware of the blending settings + of rendering code: if the blending is set up to rely on premultiplied alpha, + the results of the readback must also be interpreted as Premultiplied. + + \note When interpreting the resulting raw data, be aware that the readback + happens with a byte ordered format. A \l{QRhiTexture::RGBA8}{RGBA8} texture + maps therefore to byte ordered QImage formats, such as, + QImage::Format_RGBA8888. + */ +void QRhiResourceUpdateBatch::readBackTexture(const QRhiReadbackDescription &rb, QRhiReadbackResult *result) +{ + d->textureOps.append(QRhiResourceUpdateBatchPrivate::TextureOp::textureRead(rb, result)); +} + +/*! + Enqueues a mipmap generation operation for the specified \a layer of texture + \a tex. + + \note The texture must be created with QRhiTexture::MipMapped and + QRhiTexture::UsedWithGenerateMips. + */ +void QRhiResourceUpdateBatch::generateMips(QRhiTexture *tex, int layer) +{ + d->textureOps.append(QRhiResourceUpdateBatchPrivate::TextureOp::textureMipGen(tex, layer)); +} + +/*! + \return an available, empty batch to which copy type of operations can be + recorded. + + \note the return value is not owned by the caller and must never be + destroyed. Instead, the batch is returned the the pool for reuse by passing + it to QRhiCommandBuffer::beginPass(), QRhiCommandBuffer::endPass(), or + QRhiCommandBuffer::resourceUpdate(), or by calling + QRhiResourceUpdateBatch::release() on it. + + \note Can be called outside beginFrame() - endFrame() as well since a batch + instance just collects data on its own, it does not perform any operations. + */ +QRhiResourceUpdateBatch *QRhi::nextResourceUpdateBatch() +{ + auto nextFreeBatch = [this]() -> QRhiResourceUpdateBatch * { + for (int i = 0, ie = d->resUpdPoolMap.count(); i != ie; ++i) { + if (!d->resUpdPoolMap.testBit(i)) { + d->resUpdPoolMap.setBit(i); + QRhiResourceUpdateBatch *u = d->resUpdPool[i]; + QRhiResourceUpdateBatchPrivate::get(u)->poolIndex = i; + return u; + } + } + return nullptr; + }; + + QRhiResourceUpdateBatch *u = nextFreeBatch(); + if (!u) { + const int oldSize = d->resUpdPool.count(); + const int newSize = oldSize + 4; + d->resUpdPool.resize(newSize); + d->resUpdPoolMap.resize(newSize); + for (int i = oldSize; i < newSize; ++i) + d->resUpdPool[i] = new QRhiResourceUpdateBatch(d); + u = nextFreeBatch(); + Q_ASSERT(u); + } + + return u; +} + +void QRhiResourceUpdateBatchPrivate::free() +{ + Q_ASSERT(poolIndex >= 0 && rhi->resUpdPool[poolIndex] == q); + + dynamicBufferUpdates.clear(); + staticBufferUploads.clear(); + textureOps.clear(); + + rhi->resUpdPoolMap.clearBit(poolIndex); + poolIndex = -1; +} + +void QRhiResourceUpdateBatchPrivate::merge(QRhiResourceUpdateBatchPrivate *other) +{ + dynamicBufferUpdates += other->dynamicBufferUpdates; + staticBufferUploads += other->staticBufferUploads; + textureOps += other->textureOps; +} + +/*! + Sometimes committing resource updates is necessary without starting a + render pass. Not often needed, updates should typically be passed to + beginPass (or endPass, in case of readbacks) instead. + + \note Cannot be called inside a pass. + */ +void QRhiCommandBuffer::resourceUpdate(QRhiResourceUpdateBatch *resourceUpdates) +{ + if (resourceUpdates) + m_rhi->resourceUpdate(this, resourceUpdates); +} + +/*! + Records starting a new render pass targeting the render target \a rt. + + \a resourceUpdates, when not null, specifies a resource update batch that + is to be committed and then released. + + The color and depth/stencil buffers of the render target are normally + cleared. The clear values are specified in \a colorClearValue and \a + depthStencilClearValue. The exception is when the render target was created + with QRhiTextureRenderTarget::PreserveColorContents and/or + QRhiTextureRenderTarget::PreserveDepthStencilContents. The clear values are + ignored then. + + \note Enabling preserved color or depth contents leads to decreased + performance depending on the underlying hardware. Mobile GPUs with tiled + architecture benefit from not having to reload the previous contents into + the tile buffer. Similarly, a QRhiTextureRenderTarget with a QRhiTexture as + the depth buffer is less efficient than a QRhiRenderBuffer since using a + depth texture triggers requiring writing the data out to it, while with + renderbuffers this is not needed (as the API does not allow sampling or + reading from a renderbuffer). + + \note Do not assume that any state or resource bindings persist between + passes. + + \note The QRhiCommandBuffer's \c set and \c draw functions can only be + called inside a pass. Also, with the exception of setGraphicsPipeline(), + they expect to have a pipeline set already on the command buffer. + Unspecified issues may arise otherwise, depending on the backend. + */ +void QRhiCommandBuffer::beginPass(QRhiRenderTarget *rt, + const QColor &colorClearValue, + const QRhiDepthStencilClearValue &depthStencilClearValue, + QRhiResourceUpdateBatch *resourceUpdates) +{ + m_rhi->beginPass(this, rt, colorClearValue, depthStencilClearValue, resourceUpdates); +} + +/*! + Records ending the current render pass. + + \a resourceUpdates, when not null, specifies a resource update batch that + is to be committed and then released. + */ +void QRhiCommandBuffer::endPass(QRhiResourceUpdateBatch *resourceUpdates) +{ + m_rhi->endPass(this, resourceUpdates); +} + +/*! + Records setting a new graphics pipeline \a ps. + + \note This function must be called before recording other \c set or \c draw + commands on the command buffer. + + \note QRhi will optimize out unnecessary invocations within a pass, so + therefore overoptimizing to avoid calls to this function is not necessary + on the applications' side. + + \note This function can only be called inside a pass, meaning between a + beginPass() end endPass() call. + */ +void QRhiCommandBuffer::setGraphicsPipeline(QRhiGraphicsPipeline *ps) +{ + m_rhi->setGraphicsPipeline(this, ps); +} + +/*! + Records binding a set of shader resources, such as, uniform buffers or + textures, that are made visible to one or more shader stages. + + \a srb can be null in which case the current graphics pipeline's associated + QRhiGraphicsPipeline::shaderResourceBindings() is used. When \a srb is + non-null, it must be + \l{QRhiShaderResourceBindings::isLayoutCompatible()}{layout-compatible}, + meaning the layout (number of bindings, the type and binding number of each + binding) must fully match the QRhiShaderResourceBindings that was + associated with the pipeline at the time of calling + QRhiGraphicsPipeline::build(). + + There are cases when a seemingly unnecessary setShaderResources() call is + mandatory: when rebuilding a resource referenced from \a srb, for example + changing the size of a QRhiBuffer followed by a QRhiBuffer::build(), this + is the place where associated native objects (such as descriptor sets in + case of Vulkan) are updated to refer to the current native resources that + back the QRhiBuffer, QRhiTexture, QRhiSampler objects referenced from \a + srb. In this case setShaderResources() must be called even if \a srb is + the same as in the last call. + + \a dynamicOffsets allows specifying buffer offsets for uniform buffers that + were associated with \a srb via + QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(). This is + different from providing the offset in the \a srb itself: dynamic offsets + do not require building a new QRhiShaderResourceBindings for every + different offset, can avoid writing the underlying descriptors (with + backends where applicable), and so they may be more efficient. Each element + of \a dynamicOffsets is a \c binding - \c offset pair. + \a dynamicOffsetCount specifies the number of elements in \a dynamicOffsets. + + \note All offsets in \a dynamicOffsets must be byte aligned to the value + returned from QRhi::ubufAlignment(). + + \note QRhi will optimize out unnecessary invocations within a pass (taking + the conditions described above into account), so therefore overoptimizing + to avoid calls to this function is not necessary on the applications' side. + + \note This function can only be called inside a pass, meaning between a + beginPass() end endPass() call. + */ +void QRhiCommandBuffer::setShaderResources(QRhiShaderResourceBindings *srb, + int dynamicOffsetCount, + const DynamicOffset *dynamicOffsets) +{ + m_rhi->setShaderResources(this, srb, dynamicOffsetCount, dynamicOffsets); +} + +/*! + Records vertex input bindings. + + The index buffer used by subsequent drawIndexed() commands is specified by + \a indexBuf, \a indexOffset, and \a indexFormat. \a indexBuf can be set to + null when indexed drawing is not needed. + + Vertex buffer bindings are batched. \a startBinding specifies the first + binding number. The recorded command then binds each buffer from \a + bindings to the binding point \c{startBinding + i} where \c i is the index + in \a bindings. Each element in \a bindings specifies a QRhiBuffer and an + offset. + + Superfluous vertex input and index changes in the same pass are ignored + automatically with most backends and therefore applications do not need to + overoptimize to avoid calls to this function. + + \note This function can only be called inside a pass, meaning between a + beginPass() end endPass() call. + + As a simple example, take a vertex shader with two inputs: + + \badcode + layout(location = 0) in vec4 position; + layout(location = 1) in vec3 color; + \endcode + + and assume we have the data available in interleaved format, using only 2 + floats for position (so 5 floats per vertex: x, y, r, g, b). A QRhiGraphicsPipeline for + this shader can then be created using the input layout: + + \badcode + QRhiVertexInputLayout inputLayout; + inputLayout.setBindings({ + { 5 * sizeof(float) } + }); + inputLayout.setAttributes({ + { 0, 0, QRhiVertexInputAttribute::Float2, 0 }, + { 0, 1, QRhiVertexInputAttribute::Float3, 2 * sizeof(float) } + }); + \endcode + + Here there is one buffer binding (binding number 0), with two inputs + referencing it. When recording the pass, once the pipeline is set, the + vertex bindings can be specified simply like the following (using C++11 + initializer syntax), assuming vbuf is the QRhiBuffer with all the + interleaved position+color data: + + \badcode + const QRhiCommandBuffer::VertexInput vbufBinding(vbuf, 0); + cb->setVertexInput(0, 1, &vbufBinding); + \endcode + */ +void QRhiCommandBuffer::setVertexInput(int startBinding, int bindingCount, const VertexInput *bindings, + QRhiBuffer *indexBuf, quint32 indexOffset, + IndexFormat indexFormat) +{ + m_rhi->setVertexInput(this, startBinding, bindingCount, bindings, indexBuf, indexOffset, indexFormat); +} + +/*! + Records setting the active viewport rectangle specified in \a viewport. + + With backends where the underlying graphics API has scissoring always + enabled, this function also sets the scissor to match the viewport whenever + the active QRhiGraphicsPipeline does not have + \l{QRhiGraphicsPipeline::UsesScissor}{UsesScissor} set. + + \note QRhi assumes OpenGL-style viewport coordinates, meaning x and y are + bottom-left. + + \note This function can only be called inside a pass, meaning between a + beginPass() end endPass() call. + */ +void QRhiCommandBuffer::setViewport(const QRhiViewport &viewport) +{ + m_rhi->setViewport(this, viewport); +} + +/*! + Records setting the active scissor rectangle specified in \a scissor. + + This can only be called when the bound pipeline has + \l{QRhiGraphicsPipeline::UsesScissor}{UsesScissor} set. When the flag is + set on the active pipeline, this function must be called because scissor + testing will get enabled and so a scissor rectangle must be provided. + + \note QRhi assumes OpenGL-style viewport coordinates, meaning x and y are + bottom-left. + + \note This function can only be called inside a pass, meaning between a + beginPass() end endPass() call. + */ +void QRhiCommandBuffer::setScissor(const QRhiScissor &scissor) +{ + m_rhi->setScissor(this, scissor); +} + +/*! + Records setting the active blend constants to \a c. + + This can only be called when the bound pipeline has + QRhiGraphicsPipeline::UsesBlendConstants set. + + \note This function can only be called inside a pass, meaning between a + beginPass() end endPass() call. + */ +void QRhiCommandBuffer::setBlendConstants(const QColor &c) +{ + m_rhi->setBlendConstants(this, c); +} + +/*! + Records setting the active stencil reference value to \a refValue. + + This can only be called when the bound pipeline has + QRhiGraphicsPipeline::UsesStencilRef set. + + \note This function can only be called inside a pass, meaning between a + beginPass() end endPass() call. + */ +void QRhiCommandBuffer::setStencilRef(quint32 refValue) +{ + m_rhi->setStencilRef(this, refValue); +} + +/*! + Records a non-indexed draw. + + The number of vertices is specified in \a vertexCount. For instanced + drawing set \a instanceCount to a value other than 1. \a firstVertex is + the index of the first vertex to draw. \a firstInstance is the instance ID + of the first instance to draw. + + \note This function can only be called inside a pass, meaning between a + beginPass() end endPass() call. + */ +void QRhiCommandBuffer::draw(quint32 vertexCount, + quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) +{ + m_rhi->draw(this, vertexCount, instanceCount, firstVertex, firstInstance); +} + +/*! + Records an indexed draw. + + The number of vertices is specified in \a indexCount. \a firstIndex is the + base index. The effective offset in the index buffer is given by + \c{indexOffset + firstIndex * n} where \c n is 2 or 4 depending on the + index element type. \c indexOffset is specified in setVertexInput(). + + \note The effective offset in the index buffer must be 4 byte aligned with + some backends (for example, Metal). With these backends the + \l{QRhi::NonFourAlignedEffectiveIndexBufferOffset}{NonFourAlignedEffectiveIndexBufferOffset} + feature will be reported as not-supported. + + For instanced drawing set \a instanceCount to a value other than 1. \a + firstInstance is the instance ID of the first instance to draw. + + \a vertexOffset is added to the vertex index. + + \note This function can only be called inside a pass, meaning between a + beginPass() end endPass() call. + */ +void QRhiCommandBuffer::drawIndexed(quint32 indexCount, + quint32 instanceCount, quint32 firstIndex, + qint32 vertexOffset, quint32 firstInstance) +{ + m_rhi->drawIndexed(this, indexCount, instanceCount, firstIndex, vertexOffset, firstInstance); +} + +/*! + Records a named debug group on the command buffer. This is shown in + graphics debugging tools such as \l{https://renderdoc.org/}{RenderDoc} and + \l{https://developer.apple.com/xcode/}{XCode}. The end of the grouping is + indicated by debugMarkEnd(). + + \note Ignored when QRhi::DebugMarkers are not supported or + QRhi::EnableDebugMarkers is not set. + + \note Can be called anywhere within the frame, both inside and outside of passes. + */ +void QRhiCommandBuffer::debugMarkBegin(const QByteArray &name) +{ + m_rhi->debugMarkBegin(this, name); +} + +/*! + Records the end of a debug group. + + \note Ignored when QRhi::DebugMarkers are not supported or + QRhi::EnableDebugMarkers is not set. + + \note Can be called anywhere within the frame, both inside and outside of passes. + */ +void QRhiCommandBuffer::debugMarkEnd() +{ + m_rhi->debugMarkEnd(this); +} + +/*! + Inserts a debug message \a msg into the command stream. + + \note Ignored when QRhi::DebugMarkers are not supported or + QRhi::EnableDebugMarkers is not set. + + \note With some backends debugMarkMsg() is only supported inside a pass and + is ignored when called outside a pass. With others it is recorded anywhere + within the frame. + */ +void QRhiCommandBuffer::debugMarkMsg(const QByteArray &msg) +{ + m_rhi->debugMarkMsg(this, msg); +} + +/*! + \return a pointer to a backend-specific QRhiNativeHandles subclass, such as + QRhiVulkanCommandBufferNativeHandles. The returned value is null when + exposing the underlying native resources is not supported by, or not + applicable to, the backend. + + \sa QRhiVulkanCommandBufferNativeHandles, + QRhiMetalCommandBufferNativeHandles, beginExternal(), endExternal() + */ +const QRhiNativeHandles *QRhiCommandBuffer::nativeHandles() +{ + return m_rhi->nativeHandles(this); +} + +/*! + To be called when the application before the application is about to + enqueue commands to the current pass' command buffer by calling graphics + API functions directly. + + With Vulkan or Metal one can query the native command buffer or encoder + objects via nativeHandles() and enqueue commands to them. With OpenGL or + Direct3D 11 the (device) context can be retrieved from + QRhi::nativeHandles(). However, this must never be done without ensuring + the QRhiCommandBuffer's state stays up-to-date. Hence the requirement for + wrapping any externally added command recording between beginExternal() and + endExternal(). Conceptually this is the same as QPainter's + \l{QPainter::beginNativePainting()}{beginNativePainting()} and + \l{QPainter::endNativePainting()}{endNativePainting()} functions. + + For OpenGL in particular, this function has an additional task: it makes + sure the context is made current on the current thread. + + \note Once beginExternal() is called, no other render pass specific + functions (\c set* or \c draw*) must be called on the + QRhiCommandBuffer until endExternal(). + + \sa endExternal(), nativeHandles() + */ +void QRhiCommandBuffer::beginExternal() +{ + m_rhi->beginExternal(this); +} + +/*! + To be called once the externally added commands are recorded to the command + buffer or context. + + \note All QRhiCommandBuffer state must be assumed as invalid after calling + this function. Pipelines, vertex and index buffers, and other state must be + set again if more draw calls are recorded after the external commands. + + \sa beginExternal(), nativeHandles() + */ +void QRhiCommandBuffer::endExternal() +{ + m_rhi->endExternal(this); +} + +/*! + \return the value (typically an offset) \a v aligned to the uniform buffer + alignment given by by ubufAlignment(). + */ +int QRhi::ubufAligned(int v) const +{ + const int byteAlign = ubufAlignment(); + return (v + byteAlign - 1) & ~(byteAlign - 1); +} + +/*! + \return the number of mip levels for a given \a size. + */ +int QRhi::mipLevelsForSize(const QSize &size) const +{ + return qFloor(std::log2(qMax(size.width(), size.height()))) + 1; +} + +/*! + \return the texture image size for a given \a mipLevel, calculated based on + the level 0 size given in \a baseLevelSize. + */ +QSize QRhi::sizeForMipLevel(int mipLevel, const QSize &baseLevelSize) const +{ + const int w = qMax(1, baseLevelSize.width() >> mipLevel); + const int h = qMax(1, baseLevelSize.height() >> mipLevel); + return QSize(w, h); +} + +/*! + \return \c true if the underlying graphics API has the Y axis pointing up + in framebuffers and images. + + In practice this is \c true for OpenGL only. + */ +bool QRhi::isYUpInFramebuffer() const +{ + return d->isYUpInFramebuffer(); +} + +/*! + \return \c true if the underlying graphics API has the Y axis pointing up + in its normalized device coordinate system. + + In practice this is \c false for Vulkan only. + + \note clipSpaceCorrMatrix() includes the corresponding adjustment (to make + Y point up) in its returned matrix. + */ +bool QRhi::isYUpInNDC() const +{ + return d->isYUpInNDC(); +} + +/*! + \return \c true if the underlying graphics API uses depth 0 - 1 in clip + space. + + In practice this is \c false for OpenGL only. + + \note clipSpaceCorrMatrix() includes the corresponding adjustment in its + returned matrix. + */ +bool QRhi::isClipDepthZeroToOne() const +{ + return d->isClipDepthZeroToOne(); +} + +/*! + \return a matrix that can be used to allow applications keep using + OpenGL-targeted vertex data and perspective projection matrices (such as, + the ones generated by QMatrix4x4::perspective()), regardless of the + backend. Once \c{this_matrix * mvp} is used instead of just \c mvp, vertex + data with Y up and viewports with depth range 0 - 1 can be used without + considering what backend and so graphics API is going to be used at run + time. + + See + \l{https://matthewwellings.com/blog/the-new-vulkan-coordinate-system/}{this + page} for a discussion of the topic from Vulkan perspective. + */ +QMatrix4x4 QRhi::clipSpaceCorrMatrix() const +{ + return d->clipSpaceCorrMatrix(); +} + +/*! + \return \c true if the specified texture \a format modified by \a flags is + supported. + + The query is supported both for uncompressed and compressed formats. + */ +bool QRhi::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const +{ + return d->isTextureFormatSupported(format, flags); +} + +/*! + \return \c true if the specified \a feature is supported + */ +bool QRhi::isFeatureSupported(QRhi::Feature feature) const +{ + return d->isFeatureSupported(feature); +} + +/*! + \return the value for the specified resource \a limit. + + The values are expected to be queried by the backends upon initialization, + meaning calling this function is a light operation. + */ +int QRhi::resourceLimit(ResourceLimit limit) const +{ + return d->resourceLimit(limit); +} + +/*! + \return a pointer to the backend-specific collection of native objects + for the device, context, and similar concepts used by the backend. + + Cast to QRhiVulkanNativeHandles, QRhiD3D11NativeHandles, + QRhiGles2NativeHandles, QRhiMetalNativeHandles as appropriate. + + \note No ownership is transferred, neither for the returned pointer nor for + any native objects. + */ +const QRhiNativeHandles *QRhi::nativeHandles() +{ + return d->nativeHandles(); +} + +/*! + \return the associated QRhiProfiler instance. + + An instance is always available for each QRhi, but it is not very useful + without EnableProfiling because no data is collected without setting the + flag upon creation. + */ +QRhiProfiler *QRhi::profiler() +{ + return &d->profiler; +} + +/*! + \return a new graphics pipeline resource. + + \sa QRhiResource::release() + */ +QRhiGraphicsPipeline *QRhi::newGraphicsPipeline() +{ + return d->createGraphicsPipeline(); +} + +/*! + \return a new shader resource binding collection resource. + + \sa QRhiResource::release() + */ +QRhiShaderResourceBindings *QRhi::newShaderResourceBindings() +{ + return d->createShaderResourceBindings(); +} + +/*! + \return a new buffer with the specified \a type, \a usage, and \a size. + + \note Some \a usage and \a type combinations may not be supported by all + backends. See \l{QRhi::NonDynamicUniformBuffers}{the feature flags}. + + \sa QRhiResource::release() + */ +QRhiBuffer *QRhi::newBuffer(QRhiBuffer::Type type, + QRhiBuffer::UsageFlags usage, + int size) +{ + return d->createBuffer(type, usage, size); +} + +/*! + \return a new renderbuffer with the specified \a type, \a pixelSize, \a + sampleCount, and \a flags. + + \sa QRhiResource::release() + */ +QRhiRenderBuffer *QRhi::newRenderBuffer(QRhiRenderBuffer::Type type, + const QSize &pixelSize, + int sampleCount, + QRhiRenderBuffer::Flags flags) +{ + return d->createRenderBuffer(type, pixelSize, sampleCount, flags); +} + +/*! + \return a new texture with the specified \a format, \a pixelSize, \a + sampleCount, and \a flags. + + \note \a format specifies the requested internal and external format, + meaning the data to be uploaded to the texture will need to be in a + compatible format, while the native texture may (but is not guaranteed to, + in case of OpenGL at least) use this format internally. + + \sa QRhiResource::release() + */ +QRhiTexture *QRhi::newTexture(QRhiTexture::Format format, + const QSize &pixelSize, + int sampleCount, + QRhiTexture::Flags flags) +{ + return d->createTexture(format, pixelSize, sampleCount, flags); +} + +/*! + \return a new sampler with the specified magnification filter \a magFilter, + minification filter \a minFilter, mipmapping mode \a mipmapMpde, and S/T + addressing modes \a u and \a v. + + \sa QRhiResource::release() + */ +QRhiSampler *QRhi::newSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, + QRhiSampler::Filter mipmapMode, + QRhiSampler:: AddressMode u, QRhiSampler::AddressMode v) +{ + return d->createSampler(magFilter, minFilter, mipmapMode, u, v); +} + +/*! + \return a new texture render target with color and depth/stencil + attachments given in \a desc, and with the specified \a flags. + + \sa QRhiResource::release() + */ + +QRhiTextureRenderTarget *QRhi::newTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc, + QRhiTextureRenderTarget::Flags flags) +{ + return d->createTextureRenderTarget(desc, flags); +} + +/*! + \return a new swapchain. + + \sa QRhiResource::release(), QRhiSwapChain::buildOrResize() + */ +QRhiSwapChain *QRhi::newSwapChain() +{ + return d->createSwapChain(); +} + +/*! + Starts a new frame targeting the next available buffer of \a swapChain. + + The high level pattern of rendering into a QWindow using a swapchain: + + \list + + \li Create a swapchain. + + \li Call QRhiSwapChain::buildOrResize() whenever the surface size is + different than before. + + \li Call QRhiSwapChain::release() on + QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed. + + \li Then on every frame: + \badcode + beginFrame(sc); + updates = nextResourceUpdateBatch(); + updates->... + QRhiCommandBuffer *cb = sc->currentFrameCommandBuffer(); + cb->beginPass(sc->currentFrameRenderTarget(), colorClear, dsClear, updates); + ... + cb->endPass(); + ... // more passes as necessary + endFrame(sc); + \endcode + + \endlist + + \a flags is currently unused. + + \sa endFrame() + */ +QRhi::FrameOpResult QRhi::beginFrame(QRhiSwapChain *swapChain, BeginFrameFlags flags) +{ + if (d->inFrame) + qWarning("Attempted to call beginFrame() within a still active frame; ignored"); + + QRhi::FrameOpResult r = !d->inFrame ? d->beginFrame(swapChain, flags) : FrameOpSuccess; + if (r == FrameOpSuccess) + d->inFrame = true; + + return r; +} + +/*! + Ends, commits, and presents a frame that was started in the last + beginFrame() on \a swapChain. + + Double (or triple) buffering is managed internally by the QRhiSwapChain and + QRhi. + + \a flags can optionally be used to change the behavior in certain ways. + Passing QRhi::SkipPresent skips queuing the Present command or calling + swapBuffers. + + \sa beginFrame() + */ +QRhi::FrameOpResult QRhi::endFrame(QRhiSwapChain *swapChain, EndFrameFlags flags) +{ + if (!d->inFrame) + qWarning("Attempted to call endFrame() without an active frame; ignored"); + + QRhi::FrameOpResult r = d->inFrame ? d->endFrame(swapChain, flags) : FrameOpSuccess; + d->inFrame = false; + // releaseAndDestroyLater is a high level QRhi concept the backends know + // nothing about - handle it here. + qDeleteAll(d->pendingReleaseAndDestroyResources); + d->pendingReleaseAndDestroyResources.clear(); + + return r; +} + +/*! + \return true when there is an active frame, meaning there was a + beginFrame() (or beginOffscreenFrame()) with no corresponding endFrame() + (or endOffscreenFrame()) yet. + + \sa currentFrameSlot(), beginFrame(), endFrame() + */ +bool QRhi::isRecordingFrame() const +{ + return d->inFrame; +} + +/*! + \return the current frame slot index while recording a frame. Unspecified + when called outside an active frame (that is, when isRecordingFrame() is \c + false). + + With backends like Vulkan or Metal, it is the responsibility of the QRhi + backend to block whenever starting a new frame and finding the CPU is + already \c{FramesInFlight - 1} frames ahead of the GPU (because the command + buffer submitted in frame no. \c{current} - \c{FramesInFlight} has not yet + completed). + + Resources that tend to change between frames (such as, the native buffer + object backing a QRhiBuffer with type QRhiBuffer::Dynamic) exist in + multiple versions, so that each frame, that can be submitted while a + previous one is still being processed, works with its own copy, thus + avoiding the need to stall the pipeline when preparing the frame. (The + contents of a resource that may still be in use in the GPU should not be + touched, but simply always waiting for the previous frame to finish would + reduce GPU utilization and ultimately, performance and efficiency.) + + Conceptually this is somewhat similar to copy-on-write schemes used by some + C++ containers and other types. It may also be similar to what an OpenGL or + Direct 3D 11 implementation performs internally for certain type of objects. + + In practice, such double (or tripple) buffering resources is realized in + the Vulkan, Metal, and similar QRhi backends by having a fixed number of + native resource (such as, VkBuffer) \c slots behind a QRhiResource. That + can then be indexed by a frame slot index running 0, 1, .., + FramesInFlight-1, and then wrapping around. + + All this is managed transparently to the users of QRhi. However, + applications that integrate rendering done directly with the graphics API + may want to perform a similar double or tripple buffering of their own + graphics resources. That is then most easily achieved by knowing the values + of the maximum number of in-flight frames (retrievable via resourceLimit()) + and the current frame (slot) index (returned by this function). + + \sa isRecordingFrame(), beginFrame(), endFrame() + */ +int QRhi::currentFrameSlot() const +{ + return d->currentFrameSlot; +} + +/*! + Starts a new offscreen frame. Provides a command buffer suitable for + recording rendering commands in \a cb. + + \note The QRhiCommandBuffer stored to *cb is not owned by the caller. + + Rendering without a swapchain is possible as well. The typical use case is + to use it in completely offscreen applications, e.g. to generate image + sequences by rendering and reading back without ever showing a window. + + Usage in on-screen applications (so beginFrame, endFrame, + beginOffscreenFrame, endOffscreenFrame, beginFrame, ...) is possible too + but it does reduce parallelism so it should be done only infrequently. + + Offscreen frames do not let the CPU - potentially - generate another frame + while the GPU is still processing the previous one. This has the side + effect that if readbacks are scheduled, the results are guaranteed to be + available once endOffscreenFrame() returns. That is not the case with + frames targeting a swapchain. + + The skeleton of rendering a frame without a swapchain and then reading the + frame contents back could look like the following: + + \badcode + QRhiReadbackResult rbResult; + QRhiCommandBuffer *cb; + beginOffscreenFrame(&cb); + beginPass + ... + u = nextResourceUpdateBatch(); + u->readBackTexture(rb, &rbResult); + endPass(u); + endOffscreenFrame(); + // image data available in rbResult + \endcode + + \sa endOffscreenFrame() + */ +QRhi::FrameOpResult QRhi::beginOffscreenFrame(QRhiCommandBuffer **cb) +{ + if (d->inFrame) + qWarning("Attempted to call beginOffscreenFrame() within a still active frame; ignored"); + + QRhi::FrameOpResult r = !d->inFrame ? d->beginOffscreenFrame(cb) : FrameOpSuccess; + if (r == FrameOpSuccess) + d->inFrame = true; + + return r; +} + +/*! + Ends and waits for the offscreen frame. + + \sa beginOffscreenFrame() + */ +QRhi::FrameOpResult QRhi::endOffscreenFrame() +{ + if (!d->inFrame) + qWarning("Attempted to call endOffscreenFrame() without an active frame; ignored"); + + QRhi::FrameOpResult r = d->inFrame ? d->endOffscreenFrame() : FrameOpSuccess; + d->inFrame = false; + qDeleteAll(d->pendingReleaseAndDestroyResources); + d->pendingReleaseAndDestroyResources.clear(); + + return r; +} + +/*! + Waits for any work on the graphics queue (where applicable) to complete, + then executes all deferred operations, like completing readbacks and + resource releases. Can be called inside and outside of a frame, but not + inside a pass. Inside a frame it implies submitting any work on the + command buffer. + + \note Avoid this function. One case where it may be needed is when the + results of an enqueued readback in a swapchain-based frame are needed at a + fixed given point and so waiting for the results is desired. + */ +QRhi::FrameOpResult QRhi::finish() +{ + return d->finish(); +} + +/*! + \return the list of supported sample counts. + + A typical example would be (1, 2, 4, 8). + + With some backend this list of supported values is fixed in advance, while + with some others the (physical) device properties indicate what is + supported at run time. + */ +QVector QRhi::supportedSampleCounts() const +{ + return d->supportedSampleCounts(); +} + +/*! + \return the minimum uniform buffer offset alignment in bytes. This is + typically 256. + + Attempting to bind a uniform buffer region with an offset not aligned to + this value will lead to failures depending on the backend and the + underlying graphics API. + + \sa ubufAligned() + */ +int QRhi::ubufAlignment() const +{ + return d->ubufAlignment(); +} + +QRhiGlobalObjectIdGenerator::Type QRhiGlobalObjectIdGenerator::newId() +{ + static QRhiGlobalObjectIdGenerator inst; + return ++inst.counter; +} + +bool QRhiPassResourceTracker::isEmpty() const +{ + return m_buffers.isEmpty() && m_textures.isEmpty(); +} + +void QRhiPassResourceTracker::reset() +{ + m_buffers.clear(); + m_textures.clear(); +} + +static inline QRhiPassResourceTracker::BufferStage earlierStage(QRhiPassResourceTracker::BufferStage a, + QRhiPassResourceTracker::BufferStage b) +{ + return QRhiPassResourceTracker::BufferStage(qMin(int(a), int(b))); +} + +void QRhiPassResourceTracker::registerBufferOnce(QRhiBuffer *buf, int slot, BufferAccess access, BufferStage stage, + const UsageState &stateAtPassBegin) +{ + auto it = std::find_if(m_buffers.begin(), m_buffers.end(), [buf](const Buffer &b) { return b.buf == buf; }); + if (it != m_buffers.end()) { + if (it->access != access) { + const QByteArray name = buf->name(); + qWarning("Buffer %p (%s) used with different accesses within the same pass, this is not allowed.", + buf, name.constData()); + return; + } + if (it->stage != stage) + it->stage = earlierStage(it->stage, stage); + // Multiple registrations of the same buffer is fine as long is it is + // a compatible usage. stateAtPassBegin is not actually the state at + // pass begin in the second, third, etc. invocation but that's fine + // since we'll just return here. + return; + } + + Buffer b; + b.buf = buf; + b.slot = slot; + b.access = access; + b.stage = stage; + b.stateAtPassBegin = stateAtPassBegin; + m_buffers.append(b); +} + +static inline QRhiPassResourceTracker::TextureStage earlierStage(QRhiPassResourceTracker::TextureStage a, + QRhiPassResourceTracker::TextureStage b) +{ + return QRhiPassResourceTracker::TextureStage(qMin(int(a), int(b))); +} + +void QRhiPassResourceTracker::registerTextureOnce(QRhiTexture *tex, TextureAccess access, TextureStage stage, + const UsageState &stateAtPassBegin) +{ + auto it = std::find_if(m_textures.begin(), m_textures.end(), [tex](const Texture &t) { return t.tex == tex; }); + if (it != m_textures.end()) { + if (it->access != access) { + const QByteArray name = tex->name(); + qWarning("Texture %p (%s) used with different accesses within the same pass, this is not allowed.", + tex, name.constData()); + } + if (it->stage != stage) + it->stage = earlierStage(it->stage, stage); + // Multiple registrations of the same texture is fine as long is it is + // a compatible usage. stateAtPassBegin is not actually the state at + // pass begin in the second, third, etc. invocation but that's fine + // since we'll just return here. + return; + } + + Texture t; + t.tex = tex; + t.access = access; + t.stage = stage; + t.stateAtPassBegin = stateAtPassBegin; + m_textures.append(t); +} + +QT_END_NAMESPACE diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h new file mode 100644 index 0000000000..a0f57819e4 --- /dev/null +++ b/src/gui/rhi/qrhi_p.h @@ -0,0 +1,1375 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Gui module +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QRHI_H +#define QRHI_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QWindow; +class QRhiImplementation; +class QRhiBuffer; +class QRhiRenderBuffer; +class QRhiTexture; +class QRhiSampler; +class QRhiCommandBuffer; +class QRhiResourceUpdateBatch; +class QRhiResourceUpdateBatchPrivate; +class QRhiProfiler; +class QRhiShaderResourceBindingPrivate; + +class Q_GUI_EXPORT QRhiDepthStencilClearValue +{ +public: + QRhiDepthStencilClearValue() = default; + QRhiDepthStencilClearValue(float d, quint32 s); + + float depthClearValue() const { return m_d; } + void setDepthClearValue(float d) { m_d = d; } + + quint32 stencilClearValue() const { return m_s; } + void setStencilClearValue(quint32 s) { m_s = s; } + +private: + float m_d = 1.0f; + quint32 m_s = 0; +}; + +Q_DECLARE_TYPEINFO(QRhiDepthStencilClearValue, Q_MOVABLE_TYPE); + +Q_GUI_EXPORT bool operator==(const QRhiDepthStencilClearValue &a, const QRhiDepthStencilClearValue &b) Q_DECL_NOTHROW; +Q_GUI_EXPORT bool operator!=(const QRhiDepthStencilClearValue &a, const QRhiDepthStencilClearValue &b) Q_DECL_NOTHROW; +Q_GUI_EXPORT uint qHash(const QRhiDepthStencilClearValue &v, uint seed = 0) Q_DECL_NOTHROW; +#ifndef QT_NO_DEBUG_STREAM +Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiDepthStencilClearValue &); +#endif + +class Q_GUI_EXPORT QRhiViewport +{ +public: + QRhiViewport() = default; + QRhiViewport(float x, float y, float w, float h, float minDepth = 0.0f, float maxDepth = 1.0f); + + std::array viewport() const { return m_rect; } + void setViewport(float x, float y, float w, float h) { + m_rect[0] = x; m_rect[1] = y; m_rect[2] = w; m_rect[3] = h; + } + + float minDepth() const { return m_minDepth; } + void setMinDepth(float minDepth) { m_minDepth = minDepth; } + + float maxDepth() const { return m_maxDepth; } + void setMaxDepth(float maxDepth) { m_maxDepth = maxDepth; } + +private: + std::array m_rect { { 0.0f, 0.0f, 0.0f, 0.0f } }; + float m_minDepth = 0.0f; + float m_maxDepth = 1.0f; +}; + +Q_DECLARE_TYPEINFO(QRhiViewport, Q_MOVABLE_TYPE); + +Q_GUI_EXPORT bool operator==(const QRhiViewport &a, const QRhiViewport &b) Q_DECL_NOTHROW; +Q_GUI_EXPORT bool operator!=(const QRhiViewport &a, const QRhiViewport &b) Q_DECL_NOTHROW; +Q_GUI_EXPORT uint qHash(const QRhiViewport &v, uint seed = 0) Q_DECL_NOTHROW; +#ifndef QT_NO_DEBUG_STREAM +Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiViewport &); +#endif + +class Q_GUI_EXPORT QRhiScissor +{ +public: + QRhiScissor() = default; + QRhiScissor(int x, int y, int w, int h); + + std::array scissor() const { return m_rect; } + void setScissor(int x, int y, int w, int h) { + m_rect[0] = x; m_rect[1] = y; m_rect[2] = w; m_rect[3] = h; + } + +private: + std::array m_rect { { 0, 0, 0, 0 } }; +}; + +Q_DECLARE_TYPEINFO(QRhiScissor, Q_MOVABLE_TYPE); + +Q_GUI_EXPORT bool operator==(const QRhiScissor &a, const QRhiScissor &b) Q_DECL_NOTHROW; +Q_GUI_EXPORT bool operator!=(const QRhiScissor &a, const QRhiScissor &b) Q_DECL_NOTHROW; +Q_GUI_EXPORT uint qHash(const QRhiScissor &v, uint seed = 0) Q_DECL_NOTHROW; +#ifndef QT_NO_DEBUG_STREAM +Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiScissor &); +#endif + +class Q_GUI_EXPORT QRhiVertexInputBinding +{ +public: + enum Classification { + PerVertex, + PerInstance + }; + + QRhiVertexInputBinding() = default; + QRhiVertexInputBinding(quint32 stride, Classification cls = PerVertex, int stepRate = 1); + + quint32 stride() const { return m_stride; } + void setStride(quint32 s) { m_stride = s; } + + Classification classification() const { return m_classification; } + void setClassification(Classification c) { m_classification = c; } + + int instanceStepRate() const { return m_instanceStepRate; } + void setInstanceStepRate(int rate) { m_instanceStepRate = rate; } + +private: + quint32 m_stride = 0; + Classification m_classification = PerVertex; + int m_instanceStepRate = 1; +}; + +Q_DECLARE_TYPEINFO(QRhiVertexInputBinding, Q_MOVABLE_TYPE); + +Q_GUI_EXPORT bool operator==(const QRhiVertexInputBinding &a, const QRhiVertexInputBinding &b) Q_DECL_NOTHROW; +Q_GUI_EXPORT bool operator!=(const QRhiVertexInputBinding &a, const QRhiVertexInputBinding &b) Q_DECL_NOTHROW; +Q_GUI_EXPORT uint qHash(const QRhiVertexInputBinding &v, uint seed = 0) Q_DECL_NOTHROW; +#ifndef QT_NO_DEBUG_STREAM +Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiVertexInputBinding &); +#endif + +class Q_GUI_EXPORT QRhiVertexInputAttribute +{ +public: + enum Format { + Float4, + Float3, + Float2, + Float, + UNormByte4, + UNormByte2, + UNormByte + }; + + QRhiVertexInputAttribute() = default; + QRhiVertexInputAttribute(int binding, int location, Format format, quint32 offset); + + int binding() const { return m_binding; } + void setBinding(int b) { m_binding = b; } + + int location() const { return m_location; } + void setLocation(int loc) { m_location = loc; } + + Format format() const { return m_format; } + void setFormt(Format f) { m_format = f; } + + quint32 offset() const { return m_offset; } + void setOffset(quint32 ofs) { m_offset = ofs; } + +private: + int m_binding = 0; + int m_location = 0; + Format m_format = Float4; + quint32 m_offset = 0; +}; + +Q_DECLARE_TYPEINFO(QRhiVertexInputAttribute, Q_MOVABLE_TYPE); + +Q_GUI_EXPORT bool operator==(const QRhiVertexInputAttribute &a, const QRhiVertexInputAttribute &b) Q_DECL_NOTHROW; +Q_GUI_EXPORT bool operator!=(const QRhiVertexInputAttribute &a, const QRhiVertexInputAttribute &b) Q_DECL_NOTHROW; +Q_GUI_EXPORT uint qHash(const QRhiVertexInputAttribute &v, uint seed = 0) Q_DECL_NOTHROW; +#ifndef QT_NO_DEBUG_STREAM +Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiVertexInputAttribute &); +#endif + +class Q_GUI_EXPORT QRhiVertexInputLayout +{ +public: + QRhiVertexInputLayout() = default; + + QVector bindings() const { return m_bindings; } + void setBindings(const QVector &v) { m_bindings = v; } + + QVector attributes() const { return m_attributes; } + void setAttributes(const QVector &v) { m_attributes = v; } + +private: + QVector m_bindings; + QVector m_attributes; +}; + +Q_DECLARE_TYPEINFO(QRhiVertexInputLayout, Q_MOVABLE_TYPE); + +Q_GUI_EXPORT bool operator==(const QRhiVertexInputLayout &a, const QRhiVertexInputLayout &b) Q_DECL_NOTHROW; +Q_GUI_EXPORT bool operator!=(const QRhiVertexInputLayout &a, const QRhiVertexInputLayout &b) Q_DECL_NOTHROW; +Q_GUI_EXPORT uint qHash(const QRhiVertexInputLayout &v, uint seed = 0) Q_DECL_NOTHROW; +#ifndef QT_NO_DEBUG_STREAM +Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiVertexInputLayout &); +#endif + +class Q_GUI_EXPORT QRhiGraphicsShaderStage +{ +public: + enum Type { + Vertex, + Fragment + }; + + QRhiGraphicsShaderStage() = default; + QRhiGraphicsShaderStage(Type type, const QShader &shader, + QShader::Variant v = QShader::StandardShader); + + Type type() const { return m_type; } + void setType(Type t) { m_type = t; } + + QShader shader() const { return m_shader; } + void setShader(const QShader &s) { m_shader = s; } + + QShader::Variant shaderVariant() const { return m_shaderVariant; } + void setShaderVariant(QShader::Variant v) { m_shaderVariant = v; } + +private: + Type m_type = Vertex; + QShader m_shader; + QShader::Variant m_shaderVariant = QShader::StandardShader; +}; + +Q_DECLARE_TYPEINFO(QRhiGraphicsShaderStage, Q_MOVABLE_TYPE); + +Q_GUI_EXPORT bool operator==(const QRhiGraphicsShaderStage &a, const QRhiGraphicsShaderStage &b) Q_DECL_NOTHROW; +Q_GUI_EXPORT bool operator!=(const QRhiGraphicsShaderStage &a, const QRhiGraphicsShaderStage &b) Q_DECL_NOTHROW; +Q_GUI_EXPORT uint qHash(const QRhiGraphicsShaderStage &s, uint seed = 0) Q_DECL_NOTHROW; +#ifndef QT_NO_DEBUG_STREAM +Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiGraphicsShaderStage &); +#endif + +class Q_GUI_EXPORT QRhiShaderResourceBinding +{ +public: + enum Type { + UniformBuffer, + SampledTexture + }; + + enum StageFlag { + VertexStage = 1 << 0, + FragmentStage = 1 << 1 + }; + Q_DECLARE_FLAGS(StageFlags, StageFlag) + + QRhiShaderResourceBinding(); + QRhiShaderResourceBinding(const QRhiShaderResourceBinding &other); + QRhiShaderResourceBinding &operator=(const QRhiShaderResourceBinding &other); + ~QRhiShaderResourceBinding(); + void detach(); + + bool isLayoutCompatible(const QRhiShaderResourceBinding &other) const; + + static QRhiShaderResourceBinding uniformBuffer(int binding, StageFlags stage, QRhiBuffer *buf); + static QRhiShaderResourceBinding uniformBuffer(int binding, StageFlags stage, QRhiBuffer *buf, int offset, int size); + static QRhiShaderResourceBinding uniformBufferWithDynamicOffset(int binding, StageFlags stage, QRhiBuffer *buf, int size); + static QRhiShaderResourceBinding sampledTexture(int binding, StageFlags stage, QRhiTexture *tex, QRhiSampler *sampler); + +private: + QRhiShaderResourceBindingPrivate *d; + friend class QRhiShaderResourceBindingPrivate; + friend Q_GUI_EXPORT bool operator==(const QRhiShaderResourceBinding &, const QRhiShaderResourceBinding &) Q_DECL_NOTHROW; + friend Q_GUI_EXPORT bool operator!=(const QRhiShaderResourceBinding &, const QRhiShaderResourceBinding &) Q_DECL_NOTHROW; + friend Q_GUI_EXPORT uint qHash(const QRhiShaderResourceBinding &, uint) Q_DECL_NOTHROW; +#ifndef QT_NO_DEBUG_STREAM + friend Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiShaderResourceBinding &); +#endif +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiShaderResourceBinding::StageFlags) + +Q_GUI_EXPORT bool operator==(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b) Q_DECL_NOTHROW; +Q_GUI_EXPORT bool operator!=(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b) Q_DECL_NOTHROW; +Q_GUI_EXPORT uint qHash(const QRhiShaderResourceBinding &b, uint seed = 0) Q_DECL_NOTHROW; +#ifndef QT_NO_DEBUG_STREAM +Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiShaderResourceBinding &); +#endif + +class Q_GUI_EXPORT QRhiColorAttachment +{ +public: + QRhiColorAttachment() = default; + QRhiColorAttachment(QRhiTexture *texture); + QRhiColorAttachment(QRhiRenderBuffer *renderBuffer); + + QRhiTexture *texture() const { return m_texture; } + void setTexture(QRhiTexture *tex) { m_texture = tex; } + + QRhiRenderBuffer *renderBuffer() const { return m_renderBuffer; } + void setRenderBuffer(QRhiRenderBuffer *rb) { m_renderBuffer = rb; } + + int layer() const { return m_layer; } + void setLayer(int layer) { m_layer = layer; } + + int level() const { return m_level; } + void setLevel(int level) { m_level = level; } + + QRhiTexture *resolveTexture() const { return m_resolveTexture; } + void setResolveTexture(QRhiTexture *tex) { m_resolveTexture = tex; } + + int resolveLayer() const { return m_resolveLayer; } + void setResolveLayer(int layer) { m_resolveLayer = layer; } + + int resolveLevel() const { return m_resolveLevel; } + void setResolveLevel(int level) { m_resolveLevel = level; } + +private: + QRhiTexture *m_texture = nullptr; + QRhiRenderBuffer *m_renderBuffer = nullptr; + int m_layer = 0; + int m_level = 0; + QRhiTexture *m_resolveTexture = nullptr; + int m_resolveLayer = 0; + int m_resolveLevel = 0; +}; + +Q_DECLARE_TYPEINFO(QRhiColorAttachment, Q_MOVABLE_TYPE); + +class Q_GUI_EXPORT QRhiTextureRenderTargetDescription +{ +public: + QRhiTextureRenderTargetDescription() = default; + QRhiTextureRenderTargetDescription(const QRhiColorAttachment &colorAttachment); + QRhiTextureRenderTargetDescription(const QRhiColorAttachment &colorAttachment, QRhiRenderBuffer *depthStencilBuffer); + QRhiTextureRenderTargetDescription(const QRhiColorAttachment &colorAttachment, QRhiTexture *depthTexture); + + QVector colorAttachments() const { return m_colorAttachments; } + void setColorAttachments(const QVector &att) { m_colorAttachments = att; } + + QRhiRenderBuffer *depthStencilBuffer() const { return m_depthStencilBuffer; } + void setDepthStencilBuffer(QRhiRenderBuffer *renderBuffer) { m_depthStencilBuffer = renderBuffer; } + + QRhiTexture *depthTexture() const { return m_depthTexture; } + void setDepthTexture(QRhiTexture *texture) { m_depthTexture = texture; } + +private: + QVector m_colorAttachments; + QRhiRenderBuffer *m_depthStencilBuffer = nullptr; + QRhiTexture *m_depthTexture = nullptr; +}; + +Q_DECLARE_TYPEINFO(QRhiTextureRenderTargetDescription, Q_MOVABLE_TYPE); + +class Q_GUI_EXPORT QRhiTextureSubresourceUploadDescription +{ +public: + QRhiTextureSubresourceUploadDescription() = default; + QRhiTextureSubresourceUploadDescription(const QImage &image); + QRhiTextureSubresourceUploadDescription(const void *data, int size); + + QImage image() const { return m_image; } + void setImage(const QImage &image) { m_image = image; } + + QByteArray data() const { return m_data; } + void setData(const QByteArray &data) { m_data = data; } + + QPoint destinationTopLeft() const { return m_destinationTopLeft; } + void setDestinationTopLeft(const QPoint &p) { m_destinationTopLeft = p; } + + QSize sourceSize() const { return m_sourceSize; } + void setSourceSize(const QSize &size) { m_sourceSize = size; } + + QPoint sourceTopLeft() const { return m_sourceTopLeft; } + void setSourceTopLeft(const QPoint &p) { m_sourceTopLeft = p; } + +private: + QImage m_image; + QByteArray m_data; + QPoint m_destinationTopLeft; + QSize m_sourceSize; + QPoint m_sourceTopLeft; +}; + +Q_DECLARE_TYPEINFO(QRhiTextureSubresourceUploadDescription, Q_MOVABLE_TYPE); + +class Q_GUI_EXPORT QRhiTextureUploadEntry +{ +public: + QRhiTextureUploadEntry() = default; + QRhiTextureUploadEntry(int layer, int level, const QRhiTextureSubresourceUploadDescription &desc); + + int layer() const { return m_layer; } + void setLayer(int layer) { m_layer = layer; } + + int level() const { return m_level; } + void setLevel(int level) { m_level = level; } + + QRhiTextureSubresourceUploadDescription description() const { return m_desc; } + void setDescription(const QRhiTextureSubresourceUploadDescription &desc) { m_desc = desc; } + +private: + int m_layer = 0; + int m_level = 0; + QRhiTextureSubresourceUploadDescription m_desc; +}; + +Q_DECLARE_TYPEINFO(QRhiTextureUploadEntry, Q_MOVABLE_TYPE); + +class Q_GUI_EXPORT QRhiTextureUploadDescription +{ +public: + QRhiTextureUploadDescription() = default; + QRhiTextureUploadDescription(const QRhiTextureUploadEntry &entry); + QRhiTextureUploadDescription(const QVector &entries); + + QVector entries() const { return m_entries; } + void setEntries(const QVector &entries) { m_entries = entries; } + void append(const QRhiTextureUploadEntry &entry); + +private: + QVector m_entries; +}; + +Q_DECLARE_TYPEINFO(QRhiTextureUploadDescription, Q_MOVABLE_TYPE); + +class Q_GUI_EXPORT QRhiTextureCopyDescription +{ +public: + QRhiTextureCopyDescription() = default; + + QSize pixelSize() const { return m_pixelSize; } + void setPixelSize(const QSize &sz) { m_pixelSize = sz; } + + int sourceLayer() const { return m_sourceLayer; } + void setSourceLayer(int layer) { m_sourceLayer = layer; } + + int sourceLevel() const { return m_sourceLevel; } + void setSourceLevel(int level) { m_sourceLevel = level; } + + QPoint sourceTopLeft() const { return m_sourceTopLeft; } + void setSourceTopLeft(const QPoint &p) { m_sourceTopLeft = p; } + + int destinationLayer() const { return m_destinationLayer; } + void setDestinationLayer(int layer) { m_destinationLayer = layer; } + + int destinationLevel() const { return m_destinationLevel; } + void setDestinationLevel(int level) { m_destinationLevel = level; } + + QPoint destinationTopLeft() const { return m_destinationTopLeft; } + void setDestinationTopLeft(const QPoint &p) { m_destinationTopLeft = p; } + +private: + QSize m_pixelSize; + int m_sourceLayer = 0; + int m_sourceLevel = 0; + QPoint m_sourceTopLeft; + int m_destinationLayer = 0; + int m_destinationLevel = 0; + QPoint m_destinationTopLeft; +}; + +Q_DECLARE_TYPEINFO(QRhiTextureCopyDescription, Q_MOVABLE_TYPE); + +class Q_GUI_EXPORT QRhiReadbackDescription +{ +public: + QRhiReadbackDescription() = default; + QRhiReadbackDescription(QRhiTexture *texture); + + QRhiTexture *texture() const { return m_texture; } + void setTexture(QRhiTexture *tex) { m_texture = tex; } + + int layer() const { return m_layer; } + void setLayer(int layer) { m_layer = layer; } + + int level() const { return m_level; } + void setLevel(int level) { m_level = level; } + +private: + QRhiTexture *m_texture = nullptr; + int m_layer = 0; + int m_level = 0; +}; + +Q_DECLARE_TYPEINFO(QRhiReadbackDescription, Q_MOVABLE_TYPE); + +struct Q_GUI_EXPORT QRhiNativeHandles +{ +}; + +class Q_GUI_EXPORT QRhiResource +{ +public: + enum Type { + Buffer, + Texture, + Sampler, + RenderBuffer, + RenderPassDescriptor, + RenderTarget, + TextureRenderTarget, + ShaderResourceBindings, + GraphicsPipeline, + SwapChain, + CommandBuffer + }; + + virtual ~QRhiResource(); + + virtual Type resourceType() const = 0; + + virtual void release() = 0; + void releaseAndDestroyLater(); + + QByteArray name() const; + void setName(const QByteArray &name); + + quint64 globalResourceId() const; + +protected: + QRhiResource(QRhiImplementation *rhi); + Q_DISABLE_COPY(QRhiResource) + friend class QRhiImplementation; + QRhiImplementation *m_rhi = nullptr; + quint64 m_id; + QByteArray m_objectName; +}; + +class Q_GUI_EXPORT QRhiBuffer : public QRhiResource +{ +public: + enum Type { + Immutable, + Static, + Dynamic + }; + + enum UsageFlag { + VertexBuffer = 1 << 0, + IndexBuffer = 1 << 1, + UniformBuffer = 1 << 2 + }; + Q_DECLARE_FLAGS(UsageFlags, UsageFlag) + + QRhiResource::Type resourceType() const override; + + Type type() const { return m_type; } + void setType(Type t) { m_type = t; } + + UsageFlags usage() const { return m_usage; } + void setUsage(UsageFlags u) { m_usage = u; } + + int size() const { return m_size; } + void setSize(int sz) { m_size = sz; } + + virtual bool build() = 0; + +protected: + QRhiBuffer(QRhiImplementation *rhi, Type type_, UsageFlags usage_, int size_); + Type m_type; + UsageFlags m_usage; + int m_size; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiBuffer::UsageFlags) + +class Q_GUI_EXPORT QRhiTexture : public QRhiResource +{ +public: + enum Flag { + RenderTarget = 1 << 0, + CubeMap = 1 << 2, + MipMapped = 1 << 3, + sRGB = 1 << 4, + UsedAsTransferSource = 1 << 5, + UsedWithGenerateMips = 1 << 6 + }; + Q_DECLARE_FLAGS(Flags, Flag) + + enum Format { + UnknownFormat, + + RGBA8, + BGRA8, + R8, + R16, + RED_OR_ALPHA8, + + RGBA16F, + RGBA32F, + + D16, + D32F, + + BC1, + BC2, + BC3, + BC4, + BC5, + BC6H, + BC7, + + ETC2_RGB8, + ETC2_RGB8A1, + ETC2_RGBA8, + + ASTC_4x4, + ASTC_5x4, + ASTC_5x5, + ASTC_6x5, + ASTC_6x6, + ASTC_8x5, + ASTC_8x6, + ASTC_8x8, + ASTC_10x5, + ASTC_10x6, + ASTC_10x8, + ASTC_10x10, + ASTC_12x10, + ASTC_12x12 + }; + + QRhiResource::Type resourceType() const override; + + Format format() const { return m_format; } + void setFormat(Format fmt) { m_format = fmt; } + + QSize pixelSize() const { return m_pixelSize; } + void setPixelSize(const QSize &sz) { m_pixelSize = sz; } + + Flags flags() const { return m_flags; } + void setFlags(Flags f) { m_flags = f; } + + int sampleCount() const { return m_sampleCount; } + void setSampleCount(int s) { m_sampleCount = s; } + + virtual bool build() = 0; + virtual const QRhiNativeHandles *nativeHandles(); + virtual bool buildFrom(const QRhiNativeHandles *src); + +protected: + QRhiTexture(QRhiImplementation *rhi, Format format_, const QSize &pixelSize_, + int sampleCount_, Flags flags_); + Format m_format; + QSize m_pixelSize; + int m_sampleCount; + Flags m_flags; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiTexture::Flags) + +class Q_GUI_EXPORT QRhiSampler : public QRhiResource +{ +public: + enum Filter { + None, + Nearest, + Linear + }; + + enum AddressMode { + Repeat, + ClampToEdge, + Border, + Mirror, + MirrorOnce + }; + + enum CompareOp { + Never, + Less, + Equal, + LessOrEqual, + Greater, + NotEqual, + GreaterOrEqual, + Always + }; + + QRhiResource::Type resourceType() const override; + + Filter magFilter() const { return m_magFilter; } + void setMagFilter(Filter f) { m_magFilter = f; } + + Filter minFilter() const { return m_minFilter; } + void setMinFilter(Filter f) { m_minFilter = f; } + + Filter mipmapMode() const { return m_mipmapMode; } + void setMipmapMode(Filter f) { m_mipmapMode = f; } + + AddressMode addressU() const { return m_addressU; } + void setAddressU(AddressMode mode) { m_addressU = mode; } + + AddressMode addressV() const { return m_addressV; } + void setAddressV(AddressMode mode) { m_addressV = mode; } + + AddressMode addressW() const { return m_addressW; } + void setAddressW(AddressMode mode) { m_addressW = mode; } + + CompareOp textureCompareOp() const { return m_compareOp; } + void setTextureCompareOp(CompareOp op) { m_compareOp = op; } + + virtual bool build() = 0; + +protected: + QRhiSampler(QRhiImplementation *rhi, + Filter magFilter_, Filter minFilter_, Filter mipmapMode_, + AddressMode u_, AddressMode v_); + Filter m_magFilter; + Filter m_minFilter; + Filter m_mipmapMode; + AddressMode m_addressU; + AddressMode m_addressV; + AddressMode m_addressW; + CompareOp m_compareOp; +}; + +class Q_GUI_EXPORT QRhiRenderBuffer : public QRhiResource +{ +public: + enum Type { + DepthStencil, + Color + }; + + enum Flag { + UsedWithSwapChainOnly = 1 << 0 + }; + Q_DECLARE_FLAGS(Flags, Flag) + + QRhiResource::Type resourceType() const override; + + Type type() const { return m_type; } + void setType(Type t) { m_type = t; } + + QSize pixelSize() const { return m_pixelSize; } + void setPixelSize(const QSize &sz) { m_pixelSize = sz; } + + int sampleCount() const { return m_sampleCount; } + void setSampleCount(int s) { m_sampleCount = s; } + + Flags flags() const { return m_flags; } + void setFlags(Flags h) { m_flags = h; } + + virtual bool build() = 0; + + virtual QRhiTexture::Format backingFormat() const = 0; + +protected: + QRhiRenderBuffer(QRhiImplementation *rhi, Type type_, const QSize &pixelSize_, + int sampleCount_, Flags flags_); + Type m_type; + QSize m_pixelSize; + int m_sampleCount; + Flags m_flags; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiRenderBuffer::Flags) + +class Q_GUI_EXPORT QRhiRenderPassDescriptor : public QRhiResource +{ +public: + QRhiResource::Type resourceType() const override; + +protected: + QRhiRenderPassDescriptor(QRhiImplementation *rhi); +}; + +class Q_GUI_EXPORT QRhiRenderTarget : public QRhiResource +{ +public: + QRhiResource::Type resourceType() const override; + + virtual QSize pixelSize() const = 0; + virtual float devicePixelRatio() const = 0; + virtual int sampleCount() const = 0; + + QRhiRenderPassDescriptor *renderPassDescriptor() const { return m_renderPassDesc; } + void setRenderPassDescriptor(QRhiRenderPassDescriptor *desc) { m_renderPassDesc = desc; } + +protected: + QRhiRenderTarget(QRhiImplementation *rhi); + QRhiRenderPassDescriptor *m_renderPassDesc = nullptr; +}; + +class Q_GUI_EXPORT QRhiTextureRenderTarget : public QRhiRenderTarget +{ +public: + enum Flag { + PreserveColorContents = 1 << 0, + PreserveDepthStencilContents = 1 << 1 + }; + Q_DECLARE_FLAGS(Flags, Flag) + + QRhiResource::Type resourceType() const override; + + QRhiTextureRenderTargetDescription description() const { return m_desc; } + void setDescription(const QRhiTextureRenderTargetDescription &desc) { m_desc = desc; } + + Flags flags() const { return m_flags; } + void setFlags(Flags f) { m_flags = f; } + + virtual QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() = 0; + + virtual bool build() = 0; + +protected: + QRhiTextureRenderTarget(QRhiImplementation *rhi, const QRhiTextureRenderTargetDescription &desc_, Flags flags_); + QRhiTextureRenderTargetDescription m_desc; + Flags m_flags; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiTextureRenderTarget::Flags) + +class Q_GUI_EXPORT QRhiShaderResourceBindings : public QRhiResource +{ +public: + QRhiResource::Type resourceType() const override; + + QVector bindings() const { return m_bindings; } + void setBindings(const QVector &b) { m_bindings = b; } + + bool isLayoutCompatible(const QRhiShaderResourceBindings *other) const; + + virtual bool build() = 0; + +protected: + QRhiShaderResourceBindings(QRhiImplementation *rhi); + QVector m_bindings; +#ifndef QT_NO_DEBUG_STREAM + friend Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiShaderResourceBindings &); +#endif +}; + +#ifndef QT_NO_DEBUG_STREAM +Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiShaderResourceBindings &); +#endif + +class Q_GUI_EXPORT QRhiGraphicsPipeline : public QRhiResource +{ +public: + enum Flag { + UsesBlendConstants = 1 << 0, + UsesStencilRef = 1 << 1, + UsesScissor = 1 << 2 + }; + Q_DECLARE_FLAGS(Flags, Flag) + + enum Topology { + Triangles, + TriangleStrip, + Lines, + LineStrip, + Points + }; + + enum CullMode { + None, + Front, + Back + }; + + enum FrontFace { + CCW, + CW + }; + + enum ColorMaskComponent { + R = 1 << 0, + G = 1 << 1, + B = 1 << 2, + A = 1 << 3 + }; + Q_DECLARE_FLAGS(ColorMask, ColorMaskComponent) + + enum BlendFactor { + Zero, + One, + SrcColor, + OneMinusSrcColor, + DstColor, + OneMinusDstColor, + SrcAlpha, + OneMinusSrcAlpha, + DstAlpha, + OneMinusDstAlpha, + ConstantColor, + OneMinusConstantColor, + ConstantAlpha, + OneMinusConstantAlpha, + SrcAlphaSaturate, + Src1Color, + OneMinusSrc1Color, + Src1Alpha, + OneMinusSrc1Alpha + }; + + enum BlendOp { + Add, + Subtract, + ReverseSubtract, + Min, + Max + }; + + struct TargetBlend { + ColorMask colorWrite = ColorMask(0xF); // R | G | B | A + bool enable = false; + BlendFactor srcColor = One; + BlendFactor dstColor = OneMinusSrcAlpha; + BlendOp opColor = Add; + BlendFactor srcAlpha = One; + BlendFactor dstAlpha = OneMinusSrcAlpha; + BlendOp opAlpha = Add; + }; + + enum CompareOp { + Never, + Less, + Equal, + LessOrEqual, + Greater, + NotEqual, + GreaterOrEqual, + Always + }; + + enum StencilOp { + StencilZero, + Keep, + Replace, + IncrementAndClamp, + DecrementAndClamp, + Invert, + IncrementAndWrap, + DecrementAndWrap + }; + + struct StencilOpState { + StencilOp failOp = Keep; + StencilOp depthFailOp = Keep; + StencilOp passOp = Keep; + CompareOp compareOp = Always; + }; + + QRhiResource::Type resourceType() const override; + + Flags flags() const { return m_flags; } + void setFlags(Flags f) { m_flags = f; } + + Topology topology() const { return m_topology; } + void setTopology(Topology t) { m_topology = t; } + + CullMode cullMode() const { return m_cullMode; } + void setCullMode(CullMode mode) { m_cullMode = mode; } + + FrontFace frontFace() const { return m_frontFace; } + void setFrontFace(FrontFace f) { m_frontFace = f; } + + QVector targetBlends() const { return m_targetBlends; } + void setTargetBlends(const QVector &blends) { m_targetBlends = blends; } + + bool hasDepthTest() const { return m_depthTest; } + void setDepthTest(bool enable) { m_depthTest = enable; } + + bool hasDepthWrite() const { return m_depthWrite; } + void setDepthWrite(bool enable) { m_depthWrite = enable; } + + CompareOp depthOp() const { return m_depthOp; } + void setDepthOp(CompareOp op) { m_depthOp = op; } + + bool hasStencilTest() const { return m_stencilTest; } + void setStencilTest(bool enable) { m_stencilTest = enable; } + + StencilOpState stencilFront() const { return m_stencilFront; } + void setStencilFront(const StencilOpState &state) { m_stencilFront = state; } + + StencilOpState stencilBack() const { return m_stencilBack; } + void setStencilBack(const StencilOpState &state) { m_stencilBack = state; } + + quint32 stencilReadMask() const { return m_stencilReadMask; } + void setStencilReadMask(quint32 mask) { m_stencilReadMask = mask; } + + quint32 stencilWriteMask() const { return m_stencilWriteMask; } + void setStencilWriteMask(quint32 mask) { m_stencilWriteMask = mask; } + + int sampleCount() const { return m_sampleCount; } + void setSampleCount(int s) { m_sampleCount = s; } + + QVector shaderStages() const { return m_shaderStages; } + void setShaderStages(const QVector &stages) { m_shaderStages = stages; } + + QRhiVertexInputLayout vertexInputLayout() const { return m_vertexInputLayout; } + void setVertexInputLayout(const QRhiVertexInputLayout &layout) { m_vertexInputLayout = layout; } + + QRhiShaderResourceBindings *shaderResourceBindings() const { return m_shaderResourceBindings; } + void setShaderResourceBindings(QRhiShaderResourceBindings *srb) { m_shaderResourceBindings = srb; } + + QRhiRenderPassDescriptor *renderPassDescriptor() const { return m_renderPassDesc; } + void setRenderPassDescriptor(QRhiRenderPassDescriptor *desc) { m_renderPassDesc = desc; } + + virtual bool build() = 0; + +protected: + QRhiGraphicsPipeline(QRhiImplementation *rhi); + Flags m_flags; + Topology m_topology = Triangles; + CullMode m_cullMode = None; + FrontFace m_frontFace = CCW; + QVector m_targetBlends; + bool m_depthTest = false; + bool m_depthWrite = false; + CompareOp m_depthOp = Less; + bool m_stencilTest = false; + StencilOpState m_stencilFront; + StencilOpState m_stencilBack; + quint32 m_stencilReadMask = 0xFF; + quint32 m_stencilWriteMask = 0xFF; + int m_sampleCount = 1; + QVector m_shaderStages; + QRhiVertexInputLayout m_vertexInputLayout; + QRhiShaderResourceBindings *m_shaderResourceBindings = nullptr; + QRhiRenderPassDescriptor *m_renderPassDesc = nullptr; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiGraphicsPipeline::Flags) +Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiGraphicsPipeline::ColorMask) +Q_DECLARE_TYPEINFO(QRhiGraphicsPipeline::TargetBlend, Q_MOVABLE_TYPE); + +class Q_GUI_EXPORT QRhiSwapChain : public QRhiResource +{ +public: + enum Flag { + SurfaceHasPreMulAlpha = 1 << 0, + SurfaceHasNonPreMulAlpha = 1 << 1, + sRGB = 1 << 2, + UsedAsTransferSource = 1 << 3, + NoVSync = 1 << 4, + MinimalBufferCount = 1 << 5 + }; + Q_DECLARE_FLAGS(Flags, Flag) + + QRhiResource::Type resourceType() const override; + + QWindow *window() const { return m_window; } + void setWindow(QWindow *window) { m_window = window; } + + Flags flags() const { return m_flags; } + void setFlags(Flags f) { m_flags = f; } + + QRhiRenderBuffer *depthStencil() const { return m_depthStencil; } + void setDepthStencil(QRhiRenderBuffer *ds) { m_depthStencil = ds; } + + int sampleCount() const { return m_sampleCount; } + void setSampleCount(int samples) { m_sampleCount = samples; } + + QRhiRenderPassDescriptor *renderPassDescriptor() const { return m_renderPassDesc; } + void setRenderPassDescriptor(QRhiRenderPassDescriptor *desc) { m_renderPassDesc = desc; } + + QSize currentPixelSize() const { return m_currentPixelSize; } + + virtual QRhiCommandBuffer *currentFrameCommandBuffer() = 0; + virtual QRhiRenderTarget *currentFrameRenderTarget() = 0; + virtual QSize surfacePixelSize() = 0; + virtual QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() = 0; + virtual bool buildOrResize() = 0; + +protected: + QRhiSwapChain(QRhiImplementation *rhi); + QWindow *m_window = nullptr; + Flags m_flags; + QRhiRenderBuffer *m_depthStencil = nullptr; + int m_sampleCount = 1; + QRhiRenderPassDescriptor *m_renderPassDesc = nullptr; + QSize m_currentPixelSize; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiSwapChain::Flags) + +class Q_GUI_EXPORT QRhiCommandBuffer : public QRhiResource +{ +public: + enum IndexFormat { + IndexUInt16, + IndexUInt32 + }; + + QRhiResource::Type resourceType() const override; + + void resourceUpdate(QRhiResourceUpdateBatch *resourceUpdates); + + void beginPass(QRhiRenderTarget *rt, + const QColor &colorClearValue, + const QRhiDepthStencilClearValue &depthStencilClearValue, + QRhiResourceUpdateBatch *resourceUpdates = nullptr); + void endPass(QRhiResourceUpdateBatch *resourceUpdates = nullptr); + + void setGraphicsPipeline(QRhiGraphicsPipeline *ps); + using DynamicOffset = QPair; // binding, offset + void setShaderResources(QRhiShaderResourceBindings *srb = nullptr, + int dynamicOffsetCount = 0, + const DynamicOffset *dynamicOffsets = nullptr); + using VertexInput = QPair; // buffer, offset + void setVertexInput(int startBinding, int bindingCount, const VertexInput *bindings, + QRhiBuffer *indexBuf = nullptr, quint32 indexOffset = 0, + IndexFormat indexFormat = IndexUInt16); + + void setViewport(const QRhiViewport &viewport); + void setScissor(const QRhiScissor &scissor); + void setBlendConstants(const QColor &c); + void setStencilRef(quint32 refValue); + + void draw(quint32 vertexCount, + quint32 instanceCount = 1, + quint32 firstVertex = 0, + quint32 firstInstance = 0); + + void drawIndexed(quint32 indexCount, + quint32 instanceCount = 1, + quint32 firstIndex = 0, + qint32 vertexOffset = 0, + quint32 firstInstance = 0); + + void debugMarkBegin(const QByteArray &name); + void debugMarkEnd(); + void debugMarkMsg(const QByteArray &msg); + + const QRhiNativeHandles *nativeHandles(); + void beginExternal(); + void endExternal(); + +protected: + QRhiCommandBuffer(QRhiImplementation *rhi); +}; + +struct Q_GUI_EXPORT QRhiReadbackResult +{ + std::function completed = nullptr; + QRhiTexture::Format format; + QSize pixelSize; + QByteArray data; +}; // non-movable due to the std::function + +class Q_GUI_EXPORT QRhiResourceUpdateBatch +{ +public: + ~QRhiResourceUpdateBatch(); + + void release(); + + void merge(QRhiResourceUpdateBatch *other); + + void updateDynamicBuffer(QRhiBuffer *buf, int offset, int size, const void *data); + void uploadStaticBuffer(QRhiBuffer *buf, int offset, int size, const void *data); + void uploadStaticBuffer(QRhiBuffer *buf, const void *data); + void uploadTexture(QRhiTexture *tex, const QRhiTextureUploadDescription &desc); + void uploadTexture(QRhiTexture *tex, const QImage &image); + void copyTexture(QRhiTexture *dst, QRhiTexture *src, const QRhiTextureCopyDescription &desc = QRhiTextureCopyDescription()); + void readBackTexture(const QRhiReadbackDescription &rb, QRhiReadbackResult *result); + void generateMips(QRhiTexture *tex, int layer = 0); + +private: + QRhiResourceUpdateBatch(QRhiImplementation *rhi); + Q_DISABLE_COPY(QRhiResourceUpdateBatch) + QRhiResourceUpdateBatchPrivate *d; + friend class QRhiResourceUpdateBatchPrivate; + friend class QRhi; +}; + +struct Q_GUI_EXPORT QRhiInitParams +{ +}; + +class Q_GUI_EXPORT QRhi +{ +public: + enum Implementation { + Null, + Vulkan, + OpenGLES2, + D3D11, + Metal + }; + + enum Flag { + EnableProfiling = 1 << 0, + EnableDebugMarkers = 1 << 1 + }; + Q_DECLARE_FLAGS(Flags, Flag) + + enum FrameOpResult { + FrameOpSuccess = 0, + FrameOpError, + FrameOpSwapChainOutOfDate, + FrameOpDeviceLost + }; + + enum Feature { + MultisampleTexture = 1, + MultisampleRenderBuffer, + DebugMarkers, + Timestamps, + Instancing, + CustomInstanceStepRate, + PrimitiveRestart, + NonDynamicUniformBuffers, + NonFourAlignedEffectiveIndexBufferOffset, + NPOTTextureRepeat, + RedOrAlpha8IsRed, + ElementIndexUint + }; + + enum BeginFrameFlag { + }; + Q_DECLARE_FLAGS(BeginFrameFlags, BeginFrameFlag) + + enum EndFrameFlag { + SkipPresent = 1 << 0 + }; + Q_DECLARE_FLAGS(EndFrameFlags, EndFrameFlag) + + enum ResourceLimit { + TextureSizeMin = 1, + TextureSizeMax, + MaxColorAttachments, + FramesInFlight + }; + + ~QRhi(); + + static QRhi *create(Implementation impl, + QRhiInitParams *params, + Flags flags = Flags(), + QRhiNativeHandles *importDevice = nullptr); + + Implementation backend() const; + QThread *thread() const; + + using CleanupCallback = std::function; + void addCleanupCallback(const CleanupCallback &callback); + void runCleanup(); + + QRhiGraphicsPipeline *newGraphicsPipeline(); + QRhiShaderResourceBindings *newShaderResourceBindings(); + + QRhiBuffer *newBuffer(QRhiBuffer::Type type, + QRhiBuffer::UsageFlags usage, + int size); + + QRhiRenderBuffer *newRenderBuffer(QRhiRenderBuffer::Type type, + const QSize &pixelSize, + int sampleCount = 1, + QRhiRenderBuffer::Flags flags = QRhiRenderBuffer::Flags()); + + QRhiTexture *newTexture(QRhiTexture::Format format, + const QSize &pixelSize, + int sampleCount = 1, + QRhiTexture::Flags flags = QRhiTexture::Flags()); + + QRhiSampler *newSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, + QRhiSampler::Filter mipmapMode, + QRhiSampler::AddressMode u, QRhiSampler::AddressMode v); + + QRhiTextureRenderTarget *newTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc, + QRhiTextureRenderTarget::Flags flags = QRhiTextureRenderTarget::Flags()); + + QRhiSwapChain *newSwapChain(); + FrameOpResult beginFrame(QRhiSwapChain *swapChain, BeginFrameFlags flags = BeginFrameFlags()); + FrameOpResult endFrame(QRhiSwapChain *swapChain, EndFrameFlags flags = EndFrameFlags()); + bool isRecordingFrame() const; + int currentFrameSlot() const; + + FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb); + FrameOpResult endOffscreenFrame(); + + QRhi::FrameOpResult finish(); + + QRhiResourceUpdateBatch *nextResourceUpdateBatch(); + + QVector supportedSampleCounts() const; + + int ubufAlignment() const; + int ubufAligned(int v) const; + + int mipLevelsForSize(const QSize &size) const; + QSize sizeForMipLevel(int mipLevel, const QSize &baseLevelSize) const; + + bool isYUpInFramebuffer() const; + bool isYUpInNDC() const; + bool isClipDepthZeroToOne() const; + + QMatrix4x4 clipSpaceCorrMatrix() const; + + bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags = QRhiTexture::Flags()) const; + bool isFeatureSupported(QRhi::Feature feature) const; + int resourceLimit(ResourceLimit limit) const; + + const QRhiNativeHandles *nativeHandles(); + + QRhiProfiler *profiler(); + + static const int MAX_LAYERS = 6; // cubemaps only + static const int MAX_LEVELS = 16; // a width and/or height of 65536 should be enough for everyone + +protected: + QRhi(); + +private: + Q_DISABLE_COPY(QRhi) + QRhiImplementation *d = nullptr; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QRhi::Flags) +Q_DECLARE_OPERATORS_FOR_FLAGS(QRhi::BeginFrameFlags) +Q_DECLARE_OPERATORS_FOR_FLAGS(QRhi::EndFrameFlags) + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/rhi/qrhi_p_p.h b/src/gui/rhi/qrhi_p_p.h new file mode 100644 index 0000000000..7c110431fb --- /dev/null +++ b/src/gui/rhi/qrhi_p_p.h @@ -0,0 +1,544 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Gui module +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QRHI_P_H +#define QRHI_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qrhi_p.h" +#include "qrhiprofiler_p_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +#define QRHI_RES(t, x) static_cast(x) +#define QRHI_RES_RHI(t) t *rhiD = static_cast(m_rhi) +#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 + +class QRhiImplementation +{ +public: + virtual ~QRhiImplementation(); + + virtual bool create(QRhi::Flags flags) = 0; + virtual void destroy() = 0; + + virtual QRhiGraphicsPipeline *createGraphicsPipeline() = 0; + virtual QRhiShaderResourceBindings *createShaderResourceBindings() = 0; + virtual QRhiBuffer *createBuffer(QRhiBuffer::Type type, + QRhiBuffer::UsageFlags usage, + int size) = 0; + virtual QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type, + const QSize &pixelSize, + int sampleCount, + QRhiRenderBuffer::Flags flags) = 0; + virtual QRhiTexture *createTexture(QRhiTexture::Format format, + const QSize &pixelSize, + int sampleCount, + QRhiTexture::Flags flags) = 0; + virtual QRhiSampler *createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, + QRhiSampler::Filter mipmapMode, + QRhiSampler:: AddressMode u, QRhiSampler::AddressMode v) = 0; + + virtual QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc, + QRhiTextureRenderTarget::Flags flags) = 0; + + virtual QRhiSwapChain *createSwapChain() = 0; + virtual QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) = 0; + virtual QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) = 0; + virtual QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) = 0; + virtual QRhi::FrameOpResult endOffscreenFrame() = 0; + virtual QRhi::FrameOpResult finish() = 0; + + virtual void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) = 0; + + virtual void beginPass(QRhiCommandBuffer *cb, + QRhiRenderTarget *rt, + const QColor &colorClearValue, + const QRhiDepthStencilClearValue &depthStencilClearValue, + QRhiResourceUpdateBatch *resourceUpdates) = 0; + virtual void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) = 0; + + virtual void setGraphicsPipeline(QRhiCommandBuffer *cb, + QRhiGraphicsPipeline *ps) = 0; + + virtual void setShaderResources(QRhiCommandBuffer *cb, + QRhiShaderResourceBindings *srb, + int dynamicOffsetCount, + const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) = 0; + + virtual void setVertexInput(QRhiCommandBuffer *cb, + int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings, + QRhiBuffer *indexBuf, quint32 indexOffset, + QRhiCommandBuffer::IndexFormat indexFormat) = 0; + + virtual void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) = 0; + virtual void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) = 0; + virtual void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) = 0; + virtual void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) = 0; + + virtual void draw(QRhiCommandBuffer *cb, quint32 vertexCount, + quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) = 0; + virtual void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, + quint32 instanceCount, quint32 firstIndex, + qint32 vertexOffset, quint32 firstInstance) = 0; + + virtual void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) = 0; + virtual void debugMarkEnd(QRhiCommandBuffer *cb) = 0; + virtual void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) = 0; + + virtual const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) = 0; + virtual void beginExternal(QRhiCommandBuffer *cb) = 0; + virtual void endExternal(QRhiCommandBuffer *cb) = 0; + + virtual QVector supportedSampleCounts() const = 0; + virtual int ubufAlignment() const = 0; + virtual bool isYUpInFramebuffer() const = 0; + virtual bool isYUpInNDC() const = 0; + virtual bool isClipDepthZeroToOne() const = 0; + virtual QMatrix4x4 clipSpaceCorrMatrix() const = 0; + virtual bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const = 0; + virtual bool isFeatureSupported(QRhi::Feature feature) const = 0; + virtual int resourceLimit(QRhi::ResourceLimit limit) const = 0; + virtual const QRhiNativeHandles *nativeHandles() = 0; + virtual void sendVMemStatsToProfiler() = 0; + + bool isCompressedFormat(QRhiTexture::Format format) const; + void compressedFormatInfo(QRhiTexture::Format format, const QSize &size, + quint32 *bpl, quint32 *byteSize, + QSize *blockDim) const; + void textureFormatInfo(QRhiTexture::Format format, const QSize &size, + quint32 *bpl, quint32 *byteSize) const; + quint32 approxByteSizeForTexture(QRhiTexture::Format format, const QSize &baseSize, + int mipCount, int layerCount); + + QRhiProfilerPrivate *profilerPrivateOrNull() + { + // return null when QRhi::EnableProfiling was not set + QRhiProfilerPrivate *p = QRhiProfilerPrivate::get(&profiler); + return p->rhiDWhenEnabled ? p : nullptr; + } + + // only really care about resources that own native graphics resources underneath + void registerResource(QRhiResource *res) + { + resources.insert(res); + } + + void unregisterResource(QRhiResource *res) + { + resources.remove(res); + } + + QSet activeResources() const + { + return resources; + } + + void addReleaseAndDestroyLater(QRhiResource *res) + { + if (inFrame) + pendingReleaseAndDestroyResources.insert(res); + else + delete res; + } + + void addCleanupCallback(const QRhi::CleanupCallback &callback) + { + cleanupCallbacks.append(callback); + } + + QRhi *q; + +protected: + bool debugMarkers = false; + int currentFrameSlot = 0; // for vk, mtl, and similar. unused by gl and d3d11. + +private: + QRhi::Implementation implType; + QThread *implThread; + QRhiProfiler profiler; + QVector resUpdPool; + QBitArray resUpdPoolMap; + QSet resources; + QSet pendingReleaseAndDestroyResources; + QVector cleanupCallbacks; + bool inFrame = false; + + friend class QRhi; + friend class QRhiResourceUpdateBatchPrivate; +}; + +template +bool qrhi_toTopLeftRenderTargetRect(const QSize &outputSize, const std::array &r, + T *x, T *y, T *w, T *h) +{ + // x,y are bottom-left in QRhiScissor and QRhiViewport but top-left in + // Vulkan/Metal/D3D. We also need proper clamping since some + // validation/debug layers are allergic to out of bounds scissor or + // viewport rects. + + const T outputWidth = outputSize.width(); + const T outputHeight = outputSize.height(); + const T inputWidth = r[2]; + const T inputHeight = r[3]; + + *x = qMax(0, r[0]); + *y = qMax(0, outputHeight - (r[1] + inputHeight)); + *w = inputWidth; + *h = inputHeight; + + if (*x >= outputWidth || *y >= outputHeight) + return false; + + if (*x + *w > outputWidth) + *w = outputWidth - *x; + if (*y + *h > outputHeight) + *h = outputHeight - *y; + + return true; +} + +class QRhiResourceUpdateBatchPrivate +{ +public: + struct DynamicBufferUpdate { + DynamicBufferUpdate() { } + DynamicBufferUpdate(QRhiBuffer *buf_, int offset_, int size_, const void *data_) + : buf(buf_), offset(offset_), data(reinterpret_cast(data_), size_) + { } + + QRhiBuffer *buf = nullptr; + int offset = 0; + QByteArray data; + }; + + struct StaticBufferUpload { + StaticBufferUpload() { } + StaticBufferUpload(QRhiBuffer *buf_, int offset_, int size_, const void *data_) + : buf(buf_), offset(offset_), data(reinterpret_cast(data_), size_ ? size_ : buf_->size()) + { } + + QRhiBuffer *buf = nullptr; + int offset = 0; + QByteArray data; + }; + + struct TextureOp { + enum Type { + Upload, + Copy, + Read, + MipGen + }; + Type type; + struct SUpload { + QRhiTexture *tex = nullptr; + // Specifying multiple uploads for a subresource must be supported. + // In the backend this can then end up, where applicable, as a + // single, batched copy operation with only one set of barriers. + // This helps when doing for example glyph cache fills. + QVector subresDesc[QRhi::MAX_LAYERS][QRhi::MAX_LEVELS]; + } upload; + struct SCopy { + QRhiTexture *dst = nullptr; + QRhiTexture *src = nullptr; + QRhiTextureCopyDescription desc; + } copy; + struct SRead { + QRhiReadbackDescription rb; + QRhiReadbackResult *result; + } read; + struct SMipGen { + QRhiTexture *tex = nullptr; + int layer = 0; + } mipgen; + + static TextureOp textureUpload(QRhiTexture *tex, const QRhiTextureUploadDescription &desc) + { + TextureOp op; + op.type = Upload; + op.upload.tex = tex; + const QVector &entries(desc.entries()); + for (const QRhiTextureUploadEntry &entry : entries) + op.upload.subresDesc[entry.layer()][entry.level()].append(entry.description()); + return op; + } + + static TextureOp textureCopy(QRhiTexture *dst, QRhiTexture *src, const QRhiTextureCopyDescription &desc) + { + TextureOp op; + op.type = Copy; + op.copy.dst = dst; + op.copy.src = src; + op.copy.desc = desc; + return op; + } + + static TextureOp textureRead(const QRhiReadbackDescription &rb, QRhiReadbackResult *result) + { + TextureOp op; + op.type = Read; + op.read.rb = rb; + op.read.result = result; + return op; + } + + static TextureOp textureMipGen(QRhiTexture *tex, int layer) + { + TextureOp op; + op.type = MipGen; + op.mipgen.tex = tex; + op.mipgen.layer = layer; + return op; + } + }; + + QVector dynamicBufferUpdates; + QVector staticBufferUploads; + QVector textureOps; + + QRhiResourceUpdateBatch *q = nullptr; + QRhiImplementation *rhi = nullptr; + int poolIndex = -1; + + void free(); + void merge(QRhiResourceUpdateBatchPrivate *other); + + static QRhiResourceUpdateBatchPrivate *get(QRhiResourceUpdateBatch *b) { return b->d; } +}; + +Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::StaticBufferUpload, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::TextureOp, Q_MOVABLE_TYPE); + +class Q_GUI_EXPORT QRhiShaderResourceBindingPrivate +{ +public: + QRhiShaderResourceBindingPrivate() + : ref(1) + { + } + + QRhiShaderResourceBindingPrivate(const QRhiShaderResourceBindingPrivate *other) + : ref(1), + binding(other->binding), + stage(other->stage), + type(other->type), + u(other->u) + { + } + + static QRhiShaderResourceBindingPrivate *get(QRhiShaderResourceBinding *s) { return s->d; } + static const QRhiShaderResourceBindingPrivate *get(const QRhiShaderResourceBinding *s) { return s->d; } + + QAtomicInt ref; + int binding; + QRhiShaderResourceBinding::StageFlags stage; + QRhiShaderResourceBinding::Type type; + struct UniformBufferData { + QRhiBuffer *buf; + int offset; + int maybeSize; + bool hasDynamicOffset; + }; + struct SampledTextureData { + QRhiTexture *tex; + QRhiSampler *sampler; + }; + union { + UniformBufferData ubuf; + SampledTextureData stex; + } u; +}; + +template +struct QRhiBatchedBindings +{ + void feed(int binding, T resource) { // binding must be strictly increasing + if (curBinding == -1 || binding > curBinding + 1) { + finish(); + curBatch.startBinding = binding; + curBatch.resources.clear(); + curBatch.resources.append(resource); + } else { + Q_ASSERT(binding == curBinding + 1); + curBatch.resources.append(resource); + } + curBinding = binding; + } + + void finish() { + if (!curBatch.resources.isEmpty()) + batches.append(curBatch); + } + + void clear() { + batches.clear(); + curBatch.resources.clear(); + curBinding = -1; + } + + struct Batch { + uint startBinding; + QVarLengthArray resources; + + bool operator==(const Batch &other) const + { + return startBinding == other.startBinding && resources == other.resources; + } + + bool operator!=(const Batch &other) const + { + return !operator==(other); + } + }; + + QVarLengthArray batches; // sorted by startBinding + + bool operator==(const QRhiBatchedBindings &other) const + { + return batches == other.batches; + } + + bool operator!=(const QRhiBatchedBindings &other) const + { + return !operator==(other); + } + +private: + Batch curBatch; + int curBinding = -1; +}; + +class QRhiGlobalObjectIdGenerator +{ +public: +#ifdef Q_ATOMIC_INT64_IS_SUPPORTED + using Type = quint64; +#else + using Type = quint32; +#endif + static Type newId(); + +private: + QAtomicInteger counter; +}; + +class QRhiPassResourceTracker +{ +public: + bool isEmpty() const; + void reset(); + + struct UsageState { + int layout; + int access; + int stage; + }; + + enum BufferStage { + BufVertexInputStage, + BufVertexStage, + BufFragmentStage + }; + + enum BufferAccess { + BufVertexInput, + BufIndexRead, + BufUniformRead + }; + + void registerBufferOnce(QRhiBuffer *buf, int slot, BufferAccess access, BufferStage stage, + const UsageState &stateAtPassBegin); + + enum TextureStage { + TexVertexStage, + TexFragmentStage, + TexColorOutputStage, + TexDepthOutputStage + }; + + enum TextureAccess { + TexSample, + TexColorOutput, + TexDepthOutput + }; + + void registerTextureOnce(QRhiTexture *tex, TextureAccess access, TextureStage stage, + const UsageState &stateAtPassBegin); + + struct Buffer { + QRhiBuffer *buf; + int slot; + BufferAccess access; + BufferStage stage; + UsageState stateAtPassBegin; + }; + const QVector *buffers() const { return &m_buffers; } + + struct Texture { + QRhiTexture *tex; + TextureAccess access; + TextureStage stage; + UsageState stateAtPassBegin; + }; + const QVector *textures() const { return &m_textures; } + +private: + QVector m_buffers; + QVector m_textures; +}; + +Q_DECLARE_TYPEINFO(QRhiPassResourceTracker::Buffer, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(QRhiPassResourceTracker::Texture, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp new file mode 100644 index 0000000000..07e67d95d9 --- /dev/null +++ b/src/gui/rhi/qrhid3d11.cpp @@ -0,0 +1,3388 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Gui module +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qrhid3d11_p_p.h" +#include "qshader_p.h" +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +/* + Direct3D 11 backend. Provides a double-buffered flip model (FLIP_DISCARD) + swapchain. Textures and "static" buffers are USAGE_DEFAULT, leaving it to + UpdateSubResource to upload the data in any way it sees fit. "Dynamic" + buffers are USAGE_DYNAMIC and updating is done by mapping with WRITE_DISCARD. + (so here QRhiBuffer keeps a copy of the buffer contents and all of it is + memcpy'd every time, leaving the rest (juggling with the memory area Map + returns) to the driver). +*/ + +/*! + \class QRhiD3D11InitParams + \inmodule QtRhi + \brief Direct3D 11 specific initialization parameters. + + A D3D11-based QRhi needs no special parameters for initialization. If + desired, enableDebugLayer can be set to \c true to enable the Direct3D + debug layer. This can be useful during development, but should be avoided + in production builds. + + \badcode + QRhiD3D11InitParams params; + params.enableDebugLayer = true; + rhi = QRhi::create(QRhi::D3D11, ¶ms); + \endcode + + \note QRhiSwapChain should only be used in combination with QWindow + instances that have their surface type set to QSurface::OpenGLSurface. + There are currently no Direct3D specifics in the Windows platform support + of Qt and therefore there is no separate QSurface type available. + + \section2 Working with existing Direct3D 11 devices + + When interoperating with another graphics engine, it may be necessary to + get a QRhi instance that uses the same Direct3D device. This can be + achieved by passing a pointer to a QRhiD3D11NativeHandles to + QRhi::create(). Both the device and the device context must be set to a + non-null value then. + + The QRhi does not take ownership of any of the external objects. + + \note QRhi works with immediate contexts only. Deferred contexts are not + used in any way. + + \note Regardless of using an imported or a QRhi-created device context, the + \c ID3D11DeviceContext1 interface (Direct3D 11.1) must be supported. + Initialization will fail otherwise. + */ + +/*! + \class QRhiD3D11NativeHandles + \inmodule QtRhi + \brief Holds the D3D device and device context used by the QRhi. + + \note The class uses \c{void *} as the type since including the COM-based + \c{d3d11.h} headers is not acceptable here. The actual types are + \c{ID3D11Device *} and \c{ID3D11DeviceContext *}. + */ + +/*! + \class QRhiD3D11TextureNativeHandles + \inmodule QtRhi + \brief Holds the D3D texture object that is backing a QRhiTexture instance. + + \note The class uses \c{void *} as the type since including the COM-based + \c{d3d11.h} headers is not acceptable here. The actual type is + \c{ID3D11Texture2D *}. + */ + +QRhiD3D11::QRhiD3D11(QRhiD3D11InitParams *params, QRhiD3D11NativeHandles *importDevice) + : ofr(this) +{ + debugLayer = params->enableDebugLayer; + importedDevice = importDevice != nullptr; + if (importedDevice) { + dev = reinterpret_cast(importDevice->dev); + if (dev) { + ID3D11DeviceContext *ctx = reinterpret_cast(importDevice->context); + if (SUCCEEDED(ctx->QueryInterface(IID_ID3D11DeviceContext1, reinterpret_cast(&context)))) { + // get rid of the ref added by QueryInterface + ctx->Release(); + } else { + qWarning("ID3D11DeviceContext1 not supported by context, cannot import"); + importedDevice = false; + } + } else { + qWarning("No ID3D11Device given, cannot import"); + importedDevice = false; + } + } +} + +static QString comErrorMessage(HRESULT hr) +{ +#ifndef Q_OS_WINRT + const _com_error comError(hr); +#else + const _com_error comError(hr, nullptr); +#endif + QString result = QLatin1String("Error 0x") + QString::number(ulong(hr), 16); + if (const wchar_t *msg = comError.ErrorMessage()) + result += QLatin1String(": ") + QString::fromWCharArray(msg); + return result; +} + +static inline uint aligned(uint v, uint byteAlign) +{ + return (v + byteAlign - 1) & ~(byteAlign - 1); +} + +bool QRhiD3D11::create(QRhi::Flags flags) +{ + Q_UNUSED(flags); + + uint devFlags = 0; + if (debugLayer) + devFlags |= D3D11_CREATE_DEVICE_DEBUG; + + HRESULT hr; +#if !defined(Q_CC_MINGW) + hasDxgi2 = QOperatingSystemVersion::current() > QOperatingSystemVersion::Windows7; + if (hasDxgi2) + hr = CreateDXGIFactory2(0, IID_IDXGIFactory2, reinterpret_cast(&dxgiFactory)); + else +#endif + hr = CreateDXGIFactory1(IID_IDXGIFactory1, reinterpret_cast(&dxgiFactory)); + + if (FAILED(hr)) { + qWarning("Failed to create DXGI factory: %s", qPrintable(comErrorMessage(hr))); + return false; + } + + if (!importedDevice) { + IDXGIAdapter1 *adapterToUse = nullptr; + IDXGIAdapter1 *adapter; + int requestedAdapterIndex = -1; + if (qEnvironmentVariableIsSet("QT_D3D_ADAPTER_INDEX")) + requestedAdapterIndex = qEnvironmentVariableIntValue("QT_D3D_ADAPTER_INDEX"); + for (int adapterIndex = 0; dxgiFactory->EnumAdapters1(adapterIndex, &adapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) { + 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); + if (!adapterToUse && (requestedAdapterIndex < 0 || requestedAdapterIndex == adapterIndex)) { + adapterToUse = adapter; + qDebug(" using this adapter"); + } else { + adapter->Release(); + } + } + if (!adapterToUse) { + qWarning("No adapter"); + return false; + } + + ID3D11DeviceContext *ctx = nullptr; + HRESULT hr = D3D11CreateDevice(adapterToUse, D3D_DRIVER_TYPE_UNKNOWN, nullptr, devFlags, + nullptr, 0, D3D11_SDK_VERSION, + &dev, &featureLevel, &ctx); + adapterToUse->Release(); + if (FAILED(hr)) { + qWarning("Failed to create D3D11 device and context: %s", qPrintable(comErrorMessage(hr))); + return false; + } + if (SUCCEEDED(ctx->QueryInterface(IID_ID3D11DeviceContext1, reinterpret_cast(&context)))) { + ctx->Release(); + } else { + qWarning("ID3D11DeviceContext1 not supported"); + return false; + } + } else { + Q_ASSERT(dev && context); + featureLevel = dev->GetFeatureLevel(); + } + + if (FAILED(context->QueryInterface(IID_ID3DUserDefinedAnnotation, reinterpret_cast(&annotations)))) + annotations = nullptr; + + nativeHandlesStruct.dev = dev; + nativeHandlesStruct.context = context; + + return true; +} + +void QRhiD3D11::destroy() +{ + finishActiveReadbacks(); + + if (annotations) { + annotations->Release(); + annotations = nullptr; + } + + if (!importedDevice) { + if (context) { + context->Release(); + context = nullptr; + } + if (dev) { + dev->Release(); + dev = nullptr; + } + } + + if (dxgiFactory) { + dxgiFactory->Release(); + dxgiFactory = nullptr; + } +} + +void QRhiD3D11::reportLiveObjects(ID3D11Device *device) +{ + // this works only when params.enableDebugLayer was true + ID3D11Debug *debug; + if (SUCCEEDED(device->QueryInterface(IID_ID3D11Debug, reinterpret_cast(&debug)))) { + debug->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL); + debug->Release(); + } +} + +QVector QRhiD3D11::supportedSampleCounts() const +{ + return { 1, 2, 4, 8 }; +} + +DXGI_SAMPLE_DESC QRhiD3D11::effectiveSampleCount(int sampleCount) const +{ + DXGI_SAMPLE_DESC desc; + desc.Count = 1; + desc.Quality = 0; + + // Stay compatible with QSurfaceFormat and friends where samples == 0 means the same as 1. + int s = qBound(1, sampleCount, 64); + + if (!supportedSampleCounts().contains(s)) { + qWarning("Attempted to set unsupported sample count %d", sampleCount); + return desc; + } + + desc.Count = s; + if (s > 1) + desc.Quality = D3D11_STANDARD_MULTISAMPLE_PATTERN; + else + desc.Quality = 0; + + return desc; +} + +QRhiSwapChain *QRhiD3D11::createSwapChain() +{ + return new QD3D11SwapChain(this); +} + +QRhiBuffer *QRhiD3D11::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, int size) +{ + return new QD3D11Buffer(this, type, usage, size); +} + +int QRhiD3D11::ubufAlignment() const +{ + return 256; +} + +bool QRhiD3D11::isYUpInFramebuffer() const +{ + return false; +} + +bool QRhiD3D11::isYUpInNDC() const +{ + return true; +} + +bool QRhiD3D11::isClipDepthZeroToOne() const +{ + return true; +} + +QMatrix4x4 QRhiD3D11::clipSpaceCorrMatrix() const +{ + // Like with Vulkan, but Y is already good. + + static QMatrix4x4 m; + if (m.isIdentity()) { + // NB the ctor takes row-major + m = QMatrix4x4(1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.5f, 0.5f, + 0.0f, 0.0f, 0.0f, 1.0f); + } + return m; +} + +bool QRhiD3D11::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const +{ + Q_UNUSED(flags); + + if (format >= QRhiTexture::ETC2_RGB8 && format <= QRhiTexture::ASTC_12x12) + return false; + + return true; +} + +bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const +{ + switch (feature) { + case QRhi::MultisampleTexture: + return true; + case QRhi::MultisampleRenderBuffer: + return true; + case QRhi::DebugMarkers: + return annotations != nullptr; + case QRhi::Timestamps: + return true; + case QRhi::Instancing: + return true; + case QRhi::CustomInstanceStepRate: + return true; + case QRhi::PrimitiveRestart: + return true; + case QRhi::NonDynamicUniformBuffers: + return false; // because UpdateSubresource cannot deal with this + case QRhi::NonFourAlignedEffectiveIndexBufferOffset: + return true; + case QRhi::NPOTTextureRepeat: + return true; + case QRhi::RedOrAlpha8IsRed: + return true; + case QRhi::ElementIndexUint: + return true; + default: + Q_UNREACHABLE(); + return false; + } +} + +int QRhiD3D11::resourceLimit(QRhi::ResourceLimit limit) const +{ + switch (limit) { + case QRhi::TextureSizeMin: + return 1; + case QRhi::TextureSizeMax: + return D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION; + case QRhi::MaxColorAttachments: + return 8; + case QRhi::FramesInFlight: + return 2; // dummy + default: + Q_UNREACHABLE(); + return 0; + } +} + +const QRhiNativeHandles *QRhiD3D11::nativeHandles() +{ + return &nativeHandlesStruct; +} + +void QRhiD3D11::sendVMemStatsToProfiler() +{ + // nothing to do here +} + +QRhiRenderBuffer *QRhiD3D11::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize, + int sampleCount, QRhiRenderBuffer::Flags flags) +{ + return new QD3D11RenderBuffer(this, type, pixelSize, sampleCount, flags); +} + +QRhiTexture *QRhiD3D11::createTexture(QRhiTexture::Format format, const QSize &pixelSize, + int sampleCount, QRhiTexture::Flags flags) +{ + return new QD3D11Texture(this, format, pixelSize, sampleCount, flags); +} + +QRhiSampler *QRhiD3D11::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, + QRhiSampler::Filter mipmapMode, + QRhiSampler::AddressMode u, QRhiSampler::AddressMode v) +{ + return new QD3D11Sampler(this, magFilter, minFilter, mipmapMode, u, v); +} + +QRhiTextureRenderTarget *QRhiD3D11::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc, + QRhiTextureRenderTarget::Flags flags) +{ + return new QD3D11TextureRenderTarget(this, desc, flags); +} + +QRhiGraphicsPipeline *QRhiD3D11::createGraphicsPipeline() +{ + return new QD3D11GraphicsPipeline(this); +} + +QRhiShaderResourceBindings *QRhiD3D11::createShaderResourceBindings() +{ + return new QD3D11ShaderResourceBindings(this); +} + +void QRhiD3D11::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps) +{ + Q_ASSERT(inPass); + + QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); + QD3D11GraphicsPipeline *psD = QRHI_RES(QD3D11GraphicsPipeline, ps); + const bool pipelineChanged = cbD->currentPipeline != ps || cbD->currentPipelineGeneration != psD->generation; + + if (pipelineChanged) { + cbD->currentPipeline = ps; + cbD->currentPipelineGeneration = psD->generation; + + QD3D11CommandBuffer::Command cmd; + cmd.cmd = QD3D11CommandBuffer::Command::BindGraphicsPipeline; + cmd.args.bindGraphicsPipeline.ps = psD; + cbD->commands.append(cmd); + } +} + +void QRhiD3D11::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb, + int dynamicOffsetCount, + const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) +{ + Q_ASSERT(inPass); + + QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); + Q_ASSERT(cbD->currentPipeline); + + if (!srb) + srb = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentPipeline)->m_shaderResourceBindings; + + QD3D11ShaderResourceBindings *srbD = QRHI_RES(QD3D11ShaderResourceBindings, srb); + + bool hasDynamicOffsetInSrb = false; + bool srbUpdate = false; + for (int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) { + const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&srbD->sortedBindings[i]); + QD3D11ShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[i]); + switch (b->type) { + case QRhiShaderResourceBinding::UniformBuffer: + { + QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, b->u.ubuf.buf); + if (bufD->m_type == QRhiBuffer::Dynamic) + executeBufferHostWritesForCurrentFrame(bufD); + + if (bufD->generation != bd.ubuf.generation || bufD->m_id != bd.ubuf.id) { + srbUpdate = true; + bd.ubuf.id = bufD->m_id; + bd.ubuf.generation = bufD->generation; + } + + if (b->u.ubuf.hasDynamicOffset) + hasDynamicOffsetInSrb = true; + } + break; + case QRhiShaderResourceBinding::SampledTexture: + { + QD3D11Texture *texD = QRHI_RES(QD3D11Texture, b->u.stex.tex); + QD3D11Sampler *samplerD = QRHI_RES(QD3D11Sampler, b->u.stex.sampler); + if (texD->generation != bd.stex.texGeneration + || texD->m_id != bd.stex.texId + || samplerD->generation != bd.stex.samplerGeneration + || samplerD->m_id != bd.stex.samplerId) + { + srbUpdate = true; + bd.stex.texId = texD->m_id; + bd.stex.texGeneration = texD->generation; + bd.stex.samplerId = samplerD->m_id; + bd.stex.samplerGeneration = samplerD->generation; + } + } + break; + default: + Q_UNREACHABLE(); + break; + } + } + + if (srbUpdate) + updateShaderResourceBindings(srbD); + + const bool srbChanged = cbD->currentSrb != srb || cbD->currentSrbGeneration != srbD->generation; + + if (srbChanged || srbUpdate || hasDynamicOffsetInSrb) { + cbD->currentSrb = srb; + cbD->currentSrbGeneration = srbD->generation; + + QD3D11CommandBuffer::Command cmd; + cmd.cmd = QD3D11CommandBuffer::Command::BindShaderResources; + cmd.args.bindShaderResources.srb = srbD; + // dynamic offsets have to be applied at the time of executing the bind + // operations, not here + cmd.args.bindShaderResources.offsetOnlyChange = !srbChanged && !srbUpdate && hasDynamicOffsetInSrb; + cmd.args.bindShaderResources.dynamicOffsetCount = 0; + if (hasDynamicOffsetInSrb) { + if (dynamicOffsetCount < QD3D11CommandBuffer::Command::MAX_UBUF_BINDINGS) { + cmd.args.bindShaderResources.dynamicOffsetCount = dynamicOffsetCount; + uint *p = cmd.args.bindShaderResources.dynamicOffsetPairs; + for (int i = 0; i < dynamicOffsetCount; ++i) { + const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]); + const uint binding = dynOfs.first; + Q_ASSERT(aligned(dynOfs.second, 256) == dynOfs.second); + const uint offsetInConstants = dynOfs.second / 16; + *p++ = binding; + *p++ = offsetInConstants; + } + } else { + qWarning("Too many dynamic offsets (%d, max is %d)", + dynamicOffsetCount, QD3D11CommandBuffer::Command::MAX_UBUF_BINDINGS); + } + } + + cbD->commands.append(cmd); + } +} + +void QRhiD3D11::setVertexInput(QRhiCommandBuffer *cb, + int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings, + QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat) +{ + Q_ASSERT(inPass); + QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); + + bool needsBindVBuf = false; + for (int i = 0; i < bindingCount; ++i) { + const int inputSlot = startBinding + i; + QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, bindings[i].first); + Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::VertexBuffer)); + if (bufD->m_type == QRhiBuffer::Dynamic) + executeBufferHostWritesForCurrentFrame(bufD); + + if (cbD->currentVertexBuffers[inputSlot] != bufD->buffer + || cbD->currentVertexOffsets[inputSlot] != bindings[i].second) + { + needsBindVBuf = true; + cbD->currentVertexBuffers[inputSlot] = bufD->buffer; + cbD->currentVertexOffsets[inputSlot] = bindings[i].second; + } + } + + if (needsBindVBuf) { + QD3D11CommandBuffer::Command cmd; + cmd.cmd = QD3D11CommandBuffer::Command::BindVertexBuffers; + cmd.args.bindVertexBuffers.startSlot = startBinding; + cmd.args.bindVertexBuffers.slotCount = bindingCount; + const QVector inputBindings = + QRHI_RES(QD3D11GraphicsPipeline, cbD->currentPipeline)->m_vertexInputLayout.bindings(); + for (int i = 0, ie = qMin(bindingCount, inputBindings.count()); i != ie; ++i) { + QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, bindings[i].first); + cmd.args.bindVertexBuffers.buffers[i] = bufD->buffer; + cmd.args.bindVertexBuffers.offsets[i] = bindings[i].second; + cmd.args.bindVertexBuffers.strides[i] = inputBindings[i].stride(); + } + cbD->commands.append(cmd); + } + + if (indexBuf) { + QD3D11Buffer *ibufD = QRHI_RES(QD3D11Buffer, indexBuf); + Q_ASSERT(ibufD->m_usage.testFlag(QRhiBuffer::IndexBuffer)); + if (ibufD->m_type == QRhiBuffer::Dynamic) + executeBufferHostWritesForCurrentFrame(ibufD); + + const DXGI_FORMAT dxgiFormat = indexFormat == QRhiCommandBuffer::IndexUInt16 ? DXGI_FORMAT_R16_UINT + : DXGI_FORMAT_R32_UINT; + if (cbD->currentIndexBuffer != ibufD->buffer + || cbD->currentIndexOffset != indexOffset + || cbD->currentIndexFormat != dxgiFormat) + { + cbD->currentIndexBuffer = ibufD->buffer; + cbD->currentIndexOffset = indexOffset; + cbD->currentIndexFormat = dxgiFormat; + + QD3D11CommandBuffer::Command cmd; + cmd.cmd = QD3D11CommandBuffer::Command::BindIndexBuffer; + cmd.args.bindIndexBuffer.buffer = ibufD->buffer; + cmd.args.bindIndexBuffer.offset = indexOffset; + cmd.args.bindIndexBuffer.format = dxgiFormat; + cbD->commands.append(cmd); + } + } +} + +void QRhiD3D11::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) +{ + Q_ASSERT(inPass); + QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); + Q_ASSERT(cbD->currentTarget); + const QSize outputSize = cbD->currentTarget->pixelSize(); + + QD3D11CommandBuffer::Command cmd; + cmd.cmd = QD3D11CommandBuffer::Command::Viewport; + + // d3d expects top-left, QRhiViewport is bottom-left + float x, y, w, h; + if (!qrhi_toTopLeftRenderTargetRect(outputSize, viewport.viewport(), &x, &y, &w, &h)) + return; + + cmd.args.viewport.x = x; + cmd.args.viewport.y = y; + cmd.args.viewport.w = w; + cmd.args.viewport.h = h; + cmd.args.viewport.d0 = viewport.minDepth(); + cmd.args.viewport.d1 = viewport.maxDepth(); + cbD->commands.append(cmd); +} + +void QRhiD3D11::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) +{ + Q_ASSERT(inPass); + QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); + Q_ASSERT(cbD->currentTarget); + const QSize outputSize = cbD->currentTarget->pixelSize(); + + QD3D11CommandBuffer::Command cmd; + cmd.cmd = QD3D11CommandBuffer::Command::Scissor; + + // d3d expects top-left, QRhiScissor is bottom-left + int x, y, w, h; + if (!qrhi_toTopLeftRenderTargetRect(outputSize, scissor.scissor(), &x, &y, &w, &h)) + return; + + cmd.args.scissor.x = x; + cmd.args.scissor.y = y; + cmd.args.scissor.w = w; + cmd.args.scissor.h = h; + cbD->commands.append(cmd); +} + +void QRhiD3D11::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) +{ + Q_ASSERT(inPass); + QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); + QD3D11CommandBuffer::Command cmd; + cmd.cmd = QD3D11CommandBuffer::Command::BlendConstants; + cmd.args.blendConstants.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentPipeline); + cmd.args.blendConstants.c[0] = c.redF(); + cmd.args.blendConstants.c[1] = c.greenF(); + cmd.args.blendConstants.c[2] = c.blueF(); + cmd.args.blendConstants.c[3] = c.alphaF(); + cbD->commands.append(cmd); +} + +void QRhiD3D11::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) +{ + Q_ASSERT(inPass); + QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); + QD3D11CommandBuffer::Command cmd; + cmd.cmd = QD3D11CommandBuffer::Command::StencilRef; + cmd.args.stencilRef.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentPipeline); + cmd.args.stencilRef.ref = refValue; + cbD->commands.append(cmd); +} + +void QRhiD3D11::draw(QRhiCommandBuffer *cb, quint32 vertexCount, + quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) +{ + Q_ASSERT(inPass); + QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); + QD3D11CommandBuffer::Command cmd; + cmd.cmd = QD3D11CommandBuffer::Command::Draw; + cmd.args.draw.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentPipeline); + cmd.args.draw.vertexCount = vertexCount; + cmd.args.draw.instanceCount = instanceCount; + cmd.args.draw.firstVertex = firstVertex; + cmd.args.draw.firstInstance = firstInstance; + cbD->commands.append(cmd); +} + +void QRhiD3D11::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, + quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance) +{ + Q_ASSERT(inPass); + QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); + QD3D11CommandBuffer::Command cmd; + cmd.cmd = QD3D11CommandBuffer::Command::DrawIndexed; + cmd.args.drawIndexed.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentPipeline); + cmd.args.drawIndexed.indexCount = indexCount; + cmd.args.drawIndexed.instanceCount = instanceCount; + cmd.args.drawIndexed.firstIndex = firstIndex; + cmd.args.drawIndexed.vertexOffset = vertexOffset; + cmd.args.drawIndexed.firstInstance = firstInstance; + cbD->commands.append(cmd); +} + +void QRhiD3D11::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) +{ + if (!debugMarkers || !annotations) + return; + + QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); + QD3D11CommandBuffer::Command cmd; + cmd.cmd = QD3D11CommandBuffer::Command::DebugMarkBegin; + strncpy(cmd.args.debugMark.s, name.constData(), sizeof(cmd.args.debugMark.s)); + cmd.args.debugMark.s[sizeof(cmd.args.debugMark.s) - 1] = '\0'; + cbD->commands.append(cmd); +} + +void QRhiD3D11::debugMarkEnd(QRhiCommandBuffer *cb) +{ + if (!debugMarkers || !annotations) + return; + + QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); + QD3D11CommandBuffer::Command cmd; + cmd.cmd = QD3D11CommandBuffer::Command::DebugMarkEnd; + cbD->commands.append(cmd); +} + +void QRhiD3D11::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) +{ + if (!debugMarkers || !annotations) + return; + + QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); + QD3D11CommandBuffer::Command cmd; + cmd.cmd = QD3D11CommandBuffer::Command::DebugMarkMsg; + strncpy(cmd.args.debugMark.s, msg.constData(), sizeof(cmd.args.debugMark.s)); + cmd.args.debugMark.s[sizeof(cmd.args.debugMark.s) - 1] = '\0'; + cbD->commands.append(cmd); +} + +const QRhiNativeHandles *QRhiD3D11::nativeHandles(QRhiCommandBuffer *cb) +{ + Q_UNUSED(cb); + return nullptr; +} + +void QRhiD3D11::beginExternal(QRhiCommandBuffer *cb) +{ + Q_ASSERT(inPass); + Q_UNUSED(cb); + flushCommandBuffer(); +} + +void QRhiD3D11::endExternal(QRhiCommandBuffer *cb) +{ + Q_ASSERT(inPass); + QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); + Q_ASSERT(cbD->currentTarget); + Q_ASSERT(cbD->commands.isEmpty()); + cbD->resetCachedState(); + enqueueSetRenderTarget(cbD, cbD->currentTarget); +} + +QRhi::FrameOpResult QRhiD3D11::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) +{ + Q_UNUSED(flags); + + Q_ASSERT(!inFrame); + inFrame = true; + + QD3D11SwapChain *swapChainD = QRHI_RES(QD3D11SwapChain, swapChain); + contextState.currentSwapChain = swapChainD; + const int currentFrameSlot = swapChainD->currentFrameSlot; + QRhiProfilerPrivate *rhiP = profilerPrivateOrNull(); + + if (swapChainD->timestampActive[currentFrameSlot]) { + ID3D11Query *tsDisjoint = swapChainD->timestampDisjointQuery[currentFrameSlot]; + const int tsIdx = QD3D11SwapChain::BUFFER_COUNT * currentFrameSlot; + ID3D11Query *tsStart = swapChainD->timestampQuery[tsIdx]; + ID3D11Query *tsEnd = swapChainD->timestampQuery[tsIdx + 1]; + quint64 timestamps[2]; + D3D11_QUERY_DATA_TIMESTAMP_DISJOINT dj; + bool ok = true; + ok &= context->GetData(tsDisjoint, &dj, sizeof(dj), D3D11_ASYNC_GETDATA_DONOTFLUSH) == S_OK; + ok &= context->GetData(tsEnd, ×tamps[1], sizeof(quint64), D3D11_ASYNC_GETDATA_DONOTFLUSH) == S_OK; + // this above is often not ready, not even in frame_where_recorded+2, + // not clear why. so make the whole thing async and do not touch the + // queries until they are finally all available in frame this+2 or + // this+4 or ... + ok &= context->GetData(tsStart, ×tamps[0], sizeof(quint64), D3D11_ASYNC_GETDATA_DONOTFLUSH) == S_OK; + if (ok) { + if (!dj.Disjoint && dj.Frequency) { + const float elapsedMs = (timestamps[1] - timestamps[0]) / float(dj.Frequency) * 1000.0f; + // finally got a value, just report it, the profiler cares about min/max/avg anyway + QRHI_PROF_F(swapChainFrameGpuTime(swapChain, elapsedMs)); + } + swapChainD->timestampActive[currentFrameSlot] = false; + } // else leave timestampActive set to true, will retry in a subsequent beginFrame + } + + swapChainD->cb.resetState(); + + swapChainD->rt.d.rtv[0] = swapChainD->sampleDesc.Count > 1 ? + swapChainD->msaaRtv[currentFrameSlot] : swapChainD->rtv[currentFrameSlot]; + swapChainD->rt.d.dsv = swapChainD->ds ? swapChainD->ds->dsv : nullptr; + + QRHI_PROF_F(beginSwapChainFrame(swapChain)); + + finishActiveReadbacks(); + + return QRhi::FrameOpSuccess; +} + +QRhi::FrameOpResult QRhiD3D11::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) +{ + Q_ASSERT(inFrame); + inFrame = false; + + QD3D11SwapChain *swapChainD = QRHI_RES(QD3D11SwapChain, swapChain); + Q_ASSERT(contextState.currentSwapChain = swapChainD); + const int currentFrameSlot = swapChainD->currentFrameSlot; + + ID3D11Query *tsDisjoint = swapChainD->timestampDisjointQuery[currentFrameSlot]; + const int tsIdx = QD3D11SwapChain::BUFFER_COUNT * currentFrameSlot; + ID3D11Query *tsStart = swapChainD->timestampQuery[tsIdx]; + ID3D11Query *tsEnd = swapChainD->timestampQuery[tsIdx + 1]; + const bool recordTimestamps = tsDisjoint && tsStart && tsEnd && !swapChainD->timestampActive[currentFrameSlot]; + + // send all commands to the context + if (recordTimestamps) + executeCommandBuffer(&swapChainD->cb, swapChainD); + else + executeCommandBuffer(&swapChainD->cb); + + if (swapChainD->sampleDesc.Count > 1) { + context->ResolveSubresource(swapChainD->tex[currentFrameSlot], 0, + swapChainD->msaaTex[currentFrameSlot], 0, + swapChainD->colorFormat); + } + + // this is here because we want to include the time spent on the resolve as well + if (recordTimestamps) { + context->End(tsEnd); + context->End(tsDisjoint); + swapChainD->timestampActive[currentFrameSlot] = true; + } + + QRhiProfilerPrivate *rhiP = profilerPrivateOrNull(); + // this must be done before the Present + QRHI_PROF_F(endSwapChainFrame(swapChain, swapChainD->frameCount + 1)); + + if (!flags.testFlag(QRhi::SkipPresent)) { + const UINT presentFlags = 0; + HRESULT hr = swapChainD->swapChain->Present(swapChainD->swapInterval, presentFlags); + if (FAILED(hr)) + qWarning("Failed to present: %s", qPrintable(comErrorMessage(hr))); + + // move on to the next buffer + swapChainD->currentFrameSlot = (swapChainD->currentFrameSlot + 1) % QD3D11SwapChain::BUFFER_COUNT; + } else { + context->Flush(); + } + + swapChainD->frameCount += 1; + contextState.currentSwapChain = nullptr; + return QRhi::FrameOpSuccess; +} + +QRhi::FrameOpResult QRhiD3D11::beginOffscreenFrame(QRhiCommandBuffer **cb) +{ + Q_ASSERT(!inFrame); + inFrame = true; + ofr.active = true; + + ofr.cbWrapper.resetState(); + *cb = &ofr.cbWrapper; + + return QRhi::FrameOpSuccess; +} + +QRhi::FrameOpResult QRhiD3D11::endOffscreenFrame() +{ + Q_ASSERT(inFrame && ofr.active); + inFrame = false; + ofr.active = false; + + executeCommandBuffer(&ofr.cbWrapper); + + finishActiveReadbacks(); + + return QRhi::FrameOpSuccess; +} + +static inline DXGI_FORMAT toD3DTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags) +{ + const bool srgb = flags.testFlag(QRhiTexture::sRGB); + switch (format) { + case QRhiTexture::RGBA8: + return srgb ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R8G8B8A8_UNORM; + case QRhiTexture::BGRA8: + return srgb ? DXGI_FORMAT_B8G8R8A8_UNORM_SRGB : DXGI_FORMAT_B8G8R8A8_UNORM; + case QRhiTexture::R8: + return DXGI_FORMAT_R8_UNORM; + case QRhiTexture::R16: + return DXGI_FORMAT_R16_UNORM; + case QRhiTexture::RED_OR_ALPHA8: + return DXGI_FORMAT_R8_UNORM; + + case QRhiTexture::RGBA16F: + return DXGI_FORMAT_R16G16B16A16_FLOAT; + case QRhiTexture::RGBA32F: + return DXGI_FORMAT_R32G32B32A32_FLOAT; + + case QRhiTexture::D16: + return DXGI_FORMAT_R16_TYPELESS; + case QRhiTexture::D32F: + return DXGI_FORMAT_R32_TYPELESS; + + case QRhiTexture::BC1: + return srgb ? DXGI_FORMAT_BC1_UNORM_SRGB : DXGI_FORMAT_BC1_UNORM; + case QRhiTexture::BC2: + return srgb ? DXGI_FORMAT_BC2_UNORM_SRGB : DXGI_FORMAT_BC2_UNORM; + case QRhiTexture::BC3: + return srgb ? DXGI_FORMAT_BC3_UNORM_SRGB : DXGI_FORMAT_BC3_UNORM; + case QRhiTexture::BC4: + return DXGI_FORMAT_BC4_UNORM; + case QRhiTexture::BC5: + return DXGI_FORMAT_BC5_UNORM; + case QRhiTexture::BC6H: + return DXGI_FORMAT_BC6H_UF16; + case QRhiTexture::BC7: + return srgb ? DXGI_FORMAT_BC7_UNORM_SRGB : DXGI_FORMAT_BC7_UNORM; + + case QRhiTexture::ETC2_RGB8: + Q_FALLTHROUGH(); + case QRhiTexture::ETC2_RGB8A1: + Q_FALLTHROUGH(); + case QRhiTexture::ETC2_RGBA8: + qWarning("QRhiD3D11 does not support ETC2 textures"); + return DXGI_FORMAT_R8G8B8A8_UNORM; + + case QRhiTexture::ASTC_4x4: + Q_FALLTHROUGH(); + case QRhiTexture::ASTC_5x4: + Q_FALLTHROUGH(); + case QRhiTexture::ASTC_5x5: + Q_FALLTHROUGH(); + case QRhiTexture::ASTC_6x5: + Q_FALLTHROUGH(); + case QRhiTexture::ASTC_6x6: + Q_FALLTHROUGH(); + case QRhiTexture::ASTC_8x5: + Q_FALLTHROUGH(); + case QRhiTexture::ASTC_8x6: + Q_FALLTHROUGH(); + case QRhiTexture::ASTC_8x8: + Q_FALLTHROUGH(); + case QRhiTexture::ASTC_10x5: + Q_FALLTHROUGH(); + case QRhiTexture::ASTC_10x6: + Q_FALLTHROUGH(); + case QRhiTexture::ASTC_10x8: + Q_FALLTHROUGH(); + case QRhiTexture::ASTC_10x10: + Q_FALLTHROUGH(); + case QRhiTexture::ASTC_12x10: + Q_FALLTHROUGH(); + case QRhiTexture::ASTC_12x12: + qWarning("QRhiD3D11 does not support ASTC textures"); + return DXGI_FORMAT_R8G8B8A8_UNORM; + + default: + Q_UNREACHABLE(); + return DXGI_FORMAT_R8G8B8A8_UNORM; + } +} + +static inline QRhiTexture::Format colorTextureFormatFromDxgiFormat(DXGI_FORMAT format, QRhiTexture::Flags *flags) +{ + switch (format) { + case DXGI_FORMAT_R8G8B8A8_UNORM: + return QRhiTexture::RGBA8; + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + if (flags) + (*flags) |= QRhiTexture::sRGB; + return QRhiTexture::RGBA8; + case DXGI_FORMAT_B8G8R8A8_UNORM: + return QRhiTexture::BGRA8; + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: + if (flags) + (*flags) |= QRhiTexture::sRGB; + return QRhiTexture::BGRA8; + case DXGI_FORMAT_R8_UNORM: + return QRhiTexture::R8; + case DXGI_FORMAT_R16_UNORM: + return QRhiTexture::R16; + default: // this cannot assert, must warn and return unknown + qWarning("DXGI_FORMAT %d is not a recognized uncompressed color format", format); + break; + } + return QRhiTexture::UnknownFormat; +} + +static inline bool isDepthTextureFormat(QRhiTexture::Format format) +{ + switch (format) { + case QRhiTexture::Format::D16: + Q_FALLTHROUGH(); + case QRhiTexture::Format::D32F: + return true; + + default: + return false; + } +} + +QRhi::FrameOpResult QRhiD3D11::finish() +{ + Q_ASSERT(!inPass); + + if (inFrame) + flushCommandBuffer(); + + finishActiveReadbacks(); + + return QRhi::FrameOpSuccess; +} + +void QRhiD3D11::flushCommandBuffer() +{ + if (ofr.active) { + Q_ASSERT(!contextState.currentSwapChain); + executeCommandBuffer(&ofr.cbWrapper); + ofr.cbWrapper.resetCommands(); + } else { + Q_ASSERT(contextState.currentSwapChain); + executeCommandBuffer(&contextState.currentSwapChain->cb); // no timestampSwapChain, in order to avoid timestamp mess + contextState.currentSwapChain->cb.resetCommands(); + } +} + +void QRhiD3D11::enqueueSubresUpload(QD3D11Texture *texD, QD3D11CommandBuffer *cbD, + int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc) +{ + UINT subres = D3D11CalcSubresource(level, layer, texD->mipLevelCount); + const QPoint dp = subresDesc.destinationTopLeft(); + D3D11_BOX box; + box.front = 0; + // back, right, bottom are exclusive + box.back = 1; + QD3D11CommandBuffer::Command cmd; + cmd.cmd = QD3D11CommandBuffer::Command::UpdateSubRes; + cmd.args.updateSubRes.dst = texD->tex; + cmd.args.updateSubRes.dstSubRes = subres; + + bool cmdValid = true; + if (!subresDesc.image().isNull()) { + QImage img = subresDesc.image(); + QSize size = img.size(); + int bpl = img.bytesPerLine(); + if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) { + const QPoint sp = subresDesc.sourceTopLeft(); + if (!subresDesc.sourceSize().isEmpty()) + size = subresDesc.sourceSize(); + if (img.depth() == 32) { + const int offset = sp.y() * img.bytesPerLine() + sp.x() * 4; + cmd.args.updateSubRes.src = cbD->retainImage(img) + offset; + } else { + img = img.copy(sp.x(), sp.y(), size.width(), size.height()); + bpl = img.bytesPerLine(); + cmd.args.updateSubRes.src = cbD->retainImage(img); + } + } else { + cmd.args.updateSubRes.src = cbD->retainImage(img); + } + box.left = dp.x(); + box.top = dp.y(); + box.right = dp.x() + size.width(); + box.bottom = dp.y() + size.height(); + cmd.args.updateSubRes.hasDstBox = true; + cmd.args.updateSubRes.dstBox = box; + cmd.args.updateSubRes.srcRowPitch = bpl; + } else if (!subresDesc.data().isEmpty() && isCompressedFormat(texD->m_format)) { + const QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize) + : subresDesc.sourceSize(); + quint32 bpl = 0; + QSize blockDim; + compressedFormatInfo(texD->m_format, size, &bpl, nullptr, &blockDim); + // Everything must be a multiple of the block width and + // height, so e.g. a mip level of size 2x2 will be 4x4 when it + // comes to the actual data. + box.left = aligned(dp.x(), blockDim.width()); + box.top = aligned(dp.y(), blockDim.height()); + box.right = aligned(dp.x() + size.width(), blockDim.width()); + box.bottom = aligned(dp.y() + size.height(), blockDim.height()); + cmd.args.updateSubRes.hasDstBox = true; + cmd.args.updateSubRes.dstBox = box; + cmd.args.updateSubRes.src = cbD->retainData(subresDesc.data()); + cmd.args.updateSubRes.srcRowPitch = bpl; + } else if (!subresDesc.data().isEmpty()) { + const QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize) + : subresDesc.sourceSize(); + quint32 bpl = 0; + QSize blockDim; + textureFormatInfo(texD->m_format, size, &bpl, nullptr); + box.left = dp.x(); + box.top = dp.y(); + box.right = dp.x() + size.width(); + box.bottom = dp.y() + size.height(); + cmd.args.updateSubRes.hasDstBox = true; + cmd.args.updateSubRes.dstBox = box; + cmd.args.updateSubRes.src = cbD->retainData(subresDesc.data()); + cmd.args.updateSubRes.srcRowPitch = bpl; + } else { + qWarning("Invalid texture upload for %p layer=%d mip=%d", texD, layer, level); + cmdValid = false; + } + if (cmdValid) + cbD->commands.append(cmd); +} + +void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) +{ + QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); + QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates); + QRhiProfilerPrivate *rhiP = profilerPrivateOrNull(); + + for (const QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate &u : ud->dynamicBufferUpdates) { + QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, u.buf); + Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic); + memcpy(bufD->dynBuf.data() + u.offset, u.data.constData(), u.data.size()); + bufD->hasPendingDynamicUpdates = true; + } + + for (const QRhiResourceUpdateBatchPrivate::StaticBufferUpload &u : ud->staticBufferUploads) { + QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, u.buf); + Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic); + Q_ASSERT(u.offset + u.data.size() <= bufD->m_size); + QD3D11CommandBuffer::Command cmd; + cmd.cmd = QD3D11CommandBuffer::Command::UpdateSubRes; + cmd.args.updateSubRes.dst = bufD->buffer; + cmd.args.updateSubRes.dstSubRes = 0; + cmd.args.updateSubRes.src = cbD->retainData(u.data); + cmd.args.updateSubRes.srcRowPitch = 0; + // Specify the region (even when offset is 0 and all data is provided) + // since the ID3D11Buffer's size is rounded up to be a multiple of 256 + // while the data we have has the original size. + D3D11_BOX box; + box.left = u.offset; + box.top = box.front = 0; + box.back = box.bottom = 1; + box.right = u.offset + u.data.size(); // no -1: right, bottom, back are exclusive, see D3D11_BOX doc + cmd.args.updateSubRes.hasDstBox = true; + cmd.args.updateSubRes.dstBox = box; + cbD->commands.append(cmd); + } + + for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) { + if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) { + QD3D11Texture *texD = QRHI_RES(QD3D11Texture, u.upload.tex); + for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) { + for (int level = 0; level < QRhi::MAX_LEVELS; ++level) { + for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.upload.subresDesc[layer][level])) + enqueueSubresUpload(texD, cbD, layer, level, subresDesc); + } + } + } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) { + Q_ASSERT(u.copy.src && u.copy.dst); + QD3D11Texture *srcD = QRHI_RES(QD3D11Texture, u.copy.src); + QD3D11Texture *dstD = QRHI_RES(QD3D11Texture, u.copy.dst); + UINT srcSubRes = D3D11CalcSubresource(u.copy.desc.sourceLevel(), u.copy.desc.sourceLayer(), srcD->mipLevelCount); + UINT dstSubRes = D3D11CalcSubresource(u.copy.desc.destinationLevel(), u.copy.desc.destinationLayer(), dstD->mipLevelCount); + const QPoint dp = u.copy.desc.destinationTopLeft(); + const QSize size = u.copy.desc.pixelSize().isEmpty() ? srcD->m_pixelSize : u.copy.desc.pixelSize(); + const QPoint sp = u.copy.desc.sourceTopLeft(); + D3D11_BOX srcBox; + srcBox.left = sp.x(); + srcBox.top = sp.y(); + srcBox.front = 0; + // back, right, bottom are exclusive + srcBox.right = srcBox.left + size.width(); + srcBox.bottom = srcBox.top + size.height(); + srcBox.back = 1; + QD3D11CommandBuffer::Command cmd; + cmd.cmd = QD3D11CommandBuffer::Command::CopySubRes; + cmd.args.copySubRes.dst = dstD->tex; + cmd.args.copySubRes.dstSubRes = dstSubRes; + cmd.args.copySubRes.dstX = dp.x(); + cmd.args.copySubRes.dstY = dp.y(); + cmd.args.copySubRes.src = srcD->tex; + cmd.args.copySubRes.srcSubRes = srcSubRes; + cmd.args.copySubRes.hasSrcBox = true; + cmd.args.copySubRes.srcBox = srcBox; + cbD->commands.append(cmd); + } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) { + ActiveReadback aRb; + aRb.desc = u.read.rb; + aRb.result = u.read.result; + + ID3D11Resource *src; + DXGI_FORMAT dxgiFormat; + QSize pixelSize; + QRhiTexture::Format format; + UINT subres = 0; + QD3D11Texture *texD = QRHI_RES(QD3D11Texture, u.read.rb.texture()); + QD3D11SwapChain *swapChainD = nullptr; + + if (texD) { + if (texD->sampleDesc.Count > 1) { + qWarning("Multisample texture cannot be read back"); + continue; + } + src = texD->tex; + dxgiFormat = texD->dxgiFormat; + pixelSize = u.read.rb.level() > 0 ? q->sizeForMipLevel(u.read.rb.level(), texD->m_pixelSize) : texD->m_pixelSize; + format = texD->m_format; + subres = D3D11CalcSubresource(u.read.rb.level(), u.read.rb.layer(), texD->mipLevelCount); + } else { + Q_ASSERT(contextState.currentSwapChain); + swapChainD = QRHI_RES(QD3D11SwapChain, contextState.currentSwapChain); + if (swapChainD->sampleDesc.Count > 1) { + // Unlike with textures, reading back a multisample swapchain image + // has to be supported. Insert a resolve. + QD3D11CommandBuffer::Command rcmd; + rcmd.cmd = QD3D11CommandBuffer::Command::ResolveSubRes; + rcmd.args.resolveSubRes.dst = swapChainD->tex[swapChainD->currentFrameSlot]; + rcmd.args.resolveSubRes.dstSubRes = 0; + rcmd.args.resolveSubRes.src = swapChainD->msaaTex[swapChainD->currentFrameSlot]; + rcmd.args.resolveSubRes.srcSubRes = 0; + rcmd.args.resolveSubRes.format = swapChainD->colorFormat; + cbD->commands.append(rcmd); + } + src = swapChainD->tex[swapChainD->currentFrameSlot]; + dxgiFormat = swapChainD->colorFormat; + pixelSize = swapChainD->pixelSize; + format = colorTextureFormatFromDxgiFormat(dxgiFormat, nullptr); + if (format == QRhiTexture::UnknownFormat) + continue; + } + quint32 bufSize = 0; + quint32 bpl = 0; + textureFormatInfo(format, pixelSize, &bpl, &bufSize); + + D3D11_TEXTURE2D_DESC desc; + memset(&desc, 0, sizeof(desc)); + desc.Width = pixelSize.width(); + desc.Height = pixelSize.height(); + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.Format = dxgiFormat; + desc.SampleDesc.Count = 1; + desc.Usage = D3D11_USAGE_STAGING; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + ID3D11Texture2D *stagingTex; + HRESULT hr = dev->CreateTexture2D(&desc, nullptr, &stagingTex); + if (FAILED(hr)) { + qWarning("Failed to create readback staging texture: %s", qPrintable(comErrorMessage(hr))); + return; + } + QRHI_PROF_F(newReadbackBuffer(quint64(quintptr(stagingTex)), + texD ? static_cast(texD) : static_cast(swapChainD), + bufSize)); + + QD3D11CommandBuffer::Command cmd; + cmd.cmd = QD3D11CommandBuffer::Command::CopySubRes; + cmd.args.copySubRes.dst = stagingTex; + cmd.args.copySubRes.dstSubRes = 0; + cmd.args.copySubRes.dstX = 0; + cmd.args.copySubRes.dstY = 0; + cmd.args.copySubRes.src = src; + cmd.args.copySubRes.srcSubRes = subres; + cmd.args.copySubRes.hasSrcBox = false; + cbD->commands.append(cmd); + + aRb.stagingTex = stagingTex; + aRb.bufSize = bufSize; + aRb.bpl = bpl; + aRb.pixelSize = pixelSize; + aRb.format = format; + + activeReadbacks.append(aRb); + } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::MipGen) { + Q_ASSERT(u.mipgen.tex->flags().testFlag(QRhiTexture::UsedWithGenerateMips)); + QD3D11CommandBuffer::Command cmd; + cmd.cmd = QD3D11CommandBuffer::Command::GenMip; + cmd.args.genMip.srv = QRHI_RES(QD3D11Texture, u.mipgen.tex)->srv; + cbD->commands.append(cmd); + } + } + + ud->free(); +} + +void QRhiD3D11::finishActiveReadbacks() +{ + QVarLengthArray, 4> completedCallbacks; + QRhiProfilerPrivate *rhiP = profilerPrivateOrNull(); + + for (int i = activeReadbacks.count() - 1; i >= 0; --i) { + const QRhiD3D11::ActiveReadback &aRb(activeReadbacks[i]); + aRb.result->format = aRb.format; + aRb.result->pixelSize = aRb.pixelSize; + aRb.result->data.resize(aRb.bufSize); + + D3D11_MAPPED_SUBRESOURCE mp; + HRESULT hr = context->Map(aRb.stagingTex, 0, D3D11_MAP_READ, 0, &mp); + if (FAILED(hr)) { + qWarning("Failed to map readback staging texture: %s", qPrintable(comErrorMessage(hr))); + aRb.stagingTex->Release(); + continue; + } + // nothing says the rows are tightly packed in the texture, must take + // the stride into account + char *dst = aRb.result->data.data(); + char *src = static_cast(mp.pData); + for (int y = 0, h = aRb.pixelSize.height(); y != h; ++y) { + memcpy(dst, src, aRb.bpl); + dst += aRb.bpl; + src += mp.RowPitch; + } + context->Unmap(aRb.stagingTex, 0); + + aRb.stagingTex->Release(); + QRHI_PROF_F(releaseReadbackBuffer(quint64(quintptr(aRb.stagingTex)))); + + if (aRb.result->completed) + completedCallbacks.append(aRb.result->completed); + + activeReadbacks.removeAt(i); + } + + for (auto f : completedCallbacks) + f(); +} + +static inline QD3D11RenderTargetData *rtData(QRhiRenderTarget *rt) +{ + switch (rt->resourceType()) { + case QRhiResource::RenderTarget: + return &QRHI_RES(QD3D11ReferenceRenderTarget, rt)->d; + case QRhiResource::TextureRenderTarget: + return &QRHI_RES(QD3D11TextureRenderTarget, rt)->d; + default: + Q_UNREACHABLE(); + return nullptr; + } +} + +void QRhiD3D11::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) +{ + Q_ASSERT(inFrame && !inPass); + + enqueueResourceUpdates(cb, resourceUpdates); +} + +void QRhiD3D11::enqueueSetRenderTarget(QD3D11CommandBuffer *cbD, QRhiRenderTarget *rt) +{ + QD3D11CommandBuffer::Command fbCmd; + fbCmd.cmd = QD3D11CommandBuffer::Command::SetRenderTarget; + fbCmd.args.setRenderTarget.rt = rt; + cbD->commands.append(fbCmd); +} + +void QRhiD3D11::beginPass(QRhiCommandBuffer *cb, + QRhiRenderTarget *rt, + const QColor &colorClearValue, + const QRhiDepthStencilClearValue &depthStencilClearValue, + QRhiResourceUpdateBatch *resourceUpdates) +{ + Q_ASSERT(inFrame && !inPass); + + if (resourceUpdates) + enqueueResourceUpdates(cb, resourceUpdates); + + QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); + bool wantsColorClear = true; + bool wantsDsClear = true; + QD3D11RenderTargetData *rtD = rtData(rt); + if (rt->resourceType() == QRhiRenderTarget::TextureRenderTarget) { + QD3D11TextureRenderTarget *rtTex = QRHI_RES(QD3D11TextureRenderTarget, rt); + wantsColorClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents); + wantsDsClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents); + } + enqueueSetRenderTarget(cbD, rt); + + QD3D11CommandBuffer::Command clearCmd; + clearCmd.cmd = QD3D11CommandBuffer::Command::Clear; + clearCmd.args.clear.rt = rt; + clearCmd.args.clear.mask = 0; + if (rtD->colorAttCount && wantsColorClear) + clearCmd.args.clear.mask |= QD3D11CommandBuffer::Command::Color; + if (rtD->dsAttCount && wantsDsClear) + clearCmd.args.clear.mask |= QD3D11CommandBuffer::Command::Depth | QD3D11CommandBuffer::Command::Stencil; + + clearCmd.args.clear.c[0] = colorClearValue.redF(); + clearCmd.args.clear.c[1] = colorClearValue.greenF(); + clearCmd.args.clear.c[2] = colorClearValue.blueF(); + clearCmd.args.clear.c[3] = colorClearValue.alphaF(); + clearCmd.args.clear.d = depthStencilClearValue.depthClearValue(); + clearCmd.args.clear.s = depthStencilClearValue.stencilClearValue(); + cbD->commands.append(clearCmd); + + cbD->currentTarget = rt; + + inPass = true; +} + +void QRhiD3D11::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) +{ + Q_ASSERT(inPass); + inPass = false; + + QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); + if (cbD->currentTarget->resourceType() == QRhiResource::TextureRenderTarget) { + QD3D11TextureRenderTarget *rtTex = QRHI_RES(QD3D11TextureRenderTarget, cbD->currentTarget); + const QVector colorAttachments = rtTex->m_desc.colorAttachments(); + for (int att = 0, attCount = colorAttachments.count(); att != attCount; ++att) { + const QRhiColorAttachment &colorAtt(colorAttachments[att]); + if (!colorAtt.resolveTexture()) + continue; + + QD3D11Texture *dstTexD = QRHI_RES(QD3D11Texture, colorAtt.resolveTexture()); + QD3D11Texture *srcTexD = QRHI_RES(QD3D11Texture, colorAtt.texture()); + QD3D11RenderBuffer *srcRbD = QRHI_RES(QD3D11RenderBuffer, colorAtt.renderBuffer()); + Q_ASSERT(srcTexD || srcRbD); + QD3D11CommandBuffer::Command cmd; + cmd.cmd = QD3D11CommandBuffer::Command::ResolveSubRes; + cmd.args.resolveSubRes.dst = dstTexD->tex; + cmd.args.resolveSubRes.dstSubRes = D3D11CalcSubresource(colorAtt.resolveLevel(), + colorAtt.resolveLayer(), + dstTexD->mipLevelCount); + if (srcTexD) { + cmd.args.resolveSubRes.src = srcTexD->tex; + if (srcTexD->dxgiFormat != dstTexD->dxgiFormat) { + qWarning("Resolve source and destination formats do not match"); + continue; + } + if (srcTexD->sampleDesc.Count <= 1) { + qWarning("Cannot resolve a non-multisample texture"); + continue; + } + if (srcTexD->m_pixelSize != dstTexD->m_pixelSize) { + qWarning("Resolve source and destination sizes do not match"); + continue; + } + } else { + cmd.args.resolveSubRes.src = srcRbD->tex; + if (srcRbD->dxgiFormat != dstTexD->dxgiFormat) { + qWarning("Resolve source and destination formats do not match"); + continue; + } + if (srcRbD->m_pixelSize != dstTexD->m_pixelSize) { + qWarning("Resolve source and destination sizes do not match"); + continue; + } + } + cmd.args.resolveSubRes.srcSubRes = D3D11CalcSubresource(0, colorAtt.layer(), 1); + cmd.args.resolveSubRes.format = dstTexD->dxgiFormat; + cbD->commands.append(cmd); + } + } + + cbD->currentTarget = nullptr; + + if (resourceUpdates) + enqueueResourceUpdates(cb, resourceUpdates); +} + +void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD) +{ + srbD->vsubufs.clear(); + srbD->vsubufoffsets.clear(); + srbD->vsubufsizes.clear(); + + srbD->fsubufs.clear(); + srbD->fsubufoffsets.clear(); + srbD->fsubufsizes.clear(); + + srbD->vssamplers.clear(); + srbD->vsshaderresources.clear(); + + srbD->fssamplers.clear(); + srbD->fsshaderresources.clear(); + + for (int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) { + const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&srbD->sortedBindings[i]); + QD3D11ShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[i]); + switch (b->type) { + case QRhiShaderResourceBinding::UniformBuffer: + { + QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, b->u.ubuf.buf); + Q_ASSERT(aligned(b->u.ubuf.offset, 256) == b->u.ubuf.offset); + bd.ubuf.id = bufD->m_id; + bd.ubuf.generation = bufD->generation; + // dynamic ubuf offsets are not considered here, those are baked in + // at a later stage, which is good as vsubufoffsets and friends are + // per-srb, not per-setShaderResources call + const uint offsetInConstants = b->u.ubuf.offset / 16; + // size must be 16 mult. (in constants, i.e. multiple of 256 bytes). + // We can round up if needed since the buffers's actual size + // (ByteWidth) is always a multiple of 256. + const uint sizeInConstants = aligned(b->u.ubuf.maybeSize ? b->u.ubuf.maybeSize : bufD->m_size, 256) / 16; + if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) { + srbD->vsubufs.feed(b->binding, bufD->buffer); + srbD->vsubufoffsets.feed(b->binding, offsetInConstants); + srbD->vsubufsizes.feed(b->binding, sizeInConstants); + } + if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) { + srbD->fsubufs.feed(b->binding, bufD->buffer); + srbD->fsubufoffsets.feed(b->binding, offsetInConstants); + srbD->fsubufsizes.feed(b->binding, sizeInConstants); + } + } + break; + case QRhiShaderResourceBinding::SampledTexture: + { + // A sampler with binding N is mapped to a HLSL sampler and texture + // with registers sN and tN by SPIRV-Cross. + QD3D11Texture *texD = QRHI_RES(QD3D11Texture, b->u.stex.tex); + QD3D11Sampler *samplerD = QRHI_RES(QD3D11Sampler, b->u.stex.sampler); + bd.stex.texId = texD->m_id; + bd.stex.texGeneration = texD->generation; + bd.stex.samplerId = samplerD->m_id; + bd.stex.samplerGeneration = samplerD->generation; + if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) { + srbD->vssamplers.feed(b->binding, samplerD->samplerState); + srbD->vsshaderresources.feed(b->binding, texD->srv); + } + if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) { + srbD->fssamplers.feed(b->binding, samplerD->samplerState); + srbD->fsshaderresources.feed(b->binding, texD->srv); + } + } + break; + default: + Q_UNREACHABLE(); + break; + } + } + + srbD->vsubufs.finish(); + srbD->vsubufoffsets.finish(); + srbD->vsubufsizes.finish(); + + srbD->fsubufs.finish(); + srbD->fsubufoffsets.finish(); + srbD->fsubufsizes.finish(); + + srbD->vssamplers.finish(); + srbD->vsshaderresources.finish(); + + srbD->fssamplers.finish(); + srbD->fsshaderresources.finish(); +} + +void QRhiD3D11::executeBufferHostWritesForCurrentFrame(QD3D11Buffer *bufD) +{ + if (!bufD->hasPendingDynamicUpdates) + return; + + Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic); + bufD->hasPendingDynamicUpdates = false; + D3D11_MAPPED_SUBRESOURCE mp; + HRESULT hr = context->Map(bufD->buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mp); + if (SUCCEEDED(hr)) { + memcpy(mp.pData, bufD->dynBuf.constData(), bufD->dynBuf.size()); + context->Unmap(bufD->buffer, 0); + } else { + qWarning("Failed to map buffer: %s", qPrintable(comErrorMessage(hr))); + } +} + +void QRhiD3D11::bindShaderResources(QD3D11ShaderResourceBindings *srbD, + const uint *dynOfsPairs, int dynOfsPairCount, + bool offsetOnlyChange) +{ + if (!offsetOnlyChange) { + for (const auto &batch : srbD->vssamplers.batches) + context->VSSetSamplers(batch.startBinding, batch.resources.count(), batch.resources.constData()); + + for (const auto &batch : srbD->vsshaderresources.batches) { + context->VSSetShaderResources(batch.startBinding, batch.resources.count(), batch.resources.constData()); + contextState.vsHighestActiveSrvBinding = qMax(contextState.vsHighestActiveSrvBinding, + batch.startBinding + batch.resources.count() - 1); + } + + for (const auto &batch : srbD->fssamplers.batches) + context->PSSetSamplers(batch.startBinding, batch.resources.count(), batch.resources.constData()); + + for (const auto &batch : srbD->fsshaderresources.batches) { + context->PSSetShaderResources(batch.startBinding, batch.resources.count(), batch.resources.constData()); + contextState.fsHighestActiveSrvBinding = qMax(contextState.fsHighestActiveSrvBinding, + batch.startBinding + batch.resources.count() - 1); + } + } + + for (int i = 0, ie = srbD->vsubufs.batches.count(); i != ie; ++i) { + if (!dynOfsPairCount) { + context->VSSetConstantBuffers1(srbD->vsubufs.batches[i].startBinding, + srbD->vsubufs.batches[i].resources.count(), + srbD->vsubufs.batches[i].resources.constData(), + srbD->vsubufoffsets.batches[i].resources.constData(), + srbD->vsubufsizes.batches[i].resources.constData()); + } else { + const UINT count = srbD->vsubufs.batches[i].resources.count(); + const UINT startBinding = srbD->vsubufs.batches[i].startBinding; + QVarLengthArray offsets = srbD->vsubufoffsets.batches[i].resources; + for (UINT b = 0; b < count; ++b) { + for (int di = 0; di < dynOfsPairCount; ++di) { + const uint binding = dynOfsPairs[2 * di]; + if (binding == startBinding + b) { + const uint offsetInConstants = dynOfsPairs[2 * di + 1]; + offsets[b] = offsetInConstants; + break; + } + } + } + context->VSSetConstantBuffers1(startBinding, + count, + srbD->vsubufs.batches[i].resources.constData(), + offsets.constData(), + srbD->vsubufsizes.batches[i].resources.constData()); + } + } + + for (int i = 0, ie = srbD->fsubufs.batches.count(); i != ie; ++i) { + if (!dynOfsPairCount) { + context->PSSetConstantBuffers1(srbD->fsubufs.batches[i].startBinding, + srbD->fsubufs.batches[i].resources.count(), + srbD->fsubufs.batches[i].resources.constData(), + srbD->fsubufoffsets.batches[i].resources.constData(), + srbD->fsubufsizes.batches[i].resources.constData()); + } else { + const UINT count = srbD->fsubufs.batches[i].resources.count(); + const UINT startBinding = srbD->fsubufs.batches[i].startBinding; + QVarLengthArray offsets = srbD->fsubufoffsets.batches[i].resources; + for (UINT b = 0; b < count; ++b) { + for (int di = 0; di < dynOfsPairCount; ++di) { + const uint binding = dynOfsPairs[2 * di]; + if (binding == startBinding + b) { + const uint offsetInConstants = dynOfsPairs[2 * di + 1]; + offsets[b] = offsetInConstants; + break; + } + } + } + context->PSSetConstantBuffers1(startBinding, + count, + srbD->fsubufs.batches[i].resources.constData(), + offsets.constData(), + srbD->fsubufsizes.batches[i].resources.constData()); + } + } +} + +void QRhiD3D11::setRenderTarget(QRhiRenderTarget *rt) +{ + // The new output cannot be bound as input from the previous frame, + // otherwise the debug layer complains. Avoid this. + const int nullsrvCount = qMax(contextState.vsHighestActiveSrvBinding, contextState.fsHighestActiveSrvBinding) + 1; + if (nullsrvCount > 0) { + QVarLengthArray nullsrvs(nullsrvCount); + for (int i = 0; i < nullsrvs.count(); ++i) + nullsrvs[i] = nullptr; + if (contextState.vsHighestActiveSrvBinding >= 0) { + context->VSSetShaderResources(0, contextState.vsHighestActiveSrvBinding + 1, nullsrvs.constData()); + contextState.vsHighestActiveSrvBinding = -1; + } + if (contextState.fsHighestActiveSrvBinding >= 0) { + context->PSSetShaderResources(0, contextState.fsHighestActiveSrvBinding + 1, nullsrvs.constData()); + contextState.fsHighestActiveSrvBinding = -1; + } + } + QD3D11RenderTargetData *rtD = rtData(rt); + context->OMSetRenderTargets(rtD->colorAttCount, rtD->colorAttCount ? rtD->rtv : nullptr, rtD->dsv); +} + +void QRhiD3D11::executeCommandBuffer(QD3D11CommandBuffer *cbD, QD3D11SwapChain *timestampSwapChain) +{ + quint32 stencilRef = 0; + float blendConstants[] = { 1, 1, 1, 1 }; + + if (timestampSwapChain) { + const int currentFrameSlot = timestampSwapChain->currentFrameSlot; + ID3D11Query *tsDisjoint = timestampSwapChain->timestampDisjointQuery[currentFrameSlot]; + const int tsIdx = QD3D11SwapChain::BUFFER_COUNT * currentFrameSlot; + ID3D11Query *tsStart = timestampSwapChain->timestampQuery[tsIdx]; + if (tsDisjoint && tsStart && !timestampSwapChain->timestampActive[currentFrameSlot]) { + // The timestamps seem to include vsync time with Present(1), except + // when running on a non-primary gpu. This is not ideal. So try working + // it around by issuing a semi-fake OMSetRenderTargets early and + // writing the first timestamp only afterwards. + context->Begin(tsDisjoint); + setRenderTarget(×tampSwapChain->rt); + context->End(tsStart); // just record a timestamp, no Begin needed + } + } + + for (const QD3D11CommandBuffer::Command &cmd : qAsConst(cbD->commands)) { + switch (cmd.cmd) { + case QD3D11CommandBuffer::Command::SetRenderTarget: + setRenderTarget(cmd.args.setRenderTarget.rt); + break; + case QD3D11CommandBuffer::Command::Clear: + { + QD3D11RenderTargetData *rtD = rtData(cmd.args.clear.rt); + if (cmd.args.clear.mask & QD3D11CommandBuffer::Command::Color) { + for (int i = 0; i < rtD->colorAttCount; ++i) + context->ClearRenderTargetView(rtD->rtv[i], cmd.args.clear.c); + } + uint ds = 0; + if (cmd.args.clear.mask & QD3D11CommandBuffer::Command::Depth) + ds |= D3D11_CLEAR_DEPTH; + if (cmd.args.clear.mask & QD3D11CommandBuffer::Command::Stencil) + ds |= D3D11_CLEAR_STENCIL; + if (ds) + context->ClearDepthStencilView(rtD->dsv, ds, cmd.args.clear.d, cmd.args.clear.s); + } + break; + case QD3D11CommandBuffer::Command::Viewport: + { + D3D11_VIEWPORT v; + v.TopLeftX = cmd.args.viewport.x; + v.TopLeftY = cmd.args.viewport.y; + v.Width = cmd.args.viewport.w; + v.Height = cmd.args.viewport.h; + v.MinDepth = cmd.args.viewport.d0; + v.MaxDepth = cmd.args.viewport.d1; + context->RSSetViewports(1, &v); + } + break; + case QD3D11CommandBuffer::Command::Scissor: + { + D3D11_RECT r; + r.left = cmd.args.scissor.x; + r.top = cmd.args.scissor.y; + // right and bottom are exclusive + r.right = cmd.args.scissor.x + cmd.args.scissor.w; + r.bottom = cmd.args.scissor.y + cmd.args.scissor.h; + context->RSSetScissorRects(1, &r); + } + break; + case QD3D11CommandBuffer::Command::BindVertexBuffers: + context->IASetVertexBuffers(cmd.args.bindVertexBuffers.startSlot, + cmd.args.bindVertexBuffers.slotCount, + cmd.args.bindVertexBuffers.buffers, + cmd.args.bindVertexBuffers.strides, + cmd.args.bindVertexBuffers.offsets); + break; + case QD3D11CommandBuffer::Command::BindIndexBuffer: + context->IASetIndexBuffer(cmd.args.bindIndexBuffer.buffer, + cmd.args.bindIndexBuffer.format, + cmd.args.bindIndexBuffer.offset); + break; + case QD3D11CommandBuffer::Command::BindGraphicsPipeline: + { + QD3D11GraphicsPipeline *psD = cmd.args.bindGraphicsPipeline.ps; + context->VSSetShader(psD->vs, nullptr, 0); + context->PSSetShader(psD->fs, nullptr, 0); + context->IASetPrimitiveTopology(psD->d3dTopology); + context->IASetInputLayout(psD->inputLayout); + context->OMSetDepthStencilState(psD->dsState, stencilRef); + context->OMSetBlendState(psD->blendState, blendConstants, 0xffffffff); + context->RSSetState(psD->rastState); + } + break; + case QD3D11CommandBuffer::Command::BindShaderResources: + bindShaderResources(cmd.args.bindShaderResources.srb, + cmd.args.bindShaderResources.dynamicOffsetPairs, + cmd.args.bindShaderResources.dynamicOffsetCount, + cmd.args.bindShaderResources.offsetOnlyChange); + break; + case QD3D11CommandBuffer::Command::StencilRef: + stencilRef = cmd.args.stencilRef.ref; + context->OMSetDepthStencilState(cmd.args.stencilRef.ps->dsState, stencilRef); + break; + case QD3D11CommandBuffer::Command::BlendConstants: + memcpy(blendConstants, cmd.args.blendConstants.c, 4 * sizeof(float)); + context->OMSetBlendState(cmd.args.blendConstants.ps->blendState, blendConstants, 0xffffffff); + break; + case QD3D11CommandBuffer::Command::Draw: + if (cmd.args.draw.ps) { + if (cmd.args.draw.instanceCount == 1) + context->Draw(cmd.args.draw.vertexCount, cmd.args.draw.firstVertex); + else + context->DrawInstanced(cmd.args.draw.vertexCount, cmd.args.draw.instanceCount, + cmd.args.draw.firstVertex, cmd.args.draw.firstInstance); + } else { + qWarning("No graphics pipeline active for draw; ignored"); + } + break; + case QD3D11CommandBuffer::Command::DrawIndexed: + if (cmd.args.drawIndexed.ps) { + if (cmd.args.drawIndexed.instanceCount == 1) + context->DrawIndexed(cmd.args.drawIndexed.indexCount, cmd.args.drawIndexed.firstIndex, + cmd.args.drawIndexed.vertexOffset); + else + context->DrawIndexedInstanced(cmd.args.drawIndexed.indexCount, cmd.args.drawIndexed.instanceCount, + cmd.args.drawIndexed.firstIndex, cmd.args.drawIndexed.vertexOffset, + cmd.args.drawIndexed.firstInstance); + } else { + qWarning("No graphics pipeline active for drawIndexed; ignored"); + } + break; + case QD3D11CommandBuffer::Command::UpdateSubRes: + context->UpdateSubresource(cmd.args.updateSubRes.dst, cmd.args.updateSubRes.dstSubRes, + cmd.args.updateSubRes.hasDstBox ? &cmd.args.updateSubRes.dstBox : nullptr, + cmd.args.updateSubRes.src, cmd.args.updateSubRes.srcRowPitch, 0); + break; + case QD3D11CommandBuffer::Command::CopySubRes: + context->CopySubresourceRegion(cmd.args.copySubRes.dst, cmd.args.copySubRes.dstSubRes, + cmd.args.copySubRes.dstX, cmd.args.copySubRes.dstY, 0, + cmd.args.copySubRes.src, cmd.args.copySubRes.srcSubRes, + cmd.args.copySubRes.hasSrcBox ? &cmd.args.copySubRes.srcBox : nullptr); + break; + case QD3D11CommandBuffer::Command::ResolveSubRes: + context->ResolveSubresource(cmd.args.resolveSubRes.dst, cmd.args.resolveSubRes.dstSubRes, + cmd.args.resolveSubRes.src, cmd.args.resolveSubRes.srcSubRes, + cmd.args.resolveSubRes.format); + break; + case QD3D11CommandBuffer::Command::GenMip: + context->GenerateMips(cmd.args.genMip.srv); + break; + case QD3D11CommandBuffer::Command::DebugMarkBegin: + annotations->BeginEvent(reinterpret_cast(QString::fromLatin1(cmd.args.debugMark.s).utf16())); + break; + case QD3D11CommandBuffer::Command::DebugMarkEnd: + annotations->EndEvent(); + break; + case QD3D11CommandBuffer::Command::DebugMarkMsg: + annotations->SetMarker(reinterpret_cast(QString::fromLatin1(cmd.args.debugMark.s).utf16())); + break; + default: + break; + } + } +} + +QD3D11Buffer::QD3D11Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size) + : QRhiBuffer(rhi, type, usage, size) +{ +} + +QD3D11Buffer::~QD3D11Buffer() +{ + release(); +} + +void QD3D11Buffer::release() +{ + if (!buffer) + return; + + dynBuf.clear(); + + buffer->Release(); + buffer = nullptr; + + QRHI_RES_RHI(QRhiD3D11); + QRHI_PROF; + QRHI_PROF_F(releaseBuffer(this)); + rhiD->unregisterResource(this); +} + +static inline uint toD3DBufferUsage(QRhiBuffer::UsageFlags usage) +{ + int u = 0; + if (usage.testFlag(QRhiBuffer::VertexBuffer)) + u |= D3D11_BIND_VERTEX_BUFFER; + if (usage.testFlag(QRhiBuffer::IndexBuffer)) + u |= D3D11_BIND_INDEX_BUFFER; + if (usage.testFlag(QRhiBuffer::UniformBuffer)) + u |= D3D11_BIND_CONSTANT_BUFFER; + return u; +} + +bool QD3D11Buffer::build() +{ + if (buffer) + release(); + + const int nonZeroSize = m_size <= 0 ? 256 : m_size; + const int roundedSize = m_usage.testFlag(QRhiBuffer::UniformBuffer) ? aligned(nonZeroSize, 256) : nonZeroSize; + + D3D11_BUFFER_DESC desc; + memset(&desc, 0, sizeof(desc)); + desc.ByteWidth = roundedSize; + desc.Usage = m_type == Dynamic ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT; + desc.BindFlags = toD3DBufferUsage(m_usage); + desc.CPUAccessFlags = m_type == Dynamic ? D3D11_CPU_ACCESS_WRITE : 0; + + QRHI_RES_RHI(QRhiD3D11); + HRESULT hr = rhiD->dev->CreateBuffer(&desc, nullptr, &buffer); + if (FAILED(hr)) { + qWarning("Failed to create buffer: %s", qPrintable(comErrorMessage(hr))); + return false; + } + + if (m_type == Dynamic) { + dynBuf.resize(m_size); + hasPendingDynamicUpdates = false; + } + + if (!m_objectName.isEmpty()) + buffer->SetPrivateData(WKPDID_D3DDebugObjectName, m_objectName.size(), m_objectName.constData()); + + QRHI_PROF; + QRHI_PROF_F(newBuffer(this, roundedSize, m_type == Dynamic ? 2 : 1, m_type == Dynamic ? 1 : 0)); + + generation += 1; + rhiD->registerResource(this); + return true; +} + +QD3D11RenderBuffer::QD3D11RenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize, + int sampleCount, QRhiRenderBuffer::Flags flags) + : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags) +{ +} + +QD3D11RenderBuffer::~QD3D11RenderBuffer() +{ + release(); +} + +void QD3D11RenderBuffer::release() +{ + if (!tex) + return; + + if (dsv) { + dsv->Release(); + dsv = nullptr; + } + + if (rtv) { + rtv->Release(); + rtv = nullptr; + } + + tex->Release(); + tex = nullptr; + + QRHI_RES_RHI(QRhiD3D11); + QRHI_PROF; + QRHI_PROF_F(releaseRenderBuffer(this)); + rhiD->unregisterResource(this); +} + +bool QD3D11RenderBuffer::build() +{ + if (tex) + release(); + + if (m_pixelSize.isEmpty()) + return false; + + QRHI_RES_RHI(QRhiD3D11); + sampleDesc = rhiD->effectiveSampleCount(m_sampleCount); + + D3D11_TEXTURE2D_DESC desc; + memset(&desc, 0, sizeof(desc)); + desc.Width = m_pixelSize.width(); + desc.Height = m_pixelSize.height(); + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.SampleDesc = sampleDesc; + desc.Usage = D3D11_USAGE_DEFAULT; + + if (m_type == Color) { + dxgiFormat = DXGI_FORMAT_R8G8B8A8_UNORM; + desc.Format = dxgiFormat; + desc.BindFlags = D3D11_BIND_RENDER_TARGET; + HRESULT hr = rhiD->dev->CreateTexture2D(&desc, nullptr, &tex); + if (FAILED(hr)) { + qWarning("Failed to create color renderbuffer: %s", qPrintable(comErrorMessage(hr))); + return false; + } + D3D11_RENDER_TARGET_VIEW_DESC rtvDesc; + memset(&rtvDesc, 0, sizeof(rtvDesc)); + rtvDesc.Format = dxgiFormat; + rtvDesc.ViewDimension = desc.SampleDesc.Count > 1 ? D3D11_RTV_DIMENSION_TEXTURE2DMS + : D3D11_RTV_DIMENSION_TEXTURE2D; + hr = rhiD->dev->CreateRenderTargetView(tex, &rtvDesc, &rtv); + if (FAILED(hr)) { + qWarning("Failed to create rtv: %s", qPrintable(comErrorMessage(hr))); + return false; + } + } else if (m_type == DepthStencil) { + dxgiFormat = DXGI_FORMAT_D24_UNORM_S8_UINT; + desc.Format = dxgiFormat; + desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; + HRESULT hr = rhiD->dev->CreateTexture2D(&desc, nullptr, &tex); + if (FAILED(hr)) { + qWarning("Failed to create depth-stencil buffer: %s", qPrintable(comErrorMessage(hr))); + return false; + } + D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc; + memset(&dsvDesc, 0, sizeof(dsvDesc)); + dsvDesc.Format = dxgiFormat; + dsvDesc.ViewDimension = desc.SampleDesc.Count > 1 ? D3D11_DSV_DIMENSION_TEXTURE2DMS + : D3D11_DSV_DIMENSION_TEXTURE2D; + hr = rhiD->dev->CreateDepthStencilView(tex, &dsvDesc, &dsv); + if (FAILED(hr)) { + qWarning("Failed to create dsv: %s", qPrintable(comErrorMessage(hr))); + return false; + } + } else { + return false; + } + + if (!m_objectName.isEmpty()) + tex->SetPrivateData(WKPDID_D3DDebugObjectName, m_objectName.size(), m_objectName.constData()); + + QRHI_PROF; + QRHI_PROF_F(newRenderBuffer(this, false, false, sampleDesc.Count)); + + rhiD->registerResource(this); + return true; +} + +QRhiTexture::Format QD3D11RenderBuffer::backingFormat() const +{ + return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat; +} + +QD3D11Texture::QD3D11Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, + int sampleCount, Flags flags) + : QRhiTexture(rhi, format, pixelSize, sampleCount, flags) +{ +} + +QD3D11Texture::~QD3D11Texture() +{ + release(); +} + +void QD3D11Texture::release() +{ + if (!tex) + return; + + if (srv) { + srv->Release(); + srv = nullptr; + } + + if (owns) + tex->Release(); + + tex = nullptr; + + QRHI_RES_RHI(QRhiD3D11); + QRHI_PROF; + QRHI_PROF_F(releaseTexture(this)); + rhiD->unregisterResource(this); +} + +static inline DXGI_FORMAT toD3DDepthTextureSRVFormat(QRhiTexture::Format format) +{ + switch (format) { + case QRhiTexture::Format::D16: + return DXGI_FORMAT_R16_FLOAT; + case QRhiTexture::Format::D32F: + return DXGI_FORMAT_R32_FLOAT; + default: + Q_UNREACHABLE(); + return DXGI_FORMAT_R32_FLOAT; + } +} + +static inline DXGI_FORMAT toD3DDepthTextureDSVFormat(QRhiTexture::Format format) +{ + switch (format) { + case QRhiTexture::Format::D16: + return DXGI_FORMAT_D16_UNORM; + case QRhiTexture::Format::D32F: + return DXGI_FORMAT_D32_FLOAT; + default: + Q_UNREACHABLE(); + return DXGI_FORMAT_D32_FLOAT; + } +} + +bool QD3D11Texture::prepareBuild(QSize *adjustedSize) +{ + if (tex) + release(); + + const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize; + const bool isDepth = isDepthTextureFormat(m_format); + const bool isCube = m_flags.testFlag(CubeMap); + const bool hasMipMaps = m_flags.testFlag(MipMapped); + + QRHI_RES_RHI(QRhiD3D11); + dxgiFormat = toD3DTextureFormat(m_format, m_flags); + mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1; + sampleDesc = rhiD->effectiveSampleCount(m_sampleCount); + if (sampleDesc.Count > 1) { + if (isCube) { + qWarning("Cubemap texture cannot be multisample"); + return false; + } + if (hasMipMaps) { + qWarning("Multisample texture cannot have mipmaps"); + return false; + } + } + if (isDepth && hasMipMaps) { + qWarning("Depth texture cannot have mipmaps"); + return false; + } + + if (adjustedSize) + *adjustedSize = size; + + return true; +} + +bool QD3D11Texture::finishBuild() +{ + QRHI_RES_RHI(QRhiD3D11); + const bool isDepth = isDepthTextureFormat(m_format); + const bool isCube = m_flags.testFlag(CubeMap); + + D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; + memset(&srvDesc, 0, sizeof(srvDesc)); + srvDesc.Format = isDepth ? toD3DDepthTextureSRVFormat(m_format) : dxgiFormat; + if (isCube) { + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; + srvDesc.TextureCube.MipLevels = mipLevelCount; + } else { + if (sampleDesc.Count > 1) { + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMS; + } else { + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + srvDesc.Texture2D.MipLevels = mipLevelCount; + } + } + + HRESULT hr = rhiD->dev->CreateShaderResourceView(tex, &srvDesc, &srv); + if (FAILED(hr)) { + qWarning("Failed to create srv: %s", qPrintable(comErrorMessage(hr))); + return false; + } + + nativeHandlesStruct.texture = tex; + + generation += 1; + return true; +} + +bool QD3D11Texture::build() +{ + QSize size; + if (!prepareBuild(&size)) + return false; + + const bool isDepth = isDepthTextureFormat(m_format); + const bool isCube = m_flags.testFlag(CubeMap); + + uint bindFlags = D3D11_BIND_SHADER_RESOURCE; + uint miscFlags = isCube ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0; + if (m_flags.testFlag(RenderTarget)) { + if (isDepth) + bindFlags |= D3D11_BIND_DEPTH_STENCIL; + else + bindFlags |= D3D11_BIND_RENDER_TARGET; + } + if (m_flags.testFlag(UsedWithGenerateMips)) { + if (isDepth) { + qWarning("Depth texture cannot have mipmaps generated"); + return false; + } + bindFlags |= D3D11_BIND_RENDER_TARGET; + miscFlags |= D3D11_RESOURCE_MISC_GENERATE_MIPS; + } + + D3D11_TEXTURE2D_DESC desc; + memset(&desc, 0, sizeof(desc)); + desc.Width = size.width(); + desc.Height = size.height(); + desc.MipLevels = mipLevelCount; + desc.ArraySize = isCube ? 6 : 1; + desc.Format = dxgiFormat; + desc.SampleDesc = sampleDesc; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = bindFlags; + desc.MiscFlags = miscFlags; + + QRHI_RES_RHI(QRhiD3D11); + HRESULT hr = rhiD->dev->CreateTexture2D(&desc, nullptr, &tex); + if (FAILED(hr)) { + qWarning("Failed to create texture: %s", qPrintable(comErrorMessage(hr))); + return false; + } + + if (!finishBuild()) + return false; + + if (!m_objectName.isEmpty()) + tex->SetPrivateData(WKPDID_D3DDebugObjectName, m_objectName.size(), m_objectName.constData()); + + QRHI_PROF; + QRHI_PROF_F(newTexture(this, true, mipLevelCount, isCube ? 6 : 1, sampleDesc.Count)); + + owns = true; + rhiD->registerResource(this); + return true; +} + +bool QD3D11Texture::buildFrom(const QRhiNativeHandles *src) +{ + const QRhiD3D11TextureNativeHandles *h = static_cast(src); + if (!h || !h->texture) + return false; + + if (!prepareBuild()) + return false; + + tex = static_cast(h->texture); + + if (!finishBuild()) + return false; + + QRHI_PROF; + QRHI_PROF_F(newTexture(this, false, mipLevelCount, m_flags.testFlag(CubeMap) ? 6 : 1, sampleDesc.Count)); + + owns = false; + QRHI_RES_RHI(QRhiD3D11); + rhiD->registerResource(this); + return true; +} + +const QRhiNativeHandles *QD3D11Texture::nativeHandles() +{ + return &nativeHandlesStruct; +} + +QD3D11Sampler::QD3D11Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode, + AddressMode u, AddressMode v) + : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v) +{ +} + +QD3D11Sampler::~QD3D11Sampler() +{ + release(); +} + +void QD3D11Sampler::release() +{ + if (!samplerState) + return; + + samplerState->Release(); + samplerState = nullptr; + + QRHI_RES_RHI(QRhiD3D11); + rhiD->unregisterResource(this); +} + +static inline D3D11_FILTER toD3DFilter(QRhiSampler::Filter minFilter, QRhiSampler::Filter magFilter, QRhiSampler::Filter mipFilter) +{ + if (minFilter == QRhiSampler::Nearest) { + if (magFilter == QRhiSampler::Nearest) { + if (mipFilter == QRhiSampler::Linear) + return D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR; + else + return D3D11_FILTER_MIN_MAG_MIP_POINT; + } else { + if (mipFilter == QRhiSampler::Linear) + return D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR; + else + return D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT; + } + } else { + if (magFilter == QRhiSampler::Nearest) { + if (mipFilter == QRhiSampler::Linear) + return D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR; + else + return D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT; + } else { + if (mipFilter == QRhiSampler::Linear) + return D3D11_FILTER_MIN_MAG_MIP_LINEAR; + else + return D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT; + } + } + + Q_UNREACHABLE(); + return D3D11_FILTER_MIN_MAG_MIP_LINEAR; +} + +static inline D3D11_TEXTURE_ADDRESS_MODE toD3DAddressMode(QRhiSampler::AddressMode m) +{ + switch (m) { + case QRhiSampler::Repeat: + return D3D11_TEXTURE_ADDRESS_WRAP; + case QRhiSampler::ClampToEdge: + return D3D11_TEXTURE_ADDRESS_CLAMP; + case QRhiSampler::Border: + return D3D11_TEXTURE_ADDRESS_BORDER; + case QRhiSampler::Mirror: + return D3D11_TEXTURE_ADDRESS_MIRROR; + case QRhiSampler::MirrorOnce: + return D3D11_TEXTURE_ADDRESS_MIRROR_ONCE; + default: + Q_UNREACHABLE(); + return D3D11_TEXTURE_ADDRESS_CLAMP; + } +} + +static inline D3D11_COMPARISON_FUNC toD3DTextureComparisonFunc(QRhiSampler::CompareOp op) +{ + switch (op) { + case QRhiSampler::Never: + return D3D11_COMPARISON_NEVER; + case QRhiSampler::Less: + return D3D11_COMPARISON_LESS; + case QRhiSampler::Equal: + return D3D11_COMPARISON_EQUAL; + case QRhiSampler::LessOrEqual: + return D3D11_COMPARISON_LESS_EQUAL; + case QRhiSampler::Greater: + return D3D11_COMPARISON_GREATER; + case QRhiSampler::NotEqual: + return D3D11_COMPARISON_NOT_EQUAL; + case QRhiSampler::GreaterOrEqual: + return D3D11_COMPARISON_GREATER_EQUAL; + case QRhiSampler::Always: + return D3D11_COMPARISON_ALWAYS; + default: + Q_UNREACHABLE(); + return D3D11_COMPARISON_NEVER; + } +} + +bool QD3D11Sampler::build() +{ + if (samplerState) + release(); + + D3D11_SAMPLER_DESC desc; + memset(&desc, 0, sizeof(desc)); + desc.Filter = toD3DFilter(m_minFilter, m_magFilter, m_mipmapMode); + if (m_compareOp != Never) + desc.Filter = D3D11_FILTER(desc.Filter | 0x80); + desc.AddressU = toD3DAddressMode(m_addressU); + desc.AddressV = toD3DAddressMode(m_addressV); + desc.AddressW = toD3DAddressMode(m_addressW); + desc.MaxAnisotropy = 1.0f; + desc.ComparisonFunc = toD3DTextureComparisonFunc(m_compareOp); + desc.MaxLOD = m_mipmapMode == None ? 0.0f : 1000.0f; + + QRHI_RES_RHI(QRhiD3D11); + HRESULT hr = rhiD->dev->CreateSamplerState(&desc, &samplerState); + if (FAILED(hr)) { + qWarning("Failed to create sampler state: %s", qPrintable(comErrorMessage(hr))); + return false; + } + + generation += 1; + rhiD->registerResource(this); + return true; +} + +// dummy, no Vulkan-style RenderPass+Framebuffer concept here +QD3D11RenderPassDescriptor::QD3D11RenderPassDescriptor(QRhiImplementation *rhi) + : QRhiRenderPassDescriptor(rhi) +{ +} + +QD3D11RenderPassDescriptor::~QD3D11RenderPassDescriptor() +{ + release(); +} + +void QD3D11RenderPassDescriptor::release() +{ + // nothing to do here +} + +QD3D11ReferenceRenderTarget::QD3D11ReferenceRenderTarget(QRhiImplementation *rhi) + : QRhiRenderTarget(rhi), + d(rhi) +{ +} + +QD3D11ReferenceRenderTarget::~QD3D11ReferenceRenderTarget() +{ + release(); +} + +void QD3D11ReferenceRenderTarget::release() +{ + // nothing to do here +} + +QSize QD3D11ReferenceRenderTarget::pixelSize() const +{ + return d.pixelSize; +} + +float QD3D11ReferenceRenderTarget::devicePixelRatio() const +{ + return d.dpr; +} + +int QD3D11ReferenceRenderTarget::sampleCount() const +{ + return d.sampleCount; +} + +QD3D11TextureRenderTarget::QD3D11TextureRenderTarget(QRhiImplementation *rhi, + const QRhiTextureRenderTargetDescription &desc, + Flags flags) + : QRhiTextureRenderTarget(rhi, desc, flags), + d(rhi) +{ + for (int i = 0; i < QD3D11RenderTargetData::MAX_COLOR_ATTACHMENTS; ++i) { + ownsRtv[i] = false; + rtv[i] = nullptr; + } +} + +QD3D11TextureRenderTarget::~QD3D11TextureRenderTarget() +{ + release(); +} + +void QD3D11TextureRenderTarget::release() +{ + QRHI_RES_RHI(QRhiD3D11); + + if (!rtv[0] && !dsv) + return; + + if (dsv) { + if (ownsDsv) + dsv->Release(); + dsv = nullptr; + } + + for (int i = 0; i < QD3D11RenderTargetData::MAX_COLOR_ATTACHMENTS; ++i) { + if (rtv[i]) { + if (ownsRtv[i]) + rtv[i]->Release(); + rtv[i] = nullptr; + } + } + + rhiD->unregisterResource(this); +} + +QRhiRenderPassDescriptor *QD3D11TextureRenderTarget::newCompatibleRenderPassDescriptor() +{ + return new QD3D11RenderPassDescriptor(m_rhi); +} + +bool QD3D11TextureRenderTarget::build() +{ + if (rtv[0] || dsv) + release(); + + const QVector colorAttachments = m_desc.colorAttachments(); + Q_ASSERT(!colorAttachments.isEmpty() || m_desc.depthTexture()); + Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture()); + const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture(); + + QRHI_RES_RHI(QRhiD3D11); + + d.colorAttCount = colorAttachments.count(); + for (int i = 0; i < d.colorAttCount; ++i) { + QRhiTexture *texture = colorAttachments[i].texture(); + QRhiRenderBuffer *rb = colorAttachments[i].renderBuffer(); + Q_ASSERT(texture || rb); + if (texture) { + QD3D11Texture *texD = QRHI_RES(QD3D11Texture, texture); + D3D11_RENDER_TARGET_VIEW_DESC rtvDesc; + memset(&rtvDesc, 0, sizeof(rtvDesc)); + rtvDesc.Format = toD3DTextureFormat(texD->format(), texD->flags()); + if (texD->flags().testFlag(QRhiTexture::CubeMap)) { + rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY; + rtvDesc.Texture2DArray.MipSlice = colorAttachments[i].level(); + rtvDesc.Texture2DArray.FirstArraySlice = colorAttachments[i].layer(); + rtvDesc.Texture2DArray.ArraySize = 1; + } else { + if (texD->sampleDesc.Count > 1) { + rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS; + } else { + rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + rtvDesc.Texture2D.MipSlice = colorAttachments[i].level(); + } + } + HRESULT hr = rhiD->dev->CreateRenderTargetView(texD->tex, &rtvDesc, &rtv[i]); + if (FAILED(hr)) { + qWarning("Failed to create rtv: %s", qPrintable(comErrorMessage(hr))); + return false; + } + ownsRtv[i] = true; + if (i == 0) { + d.pixelSize = texD->pixelSize(); + d.sampleCount = texD->sampleDesc.Count; + } + } else if (rb) { + QD3D11RenderBuffer *rbD = QRHI_RES(QD3D11RenderBuffer, rb); + ownsRtv[i] = false; + rtv[i] = rbD->rtv; + if (i == 0) { + d.pixelSize = rbD->pixelSize(); + d.sampleCount = rbD->sampleDesc.Count; + } + } + } + d.dpr = 1; + + if (hasDepthStencil) { + if (m_desc.depthTexture()) { + ownsDsv = true; + QD3D11Texture *depthTexD = QRHI_RES(QD3D11Texture, m_desc.depthTexture()); + D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc; + memset(&dsvDesc, 0, sizeof(dsvDesc)); + dsvDesc.Format = toD3DDepthTextureDSVFormat(depthTexD->format()); + dsvDesc.ViewDimension = depthTexD->sampleDesc.Count > 1 ? D3D11_DSV_DIMENSION_TEXTURE2DMS + : D3D11_DSV_DIMENSION_TEXTURE2D; + HRESULT hr = rhiD->dev->CreateDepthStencilView(depthTexD->tex, &dsvDesc, &dsv); + if (FAILED(hr)) { + qWarning("Failed to create dsv: %s", qPrintable(comErrorMessage(hr))); + return false; + } + if (d.colorAttCount == 0) { + d.pixelSize = depthTexD->pixelSize(); + d.sampleCount = depthTexD->sampleDesc.Count; + } + } else { + ownsDsv = false; + QD3D11RenderBuffer *depthRbD = QRHI_RES(QD3D11RenderBuffer, m_desc.depthStencilBuffer()); + dsv = depthRbD->dsv; + if (d.colorAttCount == 0) { + d.pixelSize = m_desc.depthStencilBuffer()->pixelSize(); + d.sampleCount = depthRbD->sampleDesc.Count; + } + } + d.dsAttCount = 1; + } else { + d.dsAttCount = 0; + } + + for (int i = 0; i < QD3D11RenderTargetData::MAX_COLOR_ATTACHMENTS; ++i) + d.rtv[i] = i < d.colorAttCount ? rtv[i] : nullptr; + + d.dsv = dsv; + d.rp = QRHI_RES(QD3D11RenderPassDescriptor, m_renderPassDesc); + + rhiD->registerResource(this); + return true; +} + +QSize QD3D11TextureRenderTarget::pixelSize() const +{ + return d.pixelSize; +} + +float QD3D11TextureRenderTarget::devicePixelRatio() const +{ + return d.dpr; +} + +int QD3D11TextureRenderTarget::sampleCount() const +{ + return d.sampleCount; +} + +QD3D11ShaderResourceBindings::QD3D11ShaderResourceBindings(QRhiImplementation *rhi) + : QRhiShaderResourceBindings(rhi) +{ +} + +QD3D11ShaderResourceBindings::~QD3D11ShaderResourceBindings() +{ + release(); +} + +void QD3D11ShaderResourceBindings::release() +{ + sortedBindings.clear(); +} + +bool QD3D11ShaderResourceBindings::build() +{ + if (!sortedBindings.isEmpty()) + release(); + + sortedBindings = m_bindings; + std::sort(sortedBindings.begin(), sortedBindings.end(), + [](const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b) + { + return QRhiShaderResourceBindingPrivate::get(&a)->binding < QRhiShaderResourceBindingPrivate::get(&b)->binding; + }); + + boundResourceData.resize(sortedBindings.count()); + + QRHI_RES_RHI(QRhiD3D11); + rhiD->updateShaderResourceBindings(this); + + generation += 1; + return true; +} + +QD3D11GraphicsPipeline::QD3D11GraphicsPipeline(QRhiImplementation *rhi) + : QRhiGraphicsPipeline(rhi) +{ +} + +QD3D11GraphicsPipeline::~QD3D11GraphicsPipeline() +{ + release(); +} + +void QD3D11GraphicsPipeline::release() +{ + QRHI_RES_RHI(QRhiD3D11); + + if (!dsState) + return; + + dsState->Release(); + dsState = nullptr; + + if (blendState) { + blendState->Release(); + blendState = nullptr; + } + + if (inputLayout) { + inputLayout->Release(); + inputLayout = nullptr; + } + + if (rastState) { + rastState->Release(); + rastState = nullptr; + } + + if (vs) { + vs->Release(); + vs = nullptr; + } + + if (fs) { + fs->Release(); + fs = nullptr; + } + + rhiD->unregisterResource(this); +} + +static inline D3D11_CULL_MODE toD3DCullMode(QRhiGraphicsPipeline::CullMode c) +{ + switch (c) { + case QRhiGraphicsPipeline::None: + return D3D11_CULL_NONE; + case QRhiGraphicsPipeline::Front: + return D3D11_CULL_FRONT; + case QRhiGraphicsPipeline::Back: + return D3D11_CULL_BACK; + default: + Q_UNREACHABLE(); + return D3D11_CULL_NONE; + } +} + +static inline D3D11_COMPARISON_FUNC toD3DCompareOp(QRhiGraphicsPipeline::CompareOp op) +{ + switch (op) { + case QRhiGraphicsPipeline::Never: + return D3D11_COMPARISON_NEVER; + case QRhiGraphicsPipeline::Less: + return D3D11_COMPARISON_LESS; + case QRhiGraphicsPipeline::Equal: + return D3D11_COMPARISON_EQUAL; + case QRhiGraphicsPipeline::LessOrEqual: + return D3D11_COMPARISON_LESS_EQUAL; + case QRhiGraphicsPipeline::Greater: + return D3D11_COMPARISON_GREATER; + case QRhiGraphicsPipeline::NotEqual: + return D3D11_COMPARISON_NOT_EQUAL; + case QRhiGraphicsPipeline::GreaterOrEqual: + return D3D11_COMPARISON_GREATER_EQUAL; + case QRhiGraphicsPipeline::Always: + return D3D11_COMPARISON_ALWAYS; + default: + Q_UNREACHABLE(); + return D3D11_COMPARISON_ALWAYS; + } +} + +static inline D3D11_STENCIL_OP toD3DStencilOp(QRhiGraphicsPipeline::StencilOp op) +{ + switch (op) { + case QRhiGraphicsPipeline::StencilZero: + return D3D11_STENCIL_OP_ZERO; + case QRhiGraphicsPipeline::Keep: + return D3D11_STENCIL_OP_KEEP; + case QRhiGraphicsPipeline::Replace: + return D3D11_STENCIL_OP_REPLACE; + case QRhiGraphicsPipeline::IncrementAndClamp: + return D3D11_STENCIL_OP_INCR_SAT; + case QRhiGraphicsPipeline::DecrementAndClamp: + return D3D11_STENCIL_OP_DECR_SAT; + case QRhiGraphicsPipeline::Invert: + return D3D11_STENCIL_OP_INVERT; + case QRhiGraphicsPipeline::IncrementAndWrap: + return D3D11_STENCIL_OP_INCR; + case QRhiGraphicsPipeline::DecrementAndWrap: + return D3D11_STENCIL_OP_DECR; + default: + Q_UNREACHABLE(); + return D3D11_STENCIL_OP_KEEP; + } +} + +static inline DXGI_FORMAT toD3DAttributeFormat(QRhiVertexInputAttribute::Format format) +{ + switch (format) { + case QRhiVertexInputAttribute::Float4: + return DXGI_FORMAT_R32G32B32A32_FLOAT; + case QRhiVertexInputAttribute::Float3: + return DXGI_FORMAT_R32G32B32_FLOAT; + case QRhiVertexInputAttribute::Float2: + return DXGI_FORMAT_R32G32_FLOAT; + case QRhiVertexInputAttribute::Float: + return DXGI_FORMAT_R32_FLOAT; + case QRhiVertexInputAttribute::UNormByte4: + return DXGI_FORMAT_R8G8B8A8_UNORM; + case QRhiVertexInputAttribute::UNormByte2: + return DXGI_FORMAT_R8G8_UNORM; + case QRhiVertexInputAttribute::UNormByte: + return DXGI_FORMAT_R8_UNORM; + default: + Q_UNREACHABLE(); + return DXGI_FORMAT_R32G32B32A32_FLOAT; + } +} + +static inline D3D11_PRIMITIVE_TOPOLOGY toD3DTopology(QRhiGraphicsPipeline::Topology t) +{ + switch (t) { + case QRhiGraphicsPipeline::Triangles: + return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST; + case QRhiGraphicsPipeline::TriangleStrip: + return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP; + case QRhiGraphicsPipeline::Lines: + return D3D11_PRIMITIVE_TOPOLOGY_LINELIST; + case QRhiGraphicsPipeline::LineStrip: + return D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP; + case QRhiGraphicsPipeline::Points: + return D3D11_PRIMITIVE_TOPOLOGY_POINTLIST; + default: + Q_UNREACHABLE(); + return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST; + } +} + +static inline uint toD3DColorWriteMask(QRhiGraphicsPipeline::ColorMask c) +{ + uint f = 0; + if (c.testFlag(QRhiGraphicsPipeline::R)) + f |= D3D11_COLOR_WRITE_ENABLE_RED; + if (c.testFlag(QRhiGraphicsPipeline::G)) + f |= D3D11_COLOR_WRITE_ENABLE_GREEN; + if (c.testFlag(QRhiGraphicsPipeline::B)) + f |= D3D11_COLOR_WRITE_ENABLE_BLUE; + if (c.testFlag(QRhiGraphicsPipeline::A)) + f |= D3D11_COLOR_WRITE_ENABLE_ALPHA; + return f; +} + +static inline D3D11_BLEND toD3DBlendFactor(QRhiGraphicsPipeline::BlendFactor f) +{ + switch (f) { + case QRhiGraphicsPipeline::Zero: + return D3D11_BLEND_ZERO; + case QRhiGraphicsPipeline::One: + return D3D11_BLEND_ONE; + case QRhiGraphicsPipeline::SrcColor: + return D3D11_BLEND_SRC_COLOR; + case QRhiGraphicsPipeline::OneMinusSrcColor: + return D3D11_BLEND_INV_SRC_COLOR; + case QRhiGraphicsPipeline::DstColor: + return D3D11_BLEND_DEST_COLOR; + case QRhiGraphicsPipeline::OneMinusDstColor: + return D3D11_BLEND_INV_DEST_COLOR; + case QRhiGraphicsPipeline::SrcAlpha: + return D3D11_BLEND_SRC_ALPHA; + case QRhiGraphicsPipeline::OneMinusSrcAlpha: + return D3D11_BLEND_INV_SRC_ALPHA; + case QRhiGraphicsPipeline::DstAlpha: + return D3D11_BLEND_DEST_ALPHA; + case QRhiGraphicsPipeline::OneMinusDstAlpha: + return D3D11_BLEND_INV_DEST_ALPHA; + case QRhiGraphicsPipeline::ConstantColor: + Q_FALLTHROUGH(); + case QRhiGraphicsPipeline::ConstantAlpha: + return D3D11_BLEND_BLEND_FACTOR; + case QRhiGraphicsPipeline::OneMinusConstantColor: + Q_FALLTHROUGH(); + case QRhiGraphicsPipeline::OneMinusConstantAlpha: + return D3D11_BLEND_INV_BLEND_FACTOR; + case QRhiGraphicsPipeline::SrcAlphaSaturate: + return D3D11_BLEND_SRC_ALPHA_SAT; + case QRhiGraphicsPipeline::Src1Color: + return D3D11_BLEND_SRC1_COLOR; + case QRhiGraphicsPipeline::OneMinusSrc1Color: + return D3D11_BLEND_INV_SRC1_COLOR; + case QRhiGraphicsPipeline::Src1Alpha: + return D3D11_BLEND_SRC1_ALPHA; + case QRhiGraphicsPipeline::OneMinusSrc1Alpha: + return D3D11_BLEND_INV_SRC1_ALPHA; + default: + Q_UNREACHABLE(); + return D3D11_BLEND_ZERO; + } +} + +static inline D3D11_BLEND_OP toD3DBlendOp(QRhiGraphicsPipeline::BlendOp op) +{ + switch (op) { + case QRhiGraphicsPipeline::Add: + return D3D11_BLEND_OP_ADD; + case QRhiGraphicsPipeline::Subtract: + return D3D11_BLEND_OP_SUBTRACT; + case QRhiGraphicsPipeline::ReverseSubtract: + return D3D11_BLEND_OP_REV_SUBTRACT; + case QRhiGraphicsPipeline::Min: + return D3D11_BLEND_OP_MIN; + case QRhiGraphicsPipeline::Max: + return D3D11_BLEND_OP_MAX; + default: + Q_UNREACHABLE(); + return D3D11_BLEND_OP_ADD; + } +} + +static QByteArray compileHlslShaderSource(const QShader &shader, QShader::Variant shaderVariant, QString *error) +{ + QShaderCode dxbc = shader.shader({ QShader::DxbcShader, 50, shaderVariant }); + if (!dxbc.shader().isEmpty()) + return dxbc.shader(); + + QShaderCode hlslSource = shader.shader({ QShader::HlslShader, 50, shaderVariant }); + if (hlslSource.shader().isEmpty()) { + qWarning() << "No HLSL (shader model 5.0) code found in baked shader" << shader; + return QByteArray(); + } + + const char *target; + switch (shader.stage()) { + case QShader::VertexStage: + target = "vs_5_0"; + break; + case QShader::TessellationControlStage: + target = "hs_5_0"; + break; + case QShader::TessellationEvaluationStage: + target = "ds_5_0"; + break; + case QShader::GeometryStage: + target = "gs_5_0"; + break; + case QShader::FragmentStage: + target = "ps_5_0"; + break; + case QShader::ComputeStage: + target = "cs_5_0"; + break; + default: + Q_UNREACHABLE(); + return QByteArray(); + } + + ID3DBlob *bytecode = nullptr; + ID3DBlob *errors = nullptr; + HRESULT hr = D3DCompile(hlslSource.shader().constData(), hlslSource.shader().size(), + nullptr, nullptr, nullptr, + hlslSource.entryPoint().constData(), target, 0, 0, &bytecode, &errors); + if (FAILED(hr) || !bytecode) { + qWarning("HLSL shader compilation failed: 0x%x", uint(hr)); + if (errors) { + *error = QString::fromUtf8(static_cast(errors->GetBufferPointer()), + errors->GetBufferSize()); + errors->Release(); + } + return QByteArray(); + } + + QByteArray result; + result.resize(bytecode->GetBufferSize()); + memcpy(result.data(), bytecode->GetBufferPointer(), result.size()); + bytecode->Release(); + return result; +} + +bool QD3D11GraphicsPipeline::build() +{ + if (dsState) + release(); + + QRHI_RES_RHI(QRhiD3D11); + + D3D11_RASTERIZER_DESC rastDesc; + memset(&rastDesc, 0, sizeof(rastDesc)); + rastDesc.FillMode = D3D11_FILL_SOLID; + rastDesc.CullMode = toD3DCullMode(m_cullMode); + rastDesc.FrontCounterClockwise = m_frontFace == CCW; + rastDesc.ScissorEnable = m_flags.testFlag(UsesScissor); + rastDesc.MultisampleEnable = rhiD->effectiveSampleCount(m_sampleCount).Count > 1; + HRESULT hr = rhiD->dev->CreateRasterizerState(&rastDesc, &rastState); + if (FAILED(hr)) { + qWarning("Failed to create rasterizer state: %s", qPrintable(comErrorMessage(hr))); + return false; + } + + D3D11_DEPTH_STENCIL_DESC dsDesc; + memset(&dsDesc, 0, sizeof(dsDesc)); + dsDesc.DepthEnable = m_depthTest; + dsDesc.DepthWriteMask = m_depthWrite ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; + dsDesc.DepthFunc = toD3DCompareOp(m_depthOp); + dsDesc.StencilEnable = m_stencilTest; + if (m_stencilTest) { + dsDesc.StencilReadMask = m_stencilReadMask; + dsDesc.StencilWriteMask = m_stencilWriteMask; + dsDesc.FrontFace.StencilFailOp = toD3DStencilOp(m_stencilFront.failOp); + dsDesc.FrontFace.StencilDepthFailOp = toD3DStencilOp(m_stencilFront.depthFailOp); + dsDesc.FrontFace.StencilPassOp = toD3DStencilOp(m_stencilFront.passOp); + dsDesc.FrontFace.StencilFunc = toD3DCompareOp(m_stencilFront.compareOp); + dsDesc.BackFace.StencilFailOp = toD3DStencilOp(m_stencilBack.failOp); + dsDesc.BackFace.StencilDepthFailOp = toD3DStencilOp(m_stencilBack.depthFailOp); + dsDesc.BackFace.StencilPassOp = toD3DStencilOp(m_stencilBack.passOp); + dsDesc.BackFace.StencilFunc = toD3DCompareOp(m_stencilBack.compareOp); + } + hr = rhiD->dev->CreateDepthStencilState(&dsDesc, &dsState); + if (FAILED(hr)) { + qWarning("Failed to create depth-stencil state: %s", qPrintable(comErrorMessage(hr))); + return false; + } + + D3D11_BLEND_DESC blendDesc; + memset(&blendDesc, 0, sizeof(blendDesc)); + blendDesc.IndependentBlendEnable = m_targetBlends.count() > 1; + for (int i = 0, ie = m_targetBlends.count(); i != ie; ++i) { + const QRhiGraphicsPipeline::TargetBlend &b(m_targetBlends[i]); + D3D11_RENDER_TARGET_BLEND_DESC blend; + memset(&blend, 0, sizeof(blend)); + blend.BlendEnable = b.enable; + blend.SrcBlend = toD3DBlendFactor(b.srcColor); + blend.DestBlend = toD3DBlendFactor(b.dstColor); + blend.BlendOp = toD3DBlendOp(b.opColor); + blend.SrcBlendAlpha = toD3DBlendFactor(b.srcAlpha); + blend.DestBlendAlpha = toD3DBlendFactor(b.dstAlpha); + blend.BlendOpAlpha = toD3DBlendOp(b.opAlpha); + blend.RenderTargetWriteMask = toD3DColorWriteMask(b.colorWrite); + blendDesc.RenderTarget[i] = blend; + } + if (m_targetBlends.isEmpty()) { + D3D11_RENDER_TARGET_BLEND_DESC blend; + memset(&blend, 0, sizeof(blend)); + blend.RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; + blendDesc.RenderTarget[0] = blend; + } + hr = rhiD->dev->CreateBlendState(&blendDesc, &blendState); + if (FAILED(hr)) { + qWarning("Failed to create blend state: %s", qPrintable(comErrorMessage(hr))); + return false; + } + + QByteArray vsByteCode; + for (const QRhiGraphicsShaderStage &shaderStage : qAsConst(m_shaderStages)) { + QString error; + QByteArray bytecode = compileHlslShaderSource(shaderStage.shader(), shaderStage.shaderVariant(), &error); + if (bytecode.isEmpty()) { + qWarning("HLSL shader compilation failed: %s", qPrintable(error)); + return false; + } + switch (shaderStage.type()) { + case QRhiGraphicsShaderStage::Vertex: + hr = rhiD->dev->CreateVertexShader(bytecode.constData(), bytecode.size(), nullptr, &vs); + if (FAILED(hr)) { + qWarning("Failed to create vertex shader: %s", qPrintable(comErrorMessage(hr))); + return false; + } + vsByteCode = bytecode; + break; + case QRhiGraphicsShaderStage::Fragment: + hr = rhiD->dev->CreatePixelShader(bytecode.constData(), bytecode.size(), nullptr, &fs); + if (FAILED(hr)) { + qWarning("Failed to create pixel shader: %s", qPrintable(comErrorMessage(hr))); + return false; + } + break; + default: + break; + } + } + + d3dTopology = toD3DTopology(m_topology); + + if (!vsByteCode.isEmpty()) { + const QVector bindings = m_vertexInputLayout.bindings(); + const QVector attributes = m_vertexInputLayout.attributes(); + QVarLengthArray inputDescs; + for (const QRhiVertexInputAttribute &attribute : attributes) { + D3D11_INPUT_ELEMENT_DESC desc; + memset(&desc, 0, sizeof(desc)); + // the output from SPIRV-Cross uses TEXCOORD as the semantic + desc.SemanticName = "TEXCOORD"; + desc.SemanticIndex = attribute.location(); + desc.Format = toD3DAttributeFormat(attribute.format()); + desc.InputSlot = attribute.binding(); + desc.AlignedByteOffset = attribute.offset(); + const QRhiVertexInputBinding &binding(bindings[attribute.binding()]); + if (binding.classification() == QRhiVertexInputBinding::PerInstance) { + desc.InputSlotClass = D3D11_INPUT_PER_INSTANCE_DATA; + desc.InstanceDataStepRate = binding.instanceStepRate(); + } else { + desc.InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + } + inputDescs.append(desc); + } + hr = rhiD->dev->CreateInputLayout(inputDescs.constData(), inputDescs.count(), vsByteCode, vsByteCode.size(), &inputLayout); + if (FAILED(hr)) { + qWarning("Failed to create input layout: %s", qPrintable(comErrorMessage(hr))); + return false; + } + } + + generation += 1; + rhiD->registerResource(this); + return true; +} + +QD3D11CommandBuffer::QD3D11CommandBuffer(QRhiImplementation *rhi) + : QRhiCommandBuffer(rhi) +{ + resetState(); +} + +QD3D11CommandBuffer::~QD3D11CommandBuffer() +{ + release(); +} + +void QD3D11CommandBuffer::release() +{ + // nothing to do here +} + +QD3D11SwapChain::QD3D11SwapChain(QRhiImplementation *rhi) + : QRhiSwapChain(rhi), + rt(rhi), + cb(rhi) +{ + for (int i = 0; i < BUFFER_COUNT; ++i) { + tex[i] = nullptr; + rtv[i] = nullptr; + msaaTex[i] = nullptr; + msaaRtv[i] = nullptr; + timestampActive[i] = false; + timestampDisjointQuery[i] = nullptr; + timestampQuery[2 * i] = nullptr; + timestampQuery[2 * i + 1] = nullptr; + } +} + +QD3D11SwapChain::~QD3D11SwapChain() +{ + release(); +} + +void QD3D11SwapChain::releaseBuffers() +{ + for (int i = 0; i < BUFFER_COUNT; ++i) { + if (rtv[i]) { + rtv[i]->Release(); + rtv[i] = nullptr; + } + if (tex[i]) { + tex[i]->Release(); + tex[i] = nullptr; + } + if (msaaRtv[i]) { + msaaRtv[i]->Release(); + msaaRtv[i] = nullptr; + } + if (msaaTex[i]) { + msaaTex[i]->Release(); + msaaTex[i] = nullptr; + } + } +} + +void QD3D11SwapChain::release() +{ + if (!swapChain) + return; + + releaseBuffers(); + + for (int i = 0; i < BUFFER_COUNT; ++i) { + if (timestampDisjointQuery[i]) { + timestampDisjointQuery[i]->Release(); + timestampDisjointQuery[i] = nullptr; + } + for (int j = 0; j < 2; ++j) { + const int idx = BUFFER_COUNT * i + j; + if (timestampQuery[idx]) { + timestampQuery[idx]->Release(); + timestampQuery[idx] = nullptr; + } + } + } + + swapChain->Release(); + swapChain = nullptr; + + QRHI_PROF; + QRHI_PROF_F(releaseSwapChain(this)); + + QRHI_RES_RHI(QRhiD3D11); + rhiD->unregisterResource(this); +} + +QRhiCommandBuffer *QD3D11SwapChain::currentFrameCommandBuffer() +{ + return &cb; +} + +QRhiRenderTarget *QD3D11SwapChain::currentFrameRenderTarget() +{ + return &rt; +} + +QSize QD3D11SwapChain::surfacePixelSize() +{ + Q_ASSERT(m_window); + return m_window->size() * m_window->devicePixelRatio(); +} + +QRhiRenderPassDescriptor *QD3D11SwapChain::newCompatibleRenderPassDescriptor() +{ + return new QD3D11RenderPassDescriptor(m_rhi); +} + +bool QD3D11SwapChain::newColorBuffer(const QSize &size, DXGI_FORMAT format, DXGI_SAMPLE_DESC sampleDesc, + ID3D11Texture2D **tex, ID3D11RenderTargetView **rtv) const +{ + D3D11_TEXTURE2D_DESC desc; + memset(&desc, 0, sizeof(desc)); + desc.Width = size.width(); + desc.Height = size.height(); + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.Format = format; + desc.SampleDesc = sampleDesc; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_RENDER_TARGET; + + QRHI_RES_RHI(QRhiD3D11); + HRESULT hr = rhiD->dev->CreateTexture2D(&desc, nullptr, tex); + if (FAILED(hr)) { + qWarning("Failed to create color buffer texture: %s", qPrintable(comErrorMessage(hr))); + return false; + } + + D3D11_RENDER_TARGET_VIEW_DESC rtvDesc; + memset(&rtvDesc, 0, sizeof(rtvDesc)); + rtvDesc.Format = format; + rtvDesc.ViewDimension = sampleDesc.Count > 1 ? D3D11_RTV_DIMENSION_TEXTURE2DMS : D3D11_RTV_DIMENSION_TEXTURE2D; + hr = rhiD->dev->CreateRenderTargetView(*tex, &rtvDesc, rtv); + if (FAILED(hr)) { + qWarning("Failed to create color buffer rtv: %s", qPrintable(comErrorMessage(hr))); + (*tex)->Release(); + *tex = nullptr; + return false; + } + + return true; +} + +bool QD3D11SwapChain::buildOrResize() +{ + // Can be called multiple times due to window resizes - that is not the + // same as a simple release+build (as with other resources). Just need to + // resize the buffers then. + + const bool needsRegistration = !window || window != m_window; + + // except if the window actually changes + if (window && window != m_window) + release(); + + window = m_window; + m_currentPixelSize = surfacePixelSize(); + pixelSize = m_currentPixelSize; + + if (pixelSize.isEmpty()) + return false; + + colorFormat = DXGI_FORMAT_R8G8B8A8_UNORM; + const DXGI_FORMAT srgbAdjustedFormat = m_flags.testFlag(sRGB) ? + DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R8G8B8A8_UNORM; + + const UINT swapChainFlags = 0; + + QRHI_RES_RHI(QRhiD3D11); + if (!swapChain) { + HWND hwnd = reinterpret_cast(window->winId()); + sampleDesc = rhiD->effectiveSampleCount(m_sampleCount); + + // We use FLIP_DISCARD which implies a buffer count of 2 (as opposed to the + // old DISCARD with back buffer count == 1). This makes no difference for + // the rest of the stuff except that automatic MSAA is unsupported and + // needs to be implemented via a custom multisample render target and an + // explicit resolve. + + HRESULT hr; + if (rhiD->hasDxgi2) { + DXGI_SWAP_CHAIN_DESC1 desc; + memset(&desc, 0, sizeof(desc)); + desc.Width = pixelSize.width(); + desc.Height = pixelSize.height(); + desc.Format = colorFormat; + desc.SampleDesc.Count = 1; + desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + desc.BufferCount = BUFFER_COUNT; + desc.Scaling = DXGI_SCALING_STRETCH; + desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + if (m_flags.testFlag(SurfaceHasPreMulAlpha)) + desc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED; + else if (m_flags.testFlag(SurfaceHasNonPreMulAlpha)) + desc.AlphaMode = DXGI_ALPHA_MODE_STRAIGHT; + desc.Flags = swapChainFlags; + + IDXGISwapChain1 *sc1; + hr = static_cast(rhiD->dxgiFactory)->CreateSwapChainForHwnd(rhiD->dev, hwnd, &desc, + nullptr, nullptr, &sc1); + if (SUCCEEDED(hr)) + swapChain = sc1; + } else { + // Windows 7 + DXGI_SWAP_CHAIN_DESC desc; + memset(&desc, 0, sizeof(desc)); + desc.BufferDesc.Width = pixelSize.width(); + desc.BufferDesc.Height = pixelSize.height(); + desc.BufferDesc.RefreshRate.Numerator = 60; + desc.BufferDesc.RefreshRate.Denominator = 1; + desc.BufferDesc.Format = colorFormat; + desc.SampleDesc.Count = 1; + desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + desc.BufferCount = BUFFER_COUNT; + desc.OutputWindow = hwnd; + desc.Windowed = true; + desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + desc.Flags = swapChainFlags; + + hr = rhiD->dxgiFactory->CreateSwapChain(rhiD->dev, &desc, &swapChain); + } + if (FAILED(hr)) { + qWarning("Failed to create D3D11 swapchain: %s", qPrintable(comErrorMessage(hr))); + return false; + } + } else { + releaseBuffers(); + HRESULT hr = swapChain->ResizeBuffers(2, pixelSize.width(), pixelSize.height(), colorFormat, swapChainFlags); + if (FAILED(hr)) { + qWarning("Failed to resize D3D11 swapchain: %s", qPrintable(comErrorMessage(hr))); + return false; + } + } + + for (int i = 0; i < BUFFER_COUNT; ++i) { + HRESULT hr = swapChain->GetBuffer(0, IID_ID3D11Texture2D, reinterpret_cast(&tex[i])); + if (FAILED(hr)) { + qWarning("Failed to query swapchain buffer %d: %s", i, qPrintable(comErrorMessage(hr))); + return false; + } + D3D11_RENDER_TARGET_VIEW_DESC rtvDesc; + memset(&rtvDesc, 0, sizeof(rtvDesc)); + rtvDesc.Format = srgbAdjustedFormat; + rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + hr = rhiD->dev->CreateRenderTargetView(tex[i], &rtvDesc, &rtv[i]); + if (FAILED(hr)) { + qWarning("Failed to create rtv for swapchain buffer %d: %s", i, qPrintable(comErrorMessage(hr))); + return false; + } + if (sampleDesc.Count > 1) { + if (!newColorBuffer(pixelSize, srgbAdjustedFormat, sampleDesc, &msaaTex[i], &msaaRtv[i])) + return false; + } + } + + if (m_depthStencil && m_depthStencil->sampleCount() != m_sampleCount) { + qWarning("Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.", + m_depthStencil->sampleCount(), m_sampleCount); + } + if (m_depthStencil && m_depthStencil->pixelSize() != pixelSize) { + qWarning("Depth-stencil buffer's size (%dx%d) does not match the surface size (%dx%d). Expect problems.", + m_depthStencil->pixelSize().width(), m_depthStencil->pixelSize().height(), + pixelSize.width(), pixelSize.height()); + } + + currentFrameSlot = 0; + frameCount = 0; + ds = m_depthStencil ? QRHI_RES(QD3D11RenderBuffer, m_depthStencil) : nullptr; + swapInterval = m_flags.testFlag(QRhiSwapChain::NoVSync) ? 0 : 1; + + QD3D11ReferenceRenderTarget *rtD = QRHI_RES(QD3D11ReferenceRenderTarget, &rt); + rtD->d.rp = QRHI_RES(QD3D11RenderPassDescriptor, m_renderPassDesc); + rtD->d.pixelSize = pixelSize; + rtD->d.dpr = window->devicePixelRatio(); + rtD->d.sampleCount = sampleDesc.Count; + rtD->d.colorAttCount = 1; + rtD->d.dsAttCount = m_depthStencil ? 1 : 0; + + QRHI_PROF; + QRHI_PROF_F(resizeSwapChain(this, BUFFER_COUNT, sampleDesc.Count > 1 ? BUFFER_COUNT : 0, sampleDesc.Count)); + if (rhiP) { + D3D11_QUERY_DESC queryDesc; + memset(&queryDesc, 0, sizeof(queryDesc)); + for (int i = 0; i < BUFFER_COUNT; ++i) { + if (!timestampDisjointQuery[i]) { + queryDesc.Query = D3D11_QUERY_TIMESTAMP_DISJOINT; + HRESULT hr = rhiD->dev->CreateQuery(&queryDesc, ×tampDisjointQuery[i]); + if (FAILED(hr)) { + qWarning("Failed to create timestamp disjoint query: %s", qPrintable(comErrorMessage(hr))); + break; + } + } + queryDesc.Query = D3D11_QUERY_TIMESTAMP; + for (int j = 0; j < 2; ++j) { + const int idx = BUFFER_COUNT * i + j; // one pair per buffer (frame) + if (!timestampQuery[idx]) { + HRESULT hr = rhiD->dev->CreateQuery(&queryDesc, ×tampQuery[idx]); + if (FAILED(hr)) { + qWarning("Failed to create timestamp query: %s", qPrintable(comErrorMessage(hr))); + break; + } + } + } + } + // timestamp queries are optional so we can go on even if they failed + } + + if (needsRegistration) + rhiD->registerResource(this); + + return true; +} + +QT_END_NAMESPACE diff --git a/src/gui/rhi/qrhid3d11_p.h b/src/gui/rhi/qrhid3d11_p.h new file mode 100644 index 0000000000..3e2e492d9c --- /dev/null +++ b/src/gui/rhi/qrhid3d11_p.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Gui module +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QRHID3D11_H +#define QRHID3D11_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +// no d3d includes here, to prevent precompiled header mess (due to this being +// a public header) + +QT_BEGIN_NAMESPACE + +struct Q_GUI_EXPORT QRhiD3D11InitParams : public QRhiInitParams +{ + bool enableDebugLayer = false; +}; + +struct Q_GUI_EXPORT QRhiD3D11NativeHandles : public QRhiNativeHandles +{ + void *dev = nullptr; + void *context = nullptr; +}; + +struct Q_GUI_EXPORT QRhiD3D11TextureNativeHandles : public QRhiNativeHandles +{ + void *texture = nullptr; // ID3D11Texture2D* +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/rhi/qrhid3d11_p_p.h b/src/gui/rhi/qrhid3d11_p_p.h new file mode 100644 index 0000000000..fd5247b0b4 --- /dev/null +++ b/src/gui/rhi/qrhid3d11_p_p.h @@ -0,0 +1,624 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Gui module +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QRHID3D11_P_H +#define QRHID3D11_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qrhid3d11_p.h" +#include "qrhi_p_p.h" +#include "qshaderdescription_p.h" +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +struct QD3D11Buffer : public QRhiBuffer +{ + QD3D11Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size); + ~QD3D11Buffer(); + void release() override; + bool build() override; + + ID3D11Buffer *buffer = nullptr; + QByteArray dynBuf; + bool hasPendingDynamicUpdates = false; + uint generation = 0; + friend class QRhiD3D11; +}; + +struct QD3D11RenderBuffer : public QRhiRenderBuffer +{ + QD3D11RenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize, + int sampleCount, QRhiRenderBuffer::Flags flags); + ~QD3D11RenderBuffer(); + void release() override; + bool build() override; + QRhiTexture::Format backingFormat() const override; + + ID3D11Texture2D *tex = nullptr; + ID3D11DepthStencilView *dsv = nullptr; + ID3D11RenderTargetView *rtv = nullptr; + DXGI_FORMAT dxgiFormat; + DXGI_SAMPLE_DESC sampleDesc; + friend class QRhiD3D11; +}; + +struct QD3D11Texture : public QRhiTexture +{ + QD3D11Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, + int sampleCount, Flags flags); + ~QD3D11Texture(); + void release() override; + bool build() override; + bool buildFrom(const QRhiNativeHandles *src) override; + const QRhiNativeHandles *nativeHandles() override; + + bool prepareBuild(QSize *adjustedSize = nullptr); + bool finishBuild(); + + ID3D11Texture2D *tex = nullptr; + bool owns = true; + ID3D11ShaderResourceView *srv = nullptr; + DXGI_FORMAT dxgiFormat; + uint mipLevelCount = 0; + DXGI_SAMPLE_DESC sampleDesc; + QRhiD3D11TextureNativeHandles nativeHandlesStruct; + uint generation = 0; + friend class QRhiD3D11; +}; + +struct QD3D11Sampler : public QRhiSampler +{ + QD3D11Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode, + AddressMode u, AddressMode v); + ~QD3D11Sampler(); + void release() override; + bool build() override; + + ID3D11SamplerState *samplerState = nullptr; + uint generation = 0; + friend class QRhiD3D11; +}; + +struct QD3D11RenderPassDescriptor : public QRhiRenderPassDescriptor +{ + QD3D11RenderPassDescriptor(QRhiImplementation *rhi); + ~QD3D11RenderPassDescriptor(); + void release() override; +}; + +struct QD3D11RenderTargetData +{ + QD3D11RenderTargetData(QRhiImplementation *) + { + for (int i = 0; i < MAX_COLOR_ATTACHMENTS; ++i) + rtv[i] = nullptr; + } + + QD3D11RenderPassDescriptor *rp = nullptr; + QSize pixelSize; + float dpr = 1; + int sampleCount = 1; + int colorAttCount = 0; + int dsAttCount = 0; + + static const int MAX_COLOR_ATTACHMENTS = 8; + ID3D11RenderTargetView *rtv[MAX_COLOR_ATTACHMENTS]; + ID3D11DepthStencilView *dsv = nullptr; +}; + +struct QD3D11ReferenceRenderTarget : public QRhiRenderTarget +{ + QD3D11ReferenceRenderTarget(QRhiImplementation *rhi); + ~QD3D11ReferenceRenderTarget(); + void release() override; + + QSize pixelSize() const override; + float devicePixelRatio() const override; + int sampleCount() const override; + + QD3D11RenderTargetData d; +}; + +struct QD3D11TextureRenderTarget : public QRhiTextureRenderTarget +{ + QD3D11TextureRenderTarget(QRhiImplementation *rhi, const QRhiTextureRenderTargetDescription &desc, Flags flags); + ~QD3D11TextureRenderTarget(); + void release() override; + + QSize pixelSize() const override; + float devicePixelRatio() const override; + int sampleCount() const override; + + QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override; + bool build() override; + + QD3D11RenderTargetData d; + bool ownsRtv[QD3D11RenderTargetData::MAX_COLOR_ATTACHMENTS]; + ID3D11RenderTargetView *rtv[QD3D11RenderTargetData::MAX_COLOR_ATTACHMENTS]; + bool ownsDsv = false; + ID3D11DepthStencilView *dsv = nullptr; + friend class QRhiD3D11; +}; + +struct QD3D11ShaderResourceBindings : public QRhiShaderResourceBindings +{ + QD3D11ShaderResourceBindings(QRhiImplementation *rhi); + ~QD3D11ShaderResourceBindings(); + void release() override; + bool build() override; + + QVector sortedBindings; + uint generation = 0; + + // Keep track of the generation number of each referenced QRhi* to be able + // to detect that the batched bindings are out of date. + struct BoundUniformBufferData { + quint64 id; + uint generation; + }; + struct BoundSampledTextureData { + quint64 texId; + uint texGeneration; + quint64 samplerId; + uint samplerGeneration; + }; + struct BoundResourceData { + union { + BoundUniformBufferData ubuf; + BoundSampledTextureData stex; + }; + }; + QVector boundResourceData; + + QRhiBatchedBindings vsubufs; + QRhiBatchedBindings vsubufoffsets; + QRhiBatchedBindings vsubufsizes; + + QRhiBatchedBindings fsubufs; + QRhiBatchedBindings fsubufoffsets; + QRhiBatchedBindings fsubufsizes; + + QRhiBatchedBindings vssamplers; + QRhiBatchedBindings vsshaderresources; + + QRhiBatchedBindings fssamplers; + QRhiBatchedBindings fsshaderresources; + + friend class QRhiD3D11; +}; + +Q_DECLARE_TYPEINFO(QD3D11ShaderResourceBindings::BoundResourceData, Q_MOVABLE_TYPE); + +struct QD3D11GraphicsPipeline : public QRhiGraphicsPipeline +{ + QD3D11GraphicsPipeline(QRhiImplementation *rhi); + ~QD3D11GraphicsPipeline(); + void release() override; + bool build() override; + + ID3D11DepthStencilState *dsState = nullptr; + ID3D11BlendState *blendState = nullptr; + ID3D11VertexShader *vs = nullptr; + ID3D11PixelShader *fs = nullptr; + ID3D11InputLayout *inputLayout = nullptr; + D3D11_PRIMITIVE_TOPOLOGY d3dTopology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST; + ID3D11RasterizerState *rastState = nullptr; + uint generation = 0; + friend class QRhiD3D11; +}; + +struct QD3D11SwapChain; + +struct QD3D11CommandBuffer : public QRhiCommandBuffer +{ + QD3D11CommandBuffer(QRhiImplementation *rhi); + ~QD3D11CommandBuffer(); + void release() override; + + struct Command { + enum Cmd { + SetRenderTarget, + Clear, + Viewport, + Scissor, + BindVertexBuffers, + BindIndexBuffer, + BindGraphicsPipeline, + BindShaderResources, + StencilRef, + BlendConstants, + Draw, + DrawIndexed, + UpdateSubRes, + CopySubRes, + ResolveSubRes, + GenMip, + DebugMarkBegin, + DebugMarkEnd, + DebugMarkMsg + }; + enum ClearFlag { Color = 1, Depth = 2, Stencil = 4 }; + Cmd cmd; + + static const int MAX_UBUF_BINDINGS = 32; // should be D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT but 128 is a waste of space for our purposes + + // QRhi*/QD3D11* references should be kept at minimum (so no + // QRhiTexture/Buffer/etc. pointers). + union { + struct { + QRhiRenderTarget *rt; + } setRenderTarget; + struct { + QRhiRenderTarget *rt; + int mask; + float c[4]; + float d; + quint32 s; + } clear; + struct { + float x, y, w, h; + float d0, d1; + } viewport; + struct { + int x, y, w, h; + } scissor; + struct { + int startSlot; + int slotCount; + ID3D11Buffer *buffers[D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT]; + UINT offsets[D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT]; + UINT strides[D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT]; + } bindVertexBuffers; + struct { + ID3D11Buffer *buffer; + quint32 offset; + DXGI_FORMAT format; + } bindIndexBuffer; + struct { + QD3D11GraphicsPipeline *ps; + } bindGraphicsPipeline; + struct { + QD3D11ShaderResourceBindings *srb; + bool offsetOnlyChange; + int dynamicOffsetCount; + uint dynamicOffsetPairs[MAX_UBUF_BINDINGS * 2]; // binding, offsetInConstants + } bindShaderResources; + struct { + QD3D11GraphicsPipeline *ps; + quint32 ref; + } stencilRef; + struct { + QD3D11GraphicsPipeline *ps; + float c[4]; + } blendConstants; + struct { + QD3D11GraphicsPipeline *ps; + quint32 vertexCount; + quint32 instanceCount; + quint32 firstVertex; + quint32 firstInstance; + } draw; + struct { + QD3D11GraphicsPipeline *ps; + quint32 indexCount; + quint32 instanceCount; + quint32 firstIndex; + qint32 vertexOffset; + quint32 firstInstance; + } drawIndexed; + struct { + ID3D11Resource *dst; + UINT dstSubRes; + bool hasDstBox; + D3D11_BOX dstBox; + const void *src; // must come from retain*() + UINT srcRowPitch; + } updateSubRes; + struct { + ID3D11Resource *dst; + UINT dstSubRes; + UINT dstX; + UINT dstY; + ID3D11Resource *src; + UINT srcSubRes; + bool hasSrcBox; + D3D11_BOX srcBox; + } copySubRes; + struct { + ID3D11Resource *dst; + UINT dstSubRes; + ID3D11Resource *src; + UINT srcSubRes; + DXGI_FORMAT format; + } resolveSubRes; + struct { + ID3D11ShaderResourceView *srv; + } genMip; + struct { + char s[64]; + } debugMark; + } args; + }; + + QVector commands; + QRhiRenderTarget *currentTarget; + QRhiGraphicsPipeline *currentPipeline; + uint currentPipelineGeneration; + QRhiShaderResourceBindings *currentSrb; + uint currentSrbGeneration; + ID3D11Buffer *currentIndexBuffer; + quint32 currentIndexOffset; + DXGI_FORMAT currentIndexFormat; + ID3D11Buffer *currentVertexBuffers[D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT]; + quint32 currentVertexOffsets[D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT]; + + QVector dataRetainPool; + QVector imageRetainPool; + + // relies heavily on implicit sharing (no copies of the actual data will be made) + const uchar *retainData(const QByteArray &data) { + dataRetainPool.append(data); + return reinterpret_cast(dataRetainPool.constLast().constData()); + } + const uchar *retainImage(const QImage &image) { + imageRetainPool.append(image); + return imageRetainPool.constLast().constBits(); + } + void resetCommands() { + commands.clear(); + dataRetainPool.clear(); + imageRetainPool.clear(); + } + void resetState() { + resetCommands(); + currentTarget = nullptr; + resetCachedState(); + } + void resetCachedState() { + currentPipeline = nullptr; + currentPipelineGeneration = 0; + currentSrb = nullptr; + currentSrbGeneration = 0; + currentIndexBuffer = nullptr; + currentIndexOffset = 0; + currentIndexFormat = DXGI_FORMAT_R16_UINT; + memset(currentVertexBuffers, 0, sizeof(currentVertexBuffers)); + memset(currentVertexOffsets, 0, sizeof(currentVertexOffsets)); + } +}; + +Q_DECLARE_TYPEINFO(QD3D11CommandBuffer::Command, Q_MOVABLE_TYPE); + +struct QD3D11SwapChain : public QRhiSwapChain +{ + QD3D11SwapChain(QRhiImplementation *rhi); + ~QD3D11SwapChain(); + void release() override; + + QRhiCommandBuffer *currentFrameCommandBuffer() override; + QRhiRenderTarget *currentFrameRenderTarget() override; + + QSize surfacePixelSize() override; + + QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override; + bool buildOrResize() override; + + void releaseBuffers(); + bool newColorBuffer(const QSize &size, DXGI_FORMAT format, DXGI_SAMPLE_DESC sampleDesc, + ID3D11Texture2D **tex, ID3D11RenderTargetView **rtv) const; + + QWindow *window = nullptr; + QSize pixelSize; + QD3D11ReferenceRenderTarget rt; + QD3D11CommandBuffer cb; + DXGI_FORMAT colorFormat; + IDXGISwapChain *swapChain = nullptr; + static const int BUFFER_COUNT = 2; + ID3D11Texture2D *tex[BUFFER_COUNT]; + ID3D11RenderTargetView *rtv[BUFFER_COUNT]; + ID3D11Texture2D *msaaTex[BUFFER_COUNT]; + ID3D11RenderTargetView *msaaRtv[BUFFER_COUNT]; + DXGI_SAMPLE_DESC sampleDesc; + int currentFrameSlot = 0; + int frameCount = 0; + QD3D11RenderBuffer *ds = nullptr; + bool timestampActive[BUFFER_COUNT]; + ID3D11Query *timestampDisjointQuery[BUFFER_COUNT]; + ID3D11Query *timestampQuery[BUFFER_COUNT * 2]; + UINT swapInterval = 1; +}; + +class QRhiD3D11 : public QRhiImplementation +{ +public: + QRhiD3D11(QRhiD3D11InitParams *params, QRhiD3D11NativeHandles *importDevice = nullptr); + + bool create(QRhi::Flags flags) override; + void destroy() override; + + QRhiGraphicsPipeline *createGraphicsPipeline() override; + QRhiShaderResourceBindings *createShaderResourceBindings() override; + QRhiBuffer *createBuffer(QRhiBuffer::Type type, + QRhiBuffer::UsageFlags usage, + int size) override; + QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type, + const QSize &pixelSize, + int sampleCount, + QRhiRenderBuffer::Flags flags) override; + QRhiTexture *createTexture(QRhiTexture::Format format, + const QSize &pixelSize, + int sampleCount, + QRhiTexture::Flags flags) override; + QRhiSampler *createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, + QRhiSampler::Filter mipmapMode, + QRhiSampler:: AddressMode u, QRhiSampler::AddressMode v) override; + + QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc, + QRhiTextureRenderTarget::Flags flags) override; + + QRhiSwapChain *createSwapChain() override; + QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override; + QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override; + QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override; + QRhi::FrameOpResult endOffscreenFrame() override; + QRhi::FrameOpResult finish() override; + + void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override; + + void beginPass(QRhiCommandBuffer *cb, + QRhiRenderTarget *rt, + const QColor &colorClearValue, + const QRhiDepthStencilClearValue &depthStencilClearValue, + QRhiResourceUpdateBatch *resourceUpdates) override; + void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override; + + void setGraphicsPipeline(QRhiCommandBuffer *cb, + QRhiGraphicsPipeline *ps) override; + + void setShaderResources(QRhiCommandBuffer *cb, + QRhiShaderResourceBindings *srb, + int dynamicOffsetCount, + const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) override; + + void setVertexInput(QRhiCommandBuffer *cb, + int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings, + QRhiBuffer *indexBuf, quint32 indexOffset, + QRhiCommandBuffer::IndexFormat indexFormat) override; + + void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) override; + void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override; + void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override; + void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override; + + void draw(QRhiCommandBuffer *cb, quint32 vertexCount, + quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override; + + void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, + quint32 instanceCount, quint32 firstIndex, + qint32 vertexOffset, quint32 firstInstance) override; + + void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) override; + void debugMarkEnd(QRhiCommandBuffer *cb) override; + void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) override; + + const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) override; + void beginExternal(QRhiCommandBuffer *cb) override; + void endExternal(QRhiCommandBuffer *cb) override; + + QVector supportedSampleCounts() const override; + int ubufAlignment() const override; + bool isYUpInFramebuffer() const override; + bool isYUpInNDC() const override; + bool isClipDepthZeroToOne() const override; + QMatrix4x4 clipSpaceCorrMatrix() const override; + bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const override; + bool isFeatureSupported(QRhi::Feature feature) const override; + int resourceLimit(QRhi::ResourceLimit limit) const override; + const QRhiNativeHandles *nativeHandles() override; + void sendVMemStatsToProfiler() override; + + void flushCommandBuffer(); + void enqueueSubresUpload(QD3D11Texture *texD, QD3D11CommandBuffer *cbD, + int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc); + void enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates); + void updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD); + void executeBufferHostWritesForCurrentFrame(QD3D11Buffer *bufD); + void bindShaderResources(QD3D11ShaderResourceBindings *srbD, + const uint *dynOfsPairs, int dynOfsPairCount, + bool offsetOnlyChange); + void setRenderTarget(QRhiRenderTarget *rt); + void executeCommandBuffer(QD3D11CommandBuffer *cbD, QD3D11SwapChain *timestampSwapChain = nullptr); + DXGI_SAMPLE_DESC effectiveSampleCount(int sampleCount) const; + void finishActiveReadbacks(); + void enqueueSetRenderTarget(QD3D11CommandBuffer *cbD, QRhiRenderTarget *rt); + void reportLiveObjects(ID3D11Device *device); + + bool debugLayer = false; + bool importedDevice = false; + ID3D11Device *dev = nullptr; + ID3D11DeviceContext1 *context = nullptr; + D3D_FEATURE_LEVEL featureLevel; + ID3DUserDefinedAnnotation *annotations = nullptr; + IDXGIFactory1 *dxgiFactory = nullptr; + bool hasDxgi2 = false; + QRhiD3D11NativeHandles nativeHandlesStruct; + + bool inFrame = false; + bool inPass = false; + + struct { + int vsHighestActiveSrvBinding = -1; + int fsHighestActiveSrvBinding = -1; + QD3D11SwapChain *currentSwapChain = nullptr; + } contextState; + + struct OffscreenFrame { + OffscreenFrame(QRhiImplementation *rhi) : cbWrapper(rhi) { } + bool active = false; + QD3D11CommandBuffer cbWrapper; + } ofr; + + struct ActiveReadback { + QRhiReadbackDescription desc; + QRhiReadbackResult *result; + ID3D11Texture2D *stagingTex; + quint32 bufSize; + quint32 bpl; + QSize pixelSize; + QRhiTexture::Format format; + }; + QVector activeReadbacks; +}; + +Q_DECLARE_TYPEINFO(QRhiD3D11::ActiveReadback, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp new file mode 100644 index 0000000000..0c89a9284f --- /dev/null +++ b/src/gui/rhi/qrhigles2.cpp @@ -0,0 +1,2975 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Gui module +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qrhigles2_p_p.h" +#include +#include +#include +#include +#include + +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. +*/ + +/*! + \class QRhiGles2InitParams + \inmodule QtRhi + \brief OpenGL specific initialization parameters. + + An OpenGL-based QRhi needs an already created QOffscreenSurface at minimum. + Additionally, while optional, it is recommended that the QWindow the first + QRhiSwapChain will target is passed in as well. + + \badcode + QOffscreenSurface *fallbackSurface = QRhiGles2InitParams::newFallbackSurface(); + QRhiGles2InitParams params; + params.fallbackSurface = fallbackSurface; + params.window = window; + rhi = QRhi::create(QRhi::OpenGLES2, ¶ms); + \endcode + + By default QRhi creates a QOpenGLContext on its own. This approach works + well in most cases, included threaded scenarios, where there is a dedicated + QRhi for each rendering thread. As there will be a QOpenGLContext for each + QRhi, the OpenGL context requirements (a context can only be current on one + thread) are satisfied. The implicitly created context is destroyed + automatically together with the QRhi. + + The QSurfaceFormat for the context is specified in \l format. The + constructor sets this to QSurfaceFormat::defaultFormat() so applications + that use QSurfaceFormat::setDefaultFormat() do not need to set the format + again. + + \note The depth and stencil buffer sizes are set automatically to 24 and 8 + when no size was explicitly set for these buffers in \l format. As there + are possible adjustments to \l format, applications can use + adjustedFormat() to query the effective format that is passed to + QOpenGLContext::setFormat() internally. + + A QOffscreenSurface has to be specified in \l fallbackSurface. In order to + prevent mistakes in threaded situations, this is never created + automatically by the QRhi since, like QWindow, QOffscreenSurface can only + be created on the gui/main thread. + + As a convenience, applications can use newFallbackSurface() which creates + and returns a QOffscreenSurface that is compatible with the QOpenGLContext + that is going to be created by the QRhi afterwards. Note that the ownership + of the returned QOffscreenSurface is transferred to the caller and the QRhi + will not destroy it. + + \note QRhiSwapChain can only target QWindow instances that have their + surface type set to QSurface::OpenGLSurface. + + \note \l window is optional. It is recommended to specify it whenever + possible, in order to avoid problems on multi-adapter and multi-screen + systems. When \l window is not set, the very first + QOpenGLContext::makeCurrent() happens with \l fallbackSurface which may be + an invisible window on some platforms (for example, Windows) and that may + trigger unexpected problems in some cases. + + \section2 Working with existing OpenGL contexts + + When interoperating with another graphics engine, it may be necessary to + get a QRhi instance that uses the same OpenGL context. This can be achieved + by passing a pointer to a QRhiGles2NativeHandles to QRhi::create(). The + \l{QRhiGles2NativeHandles::context}{context} must be set to a non-null + value. + + An alternative approach is to create a QOpenGLContext that + \l{QOpenGLContext::setShareContext()}{shares resources} with the other + engine's context and passing in that context via QRhiGles2NativeHandles. + + The QRhi does not take ownership of the QOpenGLContext passed in via + QRhiGles2NativeHandles. + */ + +/*! + \class QRhiGles2NativeHandles + \inmodule QtRhi + \brief Holds the OpenGL context used by the QRhi. + */ + +/*! + \class QRhiGles2TextureNativeHandles + \inmodule QtRhi + \brief Holds the OpenGL texture object that is backing a QRhiTexture instance. + */ + +#ifndef GL_BGRA +#define GL_BGRA 0x80E1 +#endif + +#ifndef GL_R8 +#define GL_R8 0x8229 +#endif + +#ifndef GL_R16 +#define GL_R16 0x822A +#endif + +#ifndef GL_RED +#define GL_RED 0x1903 +#endif + +#ifndef GL_RGBA8 +#define GL_RGBA8 0x8058 +#endif + +#ifndef GL_RGBA32F +#define GL_RGBA32F 0x8814 +#endif + +#ifndef GL_RGBA16F +#define GL_RGBA16F 0x881A +#endif + +#ifndef GL_HALF_FLOAT +#define GL_HALF_FLOAT 0x140B +#endif + +#ifndef GL_DEPTH_COMPONENT16 +#define GL_DEPTH_COMPONENT16 0x81A5 +#endif + +#ifndef GL_DEPTH_COMPONENT32F +#define GL_DEPTH_COMPONENT32F 0x8CAC +#endif + +#ifndef GL_DEPTH24_STENCIL8 +#define GL_DEPTH24_STENCIL8 0x88F0 +#endif + +#ifndef GL_PRIMITIVE_RESTART_FIXED_INDEX +#define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69 +#endif + +#ifndef GL_FRAMEBUFFER_SRGB +#define GL_FRAMEBUFFER_SRGB 0x8DB9 +#endif + +#ifndef GL_READ_FRAMEBUFFER +#define GL_READ_FRAMEBUFFER 0x8CA8 +#endif + +#ifndef GL_DRAW_FRAMEBUFFER +#define GL_DRAW_FRAMEBUFFER 0x8CA9 +#endif + +#ifndef GL_MAX_DRAW_BUFFERS +#define GL_MAX_DRAW_BUFFERS 0x8824 +#endif + +#ifndef GL_TEXTURE_COMPARE_MODE +#define GL_TEXTURE_COMPARE_MODE 0x884C +#endif + +#ifndef GL_COMPARE_REF_TO_TEXTURE +#define GL_COMPARE_REF_TO_TEXTURE 0x884E +#endif + +#ifndef GL_TEXTURE_COMPARE_FUNC +#define GL_TEXTURE_COMPARE_FUNC 0x884D +#endif + +#ifndef GL_MAX_SAMPLES +#define GL_MAX_SAMPLES 0x8D57 +#endif + +/*! + Constructs a new QRhiGles2InitParams. + + \l format is set to QSurfaceFormat::defaultFormat(). + */ +QRhiGles2InitParams::QRhiGles2InitParams() +{ + format = QSurfaceFormat::defaultFormat(); +} + +/*! + \return the QSurfaceFormat that will be set on the QOpenGLContext before + calling QOpenGLContext::create(). This format is based on \a format, but + may be adjusted. Applicable only when QRhi creates the context. + Applications are advised to set this format on their QWindow in order to + avoid potential BAD_MATCH failures. + */ +QSurfaceFormat QRhiGles2InitParams::adjustedFormat(const QSurfaceFormat &format) +{ + QSurfaceFormat fmt = format; + + if (fmt.depthBufferSize() == -1) + fmt.setDepthBufferSize(24); + if (fmt.stencilBufferSize() == -1) + fmt.setStencilBufferSize(8); + + return fmt; +} + +/*! + \return a new QOffscreenSurface that can be used with a QRhi by passing it + via a QRhiGles2InitParams. + + \a format is adjusted as appropriate in order to avoid having problems + afterwards due to an incompatible context and surface. + + \note This function must only be called on the gui/main thread. + + \note It is the application's responsibility to destroy the returned + QOffscreenSurface on the gui/main thread once the associated QRhi has been + destroyed. The QRhi will not destroy the QOffscreenSurface. + */ +QOffscreenSurface *QRhiGles2InitParams::newFallbackSurface(const QSurfaceFormat &format) +{ + QSurfaceFormat fmt = adjustedFormat(format); + + // To resolve all fields in the format as much as possible, create a context. + // This may be heavy, but allows avoiding BAD_MATCH on some systems. + QOpenGLContext tempContext; + tempContext.setFormat(fmt); + if (tempContext.create()) + fmt = tempContext.format(); + else + qWarning("QRhiGles2: Failed to create temporary context"); + + QOffscreenSurface *s = new QOffscreenSurface; + s->setFormat(fmt); + s->create(); + + return s; +} + +QRhiGles2::QRhiGles2(QRhiGles2InitParams *params, QRhiGles2NativeHandles *importDevice) + : ofr(this) +{ + requestedFormat = QRhiGles2InitParams::adjustedFormat(params->format); + fallbackSurface = params->fallbackSurface; + maybeWindow = params->window; // may be null + + importedContext = importDevice != nullptr; + if (importedContext) { + ctx = importDevice->context; + if (!ctx) { + qWarning("No OpenGL context given, cannot import"); + importedContext = false; + } + } +} + +bool QRhiGles2::ensureContext(QSurface *surface) const +{ + bool nativeWindowGone = false; + if (surface && surface->surfaceClass() == QSurface::Window && !surface->surfaceHandle()) { + surface = fallbackSurface; + nativeWindowGone = true; + } + + if (!surface) + surface = fallbackSurface; + + if (needsMakeCurrent) + needsMakeCurrent = false; + else if (!nativeWindowGone && QOpenGLContext::currentContext() == ctx && (surface == fallbackSurface || ctx->surface() == surface)) + return true; + + if (!ctx->makeCurrent(surface)) { + qWarning("QRhiGles2: Failed to make context current. Expect bad things to happen."); + return false; + } + + return true; +} + +bool QRhiGles2::create(QRhi::Flags flags) +{ + Q_UNUSED(flags); + Q_ASSERT(fallbackSurface); + + if (!importedContext) { + ctx = new QOpenGLContext; + ctx->setFormat(requestedFormat); + if (!ctx->create()) { + qWarning("QRhiGles2: Failed to create context"); + delete ctx; + ctx = nullptr; + return false; + } + qDebug() << "Created OpenGL context" << ctx->format(); + } + + if (!ensureContext(maybeWindow ? maybeWindow : fallbackSurface)) // see 'window' discussion in QRhiGles2InitParams comments + return false; + + f = static_cast(ctx->extraFunctions()); + + const char *vendor = reinterpret_cast(f->glGetString(GL_VENDOR)); + const char *renderer = reinterpret_cast(f->glGetString(GL_RENDERER)); + const char *version = reinterpret_cast(f->glGetString(GL_VERSION)); + if (vendor && renderer && version) + qDebug("OpenGL VENDOR: %s RENDERER: %s VERSION: %s", vendor, renderer, version); + + const QSurfaceFormat actualFormat = ctx->format(); + + caps.ctxMajor = actualFormat.majorVersion(); + caps.ctxMinor = actualFormat.minorVersion(); + + GLint n = 0; + f->glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &n); + supportedCompressedFormats.resize(n); + if (n > 0) + f->glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, supportedCompressedFormats.data()); + + f->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &caps.maxTextureSize); + + if (caps.ctxMajor >= 3 || actualFormat.renderableType() == QSurfaceFormat::OpenGL) { + f->glGetIntegerv(GL_MAX_DRAW_BUFFERS, &caps.maxDrawBuffers); + f->glGetIntegerv(GL_MAX_SAMPLES, &caps.maxSamples); + caps.maxSamples = qMax(1, caps.maxSamples); + } else { + caps.maxDrawBuffers = 1; + caps.maxSamples = 1; + } + + caps.msaaRenderBuffer = f->hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample) + && f->hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit); + + caps.npotTexture = f->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures); + caps.npotTextureRepeat = f->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat); + + caps.gles = actualFormat.renderableType() == QSurfaceFormat::OpenGLES; + if (caps.gles) + caps.fixedIndexPrimitiveRestart = caps.ctxMajor >= 3; + else + caps.fixedIndexPrimitiveRestart = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 3); + + if (caps.fixedIndexPrimitiveRestart) + f->glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX); + + caps.bgraExternalFormat = f->hasOpenGLExtension(QOpenGLExtensions::BGRATextureFormat); + 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.packedDepthStencil = f->hasOpenGLExtension(QOpenGLExtensions::PackedDepthStencil); + caps.srgbCapableDefaultFramebuffer = f->hasOpenGLExtension(QOpenGLExtensions::SRGBFrameBuffer); + caps.coreProfile = actualFormat.profile() == QSurfaceFormat::CoreProfile; + caps.uniformBuffers = caps.ctxMajor >= 3 && (caps.gles || caps.ctxMinor >= 1); + caps.elementIndexUint = f->hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint); + + nativeHandlesStruct.context = ctx; + + return true; +} + +void QRhiGles2::destroy() +{ + if (!f) + return; + + ensureContext(); + executeDeferredReleases(); + + if (!importedContext) { + delete ctx; + ctx = nullptr; + } + + f = nullptr; +} + +void QRhiGles2::executeDeferredReleases() +{ + for (int i = releaseQueue.count() - 1; i >= 0; --i) { + const QRhiGles2::DeferredReleaseEntry &e(releaseQueue[i]); + switch (e.type) { + case QRhiGles2::DeferredReleaseEntry::Buffer: + f->glDeleteBuffers(1, &e.buffer.buffer); + break; + case QRhiGles2::DeferredReleaseEntry::Pipeline: + f->glDeleteProgram(e.pipeline.program); + break; + case QRhiGles2::DeferredReleaseEntry::Texture: + f->glDeleteTextures(1, &e.texture.texture); + break; + case QRhiGles2::DeferredReleaseEntry::RenderBuffer: + f->glDeleteRenderbuffers(1, &e.renderbuffer.renderbuffer); + f->glDeleteRenderbuffers(1, &e.renderbuffer.renderbuffer2); + break; + case QRhiGles2::DeferredReleaseEntry::TextureRenderTarget: + f->glDeleteFramebuffers(1, &e.textureRenderTarget.framebuffer); + break; + default: + Q_UNREACHABLE(); + break; + } + releaseQueue.removeAt(i); + } +} + +QVector QRhiGles2::supportedSampleCounts() const +{ + if (supportedSampleCountList.isEmpty()) { + // 1, 2, 4, 8, ... + for (int i = 1; i <= caps.maxSamples; i *= 2) + supportedSampleCountList.append(i); + } + return supportedSampleCountList; +} + +int QRhiGles2::effectiveSampleCount(int sampleCount) const +{ + // Stay compatible with QSurfaceFormat and friends where samples == 0 means the same as 1. + const int s = qBound(1, sampleCount, 64); + if (!supportedSampleCounts().contains(s)) { + qWarning("Attempted to set unsupported sample count %d", sampleCount); + return 1; + } + return s; +} + +QRhiSwapChain *QRhiGles2::createSwapChain() +{ + return new QGles2SwapChain(this); +} + +QRhiBuffer *QRhiGles2::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, int size) +{ + return new QGles2Buffer(this, type, usage, size); +} + +int QRhiGles2::ubufAlignment() const +{ + return 256; +} + +bool QRhiGles2::isYUpInFramebuffer() const +{ + return true; +} + +bool QRhiGles2::isYUpInNDC() const +{ + return true; +} + +bool QRhiGles2::isClipDepthZeroToOne() const +{ + return false; +} + +QMatrix4x4 QRhiGles2::clipSpaceCorrMatrix() const +{ + return QMatrix4x4(); // identity +} + +static inline GLenum toGlCompressedTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags) +{ + const bool srgb = flags.testFlag(QRhiTexture::sRGB); + switch (format) { + case QRhiTexture::BC1: + return srgb ? 0x8C4C : 0x83F0; + case QRhiTexture::BC3: + return srgb ? 0x8C4E : 0x83F2; + case QRhiTexture::BC5: + return srgb ? 0x8C4F : 0x83F3; + + case QRhiTexture::ETC2_RGB8: + return srgb ? 0x9275 : 0x9274; + case QRhiTexture::ETC2_RGB8A1: + return srgb ? 0x9277 : 0x9276; + case QRhiTexture::ETC2_RGBA8: + return srgb ? 0x9279 : 0x9278; + + case QRhiTexture::ASTC_4x4: + return srgb ? 0x93D0 : 0x93B0; + case QRhiTexture::ASTC_5x4: + return srgb ? 0x93D1 : 0x93B1; + case QRhiTexture::ASTC_5x5: + return srgb ? 0x93D2 : 0x93B2; + case QRhiTexture::ASTC_6x5: + return srgb ? 0x93D3 : 0x93B3; + case QRhiTexture::ASTC_6x6: + return srgb ? 0x93D4 : 0x93B4; + case QRhiTexture::ASTC_8x5: + return srgb ? 0x93D5 : 0x93B5; + case QRhiTexture::ASTC_8x6: + return srgb ? 0x93D6 : 0x93B6; + case QRhiTexture::ASTC_8x8: + return srgb ? 0x93D7 : 0x93B7; + case QRhiTexture::ASTC_10x5: + return srgb ? 0x93D8 : 0x93B8; + case QRhiTexture::ASTC_10x6: + return srgb ? 0x93D9 : 0x93B9; + case QRhiTexture::ASTC_10x8: + return srgb ? 0x93DA : 0x93BA; + case QRhiTexture::ASTC_10x10: + return srgb ? 0x93DB : 0x93BB; + case QRhiTexture::ASTC_12x10: + return srgb ? 0x93DC : 0x93BC; + case QRhiTexture::ASTC_12x12: + return srgb ? 0x93DD : 0x93BD; + + default: + return 0; // this is reachable, just return an invalid format + } +} + +bool QRhiGles2::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const +{ + if (isCompressedFormat(format)) + return supportedCompressedFormats.contains(toGlCompressedTextureFormat(format, flags)); + + switch (format) { + case QRhiTexture::D16: + Q_FALLTHROUGH(); + case QRhiTexture::D32F: + return caps.depthTexture; + + case QRhiTexture::BGRA8: + return caps.bgraExternalFormat; + + case QRhiTexture::R8: + return caps.r8Format; + + case QRhiTexture::R16: + return caps.r16Format; + + case QRhiTexture::RGBA16F: + Q_FALLTHROUGH(); + case QRhiTexture::RGBA32F: + return caps.floatFormats; + + default: + break; + } + + return true; +} + +bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const +{ + switch (feature) { + case QRhi::MultisampleTexture: + return false; + case QRhi::MultisampleRenderBuffer: + return caps.msaaRenderBuffer; + case QRhi::DebugMarkers: + return false; + case QRhi::Timestamps: + return false; + case QRhi::Instancing: + return false; + case QRhi::CustomInstanceStepRate: + return false; + case QRhi::PrimitiveRestart: + return caps.fixedIndexPrimitiveRestart; + case QRhi::NonDynamicUniformBuffers: + return true; + case QRhi::NonFourAlignedEffectiveIndexBufferOffset: + return true; + case QRhi::NPOTTextureRepeat: + return caps.npotTextureRepeat; + case QRhi::RedOrAlpha8IsRed: + return caps.coreProfile; + case QRhi::ElementIndexUint: + return caps.elementIndexUint; + default: + Q_UNREACHABLE(); + return false; + } +} + +int QRhiGles2::resourceLimit(QRhi::ResourceLimit limit) const +{ + switch (limit) { + case QRhi::TextureSizeMin: + return 1; + case QRhi::TextureSizeMax: + return caps.maxTextureSize; + case QRhi::MaxColorAttachments: + return caps.maxDrawBuffers; + case QRhi::FramesInFlight: + return 2; // dummy + default: + Q_UNREACHABLE(); + return 0; + } +} + +const QRhiNativeHandles *QRhiGles2::nativeHandles() +{ + return &nativeHandlesStruct; +} + +void QRhiGles2::sendVMemStatsToProfiler() +{ + // nothing to do here +} + +QRhiRenderBuffer *QRhiGles2::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize, + int sampleCount, QRhiRenderBuffer::Flags flags) +{ + return new QGles2RenderBuffer(this, type, pixelSize, sampleCount, flags); +} + +QRhiTexture *QRhiGles2::createTexture(QRhiTexture::Format format, const QSize &pixelSize, + int sampleCount, QRhiTexture::Flags flags) +{ + return new QGles2Texture(this, format, pixelSize, sampleCount, flags); +} + +QRhiSampler *QRhiGles2::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, + QRhiSampler::Filter mipmapMode, + QRhiSampler::AddressMode u, QRhiSampler::AddressMode v) +{ + return new QGles2Sampler(this, magFilter, minFilter, mipmapMode, u, v); +} + +QRhiTextureRenderTarget *QRhiGles2::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc, + QRhiTextureRenderTarget::Flags flags) +{ + return new QGles2TextureRenderTarget(this, desc, flags); +} + +QRhiGraphicsPipeline *QRhiGles2::createGraphicsPipeline() +{ + return new QGles2GraphicsPipeline(this); +} + +QRhiShaderResourceBindings *QRhiGles2::createShaderResourceBindings() +{ + return new QGles2ShaderResourceBindings(this); +} + +void QRhiGles2::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps) +{ + Q_ASSERT(inPass); + + QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); + QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, ps); + const bool pipelineChanged = cbD->currentPipeline != ps || cbD->currentPipelineGeneration != psD->generation; + + if (pipelineChanged) { + cbD->currentPipeline = ps; + cbD->currentPipelineGeneration = psD->generation; + + QGles2CommandBuffer::Command cmd; + cmd.cmd = QGles2CommandBuffer::Command::BindGraphicsPipeline; + cmd.args.bindGraphicsPipeline.ps = ps; + cbD->commands.append(cmd); + } +} + +void QRhiGles2::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb, + int dynamicOffsetCount, + const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) +{ + Q_ASSERT(inPass); + + QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); + Q_ASSERT(cbD->currentPipeline); + + if (!srb) + srb = QRHI_RES(QGles2GraphicsPipeline, cbD->currentPipeline)->m_shaderResourceBindings; + + 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: + if (b->u.ubuf.hasDynamicOffset) + hasDynamicOffsetInSrb = true; + break; + default: + break; + } + } + + const bool srbChanged = cbD->currentSrb != srb || cbD->currentSrbGeneration != srbD->generation; + + if (srbChanged || hasDynamicOffsetInSrb) { + cbD->currentSrb = srb; + cbD->currentSrbGeneration = srbD->generation; + + QGles2CommandBuffer::Command cmd; + cmd.cmd = QGles2CommandBuffer::Command::BindShaderResources; + cmd.args.bindShaderResources.ps = cbD->currentPipeline; + cmd.args.bindShaderResources.srb = srb; + cmd.args.bindShaderResources.dynamicOffsetCount = 0; + if (hasDynamicOffsetInSrb) { + if (dynamicOffsetCount < QGles2CommandBuffer::Command::MAX_UBUF_BINDINGS) { + cmd.args.bindShaderResources.dynamicOffsetCount = dynamicOffsetCount; + uint *p = cmd.args.bindShaderResources.dynamicOffsetPairs; + for (int i = 0; i < dynamicOffsetCount; ++i) { + const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]); + *p++ = dynOfs.first; + *p++ = dynOfs.second; + } + } else { + qWarning("Too many dynamic offsets (%d, max is %d)", + dynamicOffsetCount, QGles2CommandBuffer::Command::MAX_UBUF_BINDINGS); + } + } + cbD->commands.append(cmd); + } +} + +void QRhiGles2::setVertexInput(QRhiCommandBuffer *cb, + int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings, + QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat) +{ + Q_ASSERT(inPass); + QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); + + 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.buffer = bufD->buffer; + cmd.args.bindVertexBuffer.offset = ofs; + cmd.args.bindVertexBuffer.binding = startBinding + i; + cbD->commands.append(cmd); + } + + 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); + } +} + +void QRhiGles2::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) +{ + Q_ASSERT(inPass); + QGles2CommandBuffer::Command cmd; + cmd.cmd = QGles2CommandBuffer::Command::Viewport; + const std::array r = viewport.viewport(); + cmd.args.viewport.x = qMax(0.0f, r[0]); + cmd.args.viewport.y = qMax(0.0f, r[1]); + cmd.args.viewport.w = r[2]; + cmd.args.viewport.h = r[3]; + cmd.args.viewport.d0 = viewport.minDepth(); + cmd.args.viewport.d1 = viewport.maxDepth(); + QRHI_RES(QGles2CommandBuffer, cb)->commands.append(cmd); +} + +void QRhiGles2::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) +{ + Q_ASSERT(inPass); + QGles2CommandBuffer::Command cmd; + cmd.cmd = QGles2CommandBuffer::Command::Scissor; + const std::array r = scissor.scissor(); + cmd.args.scissor.x = qMax(0, r[0]); + cmd.args.scissor.y = qMax(0, r[1]); + cmd.args.scissor.w = r[2]; + cmd.args.scissor.h = r[3]; + QRHI_RES(QGles2CommandBuffer, cb)->commands.append(cmd); +} + +void QRhiGles2::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) +{ + Q_ASSERT(inPass); + QGles2CommandBuffer::Command cmd; + cmd.cmd = QGles2CommandBuffer::Command::BlendConstants; + cmd.args.blendConstants.r = c.redF(); + cmd.args.blendConstants.g = c.greenF(); + cmd.args.blendConstants.b = c.blueF(); + cmd.args.blendConstants.a = c.alphaF(); + QRHI_RES(QGles2CommandBuffer, cb)->commands.append(cmd); +} + +void QRhiGles2::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) +{ + Q_ASSERT(inPass); + QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); + + QGles2CommandBuffer::Command cmd; + cmd.cmd = QGles2CommandBuffer::Command::StencilRef; + cmd.args.stencilRef.ref = refValue; + cmd.args.stencilRef.ps = cbD->currentPipeline; + cbD->commands.append(cmd); +} + +void QRhiGles2::draw(QRhiCommandBuffer *cb, quint32 vertexCount, + quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) +{ + Q_ASSERT(inPass); + Q_UNUSED(instanceCount); // no instancing + Q_UNUSED(firstInstance); + QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); + + QGles2CommandBuffer::Command cmd; + cmd.cmd = QGles2CommandBuffer::Command::Draw; + cmd.args.draw.ps = cbD->currentPipeline; + cmd.args.draw.vertexCount = vertexCount; + cmd.args.draw.firstVertex = firstVertex; + cbD->commands.append(cmd); +} + +void QRhiGles2::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, + quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance) +{ + Q_ASSERT(inPass); + Q_UNUSED(instanceCount); // no instancing + Q_UNUSED(firstInstance); + Q_UNUSED(vertexOffset); // no glDrawElementsBaseVertex + QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); + + QGles2CommandBuffer::Command cmd; + cmd.cmd = QGles2CommandBuffer::Command::DrawIndexed; + cmd.args.drawIndexed.ps = cbD->currentPipeline; + cmd.args.drawIndexed.indexCount = indexCount; + cmd.args.drawIndexed.firstIndex = firstIndex; + cbD->commands.append(cmd); +} + +void QRhiGles2::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) +{ + if (!debugMarkers) + return; + + Q_UNUSED(cb); + Q_UNUSED(name); +} + +void QRhiGles2::debugMarkEnd(QRhiCommandBuffer *cb) +{ + if (!debugMarkers) + return; + + Q_UNUSED(cb); +} + +void QRhiGles2::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) +{ + if (!debugMarkers) + return; + + Q_UNUSED(cb); + Q_UNUSED(msg); +} + +const QRhiNativeHandles *QRhiGles2::nativeHandles(QRhiCommandBuffer *cb) +{ + Q_UNUSED(cb); + return nullptr; +} + +void QRhiGles2::beginExternal(QRhiCommandBuffer *cb) +{ + Q_ASSERT(inPass); + Q_UNUSED(cb); + flushCommandBuffer(); // also ensures the context is current +} + +void QRhiGles2::endExternal(QRhiCommandBuffer *cb) +{ + Q_ASSERT(inPass); + QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); + Q_ASSERT(cbD->currentTarget); + Q_ASSERT(cbD->commands.isEmpty()); + cbD->resetCachedState(); + enqueueBindFramebuffer(cbD->currentTarget, cbD); +} + +static void addBoundaryCommand(QGles2CommandBuffer *cb, QGles2CommandBuffer::Command::Cmd type) +{ + QGles2CommandBuffer::Command cmd; + cmd.cmd = type; + cb->commands.append(cmd); +} + +QRhi::FrameOpResult QRhiGles2::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) +{ + Q_UNUSED(flags); + Q_ASSERT(!inFrame); + + QGles2SwapChain *swapChainD = QRHI_RES(QGles2SwapChain, swapChain); + if (!ensureContext(swapChainD->surface)) + return QRhi::FrameOpError; + + inFrame = true; + currentSwapChain = swapChainD; + + QRhiProfilerPrivate *rhiP = profilerPrivateOrNull(); + QRHI_PROF_F(beginSwapChainFrame(swapChain)); + + executeDeferredReleases(); + swapChainD->cb.resetState(); + + addBoundaryCommand(&swapChainD->cb, QGles2CommandBuffer::Command::BeginFrame); + + return QRhi::FrameOpSuccess; +} + +QRhi::FrameOpResult QRhiGles2::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) +{ + Q_ASSERT(inFrame); + inFrame = false; + + QGles2SwapChain *swapChainD = QRHI_RES(QGles2SwapChain, swapChain); + Q_ASSERT(currentSwapChain == swapChainD); + + addBoundaryCommand(&swapChainD->cb, QGles2CommandBuffer::Command::EndFrame); + + if (!ensureContext(swapChainD->surface)) + return QRhi::FrameOpError; + + executeCommandBuffer(&swapChainD->cb); + + QRhiProfilerPrivate *rhiP = profilerPrivateOrNull(); + // this must be done before the swap + QRHI_PROF_F(endSwapChainFrame(swapChain, swapChainD->frameCount + 1)); + + if (swapChainD->surface && !flags.testFlag(QRhi::SkipPresent)) { + ctx->swapBuffers(swapChainD->surface); + needsMakeCurrent = true; + } else { + f->glFlush(); + } + + swapChainD->frameCount += 1; + currentSwapChain = nullptr; + return QRhi::FrameOpSuccess; +} + +QRhi::FrameOpResult QRhiGles2::beginOffscreenFrame(QRhiCommandBuffer **cb) +{ + Q_ASSERT(!inFrame); + + if (!ensureContext()) + return QRhi::FrameOpError; + + inFrame = true; + ofr.active = true; + + executeDeferredReleases(); + ofr.cbWrapper.resetState(); + + addBoundaryCommand(&ofr.cbWrapper, QGles2CommandBuffer::Command::BeginFrame); + *cb = &ofr.cbWrapper; + + return QRhi::FrameOpSuccess; +} + +QRhi::FrameOpResult QRhiGles2::endOffscreenFrame() +{ + Q_ASSERT(inFrame && ofr.active); + inFrame = false; + ofr.active = false; + + addBoundaryCommand(&ofr.cbWrapper, QGles2CommandBuffer::Command::EndFrame); + + if (!ensureContext()) + return QRhi::FrameOpError; + + executeCommandBuffer(&ofr.cbWrapper); + + return QRhi::FrameOpSuccess; +} + +QRhi::FrameOpResult QRhiGles2::finish() +{ + Q_ASSERT(!inPass); // because that's what the QRhi docs say, even though not required by this backend + return inFrame ? flushCommandBuffer() : QRhi::FrameOpSuccess; +} + +QRhi::FrameOpResult QRhiGles2::flushCommandBuffer() +{ + if (ofr.active) { + Q_ASSERT(!currentSwapChain); + if (!ensureContext()) + return QRhi::FrameOpError; + executeCommandBuffer(&ofr.cbWrapper); + ofr.cbWrapper.resetCommands(); + } else { + Q_ASSERT(currentSwapChain); + if (!ensureContext(currentSwapChain->surface)) + return QRhi::FrameOpError; + executeCommandBuffer(¤tSwapChain->cb); + currentSwapChain->cb.resetCommands(); + } + return QRhi::FrameOpSuccess; +} + +void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cbD, + int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc) +{ + 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; + const QPoint dp = subresDesc.destinationTopLeft(); + const QByteArray rawData = subresDesc.data(); + if (!subresDesc.image().isNull()) { + QImage img = subresDesc.image(); + QSize size = img.size(); + QGles2CommandBuffer::Command cmd; + cmd.cmd = QGles2CommandBuffer::Command::SubImage; + if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) { + const QPoint sp = subresDesc.sourceTopLeft(); + if (!subresDesc.sourceSize().isEmpty()) + size = subresDesc.sourceSize(); + img = img.copy(sp.x(), sp.y(), size.width(), size.height()); + } + cmd.args.subImage.target = texD->target; + cmd.args.subImage.texture = texD->texture; + cmd.args.subImage.faceTarget = faceTargetBase + layer; + cmd.args.subImage.level = level; + cmd.args.subImage.dx = dp.x(); + cmd.args.subImage.dy = dp.y(); + cmd.args.subImage.w = size.width(); + cmd.args.subImage.h = size.height(); + cmd.args.subImage.glformat = texD->glformat; + cmd.args.subImage.gltype = texD->gltype; + cmd.args.subImage.rowStartAlign = 4; + cmd.args.subImage.data = cbD->retainImage(img); + cbD->commands.append(cmd); + } else if (!rawData.isEmpty() && isCompressed) { + const QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize) + : subresDesc.sourceSize(); + if (texD->specified) { + QGles2CommandBuffer::Command cmd; + cmd.cmd = QGles2CommandBuffer::Command::CompressedSubImage; + cmd.args.compressedSubImage.target = texD->target; + cmd.args.compressedSubImage.texture = texD->texture; + cmd.args.compressedSubImage.faceTarget = faceTargetBase + layer; + cmd.args.compressedSubImage.level = level; + cmd.args.compressedSubImage.dx = dp.x(); + cmd.args.compressedSubImage.dy = dp.y(); + cmd.args.compressedSubImage.w = size.width(); + cmd.args.compressedSubImage.h = size.height(); + cmd.args.compressedSubImage.glintformat = texD->glintformat; + cmd.args.compressedSubImage.size = rawData.size(); + cmd.args.compressedSubImage.data = cbD->retainData(rawData); + cbD->commands.append(cmd); + } else { + QGles2CommandBuffer::Command cmd; + cmd.cmd = QGles2CommandBuffer::Command::CompressedImage; + cmd.args.compressedImage.target = texD->target; + cmd.args.compressedImage.texture = texD->texture; + cmd.args.compressedImage.faceTarget = faceTargetBase + layer; + cmd.args.compressedImage.level = level; + cmd.args.compressedImage.glintformat = texD->glintformat; + cmd.args.compressedImage.w = size.width(); + cmd.args.compressedImage.h = size.height(); + cmd.args.compressedImage.size = rawData.size(); + cmd.args.compressedImage.data = cbD->retainData(rawData); + cbD->commands.append(cmd); + } + } else if (!rawData.isEmpty()) { + const QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize) + : subresDesc.sourceSize(); + quint32 bpl = 0; + textureFormatInfo(texD->m_format, size, &bpl, nullptr); + QGles2CommandBuffer::Command cmd; + cmd.cmd = QGles2CommandBuffer::Command::SubImage; + cmd.args.subImage.target = texD->target; + cmd.args.subImage.texture = texD->texture; + cmd.args.subImage.faceTarget = faceTargetBase + layer; + cmd.args.subImage.level = level; + cmd.args.subImage.dx = dp.x(); + cmd.args.subImage.dy = dp.y(); + cmd.args.subImage.w = size.width(); + cmd.args.subImage.h = size.height(); + cmd.args.subImage.glformat = texD->glformat; + cmd.args.subImage.gltype = texD->gltype; + // Default unpack alignment (row start aligment + // requirement) is 4. QImage guarantees 4 byte aligned + // row starts, but our raw data here does not. + cmd.args.subImage.rowStartAlign = (bpl & 3) ? 1 : 4; + cmd.args.subImage.data = cbD->retainData(rawData); + cbD->commands.append(cmd); + } else { + qWarning("Invalid texture upload for %p layer=%d mip=%d", texD, layer, level); + } +} + +void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) +{ + QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); + QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates); + + for (const QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate &u : ud->dynamicBufferUpdates) { + QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, u.buf); + Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic); + if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) { + memcpy(bufD->ubuf.data() + u.offset, u.data.constData(), u.data.size()); + } else { + QGles2CommandBuffer::Command cmd; + cmd.cmd = QGles2CommandBuffer::Command::BufferSubData; + cmd.args.bufferSubData.target = bufD->target; + cmd.args.bufferSubData.buffer = bufD->buffer; + cmd.args.bufferSubData.offset = u.offset; + cmd.args.bufferSubData.size = u.data.size(); + cmd.args.bufferSubData.data = cbD->retainData(u.data); + cbD->commands.append(cmd); + } + } + + for (const QRhiResourceUpdateBatchPrivate::StaticBufferUpload &u : ud->staticBufferUploads) { + QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, u.buf); + Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic); + Q_ASSERT(u.offset + u.data.size() <= bufD->m_size); + if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) { + memcpy(bufD->ubuf.data() + u.offset, u.data.constData(), u.data.size()); + } else { + QGles2CommandBuffer::Command cmd; + cmd.cmd = QGles2CommandBuffer::Command::BufferSubData; + cmd.args.bufferSubData.target = bufD->target; + cmd.args.bufferSubData.buffer = bufD->buffer; + cmd.args.bufferSubData.offset = u.offset; + cmd.args.bufferSubData.size = u.data.size(); + cmd.args.bufferSubData.data = cbD->retainData(u.data); + cbD->commands.append(cmd); + } + } + + for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) { + if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) { + QGles2Texture *texD = QRHI_RES(QGles2Texture, u.upload.tex); + for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) { + for (int level = 0; level < QRhi::MAX_LEVELS; ++level) { + for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.upload.subresDesc[layer][level])) + enqueueSubresUpload(texD, cbD, layer, level, subresDesc); + } + } + texD->specified = true; + } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) { + Q_ASSERT(u.copy.src && u.copy.dst); + QGles2Texture *srcD = QRHI_RES(QGles2Texture, u.copy.src); + QGles2Texture *dstD = QRHI_RES(QGles2Texture, u.copy.dst); + + 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(); + const QPoint dp = u.copy.desc.destinationTopLeft(); + + const GLenum srcFaceTargetBase = srcD->m_flags.testFlag(QRhiTexture::CubeMap) + ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : srcD->target; + const GLenum dstFaceTargetBase = dstD->m_flags.testFlag(QRhiTexture::CubeMap) + ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : dstD->target; + + QGles2CommandBuffer::Command cmd; + cmd.cmd = QGles2CommandBuffer::Command::CopyTex; + + cmd.args.copyTex.srcFaceTarget = srcFaceTargetBase + u.copy.desc.sourceLayer(); + cmd.args.copyTex.srcTexture = srcD->texture; + cmd.args.copyTex.srcLevel = u.copy.desc.sourceLevel(); + cmd.args.copyTex.srcX = sp.x(); + cmd.args.copyTex.srcY = sp.y(); + + cmd.args.copyTex.dstTarget = dstD->target; + cmd.args.copyTex.dstTexture = dstD->texture; + cmd.args.copyTex.dstFaceTarget = dstFaceTargetBase + u.copy.desc.destinationLayer(); + cmd.args.copyTex.dstLevel = u.copy.desc.destinationLevel(); + cmd.args.copyTex.dstX = dp.x(); + cmd.args.copyTex.dstY = dp.y(); + + cmd.args.copyTex.w = size.width(); + cmd.args.copyTex.h = size.height(); + + cbD->commands.append(cmd); + } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) { + QGles2CommandBuffer::Command cmd; + cmd.cmd = QGles2CommandBuffer::Command::ReadPixels; + cmd.args.readPixels.result = u.read.result; + QGles2Texture *texD = QRHI_RES(QGles2Texture, u.read.rb.texture()); + cmd.args.readPixels.texture = texD ? texD->texture : 0; + if (texD) { + cmd.args.readPixels.w = texD->m_pixelSize.width(); + cmd.args.readPixels.h = texD->m_pixelSize.height(); + cmd.args.readPixels.format = texD->m_format; + const GLenum faceTargetBase = texD->m_flags.testFlag(QRhiTexture::CubeMap) + ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target; + cmd.args.readPixels.readTarget = faceTargetBase + u.read.rb.layer(); + cmd.args.readPixels.level = u.read.rb.level(); + } + cbD->commands.append(cmd); + } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::MipGen) { + 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); + } + } + + ud->free(); +} + +static inline GLenum toGlTopology(QRhiGraphicsPipeline::Topology t) +{ + switch (t) { + case QRhiGraphicsPipeline::Triangles: + return GL_TRIANGLES; + case QRhiGraphicsPipeline::TriangleStrip: + return GL_TRIANGLE_STRIP; + case QRhiGraphicsPipeline::Lines: + return GL_LINES; + case QRhiGraphicsPipeline::LineStrip: + return GL_LINE_STRIP; + case QRhiGraphicsPipeline::Points: + return GL_POINTS; + default: + Q_UNREACHABLE(); + return GL_TRIANGLES; + } +} + +static inline GLenum toGlCullMode(QRhiGraphicsPipeline::CullMode c) +{ + switch (c) { + case QRhiGraphicsPipeline::Front: + return GL_FRONT; + case QRhiGraphicsPipeline::Back: + return GL_BACK; + default: + Q_UNREACHABLE(); + return GL_BACK; + } +} + +static inline GLenum toGlFrontFace(QRhiGraphicsPipeline::FrontFace f) +{ + switch (f) { + case QRhiGraphicsPipeline::CCW: + return GL_CCW; + case QRhiGraphicsPipeline::CW: + return GL_CW; + default: + Q_UNREACHABLE(); + return GL_CCW; + } +} + +static inline GLenum toGlBlendFactor(QRhiGraphicsPipeline::BlendFactor f) +{ + switch (f) { + case QRhiGraphicsPipeline::Zero: + return GL_ZERO; + case QRhiGraphicsPipeline::One: + return GL_ONE; + case QRhiGraphicsPipeline::SrcColor: + return GL_SRC_COLOR; + case QRhiGraphicsPipeline::OneMinusSrcColor: + return GL_ONE_MINUS_SRC_COLOR; + case QRhiGraphicsPipeline::DstColor: + return GL_DST_COLOR; + case QRhiGraphicsPipeline::OneMinusDstColor: + return GL_ONE_MINUS_DST_COLOR; + case QRhiGraphicsPipeline::SrcAlpha: + return GL_SRC_ALPHA; + case QRhiGraphicsPipeline::OneMinusSrcAlpha: + return GL_ONE_MINUS_SRC_ALPHA; + case QRhiGraphicsPipeline::DstAlpha: + return GL_DST_ALPHA; + case QRhiGraphicsPipeline::OneMinusDstAlpha: + return GL_ONE_MINUS_DST_ALPHA; + case QRhiGraphicsPipeline::ConstantColor: + return GL_CONSTANT_COLOR; + case QRhiGraphicsPipeline::OneMinusConstantColor: + return GL_ONE_MINUS_CONSTANT_COLOR; + case QRhiGraphicsPipeline::ConstantAlpha: + return GL_CONSTANT_ALPHA; + case QRhiGraphicsPipeline::OneMinusConstantAlpha: + return GL_ONE_MINUS_CONSTANT_ALPHA; + case QRhiGraphicsPipeline::SrcAlphaSaturate: + return GL_SRC_ALPHA_SATURATE; + case QRhiGraphicsPipeline::Src1Color: + Q_FALLTHROUGH(); + case QRhiGraphicsPipeline::OneMinusSrc1Color: + Q_FALLTHROUGH(); + case QRhiGraphicsPipeline::Src1Alpha: + Q_FALLTHROUGH(); + case QRhiGraphicsPipeline::OneMinusSrc1Alpha: + qWarning("Unsupported blend factor %d", f); + return GL_ZERO; + default: + Q_UNREACHABLE(); + return GL_ZERO; + } +} + +static inline GLenum toGlBlendOp(QRhiGraphicsPipeline::BlendOp op) +{ + switch (op) { + case QRhiGraphicsPipeline::Add: + return GL_FUNC_ADD; + case QRhiGraphicsPipeline::Subtract: + return GL_FUNC_SUBTRACT; + case QRhiGraphicsPipeline::ReverseSubtract: + return GL_FUNC_REVERSE_SUBTRACT; + case QRhiGraphicsPipeline::Min: + return GL_MIN; + case QRhiGraphicsPipeline::Max: + return GL_MAX; + default: + Q_UNREACHABLE(); + return GL_FUNC_ADD; + } +} + +static inline GLenum toGlCompareOp(QRhiGraphicsPipeline::CompareOp op) +{ + switch (op) { + case QRhiGraphicsPipeline::Never: + return GL_NEVER; + case QRhiGraphicsPipeline::Less: + return GL_LESS; + case QRhiGraphicsPipeline::Equal: + return GL_EQUAL; + case QRhiGraphicsPipeline::LessOrEqual: + return GL_LEQUAL; + case QRhiGraphicsPipeline::Greater: + return GL_GREATER; + case QRhiGraphicsPipeline::NotEqual: + return GL_NOTEQUAL; + case QRhiGraphicsPipeline::GreaterOrEqual: + return GL_GEQUAL; + case QRhiGraphicsPipeline::Always: + return GL_ALWAYS; + default: + Q_UNREACHABLE(); + return GL_ALWAYS; + } +} + +static inline GLenum toGlStencilOp(QRhiGraphicsPipeline::StencilOp op) +{ + switch (op) { + case QRhiGraphicsPipeline::StencilZero: + return GL_ZERO; + case QRhiGraphicsPipeline::Keep: + return GL_KEEP; + case QRhiGraphicsPipeline::Replace: + return GL_REPLACE; + case QRhiGraphicsPipeline::IncrementAndClamp: + return GL_INCR; + case QRhiGraphicsPipeline::DecrementAndClamp: + return GL_DECR; + case QRhiGraphicsPipeline::Invert: + return GL_INVERT; + case QRhiGraphicsPipeline::IncrementAndWrap: + return GL_INCR_WRAP; + case QRhiGraphicsPipeline::DecrementAndWrap: + return GL_DECR_WRAP; + default: + Q_UNREACHABLE(); + return GL_KEEP; + } +} + +static inline GLenum toGlMinFilter(QRhiSampler::Filter f, QRhiSampler::Filter m) +{ + switch (f) { + case QRhiSampler::Nearest: + if (m == QRhiSampler::None) + return GL_NEAREST; + else + return m == QRhiSampler::Nearest ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST_MIPMAP_LINEAR; + case QRhiSampler::Linear: + if (m == QRhiSampler::None) + return GL_LINEAR; + else + return m == QRhiSampler::Nearest ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR_MIPMAP_LINEAR; + default: + Q_UNREACHABLE(); + return GL_LINEAR; + } +} + +static inline GLenum toGlMagFilter(QRhiSampler::Filter f) +{ + switch (f) { + case QRhiSampler::Nearest: + return GL_NEAREST; + case QRhiSampler::Linear: + return GL_LINEAR; + default: + Q_UNREACHABLE(); + return GL_LINEAR; + } +} + +static inline GLenum toGlWrapMode(QRhiSampler::AddressMode m) +{ + switch (m) { + case QRhiSampler::Repeat: + return GL_REPEAT; + case QRhiSampler::ClampToEdge: + return GL_CLAMP_TO_EDGE; + case QRhiSampler::Mirror: + return GL_MIRRORED_REPEAT; + case QRhiSampler::MirrorOnce: + Q_FALLTHROUGH(); + case QRhiSampler::Border: + qWarning("Unsupported wrap mode %d", m); + return GL_CLAMP_TO_EDGE; + default: + Q_UNREACHABLE(); + return GL_CLAMP_TO_EDGE; + } +} + +static inline GLenum toGlTextureCompareFunc(QRhiSampler::CompareOp op) +{ + switch (op) { + case QRhiSampler::Never: + return GL_NEVER; + case QRhiSampler::Less: + return GL_LESS; + case QRhiSampler::Equal: + return GL_EQUAL; + case QRhiSampler::LessOrEqual: + return GL_LEQUAL; + case QRhiSampler::Greater: + return GL_GREATER; + case QRhiSampler::NotEqual: + return GL_NOTEQUAL; + case QRhiSampler::GreaterOrEqual: + return GL_GEQUAL; + case QRhiSampler::Always: + return GL_ALWAYS; + default: + Q_UNREACHABLE(); + return GL_NEVER; + } +} + +void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb) +{ + QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); + GLenum indexType = GL_UNSIGNED_SHORT; + quint32 indexStride = sizeof(quint16); + quint32 indexOffset = 0; + + for (const QGles2CommandBuffer::Command &cmd : qAsConst(cbD->commands)) { + switch (cmd.cmd) { + case QGles2CommandBuffer::Command::BeginFrame: + if (caps.coreProfile) { + if (!vao) + f->glGenVertexArrays(1, &vao); + f->glBindVertexArray(vao); + } + break; + case QGles2CommandBuffer::Command::EndFrame: + if (vao) + f->glBindVertexArray(0); + break; + case QGles2CommandBuffer::Command::Viewport: + f->glViewport(cmd.args.viewport.x, cmd.args.viewport.y, cmd.args.viewport.w, cmd.args.viewport.h); + f->glDepthRangef(cmd.args.viewport.d0, cmd.args.viewport.d1); + break; + case QGles2CommandBuffer::Command::Scissor: + f->glScissor(cmd.args.scissor.x, cmd.args.scissor.y, cmd.args.scissor.w, cmd.args.scissor.h); + break; + case QGles2CommandBuffer::Command::BlendConstants: + f->glBlendColor(cmd.args.blendConstants.r, cmd.args.blendConstants.g, cmd.args.blendConstants.b, cmd.args.blendConstants.a); + break; + case QGles2CommandBuffer::Command::StencilRef: + { + QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.stencilRef.ps); + if (psD) { + f->glStencilFuncSeparate(GL_FRONT, toGlCompareOp(psD->m_stencilFront.compareOp), cmd.args.stencilRef.ref, psD->m_stencilReadMask); + f->glStencilFuncSeparate(GL_BACK, toGlCompareOp(psD->m_stencilBack.compareOp), cmd.args.stencilRef.ref, psD->m_stencilReadMask); + } else { + qWarning("No graphics pipeline active for setStencilRef; ignored"); + } + } + break; + case QGles2CommandBuffer::Command::BindVertexBuffer: + { + QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.bindVertexBuffer.ps); + if (psD) { + const QVector bindings = psD->m_vertexInputLayout.bindings(); + const QVector attributes = psD->m_vertexInputLayout.attributes(); + for (const QRhiVertexInputAttribute &a : attributes) { + if (a.binding() != cmd.args.bindVertexBuffer.binding) + continue; + + // we do not support more than one vertex buffer + f->glBindBuffer(GL_ARRAY_BUFFER, cmd.args.bindVertexBuffer.buffer); + + const int stride = bindings[a.binding()].stride(); + int size = 1; + GLenum type = GL_FLOAT; + bool normalize = false; + switch (a.format()) { + case QRhiVertexInputAttribute::Float4: + type = GL_FLOAT; + size = 4; + break; + case QRhiVertexInputAttribute::Float3: + type = GL_FLOAT; + size = 3; + break; + case QRhiVertexInputAttribute::Float2: + type = GL_FLOAT; + size = 2; + break; + case QRhiVertexInputAttribute::Float: + type = GL_FLOAT; + size = 1; + break; + case QRhiVertexInputAttribute::UNormByte4: + type = GL_UNSIGNED_BYTE; + normalize = true; + size = 4; + break; + case QRhiVertexInputAttribute::UNormByte2: + type = GL_UNSIGNED_BYTE; + normalize = true; + size = 2; + break; + case QRhiVertexInputAttribute::UNormByte: + type = GL_UNSIGNED_BYTE; + normalize = true; + size = 1; + break; + default: + break; + } + quint32 ofs = a.offset() + cmd.args.bindVertexBuffer.offset; + f->glVertexAttribPointer(a.location(), size, type, normalize, stride, + reinterpret_cast(quintptr(ofs))); + f->glEnableVertexAttribArray(a.location()); + } + } else { + qWarning("No graphics pipeline active for setVertexInput; ignored"); + } + } + break; + case QGles2CommandBuffer::Command::BindIndexBuffer: + indexType = cmd.args.bindIndexBuffer.type; + indexStride = indexType == GL_UNSIGNED_SHORT ? sizeof(quint16) : sizeof(quint32); + indexOffset = cmd.args.bindIndexBuffer.offset; + f->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cmd.args.bindIndexBuffer.buffer); + break; + case QGles2CommandBuffer::Command::Draw: + { + QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.draw.ps); + if (psD) + f->glDrawArrays(psD->drawMode, cmd.args.draw.firstVertex, cmd.args.draw.vertexCount); + else + qWarning("No graphics pipeline active for draw; ignored"); + } + break; + case QGles2CommandBuffer::Command::DrawIndexed: + { + QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.drawIndexed.ps); + if (psD) { + quint32 ofs = cmd.args.drawIndexed.firstIndex * indexStride + indexOffset; + f->glDrawElements(psD->drawMode, + cmd.args.drawIndexed.indexCount, + indexType, + reinterpret_cast(quintptr(ofs))); + } else { + qWarning("No graphics pipeline active for drawIndexed; ignored"); + } + } + break; + case QGles2CommandBuffer::Command::BindGraphicsPipeline: + executeBindGraphicsPipeline(cmd.args.bindGraphicsPipeline.ps); + break; + case QGles2CommandBuffer::Command::BindShaderResources: + bindShaderResources(cmd.args.bindShaderResources.ps, + cmd.args.bindShaderResources.srb, + cmd.args.bindShaderResources.dynamicOffsetPairs, + cmd.args.bindShaderResources.dynamicOffsetCount); + break; + case QGles2CommandBuffer::Command::BindFramebuffer: + if (cmd.args.bindFramebuffer.fbo) { + f->glBindFramebuffer(GL_FRAMEBUFFER, cmd.args.bindFramebuffer.fbo); + if (caps.maxDrawBuffers > 1) { + const int colorAttCount = cmd.args.bindFramebuffer.colorAttCount; + QVarLengthArray bufs; + for (int i = 0; i < colorAttCount; ++i) + bufs.append(GL_COLOR_ATTACHMENT0 + i); + f->glDrawBuffers(colorAttCount, bufs.constData()); + } + } else { + f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject()); + if (caps.maxDrawBuffers > 1) { + GLenum bufs = GL_BACK; + f->glDrawBuffers(1, &bufs); + } + } + if (caps.srgbCapableDefaultFramebuffer) { + if (cmd.args.bindFramebuffer.srgb) + f->glEnable(GL_FRAMEBUFFER_SRGB); + else + f->glDisable(GL_FRAMEBUFFER_SRGB); + } + break; + case QGles2CommandBuffer::Command::Clear: + f->glDisable(GL_SCISSOR_TEST); + if (cmd.args.clear.mask & GL_COLOR_BUFFER_BIT) { + f->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + f->glClearColor(cmd.args.clear.c[0], cmd.args.clear.c[1], cmd.args.clear.c[2], cmd.args.clear.c[3]); + } + if (cmd.args.clear.mask & GL_DEPTH_BUFFER_BIT) { + f->glDepthMask(GL_TRUE); + f->glClearDepthf(cmd.args.clear.d); + } + if (cmd.args.clear.mask & GL_STENCIL_BUFFER_BIT) + f->glClearStencil(cmd.args.clear.s); + f->glClear(cmd.args.clear.mask); + break; + case QGles2CommandBuffer::Command::BufferSubData: + f->glBindBuffer(cmd.args.bufferSubData.target, cmd.args.bufferSubData.buffer); + f->glBufferSubData(cmd.args.bufferSubData.target, cmd.args.bufferSubData.offset, cmd.args.bufferSubData.size, + cmd.args.bufferSubData.data); + break; + case QGles2CommandBuffer::Command::CopyTex: + { + GLuint fbo; + f->glGenFramebuffers(1, &fbo); + f->glBindFramebuffer(GL_FRAMEBUFFER, fbo); + f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + cmd.args.copyTex.srcFaceTarget, cmd.args.copyTex.srcTexture, cmd.args.copyTex.srcLevel); + f->glBindTexture(cmd.args.copyTex.dstTarget, cmd.args.copyTex.dstTexture); + f->glCopyTexSubImage2D(cmd.args.copyTex.dstFaceTarget, cmd.args.copyTex.dstLevel, + cmd.args.copyTex.dstX, cmd.args.copyTex.dstY, + cmd.args.copyTex.srcX, cmd.args.copyTex.srcY, + cmd.args.copyTex.w, cmd.args.copyTex.h); + f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject()); + f->glDeleteFramebuffers(1, &fbo); + } + break; + case QGles2CommandBuffer::Command::ReadPixels: + { + QRhiReadbackResult *result = cmd.args.readPixels.result; + GLuint tex = cmd.args.readPixels.texture; + GLuint fbo = 0; + if (tex) { + result->pixelSize = QSize(cmd.args.readPixels.w, cmd.args.readPixels.h); + result->format = cmd.args.readPixels.format; + f->glGenFramebuffers(1, &fbo); + f->glBindFramebuffer(GL_FRAMEBUFFER, fbo); + f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + cmd.args.readPixels.readTarget, cmd.args.readPixels.texture, cmd.args.readPixels.level); + } else { + result->pixelSize = currentSwapChain->pixelSize; + result->format = QRhiTexture::RGBA8; + // readPixels handles multisample resolving implicitly + } + result->data.resize(result->pixelSize.width() * result->pixelSize.height() * 4); + // With GLES (2.0?) GL_RGBA is the only mandated readback format, so stick with it. + f->glReadPixels(0, 0, result->pixelSize.width(), result->pixelSize.height(), + GL_RGBA, GL_UNSIGNED_BYTE, + result->data.data()); + if (fbo) { + f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject()); + f->glDeleteFramebuffers(1, &fbo); + } + if (result->completed) + result->completed(); + } + break; + case QGles2CommandBuffer::Command::SubImage: + f->glBindTexture(cmd.args.subImage.target, cmd.args.subImage.texture); + if (cmd.args.subImage.rowStartAlign != 4) + f->glPixelStorei(GL_UNPACK_ALIGNMENT, cmd.args.subImage.rowStartAlign); + f->glTexSubImage2D(cmd.args.subImage.faceTarget, cmd.args.subImage.level, + cmd.args.subImage.dx, cmd.args.subImage.dy, + cmd.args.subImage.w, cmd.args.subImage.h, + cmd.args.subImage.glformat, cmd.args.subImage.gltype, + cmd.args.subImage.data); + if (cmd.args.subImage.rowStartAlign != 4) + f->glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + break; + case QGles2CommandBuffer::Command::CompressedImage: + f->glBindTexture(cmd.args.compressedImage.target, cmd.args.compressedImage.texture); + f->glCompressedTexImage2D(cmd.args.compressedImage.faceTarget, cmd.args.compressedImage.level, + cmd.args.compressedImage.glintformat, + cmd.args.compressedImage.w, cmd.args.compressedImage.h, 0, + cmd.args.compressedImage.size, cmd.args.compressedImage.data); + break; + case QGles2CommandBuffer::Command::CompressedSubImage: + f->glBindTexture(cmd.args.compressedSubImage.target, cmd.args.compressedSubImage.texture); + f->glCompressedTexSubImage2D(cmd.args.compressedSubImage.faceTarget, cmd.args.compressedSubImage.level, + cmd.args.compressedSubImage.dx, cmd.args.compressedSubImage.dy, + cmd.args.compressedSubImage.w, cmd.args.compressedSubImage.h, + cmd.args.compressedSubImage.glintformat, + cmd.args.compressedSubImage.size, cmd.args.compressedSubImage.data); + break; + case QGles2CommandBuffer::Command::BlitFromRenderbuffer: + { + GLuint fbo[2]; + f->glGenFramebuffers(2, fbo); + f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo[0]); + f->glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_RENDERBUFFER, cmd.args.blitFromRb.renderbuffer); + f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[1]); + + f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromRb.target, + cmd.args.blitFromRb.texture, cmd.args.blitFromRb.dstLevel); + f->glBlitFramebuffer(0, 0, cmd.args.blitFromRb.w, cmd.args.blitFromRb.h, + 0, 0, cmd.args.blitFromRb.w, cmd.args.blitFromRb.h, + GL_COLOR_BUFFER_BIT, + GL_LINEAR); + f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject()); + } + break; + case QGles2CommandBuffer::Command::GenMip: + f->glBindTexture(cmd.args.genMip.target, cmd.args.genMip.texture); + f->glGenerateMipmap(cmd.args.genMip.target); + break; + default: + break; + } + } +} + +void QRhiGles2::executeBindGraphicsPipeline(QRhiGraphicsPipeline *ps) +{ + QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, ps); + + // No state tracking logic as of now. Could introduce something to reduce + // the number of gl* calls (when using and changing between multiple + // pipelines), but then begin/endExternal() should invalidate the cached + // state as appropriate. + + if (psD->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)) + f->glEnable(GL_SCISSOR_TEST); + else + f->glDisable(GL_SCISSOR_TEST); + if (psD->m_cullMode == QRhiGraphicsPipeline::None) { + f->glDisable(GL_CULL_FACE); + } else { + f->glEnable(GL_CULL_FACE); + f->glCullFace(toGlCullMode(psD->m_cullMode)); + } + f->glFrontFace(toGlFrontFace(psD->m_frontFace)); + if (!psD->m_targetBlends.isEmpty()) { + const QRhiGraphicsPipeline::TargetBlend &blend(psD->m_targetBlends.first()); // no MRT + GLboolean wr = blend.colorWrite.testFlag(QRhiGraphicsPipeline::R); + GLboolean wg = blend.colorWrite.testFlag(QRhiGraphicsPipeline::G); + GLboolean wb = blend.colorWrite.testFlag(QRhiGraphicsPipeline::B); + GLboolean wa = blend.colorWrite.testFlag(QRhiGraphicsPipeline::A); + f->glColorMask(wr, wg, wb, wa); + if (blend.enable) { + f->glEnable(GL_BLEND); + f->glBlendFuncSeparate(toGlBlendFactor(blend.srcColor), + toGlBlendFactor(blend.dstColor), + toGlBlendFactor(blend.srcAlpha), + toGlBlendFactor(blend.dstAlpha)); + f->glBlendEquationSeparate(toGlBlendOp(blend.opColor), toGlBlendOp(blend.opAlpha)); + } else { + f->glDisable(GL_BLEND); + } + } else { + f->glDisable(GL_BLEND); + } + if (psD->m_depthTest) + f->glEnable(GL_DEPTH_TEST); + else + f->glDisable(GL_DEPTH_TEST); + if (psD->m_depthWrite) + f->glDepthMask(GL_TRUE); + else + f->glDepthMask(GL_FALSE); + f->glDepthFunc(toGlCompareOp(psD->m_depthOp)); + if (psD->m_stencilTest) { + f->glEnable(GL_STENCIL_TEST); + f->glStencilFuncSeparate(GL_FRONT, toGlCompareOp(psD->m_stencilFront.compareOp), 0, psD->m_stencilReadMask); + f->glStencilOpSeparate(GL_FRONT, + toGlStencilOp(psD->m_stencilFront.failOp), + toGlStencilOp(psD->m_stencilFront.depthFailOp), + toGlStencilOp(psD->m_stencilFront.passOp)); + f->glStencilMaskSeparate(GL_FRONT, psD->m_stencilWriteMask); + f->glStencilFuncSeparate(GL_BACK, toGlCompareOp(psD->m_stencilBack.compareOp), 0, psD->m_stencilReadMask); + f->glStencilOpSeparate(GL_BACK, + toGlStencilOp(psD->m_stencilBack.failOp), + toGlStencilOp(psD->m_stencilBack.depthFailOp), + toGlStencilOp(psD->m_stencilBack.passOp)); + f->glStencilMaskSeparate(GL_BACK, psD->m_stencilWriteMask); + } else { + f->glDisable(GL_STENCIL_TEST); + } + + f->glUseProgram(psD->program); +} + +void QRhiGles2::bindShaderResources(QRhiGraphicsPipeline *ps, QRhiShaderResourceBindings *srb, + const uint *dynOfsPairs, int dynOfsCount) +{ + QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, ps); + QGles2ShaderResourceBindings *srbD = QRHI_RES(QGles2ShaderResourceBindings, srb); + int texUnit = 0; + + 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: + { + int viewOffset = b->u.ubuf.offset; + if (dynOfsCount) { + for (int j = 0; j < dynOfsCount; ++j) { + if (dynOfsPairs[2 * j] == uint(b->binding)) { + viewOffset = dynOfsPairs[2 * j + 1]; + break; + } + } + } + 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) { + if (uniform.binding == b->binding) { + // in a uniform buffer everything is at least 4 byte aligned + // so this should not cause unaligned reads + const void *src = bufView.constData() + uniform.offset; + + switch (uniform.type) { + case QShaderDescription::Float: + f->glUniform1f(uniform.glslLocation, *reinterpret_cast(src)); + break; + case QShaderDescription::Vec2: + f->glUniform2fv(uniform.glslLocation, 1, reinterpret_cast(src)); + break; + case QShaderDescription::Vec3: + f->glUniform3fv(uniform.glslLocation, 1, reinterpret_cast(src)); + break; + case QShaderDescription::Vec4: + f->glUniform4fv(uniform.glslLocation, 1, reinterpret_cast(src)); + break; + case QShaderDescription::Mat2: + f->glUniformMatrix2fv(uniform.glslLocation, 1, GL_FALSE, reinterpret_cast(src)); + break; + case QShaderDescription::Mat3: + f->glUniformMatrix3fv(uniform.glslLocation, 1, GL_FALSE, reinterpret_cast(src)); + break; + case QShaderDescription::Mat4: + f->glUniformMatrix4fv(uniform.glslLocation, 1, GL_FALSE, reinterpret_cast(src)); + break; + case QShaderDescription::Int: + f->glUniform1i(uniform.glslLocation, *reinterpret_cast(src)); + break; + case QShaderDescription::Int2: + f->glUniform2iv(uniform.glslLocation, 1, reinterpret_cast(src)); + break; + case QShaderDescription::Int3: + f->glUniform3iv(uniform.glslLocation, 1, reinterpret_cast(src)); + break; + case QShaderDescription::Int4: + f->glUniform4iv(uniform.glslLocation, 1, reinterpret_cast(src)); + break; + // ### more types + default: + break; + } + } + } + } + break; + case QRhiShaderResourceBinding::SampledTexture: + { + QGles2Texture *texD = QRHI_RES(QGles2Texture, b->u.stex.tex); + QGles2Sampler *samplerD = QRHI_RES(QGles2Sampler, b->u.stex.sampler); + + for (QGles2GraphicsPipeline::Sampler &sampler : psD->samplers) { + if (sampler.binding == b->binding) { + f->glActiveTexture(GL_TEXTURE0 + texUnit); + f->glBindTexture(texD->target, texD->texture); + + if (texD->samplerState != samplerD->d) { + f->glTexParameteri(texD->target, GL_TEXTURE_MIN_FILTER, samplerD->d.glminfilter); + f->glTexParameteri(texD->target, GL_TEXTURE_MAG_FILTER, samplerD->d.glmagfilter); + f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_S, samplerD->d.glwraps); + f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_T, samplerD->d.glwrapt); + // 3D textures not supported by GLES 2.0 or by us atm... + //f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_R, samplerD->d.glwrapr); + if (samplerD->d.gltexcomparefunc != GL_NEVER) { + f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_FUNC, samplerD->d.gltexcomparefunc); + } else { + f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_NONE); + } + texD->samplerState = samplerD->d; + } + + f->glUniform1i(sampler.glslLocation, texUnit); + ++texUnit; + } + } + } + break; + default: + Q_UNREACHABLE(); + break; + } + } + + if (texUnit > 1) + f->glActiveTexture(GL_TEXTURE0); +} + +void QRhiGles2::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) +{ + Q_ASSERT(inFrame && !inPass); + + enqueueResourceUpdates(cb, resourceUpdates); +} + +QGles2RenderTargetData *QRhiGles2::enqueueBindFramebuffer(QRhiRenderTarget *rt, QGles2CommandBuffer *cbD, + bool *wantsColorClear, bool *wantsDsClear) +{ + QGles2RenderTargetData *rtD = nullptr; + QGles2CommandBuffer::Command fbCmd; + fbCmd.cmd = QGles2CommandBuffer::Command::BindFramebuffer; + switch (rt->resourceType()) { + case QRhiResource::RenderTarget: + rtD = &QRHI_RES(QGles2ReferenceRenderTarget, rt)->d; + if (wantsColorClear) + *wantsColorClear = true; + if (wantsDsClear) + *wantsDsClear = true; + fbCmd.args.bindFramebuffer.fbo = 0; + fbCmd.args.bindFramebuffer.colorAttCount = 1; + break; + case QRhiResource::TextureRenderTarget: + { + QGles2TextureRenderTarget *rtTex = QRHI_RES(QGles2TextureRenderTarget, rt); + rtD = &rtTex->d; + if (wantsColorClear) + *wantsColorClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents); + if (wantsDsClear) + *wantsDsClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents); + fbCmd.args.bindFramebuffer.fbo = rtTex->framebuffer; + fbCmd.args.bindFramebuffer.colorAttCount = rtD->colorAttCount; + } + break; + default: + Q_UNREACHABLE(); + break; + } + fbCmd.args.bindFramebuffer.srgb = rtD->srgbUpdateAndBlend; + cbD->commands.append(fbCmd); + return rtD; +} + +void QRhiGles2::beginPass(QRhiCommandBuffer *cb, + QRhiRenderTarget *rt, + const QColor &colorClearValue, + const QRhiDepthStencilClearValue &depthStencilClearValue, + QRhiResourceUpdateBatch *resourceUpdates) +{ + Q_ASSERT(inFrame && !inPass); + + if (resourceUpdates) + enqueueResourceUpdates(cb, resourceUpdates); + + QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); + bool wantsColorClear, wantsDsClear; + QGles2RenderTargetData *rtD = enqueueBindFramebuffer(rt, cbD, &wantsColorClear, &wantsDsClear); + + QGles2CommandBuffer::Command clearCmd; + clearCmd.cmd = QGles2CommandBuffer::Command::Clear; + clearCmd.args.clear.mask = 0; + if (rtD->colorAttCount && wantsColorClear) + clearCmd.args.clear.mask |= GL_COLOR_BUFFER_BIT; + if (rtD->dsAttCount && wantsDsClear) + clearCmd.args.clear.mask |= GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; + clearCmd.args.clear.c[0] = colorClearValue.redF(); + clearCmd.args.clear.c[1] = colorClearValue.greenF(); + clearCmd.args.clear.c[2] = colorClearValue.blueF(); + clearCmd.args.clear.c[3] = colorClearValue.alphaF(); + clearCmd.args.clear.d = depthStencilClearValue.depthClearValue(); + clearCmd.args.clear.s = depthStencilClearValue.stencilClearValue(); + cbD->commands.append(clearCmd); + + cbD->currentTarget = rt; + + inPass = true; +} + +void QRhiGles2::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) +{ + Q_ASSERT(inPass); + inPass = false; + + QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); + if (cbD->currentTarget->resourceType() == QRhiResource::TextureRenderTarget) { + QGles2TextureRenderTarget *rtTex = QRHI_RES(QGles2TextureRenderTarget, cbD->currentTarget); + const QVector colorAttachments = rtTex->m_desc.colorAttachments(); + if (!colorAttachments.isEmpty()) { + // handle only 1 color attachment and only (msaa) renderbuffer + const QRhiColorAttachment &colorAtt(colorAttachments[0]); + if (colorAtt.resolveTexture()) { + Q_ASSERT(colorAtt.renderBuffer()); + QGles2RenderBuffer *rbD = QRHI_RES(QGles2RenderBuffer, colorAtt.renderBuffer()); + const QSize size = colorAtt.resolveTexture()->pixelSize(); + if (rbD->pixelSize() != size) { + qWarning("Resolve source (%dx%d) and target (%dx%d) size does not match", + rbD->pixelSize().width(), rbD->pixelSize().height(), size.width(), size.height()); + } + QGles2CommandBuffer::Command cmd; + cmd.cmd = QGles2CommandBuffer::Command::BlitFromRenderbuffer; + cmd.args.blitFromRb.renderbuffer = rbD->renderbuffer; + cmd.args.blitFromRb.w = size.width(); + cmd.args.blitFromRb.h = size.height(); + QGles2Texture *colorTexD = QRHI_RES(QGles2Texture, colorAtt.resolveTexture()); + const GLenum faceTargetBase = colorTexD->m_flags.testFlag(QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + : colorTexD->target; + cmd.args.blitFromRb.target = faceTargetBase + colorAtt.resolveLayer(); + cmd.args.blitFromRb.texture = colorTexD->texture; + cmd.args.blitFromRb.dstLevel = colorAtt.resolveLevel(); + cbD->commands.append(cmd); + } + } + } + + cbD->currentTarget = nullptr; + + if (resourceUpdates) + enqueueResourceUpdates(cb, resourceUpdates); +} + +QGles2Buffer::QGles2Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size) + : QRhiBuffer(rhi, type, usage, size) +{ +} + +QGles2Buffer::~QGles2Buffer() +{ + release(); +} + +void QGles2Buffer::release() +{ + if (!buffer) + return; + + QRhiGles2::DeferredReleaseEntry e; + e.type = QRhiGles2::DeferredReleaseEntry::Buffer; + + e.buffer.buffer = buffer; + + buffer = 0; + + QRHI_RES_RHI(QRhiGles2); + rhiD->releaseQueue.append(e); + QRHI_PROF; + QRHI_PROF_F(releaseBuffer(this)); + rhiD->unregisterResource(this); +} + +bool QGles2Buffer::build() +{ + if (buffer) + release(); + + QRHI_RES_RHI(QRhiGles2); + QRHI_PROF; + + if (m_usage.testFlag(QRhiBuffer::UniformBuffer)) { + if (m_usage.testFlag(QRhiBuffer::VertexBuffer) || m_usage.testFlag(QRhiBuffer::IndexBuffer)) { + 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)); + 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; + } + if (m_usage.testFlag(QRhiBuffer::IndexBuffer)) + target = GL_ELEMENT_ARRAY_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); + + QRHI_PROF_F(newBuffer(this, m_size, 1, 0)); + rhiD->registerResource(this); + return true; +} + +QGles2RenderBuffer::QGles2RenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize, + int sampleCount, QRhiRenderBuffer::Flags flags) + : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags) +{ +} + +QGles2RenderBuffer::~QGles2RenderBuffer() +{ + release(); +} + +void QGles2RenderBuffer::release() +{ + if (!renderbuffer) + return; + + QRhiGles2::DeferredReleaseEntry e; + e.type = QRhiGles2::DeferredReleaseEntry::RenderBuffer; + + e.renderbuffer.renderbuffer = renderbuffer; + e.renderbuffer.renderbuffer2 = stencilRenderbuffer; + + renderbuffer = 0; + stencilRenderbuffer = 0; + + QRHI_RES_RHI(QRhiGles2); + rhiD->releaseQueue.append(e); + QRHI_PROF; + QRHI_PROF_F(releaseRenderBuffer(this)); + rhiD->unregisterResource(this); +} + +bool QGles2RenderBuffer::build() +{ + if (renderbuffer) + release(); + + QRHI_RES_RHI(QRhiGles2); + QRHI_PROF; + samples = rhiD->effectiveSampleCount(m_sampleCount); + + if (m_flags.testFlag(UsedWithSwapChainOnly)) { + if (m_type == DepthStencil) { + QRHI_PROF_F(newRenderBuffer(this, false, true, samples)); + return true; + } + + qWarning("RenderBuffer: UsedWithSwapChainOnly is meaningless in combination with Color"); + } + + if (m_pixelSize.isEmpty()) + return false; + + if (!rhiD->ensureContext()) + return false; + + rhiD->f->glGenRenderbuffers(1, &renderbuffer); + rhiD->f->glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); + + switch (m_type) { + case QRhiRenderBuffer::DepthStencil: + if (rhiD->caps.msaaRenderBuffer && samples > 1) { + rhiD->f->glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_DEPTH24_STENCIL8, + m_pixelSize.width(), m_pixelSize.height()); + } else { + if (rhiD->caps.packedDepthStencil) { + rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, + m_pixelSize.width(), m_pixelSize.height()); + stencilRenderbuffer = 0; + } else { + rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_ATTACHMENT, + m_pixelSize.width(), m_pixelSize.height()); + rhiD->f->glGenRenderbuffers(1, &stencilRenderbuffer); + rhiD->f->glBindRenderbuffer(GL_RENDERBUFFER, stencilRenderbuffer); + rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_ATTACHMENT, + m_pixelSize.width(), m_pixelSize.height()); + } + } + QRHI_PROF_F(newRenderBuffer(this, false, false, samples)); + break; + case QRhiRenderBuffer::Color: + if (rhiD->caps.msaaRenderBuffer && samples > 1) + rhiD->f->glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_RGBA8, + m_pixelSize.width(), m_pixelSize.height()); + else + rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, + m_pixelSize.width(), m_pixelSize.height()); + QRHI_PROF_F(newRenderBuffer(this, false, false, samples)); + break; + default: + Q_UNREACHABLE(); + break; + } + + rhiD->registerResource(this); + return true; +} + +QRhiTexture::Format QGles2RenderBuffer::backingFormat() const +{ + return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat; +} + +QGles2Texture::QGles2Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, + int sampleCount, Flags flags) + : QRhiTexture(rhi, format, pixelSize, sampleCount, flags) +{ +} + +QGles2Texture::~QGles2Texture() +{ + release(); +} + +void QGles2Texture::release() +{ + if (!texture) + return; + + QRhiGles2::DeferredReleaseEntry e; + e.type = QRhiGles2::DeferredReleaseEntry::Texture; + + e.texture.texture = texture; + + texture = 0; + specified = false; + nativeHandlesStruct.texture = 0; + + QRHI_RES_RHI(QRhiGles2); + if (owns) + rhiD->releaseQueue.append(e); + QRHI_PROF; + QRHI_PROF_F(releaseTexture(this)); + rhiD->unregisterResource(this); +} + +static inline bool isPowerOfTwo(int x) +{ + // Assumption: x >= 1 + return x == (x & -x); +} + +bool QGles2Texture::prepareBuild(QSize *adjustedSize) +{ + if (texture) + release(); + + QRHI_RES_RHI(QRhiGles2); + if (!rhiD->ensureContext()) + return false; + + QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize; + if (!rhiD->caps.npotTexture && (!isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height()))) + size = QSize(qNextPowerOfTwo(size.width()), qNextPowerOfTwo(size.height())); + + const bool isCube = m_flags.testFlag(CubeMap); + const bool hasMipMaps = m_flags.testFlag(MipMapped); + const bool isCompressed = rhiD->isCompressedFormat(m_format); + + target = isCube ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D; + mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1; + gltype = GL_UNSIGNED_BYTE; + + if (isCompressed) { + glintformat = toGlCompressedTextureFormat(m_format, m_flags); + if (!glintformat) { + qWarning("Compressed format %d not mappable to GL compressed format", m_format); + return false; + } + glformat = GL_RGBA; + } else { + switch (m_format) { + case QRhiTexture::RGBA8: + glintformat = GL_RGBA; + glformat = GL_RGBA; + break; + case QRhiTexture::BGRA8: + glintformat = rhiD->caps.bgraInternalFormat ? GL_BGRA : GL_RGBA; + glformat = GL_BGRA; + break; + case QRhiTexture::R16: + glintformat = GL_R16; + glformat = GL_RED; + gltype = GL_UNSIGNED_SHORT; + break; + case QRhiTexture::R8: + glintformat = GL_R8; + glformat = GL_RED; + break; + case QRhiTexture::RED_OR_ALPHA8: + glintformat = rhiD->caps.coreProfile ? GL_R8 : GL_ALPHA; + glformat = rhiD->caps.coreProfile ? GL_RED : GL_ALPHA; + break; + case QRhiTexture::RGBA16F: + glintformat = GL_RGBA16F; + glformat = GL_RGBA; + gltype = GL_HALF_FLOAT; + break; + case QRhiTexture::RGBA32F: + glintformat = GL_RGBA32F; + glformat = GL_RGBA; + gltype = GL_FLOAT; + break; + case QRhiTexture::D16: + glintformat = GL_DEPTH_COMPONENT16; + glformat = GL_DEPTH_COMPONENT; + gltype = GL_UNSIGNED_SHORT; + break; + case QRhiTexture::D32F: + glintformat = GL_DEPTH_COMPONENT32F; + glformat = GL_DEPTH_COMPONENT; + gltype = GL_FLOAT; + break; + default: + Q_UNREACHABLE(); + glintformat = GL_RGBA; + glformat = GL_RGBA; + break; + } + } + + samplerState = QGles2SamplerData(); + + if (adjustedSize) + *adjustedSize = size; + + return true; +} + +bool QGles2Texture::build() +{ + QSize size; + if (!prepareBuild(&size)) + return false; + + QRHI_RES_RHI(QRhiGles2); + rhiD->f->glGenTextures(1, &texture); + + const bool isCube = m_flags.testFlag(CubeMap); + const bool hasMipMaps = m_flags.testFlag(MipMapped); + 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); + } + } + } else { + rhiD->f->glTexImage2D(target, 0, glintformat, size.width(), size.height(), 0, glformat, gltype, nullptr); + } + specified = true; + } else { + // Cannot use glCompressedTexImage2D without valid data, so defer. + // Compressed textures will not be used as render targets so this is + // not an issue. + specified = false; + } + + QRHI_PROF; + QRHI_PROF_F(newTexture(this, true, mipLevelCount, isCube ? 6 : 1, 1)); + + owns = true; + nativeHandlesStruct.texture = texture; + + generation += 1; + rhiD->registerResource(this); + return true; +} + +bool QGles2Texture::buildFrom(const QRhiNativeHandles *src) +{ + const QRhiGles2TextureNativeHandles *h = static_cast(src); + if (!h || !h->texture) + return false; + + if (!prepareBuild()) + return false; + + texture = h->texture; + specified = true; + + QRHI_RES_RHI(QRhiGles2); + QRHI_PROF; + QRHI_PROF_F(newTexture(this, false, mipLevelCount, m_flags.testFlag(CubeMap) ? 6 : 1, 1)); + + owns = false; + nativeHandlesStruct.texture = texture; + + generation += 1; + rhiD->registerResource(this); + return true; +} + +const QRhiNativeHandles *QGles2Texture::nativeHandles() +{ + return &nativeHandlesStruct; +} + +QGles2Sampler::QGles2Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode, + AddressMode u, AddressMode v) + : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v) +{ +} + +QGles2Sampler::~QGles2Sampler() +{ + release(); +} + +void QGles2Sampler::release() +{ + // nothing to do here +} + +bool QGles2Sampler::build() +{ + d.glminfilter = toGlMinFilter(m_minFilter, m_mipmapMode); + d.glmagfilter = toGlMagFilter(m_magFilter); + d.glwraps = toGlWrapMode(m_addressU); + d.glwrapt = toGlWrapMode(m_addressV); + d.glwrapr = toGlWrapMode(m_addressW); + d.gltexcomparefunc = toGlTextureCompareFunc(m_compareOp); + + generation += 1; + return true; +} + +// dummy, no Vulkan-style RenderPass+Framebuffer concept here +QGles2RenderPassDescriptor::QGles2RenderPassDescriptor(QRhiImplementation *rhi) + : QRhiRenderPassDescriptor(rhi) +{ +} + +QGles2RenderPassDescriptor::~QGles2RenderPassDescriptor() +{ + release(); +} + +void QGles2RenderPassDescriptor::release() +{ + // nothing to do here +} + +QGles2ReferenceRenderTarget::QGles2ReferenceRenderTarget(QRhiImplementation *rhi) + : QRhiRenderTarget(rhi), + d(rhi) +{ +} + +QGles2ReferenceRenderTarget::~QGles2ReferenceRenderTarget() +{ + release(); +} + +void QGles2ReferenceRenderTarget::release() +{ + // nothing to do here +} + +QSize QGles2ReferenceRenderTarget::pixelSize() const +{ + return d.pixelSize; +} + +float QGles2ReferenceRenderTarget::devicePixelRatio() const +{ + return d.dpr; +} + +int QGles2ReferenceRenderTarget::sampleCount() const +{ + return d.sampleCount; +} + +QGles2TextureRenderTarget::QGles2TextureRenderTarget(QRhiImplementation *rhi, + const QRhiTextureRenderTargetDescription &desc, + Flags flags) + : QRhiTextureRenderTarget(rhi, desc, flags), + d(rhi) +{ +} + +QGles2TextureRenderTarget::~QGles2TextureRenderTarget() +{ + release(); +} + +void QGles2TextureRenderTarget::release() +{ + if (!framebuffer) + return; + + QRhiGles2::DeferredReleaseEntry e; + e.type = QRhiGles2::DeferredReleaseEntry::TextureRenderTarget; + + e.textureRenderTarget.framebuffer = framebuffer; + + framebuffer = 0; + + QRHI_RES_RHI(QRhiGles2); + rhiD->releaseQueue.append(e); + + rhiD->unregisterResource(this); +} + +QRhiRenderPassDescriptor *QGles2TextureRenderTarget::newCompatibleRenderPassDescriptor() +{ + return new QGles2RenderPassDescriptor(m_rhi); +} + +bool QGles2TextureRenderTarget::build() +{ + QRHI_RES_RHI(QRhiGles2); + + if (framebuffer) + release(); + + const QVector colorAttachments = m_desc.colorAttachments(); + Q_ASSERT(!colorAttachments.isEmpty() || m_desc.depthTexture()); + Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture()); + const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture(); + + if (colorAttachments.count() > rhiD->caps.maxDrawBuffers) + qWarning("QGles2TextureRenderTarget: Too many color attachments (%d, max is %d)", + colorAttachments.count(), rhiD->caps.maxDrawBuffers); + if (m_desc.depthTexture() && !rhiD->caps.depthTexture) + qWarning("QGles2TextureRenderTarget: Depth texture is not supported and will be ignored"); + + if (!rhiD->ensureContext()) + return false; + + rhiD->f->glGenFramebuffers(1, &framebuffer); + rhiD->f->glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); + + d.colorAttCount = colorAttachments.count(); + for (int i = 0; i < d.colorAttCount; ++i) { + const QRhiColorAttachment &colorAtt(colorAttachments[i]); + QRhiTexture *texture = colorAtt.texture(); + QRhiRenderBuffer *renderBuffer = colorAtt.renderBuffer(); + Q_ASSERT(texture || renderBuffer); + if (texture) { + QGles2Texture *texD = QRHI_RES(QGles2Texture, texture); + Q_ASSERT(texD->texture && texD->specified); + const GLenum faceTargetBase = texD->flags().testFlag(QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target; + rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, faceTargetBase + colorAtt.layer(), texD->texture, colorAtt.level()); + if (i == 0) { + d.pixelSize = texD->pixelSize(); + d.sampleCount = 1; + } + } else if (renderBuffer) { + QGles2RenderBuffer *rbD = QRHI_RES(QGles2RenderBuffer, renderBuffer); + rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_RENDERBUFFER, rbD->renderbuffer); + if (i == 0) { + d.pixelSize = rbD->pixelSize(); + d.sampleCount = rbD->samples; + } + } + } + + if (hasDepthStencil) { + if (m_desc.depthStencilBuffer()) { + QGles2RenderBuffer *depthRbD = QRHI_RES(QGles2RenderBuffer, m_desc.depthStencilBuffer()); + rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRbD->renderbuffer); + if (depthRbD->stencilRenderbuffer) + rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthRbD->stencilRenderbuffer); + else // packed + rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthRbD->renderbuffer); + if (d.colorAttCount == 0) { + d.pixelSize = depthRbD->pixelSize(); + d.sampleCount = depthRbD->samples; + } + } else { + QGles2Texture *depthTexD = QRHI_RES(QGles2Texture, m_desc.depthTexture()); + rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexD->texture, 0); + if (d.colorAttCount == 0) { + d.pixelSize = depthTexD->pixelSize(); + d.sampleCount = 1; + } + } + d.dsAttCount = 1; + } else { + d.dsAttCount = 0; + } + + d.dpr = 1; + d.rp = QRHI_RES(QGles2RenderPassDescriptor, m_renderPassDesc); + + GLenum status = rhiD->f->glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_NO_ERROR && status != GL_FRAMEBUFFER_COMPLETE) { + qWarning("Framebuffer incomplete: 0x%x", status); + return false; + } + + rhiD->registerResource(this); + return true; +} + +QSize QGles2TextureRenderTarget::pixelSize() const +{ + return d.pixelSize; +} + +float QGles2TextureRenderTarget::devicePixelRatio() const +{ + return d.dpr; +} + +int QGles2TextureRenderTarget::sampleCount() const +{ + return d.sampleCount; +} + +QGles2ShaderResourceBindings::QGles2ShaderResourceBindings(QRhiImplementation *rhi) + : QRhiShaderResourceBindings(rhi) +{ +} + +QGles2ShaderResourceBindings::~QGles2ShaderResourceBindings() +{ + release(); +} + +void QGles2ShaderResourceBindings::release() +{ + // nothing to do here +} + +bool QGles2ShaderResourceBindings::build() +{ + generation += 1; + return true; +} + +QGles2GraphicsPipeline::QGles2GraphicsPipeline(QRhiImplementation *rhi) + : QRhiGraphicsPipeline(rhi) +{ +} + +QGles2GraphicsPipeline::~QGles2GraphicsPipeline() +{ + release(); +} + +void QGles2GraphicsPipeline::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 QGles2GraphicsPipeline::build() +{ + QRHI_RES_RHI(QRhiGles2); + + if (program) + release(); + + if (!rhiD->ensureContext()) + return false; + + drawMode = toGlTopology(m_topology); + + program = rhiD->f->glCreateProgram(); + + int sourceVer = 0; + for (const QRhiGraphicsShaderStage &shaderStage : qAsConst(m_shaderStages)) { + const bool isVertex = shaderStage.type() == QRhiGraphicsShaderStage::Vertex; + const bool isFragment = shaderStage.type() == QRhiGraphicsShaderStage::Fragment; + if (!isVertex && !isFragment) + continue; + + GLuint shader = rhiD->f->glCreateShader(isVertex ? GL_VERTEX_SHADER : GL_FRAGMENT_SHADER); + const QShader bakedShader = shaderStage.shader(); + QVector 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; + } + + 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()); + 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); + + 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); + } + }; + + for (const QShaderDescription::InOutVariable &v : vsDesc.combinedImageSamplers()) + lookupSamplers(v); + + for (const QShaderDescription::InOutVariable &v : fsDesc.combinedImageSamplers()) + lookupSamplers(v); + + generation += 1; + rhiD->registerResource(this); + return true; +} + +QGles2CommandBuffer::QGles2CommandBuffer(QRhiImplementation *rhi) + : QRhiCommandBuffer(rhi) +{ + resetState(); +} + +QGles2CommandBuffer::~QGles2CommandBuffer() +{ + release(); +} + +void QGles2CommandBuffer::release() +{ + // nothing to do here +} + +QGles2SwapChain::QGles2SwapChain(QRhiImplementation *rhi) + : QRhiSwapChain(rhi), + rt(rhi), + cb(rhi) +{ +} + +QGles2SwapChain::~QGles2SwapChain() +{ + release(); +} + +void QGles2SwapChain::release() +{ + QRHI_PROF; + QRHI_PROF_F(releaseSwapChain(this)); +} + +QRhiCommandBuffer *QGles2SwapChain::currentFrameCommandBuffer() +{ + return &cb; +} + +QRhiRenderTarget *QGles2SwapChain::currentFrameRenderTarget() +{ + return &rt; +} + +QSize QGles2SwapChain::surfacePixelSize() +{ + Q_ASSERT(m_window); + return m_window->size() * m_window->devicePixelRatio(); +} + +QRhiRenderPassDescriptor *QGles2SwapChain::newCompatibleRenderPassDescriptor() +{ + return new QGles2RenderPassDescriptor(m_rhi); +} + +bool QGles2SwapChain::buildOrResize() +{ + surface = m_window; + m_currentPixelSize = surfacePixelSize(); + pixelSize = m_currentPixelSize; + + rt.d.rp = QRHI_RES(QGles2RenderPassDescriptor, m_renderPassDesc); + rt.d.pixelSize = pixelSize; + rt.d.dpr = m_window->devicePixelRatio(); + rt.d.sampleCount = qBound(1, m_sampleCount, 64); + rt.d.colorAttCount = 1; + rt.d.dsAttCount = m_depthStencil ? 1 : 0; + rt.d.srgbUpdateAndBlend = m_flags.testFlag(QRhiSwapChain::sRGB); + + frameCount = 0; + + QRHI_PROF; + // make something up + QRHI_PROF_F(resizeSwapChain(this, 2, m_sampleCount > 1 ? 2 : 0, m_sampleCount)); + + return true; +} + +QT_END_NAMESPACE diff --git a/src/gui/rhi/qrhigles2_p.h b/src/gui/rhi/qrhigles2_p.h new file mode 100644 index 0000000000..7f7c8b4c40 --- /dev/null +++ b/src/gui/rhi/qrhigles2_p.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Gui module +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QRHIGLES2_H +#define QRHIGLES2_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +class QOpenGLContext; +class QOffscreenSurface; +class QWindow; + +struct Q_GUI_EXPORT QRhiGles2InitParams : public QRhiInitParams +{ + QRhiGles2InitParams(); + + QSurfaceFormat format; + QOffscreenSurface *fallbackSurface = nullptr; + QWindow *window = nullptr; + + static QOffscreenSurface *newFallbackSurface(const QSurfaceFormat &format = QSurfaceFormat::defaultFormat()); + static QSurfaceFormat adjustedFormat(const QSurfaceFormat &format = QSurfaceFormat::defaultFormat()); +}; + +struct Q_GUI_EXPORT QRhiGles2NativeHandles : public QRhiNativeHandles +{ + QOpenGLContext *context = nullptr; +}; + +struct Q_GUI_EXPORT QRhiGles2TextureNativeHandles : public QRhiNativeHandles +{ + uint texture = 0; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h new file mode 100644 index 0000000000..d1a8a1fadf --- /dev/null +++ b/src/gui/rhi/qrhigles2_p_p.h @@ -0,0 +1,695 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Gui module +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QRHIGLES2_P_H +#define QRHIGLES2_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qrhigles2_p.h" +#include "qrhi_p_p.h" +#include "qshaderdescription_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + +class QOpenGLExtensions; + +struct QGles2Buffer : public QRhiBuffer +{ + QGles2Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size); + ~QGles2Buffer(); + void release() override; + bool build() override; + + GLuint buffer = 0; + GLenum target; + QByteArray ubuf; + friend class QRhiGles2; +}; + +struct QGles2RenderBuffer : public QRhiRenderBuffer +{ + QGles2RenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize, + int sampleCount, QRhiRenderBuffer::Flags flags); + ~QGles2RenderBuffer(); + void release() override; + bool build() override; + QRhiTexture::Format backingFormat() const override; + + GLuint renderbuffer = 0; + GLuint stencilRenderbuffer = 0; // when packed depth-stencil not supported + int samples; + friend class QRhiGles2; +}; + +struct QGles2SamplerData +{ + GLenum glminfilter = 0; + GLenum glmagfilter = 0; + GLenum glwraps = 0; + GLenum glwrapt = 0; + GLenum glwrapr = 0; + GLenum gltexcomparefunc = 0; +}; + +inline bool operator==(const QGles2SamplerData &a, const QGles2SamplerData &b) +{ + return a.glminfilter == b.glminfilter + && a.glmagfilter == b.glmagfilter + && a.glwraps == b.glwraps + && a.glwrapt == b.glwrapt + && a.glwrapr == b.glwrapr + && a.gltexcomparefunc == b.gltexcomparefunc; +} + +inline bool operator!=(const QGles2SamplerData &a, const QGles2SamplerData &b) +{ + return !(a == b); +} + +struct QGles2Texture : public QRhiTexture +{ + QGles2Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, + int sampleCount, Flags flags); + ~QGles2Texture(); + void release() override; + bool build() override; + bool buildFrom(const QRhiNativeHandles *src) override; + const QRhiNativeHandles *nativeHandles() override; + + bool prepareBuild(QSize *adjustedSize = nullptr); + + GLuint texture = 0; + bool owns = true; + GLenum target; + GLenum glintformat; + GLenum glformat; + GLenum gltype; + QGles2SamplerData samplerState; + bool specified = false; + int mipLevelCount = 0; + QRhiGles2TextureNativeHandles nativeHandlesStruct; + + uint generation = 0; + friend class QRhiGles2; +}; + +struct QGles2Sampler : public QRhiSampler +{ + QGles2Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode, + AddressMode u, AddressMode v); + ~QGles2Sampler(); + void release() override; + bool build() override; + + QGles2SamplerData d; + uint generation = 0; + friend class QRhiGles2; +}; + +struct QGles2RenderPassDescriptor : public QRhiRenderPassDescriptor +{ + QGles2RenderPassDescriptor(QRhiImplementation *rhi); + ~QGles2RenderPassDescriptor(); + void release() override; +}; + +struct QGles2RenderTargetData +{ + QGles2RenderTargetData(QRhiImplementation *) { } + + QGles2RenderPassDescriptor *rp = nullptr; + QSize pixelSize; + float dpr = 1; + int sampleCount = 1; + int colorAttCount = 0; + int dsAttCount = 0; + bool srgbUpdateAndBlend = false; +}; + +struct QGles2ReferenceRenderTarget : public QRhiRenderTarget +{ + QGles2ReferenceRenderTarget(QRhiImplementation *rhi); + ~QGles2ReferenceRenderTarget(); + void release() override; + + QSize pixelSize() const override; + float devicePixelRatio() const override; + int sampleCount() const override; + + QGles2RenderTargetData d; +}; + +struct QGles2TextureRenderTarget : public QRhiTextureRenderTarget +{ + QGles2TextureRenderTarget(QRhiImplementation *rhi, const QRhiTextureRenderTargetDescription &desc, Flags flags); + ~QGles2TextureRenderTarget(); + void release() override; + + QSize pixelSize() const override; + float devicePixelRatio() const override; + int sampleCount() const override; + + QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override; + bool build() override; + + QGles2RenderTargetData d; + GLuint framebuffer = 0; + friend class QRhiGles2; +}; + +struct QGles2ShaderResourceBindings : public QRhiShaderResourceBindings +{ + QGles2ShaderResourceBindings(QRhiImplementation *rhi); + ~QGles2ShaderResourceBindings(); + void release() override; + bool build() override; + + uint generation = 0; + friend class QRhiGles2; +}; + +struct QGles2GraphicsPipeline : public QRhiGraphicsPipeline +{ + QGles2GraphicsPipeline(QRhiImplementation *rhi); + ~QGles2GraphicsPipeline(); + void release() override; + bool build() override; + + 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 uniforms; + + struct Sampler { + int glslLocation; + int binding; + }; + QVector samplers; + + uint generation = 0; + friend class QRhiGles2; +}; + +Q_DECLARE_TYPEINFO(QGles2GraphicsPipeline::Uniform, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(QGles2GraphicsPipeline::Sampler, Q_MOVABLE_TYPE); + +struct QGles2CommandBuffer : public QRhiCommandBuffer +{ + QGles2CommandBuffer(QRhiImplementation *rhi); + ~QGles2CommandBuffer(); + void release() override; + + struct Command { + enum Cmd { + BeginFrame, + EndFrame, + Viewport, + Scissor, + BlendConstants, + StencilRef, + BindVertexBuffer, + BindIndexBuffer, + Draw, + DrawIndexed, + BindGraphicsPipeline, + BindShaderResources, + BindFramebuffer, + Clear, + BufferData, + BufferSubData, + CopyTex, + ReadPixels, + SubImage, + CompressedImage, + CompressedSubImage, + BlitFromRenderbuffer, + GenMip + }; + Cmd cmd; + + static const int MAX_UBUF_BINDINGS = 32; // should be more than enough + + // QRhi*/QGles2* references should be kept at minimum (so no + // QRhiTexture/Buffer/etc. pointers). + union { + struct { + float x, y, w, h; + float d0, d1; + } viewport; + struct { + int x, y, w, h; + } scissor; + struct { + float r, g, b, a; + } blendConstants; + struct { + quint32 ref; + QRhiGraphicsPipeline *ps; + } stencilRef; + struct { + QRhiGraphicsPipeline *ps; + GLuint buffer; + quint32 offset; + int binding; + } bindVertexBuffer; + struct { + GLuint buffer; + quint32 offset; + GLenum type; + } bindIndexBuffer; + struct { + QRhiGraphicsPipeline *ps; + quint32 vertexCount; + quint32 firstVertex; + } draw; + struct { + QRhiGraphicsPipeline *ps; + quint32 indexCount; + quint32 firstIndex; + } drawIndexed; + struct { + QRhiGraphicsPipeline *ps; + } bindGraphicsPipeline; + struct { + QRhiGraphicsPipeline *ps; + QRhiShaderResourceBindings *srb; + int dynamicOffsetCount; + uint dynamicOffsetPairs[MAX_UBUF_BINDINGS * 2]; // binding, offsetInConstants + } bindShaderResources; + struct { + GLbitfield mask; + float c[4]; + float d; + quint32 s; + } clear; + struct { + GLuint fbo; + bool srgb; + int colorAttCount; + } bindFramebuffer; + struct { + GLenum target; + GLuint buffer; + int offset; + int size; + const void *data; // must come from retainData() + } bufferSubData; + struct { + GLenum srcFaceTarget; + GLuint srcTexture; + int srcLevel; + int srcX; + int srcY; + GLenum dstTarget; + GLuint dstTexture; + GLenum dstFaceTarget; + int dstLevel; + int dstX; + int dstY; + int w; + int h; + } copyTex; + struct { + QRhiReadbackResult *result; + GLuint texture; + int w; + int h; + QRhiTexture::Format format; + GLenum readTarget; + int level; + } readPixels; + struct { + GLenum target; + GLuint texture; + GLenum faceTarget; + int level; + int dx; + int dy; + int w; + int h; + GLenum glformat; + GLenum gltype; + int rowStartAlign; + const void *data; // must come from retainImage() + } subImage; + struct { + GLenum target; + GLuint texture; + GLenum faceTarget; + int level; + GLenum glintformat; + int w; + int h; + int size; + const void *data; // must come from retainData() + } compressedImage; + struct { + GLenum target; + GLuint texture; + GLenum faceTarget; + int level; + int dx; + int dy; + int w; + int h; + GLenum glintformat; + int size; + const void *data; // must come from retainData() + } compressedSubImage; + struct { + GLuint renderbuffer; + int w; + int h; + GLenum target; + GLuint texture; + int dstLevel; + } blitFromRb; + struct { + GLenum target; + GLuint texture; + } genMip; + } args; + }; + + QVector commands; + QRhiRenderTarget *currentTarget; + QRhiGraphicsPipeline *currentPipeline; + uint currentPipelineGeneration; + QRhiShaderResourceBindings *currentSrb; + uint currentSrbGeneration; + + QVector dataRetainPool; + QVector imageRetainPool; + + // relies heavily on implicit sharing (no copies of the actual data will be made) + const void *retainData(const QByteArray &data) { + dataRetainPool.append(data); + return dataRetainPool.constLast().constData(); + } + const void *retainImage(const QImage &image) { + imageRetainPool.append(image); + return imageRetainPool.constLast().constBits(); + } + void resetCommands() { + commands.clear(); + dataRetainPool.clear(); + imageRetainPool.clear(); + } + void resetState() { + resetCommands(); + currentTarget = nullptr; + resetCachedState(); + } + void resetCachedState() { + currentPipeline = nullptr; + currentPipelineGeneration = 0; + currentSrb = nullptr; + currentSrbGeneration = 0; + } +}; + +Q_DECLARE_TYPEINFO(QGles2CommandBuffer::Command, Q_MOVABLE_TYPE); + +struct QGles2SwapChain : public QRhiSwapChain +{ + QGles2SwapChain(QRhiImplementation *rhi); + ~QGles2SwapChain(); + void release() override; + + QRhiCommandBuffer *currentFrameCommandBuffer() override; + QRhiRenderTarget *currentFrameRenderTarget() override; + + QSize surfacePixelSize() override; + + QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override; + bool buildOrResize() override; + + QSurface *surface = nullptr; + QSize pixelSize; + QGles2ReferenceRenderTarget rt; + QGles2CommandBuffer cb; + int frameCount = 0; +}; + +class QRhiGles2 : public QRhiImplementation +{ +public: + QRhiGles2(QRhiGles2InitParams *params, QRhiGles2NativeHandles *importDevice = nullptr); + + bool create(QRhi::Flags flags) override; + void destroy() override; + + QRhiGraphicsPipeline *createGraphicsPipeline() override; + QRhiShaderResourceBindings *createShaderResourceBindings() override; + QRhiBuffer *createBuffer(QRhiBuffer::Type type, + QRhiBuffer::UsageFlags usage, + int size) override; + QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type, + const QSize &pixelSize, + int sampleCount, + QRhiRenderBuffer::Flags flags) override; + QRhiTexture *createTexture(QRhiTexture::Format format, + const QSize &pixelSize, + int sampleCount, + QRhiTexture::Flags flags) override; + QRhiSampler *createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, + QRhiSampler::Filter mipmapMode, + QRhiSampler:: AddressMode u, QRhiSampler::AddressMode v) override; + + QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc, + QRhiTextureRenderTarget::Flags flags) override; + + QRhiSwapChain *createSwapChain() override; + QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override; + QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override; + QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override; + QRhi::FrameOpResult endOffscreenFrame() override; + QRhi::FrameOpResult finish() override; + + void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override; + + void beginPass(QRhiCommandBuffer *cb, + QRhiRenderTarget *rt, + const QColor &colorClearValue, + const QRhiDepthStencilClearValue &depthStencilClearValue, + QRhiResourceUpdateBatch *resourceUpdates) override; + void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override; + + void setGraphicsPipeline(QRhiCommandBuffer *cb, + QRhiGraphicsPipeline *ps) override; + + void setShaderResources(QRhiCommandBuffer *cb, + QRhiShaderResourceBindings *srb, + int dynamicOffsetCount, + const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) override; + + void setVertexInput(QRhiCommandBuffer *cb, + int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings, + QRhiBuffer *indexBuf, quint32 indexOffset, + QRhiCommandBuffer::IndexFormat indexFormat) override; + + void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) override; + void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override; + void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override; + void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override; + + void draw(QRhiCommandBuffer *cb, quint32 vertexCount, + quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override; + + void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, + quint32 instanceCount, quint32 firstIndex, + qint32 vertexOffset, quint32 firstInstance) override; + + void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) override; + void debugMarkEnd(QRhiCommandBuffer *cb) override; + void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) override; + + const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) override; + void beginExternal(QRhiCommandBuffer *cb) override; + void endExternal(QRhiCommandBuffer *cb) override; + + QVector supportedSampleCounts() const override; + int ubufAlignment() const override; + bool isYUpInFramebuffer() const override; + bool isYUpInNDC() const override; + bool isClipDepthZeroToOne() const override; + QMatrix4x4 clipSpaceCorrMatrix() const override; + bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const override; + bool isFeatureSupported(QRhi::Feature feature) const override; + int resourceLimit(QRhi::ResourceLimit limit) const override; + const QRhiNativeHandles *nativeHandles() override; + void sendVMemStatsToProfiler() override; + + bool ensureContext(QSurface *surface = nullptr) const; + void executeDeferredReleases(); + QRhi::FrameOpResult flushCommandBuffer(); + void enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cbD, + int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc); + void enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates); + void executeCommandBuffer(QRhiCommandBuffer *cb); + void executeBindGraphicsPipeline(QRhiGraphicsPipeline *ps); + void bindShaderResources(QRhiGraphicsPipeline *ps, QRhiShaderResourceBindings *srb, + const uint *dynOfsPairs, int dynOfsCount); + QGles2RenderTargetData *enqueueBindFramebuffer(QRhiRenderTarget *rt, QGles2CommandBuffer *cbD, + bool *wantsColorClear = nullptr, bool *wantsDsClear = nullptr); + int effectiveSampleCount(int sampleCount) const; + + QOpenGLContext *ctx = nullptr; + bool importedContext = false; + QSurfaceFormat requestedFormat; + QSurface *fallbackSurface = nullptr; + QWindow *maybeWindow = nullptr; + mutable bool needsMakeCurrent = false; + QOpenGLExtensions *f = nullptr; + uint vao = 0; + struct Caps { + Caps() + : ctxMajor(2), + ctxMinor(0), + maxTextureSize(2048), + maxDrawBuffers(4), + msaaRenderBuffer(false), + npotTexture(true), + npotTextureRepeat(true), + gles(false), + fixedIndexPrimitiveRestart(false), + bgraExternalFormat(false), + bgraInternalFormat(false), + r8Format(false), + r16Format(false), + floatFormats(false), + depthTexture(false), + packedDepthStencil(false), + srgbCapableDefaultFramebuffer(false), + coreProfile(false), + uniformBuffers(false), + elementIndexUint(false) + { } + int ctxMajor; + int ctxMinor; + int maxTextureSize; + int maxDrawBuffers; + int maxSamples; + // Multisample fb and blit are supported (GLES 3.0 or OpenGL 3.x). Not + // the same as multisample textures! + uint msaaRenderBuffer : 1; + uint npotTexture : 1; + uint npotTextureRepeat : 1; + uint gles : 1; + uint fixedIndexPrimitiveRestart : 1; + uint bgraExternalFormat : 1; + uint bgraInternalFormat : 1; + uint r8Format : 1; + uint r16Format : 1; + uint floatFormats : 1; + uint depthTexture : 1; + uint packedDepthStencil : 1; + uint srgbCapableDefaultFramebuffer : 1; + uint coreProfile : 1; + uint uniformBuffers : 1; + uint elementIndexUint : 1; + } caps; + bool inFrame = false; + bool inPass = false; + QGles2SwapChain *currentSwapChain = nullptr; + QVector supportedCompressedFormats; + mutable QVector supportedSampleCountList; + QRhiGles2NativeHandles nativeHandlesStruct; + + struct DeferredReleaseEntry { + enum Type { + Buffer, + Pipeline, + Texture, + RenderBuffer, + TextureRenderTarget + }; + Type type; + union { + struct { + GLuint buffer; + } buffer; + struct { + GLuint program; + } pipeline; + struct { + GLuint texture; + } texture; + struct { + GLuint renderbuffer; + GLuint renderbuffer2; + } renderbuffer; + struct { + GLuint framebuffer; + } textureRenderTarget; + }; + }; + QVector releaseQueue; + + struct OffscreenFrame { + OffscreenFrame(QRhiImplementation *rhi) : cbWrapper(rhi) { } + bool active = false; + QGles2CommandBuffer cbWrapper; + } ofr; +}; + +Q_DECLARE_TYPEINFO(QRhiGles2::DeferredReleaseEntry, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm new file mode 100644 index 0000000000..8739a42738 --- /dev/null +++ b/src/gui/rhi/qrhimetal.mm @@ -0,0 +1,3249 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Gui module +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qrhimetal_p_p.h" +#include "qshader_p.h" +#include +#include +#include + +#ifdef Q_OS_MACOS +#include +#endif + +#include +#include + +QT_BEGIN_NAMESPACE + +/* + Metal backend. Double buffers and throttles to vsync. "Dynamic" buffers are + Shared (host visible) and duplicated (due to 2 frames in flight), "static" + are Managed on macOS and Shared on iOS/tvOS, and still duplicated. + "Immutable" is like "static" but with only one native buffer underneath. + 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 (hence uses commandBufferWithUnretainedReferences), but + does rely on automatic dependency tracking between encoders (hence no + MTLResourceHazardTrackingModeUntracked atm). +*/ + +#if __has_feature(objc_arc) +#error ARC not supported +#endif + +// Note: we expect everything here pass the Metal API validation when running +// in Debug mode in XCode. Some of the issues that break validation are not +// obvious and not visible when running outside XCode. +// +// An exception is the nextDrawable Called Early blah blah warning, which is +// plain and simply false. + +/*! + \class QRhiMetalInitParams + \inmodule QtRhi + \brief Metal specific initialization parameters. + + A Metal-based QRhi needs no special parameters for initialization. + + \badcode + QRhiMetalInitParams params; + rhi = QRhi::create(QRhi::Metal, ¶ms); + \endcode + + \note Metal API validation cannot be enabled by the application. Instead, + run the debug build of the application in XCode. Generating a + \c{.xcodeproj} file via \c{qmake -spec macx-xcode} provides a convenient + way to enable this. + + \note QRhiSwapChain can only target QWindow instances that have their + surface type set to QSurface::MetalSurface. + + \section2 Working with existing Metal devices + + When interoperating with another graphics engine, it may be necessary to + get a QRhi instance that uses the same Metal device. This can be achieved + by passing a pointer to a QRhiMetalNativeHandles to QRhi::create(). The + device must be set to a non-null value then. Optionally, a command queue + object can be specified as well. + + The QRhi does not take ownership of any of the external objects. + */ + +/*! + \class QRhiMetalNativeHandles + \inmodule QtRhi + \brief Holds the Metal device used by the QRhi. + + \note The class uses \c{void *} as the type since including the Objective C + headers is not acceptable here. The actual types are \c{id} and + \c{id}. + */ + +/*! + \class QRhiMetalTextureNativeHandles + \inmodule QtRhi + \brief Holds the Metal texture object that is backing a QRhiTexture instance. + + \note The class uses \c{void *} as the type since including the Objective C + headers is not acceptable here. The actual type is \c{id}. + */ + +/*! + \class QRhiMetalCommandBufferNativeHandles + \inmodule QtRhi + \brief Holds the MTLCommandBuffer and MTLRenderCommandEncoder objects that are backing a QRhiCommandBuffer. + + \note The command buffer object is only guaranteed to be valid while + recording a frame, that is, between a \l{QRhi::beginFrame()}{beginFrame()} + - \l{QRhi::endFrame()}{endFrame()} or + \l{QRhi::beginOffscreenFrame()}{beginOffscreenFrame()} - + \l{QRhi::endOffsrceenFrame()}{endOffscreenFrame()} pair. + + \note The command encoder is only valid while recording a pass, that is, + between \l{QRhiCommandBuffer::beginPass()} - + \l{QRhiCommandBuffer::endPass()}. + */ + +struct QRhiMetalData +{ + QRhiMetalData(QRhiImplementation *rhi) : ofr(rhi) { } + + id dev = nil; + id cmdQueue = nil; + + MTLRenderPassDescriptor *createDefaultRenderPass(bool hasDepthStencil, + const QColor &colorClearValue, + const QRhiDepthStencilClearValue &depthStencilClearValue, + int colorAttCount); + id createMetalLib(const QShader &shader, QShader::Variant shaderVariant, + QString *error, QByteArray *entryPoint); + id createMSLShaderFunction(id lib, const QByteArray &entryPoint); + + struct DeferredReleaseEntry { + enum Type { + Buffer, + RenderBuffer, + Texture, + Sampler, + StagingBuffer + }; + Type type; + int lastActiveFrameSlot; // -1 if not used otherwise 0..FRAMES_IN_FLIGHT-1 + union { + struct { + id buffers[QMTL_FRAMES_IN_FLIGHT]; + } buffer; + struct { + id texture; + } renderbuffer; + struct { + id texture; + id stagingBuffers[QMTL_FRAMES_IN_FLIGHT]; + } texture; + struct { + id samplerState; + } sampler; + struct { + id buffer; + } stagingBuffer; + }; + }; + QVector releaseQueue; + + struct OffscreenFrame { + OffscreenFrame(QRhiImplementation *rhi) : cbWrapper(rhi) { } + bool active = false; + QMetalCommandBuffer cbWrapper; + } ofr; + + struct ActiveReadback { + int activeFrameSlot = -1; + QRhiReadbackDescription desc; + QRhiReadbackResult *result; + id buf; + quint32 bufSize; + QSize pixelSize; + QRhiTexture::Format format; + }; + QVector activeReadbacks; + + API_AVAILABLE(macos(10.13), ios(11.0)) MTLCaptureManager *captureMgr; + API_AVAILABLE(macos(10.13), ios(11.0)) id captureScope = nil; + + static const int TEXBUF_ALIGN = 256; // probably not accurate +}; + +Q_DECLARE_TYPEINFO(QRhiMetalData::DeferredReleaseEntry, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(QRhiMetalData::ActiveReadback, Q_MOVABLE_TYPE); + +struct QMetalBufferData +{ + bool managed; + id buf[QMTL_FRAMES_IN_FLIGHT]; + QVector pendingUpdates[QMTL_FRAMES_IN_FLIGHT]; +}; + +struct QMetalRenderBufferData +{ + MTLPixelFormat format; + id tex = nil; +}; + +struct QMetalTextureData +{ + MTLPixelFormat format; + id tex = nil; + id stagingBuf[QMTL_FRAMES_IN_FLIGHT]; + bool owns = true; +}; + +struct QMetalSamplerData +{ + id samplerState = nil; +}; + +struct QMetalCommandBufferData +{ + id cb; + id currentPassEncoder; + MTLRenderPassDescriptor *currentPassRpDesc; + int currentFirstVertexBinding; + QRhiBatchedBindings > currentVertexInputsBuffers; + QRhiBatchedBindings currentVertexInputOffsets; +}; + +struct QMetalRenderTargetData +{ + QSize pixelSize; + float dpr = 1; + int sampleCount = 1; + int colorAttCount = 0; + int dsAttCount = 0; + + struct ColorAtt { + bool needsDrawableForTex = false; + id tex = nil; + int layer = 0; + int level = 0; + bool needsDrawableForResolveTex = false; + id resolveTex = nil; + int resolveLayer = 0; + int resolveLevel = 0; + }; + + struct { + ColorAtt colorAtt[QMetalRenderPassDescriptor::MAX_COLOR_ATTACHMENTS]; + id dsTex = nil; + bool hasStencil = false; + bool depthNeedsStore = false; + } fb; +}; + +struct QMetalGraphicsPipelineData +{ + id ps = nil; + id ds = nil; + MTLPrimitiveType primitiveType; + MTLWinding winding; + MTLCullMode cullMode; + id vsLib = nil; + id vsFunc = nil; + id fsLib = nil; + id fsFunc = nil; +}; + +struct QMetalSwapChainData +{ + CAMetalLayer *layer = nullptr; + id curDrawable; + dispatch_semaphore_t sem[QMTL_FRAMES_IN_FLIGHT]; + MTLRenderPassDescriptor *rp = nullptr; + id msaaTex[QMTL_FRAMES_IN_FLIGHT]; + QRhiTexture::Format rhiColorFormat; + MTLPixelFormat colorFormat; +}; + +QRhiMetal::QRhiMetal(QRhiMetalInitParams *params, QRhiMetalNativeHandles *importDevice) +{ + Q_UNUSED(params); + + d = new QRhiMetalData(this); + + importedDevice = importDevice != nullptr; + if (importedDevice) { + if (d->dev) { + d->dev = (id) importDevice->dev; + importedCmdQueue = importDevice->cmdQueue != nullptr; + if (importedCmdQueue) + d->cmdQueue = (id) importDevice->cmdQueue; + } else { + qWarning("No MTLDevice given, cannot import"); + importedDevice = false; + } + } +} + +QRhiMetal::~QRhiMetal() +{ + delete d; +} + +static inline uint aligned(uint v, uint byteAlign) +{ + return (v + byteAlign - 1) & ~(byteAlign - 1); +} + +bool QRhiMetal::create(QRhi::Flags flags) +{ + Q_UNUSED(flags); + + if (importedDevice) + [d->dev retain]; + else + d->dev = MTLCreateSystemDefaultDevice(); + + qDebug("Metal device: %s", qPrintable(QString::fromNSString([d->dev name]))); + + if (importedCmdQueue) + [d->cmdQueue retain]; + else + d->cmdQueue = [d->dev newCommandQueue]; + + if (@available(macOS 10.13, iOS 11.0, *)) { + d->captureMgr = [MTLCaptureManager sharedCaptureManager]; + // Have a custom capture scope as well which then shows up in XCode as + // an option when capturing, and becomes especially useful when having + // multiple windows with multiple QRhis. + d->captureScope = [d->captureMgr newCaptureScopeWithCommandQueue: d->cmdQueue]; + const QString label = QString::asprintf("Qt capture scope for QRhi %p", this); + d->captureScope.label = label.toNSString(); + } + +#if defined(Q_OS_MACOS) + caps.maxTextureSize = 16384; +#elif defined(Q_OS_TVOS) + if ([d->dev supportsFeatureSet: MTLFeatureSet(30003)]) // MTLFeatureSet_tvOS_GPUFamily2_v1 + caps.maxTextureSize = 16384; + else + caps.maxTextureSize = 8192; +#elif defined(Q_OS_IOS) + // welcome to feature set hell + if ([d->dev supportsFeatureSet: MTLFeatureSet(16)] // MTLFeatureSet_iOS_GPUFamily5_v1 + || [d->dev supportsFeatureSet: MTLFeatureSet(11)] // MTLFeatureSet_iOS_GPUFamily4_v1 + || [d->dev supportsFeatureSet: MTLFeatureSet(4)]) // MTLFeatureSet_iOS_GPUFamily3_v1 + { + caps.maxTextureSize = 16384; + } else if ([d->dev supportsFeatureSet: MTLFeatureSet(3)] // MTLFeatureSet_iOS_GPUFamily2_v2 + || [d->dev supportsFeatureSet: MTLFeatureSet(2)]) // MTLFeatureSet_iOS_GPUFamily1_v2 + { + caps.maxTextureSize = 8192; + } else { + caps.maxTextureSize = 4096; + } +#endif + + nativeHandlesStruct.dev = d->dev; + nativeHandlesStruct.cmdQueue = d->cmdQueue; + + return true; +} + +void QRhiMetal::destroy() +{ + executeDeferredReleases(true); + finishActiveReadbacks(true); + + if (@available(macOS 10.13, iOS 11.0, *)) { + [d->captureScope release]; + d->captureScope = nil; + } + + [d->cmdQueue release]; + if (!importedCmdQueue) + d->cmdQueue = nil; + + [d->dev release]; + if (!importedDevice) + d->dev = nil; +} + +QVector QRhiMetal::supportedSampleCounts() const +{ + return { 1, 2, 4, 8 }; +} + +int QRhiMetal::effectiveSampleCount(int sampleCount) const +{ + // Stay compatible with QSurfaceFormat and friends where samples == 0 means the same as 1. + const int s = qBound(1, sampleCount, 64); + if (!supportedSampleCounts().contains(s)) { + qWarning("Attempted to set unsupported sample count %d", sampleCount); + return 1; + } + return s; +} + +QRhiSwapChain *QRhiMetal::createSwapChain() +{ + return new QMetalSwapChain(this); +} + +QRhiBuffer *QRhiMetal::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, int size) +{ + return new QMetalBuffer(this, type, usage, size); +} + +int QRhiMetal::ubufAlignment() const +{ + return 256; +} + +bool QRhiMetal::isYUpInFramebuffer() const +{ + return false; +} + +bool QRhiMetal::isYUpInNDC() const +{ + return true; +} + +bool QRhiMetal::isClipDepthZeroToOne() const +{ + return true; +} + +QMatrix4x4 QRhiMetal::clipSpaceCorrMatrix() const +{ + // depth range 0..1 + static QMatrix4x4 m; + if (m.isIdentity()) { + // NB the ctor takes row-major + m = QMatrix4x4(1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.5f, 0.5f, + 0.0f, 0.0f, 0.0f, 1.0f); + } + return m; +} + +bool QRhiMetal::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const +{ + Q_UNUSED(flags); + +#ifdef Q_OS_MACOS + if (format >= QRhiTexture::ETC2_RGB8 && format <= QRhiTexture::ETC2_RGBA8) + return false; + if (format >= QRhiTexture::ASTC_4x4 && format <= QRhiTexture::ASTC_12x12) + return false; +#else + if (format >= QRhiTexture::BC1 && format <= QRhiTexture::BC7) + return false; +#endif + + return true; +} + +bool QRhiMetal::isFeatureSupported(QRhi::Feature feature) const +{ + switch (feature) { + case QRhi::MultisampleTexture: + return true; + case QRhi::MultisampleRenderBuffer: + return true; + case QRhi::DebugMarkers: + return true; + case QRhi::Timestamps: + return false; + case QRhi::Instancing: + return true; + case QRhi::CustomInstanceStepRate: + return true; + case QRhi::PrimitiveRestart: + return true; + case QRhi::NonDynamicUniformBuffers: + return true; + case QRhi::NonFourAlignedEffectiveIndexBufferOffset: + return false; + case QRhi::NPOTTextureRepeat: + return true; + case QRhi::RedOrAlpha8IsRed: + return true; + case QRhi::ElementIndexUint: + return true; + default: + Q_UNREACHABLE(); + return false; + } +} + +int QRhiMetal::resourceLimit(QRhi::ResourceLimit limit) const +{ + switch (limit) { + case QRhi::TextureSizeMin: + return 1; + case QRhi::TextureSizeMax: + return caps.maxTextureSize; + case QRhi::MaxColorAttachments: + return 8; + case QRhi::FramesInFlight: + return QMTL_FRAMES_IN_FLIGHT; + default: + Q_UNREACHABLE(); + return 0; + } +} + +const QRhiNativeHandles *QRhiMetal::nativeHandles() +{ + return &nativeHandlesStruct; +} + +void QRhiMetal::sendVMemStatsToProfiler() +{ + // nothing to do here +} + +QRhiRenderBuffer *QRhiMetal::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize, + int sampleCount, QRhiRenderBuffer::Flags flags) +{ + return new QMetalRenderBuffer(this, type, pixelSize, sampleCount, flags); +} + +QRhiTexture *QRhiMetal::createTexture(QRhiTexture::Format format, const QSize &pixelSize, + int sampleCount, QRhiTexture::Flags flags) +{ + return new QMetalTexture(this, format, pixelSize, sampleCount, flags); +} + +QRhiSampler *QRhiMetal::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, + QRhiSampler::Filter mipmapMode, + QRhiSampler::AddressMode u, QRhiSampler::AddressMode v) +{ + return new QMetalSampler(this, magFilter, minFilter, mipmapMode, u, v); +} + +QRhiTextureRenderTarget *QRhiMetal::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc, + QRhiTextureRenderTarget::Flags flags) +{ + return new QMetalTextureRenderTarget(this, desc, flags); +} + +QRhiGraphicsPipeline *QRhiMetal::createGraphicsPipeline() +{ + return new QMetalGraphicsPipeline(this); +} + +QRhiShaderResourceBindings *QRhiMetal::createShaderResourceBindings() +{ + return new QMetalShaderResourceBindings(this); +} + +void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD, QMetalCommandBuffer *cbD, + int dynamicOffsetCount, + const QRhiCommandBuffer::DynamicOffset *dynamicOffsets, + bool offsetOnlyChange) +{ + static const int KNOWN_STAGES = 2; + struct { + QRhiBatchedBindings > buffers; + QRhiBatchedBindings bufferOffsets; + QRhiBatchedBindings > textures; + QRhiBatchedBindings > samplers; + } res[KNOWN_STAGES]; + + for (const QRhiShaderResourceBinding &binding : qAsConst(srbD->sortedBindings)) { + const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&binding); + switch (b->type) { + case QRhiShaderResourceBinding::UniformBuffer: + { + QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, b->u.ubuf.buf); + id mtlbuf = bufD->d->buf[bufD->m_type == QRhiBuffer::Immutable ? 0 : currentFrameSlot]; + uint offset = b->u.ubuf.offset; + for (int i = 0; i < dynamicOffsetCount; ++i) { + const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]); + if (dynOfs.first == b->binding) { + offset = dynOfs.second; + break; + } + } + if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) { + res[0].buffers.feed(b->binding, mtlbuf); + res[0].bufferOffsets.feed(b->binding, offset); + } + if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) { + res[1].buffers.feed(b->binding, mtlbuf); + res[1].bufferOffsets.feed(b->binding, offset); + } + } + break; + case QRhiShaderResourceBinding::SampledTexture: + { + QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.tex); + QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.sampler); + if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) { + res[0].textures.feed(b->binding, texD->d->tex); + res[0].samplers.feed(b->binding, samplerD->d->samplerState); + } + if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) { + res[1].textures.feed(b->binding, texD->d->tex); + res[1].samplers.feed(b->binding, samplerD->d->samplerState); + } + } + break; + default: + Q_UNREACHABLE(); + break; + } + } + + for (int idx = 0; idx < KNOWN_STAGES; ++idx) { + res[idx].buffers.finish(); + res[idx].bufferOffsets.finish(); + + for (int i = 0, ie = res[idx].buffers.batches.count(); i != ie; ++i) { + const auto &bufferBatch(res[idx].buffers.batches[i]); + const auto &offsetBatch(res[idx].bufferOffsets.batches[i]); + switch (idx) { + case 0: + [cbD->d->currentPassEncoder setVertexBuffers: bufferBatch.resources.constData() + offsets: offsetBatch.resources.constData() + withRange: NSMakeRange(bufferBatch.startBinding, bufferBatch.resources.count())]; + break; + case 1: + [cbD->d->currentPassEncoder setFragmentBuffers: bufferBatch.resources.constData() + offsets: offsetBatch.resources.constData() + withRange: NSMakeRange(bufferBatch.startBinding, bufferBatch.resources.count())]; + break; + default: + Q_UNREACHABLE(); + break; + } + } + + if (offsetOnlyChange) + continue; + + res[idx].textures.finish(); + res[idx].samplers.finish(); + + for (int i = 0, ie = res[idx].textures.batches.count(); i != ie; ++i) { + const auto &batch(res[idx].textures.batches[i]); + switch (idx) { + case 0: + [cbD->d->currentPassEncoder setVertexTextures: batch.resources.constData() + withRange: NSMakeRange(batch.startBinding, batch.resources.count())]; + break; + case 1: + [cbD->d->currentPassEncoder setFragmentTextures: batch.resources.constData() + withRange: NSMakeRange(batch.startBinding, batch.resources.count())]; + break; + default: + Q_UNREACHABLE(); + break; + } + } + for (int i = 0, ie = res[idx].samplers.batches.count(); i != ie; ++i) { + const auto &batch(res[idx].samplers.batches[i]); + switch (idx) { + case 0: + [cbD->d->currentPassEncoder setVertexSamplerStates: batch.resources.constData() + withRange: NSMakeRange(batch.startBinding, batch.resources.count())]; + break; + case 1: + [cbD->d->currentPassEncoder setFragmentSamplerStates: batch.resources.constData() + withRange: NSMakeRange(batch.startBinding, batch.resources.count())]; + break; + default: + Q_UNREACHABLE(); + break; + } + } + } +} + +void QRhiMetal::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps) +{ + Q_ASSERT(inPass); + + QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); + QMetalGraphicsPipeline *psD = QRHI_RES(QMetalGraphicsPipeline, ps); + + if (cbD->currentPipeline != ps || cbD->currentPipelineGeneration != psD->generation) { + cbD->currentPipeline = ps; + cbD->currentPipelineGeneration = psD->generation; + + [cbD->d->currentPassEncoder setRenderPipelineState: psD->d->ps]; + [cbD->d->currentPassEncoder setDepthStencilState: psD->d->ds]; + [cbD->d->currentPassEncoder setCullMode: psD->d->cullMode]; + [cbD->d->currentPassEncoder setFrontFacingWinding: psD->d->winding]; + } + + psD->lastActiveFrameSlot = currentFrameSlot; +} + +void QRhiMetal::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb, + int dynamicOffsetCount, + const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) +{ + Q_ASSERT(inPass); + + QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); + Q_ASSERT(cbD->currentPipeline); + if (!srb) + srb = QRHI_RES(QMetalGraphicsPipeline, cbD->currentPipeline)->m_shaderResourceBindings; + + QMetalShaderResourceBindings *srbD = QRHI_RES(QMetalShaderResourceBindings, srb); + bool hasSlottedResourceInSrb = false; + bool hasDynamicOffsetInSrb = false; + bool resNeedsRebind = false; + + // do buffer writes, figure out if we need to rebind, and mark as in-use + for (int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) { + const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&srbD->sortedBindings[i]); + QMetalShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[i]); + switch (b->type) { + case QRhiShaderResourceBinding::UniformBuffer: + { + QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, b->u.ubuf.buf); + Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)); + executeBufferHostWritesForCurrentFrame(bufD); + if (bufD->m_type != QRhiBuffer::Immutable) + hasSlottedResourceInSrb = true; + if (b->u.ubuf.hasDynamicOffset) + hasDynamicOffsetInSrb = true; + if (bufD->generation != bd.ubuf.generation || bufD->m_id != bd.ubuf.id) { + resNeedsRebind = true; + bd.ubuf.id = bufD->m_id; + bd.ubuf.generation = bufD->generation; + } + bufD->lastActiveFrameSlot = currentFrameSlot; + } + break; + case QRhiShaderResourceBinding::SampledTexture: + { + QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.tex); + QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.sampler); + if (texD->generation != bd.stex.texGeneration + || texD->m_id != bd.stex.texId + || samplerD->generation != bd.stex.samplerGeneration + || samplerD->m_id != bd.stex.samplerId) + { + resNeedsRebind = true; + bd.stex.texId = texD->m_id; + bd.stex.texGeneration = texD->generation; + bd.stex.samplerId = samplerD->m_id; + bd.stex.samplerGeneration = samplerD->generation; + } + texD->lastActiveFrameSlot = currentFrameSlot; + samplerD->lastActiveFrameSlot = currentFrameSlot; + } + break; + default: + Q_UNREACHABLE(); + break; + } + } + + // make sure the resources for the correct slot get bound + const int resSlot = hasSlottedResourceInSrb ? currentFrameSlot : 0; + if (hasSlottedResourceInSrb && cbD->currentResSlot != resSlot) + resNeedsRebind = true; + + const bool srbChange = cbD->currentSrb != srb || cbD->currentSrbGeneration != srbD->generation; + + // dynamic uniform buffer offsets always trigger a rebind + if (hasDynamicOffsetInSrb || resNeedsRebind || srbChange) { + cbD->currentSrb = srb; + cbD->currentSrbGeneration = srbD->generation; + cbD->currentResSlot = resSlot; + + const bool offsetOnlyChange = hasDynamicOffsetInSrb && !resNeedsRebind && !srbChange; + enqueueShaderResourceBindings(srbD, cbD, dynamicOffsetCount, dynamicOffsets, offsetOnlyChange); + } +} + +void QRhiMetal::setVertexInput(QRhiCommandBuffer *cb, + int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings, + QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat) +{ + Q_ASSERT(inPass); + QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); + Q_ASSERT(cbD->currentPipeline); + + QRhiBatchedBindings > buffers; + QRhiBatchedBindings offsets; + for (int i = 0; i < bindingCount; ++i) { + QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, bindings[i].first); + executeBufferHostWritesForCurrentFrame(bufD); + bufD->lastActiveFrameSlot = currentFrameSlot; + id mtlbuf = bufD->d->buf[bufD->m_type == QRhiBuffer::Immutable ? 0 : currentFrameSlot]; + buffers.feed(startBinding + i, mtlbuf); + offsets.feed(startBinding + i, bindings[i].second); + } + buffers.finish(); + offsets.finish(); + + // same binding space for vertex and constant buffers - work it around + QRhiShaderResourceBindings *srb = cbD->currentSrb; + // There's nothing guaranteeing setShaderResources() was called before + // setVertexInput()... but whatever srb will get bound will have to be + // layout-compatible anyways so maxBinding is the same. + if (!srb) + srb = cbD->currentPipeline->shaderResourceBindings(); + const int firstVertexBinding = QRHI_RES(QMetalShaderResourceBindings, srb)->maxBinding + 1; + + if (firstVertexBinding != cbD->d->currentFirstVertexBinding + || buffers != cbD->d->currentVertexInputsBuffers + || offsets != cbD->d->currentVertexInputOffsets) + { + cbD->d->currentFirstVertexBinding = firstVertexBinding; + cbD->d->currentVertexInputsBuffers = buffers; + cbD->d->currentVertexInputOffsets = offsets; + + for (int i = 0, ie = buffers.batches.count(); i != ie; ++i) { + const auto &bufferBatch(buffers.batches[i]); + const auto &offsetBatch(offsets.batches[i]); + [cbD->d->currentPassEncoder setVertexBuffers: + bufferBatch.resources.constData() + offsets: offsetBatch.resources.constData() + withRange: NSMakeRange(firstVertexBinding + bufferBatch.startBinding, bufferBatch.resources.count())]; + } + } + + if (indexBuf) { + QMetalBuffer *ibufD = QRHI_RES(QMetalBuffer, indexBuf); + executeBufferHostWritesForCurrentFrame(ibufD); + ibufD->lastActiveFrameSlot = currentFrameSlot; + cbD->currentIndexBuffer = indexBuf; + cbD->currentIndexOffset = indexOffset; + cbD->currentIndexFormat = indexFormat; + } else { + cbD->currentIndexBuffer = nullptr; + } +} + +void QRhiMetal::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) +{ + Q_ASSERT(inPass); + QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); + Q_ASSERT(cbD->currentPipeline && cbD->currentTarget); + const QSize outputSize = cbD->currentTarget->pixelSize(); + + // x,y is top-left in MTLViewportRect but bottom-left in QRhiViewport + float x, y, w, h; + if (!qrhi_toTopLeftRenderTargetRect(outputSize, viewport.viewport(), &x, &y, &w, &h)) + return; + + MTLViewport vp; + vp.originX = x; + vp.originY = y; + vp.width = w; + vp.height = h; + vp.znear = viewport.minDepth(); + vp.zfar = viewport.maxDepth(); + + [cbD->d->currentPassEncoder setViewport: vp]; + + if (!QRHI_RES(QMetalGraphicsPipeline, cbD->currentPipeline)->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)) { + MTLScissorRect s; + s.x = x; + s.y = y; + s.width = w; + s.height = h; + [cbD->d->currentPassEncoder setScissorRect: s]; + } +} + +void QRhiMetal::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) +{ + Q_ASSERT(inPass); + QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); + Q_ASSERT(cbD->currentPipeline && cbD->currentTarget); + Q_ASSERT(QRHI_RES(QMetalGraphicsPipeline, cbD->currentPipeline)->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)); + const QSize outputSize = cbD->currentTarget->pixelSize(); + + // x,y is top-left in MTLScissorRect but bottom-left in QRhiScissor + int x, y, w, h; + if (!qrhi_toTopLeftRenderTargetRect(outputSize, scissor.scissor(), &x, &y, &w, &h)) + return; + + MTLScissorRect s; + s.x = x; + s.y = y; + s.width = w; + s.height = h; + + [cbD->d->currentPassEncoder setScissorRect: s]; +} + +void QRhiMetal::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) +{ + Q_ASSERT(inPass); + QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); + [cbD->d->currentPassEncoder setBlendColorRed: c.redF() green: c.greenF() blue: c.blueF() alpha: c.alphaF()]; +} + +void QRhiMetal::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) +{ + Q_ASSERT(inPass); + QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); + [cbD->d->currentPassEncoder setStencilReferenceValue: refValue]; +} + +void QRhiMetal::draw(QRhiCommandBuffer *cb, quint32 vertexCount, + quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) +{ + Q_ASSERT(inPass); + QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); + [cbD->d->currentPassEncoder drawPrimitives: + QRHI_RES(QMetalGraphicsPipeline, cbD->currentPipeline)->d->primitiveType + vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount baseInstance: firstInstance]; +} + +void QRhiMetal::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, + quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance) +{ + Q_ASSERT(inPass); + QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); + if (!cbD->currentIndexBuffer) + return; + + const quint32 indexOffset = cbD->currentIndexOffset + firstIndex * (cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? 2 : 4); + Q_ASSERT(indexOffset == aligned(indexOffset, 4)); + + QMetalBuffer *ibufD = QRHI_RES(QMetalBuffer, cbD->currentIndexBuffer); + id mtlbuf = ibufD->d->buf[ibufD->m_type == QRhiBuffer::Immutable ? 0 : currentFrameSlot]; + + [cbD->d->currentPassEncoder drawIndexedPrimitives: QRHI_RES(QMetalGraphicsPipeline, cbD->currentPipeline)->d->primitiveType + indexCount: indexCount + indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32 + indexBuffer: mtlbuf + indexBufferOffset: indexOffset + instanceCount: instanceCount + baseVertex: vertexOffset + baseInstance: firstInstance]; +} + +void QRhiMetal::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) +{ + if (!debugMarkers) + return; + + NSString *str = [NSString stringWithUTF8String: name.constData()]; + QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); + if (inPass) { + [cbD->d->currentPassEncoder pushDebugGroup: str]; + } else { + if (@available(macOS 10.13, iOS 11.0, *)) + [cbD->d->cb pushDebugGroup: str]; + } +} + +void QRhiMetal::debugMarkEnd(QRhiCommandBuffer *cb) +{ + if (!debugMarkers) + return; + + QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); + if (inPass) { + [cbD->d->currentPassEncoder popDebugGroup]; + } else { + if (@available(macOS 10.13, iOS 11.0, *)) + [cbD->d->cb popDebugGroup]; + } +} + +void QRhiMetal::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) +{ + if (!debugMarkers) + return; + + if (inPass) { + QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); + [cbD->d->currentPassEncoder insertDebugSignpost: [NSString stringWithUTF8String: msg.constData()]]; + } +} + +const QRhiNativeHandles *QRhiMetal::nativeHandles(QRhiCommandBuffer *cb) +{ + return QRHI_RES(QMetalCommandBuffer, cb)->nativeHandles(); +} + +void QRhiMetal::beginExternal(QRhiCommandBuffer *cb) +{ + Q_UNUSED(cb); +} + +void QRhiMetal::endExternal(QRhiCommandBuffer *cb) +{ + QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); + cbD->resetPerPassCachedState(); +} + +QRhi::FrameOpResult QRhiMetal::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) +{ + Q_UNUSED(flags); + Q_ASSERT(!inFrame); + inFrame = true; + + QMetalSwapChain *swapChainD = QRHI_RES(QMetalSwapChain, swapChain); + + // This is a bit messed up since for this swapchain we want to wait for the + // commands+present to complete, while for others just for the commands + // (for this same frame slot) but not sure how to do that in a sane way so + // wait for full cb completion for now. + for (QMetalSwapChain *sc : qAsConst(swapchains)) { + dispatch_semaphore_t sem = sc->d->sem[swapChainD->currentFrameSlot]; + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + if (sc != swapChainD) + dispatch_semaphore_signal(sem); + } + + currentSwapChain = swapChainD; + currentFrameSlot = swapChainD->currentFrameSlot; + if (swapChainD->ds) + swapChainD->ds->lastActiveFrameSlot = currentFrameSlot; + + if (@available(macOS 10.13, iOS 11.0, *)) + [d->captureScope beginScope]; + + // Do not let the command buffer mess with the refcount of objects. We do + // have a proper render loop and will manage lifetimes similarly to other + // backends (Vulkan). + swapChainD->cbWrapper.d->cb = [d->cmdQueue commandBufferWithUnretainedReferences]; + + QMetalRenderTargetData::ColorAtt colorAtt; + if (swapChainD->samples > 1) { + colorAtt.tex = swapChainD->d->msaaTex[currentFrameSlot]; + colorAtt.needsDrawableForResolveTex = true; + } else { + colorAtt.needsDrawableForTex = true; + } + + swapChainD->rtWrapper.d->fb.colorAtt[0] = colorAtt; + swapChainD->rtWrapper.d->fb.dsTex = swapChainD->ds ? swapChainD->ds->d->tex : nil; + swapChainD->rtWrapper.d->fb.hasStencil = swapChainD->ds ? true : false; + swapChainD->rtWrapper.d->fb.depthNeedsStore = false; + + QRhiProfilerPrivate *rhiP = profilerPrivateOrNull(); + QRHI_PROF_F(beginSwapChainFrame(swapChain)); + + executeDeferredReleases(); + swapChainD->cbWrapper.resetState(); + finishActiveReadbacks(); + + return QRhi::FrameOpSuccess; +} + +QRhi::FrameOpResult QRhiMetal::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) +{ + Q_ASSERT(inFrame); + inFrame = false; + + QMetalSwapChain *swapChainD = QRHI_RES(QMetalSwapChain, swapChain); + Q_ASSERT(currentSwapChain == swapChainD); + + const bool needsPresent = !flags.testFlag(QRhi::SkipPresent); + if (needsPresent) + [swapChainD->cbWrapper.d->cb presentDrawable: swapChainD->d->curDrawable]; + + __block int thisFrameSlot = currentFrameSlot; + [swapChainD->cbWrapper.d->cb addCompletedHandler: ^(id) { + dispatch_semaphore_signal(swapChainD->d->sem[thisFrameSlot]); + }]; + + [swapChainD->cbWrapper.d->cb commit]; + + QRhiProfilerPrivate *rhiP = profilerPrivateOrNull(); + QRHI_PROF_F(endSwapChainFrame(swapChain, swapChainD->frameCount + 1)); + + if (@available(macOS 10.13, iOS 11.0, *)) + [d->captureScope endScope]; + + if (needsPresent) + swapChainD->currentFrameSlot = (swapChainD->currentFrameSlot + 1) % QMTL_FRAMES_IN_FLIGHT; + + swapChainD->frameCount += 1; + currentSwapChain = nullptr; + return QRhi::FrameOpSuccess; +} + +QRhi::FrameOpResult QRhiMetal::beginOffscreenFrame(QRhiCommandBuffer **cb) +{ + Q_ASSERT(!inFrame); + inFrame = true; + + currentFrameSlot = (currentFrameSlot + 1) % QMTL_FRAMES_IN_FLIGHT; + if (swapchains.count() > 1) { + for (QMetalSwapChain *sc : qAsConst(swapchains)) { + // wait+signal is the general pattern to ensure the commands for a + // given frame slot have completed (if sem is 1, we go 0 then 1; if + // sem is 0 we go -1, block, completion increments to 0, then us to 1) + dispatch_semaphore_t sem = sc->d->sem[currentFrameSlot]; + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + dispatch_semaphore_signal(sem); + } + } + + d->ofr.active = true; + *cb = &d->ofr.cbWrapper; + d->ofr.cbWrapper.d->cb = [d->cmdQueue commandBufferWithUnretainedReferences]; + + executeDeferredReleases(); + d->ofr.cbWrapper.resetState(); + finishActiveReadbacks(); + + return QRhi::FrameOpSuccess; +} + +QRhi::FrameOpResult QRhiMetal::endOffscreenFrame() +{ + Q_ASSERT(d->ofr.active); + d->ofr.active = false; + Q_ASSERT(inFrame); + inFrame = false; + + [d->ofr.cbWrapper.d->cb commit]; + + // offscreen frames wait for completion, unlike swapchain ones + [d->ofr.cbWrapper.d->cb waitUntilCompleted]; + + finishActiveReadbacks(true); + + return QRhi::FrameOpSuccess; +} + +QRhi::FrameOpResult QRhiMetal::finish() +{ + Q_ASSERT(!inPass); + + id cb = nil; + QMetalSwapChain *swapChainD = nullptr; + if (inFrame) { + if (d->ofr.active) { + Q_ASSERT(!currentSwapChain); + cb = d->ofr.cbWrapper.d->cb; + } else { + Q_ASSERT(currentSwapChain); + swapChainD = currentSwapChain; + cb = swapChainD->cbWrapper.d->cb; + } + } + + for (QMetalSwapChain *sc : qAsConst(swapchains)) { + for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) { + if (currentSwapChain && sc == currentSwapChain && i == currentFrameSlot) { + // no wait as this is the thing we're going to be commit below and + // beginFrame decremented sem already and going to be signaled by endFrame + continue; + } + dispatch_semaphore_t sem = sc->d->sem[i]; + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + dispatch_semaphore_signal(sem); + } + } + + if (cb) { + [cb commit]; + [cb waitUntilCompleted]; + } + + if (inFrame) { + if (d->ofr.active) + d->ofr.cbWrapper.d->cb = [d->cmdQueue commandBufferWithUnretainedReferences]; + else + swapChainD->cbWrapper.d->cb = [d->cmdQueue commandBufferWithUnretainedReferences]; + } + + executeDeferredReleases(true); + + finishActiveReadbacks(true); + + return QRhi::FrameOpSuccess; +} + +MTLRenderPassDescriptor *QRhiMetalData::createDefaultRenderPass(bool hasDepthStencil, + const QColor &colorClearValue, + const QRhiDepthStencilClearValue &depthStencilClearValue, + int colorAttCount) +{ + MTLRenderPassDescriptor *rp = [MTLRenderPassDescriptor renderPassDescriptor]; + MTLClearColor c = MTLClearColorMake(colorClearValue.redF(), colorClearValue.greenF(), colorClearValue.blueF(), + colorClearValue.alphaF()); + + for (int i = 0; i < colorAttCount; ++i) { + rp.colorAttachments[i].loadAction = MTLLoadActionClear; + rp.colorAttachments[i].storeAction = MTLStoreActionStore; + rp.colorAttachments[i].clearColor = c; + } + + if (hasDepthStencil) { + rp.depthAttachment.loadAction = MTLLoadActionClear; + rp.depthAttachment.storeAction = MTLStoreActionDontCare; + rp.stencilAttachment.loadAction = MTLLoadActionClear; + rp.stencilAttachment.storeAction = MTLStoreActionDontCare; + rp.depthAttachment.clearDepth = depthStencilClearValue.depthClearValue(); + rp.stencilAttachment.clearStencil = depthStencilClearValue.stencilClearValue(); + } + + return rp; +} + +qsizetype QRhiMetal::subresUploadByteSize(const QRhiTextureSubresourceUploadDescription &subresDesc) const +{ + qsizetype size = 0; + const qsizetype imageSizeBytes = subresDesc.image().isNull() ? + subresDesc.data().size() : subresDesc.image().sizeInBytes(); + if (imageSizeBytes > 0) + size += aligned(imageSizeBytes, QRhiMetalData::TEXBUF_ALIGN); + return size; +} + +void QRhiMetal::enqueueSubresUpload(QMetalTexture *texD, void *mp, void *blitEncPtr, + int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc, + qsizetype *curOfs) +{ + const QPoint dp = subresDesc.destinationTopLeft(); + const QByteArray rawData = subresDesc.data(); + QImage img = subresDesc.image(); + id blitEnc = (id) blitEncPtr; + + if (!img.isNull()) { + const qsizetype fullImageSizeBytes = img.sizeInBytes(); + int w = img.width(); + int h = img.height(); + int bpl = img.bytesPerLine(); + int srcOffset = 0; + + if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) { + const int sx = subresDesc.sourceTopLeft().x(); + const int sy = subresDesc.sourceTopLeft().y(); + if (!subresDesc.sourceSize().isEmpty()) { + w = subresDesc.sourceSize().width(); + h = subresDesc.sourceSize().height(); + } + if (img.depth() == 32) { + memcpy(reinterpret_cast(mp) + *curOfs, img.constBits(), fullImageSizeBytes); + srcOffset = sy * bpl + sx * 4; + // bpl remains set to the original image's row stride + } else { + img = img.copy(sx, sy, w, h); + bpl = img.bytesPerLine(); + Q_ASSERT(img.sizeInBytes() <= fullImageSizeBytes); + memcpy(reinterpret_cast(mp) + *curOfs, img.constBits(), img.sizeInBytes()); + } + } else { + memcpy(reinterpret_cast(mp) + *curOfs, img.constBits(), fullImageSizeBytes); + } + + [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot] + sourceOffset: *curOfs + srcOffset + sourceBytesPerRow: bpl + sourceBytesPerImage: 0 + sourceSize: MTLSizeMake(w, h, 1) + toTexture: texD->d->tex + destinationSlice: layer + destinationLevel: level + destinationOrigin: MTLOriginMake(dp.x(), dp.y(), 0) + options: MTLBlitOptionNone]; + + *curOfs += aligned(fullImageSizeBytes, QRhiMetalData::TEXBUF_ALIGN); + } else if (!rawData.isEmpty() && isCompressedFormat(texD->m_format)) { + const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize); + const int subresw = subresSize.width(); + const int subresh = subresSize.height(); + int w, h; + if (subresDesc.sourceSize().isEmpty()) { + w = subresw; + h = subresh; + } else { + w = subresDesc.sourceSize().width(); + h = subresDesc.sourceSize().height(); + } + + quint32 bpl = 0; + QSize blockDim; + compressedFormatInfo(texD->m_format, QSize(w, h), &bpl, nullptr, &blockDim); + + const int dx = aligned(dp.x(), blockDim.width()); + const int dy = aligned(dp.y(), blockDim.height()); + if (dx + w != subresw) + w = aligned(w, blockDim.width()); + if (dy + h != subresh) + h = aligned(h, blockDim.height()); + + memcpy(reinterpret_cast(mp) + *curOfs, rawData.constData(), rawData.size()); + + [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot] + sourceOffset: *curOfs + sourceBytesPerRow: bpl + sourceBytesPerImage: 0 + sourceSize: MTLSizeMake(w, h, 1) + toTexture: texD->d->tex + destinationSlice: layer + destinationLevel: level + destinationOrigin: MTLOriginMake(dx, dy, 0) + options: MTLBlitOptionNone]; + + *curOfs += aligned(rawData.size(), QRhiMetalData::TEXBUF_ALIGN); + } else if (!rawData.isEmpty()) { + const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize); + const int subresw = subresSize.width(); + const int subresh = subresSize.height(); + int w, h; + if (subresDesc.sourceSize().isEmpty()) { + w = subresw; + h = subresh; + } else { + w = subresDesc.sourceSize().width(); + h = subresDesc.sourceSize().height(); + } + + quint32 bpl = 0; + textureFormatInfo(texD->m_format, QSize(w, h), &bpl, nullptr); + memcpy(reinterpret_cast(mp) + *curOfs, rawData.constData(), rawData.size()); + + [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot] + sourceOffset: *curOfs + sourceBytesPerRow: bpl + sourceBytesPerImage: 0 + sourceSize: MTLSizeMake(w, h, 1) + toTexture: texD->d->tex + destinationSlice: layer + destinationLevel: level + destinationOrigin: MTLOriginMake(dp.x(), dp.y(), 0) + options: MTLBlitOptionNone]; + + *curOfs += aligned(rawData.size(), QRhiMetalData::TEXBUF_ALIGN); + } else { + qWarning("Invalid texture upload for %p layer=%d mip=%d", texD, layer, level); + } +} + +void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) +{ + QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); + QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates); + QRhiProfilerPrivate *rhiP = profilerPrivateOrNull(); + + for (const QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate &u : ud->dynamicBufferUpdates) { + QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, u.buf); + Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic); + for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) + bufD->d->pendingUpdates[i].append(u); + } + + for (const QRhiResourceUpdateBatchPrivate::StaticBufferUpload &u : ud->staticBufferUploads) { + QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, u.buf); + Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic); + Q_ASSERT(u.offset + u.data.size() <= bufD->m_size); + for (int i = 0, ie = bufD->m_type == QRhiBuffer::Immutable ? 1 : QMTL_FRAMES_IN_FLIGHT; i != ie; ++i) + bufD->d->pendingUpdates[i].append({ u.buf, u.offset, u.data.size(), u.data.constData() }); + } + + id blitEnc = nil; + auto ensureBlit = [&blitEnc, cbD, this] { + if (!blitEnc) { + blitEnc = [cbD->d->cb blitCommandEncoder]; + if (debugMarkers) + [blitEnc pushDebugGroup: @"Texture upload/copy"]; + } + }; + + for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) { + if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) { + QMetalTexture *utexD = QRHI_RES(QMetalTexture, u.upload.tex); + qsizetype stagingSize = 0; + for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) { + for (int level = 0; level < QRhi::MAX_LEVELS; ++level) { + for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.upload.subresDesc[layer][level])) + stagingSize += subresUploadByteSize(subresDesc); + } + } + + ensureBlit(); + Q_ASSERT(!utexD->d->stagingBuf[currentFrameSlot]); + utexD->d->stagingBuf[currentFrameSlot] = [d->dev newBufferWithLength: stagingSize + options: MTLResourceStorageModeShared]; + QRHI_PROF_F(newTextureStagingArea(utexD, currentFrameSlot, stagingSize)); + + void *mp = [utexD->d->stagingBuf[currentFrameSlot] contents]; + qsizetype curOfs = 0; + for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) { + for (int level = 0; level < QRhi::MAX_LEVELS; ++level) { + for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.upload.subresDesc[layer][level])) + enqueueSubresUpload(utexD, mp, blitEnc, layer, level, subresDesc, &curOfs); + } + } + + utexD->lastActiveFrameSlot = currentFrameSlot; + + QRhiMetalData::DeferredReleaseEntry e; + e.type = QRhiMetalData::DeferredReleaseEntry::StagingBuffer; + e.lastActiveFrameSlot = currentFrameSlot; + e.stagingBuffer.buffer = utexD->d->stagingBuf[currentFrameSlot]; + utexD->d->stagingBuf[currentFrameSlot] = nil; + d->releaseQueue.append(e); + QRHI_PROF_F(releaseTextureStagingArea(utexD, currentFrameSlot)); + } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) { + Q_ASSERT(u.copy.src && u.copy.dst); + QMetalTexture *srcD = QRHI_RES(QMetalTexture, u.copy.src); + QMetalTexture *dstD = QRHI_RES(QMetalTexture, u.copy.dst); + const QPoint dp = u.copy.desc.destinationTopLeft(); + const QSize size = u.copy.desc.pixelSize().isEmpty() ? srcD->m_pixelSize : u.copy.desc.pixelSize(); + const QPoint sp = u.copy.desc.sourceTopLeft(); + + ensureBlit(); + [blitEnc copyFromTexture: srcD->d->tex + sourceSlice: u.copy.desc.sourceLayer() + sourceLevel: u.copy.desc.sourceLevel() + sourceOrigin: MTLOriginMake(sp.x(), sp.y(), 0) + sourceSize: MTLSizeMake(size.width(), size.height(), 1) + toTexture: dstD->d->tex + destinationSlice: u.copy.desc.destinationLayer() + destinationLevel: u.copy.desc.destinationLevel() + destinationOrigin: MTLOriginMake(dp.x(), dp.y(), 0)]; + + srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot; + } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) { + QRhiMetalData::ActiveReadback aRb; + aRb.activeFrameSlot = currentFrameSlot; + aRb.desc = u.read.rb; + aRb.result = u.read.result; + + QMetalTexture *texD = QRHI_RES(QMetalTexture, u.read.rb.texture()); + QMetalSwapChain *swapChainD = nullptr; + id src; + QSize srcSize; + if (texD) { + if (texD->samples > 1) { + qWarning("Multisample texture cannot be read back"); + continue; + } + aRb.pixelSize = u.read.rb.level() > 0 ? q->sizeForMipLevel(u.read.rb.level(), texD->m_pixelSize) + : texD->m_pixelSize; + aRb.format = texD->m_format; + src = texD->d->tex; + srcSize = texD->m_pixelSize; + texD->lastActiveFrameSlot = currentFrameSlot; + } else { + Q_ASSERT(currentSwapChain); + swapChainD = QRHI_RES(QMetalSwapChain, currentSwapChain); + aRb.pixelSize = swapChainD->pixelSize; + aRb.format = swapChainD->d->rhiColorFormat; + // Multisample swapchains need nothing special since resolving + // happens when ending a renderpass. + const QMetalRenderTargetData::ColorAtt &colorAtt(swapChainD->rtWrapper.d->fb.colorAtt[0]); + src = colorAtt.resolveTex ? colorAtt.resolveTex : colorAtt.tex; + srcSize = swapChainD->rtWrapper.d->pixelSize; + } + + quint32 bpl = 0; + textureFormatInfo(aRb.format, aRb.pixelSize, &bpl, &aRb.bufSize); + aRb.buf = [d->dev newBufferWithLength: aRb.bufSize options: MTLResourceStorageModeShared]; + + QRHI_PROF_F(newReadbackBuffer(quint64(quintptr(aRb.buf)), + texD ? static_cast(texD) : static_cast(swapChainD), + aRb.bufSize)); + + ensureBlit(); + [blitEnc copyFromTexture: src + sourceSlice: u.read.rb.layer() + sourceLevel: u.read.rb.level() + sourceOrigin: MTLOriginMake(0, 0, 0) + sourceSize: MTLSizeMake(srcSize.width(), srcSize.height(), 1) + toBuffer: aRb.buf + destinationOffset: 0 + destinationBytesPerRow: bpl + destinationBytesPerImage: 0 + options: MTLBlitOptionNone]; + + d->activeReadbacks.append(aRb); + } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::MipGen) { + QMetalTexture *utexD = QRHI_RES(QMetalTexture, u.mipgen.tex); + ensureBlit(); + [blitEnc generateMipmapsForTexture: utexD->d->tex]; + utexD->lastActiveFrameSlot = currentFrameSlot; + } + } + + if (blitEnc) { + if (debugMarkers) + [blitEnc popDebugGroup]; + [blitEnc endEncoding]; + } + + ud->free(); +} + +void QRhiMetal::executeBufferHostWritesForCurrentFrame(QMetalBuffer *bufD) +{ + const int idx = bufD->m_type == QRhiBuffer::Immutable ? 0 : currentFrameSlot; + QVector &updates(bufD->d->pendingUpdates[idx]); + if (updates.isEmpty()) + return; + + void *p = [bufD->d->buf[idx] contents]; + int changeBegin = -1; + int changeEnd = -1; + for (const QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate &u : updates) { + Q_ASSERT(bufD == QRHI_RES(QMetalBuffer, u.buf)); + memcpy(static_cast(p) + u.offset, u.data.constData(), u.data.size()); + if (changeBegin == -1 || u.offset < changeBegin) + changeBegin = u.offset; + if (changeEnd == -1 || u.offset + u.data.size() > changeEnd) + changeEnd = u.offset + u.data.size(); + } + if (changeBegin >= 0 && bufD->d->managed) + [bufD->d->buf[idx] didModifyRange: NSMakeRange(changeBegin, changeEnd - changeBegin)]; + + updates.clear(); +} + +void QRhiMetal::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) +{ + Q_ASSERT(inFrame && !inPass); + + enqueueResourceUpdates(cb, resourceUpdates); +} + +void QRhiMetal::beginPass(QRhiCommandBuffer *cb, + QRhiRenderTarget *rt, + const QColor &colorClearValue, + const QRhiDepthStencilClearValue &depthStencilClearValue, + QRhiResourceUpdateBatch *resourceUpdates) +{ + Q_ASSERT(inFrame && !inPass); + + if (resourceUpdates) + enqueueResourceUpdates(cb, resourceUpdates); + + QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); + + QMetalRenderTargetData *rtD = nullptr; + switch (rt->resourceType()) { + case QRhiResource::RenderTarget: + rtD = QRHI_RES(QMetalReferenceRenderTarget, rt)->d; + cbD->d->currentPassRpDesc = d->createDefaultRenderPass(rtD->dsAttCount, colorClearValue, depthStencilClearValue, rtD->colorAttCount); + if (rtD->colorAttCount) { + QMetalRenderTargetData::ColorAtt &color0(rtD->fb.colorAtt[0]); + if (color0.needsDrawableForTex || color0.needsDrawableForResolveTex) { + Q_ASSERT(currentSwapChain); + QMetalSwapChain *swapChainD = QRHI_RES(QMetalSwapChain, currentSwapChain); + swapChainD->d->curDrawable = [swapChainD->d->layer nextDrawable]; + if (!swapChainD->d->curDrawable) { + qWarning("No drawable"); + return; + } + id scTex = swapChainD->d->curDrawable.texture; + if (color0.needsDrawableForTex) { + color0.tex = scTex; + color0.needsDrawableForTex = false; + } else { + color0.resolveTex = scTex; + color0.needsDrawableForResolveTex = false; + } + } + } + break; + case QRhiResource::TextureRenderTarget: + { + QMetalTextureRenderTarget *rtTex = QRHI_RES(QMetalTextureRenderTarget, rt); + rtD = rtTex->d; + cbD->d->currentPassRpDesc = d->createDefaultRenderPass(rtD->dsAttCount, colorClearValue, depthStencilClearValue, rtD->colorAttCount); + if (rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents)) { + for (int i = 0; i < rtD->colorAttCount; ++i) + cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad; + } + if (rtD->dsAttCount && rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents)) { + cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad; + cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad; + } + const QVector colorAttachments = rtTex->m_desc.colorAttachments(); + for (const QRhiColorAttachment &colorAttachment : colorAttachments) { + if (colorAttachment.texture()) + QRHI_RES(QMetalTexture, colorAttachment.texture())->lastActiveFrameSlot = currentFrameSlot; + else if (colorAttachment.renderBuffer()) + QRHI_RES(QMetalRenderBuffer, colorAttachment.renderBuffer())->lastActiveFrameSlot = currentFrameSlot; + if (colorAttachment.resolveTexture()) + QRHI_RES(QMetalTexture, colorAttachment.resolveTexture())->lastActiveFrameSlot = currentFrameSlot; + } + if (rtTex->m_desc.depthStencilBuffer()) + QRHI_RES(QMetalRenderBuffer, rtTex->m_desc.depthStencilBuffer())->lastActiveFrameSlot = currentFrameSlot; + if (rtTex->m_desc.depthTexture()) + QRHI_RES(QMetalTexture, rtTex->m_desc.depthTexture())->lastActiveFrameSlot = currentFrameSlot; + } + break; + default: + Q_UNREACHABLE(); + break; + } + + for (int i = 0; i < rtD->colorAttCount; ++i) { + cbD->d->currentPassRpDesc.colorAttachments[i].texture = rtD->fb.colorAtt[i].tex; + cbD->d->currentPassRpDesc.colorAttachments[i].slice = rtD->fb.colorAtt[i].layer; + cbD->d->currentPassRpDesc.colorAttachments[i].level = rtD->fb.colorAtt[i].level; + if (rtD->fb.colorAtt[i].resolveTex) { + cbD->d->currentPassRpDesc.colorAttachments[i].storeAction = MTLStoreActionMultisampleResolve; + cbD->d->currentPassRpDesc.colorAttachments[i].resolveTexture = rtD->fb.colorAtt[i].resolveTex; + cbD->d->currentPassRpDesc.colorAttachments[i].resolveSlice = rtD->fb.colorAtt[i].resolveLayer; + cbD->d->currentPassRpDesc.colorAttachments[i].resolveLevel = rtD->fb.colorAtt[i].resolveLevel; + } + } + + if (rtD->dsAttCount) { + Q_ASSERT(rtD->fb.dsTex); + cbD->d->currentPassRpDesc.depthAttachment.texture = rtD->fb.dsTex; + cbD->d->currentPassRpDesc.stencilAttachment.texture = rtD->fb.hasStencil ? rtD->fb.dsTex : nil; + if (rtD->fb.depthNeedsStore) // Depth/Stencil is set to DontCare by default, override if needed + cbD->d->currentPassRpDesc.depthAttachment.storeAction = MTLStoreActionStore; + } + + cbD->d->currentPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc]; + + cbD->resetPerPassState(); + + cbD->currentTarget = rt; + inPass = true; +} + +void QRhiMetal::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) +{ + Q_ASSERT(inPass); + inPass = false; + + QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); + [cbD->d->currentPassEncoder endEncoding]; + + cbD->currentTarget = nullptr; + + if (resourceUpdates) + enqueueResourceUpdates(cb, resourceUpdates); +} + +static void qrhimtl_releaseBuffer(const QRhiMetalData::DeferredReleaseEntry &e) +{ + for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) + [e.buffer.buffers[i] release]; +} + +static void qrhimtl_releaseRenderBuffer(const QRhiMetalData::DeferredReleaseEntry &e) +{ + [e.renderbuffer.texture release]; +} + +static void qrhimtl_releaseTexture(const QRhiMetalData::DeferredReleaseEntry &e) +{ + [e.texture.texture release]; + for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) + [e.texture.stagingBuffers[i] release]; +} + +static void qrhimtl_releaseSampler(const QRhiMetalData::DeferredReleaseEntry &e) +{ + [e.sampler.samplerState release]; +} + +void QRhiMetal::executeDeferredReleases(bool forced) +{ + for (int i = d->releaseQueue.count() - 1; i >= 0; --i) { + const QRhiMetalData::DeferredReleaseEntry &e(d->releaseQueue[i]); + if (forced || currentFrameSlot == e.lastActiveFrameSlot || e.lastActiveFrameSlot < 0) { + switch (e.type) { + case QRhiMetalData::DeferredReleaseEntry::Buffer: + qrhimtl_releaseBuffer(e); + break; + case QRhiMetalData::DeferredReleaseEntry::RenderBuffer: + qrhimtl_releaseRenderBuffer(e); + break; + case QRhiMetalData::DeferredReleaseEntry::Texture: + qrhimtl_releaseTexture(e); + break; + case QRhiMetalData::DeferredReleaseEntry::Sampler: + qrhimtl_releaseSampler(e); + break; + case QRhiMetalData::DeferredReleaseEntry::StagingBuffer: + [e.stagingBuffer.buffer release]; + break; + default: + break; + } + d->releaseQueue.removeAt(i); + } + } +} + +void QRhiMetal::finishActiveReadbacks(bool forced) +{ + QVarLengthArray, 4> completedCallbacks; + QRhiProfilerPrivate *rhiP = profilerPrivateOrNull(); + + for (int i = d->activeReadbacks.count() - 1; i >= 0; --i) { + const QRhiMetalData::ActiveReadback &aRb(d->activeReadbacks[i]); + if (forced || currentFrameSlot == aRb.activeFrameSlot || aRb.activeFrameSlot < 0) { + aRb.result->format = aRb.format; + aRb.result->pixelSize = aRb.pixelSize; + aRb.result->data.resize(aRb.bufSize); + void *p = [aRb.buf contents]; + memcpy(aRb.result->data.data(), p, aRb.bufSize); + [aRb.buf release]; + + QRHI_PROF_F(releaseReadbackBuffer(quint64(quintptr(aRb.buf)))); + + if (aRb.result->completed) + completedCallbacks.append(aRb.result->completed); + + d->activeReadbacks.removeAt(i); + } + } + + for (auto f : completedCallbacks) + f(); +} + +QMetalBuffer::QMetalBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size) + : QRhiBuffer(rhi, type, usage, size), + d(new QMetalBufferData) +{ + for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) + d->buf[i] = nil; +} + +QMetalBuffer::~QMetalBuffer() +{ + release(); + delete d; +} + +void QMetalBuffer::release() +{ + if (!d->buf[0]) + return; + + QRhiMetalData::DeferredReleaseEntry e; + e.type = QRhiMetalData::DeferredReleaseEntry::Buffer; + e.lastActiveFrameSlot = lastActiveFrameSlot; + + for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) { + e.buffer.buffers[i] = d->buf[i]; + d->buf[i] = nil; + d->pendingUpdates[i].clear(); + } + + QRHI_RES_RHI(QRhiMetal); + rhiD->d->releaseQueue.append(e); + QRHI_PROF; + QRHI_PROF_F(releaseBuffer(this)); + rhiD->unregisterResource(this); +} + +bool QMetalBuffer::build() +{ + if (d->buf[0]) + release(); + + const int nonZeroSize = m_size <= 0 ? 256 : m_size; + const int roundedSize = m_usage.testFlag(QRhiBuffer::UniformBuffer) ? aligned(nonZeroSize, 256) : nonZeroSize; + + d->managed = false; + MTLResourceOptions opts = MTLResourceStorageModeShared; +#ifdef Q_OS_MACOS + if (m_type != Dynamic) { + opts = MTLResourceStorageModeManaged; + d->managed = true; + } +#endif + + QRHI_RES_RHI(QRhiMetal); + for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) { + // Immutable only has buf[0] and pendingUpdates[0] in use. + // Static and Dynamic use all. + if (i == 0 || m_type != Immutable) { + d->buf[i] = [rhiD->d->dev newBufferWithLength: roundedSize options: opts]; + d->pendingUpdates[i].reserve(16); + if (!m_objectName.isEmpty()) { + if (m_type == Immutable) { + d->buf[i].label = [NSString stringWithUTF8String: m_objectName.constData()]; + } else { + const QByteArray name = m_objectName + '/' + QByteArray::number(i); + d->buf[i].label = [NSString stringWithUTF8String: name.constData()]; + } + } + } + } + + QRHI_PROF; + QRHI_PROF_F(newBuffer(this, roundedSize, m_type == Immutable ? 1 : QMTL_FRAMES_IN_FLIGHT, 0)); + + lastActiveFrameSlot = -1; + generation += 1; + rhiD->registerResource(this); + return true; +} + +QMetalRenderBuffer::QMetalRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize, + int sampleCount, QRhiRenderBuffer::Flags flags) + : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags), + d(new QMetalRenderBufferData) +{ +} + +QMetalRenderBuffer::~QMetalRenderBuffer() +{ + release(); + delete d; +} + +void QMetalRenderBuffer::release() +{ + if (!d->tex) + return; + + QRhiMetalData::DeferredReleaseEntry e; + e.type = QRhiMetalData::DeferredReleaseEntry::RenderBuffer; + e.lastActiveFrameSlot = lastActiveFrameSlot; + + e.renderbuffer.texture = d->tex; + d->tex = nil; + + QRHI_RES_RHI(QRhiMetal); + rhiD->d->releaseQueue.append(e); + QRHI_PROF; + QRHI_PROF_F(releaseRenderBuffer(this)); + rhiD->unregisterResource(this); +} + +bool QMetalRenderBuffer::build() +{ + if (d->tex) + release(); + + if (m_pixelSize.isEmpty()) + return false; + + QRHI_RES_RHI(QRhiMetal); + samples = rhiD->effectiveSampleCount(m_sampleCount); + + MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init]; + desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D; + desc.width = m_pixelSize.width(); + desc.height = m_pixelSize.height(); + if (samples > 1) + desc.sampleCount = samples; + desc.resourceOptions = MTLResourceStorageModePrivate; + desc.usage = MTLTextureUsageRenderTarget; + + bool transientBacking = false; + switch (m_type) { + case DepthStencil: +#ifdef Q_OS_MACOS + desc.storageMode = MTLStorageModePrivate; +#else + desc.storageMode = MTLResourceStorageModeMemoryless; + transientBacking = true; +#endif + d->format = rhiD->d->dev.depth24Stencil8PixelFormatSupported + ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8; + desc.pixelFormat = d->format; + break; + case Color: + desc.storageMode = MTLStorageModePrivate; + d->format = MTLPixelFormatRGBA8Unorm; + desc.pixelFormat = d->format; + break; + default: + Q_UNREACHABLE(); + break; + } + + d->tex = [rhiD->d->dev newTextureWithDescriptor: desc]; + [desc release]; + + if (!m_objectName.isEmpty()) + d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()]; + + QRHI_PROF; + QRHI_PROF_F(newRenderBuffer(this, transientBacking, false, samples)); + + lastActiveFrameSlot = -1; + generation += 1; + rhiD->registerResource(this); + return true; +} + +QRhiTexture::Format QMetalRenderBuffer::backingFormat() const +{ + return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat; +} + +QMetalTexture::QMetalTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, + int sampleCount, Flags flags) + : QRhiTexture(rhi, format, pixelSize, sampleCount, flags), + d(new QMetalTextureData) +{ + for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) + d->stagingBuf[i] = nil; +} + +QMetalTexture::~QMetalTexture() +{ + release(); + delete d; +} + +void QMetalTexture::release() +{ + if (!d->tex) + return; + + QRhiMetalData::DeferredReleaseEntry e; + e.type = QRhiMetalData::DeferredReleaseEntry::Texture; + e.lastActiveFrameSlot = lastActiveFrameSlot; + + e.texture.texture = d->owns ? d->tex : nil; + d->tex = nil; + nativeHandlesStruct.texture = nullptr; + + for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) { + e.texture.stagingBuffers[i] = d->stagingBuf[i]; + d->stagingBuf[i] = nil; + } + + QRHI_RES_RHI(QRhiMetal); + rhiD->d->releaseQueue.append(e); + QRHI_PROF; + QRHI_PROF_F(releaseTexture(this)); + rhiD->unregisterResource(this); +} + +static inline MTLPixelFormat toMetalTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags) +{ + const bool srgb = flags.testFlag(QRhiTexture::sRGB); + switch (format) { + case QRhiTexture::RGBA8: + return srgb ? MTLPixelFormatRGBA8Unorm_sRGB : MTLPixelFormatRGBA8Unorm; + case QRhiTexture::BGRA8: + return srgb ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm; + case QRhiTexture::R8: +#ifdef Q_OS_MACOS + return MTLPixelFormatR8Unorm; +#else + return srgb ? MTLPixelFormatR8Unorm_sRGB : MTLPixelFormatR8Unorm; +#endif + case QRhiTexture::R16: + return MTLPixelFormatR16Unorm; + case QRhiTexture::RED_OR_ALPHA8: + return MTLPixelFormatR8Unorm; + + case QRhiTexture::RGBA16F: + return MTLPixelFormatRGBA16Float; + case QRhiTexture::RGBA32F: + return MTLPixelFormatRGBA32Float; + + case QRhiTexture::D16: +#ifdef Q_OS_MACOS + return MTLPixelFormatDepth16Unorm; +#else + return MTLPixelFormatDepth32Float; +#endif + case QRhiTexture::D32F: + return MTLPixelFormatDepth32Float; + +#ifdef Q_OS_MACOS + case QRhiTexture::BC1: + return srgb ? MTLPixelFormatBC1_RGBA_sRGB : MTLPixelFormatBC1_RGBA; + case QRhiTexture::BC2: + return srgb ? MTLPixelFormatBC2_RGBA_sRGB : MTLPixelFormatBC2_RGBA; + case QRhiTexture::BC3: + return srgb ? MTLPixelFormatBC3_RGBA_sRGB : MTLPixelFormatBC3_RGBA; + case QRhiTexture::BC4: + return MTLPixelFormatBC4_RUnorm; + case QRhiTexture::BC5: + qWarning("QRhiMetal does not support BC5"); + return MTLPixelFormatRGBA8Unorm; + case QRhiTexture::BC6H: + return MTLPixelFormatBC6H_RGBUfloat; + case QRhiTexture::BC7: + return srgb ? MTLPixelFormatBC7_RGBAUnorm_sRGB : MTLPixelFormatBC7_RGBAUnorm; +#else + case QRhiTexture::BC1: + case QRhiTexture::BC2: + case QRhiTexture::BC3: + case QRhiTexture::BC4: + case QRhiTexture::BC5: + case QRhiTexture::BC6H: + case QRhiTexture::BC7: + qWarning("QRhiMetal: BCx compression not supported on this platform"); + return MTLPixelFormatRGBA8Unorm; +#endif + +#ifndef Q_OS_MACOS + case QRhiTexture::ETC2_RGB8: + return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8; + case QRhiTexture::ETC2_RGB8A1: + return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1; + case QRhiTexture::ETC2_RGBA8: + return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8; + + case QRhiTexture::ASTC_4x4: + return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_LDR; + case QRhiTexture::ASTC_5x4: + return srgb ? MTLPixelFormatASTC_5x4_sRGB : MTLPixelFormatASTC_5x4_LDR; + case QRhiTexture::ASTC_5x5: + return srgb ? MTLPixelFormatASTC_5x5_sRGB : MTLPixelFormatASTC_5x5_LDR; + case QRhiTexture::ASTC_6x5: + return srgb ? MTLPixelFormatASTC_6x5_sRGB : MTLPixelFormatASTC_6x5_LDR; + case QRhiTexture::ASTC_6x6: + return srgb ? MTLPixelFormatASTC_6x6_sRGB : MTLPixelFormatASTC_6x6_LDR; + case QRhiTexture::ASTC_8x5: + return srgb ? MTLPixelFormatASTC_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR; + case QRhiTexture::ASTC_8x6: + return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR; + case QRhiTexture::ASTC_8x8: + return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR; + case QRhiTexture::ASTC_10x5: + return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR; + case QRhiTexture::ASTC_10x6: + return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR; + case QRhiTexture::ASTC_10x8: + return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR; + case QRhiTexture::ASTC_10x10: + return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR; + case QRhiTexture::ASTC_12x10: + return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR; + case QRhiTexture::ASTC_12x12: + return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR; +#else + case QRhiTexture::ETC2_RGB8: + case QRhiTexture::ETC2_RGB8A1: + case QRhiTexture::ETC2_RGBA8: + qWarning("QRhiMetal: ETC2 compression not supported on this platform"); + return MTLPixelFormatRGBA8Unorm; + + case QRhiTexture::ASTC_4x4: + case QRhiTexture::ASTC_5x4: + case QRhiTexture::ASTC_5x5: + case QRhiTexture::ASTC_6x5: + case QRhiTexture::ASTC_6x6: + case QRhiTexture::ASTC_8x5: + case QRhiTexture::ASTC_8x6: + case QRhiTexture::ASTC_8x8: + case QRhiTexture::ASTC_10x5: + case QRhiTexture::ASTC_10x6: + case QRhiTexture::ASTC_10x8: + case QRhiTexture::ASTC_10x10: + case QRhiTexture::ASTC_12x10: + case QRhiTexture::ASTC_12x12: + qWarning("QRhiMetal: ASTC compression not supported on this platform"); + return MTLPixelFormatRGBA8Unorm; +#endif + + default: + Q_UNREACHABLE(); + return MTLPixelFormatRGBA8Unorm; + } +} + +bool QMetalTexture::prepareBuild(QSize *adjustedSize) +{ + if (d->tex) + release(); + + const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize; + const bool isCube = m_flags.testFlag(CubeMap); + const bool hasMipMaps = m_flags.testFlag(MipMapped); + + QRHI_RES_RHI(QRhiMetal); + d->format = toMetalTextureFormat(m_format, m_flags); + mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1; + samples = rhiD->effectiveSampleCount(m_sampleCount); + if (samples > 1) { + if (isCube) { + qWarning("Cubemap texture cannot be multisample"); + return false; + } + if (hasMipMaps) { + qWarning("Multisample texture cannot have mipmaps"); + return false; + } + } + + if (adjustedSize) + *adjustedSize = size; + + return true; +} + +bool QMetalTexture::build() +{ + QSize size; + if (!prepareBuild(&size)) + return false; + + MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init]; + + const bool isCube = m_flags.testFlag(CubeMap); + if (isCube) + desc.textureType = MTLTextureTypeCube; + else + desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D; + desc.pixelFormat = d->format; + desc.width = size.width(); + desc.height = size.height(); + desc.mipmapLevelCount = mipLevelCount; + if (samples > 1) + desc.sampleCount = samples; + desc.resourceOptions = MTLResourceStorageModePrivate; + desc.storageMode = MTLStorageModePrivate; + desc.usage = MTLTextureUsageShaderRead; + if (m_flags.testFlag(RenderTarget)) + desc.usage |= MTLTextureUsageRenderTarget; + + QRHI_RES_RHI(QRhiMetal); + d->tex = [rhiD->d->dev newTextureWithDescriptor: desc]; + [desc release]; + + if (!m_objectName.isEmpty()) + d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()]; + + d->owns = true; + nativeHandlesStruct.texture = d->tex; + + QRHI_PROF; + QRHI_PROF_F(newTexture(this, true, mipLevelCount, isCube ? 6 : 1, samples)); + + lastActiveFrameSlot = -1; + generation += 1; + rhiD->registerResource(this); + return true; +} + +bool QMetalTexture::buildFrom(const QRhiNativeHandles *src) +{ + const QRhiMetalTextureNativeHandles *h = static_cast(src); + if (!h || !h->texture) + return false; + + if (!prepareBuild()) + return false; + + d->tex = (id) h->texture; + + d->owns = false; + nativeHandlesStruct.texture = d->tex; + + QRHI_PROF; + QRHI_PROF_F(newTexture(this, false, mipLevelCount, m_flags.testFlag(CubeMap) ? 6 : 1, samples)); + + lastActiveFrameSlot = -1; + generation += 1; + QRHI_RES_RHI(QRhiMetal); + rhiD->registerResource(this); + return true; +} + +const QRhiNativeHandles *QMetalTexture::nativeHandles() +{ + return &nativeHandlesStruct; +} + +QMetalSampler::QMetalSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode, + AddressMode u, AddressMode v) + : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v), + d(new QMetalSamplerData) +{ +} + +QMetalSampler::~QMetalSampler() +{ + release(); + delete d; +} + +void QMetalSampler::release() +{ + if (!d->samplerState) + return; + + QRhiMetalData::DeferredReleaseEntry e; + e.type = QRhiMetalData::DeferredReleaseEntry::Sampler; + e.lastActiveFrameSlot = lastActiveFrameSlot; + + e.sampler.samplerState = d->samplerState; + d->samplerState = nil; + + QRHI_RES_RHI(QRhiMetal); + rhiD->d->releaseQueue.append(e); + rhiD->unregisterResource(this); +} + +static inline MTLSamplerMinMagFilter toMetalFilter(QRhiSampler::Filter f) +{ + switch (f) { + case QRhiSampler::Nearest: + return MTLSamplerMinMagFilterNearest; + case QRhiSampler::Linear: + return MTLSamplerMinMagFilterLinear; + default: + Q_UNREACHABLE(); + return MTLSamplerMinMagFilterNearest; + } +} + +static inline MTLSamplerMipFilter toMetalMipmapMode(QRhiSampler::Filter f) +{ + switch (f) { + case QRhiSampler::None: + return MTLSamplerMipFilterNotMipmapped; + case QRhiSampler::Nearest: + return MTLSamplerMipFilterNearest; + case QRhiSampler::Linear: + return MTLSamplerMipFilterLinear; + default: + Q_UNREACHABLE(); + return MTLSamplerMipFilterNotMipmapped; + } +} + +static inline MTLSamplerAddressMode toMetalAddressMode(QRhiSampler::AddressMode m) +{ + switch (m) { + case QRhiSampler::Repeat: + return MTLSamplerAddressModeRepeat; + case QRhiSampler::ClampToEdge: + return MTLSamplerAddressModeClampToEdge; + case QRhiSampler::Border: + return MTLSamplerAddressModeClampToBorderColor; + case QRhiSampler::Mirror: + return MTLSamplerAddressModeMirrorRepeat; + case QRhiSampler::MirrorOnce: + return MTLSamplerAddressModeMirrorClampToEdge; + default: + Q_UNREACHABLE(); + return MTLSamplerAddressModeClampToEdge; + } +} + +static inline MTLCompareFunction toMetalTextureCompareFunction(QRhiSampler::CompareOp op) +{ + switch (op) { + case QRhiSampler::Never: + return MTLCompareFunctionNever; + case QRhiSampler::Less: + return MTLCompareFunctionLess; + case QRhiSampler::Equal: + return MTLCompareFunctionEqual; + case QRhiSampler::LessOrEqual: + return MTLCompareFunctionLessEqual; + case QRhiSampler::Greater: + return MTLCompareFunctionGreater; + case QRhiSampler::NotEqual: + return MTLCompareFunctionNotEqual; + case QRhiSampler::GreaterOrEqual: + return MTLCompareFunctionGreaterEqual; + case QRhiSampler::Always: + return MTLCompareFunctionAlways; + default: + Q_UNREACHABLE(); + return MTLCompareFunctionNever; + } +} + +bool QMetalSampler::build() +{ + if (d->samplerState) + release(); + + MTLSamplerDescriptor *desc = [[MTLSamplerDescriptor alloc] init]; + desc.minFilter = toMetalFilter(m_minFilter); + desc.magFilter = toMetalFilter(m_magFilter); + desc.mipFilter = toMetalMipmapMode(m_mipmapMode); + desc.sAddressMode = toMetalAddressMode(m_addressU); + desc.tAddressMode = toMetalAddressMode(m_addressV); + desc.rAddressMode = toMetalAddressMode(m_addressW); + desc.compareFunction = toMetalTextureCompareFunction(m_compareOp); + + QRHI_RES_RHI(QRhiMetal); + d->samplerState = [rhiD->d->dev newSamplerStateWithDescriptor: desc]; + [desc release]; + + lastActiveFrameSlot = -1; + generation += 1; + rhiD->registerResource(this); + return true; +} + +// dummy, no Vulkan-style RenderPass+Framebuffer concept here. +// We do have MTLRenderPassDescriptor of course, but it will be created on the fly for each pass. +QMetalRenderPassDescriptor::QMetalRenderPassDescriptor(QRhiImplementation *rhi) + : QRhiRenderPassDescriptor(rhi) +{ +} + +QMetalRenderPassDescriptor::~QMetalRenderPassDescriptor() +{ + release(); +} + +void QMetalRenderPassDescriptor::release() +{ + // nothing to do here +} + +QMetalReferenceRenderTarget::QMetalReferenceRenderTarget(QRhiImplementation *rhi) + : QRhiRenderTarget(rhi), + d(new QMetalRenderTargetData) +{ +} + +QMetalReferenceRenderTarget::~QMetalReferenceRenderTarget() +{ + release(); + delete d; +} + +void QMetalReferenceRenderTarget::release() +{ + // nothing to do here +} + +QSize QMetalReferenceRenderTarget::pixelSize() const +{ + return d->pixelSize; +} + +float QMetalReferenceRenderTarget::devicePixelRatio() const +{ + return d->dpr; +} + +int QMetalReferenceRenderTarget::sampleCount() const +{ + return d->sampleCount; +} + +QMetalTextureRenderTarget::QMetalTextureRenderTarget(QRhiImplementation *rhi, + const QRhiTextureRenderTargetDescription &desc, + Flags flags) + : QRhiTextureRenderTarget(rhi, desc, flags), + d(new QMetalRenderTargetData) +{ +} + +QMetalTextureRenderTarget::~QMetalTextureRenderTarget() +{ + release(); + delete d; +} + +void QMetalTextureRenderTarget::release() +{ + // nothing to do here +} + +QRhiRenderPassDescriptor *QMetalTextureRenderTarget::newCompatibleRenderPassDescriptor() +{ + const QVector colorAttachments = m_desc.colorAttachments(); + QMetalRenderPassDescriptor *rpD = new QMetalRenderPassDescriptor(m_rhi); + rpD->colorAttachmentCount = colorAttachments.count(); + rpD->hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture(); + + for (int i = 0, ie = colorAttachments.count(); i != ie; ++i) { + QMetalTexture *texD = QRHI_RES(QMetalTexture, colorAttachments[i].texture()); + QMetalRenderBuffer *rbD = QRHI_RES(QMetalRenderBuffer, colorAttachments[i].renderBuffer()); + rpD->colorFormat[i] = texD ? texD->d->format : rbD->d->format; + } + + if (m_desc.depthTexture()) + rpD->dsFormat = QRHI_RES(QMetalTexture, m_desc.depthTexture())->d->format; + else if (m_desc.depthStencilBuffer()) + rpD->dsFormat = QRHI_RES(QMetalRenderBuffer, m_desc.depthStencilBuffer())->d->format; + + return rpD; +} + +bool QMetalTextureRenderTarget::build() +{ + const QVector colorAttachments = m_desc.colorAttachments(); + Q_ASSERT(!colorAttachments.isEmpty() || m_desc.depthTexture()); + Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture()); + const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture(); + + d->colorAttCount = colorAttachments.count(); + for (int i = 0; i < d->colorAttCount; ++i) { + QMetalTexture *texD = QRHI_RES(QMetalTexture, colorAttachments[i].texture()); + QMetalRenderBuffer *rbD = QRHI_RES(QMetalRenderBuffer, colorAttachments[i].renderBuffer()); + Q_ASSERT(texD || rbD); + id dst = nil; + if (texD) { + dst = texD->d->tex; + if (i == 0) { + d->pixelSize = texD->pixelSize(); + d->sampleCount = texD->samples; + } + } else if (rbD) { + dst = rbD->d->tex; + if (i == 0) { + d->pixelSize = rbD->pixelSize(); + d->sampleCount = rbD->samples; + } + } + QMetalRenderTargetData::ColorAtt colorAtt; + colorAtt.tex = dst; + colorAtt.layer = colorAttachments[i].layer(); + colorAtt.level = colorAttachments[i].level(); + QMetalTexture *resTexD = QRHI_RES(QMetalTexture, colorAttachments[i].resolveTexture()); + colorAtt.resolveTex = resTexD ? resTexD->d->tex : nil; + colorAtt.resolveLayer = colorAttachments[i].resolveLayer(); + colorAtt.resolveLevel = colorAttachments[i].resolveLevel(); + d->fb.colorAtt[i] = colorAtt; + } + d->dpr = 1; + + if (hasDepthStencil) { + if (m_desc.depthTexture()) { + QMetalTexture *depthTexD = QRHI_RES(QMetalTexture, m_desc.depthTexture()); + d->fb.dsTex = depthTexD->d->tex; + d->fb.hasStencil = false; + d->fb.depthNeedsStore = true; + if (d->colorAttCount == 0) { + d->pixelSize = depthTexD->pixelSize(); + d->sampleCount = depthTexD->samples; + } + } else { + QMetalRenderBuffer *depthRbD = QRHI_RES(QMetalRenderBuffer, m_desc.depthStencilBuffer()); + d->fb.dsTex = depthRbD->d->tex; + d->fb.hasStencil = true; + d->fb.depthNeedsStore = false; + if (d->colorAttCount == 0) { + d->pixelSize = depthRbD->pixelSize(); + d->sampleCount = depthRbD->samples; + } + } + d->dsAttCount = 1; + } else { + d->dsAttCount = 0; + } + + return true; +} + +QSize QMetalTextureRenderTarget::pixelSize() const +{ + return d->pixelSize; +} + +float QMetalTextureRenderTarget::devicePixelRatio() const +{ + return d->dpr; +} + +int QMetalTextureRenderTarget::sampleCount() const +{ + return d->sampleCount; +} + +QMetalShaderResourceBindings::QMetalShaderResourceBindings(QRhiImplementation *rhi) + : QRhiShaderResourceBindings(rhi) +{ +} + +QMetalShaderResourceBindings::~QMetalShaderResourceBindings() +{ + release(); +} + +void QMetalShaderResourceBindings::release() +{ + sortedBindings.clear(); + maxBinding = -1; +} + +bool QMetalShaderResourceBindings::build() +{ + if (!sortedBindings.isEmpty()) + release(); + + sortedBindings = m_bindings; + std::sort(sortedBindings.begin(), sortedBindings.end(), + [](const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b) + { + return QRhiShaderResourceBindingPrivate::get(&a)->binding < QRhiShaderResourceBindingPrivate::get(&b)->binding; + }); + if (!sortedBindings.isEmpty()) + maxBinding = QRhiShaderResourceBindingPrivate::get(&sortedBindings.last())->binding; + else + maxBinding = -1; + + boundResourceData.resize(sortedBindings.count()); + + for (int i = 0, ie = sortedBindings.count(); i != ie; ++i) { + const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&sortedBindings[i]); + QMetalShaderResourceBindings::BoundResourceData &bd(boundResourceData[i]); + switch (b->type) { + case QRhiShaderResourceBinding::UniformBuffer: + { + QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, b->u.ubuf.buf); + bd.ubuf.id = bufD->m_id; + bd.ubuf.generation = bufD->generation; + } + break; + case QRhiShaderResourceBinding::SampledTexture: + { + QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.tex); + QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.sampler); + bd.stex.texId = texD->m_id; + bd.stex.texGeneration = texD->generation; + bd.stex.samplerId = samplerD->m_id; + bd.stex.samplerGeneration = samplerD->generation; + } + break; + default: + Q_UNREACHABLE(); + break; + } + } + + generation += 1; + return true; +} + +QMetalGraphicsPipeline::QMetalGraphicsPipeline(QRhiImplementation *rhi) + : QRhiGraphicsPipeline(rhi), + d(new QMetalGraphicsPipelineData) +{ +} + +QMetalGraphicsPipeline::~QMetalGraphicsPipeline() +{ + release(); + delete d; +} + +void QMetalGraphicsPipeline::release() +{ + QRHI_RES_RHI(QRhiMetal); + + if (!d->ps) + return; + + if (d->ps) { + [d->ps release]; + d->ps = nil; + } + + if (d->ds) { + [d->ds release]; + d->ds = nil; + } + + if (d->vsFunc) { + [d->vsFunc release]; + d->vsFunc = nil; + } + if (d->vsLib) { + [d->vsLib release]; + d->vsLib = nil; + } + + if (d->fsFunc) { + [d->fsFunc release]; + d->fsFunc = nil; + } + if (d->fsLib) { + [d->fsLib release]; + d->fsLib = nil; + } + + rhiD->unregisterResource(this); +} + +static inline MTLVertexFormat toMetalAttributeFormat(QRhiVertexInputAttribute::Format format) +{ + switch (format) { + case QRhiVertexInputAttribute::Float4: + return MTLVertexFormatFloat4; + case QRhiVertexInputAttribute::Float3: + return MTLVertexFormatFloat3; + case QRhiVertexInputAttribute::Float2: + return MTLVertexFormatFloat2; + case QRhiVertexInputAttribute::Float: + return MTLVertexFormatFloat; + case QRhiVertexInputAttribute::UNormByte4: + return MTLVertexFormatUChar4Normalized; + case QRhiVertexInputAttribute::UNormByte2: + return MTLVertexFormatUChar2Normalized; + case QRhiVertexInputAttribute::UNormByte: + if (@available(macOS 10.13, iOS 11.0, *)) + return MTLVertexFormatUCharNormalized; + else + Q_UNREACHABLE(); + default: + Q_UNREACHABLE(); + return MTLVertexFormatFloat4; + } +} + +static inline MTLBlendFactor toMetalBlendFactor(QRhiGraphicsPipeline::BlendFactor f) +{ + switch (f) { + case QRhiGraphicsPipeline::Zero: + return MTLBlendFactorZero; + case QRhiGraphicsPipeline::One: + return MTLBlendFactorOne; + case QRhiGraphicsPipeline::SrcColor: + return MTLBlendFactorSourceColor; + case QRhiGraphicsPipeline::OneMinusSrcColor: + return MTLBlendFactorOneMinusSourceColor; + case QRhiGraphicsPipeline::DstColor: + return MTLBlendFactorDestinationColor; + case QRhiGraphicsPipeline::OneMinusDstColor: + return MTLBlendFactorOneMinusDestinationColor; + case QRhiGraphicsPipeline::SrcAlpha: + return MTLBlendFactorSourceAlpha; + case QRhiGraphicsPipeline::OneMinusSrcAlpha: + return MTLBlendFactorOneMinusSourceAlpha; + case QRhiGraphicsPipeline::DstAlpha: + return MTLBlendFactorDestinationAlpha; + case QRhiGraphicsPipeline::OneMinusDstAlpha: + return MTLBlendFactorOneMinusDestinationAlpha; + case QRhiGraphicsPipeline::ConstantColor: + return MTLBlendFactorBlendColor; + case QRhiGraphicsPipeline::ConstantAlpha: + return MTLBlendFactorBlendAlpha; + case QRhiGraphicsPipeline::OneMinusConstantColor: + return MTLBlendFactorOneMinusBlendColor; + case QRhiGraphicsPipeline::OneMinusConstantAlpha: + return MTLBlendFactorOneMinusBlendAlpha; + case QRhiGraphicsPipeline::SrcAlphaSaturate: + return MTLBlendFactorSourceAlphaSaturated; + case QRhiGraphicsPipeline::Src1Color: + return MTLBlendFactorSource1Color; + case QRhiGraphicsPipeline::OneMinusSrc1Color: + return MTLBlendFactorOneMinusSource1Color; + case QRhiGraphicsPipeline::Src1Alpha: + return MTLBlendFactorSource1Alpha; + case QRhiGraphicsPipeline::OneMinusSrc1Alpha: + return MTLBlendFactorOneMinusSource1Alpha; + default: + Q_UNREACHABLE(); + return MTLBlendFactorZero; + } +} + +static inline MTLBlendOperation toMetalBlendOp(QRhiGraphicsPipeline::BlendOp op) +{ + switch (op) { + case QRhiGraphicsPipeline::Add: + return MTLBlendOperationAdd; + case QRhiGraphicsPipeline::Subtract: + return MTLBlendOperationSubtract; + case QRhiGraphicsPipeline::ReverseSubtract: + return MTLBlendOperationReverseSubtract; + case QRhiGraphicsPipeline::Min: + return MTLBlendOperationMin; + case QRhiGraphicsPipeline::Max: + return MTLBlendOperationMax; + default: + Q_UNREACHABLE(); + return MTLBlendOperationAdd; + } +} + +static inline uint toMetalColorWriteMask(QRhiGraphicsPipeline::ColorMask c) +{ + uint f = 0; + if (c.testFlag(QRhiGraphicsPipeline::R)) + f |= MTLColorWriteMaskRed; + if (c.testFlag(QRhiGraphicsPipeline::G)) + f |= MTLColorWriteMaskGreen; + if (c.testFlag(QRhiGraphicsPipeline::B)) + f |= MTLColorWriteMaskBlue; + if (c.testFlag(QRhiGraphicsPipeline::A)) + f |= MTLColorWriteMaskAlpha; + return f; +} + +static inline MTLCompareFunction toMetalCompareOp(QRhiGraphicsPipeline::CompareOp op) +{ + switch (op) { + case QRhiGraphicsPipeline::Never: + return MTLCompareFunctionNever; + case QRhiGraphicsPipeline::Less: + return MTLCompareFunctionLess; + case QRhiGraphicsPipeline::Equal: + return MTLCompareFunctionEqual; + case QRhiGraphicsPipeline::LessOrEqual: + return MTLCompareFunctionLessEqual; + case QRhiGraphicsPipeline::Greater: + return MTLCompareFunctionGreater; + case QRhiGraphicsPipeline::NotEqual: + return MTLCompareFunctionNotEqual; + case QRhiGraphicsPipeline::GreaterOrEqual: + return MTLCompareFunctionGreaterEqual; + case QRhiGraphicsPipeline::Always: + return MTLCompareFunctionAlways; + default: + Q_UNREACHABLE(); + return MTLCompareFunctionAlways; + } +} + +static inline MTLStencilOperation toMetalStencilOp(QRhiGraphicsPipeline::StencilOp op) +{ + switch (op) { + case QRhiGraphicsPipeline::StencilZero: + return MTLStencilOperationZero; + case QRhiGraphicsPipeline::Keep: + return MTLStencilOperationKeep; + case QRhiGraphicsPipeline::Replace: + return MTLStencilOperationReplace; + case QRhiGraphicsPipeline::IncrementAndClamp: + return MTLStencilOperationIncrementClamp; + case QRhiGraphicsPipeline::DecrementAndClamp: + return MTLStencilOperationDecrementClamp; + case QRhiGraphicsPipeline::Invert: + return MTLStencilOperationInvert; + case QRhiGraphicsPipeline::IncrementAndWrap: + return MTLStencilOperationIncrementWrap; + case QRhiGraphicsPipeline::DecrementAndWrap: + return MTLStencilOperationDecrementWrap; + default: + Q_UNREACHABLE(); + return MTLStencilOperationKeep; + } +} + +static inline MTLPrimitiveType toMetalPrimitiveType(QRhiGraphicsPipeline::Topology t) +{ + switch (t) { + case QRhiGraphicsPipeline::Triangles: + return MTLPrimitiveTypeTriangle; + case QRhiGraphicsPipeline::TriangleStrip: + return MTLPrimitiveTypeTriangleStrip; + case QRhiGraphicsPipeline::Lines: + return MTLPrimitiveTypeLine; + case QRhiGraphicsPipeline::LineStrip: + return MTLPrimitiveTypeLineStrip; + case QRhiGraphicsPipeline::Points: + return MTLPrimitiveTypePoint; + default: + Q_UNREACHABLE(); + return MTLPrimitiveTypeTriangle; + } +} + +static inline MTLCullMode toMetalCullMode(QRhiGraphicsPipeline::CullMode c) +{ + switch (c) { + case QRhiGraphicsPipeline::None: + return MTLCullModeNone; + case QRhiGraphicsPipeline::Front: + return MTLCullModeFront; + case QRhiGraphicsPipeline::Back: + return MTLCullModeBack; + default: + Q_UNREACHABLE(); + return MTLCullModeNone; + } +} + +id QRhiMetalData::createMetalLib(const QShader &shader, QShader::Variant shaderVariant, + QString *error, QByteArray *entryPoint) +{ + QShaderCode mtllib = shader.shader({ QShader::MetalLibShader, 12, shaderVariant }); + if (!mtllib.shader().isEmpty()) { + dispatch_data_t data = dispatch_data_create(mtllib.shader().constData(), + mtllib.shader().size(), + dispatch_get_global_queue(0, 0), + DISPATCH_DATA_DESTRUCTOR_DEFAULT); + NSError *err = nil; + id lib = [dev newLibraryWithData: data error: &err]; + dispatch_release(data); + if (!err) { + *entryPoint = mtllib.entryPoint(); + return lib; + } else { + const QString msg = QString::fromNSString(err.localizedDescription); + qWarning("Failed to load metallib from baked shader: %s", qPrintable(msg)); + } + } + + QShaderCode mslSource = shader.shader({ QShader::MslShader, 12, shaderVariant }); + if (mslSource.shader().isEmpty()) { + qWarning() << "No MSL 1.2 code found in baked shader" << shader; + return nil; + } + + NSString *src = [NSString stringWithUTF8String: mslSource.shader().constData()]; + MTLCompileOptions *opts = [[MTLCompileOptions alloc] init]; + opts.languageVersion = MTLLanguageVersion1_2; + NSError *err = nil; + id lib = [dev newLibraryWithSource: src options: opts error: &err]; + [opts release]; + // src is autoreleased + + if (err) { + const QString msg = QString::fromNSString(err.localizedDescription); + *error = msg; + return nil; + } + + *entryPoint = mslSource.entryPoint(); + return lib; +} + +id QRhiMetalData::createMSLShaderFunction(id lib, const QByteArray &entryPoint) +{ + NSString *name = [NSString stringWithUTF8String: entryPoint.constData()]; + id f = [lib newFunctionWithName: name]; + [name release]; + return f; +} + +bool QMetalGraphicsPipeline::build() +{ + if (d->ps) + release(); + + QRHI_RES_RHI(QRhiMetal); + + // same binding space for vertex and constant buffers - work it around + const int firstVertexBinding = QRHI_RES(QMetalShaderResourceBindings, m_shaderResourceBindings)->maxBinding + 1; + + MTLVertexDescriptor *inputLayout = [MTLVertexDescriptor vertexDescriptor]; + const QVector attributes = m_vertexInputLayout.attributes(); + for (const QRhiVertexInputAttribute &attribute : attributes) { + const int loc = attribute.location(); + inputLayout.attributes[loc].format = toMetalAttributeFormat(attribute.format()); + inputLayout.attributes[loc].offset = attribute.offset(); + inputLayout.attributes[loc].bufferIndex = firstVertexBinding + attribute.binding(); + } + const QVector bindings = m_vertexInputLayout.bindings(); + for (int i = 0, ie = bindings.count(); i != ie; ++i) { + const QRhiVertexInputBinding &binding(bindings[i]); + const int layoutIdx = firstVertexBinding + i; + inputLayout.layouts[layoutIdx].stepFunction = + binding.classification() == QRhiVertexInputBinding::PerInstance + ? MTLVertexStepFunctionPerInstance : MTLVertexStepFunctionPerVertex; + inputLayout.layouts[layoutIdx].stepRate = binding.instanceStepRate(); + inputLayout.layouts[layoutIdx].stride = binding.stride(); + } + + MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init]; + + rpDesc.vertexDescriptor = inputLayout; + + if (@available(macOS 10.13, iOS 11.0, *)) { + // Everything is immutable because we can guarantee that "neither the + // CPU nor the GPU will modify a buffer's contents between the time the + // buffer is set in a function's argument table and the time its + // associated command buffer completes execution" (as that's the point + // of our Vulkan-style buffer juggling in the first place). + const int vertexBufferCount = firstVertexBinding + bindings.count(); // cbuf + vbuf + const int fragmentBufferCount = firstVertexBinding; // cbuf + for (int i = 0; i < vertexBufferCount; ++i) + rpDesc.vertexBuffers[i].mutability = MTLMutabilityImmutable; + for (int i = 0; i < fragmentBufferCount; ++i) + rpDesc.fragmentBuffers[i].mutability = MTLMutabilityImmutable; + } + + for (const QRhiGraphicsShaderStage &shaderStage : qAsConst(m_shaderStages)) { + QString error; + QByteArray entryPoint; + id lib = rhiD->d->createMetalLib(shaderStage.shader(), shaderStage.shaderVariant(), &error, &entryPoint); + if (!lib) { + qWarning("MSL shader compilation failed: %s", qPrintable(error)); + return false; + } + id func = rhiD->d->createMSLShaderFunction(lib, entryPoint); + if (!func) { + qWarning("MSL function for entry point %s not found", entryPoint.constData()); + [lib release]; + return false; + } + switch (shaderStage.type()) { + case QRhiGraphicsShaderStage::Vertex: + rpDesc.vertexFunction = func; + d->vsLib = lib; + d->vsFunc = func; + break; + case QRhiGraphicsShaderStage::Fragment: + rpDesc.fragmentFunction = func; + d->fsLib = lib; + d->fsFunc = func; + break; + default: + [func release]; + [lib release]; + break; + } + } + + QMetalRenderPassDescriptor *rpD = QRHI_RES(QMetalRenderPassDescriptor, m_renderPassDesc); + + if (rpD->colorAttachmentCount) { + // defaults when no targetBlends are provided + rpDesc.colorAttachments[0].pixelFormat = MTLPixelFormat(rpD->colorFormat[0]); + rpDesc.colorAttachments[0].writeMask = MTLColorWriteMaskAll; + rpDesc.colorAttachments[0].blendingEnabled = false; + + Q_ASSERT(m_targetBlends.count() == rpD->colorAttachmentCount + || (m_targetBlends.isEmpty() && rpD->colorAttachmentCount == 1)); + + for (int i = 0, ie = m_targetBlends.count(); i != ie; ++i) { + const QRhiGraphicsPipeline::TargetBlend &b(m_targetBlends[i]); + rpDesc.colorAttachments[i].pixelFormat = MTLPixelFormat(rpD->colorFormat[i]); + rpDesc.colorAttachments[i].blendingEnabled = b.enable; + rpDesc.colorAttachments[i].sourceRGBBlendFactor = toMetalBlendFactor(b.srcColor); + rpDesc.colorAttachments[i].destinationRGBBlendFactor = toMetalBlendFactor(b.dstColor); + rpDesc.colorAttachments[i].rgbBlendOperation = toMetalBlendOp(b.opColor); + rpDesc.colorAttachments[i].sourceAlphaBlendFactor = toMetalBlendFactor(b.srcAlpha); + rpDesc.colorAttachments[i].destinationAlphaBlendFactor = toMetalBlendFactor(b.dstAlpha); + rpDesc.colorAttachments[i].alphaBlendOperation = toMetalBlendOp(b.opAlpha); + rpDesc.colorAttachments[i].writeMask = toMetalColorWriteMask(b.colorWrite); + } + } + + if (rpD->hasDepthStencil) { + // Must only be set when a depth-stencil buffer will actually be bound, + // validation blows up otherwise. + MTLPixelFormat fmt = MTLPixelFormat(rpD->dsFormat); + rpDesc.depthAttachmentPixelFormat = fmt; + if (fmt != MTLPixelFormatDepth16Unorm && fmt != MTLPixelFormatDepth32Float) + rpDesc.stencilAttachmentPixelFormat = fmt; + } + + rpDesc.sampleCount = rhiD->effectiveSampleCount(m_sampleCount); + + NSError *err = nil; + d->ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err]; + if (!d->ps) { + const QString msg = QString::fromNSString(err.localizedDescription); + qWarning("Failed to create render pipeline state: %s", qPrintable(msg)); + [rpDesc release]; + return false; + } + [rpDesc release]; + + MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init]; + dsDesc.depthCompareFunction = m_depthTest ? toMetalCompareOp(m_depthOp) : MTLCompareFunctionAlways; + dsDesc.depthWriteEnabled = m_depthWrite; + if (m_stencilTest) { + dsDesc.frontFaceStencil = [[MTLStencilDescriptor alloc] init]; + dsDesc.frontFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilFront.failOp); + dsDesc.frontFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilFront.depthFailOp); + dsDesc.frontFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilFront.passOp); + dsDesc.frontFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilFront.compareOp); + dsDesc.frontFaceStencil.readMask = m_stencilReadMask; + dsDesc.frontFaceStencil.writeMask = m_stencilWriteMask; + + dsDesc.backFaceStencil = [[MTLStencilDescriptor alloc] init]; + dsDesc.backFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilBack.failOp); + dsDesc.backFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilBack.depthFailOp); + dsDesc.backFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilBack.passOp); + dsDesc.backFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilBack.compareOp); + dsDesc.backFaceStencil.readMask = m_stencilReadMask; + dsDesc.backFaceStencil.writeMask = m_stencilWriteMask; + } + + d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc]; + [dsDesc release]; + + d->primitiveType = toMetalPrimitiveType(m_topology); + d->winding = m_frontFace == CCW ? MTLWindingCounterClockwise : MTLWindingClockwise; + d->cullMode = toMetalCullMode(m_cullMode); + + lastActiveFrameSlot = -1; + generation += 1; + rhiD->registerResource(this); + return true; +} + +QMetalCommandBuffer::QMetalCommandBuffer(QRhiImplementation *rhi) + : QRhiCommandBuffer(rhi), + d(new QMetalCommandBufferData) +{ + resetState(); +} + +QMetalCommandBuffer::~QMetalCommandBuffer() +{ + release(); + delete d; +} + +void QMetalCommandBuffer::release() +{ + // nothing to do here, we do not own the MTL cb object +} + +const QRhiNativeHandles *QMetalCommandBuffer::nativeHandles() +{ + nativeHandlesStruct.commandBuffer = d->cb; + nativeHandlesStruct.encoder = d->currentPassEncoder; + return &nativeHandlesStruct; +} + +void QMetalCommandBuffer::resetState() +{ + d->currentPassEncoder = nil; + d->currentPassRpDesc = nil; + resetPerPassState(); +} + +void QMetalCommandBuffer::resetPerPassState() +{ + currentTarget = nullptr; + resetPerPassCachedState(); +} + +void QMetalCommandBuffer::resetPerPassCachedState() +{ + currentPipeline = nullptr; + currentPipelineGeneration = 0; + currentSrb = nullptr; + currentSrbGeneration = 0; + currentResSlot = -1; + currentIndexBuffer = nullptr; + + d->currentFirstVertexBinding = -1; + d->currentVertexInputsBuffers.clear(); + d->currentVertexInputOffsets.clear(); +} + +QMetalSwapChain::QMetalSwapChain(QRhiImplementation *rhi) + : QRhiSwapChain(rhi), + rtWrapper(rhi), + cbWrapper(rhi), + d(new QMetalSwapChainData) +{ + for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) { + d->sem[i] = nullptr; + d->msaaTex[i] = nil; + } +} + +QMetalSwapChain::~QMetalSwapChain() +{ + release(); + delete d; +} + +void QMetalSwapChain::release() +{ + if (!d->layer) + return; + + for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) { + if (d->sem[i]) { + // the semaphores cannot be released if they do not have the initial value + dispatch_semaphore_wait(d->sem[i], DISPATCH_TIME_FOREVER); + dispatch_semaphore_signal(d->sem[i]); + + dispatch_release(d->sem[i]); + d->sem[i] = nullptr; + } + } + + for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) { + [d->msaaTex[i] release]; + d->msaaTex[i] = nil; + } + + d->layer = nullptr; + + QRHI_RES_RHI(QRhiMetal); + rhiD->swapchains.remove(this); + + QRHI_PROF; + QRHI_PROF_F(releaseSwapChain(this)); + + rhiD->unregisterResource(this); +} + +QRhiCommandBuffer *QMetalSwapChain::currentFrameCommandBuffer() +{ + return &cbWrapper; +} + +QRhiRenderTarget *QMetalSwapChain::currentFrameRenderTarget() +{ + return &rtWrapper; +} + +QSize QMetalSwapChain::surfacePixelSize() +{ + // may be called before build, must not access other than m_* + + NSView *v = (NSView *) m_window->winId(); + if (v) { + CAMetalLayer *layer = (CAMetalLayer *) [v layer]; + if (layer) { + CGSize size = [layer drawableSize]; + return QSize(size.width, size.height); + } + } + return QSize(); +} + +QRhiRenderPassDescriptor *QMetalSwapChain::newCompatibleRenderPassDescriptor() +{ + chooseFormats(); // ensure colorFormat and similar are filled out + + QRHI_RES_RHI(QRhiMetal); + QMetalRenderPassDescriptor *rpD = new QMetalRenderPassDescriptor(m_rhi); + rpD->colorAttachmentCount = 1; + rpD->hasDepthStencil = m_depthStencil != nullptr; + + rpD->colorFormat[0] = d->colorFormat; + + // m_depthStencil may not be built yet so cannot rely on computed fields in it + rpD->dsFormat = rhiD->d->dev.depth24Stencil8PixelFormatSupported + ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8; + + return rpD; +} + +void QMetalSwapChain::chooseFormats() +{ + QRHI_RES_RHI(QRhiMetal); + samples = rhiD->effectiveSampleCount(m_sampleCount); + // pick a format that is allowed for CAMetalLayer.pixelFormat + d->colorFormat = m_flags.testFlag(sRGB) ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm; + d->rhiColorFormat = QRhiTexture::BGRA8; +} + +bool QMetalSwapChain::buildOrResize() +{ + Q_ASSERT(m_window); + + const bool needsRegistration = !window || window != m_window; + + if (window && window != m_window) + release(); + // else no release(), this is intentional + + QRHI_RES_RHI(QRhiMetal); + if (needsRegistration) + rhiD->swapchains.insert(this); + + window = m_window; + + if (window->surfaceType() != QSurface::MetalSurface) { + qWarning("QMetalSwapChain only supports MetalSurface windows"); + return false; + } + + NSView *v = (NSView *) window->winId(); + d->layer = (CAMetalLayer *) [v layer]; + Q_ASSERT(d->layer); + + chooseFormats(); + if (d->colorFormat != d->layer.pixelFormat) + d->layer.pixelFormat = d->colorFormat; + + if (m_flags.testFlag(UsedAsTransferSource)) + d->layer.framebufferOnly = NO; + +#ifdef Q_OS_MACOS + if (m_flags.testFlag(NoVSync)) { + if (@available(macOS 10.13, *)) + d->layer.displaySyncEnabled = NO; + } +#endif + + m_currentPixelSize = surfacePixelSize(); + pixelSize = m_currentPixelSize; + + [d->layer setDevice: rhiD->d->dev]; + + for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) { + if (!d->sem[i]) + d->sem[i] = dispatch_semaphore_create(QMTL_FRAMES_IN_FLIGHT - 1); + } + + currentFrameSlot = 0; + frameCount = 0; + + ds = m_depthStencil ? QRHI_RES(QMetalRenderBuffer, m_depthStencil) : nullptr; + if (m_depthStencil && m_depthStencil->sampleCount() != m_sampleCount) { + qWarning("Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.", + m_depthStencil->sampleCount(), m_sampleCount); + } + if (m_depthStencil && m_depthStencil->pixelSize() != pixelSize) { + qWarning("Depth-stencil buffer's size (%dx%d) does not match the layer size (%dx%d). Expect problems.", + m_depthStencil->pixelSize().width(), m_depthStencil->pixelSize().height(), + pixelSize.width(), pixelSize.height()); + } + + rtWrapper.d->pixelSize = pixelSize; + rtWrapper.d->dpr = window->devicePixelRatio(); + rtWrapper.d->sampleCount = samples; + rtWrapper.d->colorAttCount = 1; + rtWrapper.d->dsAttCount = ds ? 1 : 0; + + qDebug("got CAMetalLayer, size %dx%d", pixelSize.width(), pixelSize.height()); + + if (samples > 1) { + MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init]; + desc.textureType = MTLTextureType2DMultisample; + desc.pixelFormat = d->colorFormat; + desc.width = pixelSize.width(); + desc.height = pixelSize.height(); + desc.sampleCount = samples; + desc.resourceOptions = MTLResourceStorageModePrivate; + desc.storageMode = MTLStorageModePrivate; + desc.usage = MTLTextureUsageRenderTarget; + for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) { + [d->msaaTex[i] release]; + d->msaaTex[i] = [rhiD->d->dev newTextureWithDescriptor: desc]; + } + [desc release]; + } + + QRHI_PROF; + QRHI_PROF_F(resizeSwapChain(this, QMTL_FRAMES_IN_FLIGHT, samples > 1 ? QMTL_FRAMES_IN_FLIGHT : 0, samples)); + + if (needsRegistration) + rhiD->registerResource(this); + + return true; +} + +QT_END_NAMESPACE diff --git a/src/gui/rhi/qrhimetal_p.h b/src/gui/rhi/qrhimetal_p.h new file mode 100644 index 0000000000..094801c58c --- /dev/null +++ b/src/gui/rhi/qrhimetal_p.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Gui module +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QRHIMETAL_H +#define QRHIMETAL_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +// no Metal includes here, the user code may be plain C++ + +QT_BEGIN_NAMESPACE + +struct Q_GUI_EXPORT QRhiMetalInitParams : public QRhiInitParams +{ +}; + +struct Q_GUI_EXPORT QRhiMetalNativeHandles : public QRhiNativeHandles +{ + void *dev = nullptr; // id + void *cmdQueue = nullptr; // id +}; + +struct Q_GUI_EXPORT QRhiMetalTextureNativeHandles : public QRhiNativeHandles +{ + void *texture = nullptr; // id +}; + +struct Q_GUI_EXPORT QRhiMetalCommandBufferNativeHandles : public QRhiNativeHandles +{ + void *commandBuffer = nullptr; // id + void *encoder = nullptr; // id +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/rhi/qrhimetal_p_p.h b/src/gui/rhi/qrhimetal_p_p.h new file mode 100644 index 0000000000..039a5bdbf6 --- /dev/null +++ b/src/gui/rhi/qrhimetal_p_p.h @@ -0,0 +1,410 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Gui module +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QRHIMETAL_P_H +#define QRHIMETAL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qrhimetal_p.h" +#include "qrhi_p_p.h" +#include + +QT_BEGIN_NAMESPACE + +static const int QMTL_FRAMES_IN_FLIGHT = 2; + +// have to hide the ObjC stuff, this header cannot contain MTL* at all +struct QMetalBufferData; + +struct QMetalBuffer : public QRhiBuffer +{ + QMetalBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size); + ~QMetalBuffer(); + void release() override; + bool build() override; + + QMetalBufferData *d; + uint generation = 0; + int lastActiveFrameSlot = -1; + friend class QRhiMetal; + friend struct QMetalShaderResourceBindings; +}; + +struct QMetalRenderBufferData; + +struct QMetalRenderBuffer : public QRhiRenderBuffer +{ + QMetalRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize, + int sampleCount, QRhiRenderBuffer::Flags flags); + ~QMetalRenderBuffer(); + void release() override; + bool build() override; + QRhiTexture::Format backingFormat() const override; + + QMetalRenderBufferData *d; + int samples = 1; + uint generation = 0; + int lastActiveFrameSlot = -1; + friend class QRhiMetal; +}; + +struct QMetalTextureData; + +struct QMetalTexture : public QRhiTexture +{ + QMetalTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, + int sampleCount, Flags flags); + ~QMetalTexture(); + void release() override; + bool build() override; + bool buildFrom(const QRhiNativeHandles *src) override; + const QRhiNativeHandles *nativeHandles() override; + + bool prepareBuild(QSize *adjustedSize = nullptr); + + QMetalTextureData *d; + QRhiMetalTextureNativeHandles nativeHandlesStruct; + int mipLevelCount = 0; + int samples = 1; + uint generation = 0; + int lastActiveFrameSlot = -1; + friend class QRhiMetal; + friend struct QMetalShaderResourceBindings; +}; + +struct QMetalSamplerData; + +struct QMetalSampler : public QRhiSampler +{ + QMetalSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode, + AddressMode u, AddressMode v); + ~QMetalSampler(); + void release() override; + bool build() override; + + QMetalSamplerData *d; + uint generation = 0; + int lastActiveFrameSlot = -1; + friend class QRhiMetal; + friend struct QMetalShaderResourceBindings; +}; + +struct QMetalRenderPassDescriptor : public QRhiRenderPassDescriptor +{ + QMetalRenderPassDescriptor(QRhiImplementation *rhi); + ~QMetalRenderPassDescriptor(); + void release() override; + + // there is no MTLRenderPassDescriptor here as one will be created for each pass in beginPass() + + // but the things needed for the render pipeline descriptor have to be provided + static const int MAX_COLOR_ATTACHMENTS = 8; + int colorAttachmentCount = 0; + bool hasDepthStencil = false; + int colorFormat[MAX_COLOR_ATTACHMENTS]; + int dsFormat; +}; + +struct QMetalRenderTargetData; + +struct QMetalReferenceRenderTarget : public QRhiRenderTarget +{ + QMetalReferenceRenderTarget(QRhiImplementation *rhi); + ~QMetalReferenceRenderTarget(); + void release() override; + + QSize pixelSize() const override; + float devicePixelRatio() const override; + int sampleCount() const override; + + QMetalRenderTargetData *d; +}; + +struct QMetalTextureRenderTarget : public QRhiTextureRenderTarget +{ + QMetalTextureRenderTarget(QRhiImplementation *rhi, const QRhiTextureRenderTargetDescription &desc, Flags flags); + ~QMetalTextureRenderTarget(); + void release() override; + + QSize pixelSize() const override; + float devicePixelRatio() const override; + int sampleCount() const override; + + QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override; + bool build() override; + + QMetalRenderTargetData *d; + friend class QRhiMetal; +}; + +struct QMetalShaderResourceBindings : public QRhiShaderResourceBindings +{ + QMetalShaderResourceBindings(QRhiImplementation *rhi); + ~QMetalShaderResourceBindings(); + void release() override; + bool build() override; + + QVector sortedBindings; + int maxBinding = -1; + + struct BoundUniformBufferData { + quint64 id; + uint generation; + }; + struct BoundSampledTextureData { + quint64 texId; + uint texGeneration; + quint64 samplerId; + uint samplerGeneration; + }; + struct BoundResourceData { + union { + BoundUniformBufferData ubuf; + BoundSampledTextureData stex; + }; + }; + QVector boundResourceData; + + uint generation = 0; + friend class QRhiMetal; +}; + +struct QMetalGraphicsPipelineData; + +struct QMetalGraphicsPipeline : public QRhiGraphicsPipeline +{ + QMetalGraphicsPipeline(QRhiImplementation *rhi); + ~QMetalGraphicsPipeline(); + void release() override; + bool build() override; + + QMetalGraphicsPipelineData *d; + uint generation = 0; + int lastActiveFrameSlot = -1; + friend class QRhiMetal; +}; + +struct QMetalCommandBufferData; +struct QMetalSwapChain; + +struct QMetalCommandBuffer : public QRhiCommandBuffer +{ + QMetalCommandBuffer(QRhiImplementation *rhi); + ~QMetalCommandBuffer(); + void release() override; + + QMetalCommandBufferData *d = nullptr; + QRhiMetalCommandBufferNativeHandles nativeHandlesStruct; + + QRhiRenderTarget *currentTarget; + QRhiGraphicsPipeline *currentPipeline; + uint currentPipelineGeneration; + QRhiShaderResourceBindings *currentSrb; + uint currentSrbGeneration; + int currentResSlot; + QRhiBuffer *currentIndexBuffer; + quint32 currentIndexOffset; + QRhiCommandBuffer::IndexFormat currentIndexFormat; + + const QRhiNativeHandles *nativeHandles(); + void resetState(); + void resetPerPassState(); + void resetPerPassCachedState(); +}; + +struct QMetalSwapChainData; + +struct QMetalSwapChain : public QRhiSwapChain +{ + QMetalSwapChain(QRhiImplementation *rhi); + ~QMetalSwapChain(); + void release() override; + + QRhiCommandBuffer *currentFrameCommandBuffer() override; + QRhiRenderTarget *currentFrameRenderTarget() override; + QSize surfacePixelSize() override; + + QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override; + + bool buildOrResize() override; + + void chooseFormats(); + + QWindow *window = nullptr; + QSize pixelSize; + int currentFrameSlot = 0; // 0..QMTL_FRAMES_IN_FLIGHT-1 + int frameCount = 0; + int samples = 1; + QMetalReferenceRenderTarget rtWrapper; + QMetalCommandBuffer cbWrapper; + QMetalRenderBuffer *ds = nullptr; + QMetalSwapChainData *d = nullptr; +}; + +struct QRhiMetalData; + +class QRhiMetal : public QRhiImplementation +{ +public: + QRhiMetal(QRhiMetalInitParams *params, QRhiMetalNativeHandles *importDevice = nullptr); + ~QRhiMetal(); + + bool create(QRhi::Flags flags) override; + void destroy() override; + + QRhiGraphicsPipeline *createGraphicsPipeline() override; + QRhiShaderResourceBindings *createShaderResourceBindings() override; + QRhiBuffer *createBuffer(QRhiBuffer::Type type, + QRhiBuffer::UsageFlags usage, + int size) override; + QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type, + const QSize &pixelSize, + int sampleCount, + QRhiRenderBuffer::Flags flags) override; + QRhiTexture *createTexture(QRhiTexture::Format format, + const QSize &pixelSize, + int sampleCount, + QRhiTexture::Flags flags) override; + QRhiSampler *createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, + QRhiSampler::Filter mipmapMode, + QRhiSampler:: AddressMode u, QRhiSampler::AddressMode v) override; + + QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc, + QRhiTextureRenderTarget::Flags flags) override; + + QRhiSwapChain *createSwapChain() override; + QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override; + QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override; + QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override; + QRhi::FrameOpResult endOffscreenFrame() override; + QRhi::FrameOpResult finish() override; + + void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override; + + void beginPass(QRhiCommandBuffer *cb, + QRhiRenderTarget *rt, + const QColor &colorClearValue, + const QRhiDepthStencilClearValue &depthStencilClearValue, + QRhiResourceUpdateBatch *resourceUpdates) override; + void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override; + + void setGraphicsPipeline(QRhiCommandBuffer *cb, + QRhiGraphicsPipeline *ps) override; + + void setShaderResources(QRhiCommandBuffer *cb, + QRhiShaderResourceBindings *srb, + int dynamicOffsetCount, + const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) override; + + void setVertexInput(QRhiCommandBuffer *cb, + int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings, + QRhiBuffer *indexBuf, quint32 indexOffset, + QRhiCommandBuffer::IndexFormat indexFormat) override; + + void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) override; + void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override; + void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override; + void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override; + + void draw(QRhiCommandBuffer *cb, quint32 vertexCount, + quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override; + + void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, + quint32 instanceCount, quint32 firstIndex, + qint32 vertexOffset, quint32 firstInstance) override; + + void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) override; + void debugMarkEnd(QRhiCommandBuffer *cb) override; + void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) override; + + const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) override; + void beginExternal(QRhiCommandBuffer *cb) override; + void endExternal(QRhiCommandBuffer *cb) override; + + QVector supportedSampleCounts() const override; + int ubufAlignment() const override; + bool isYUpInFramebuffer() const override; + bool isYUpInNDC() const override; + bool isClipDepthZeroToOne() const override; + QMatrix4x4 clipSpaceCorrMatrix() const override; + bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const override; + bool isFeatureSupported(QRhi::Feature feature) const override; + int resourceLimit(QRhi::ResourceLimit limit) const override; + const QRhiNativeHandles *nativeHandles() override; + void sendVMemStatsToProfiler() override; + + void executeDeferredReleases(bool forced = false); + void finishActiveReadbacks(bool forced = false); + qsizetype subresUploadByteSize(const QRhiTextureSubresourceUploadDescription &subresDesc) const; + void enqueueSubresUpload(QMetalTexture *texD, void *mp, void *blitEncPtr, + int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc, + qsizetype *curOfs); + void enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates); + void executeBufferHostWritesForCurrentFrame(QMetalBuffer *bufD); + void enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD, QMetalCommandBuffer *cbD, + int dynamicOffsetCount, + const QRhiCommandBuffer::DynamicOffset *dynamicOffsets, + bool offsetOnlyChange); + int effectiveSampleCount(int sampleCount) const; + + bool importedDevice = false; + bool importedCmdQueue = false; + bool inFrame = false; + bool inPass = false; + QMetalSwapChain *currentSwapChain = nullptr; + QSet swapchains; + QRhiMetalNativeHandles nativeHandlesStruct; + + struct { + int maxTextureSize = 4096; + } caps; + + QRhiMetalData *d = nullptr; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/rhi/qrhinull.cpp b/src/gui/rhi/qrhinull.cpp new file mode 100644 index 0000000000..ad7b74a5c3 --- /dev/null +++ b/src/gui/rhi/qrhinull.cpp @@ -0,0 +1,709 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Gui module +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qrhinull_p_p.h" +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QRhiNullInitParams + \inmodule QtRhi + \brief Null backend specific initialization parameters. + + A Null QRhi needs no special parameters for initialization. + + \badcode + QRhiNullInitParams params; + rhi = QRhi::create(QRhi::Null, ¶ms); + \endcode + + The Null backend does not issue any graphics calls and creates no + resources. All QRhi operations will succeed as normal so applications can + still be run, albeit potentially at an unthrottled speed, depending on + their frame rendering strategy. The backend reports resources to + QRhiProfiler as usual. + */ + +/*! + \class QRhiNullNativeHandles + \inmodule QtRhi + \brief Empty. + */ + +/*! + \class QRhiNullTextureNativeHandles + \inmodule QtRhi + \brief Empty. + */ + +QRhiNull::QRhiNull(QRhiNullInitParams *params) + : offscreenCommandBuffer(this) +{ + Q_UNUSED(params); +} + +bool QRhiNull::create(QRhi::Flags flags) +{ + Q_UNUSED(flags); + return true; +} + +void QRhiNull::destroy() +{ +} + +QVector QRhiNull::supportedSampleCounts() const +{ + return { 1 }; +} + +QRhiSwapChain *QRhiNull::createSwapChain() +{ + return new QNullSwapChain(this); +} + +QRhiBuffer *QRhiNull::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, int size) +{ + return new QNullBuffer(this, type, usage, size); +} + +int QRhiNull::ubufAlignment() const +{ + return 256; +} + +bool QRhiNull::isYUpInFramebuffer() const +{ + return false; +} + +bool QRhiNull::isYUpInNDC() const +{ + return true; +} + +bool QRhiNull::isClipDepthZeroToOne() const +{ + return true; +} + +QMatrix4x4 QRhiNull::clipSpaceCorrMatrix() const +{ + return QMatrix4x4(); // identity +} + +bool QRhiNull::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const +{ + Q_UNUSED(format); + Q_UNUSED(flags); + return true; +} + +bool QRhiNull::isFeatureSupported(QRhi::Feature feature) const +{ + Q_UNUSED(feature); + return true; +} + +int QRhiNull::resourceLimit(QRhi::ResourceLimit limit) const +{ + switch (limit) { + case QRhi::TextureSizeMin: + return 1; + case QRhi::TextureSizeMax: + return 16384; + case QRhi::MaxColorAttachments: + return 8; + case QRhi::FramesInFlight: + return 2; // dummy + default: + Q_UNREACHABLE(); + return 0; + } +} + +const QRhiNativeHandles *QRhiNull::nativeHandles() +{ + return &nativeHandlesStruct; +} + +void QRhiNull::sendVMemStatsToProfiler() +{ + // nothing to do here +} + +QRhiRenderBuffer *QRhiNull::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize, + int sampleCount, QRhiRenderBuffer::Flags flags) +{ + return new QNullRenderBuffer(this, type, pixelSize, sampleCount, flags); +} + +QRhiTexture *QRhiNull::createTexture(QRhiTexture::Format format, const QSize &pixelSize, + int sampleCount, QRhiTexture::Flags flags) +{ + return new QNullTexture(this, format, pixelSize, sampleCount, flags); +} + +QRhiSampler *QRhiNull::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, + QRhiSampler::Filter mipmapMode, + QRhiSampler::AddressMode u, QRhiSampler::AddressMode v) +{ + return new QNullSampler(this, magFilter, minFilter, mipmapMode, u, v); +} + +QRhiTextureRenderTarget *QRhiNull::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc, + QRhiTextureRenderTarget::Flags flags) +{ + return new QNullTextureRenderTarget(this, desc, flags); +} + +QRhiGraphicsPipeline *QRhiNull::createGraphicsPipeline() +{ + return new QNullGraphicsPipeline(this); +} + +QRhiShaderResourceBindings *QRhiNull::createShaderResourceBindings() +{ + return new QNullShaderResourceBindings(this); +} + +void QRhiNull::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps) +{ + Q_UNUSED(cb); + Q_UNUSED(ps); +} + +void QRhiNull::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb, + int dynamicOffsetCount, + const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) +{ + Q_UNUSED(cb); + Q_UNUSED(srb); + Q_UNUSED(dynamicOffsetCount); + Q_UNUSED(dynamicOffsets); +} + +void QRhiNull::setVertexInput(QRhiCommandBuffer *cb, + int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings, + QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat) +{ + Q_UNUSED(cb); + Q_UNUSED(startBinding); + Q_UNUSED(bindingCount); + Q_UNUSED(bindings); + Q_UNUSED(indexBuf); + Q_UNUSED(indexOffset); + Q_UNUSED(indexFormat); +} + +void QRhiNull::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) +{ + Q_UNUSED(cb); + Q_UNUSED(viewport); +} + +void QRhiNull::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) +{ + Q_UNUSED(cb); + Q_UNUSED(scissor); +} + +void QRhiNull::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) +{ + Q_UNUSED(cb); + Q_UNUSED(c); +} + +void QRhiNull::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) +{ + Q_UNUSED(cb); + Q_UNUSED(refValue); +} + +void QRhiNull::draw(QRhiCommandBuffer *cb, quint32 vertexCount, + quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) +{ + Q_UNUSED(cb); + Q_UNUSED(vertexCount); + Q_UNUSED(instanceCount); + Q_UNUSED(firstVertex); + Q_UNUSED(firstInstance); +} + +void QRhiNull::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, + quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance) +{ + Q_UNUSED(cb); + Q_UNUSED(indexCount); + Q_UNUSED(instanceCount); + Q_UNUSED(firstIndex); + Q_UNUSED(vertexOffset); + Q_UNUSED(firstInstance); +} + +void QRhiNull::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) +{ + Q_UNUSED(cb); + Q_UNUSED(name); +} + +void QRhiNull::debugMarkEnd(QRhiCommandBuffer *cb) +{ + Q_UNUSED(cb); +} + +void QRhiNull::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) +{ + Q_UNUSED(cb); + Q_UNUSED(msg); +} + +const QRhiNativeHandles *QRhiNull::nativeHandles(QRhiCommandBuffer *cb) +{ + Q_UNUSED(cb); + return nullptr; +} + +void QRhiNull::beginExternal(QRhiCommandBuffer *cb) +{ + Q_UNUSED(cb); +} + +void QRhiNull::endExternal(QRhiCommandBuffer *cb) +{ + Q_UNUSED(cb); +} + +QRhi::FrameOpResult QRhiNull::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) +{ + Q_UNUSED(flags); + currentSwapChain = swapChain; + QRhiProfilerPrivate *rhiP = profilerPrivateOrNull(); + QRHI_PROF_F(beginSwapChainFrame(swapChain)); + return QRhi::FrameOpSuccess; +} + +QRhi::FrameOpResult QRhiNull::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) +{ + Q_UNUSED(flags); + QNullSwapChain *swapChainD = QRHI_RES(QNullSwapChain, swapChain); + QRhiProfilerPrivate *rhiP = profilerPrivateOrNull(); + QRHI_PROF_F(endSwapChainFrame(swapChain, swapChainD->frameCount + 1)); + QRHI_PROF_F(swapChainFrameGpuTime(swapChain, 0.000666f)); + swapChainD->frameCount += 1; + currentSwapChain = nullptr; + return QRhi::FrameOpSuccess; +} + +QRhi::FrameOpResult QRhiNull::beginOffscreenFrame(QRhiCommandBuffer **cb) +{ + *cb = &offscreenCommandBuffer; + return QRhi::FrameOpSuccess; +} + +QRhi::FrameOpResult QRhiNull::endOffscreenFrame() +{ + return QRhi::FrameOpSuccess; +} + +QRhi::FrameOpResult QRhiNull::finish() +{ + return QRhi::FrameOpSuccess; +} + +void QRhiNull::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) +{ + Q_UNUSED(cb); + QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates); + for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) { + if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) { + QRhiReadbackResult *result = u.read.result; + QRhiTexture *tex = u.read.rb.texture(); + if (tex) { + result->format = tex->format(); + result->pixelSize = q->sizeForMipLevel(u.read.rb.level(), tex->pixelSize()); + } else { + Q_ASSERT(currentSwapChain); + result->format = QRhiTexture::RGBA8; + result->pixelSize = currentSwapChain->currentPixelSize(); + } + quint32 byteSize = 0; + textureFormatInfo(result->format, result->pixelSize, nullptr, &byteSize); + result->data.fill(0, byteSize); + if (result->completed) + result->completed(); + } + } + ud->free(); +} + +void QRhiNull::beginPass(QRhiCommandBuffer *cb, + QRhiRenderTarget *rt, + const QColor &colorClearValue, + const QRhiDepthStencilClearValue &depthStencilClearValue, + QRhiResourceUpdateBatch *resourceUpdates) +{ + Q_UNUSED(rt); + Q_UNUSED(colorClearValue); + Q_UNUSED(depthStencilClearValue); + if (resourceUpdates) + resourceUpdate(cb, resourceUpdates); +} + +void QRhiNull::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) +{ + if (resourceUpdates) + resourceUpdate(cb, resourceUpdates); +} + +QNullBuffer::QNullBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size) + : QRhiBuffer(rhi, type, usage, size) +{ +} + +QNullBuffer::~QNullBuffer() +{ + release(); +} + +void QNullBuffer::release() +{ + QRHI_PROF; + QRHI_PROF_F(releaseBuffer(this)); +} + +bool QNullBuffer::build() +{ + QRHI_PROF; + QRHI_PROF_F(newBuffer(this, m_size, 1, 0)); + return true; +} + +QNullRenderBuffer::QNullRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize, + int sampleCount, QRhiRenderBuffer::Flags flags) + : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags) +{ +} + +QNullRenderBuffer::~QNullRenderBuffer() +{ + release(); +} + +void QNullRenderBuffer::release() +{ + QRHI_PROF; + QRHI_PROF_F(releaseRenderBuffer(this)); +} + +bool QNullRenderBuffer::build() +{ + QRHI_PROF; + QRHI_PROF_F(newRenderBuffer(this, false, false, 1)); + return true; +} + +QRhiTexture::Format QNullRenderBuffer::backingFormat() const +{ + return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat; +} + +QNullTexture::QNullTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, + int sampleCount, Flags flags) + : QRhiTexture(rhi, format, pixelSize, sampleCount, flags) +{ +} + +QNullTexture::~QNullTexture() +{ + release(); +} + +void QNullTexture::release() +{ + QRHI_PROF; + QRHI_PROF_F(releaseTexture(this)); +} + +bool QNullTexture::build() +{ + const bool isCube = m_flags.testFlag(CubeMap); + const bool hasMipMaps = m_flags.testFlag(MipMapped); + QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize; + const int mipLevelCount = hasMipMaps ? qCeil(log2(qMax(size.width(), size.height()))) + 1 : 1; + QRHI_PROF; + QRHI_PROF_F(newTexture(this, true, mipLevelCount, isCube ? 6 : 1, 1)); + return true; +} + +bool QNullTexture::buildFrom(const QRhiNativeHandles *src) +{ + Q_UNUSED(src); + const bool isCube = m_flags.testFlag(CubeMap); + const bool hasMipMaps = m_flags.testFlag(MipMapped); + QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize; + const int mipLevelCount = hasMipMaps ? qCeil(log2(qMax(size.width(), size.height()))) + 1 : 1; + QRHI_PROF; + QRHI_PROF_F(newTexture(this, false, mipLevelCount, isCube ? 6 : 1, 1)); + return true; +} + +const QRhiNativeHandles *QNullTexture::nativeHandles() +{ + return &nativeHandlesStruct; +} + +QNullSampler::QNullSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode, + AddressMode u, AddressMode v) + : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v) +{ +} + +QNullSampler::~QNullSampler() +{ + release(); +} + +void QNullSampler::release() +{ +} + +bool QNullSampler::build() +{ + return true; +} + +QNullRenderPassDescriptor::QNullRenderPassDescriptor(QRhiImplementation *rhi) + : QRhiRenderPassDescriptor(rhi) +{ +} + +QNullRenderPassDescriptor::~QNullRenderPassDescriptor() +{ + release(); +} + +void QNullRenderPassDescriptor::release() +{ +} + +QNullReferenceRenderTarget::QNullReferenceRenderTarget(QRhiImplementation *rhi) + : QRhiRenderTarget(rhi), + d(rhi) +{ +} + +QNullReferenceRenderTarget::~QNullReferenceRenderTarget() +{ + release(); +} + +void QNullReferenceRenderTarget::release() +{ +} + +QSize QNullReferenceRenderTarget::pixelSize() const +{ + return d.pixelSize; +} + +float QNullReferenceRenderTarget::devicePixelRatio() const +{ + return d.dpr; +} + +int QNullReferenceRenderTarget::sampleCount() const +{ + return 1; +} + +QNullTextureRenderTarget::QNullTextureRenderTarget(QRhiImplementation *rhi, + const QRhiTextureRenderTargetDescription &desc, + Flags flags) + : QRhiTextureRenderTarget(rhi, desc, flags), + d(rhi) +{ +} + +QNullTextureRenderTarget::~QNullTextureRenderTarget() +{ + release(); +} + +void QNullTextureRenderTarget::release() +{ +} + +QRhiRenderPassDescriptor *QNullTextureRenderTarget::newCompatibleRenderPassDescriptor() +{ + return new QNullRenderPassDescriptor(m_rhi); +} + +bool QNullTextureRenderTarget::build() +{ + d.rp = QRHI_RES(QNullRenderPassDescriptor, m_renderPassDesc); + const QVector colorAttachments = m_desc.colorAttachments(); + if (!colorAttachments.isEmpty()) { + QRhiTexture *tex = colorAttachments.first().texture(); + QRhiRenderBuffer *rb = colorAttachments.first().renderBuffer(); + d.pixelSize = tex ? tex->pixelSize() : rb->pixelSize(); + } else if (m_desc.depthStencilBuffer()) { + d.pixelSize = m_desc.depthStencilBuffer()->pixelSize(); + } else if (m_desc.depthTexture()) { + d.pixelSize = m_desc.depthTexture()->pixelSize(); + } + return true; +} + +QSize QNullTextureRenderTarget::pixelSize() const +{ + return d.pixelSize; +} + +float QNullTextureRenderTarget::devicePixelRatio() const +{ + return d.dpr; +} + +int QNullTextureRenderTarget::sampleCount() const +{ + return 1; +} + +QNullShaderResourceBindings::QNullShaderResourceBindings(QRhiImplementation *rhi) + : QRhiShaderResourceBindings(rhi) +{ +} + +QNullShaderResourceBindings::~QNullShaderResourceBindings() +{ + release(); +} + +void QNullShaderResourceBindings::release() +{ +} + +bool QNullShaderResourceBindings::build() +{ + return true; +} + +QNullGraphicsPipeline::QNullGraphicsPipeline(QRhiImplementation *rhi) + : QRhiGraphicsPipeline(rhi) +{ +} + +QNullGraphicsPipeline::~QNullGraphicsPipeline() +{ + release(); +} + +void QNullGraphicsPipeline::release() +{ +} + +bool QNullGraphicsPipeline::build() +{ + return true; +} + +QNullCommandBuffer::QNullCommandBuffer(QRhiImplementation *rhi) + : QRhiCommandBuffer(rhi) +{ +} + +QNullCommandBuffer::~QNullCommandBuffer() +{ + release(); +} + +void QNullCommandBuffer::release() +{ + // nothing to do here +} + +QNullSwapChain::QNullSwapChain(QRhiImplementation *rhi) + : QRhiSwapChain(rhi), + rt(rhi), + cb(rhi) +{ +} + +QNullSwapChain::~QNullSwapChain() +{ + release(); +} + +void QNullSwapChain::release() +{ + QRHI_PROF; + QRHI_PROF_F(releaseSwapChain(this)); +} + +QRhiCommandBuffer *QNullSwapChain::currentFrameCommandBuffer() +{ + return &cb; +} + +QRhiRenderTarget *QNullSwapChain::currentFrameRenderTarget() +{ + return &rt; +} + +QSize QNullSwapChain::surfacePixelSize() +{ + return QSize(1280, 720); +} + +QRhiRenderPassDescriptor *QNullSwapChain::newCompatibleRenderPassDescriptor() +{ + return new QNullRenderPassDescriptor(m_rhi); +} + +bool QNullSwapChain::buildOrResize() +{ + m_currentPixelSize = surfacePixelSize(); + rt.d.rp = QRHI_RES(QNullRenderPassDescriptor, m_renderPassDesc); + rt.d.pixelSize = m_currentPixelSize; + frameCount = 0; + QRHI_PROF; + QRHI_PROF_F(resizeSwapChain(this, 1, 0, 1)); + return true; +} + +QT_END_NAMESPACE diff --git a/src/gui/rhi/qrhinull_p.h b/src/gui/rhi/qrhinull_p.h new file mode 100644 index 0000000000..7d3ce5dbf1 --- /dev/null +++ b/src/gui/rhi/qrhinull_p.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Gui module +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QRHINULL_H +#define QRHINULL_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +QT_BEGIN_NAMESPACE + +struct Q_GUI_EXPORT QRhiNullInitParams : public QRhiInitParams +{ +}; + +struct Q_GUI_EXPORT QRhiNullNativeHandles : public QRhiNativeHandles +{ +}; + +struct Q_GUI_EXPORT QRhiNullTextureNativeHandles : public QRhiNativeHandles +{ +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/rhi/qrhinull_p_p.h b/src/gui/rhi/qrhinull_p_p.h new file mode 100644 index 0000000000..da48b72656 --- /dev/null +++ b/src/gui/rhi/qrhinull_p_p.h @@ -0,0 +1,279 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Gui module +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QRHINULL_P_H +#define QRHINULL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qrhinull_p.h" +#include "qrhi_p_p.h" + +QT_BEGIN_NAMESPACE + +struct QNullBuffer : public QRhiBuffer +{ + QNullBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size); + ~QNullBuffer(); + void release() override; + bool build() override; +}; + +struct QNullRenderBuffer : public QRhiRenderBuffer +{ + QNullRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize, + int sampleCount, QRhiRenderBuffer::Flags flags); + ~QNullRenderBuffer(); + void release() override; + bool build() override; + QRhiTexture::Format backingFormat() const override; +}; + +struct QNullTexture : public QRhiTexture +{ + QNullTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, + int sampleCount, Flags flags); + ~QNullTexture(); + void release() override; + bool build() override; + bool buildFrom(const QRhiNativeHandles *src) override; + const QRhiNativeHandles *nativeHandles() override; + + QRhiNullTextureNativeHandles nativeHandlesStruct; +}; + +struct QNullSampler : public QRhiSampler +{ + QNullSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode, + AddressMode u, AddressMode v); + ~QNullSampler(); + void release() override; + bool build() override; +}; + +struct QNullRenderPassDescriptor : public QRhiRenderPassDescriptor +{ + QNullRenderPassDescriptor(QRhiImplementation *rhi); + ~QNullRenderPassDescriptor(); + void release() override; +}; + +struct QNullRenderTargetData +{ + QNullRenderTargetData(QRhiImplementation *) { } + + QNullRenderPassDescriptor *rp = nullptr; + QSize pixelSize; + float dpr = 1; +}; + +struct QNullReferenceRenderTarget : public QRhiRenderTarget +{ + QNullReferenceRenderTarget(QRhiImplementation *rhi); + ~QNullReferenceRenderTarget(); + void release() override; + + QSize pixelSize() const override; + float devicePixelRatio() const override; + int sampleCount() const override; + + QNullRenderTargetData d; +}; + +struct QNullTextureRenderTarget : public QRhiTextureRenderTarget +{ + QNullTextureRenderTarget(QRhiImplementation *rhi, const QRhiTextureRenderTargetDescription &desc, Flags flags); + ~QNullTextureRenderTarget(); + void release() override; + + QSize pixelSize() const override; + float devicePixelRatio() const override; + int sampleCount() const override; + + QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override; + bool build() override; + + QNullRenderTargetData d; +}; + +struct QNullShaderResourceBindings : public QRhiShaderResourceBindings +{ + QNullShaderResourceBindings(QRhiImplementation *rhi); + ~QNullShaderResourceBindings(); + void release() override; + bool build() override; +}; + +struct QNullGraphicsPipeline : public QRhiGraphicsPipeline +{ + QNullGraphicsPipeline(QRhiImplementation *rhi); + ~QNullGraphicsPipeline(); + void release() override; + bool build() override; +}; + +struct QNullCommandBuffer : public QRhiCommandBuffer +{ + QNullCommandBuffer(QRhiImplementation *rhi); + ~QNullCommandBuffer(); + void release() override; +}; + +struct QNullSwapChain : public QRhiSwapChain +{ + QNullSwapChain(QRhiImplementation *rhi); + ~QNullSwapChain(); + void release() override; + + QRhiCommandBuffer *currentFrameCommandBuffer() override; + QRhiRenderTarget *currentFrameRenderTarget() override; + + QSize surfacePixelSize() override; + + QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override; + bool buildOrResize() override; + + QNullReferenceRenderTarget rt; + QNullCommandBuffer cb; + int frameCount = 0; +}; + +class QRhiNull : public QRhiImplementation +{ +public: + QRhiNull(QRhiNullInitParams *params); + + bool create(QRhi::Flags flags) override; + void destroy() override; + + QRhiGraphicsPipeline *createGraphicsPipeline() override; + QRhiShaderResourceBindings *createShaderResourceBindings() override; + QRhiBuffer *createBuffer(QRhiBuffer::Type type, + QRhiBuffer::UsageFlags usage, + int size) override; + QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type, + const QSize &pixelSize, + int sampleCount, + QRhiRenderBuffer::Flags flags) override; + QRhiTexture *createTexture(QRhiTexture::Format format, + const QSize &pixelSize, + int sampleCount, + QRhiTexture::Flags flags) override; + QRhiSampler *createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, + QRhiSampler::Filter mipmapMode, + QRhiSampler:: AddressMode u, QRhiSampler::AddressMode v) override; + + QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc, + QRhiTextureRenderTarget::Flags flags) override; + + QRhiSwapChain *createSwapChain() override; + QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override; + QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override; + QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override; + QRhi::FrameOpResult endOffscreenFrame() override; + QRhi::FrameOpResult finish() override; + + void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override; + + void beginPass(QRhiCommandBuffer *cb, + QRhiRenderTarget *rt, + const QColor &colorClearValue, + const QRhiDepthStencilClearValue &depthStencilClearValue, + QRhiResourceUpdateBatch *resourceUpdates) override; + void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override; + + void setGraphicsPipeline(QRhiCommandBuffer *cb, + QRhiGraphicsPipeline *ps) override; + + void setShaderResources(QRhiCommandBuffer *cb, + QRhiShaderResourceBindings *srb, + int dynamicOffsetCount, + const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) override; + + void setVertexInput(QRhiCommandBuffer *cb, + int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings, + QRhiBuffer *indexBuf, quint32 indexOffset, + QRhiCommandBuffer::IndexFormat indexFormat) override; + + void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) override; + void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override; + void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override; + void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override; + + void draw(QRhiCommandBuffer *cb, quint32 vertexCount, + quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override; + + void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, + quint32 instanceCount, quint32 firstIndex, + qint32 vertexOffset, quint32 firstInstance) override; + + void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) override; + void debugMarkEnd(QRhiCommandBuffer *cb) override; + void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) override; + + const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) override; + void beginExternal(QRhiCommandBuffer *cb) override; + void endExternal(QRhiCommandBuffer *cb) override; + + QVector supportedSampleCounts() const override; + int ubufAlignment() const override; + bool isYUpInFramebuffer() const override; + bool isYUpInNDC() const override; + bool isClipDepthZeroToOne() const override; + QMatrix4x4 clipSpaceCorrMatrix() const override; + bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const override; + bool isFeatureSupported(QRhi::Feature feature) const override; + int resourceLimit(QRhi::ResourceLimit limit) const override; + const QRhiNativeHandles *nativeHandles() override; + void sendVMemStatsToProfiler() override; + + QRhiNullNativeHandles nativeHandlesStruct; + QRhiSwapChain *currentSwapChain = nullptr; + QNullCommandBuffer offscreenCommandBuffer; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/rhi/qrhiprofiler.cpp b/src/gui/rhi/qrhiprofiler.cpp new file mode 100644 index 0000000000..15e3007d49 --- /dev/null +++ b/src/gui/rhi/qrhiprofiler.cpp @@ -0,0 +1,603 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Gui module +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qrhiprofiler_p_p.h" +#include "qrhi_p_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QRhiProfiler + \inmodule QtRhi + + \brief Collects resource and timing information from an active QRhi. + + A QRhiProfiler is present for each QRhi. Query it via QRhi::profiler(). The + profiler is active only when the QRhi was created with + QRhi::EnableProfiling. No data is collected otherwise. + + \note GPU timings are only available when QRhi indicates that + QRhi::Timestamps is supported. + + Besides collecting data from the QRhi implementations, some additional + values are calculated. For example, for textures and similar resources the + profiler gives an estimate of the complete amount of memory the resource + needs. + + \section2 Output Format + + The output is comma-separated text. Each line has a number of + comma-separated entries and each line ends with a comma. + + For example: + + \badcode + 1,0,140446057946208,Triangle vbuf,type,0,usage,1,logical_size,84,effective_size,84,backing_gpu_buf_count,1,backing_cpu_buf_count,0, + 1,0,140446057947376,Triangle ubuf,type,2,usage,4,logical_size,68,effective_size,256,backing_gpu_buf_count,2,backing_cpu_buf_count,0, + 1,1,140446057950416,,type,0,usage,1,logical_size,112,effective_size,112,backing_gpu_buf_count,1,backing_cpu_buf_count,0, + 1,1,140446057950544,,type,0,usage,2,logical_size,12,effective_size,12,backing_gpu_buf_count,1,backing_cpu_buf_count,0, + 1,1,140446057947440,,type,2,usage,4,logical_size,68,effective_size,256,backing_gpu_buf_count,2,backing_cpu_buf_count,0, + 1,1,140446057984784,Cube vbuf (textured),type,0,usage,1,logical_size,720,effective_size,720,backing_gpu_buf_count,1,backing_cpu_buf_count,0, + 1,1,140446057982528,Cube ubuf (textured),type,2,usage,4,logical_size,68,effective_size,256,backing_gpu_buf_count,2,backing_cpu_buf_count,0, + 7,8,140446058913648,Qt texture,width,256,height,256,format,1,owns_native_resource,1,mip_count,9,layer_count,1,effective_sample_count,1,approx_byte_size,349524, + 1,8,140446058795856,Cube vbuf (textured with offscreen),type,0,usage,1,logical_size,720,effective_size,720,backing_gpu_buf_count,1,backing_cpu_buf_count,0, + 1,8,140446058947920,Cube ubuf (textured with offscreen),type,2,usage,4,logical_size,68,effective_size,256,backing_gpu_buf_count,2,backing_cpu_buf_count,0, + 7,8,140446058794928,Texture for offscreen content,width,512,height,512,format,1,owns_native_resource,1,mip_count,1,layer_count,1,effective_sample_count,1,approx_byte_size,1048576, + 1,8,140446058963904,Triangle vbuf,type,0,usage,1,logical_size,84,effective_size,84,backing_gpu_buf_count,1,backing_cpu_buf_count,0, + 1,8,140446058964560,Triangle ubuf,type,2,usage,4,logical_size,68,effective_size,256,backing_gpu_buf_count,2,backing_cpu_buf_count,0, + 5,9,140446057945392,,type,0,width,1280,height,720,effective_sample_count,1,transient_backing,0,winsys_backing,0,approx_byte_size,3686400, + 11,9,140446057944592,,width,1280,height,720,buffer_count,2,msaa_buffer_count,0,effective_sample_count,1,approx_total_byte_size,7372800, + 9,9,140446058913648,Qt texture,slot,0,size,262144, + 10,9,140446058913648,Qt texture,slot,0, + 17,2019,140446057944592,,frames_since_resize,121,min_ms_frame_delta,9,max_ms_frame_delta,33,Favg_ms_frame_delta,16.1167, + 18,2019,140446057944592,,frames_since_resize,121,min_ms_frame_build,0,max_ms_frame_build,1,Favg_ms_frame_build,0.00833333, + 17,4019,140446057944592,,frames_since_resize,241,min_ms_frame_delta,15,max_ms_frame_delta,17,Favg_ms_frame_delta,16.0583, + 18,4019,140446057944592,,frames_since_resize,241,min_ms_frame_build,0,max_ms_frame_build,0,Favg_ms_frame_build,0, + 12,5070,140446057944592,, + 2,5079,140446057947376,Triangle ubuf, + 2,5079,140446057946208,Triangle vbuf, + 2,5079,140446057947440,, + 2,5079,140446057950544,, + 2,5079,140446057950416,, + 8,5079,140446058913648,Qt texture, + 2,5079,140446057982528,Cube ubuf (textured), + 2,5079,140446057984784,Cube vbuf (textured), + 2,5079,140446058964560,Triangle ubuf, + 2,5079,140446058963904,Triangle vbuf, + 8,5079,140446058794928,Texture for offscreen content, + 2,5079,140446058947920,Cube ubuf (textured with offscreen), + 2,5079,140446058795856,Cube vbuf (textured with offscreen), + 6,5079,140446057945392,, + \endcode + + Each line starts with \c op, \c timestamp, \c res, \c name where op is a + value from StreamOp, timestamp is a recording timestamp in milliseconds + (qint64), res is a number (quint64) referring to the QRhiResource the entry + refers to, or 0 if not applicable. \c name is the value of + QRhiResource::name() and may be empty as well. The \c name will never + contain a comma. + + This is followed by any number of \c{key, value} pairs where \c key is an + unspecified string and \c value is a number. If \c key starts with \c F, it + indicates the value is a float. Otherwise assume that the value is a + qint64. + */ + +/*! + \enum QRhiProfiler::StreamOp + Describes an entry in the profiler's output stream. + + \value NewBuffer A buffer is created + \value ReleaseBuffer A buffer is destroyed + \value NewBufferStagingArea A staging buffer for buffer upload is created + \value ReleaseBufferStagingArea A staging buffer for buffer upload is destroyed + \value NewRenderBuffer A renderbuffer is created + \value ReleaseRenderBuffer A renderbuffer is destroyed + \value NewTexture A texture is created + \value ReleaseTexture A texture is destroyed + \value NewTextureStagingArea A staging buffer for texture upload is created + \value ReleaseTextureStagingArea A staging buffer for texture upload is destroyed + \value ResizeSwapChain A swapchain is created or resized + \value ReleaseSwapChain A swapchain is destroyed + \value NewReadbackBuffer A staging buffer for readback is created + \value ReleaseReadbackBuffer A staging buffer for readback is destroyed + \value GpuMemAllocStats GPU memory allocator statistics + \value GpuFrameTime GPU frame times + \value FrameToFrameTime CPU frame-to-frame times + \value FrameBuildTime CPU beginFrame-endFrame times + */ + +/*! + \class QRhiProfiler::CpuTime + \inmodule QtRhi + \brief Contains CPU-side frame timings. + + Once sufficient number of frames have been rendered, the minimum, maximum, + and average values (in milliseconds) from various measurements are made + available in this struct queriable from QRhiProfiler::frameToFrameTimes() + and QRhiProfiler::frameBuildTimes(). + + \sa QRhiProfiler::setFrameTimingWriteInterval() + */ + +/*! + \class QRhiProfiler::GpuTime + \inmodule QtRhi + \brief Contains GPU-side frame timings. + + Once sufficient number of frames have been rendered, the minimum, maximum, + and average values (in milliseconds) calculated from GPU command buffer + timestamps are made available in this struct queriable from + QRhiProfiler::gpuFrameTimes(). + + \sa QRhiProfiler::setFrameTimingWriteInterval() + */ + +/*! + \internal + */ +QRhiProfiler::QRhiProfiler() + : d(new QRhiProfilerPrivate) +{ + d->ts.start(); +} + +/*! + Destructor. + */ +QRhiProfiler::~QRhiProfiler() +{ + // Flush because there is a high chance we have writes that were made since + // the event loop last ran. (esp. relevant for network devices like QTcpSocket) + if (d->outputDevice) + d->outputDevice->waitForBytesWritten(1000); + + delete d; +} + +/*! + Sets the output \a device. + + \note No output will be generated when QRhi::EnableProfiling was not set. + */ +void QRhiProfiler::setDevice(QIODevice *device) +{ + d->outputDevice = device; +} + +/*! + Requests writing a GpuMemAllocStats entry into the output, when applicable. + Backends that do not support this will ignore the request. This is an + explicit request since getting the allocator status and statistics may be + an expensive operation. + */ +void QRhiProfiler::addVMemAllocatorStats() +{ + if (d->rhiDWhenEnabled) + d->rhiDWhenEnabled->sendVMemStatsToProfiler(); +} + +/*! + \return the currently set frame timing writeout interval. + */ +int QRhiProfiler::frameTimingWriteInterval() const +{ + return d->frameTimingWriteInterval; +} + +/*! + Sets the number of frames that need to be rendered before the collected CPU + and GPU timings are processed (min, max, average are calculated) to \a + frameCount. + + The default value is 120. + */ +void QRhiProfiler::setFrameTimingWriteInterval(int frameCount) +{ + if (frameCount > 0) + d->frameTimingWriteInterval = frameCount; +} + +/*! + \return min, max, and avg in milliseconds for the time that elapsed between two + QRhi::endFrame() calls. + + \note The values are all 0 until at least frameTimingWriteInterval() frames + have been rendered. + */ +QRhiProfiler::CpuTime QRhiProfiler::frameToFrameTimes(QRhiSwapChain *sc) const +{ + auto it = d->swapchains.constFind(sc); + if (it != d->swapchains.constEnd()) + return it->frameToFrameTime; + + return QRhiProfiler::CpuTime(); +} + +/*! + \return min, max, and avg in milliseconds for the time that elapsed between + a QRhi::beginFrame() and QRhi::endFrame(). + + \note The values are all 0 until at least frameTimingWriteInterval() frames + have been rendered. + */ +QRhiProfiler::CpuTime QRhiProfiler::frameBuildTimes(QRhiSwapChain *sc) const +{ + auto it = d->swapchains.constFind(sc); + if (it != d->swapchains.constEnd()) + return it->beginToEndFrameTime; + + return QRhiProfiler::CpuTime(); +} + +/*! + \return min, max, and avg in milliseconds for the GPU time that is spent on + one frame. + + \note The values are all 0 until at least frameTimingWriteInterval() frames + have been rendered. + + The GPU times should only be compared between runs on the same GPU of the + same system with the same backend. Comparing times for different graphics + cards or for different backends can give misleading results. The numbers are + not meant to be comparable that way. + + \note Some backends have no support for this, and even for those that have, + it is not guaranteed that the driver will support it at run time. Support + can be checked via QRhi::Timestamps. + */ +QRhiProfiler::GpuTime QRhiProfiler::gpuFrameTimes(QRhiSwapChain *sc) const +{ + auto it = d->swapchains.constFind(sc); + if (it != d->swapchains.constEnd()) + return it->gpuFrameTime; + + return QRhiProfiler::GpuTime(); +} + +void QRhiProfilerPrivate::startEntry(QRhiProfiler::StreamOp op, qint64 timestamp, QRhiResource *res) +{ + buf.clear(); + buf.append(QByteArray::number(op)); + buf.append(','); + buf.append(QByteArray::number(timestamp)); + buf.append(','); + buf.append(QByteArray::number(quint64(quintptr(res)))); + buf.append(','); + if (res) + buf.append(res->name()); + buf.append(','); +} + +void QRhiProfilerPrivate::writeInt(const char *key, qint64 v) +{ + Q_ASSERT(key[0] != 'F'); + buf.append(key); + buf.append(','); + buf.append(QByteArray::number(v)); + buf.append(','); +} + +void QRhiProfilerPrivate::writeFloat(const char *key, float f) +{ + Q_ASSERT(key[0] == 'F'); + buf.append(key); + buf.append(','); + buf.append(QByteArray::number(f)); + buf.append(','); +} + +void QRhiProfilerPrivate::endEntry() +{ + buf.append('\n'); + outputDevice->write(buf); +} + +void QRhiProfilerPrivate::newBuffer(QRhiBuffer *buf, quint32 realSize, int backingGpuBufCount, int backingCpuBufCount) +{ + if (!outputDevice) + return; + + startEntry(QRhiProfiler::NewBuffer, ts.elapsed(), buf); + writeInt("type", buf->type()); + writeInt("usage", buf->usage()); + writeInt("logical_size", buf->size()); + writeInt("effective_size", realSize); + writeInt("backing_gpu_buf_count", backingGpuBufCount); + writeInt("backing_cpu_buf_count", backingCpuBufCount); + endEntry(); +} + +void QRhiProfilerPrivate::releaseBuffer(QRhiBuffer *buf) +{ + if (!outputDevice) + return; + + startEntry(QRhiProfiler::ReleaseBuffer, ts.elapsed(), buf); + endEntry(); +} + +void QRhiProfilerPrivate::newBufferStagingArea(QRhiBuffer *buf, int slot, quint32 size) +{ + if (!outputDevice) + return; + + startEntry(QRhiProfiler::NewBufferStagingArea, ts.elapsed(), buf); + writeInt("slot", slot); + writeInt("size", size); + endEntry(); +} + +void QRhiProfilerPrivate::releaseBufferStagingArea(QRhiBuffer *buf, int slot) +{ + if (!outputDevice) + return; + + startEntry(QRhiProfiler::ReleaseBufferStagingArea, ts.elapsed(), buf); + writeInt("slot", slot); + endEntry(); +} + +void QRhiProfilerPrivate::newRenderBuffer(QRhiRenderBuffer *rb, bool transientBacking, bool winSysBacking, int sampleCount) +{ + if (!outputDevice) + return; + + const QRhiRenderBuffer::Type type = rb->type(); + const QSize sz = rb->pixelSize(); + // just make up something, ds is likely D24S8 while color is RGBA8 or similar + const QRhiTexture::Format assumedFormat = type == QRhiRenderBuffer::DepthStencil ? QRhiTexture::D32F : QRhiTexture::RGBA8; + quint32 byteSize = rhiDWhenEnabled->approxByteSizeForTexture(assumedFormat, sz, 1, 1); + if (sampleCount > 1) + byteSize *= sampleCount; + + startEntry(QRhiProfiler::NewRenderBuffer, ts.elapsed(), rb); + writeInt("type", type); + writeInt("width", sz.width()); + writeInt("height", sz.height()); + writeInt("effective_sample_count", sampleCount); + writeInt("transient_backing", transientBacking); + writeInt("winsys_backing", winSysBacking); + writeInt("approx_byte_size", byteSize); + endEntry(); +} + +void QRhiProfilerPrivate::releaseRenderBuffer(QRhiRenderBuffer *rb) +{ + if (!outputDevice) + return; + + startEntry(QRhiProfiler::ReleaseRenderBuffer, ts.elapsed(), rb); + endEntry(); +} + +void QRhiProfilerPrivate::newTexture(QRhiTexture *tex, bool owns, int mipCount, int layerCount, int sampleCount) +{ + if (!outputDevice) + return; + + const QRhiTexture::Format format = tex->format(); + const QSize sz = tex->pixelSize(); + quint32 byteSize = rhiDWhenEnabled->approxByteSizeForTexture(format, sz, mipCount, layerCount); + if (sampleCount > 1) + byteSize *= sampleCount; + + startEntry(QRhiProfiler::NewTexture, ts.elapsed(), tex); + writeInt("width", sz.width()); + writeInt("height", sz.height()); + writeInt("format", format); + writeInt("owns_native_resource", owns); + writeInt("mip_count", mipCount); + writeInt("layer_count", layerCount); + writeInt("effective_sample_count", sampleCount); + writeInt("approx_byte_size", byteSize); + endEntry(); +} + +void QRhiProfilerPrivate::releaseTexture(QRhiTexture *tex) +{ + if (!outputDevice) + return; + + startEntry(QRhiProfiler::ReleaseTexture, ts.elapsed(), tex); + endEntry(); +} + +void QRhiProfilerPrivate::newTextureStagingArea(QRhiTexture *tex, int slot, quint32 size) +{ + if (!outputDevice) + return; + + startEntry(QRhiProfiler::NewTextureStagingArea, ts.elapsed(), tex); + writeInt("slot", slot); + writeInt("size", size); + endEntry(); +} + +void QRhiProfilerPrivate::releaseTextureStagingArea(QRhiTexture *tex, int slot) +{ + if (!outputDevice) + return; + + startEntry(QRhiProfiler::ReleaseTextureStagingArea, ts.elapsed(), tex); + writeInt("slot", slot); + endEntry(); +} + +void QRhiProfilerPrivate::resizeSwapChain(QRhiSwapChain *sc, int bufferCount, int msaaBufferCount, int sampleCount) +{ + if (!outputDevice) + return; + + const QSize sz = sc->currentPixelSize(); + quint32 byteSize = rhiDWhenEnabled->approxByteSizeForTexture(QRhiTexture::BGRA8, sz, 1, 1); + byteSize = byteSize * bufferCount + byteSize * msaaBufferCount * sampleCount; + + startEntry(QRhiProfiler::ResizeSwapChain, ts.elapsed(), sc); + writeInt("width", sz.width()); + writeInt("height", sz.height()); + writeInt("buffer_count", bufferCount); + writeInt("msaa_buffer_count", msaaBufferCount); + writeInt("effective_sample_count", sampleCount); + writeInt("approx_total_byte_size", byteSize); + endEntry(); +} + +void QRhiProfilerPrivate::releaseSwapChain(QRhiSwapChain *sc) +{ + if (!outputDevice) + return; + + startEntry(QRhiProfiler::ReleaseSwapChain, ts.elapsed(), sc); + endEntry(); +} + +template +void calcTiming(QVector *vec, T *minDelta, T *maxDelta, float *avgDelta) +{ + if (vec->isEmpty()) + return; + + *minDelta = *maxDelta = 0; + float totalDelta = 0; + for (T delta : qAsConst(*vec)) { + totalDelta += float(delta); + if (*minDelta == 0 || delta < *minDelta) + *minDelta = delta; + if (*maxDelta == 0 || delta > *maxDelta) + *maxDelta = delta; + } + *avgDelta = totalDelta / vec->count(); + + vec->clear(); +} + +void QRhiProfilerPrivate::beginSwapChainFrame(QRhiSwapChain *sc) +{ + Sc &scd(swapchains[sc]); + scd.beginToEndTimer.start(); +} + +void QRhiProfilerPrivate::endSwapChainFrame(QRhiSwapChain *sc, int frameCount) +{ + Sc &scd(swapchains[sc]); + if (!scd.frameToFrameRunning) { + scd.frameToFrameTimer.start(); + scd.frameToFrameRunning = true; + return; + } + + scd.frameToFrameSamples.append(scd.frameToFrameTimer.restart()); + if (scd.frameToFrameSamples.count() >= frameTimingWriteInterval) { + calcTiming(&scd.frameToFrameSamples, + &scd.frameToFrameTime.minTime, &scd.frameToFrameTime.maxTime, &scd.frameToFrameTime.avgTime); + if (outputDevice) { + startEntry(QRhiProfiler::FrameToFrameTime, ts.elapsed(), sc); + writeInt("frames_since_resize", frameCount); + writeInt("min_ms_frame_delta", scd.frameToFrameTime.minTime); + writeInt("max_ms_frame_delta", scd.frameToFrameTime.maxTime); + writeFloat("Favg_ms_frame_delta", scd.frameToFrameTime.avgTime); + endEntry(); + } + } + + scd.beginToEndSamples.append(scd.beginToEndTimer.elapsed()); + if (scd.beginToEndSamples.count() >= frameTimingWriteInterval) { + calcTiming(&scd.beginToEndSamples, + &scd.beginToEndFrameTime.minTime, &scd.beginToEndFrameTime.maxTime, &scd.beginToEndFrameTime.avgTime); + if (outputDevice) { + startEntry(QRhiProfiler::FrameBuildTime, ts.elapsed(), sc); + writeInt("frames_since_resize", frameCount); + writeInt("min_ms_frame_build", scd.beginToEndFrameTime.minTime); + writeInt("max_ms_frame_build", scd.beginToEndFrameTime.maxTime); + writeFloat("Favg_ms_frame_build", scd.beginToEndFrameTime.avgTime); + endEntry(); + } + } +} + +void QRhiProfilerPrivate::swapChainFrameGpuTime(QRhiSwapChain *sc, float gpuTime) +{ + Sc &scd(swapchains[sc]); + scd.gpuFrameSamples.append(gpuTime); + if (scd.gpuFrameSamples.count() >= frameTimingWriteInterval) { + calcTiming(&scd.gpuFrameSamples, + &scd.gpuFrameTime.minTime, &scd.gpuFrameTime.maxTime, &scd.gpuFrameTime.avgTime); + if (outputDevice) { + startEntry(QRhiProfiler::GpuFrameTime, ts.elapsed(), sc); + writeFloat("Fmin_ms_gpu_frame_time", scd.gpuFrameTime.minTime); + writeFloat("Fmax_ms_gpu_frame_time", scd.gpuFrameTime.maxTime); + writeFloat("Favg_ms_gpu_frame_time", scd.gpuFrameTime.avgTime); + endEntry(); + } + } +} + +void QRhiProfilerPrivate::newReadbackBuffer(quint64 id, QRhiResource *src, quint32 size) +{ + if (!outputDevice) + return; + + startEntry(QRhiProfiler::NewReadbackBuffer, ts.elapsed(), src); + writeInt("id", id); + writeInt("size", size); + endEntry(); +} + +void QRhiProfilerPrivate::releaseReadbackBuffer(quint64 id) +{ + if (!outputDevice) + return; + + startEntry(QRhiProfiler::ReleaseReadbackBuffer, ts.elapsed(), nullptr); + writeInt("id", id); + endEntry(); +} + +void QRhiProfilerPrivate::vmemStat(int realAllocCount, int subAllocCount, quint32 totalSize, quint32 unusedSize) +{ + if (!outputDevice) + return; + + startEntry(QRhiProfiler::GpuMemAllocStats, ts.elapsed(), nullptr); + writeInt("real_alloc_count", realAllocCount); + writeInt("sub_alloc_count", subAllocCount); + writeInt("total_size", totalSize); + writeInt("unused_size", unusedSize); + endEntry(); +} + +QT_END_NAMESPACE diff --git a/src/gui/rhi/qrhiprofiler_p.h b/src/gui/rhi/qrhiprofiler_p.h new file mode 100644 index 0000000000..89fd0a8798 --- /dev/null +++ b/src/gui/rhi/qrhiprofiler_p.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Gui module +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QRHIPROFILER_H +#define QRHIPROFILER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +QT_BEGIN_NAMESPACE + +class QRhiProfilerPrivate; +class QIODevice; + +class Q_GUI_EXPORT QRhiProfiler +{ +public: + enum StreamOp { + NewBuffer = 1, + ReleaseBuffer, + NewBufferStagingArea, + ReleaseBufferStagingArea, + NewRenderBuffer, + ReleaseRenderBuffer, + NewTexture, + ReleaseTexture, + NewTextureStagingArea, + ReleaseTextureStagingArea, + ResizeSwapChain, + ReleaseSwapChain, + NewReadbackBuffer, + ReleaseReadbackBuffer, + GpuMemAllocStats, + GpuFrameTime, + FrameToFrameTime, + FrameBuildTime + }; + + ~QRhiProfiler(); + + void setDevice(QIODevice *device); + + void addVMemAllocatorStats(); + + int frameTimingWriteInterval() const; + void setFrameTimingWriteInterval(int frameCount); + + struct CpuTime { + qint64 minTime = 0; + qint64 maxTime = 0; + float avgTime = 0; + }; + + struct GpuTime { + float minTime = 0; + float maxTime = 0; + float avgTime = 0; + }; + + CpuTime frameToFrameTimes(QRhiSwapChain *sc) const; + CpuTime frameBuildTimes(QRhiSwapChain *sc) const; // beginFrame - endFrame + GpuTime gpuFrameTimes(QRhiSwapChain *sc) const; + +private: + Q_DISABLE_COPY(QRhiProfiler) + QRhiProfiler(); + QRhiProfilerPrivate *d; + friend class QRhiImplementation; + friend class QRhiProfilerPrivate; +}; + +Q_DECLARE_TYPEINFO(QRhiProfiler::CpuTime, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(QRhiProfiler::GpuTime, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/rhi/qrhiprofiler_p_p.h b/src/gui/rhi/qrhiprofiler_p_p.h new file mode 100644 index 0000000000..49c6bd78ed --- /dev/null +++ b/src/gui/rhi/qrhiprofiler_p_p.h @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Gui module +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QRHIPROFILER_P_H +#define QRHIPROFILER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qrhiprofiler_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + +class QRhiProfilerPrivate +{ +public: + static QRhiProfilerPrivate *get(QRhiProfiler *p) { return p->d; } + + void newBuffer(QRhiBuffer *buf, quint32 realSize, int backingGpuBufCount, int backingCpuBufCount); + void releaseBuffer(QRhiBuffer *buf); + void newBufferStagingArea(QRhiBuffer *buf, int slot, quint32 size); + void releaseBufferStagingArea(QRhiBuffer *buf, int slot); + + void newRenderBuffer(QRhiRenderBuffer *rb, bool transientBacking, bool winSysBacking, int sampleCount); + void releaseRenderBuffer(QRhiRenderBuffer *rb); + + void newTexture(QRhiTexture *tex, bool owns, int mipCount, int layerCount, int sampleCount); + void releaseTexture(QRhiTexture *tex); + void newTextureStagingArea(QRhiTexture *tex, int slot, quint32 size); + void releaseTextureStagingArea(QRhiTexture *tex, int slot); + + void resizeSwapChain(QRhiSwapChain *sc, int bufferCount, int msaaBufferCount, int sampleCount); + void releaseSwapChain(QRhiSwapChain *sc); + + void beginSwapChainFrame(QRhiSwapChain *sc); + void endSwapChainFrame(QRhiSwapChain *sc, int frameCount); + void swapChainFrameGpuTime(QRhiSwapChain *sc, float gpuTimeMs); + + void newReadbackBuffer(quint64 id, QRhiResource *src, quint32 size); + void releaseReadbackBuffer(quint64 id); + + void vmemStat(int realAllocCount, int subAllocCount, quint32 totalSize, quint32 unusedSize); + + void startEntry(QRhiProfiler::StreamOp op, qint64 timestamp, QRhiResource *res); + void writeInt(const char *key, qint64 v); + void writeFloat(const char *key, float f); + void endEntry(); + + QRhiImplementation *rhiDWhenEnabled = nullptr; + QIODevice *outputDevice = nullptr; + QElapsedTimer ts; + QByteArray buf; + static const int DEFAULT_FRAME_TIMING_WRITE_INTERVAL = 120; // frames + int frameTimingWriteInterval = DEFAULT_FRAME_TIMING_WRITE_INTERVAL; + struct Sc { + Sc() { + frameToFrameSamples.reserve(DEFAULT_FRAME_TIMING_WRITE_INTERVAL); + beginToEndSamples.reserve(DEFAULT_FRAME_TIMING_WRITE_INTERVAL); + gpuFrameSamples.reserve(DEFAULT_FRAME_TIMING_WRITE_INTERVAL); + } + QElapsedTimer frameToFrameTimer; + bool frameToFrameRunning = false; + QElapsedTimer beginToEndTimer; + QVector frameToFrameSamples; + QVector beginToEndSamples; + QVector gpuFrameSamples; + QRhiProfiler::CpuTime frameToFrameTime; + QRhiProfiler::CpuTime beginToEndFrameTime; + QRhiProfiler::GpuTime gpuFrameTime; + }; + QHash swapchains; +}; + +Q_DECLARE_TYPEINFO(QRhiProfilerPrivate::Sc, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp new file mode 100644 index 0000000000..1f80df6d0d --- /dev/null +++ b/src/gui/rhi/qrhivulkan.cpp @@ -0,0 +1,5681 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Gui module +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qrhivulkan_p_p.h" +#include "qrhivulkanext_p.h" + +#define VMA_IMPLEMENTATION +#define VMA_STATIC_VULKAN_FUNCTIONS 0 +#define VMA_RECORDING_ENABLED 0 +#define VMA_DEDICATED_ALLOCATION 0 +#ifdef QT_DEBUG +#define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1 +#endif +#include "vk_mem_alloc.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/* + Vulkan 1.0 backend. Provides a double-buffered swapchain that throttles the + rendering thread to vsync. Textures and "static" buffers are device local, + and a separate, host visible staging buffer is used to upload data to them. + "Dynamic" buffers are in host visible memory and are duplicated (since there + can be 2 frames in flight). This is handled transparently to the application. +*/ + +/*! + \class QRhiVulkanInitParams + \inmodule QtRhi + \brief Vulkan specific initialization parameters. + + A Vulkan-based QRhi needs at minimum a valid QVulkanInstance. It is up to + the user to ensure this is available and initialized. This is typically + done in main() similarly to the following: + + \badcode + int main(int argc, char **argv) + { + ... + + QVulkanInstance inst; + #ifndef Q_OS_ANDROID + inst.setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation"); + #else + inst.setLayers(QByteArrayList() + << "VK_LAYER_GOOGLE_threading" + << "VK_LAYER_LUNARG_parameter_validation" + << "VK_LAYER_LUNARG_object_tracker" + << "VK_LAYER_LUNARG_core_validation" + << "VK_LAYER_LUNARG_image" + << "VK_LAYER_LUNARG_swapchain" + << "VK_LAYER_GOOGLE_unique_objects"); + #endif + inst.setExtensions(QByteArrayList() + << "VK_KHR_get_physical_device_properties2"); + if (!inst.create()) + qFatal("Vulkan not available"); + + ... + } + \endcode + + The example here has two optional aspects: it enables the + \l{https://github.com/KhronosGroup/Vulkan-ValidationLayers}{Vulkan + validation layers}, when they are available, and also enables the + VK_KHR_get_physical_device_properties2 extension (part of Vulkan 1.1), when + available. The former is useful during the development phase (remember that + QVulkanInstance conveniently redirects messages and warnings to qDebug). + Avoid enabling it in production builds, however. The latter is important in + order to make QRhi::CustomInstanceStepRate available with Vulkan since + VK_EXT_vertex_attribute_divisor (part of Vulkan 1.1) depends on it. It can + be omitted when instanced drawing with a non-one step rate is not used. + + Once this is done, a Vulkan-based QRhi can be created by passing the + instance and a QWindow with its surface type set to + QSurface::VulkanSurface: + + \badcode + QRhiVulkanInitParams params; + params.inst = vulkanInstance; + params.window = window; + rhi = QRhi::create(QRhi::Vulkan, ¶ms); + \endcode + + The window is optional and can be omitted. This is not recommended however + because there is then no way to ensure presenting is supported while + choosing a graphics queue. + + \note Even when a window is specified, QRhiSwapChain objects can be created + for other windows as well, as long as they all have their + QWindow::surfaceType() set to QSurface::VulkanSurface. + + \section2 Working with existing Vulkan devices + + When interoperating with another graphics engine, it may be necessary to + get a QRhi instance that uses the same Vulkan device. This can be achieved + by passing a pointer to a QRhiVulkanNativeHandles to QRhi::create(). + + The physical device and device object must then be set to a non-null value. + In addition, either the graphics queue family index or the graphics queue + object itself is required. Prefer the former, whenever possible since + deducing the index is not possible afterwards. Optionally, an existing + command pool object can be specified as well, and, also optionally, + vmemAllocator can be used to share the same + \l{https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator}{Vulkan + memory allocator} between two QRhi instances. + + The QRhi does not take ownership of any of the external objects. + */ + +/*! + \class QRhiVulkanNativeHandles + \inmodule QtRhi + \brief Collects device, queue, and other Vulkan objects that are used by the QRhi. + + \note Ownership of the Vulkan objects is never transferred. + */ + +/*! + \class QRhiVulkanTextureNativeHandles + \inmodule QtRhi + \brief Holds the Vulkan image object that is backing a QRhiTexture. + + Importing and exporting Vulkan image objects that back a QRhiTexture when + running with the Vulkan backend is supported via this class. Ownership of + the Vulkan object is never transferred. + + \note Memory allocation details are not exposed. This is intentional since + memory is typically suballocated from a bigger chunk of VkDeviceMemory, and + exposing the allocator details is not desirable for now. + */ + +/*! + \class QRhiVulkanCommandBufferNativeHandles + \inmodule QtRhi + \brief Holds the Vulkan command buffer object that is backing a QRhiCommandBuffer. + + \note The Vulkan command buffer object is only guaranteed to be valid, and + in recording state, while recording a frame. That is, between a + \l{QRhi::beginFrame()}{beginFrame()} - \l{QRhi::endFrame()}{endFrame()} or + \l{QRhi::beginOffscreenFrame()}{beginOffscreenFrame()} - + \l{QRhi::endOffsrceenFrame()}{endOffscreenFrame()} pair. + */ + +static inline VkDeviceSize aligned(VkDeviceSize v, VkDeviceSize byteAlign) +{ + return (v + byteAlign - 1) & ~(byteAlign - 1); +} + +static QVulkanInstance *globalVulkanInstance; + +static void VKAPI_PTR wrap_vkGetPhysicalDeviceProperties(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties* pProperties) +{ + globalVulkanInstance->functions()->vkGetPhysicalDeviceProperties(physicalDevice, pProperties); +} + +static void VKAPI_PTR wrap_vkGetPhysicalDeviceMemoryProperties(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties* pMemoryProperties) +{ + globalVulkanInstance->functions()->vkGetPhysicalDeviceMemoryProperties(physicalDevice, pMemoryProperties); +} + +static VkResult VKAPI_PTR wrap_vkAllocateMemory(VkDevice device, const VkMemoryAllocateInfo* pAllocateInfo, const VkAllocationCallbacks* pAllocator, VkDeviceMemory* pMemory) +{ + return globalVulkanInstance->deviceFunctions(device)->vkAllocateMemory(device, pAllocateInfo, pAllocator, pMemory); +} + +void VKAPI_PTR wrap_vkFreeMemory(VkDevice device, VkDeviceMemory memory, const VkAllocationCallbacks* pAllocator) +{ + globalVulkanInstance->deviceFunctions(device)->vkFreeMemory(device, memory, pAllocator); +} + +VkResult VKAPI_PTR wrap_vkMapMemory(VkDevice device, VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void** ppData) +{ + return globalVulkanInstance->deviceFunctions(device)->vkMapMemory(device, memory, offset, size, flags, ppData); +} + +void VKAPI_PTR wrap_vkUnmapMemory(VkDevice device, VkDeviceMemory memory) +{ + globalVulkanInstance->deviceFunctions(device)->vkUnmapMemory(device, memory); +} + +VkResult VKAPI_PTR wrap_vkFlushMappedMemoryRanges(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges) +{ + return globalVulkanInstance->deviceFunctions(device)->vkFlushMappedMemoryRanges(device, memoryRangeCount, pMemoryRanges); +} + +VkResult VKAPI_PTR wrap_vkInvalidateMappedMemoryRanges(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges) +{ + return globalVulkanInstance->deviceFunctions(device)->vkInvalidateMappedMemoryRanges(device, memoryRangeCount, pMemoryRanges); +} + +VkResult VKAPI_PTR wrap_vkBindBufferMemory(VkDevice device, VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize memoryOffset) +{ + return globalVulkanInstance->deviceFunctions(device)->vkBindBufferMemory(device, buffer, memory, memoryOffset); +} + +VkResult VKAPI_PTR wrap_vkBindImageMemory(VkDevice device, VkImage image, VkDeviceMemory memory, VkDeviceSize memoryOffset) +{ + return globalVulkanInstance->deviceFunctions(device)->vkBindImageMemory(device, image, memory, memoryOffset); +} + +void VKAPI_PTR wrap_vkGetBufferMemoryRequirements(VkDevice device, VkBuffer buffer, VkMemoryRequirements* pMemoryRequirements) +{ + globalVulkanInstance->deviceFunctions(device)->vkGetBufferMemoryRequirements(device, buffer, pMemoryRequirements); +} + +void VKAPI_PTR wrap_vkGetImageMemoryRequirements(VkDevice device, VkImage image, VkMemoryRequirements* pMemoryRequirements) +{ + globalVulkanInstance->deviceFunctions(device)->vkGetImageMemoryRequirements(device, image, pMemoryRequirements); +} + +VkResult VKAPI_PTR wrap_vkCreateBuffer(VkDevice device, const VkBufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer) +{ + return globalVulkanInstance->deviceFunctions(device)->vkCreateBuffer(device, pCreateInfo, pAllocator, pBuffer); +} + +void VKAPI_PTR wrap_vkDestroyBuffer(VkDevice device, VkBuffer buffer, const VkAllocationCallbacks* pAllocator) +{ + globalVulkanInstance->deviceFunctions(device)->vkDestroyBuffer(device, buffer, pAllocator); +} + +VkResult VKAPI_PTR wrap_vkCreateImage(VkDevice device, const VkImageCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkImage* pImage) +{ + return globalVulkanInstance->deviceFunctions(device)->vkCreateImage(device, pCreateInfo, pAllocator, pImage); +} + +void VKAPI_PTR wrap_vkDestroyImage(VkDevice device, VkImage image, const VkAllocationCallbacks* pAllocator) +{ + globalVulkanInstance->deviceFunctions(device)->vkDestroyImage(device, image, pAllocator); +} + +static inline VmaAllocation toVmaAllocation(QVkAlloc a) +{ + return reinterpret_cast(a); +} + +static inline VmaAllocator toVmaAllocator(QVkAllocator a) +{ + return reinterpret_cast(a); +} + +QRhiVulkan::QRhiVulkan(QRhiVulkanInitParams *params, QRhiVulkanNativeHandles *importDevice) + : ofr(this) +{ + inst = params->inst; + maybeWindow = params->window; // may be null + + importedDevice = importDevice != nullptr; + if (importedDevice) { + physDev = importDevice->physDev; + dev = importDevice->dev; + if (physDev && dev) { + gfxQueueFamilyIdx = importDevice->gfxQueueFamilyIdx; + gfxQueue = importDevice->gfxQueue; + if (importDevice->cmdPool) { + importedCmdPool = true; + cmdPool = importDevice->cmdPool; + } + if (importDevice->vmemAllocator) { + importedAllocator = true; + allocator = importDevice->vmemAllocator; + } + } else { + qWarning("No (physical) Vulkan device is given, cannot import"); + importedDevice = false; + } + } +} + +static bool qvk_debug_filter(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, + size_t location, int32_t messageCode, const char *pLayerPrefix, const char *pMessage) +{ + Q_UNUSED(flags); + Q_UNUSED(objectType); + Q_UNUSED(object); + Q_UNUSED(location); + Q_UNUSED(messageCode); + Q_UNUSED(pLayerPrefix); + + // Filter out certain misleading validation layer messages, as per + // VulkanMemoryAllocator documentation. + if (strstr(pMessage, "Mapping an image with layout") + && strstr(pMessage, "can result in undefined behavior if this memory is used by the device")) + { + return true; + } + + return false; +} + +bool QRhiVulkan::create(QRhi::Flags flags) +{ + Q_UNUSED(flags); + Q_ASSERT(inst); + + globalVulkanInstance = inst; // assume this will not change during the lifetime of the entire application + + f = inst->functions(); + + QVector queueFamilyProps; + auto queryQueueFamilyProps = [this, &queueFamilyProps] { + uint32_t queueCount = 0; + f->vkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, nullptr); + queueFamilyProps.resize(queueCount); + f->vkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, queueFamilyProps.data()); + }; + + if (!importedDevice) { + uint32_t physDevCount = 0; + f->vkEnumeratePhysicalDevices(inst->vkInstance(), &physDevCount, nullptr); + if (!physDevCount) { + qWarning("No physical devices"); + return false; + } + QVarLengthArray physDevs(physDevCount); + VkResult err = f->vkEnumeratePhysicalDevices(inst->vkInstance(), &physDevCount, physDevs.data()); + if (err != VK_SUCCESS || !physDevCount) { + qWarning("Failed to enumerate physical devices: %d", err); + return false; + } + int physDevIndex = -1; + int requestedPhysDevIndex = -1; + if (qEnvironmentVariableIsSet("QT_VK_PHYSICAL_DEVICE_INDEX")) + 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)); + if (physDevIndex < 0 && (requestedPhysDevIndex < 0 || requestedPhysDevIndex == int(i))) { + physDevIndex = i; + qDebug(" using this physical device"); + } + } + if (physDevIndex < 0) { + qWarning("No matching physical device"); + return false; + } + physDev = physDevs[physDevIndex]; + + queryQueueFamilyProps(); + + gfxQueue = VK_NULL_HANDLE; + gfxQueueFamilyIdx = -1; + int presQueueFamilyIdx = -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); + if (gfxQueueFamilyIdx == -1 + && (queueFamilyProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) + && (!maybeWindow || inst->supportsPresent(physDev, i, maybeWindow))) + { + gfxQueueFamilyIdx = i; + } + } + if (gfxQueueFamilyIdx != -1) { + presQueueFamilyIdx = gfxQueueFamilyIdx; + } else { + // ### + qWarning("No graphics queue that can present. This is not supported atm."); + } + if (gfxQueueFamilyIdx == -1) { + qWarning("No graphics queue family found"); + return false; + } + if (presQueueFamilyIdx == -1) { + qWarning("No present queue family found"); + return false; + } + + VkDeviceQueueCreateInfo queueInfo[2]; + const float prio[] = { 0 }; + memset(queueInfo, 0, sizeof(queueInfo)); + queueInfo[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueInfo[0].queueFamilyIndex = gfxQueueFamilyIdx; + queueInfo[0].queueCount = 1; + queueInfo[0].pQueuePriorities = prio; + if (gfxQueueFamilyIdx != presQueueFamilyIdx) { + queueInfo[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueInfo[1].queueFamilyIndex = presQueueFamilyIdx; + queueInfo[1].queueCount = 1; + queueInfo[1].pQueuePriorities = prio; + } + + QVector devLayers; + if (inst->layers().contains("VK_LAYER_LUNARG_standard_validation")) + devLayers.append("VK_LAYER_LUNARG_standard_validation"); + + uint32_t devExtCount = 0; + f->vkEnumerateDeviceExtensionProperties(physDev, nullptr, &devExtCount, nullptr); + QVector devExts(devExtCount); + f->vkEnumerateDeviceExtensionProperties(physDev, nullptr, &devExtCount, devExts.data()); + qDebug("%d device extensions available", devExts.count()); + + QVector requestedDevExts; + requestedDevExts.append("VK_KHR_swapchain"); + + debugMarkersAvailable = false; + vertexAttribDivisorAvailable = false; + for (const VkExtensionProperties &ext : devExts) { + if (!strcmp(ext.extensionName, VK_EXT_DEBUG_MARKER_EXTENSION_NAME)) { + requestedDevExts.append(VK_EXT_DEBUG_MARKER_EXTENSION_NAME); + debugMarkersAvailable = true; + } else if (!strcmp(ext.extensionName, VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME)) { + if (inst->extensions().contains(QByteArrayLiteral("VK_KHR_get_physical_device_properties2"))) { + requestedDevExts.append(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME); + vertexAttribDivisorAvailable = true; + } + } + } + + VkDeviceCreateInfo devInfo; + memset(&devInfo, 0, sizeof(devInfo)); + devInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + devInfo.queueCreateInfoCount = gfxQueueFamilyIdx == presQueueFamilyIdx ? 1 : 2; + devInfo.pQueueCreateInfos = queueInfo; + devInfo.enabledLayerCount = devLayers.count(); + devInfo.ppEnabledLayerNames = devLayers.constData(); + devInfo.enabledExtensionCount = requestedDevExts.count(); + devInfo.ppEnabledExtensionNames = requestedDevExts.constData(); + + err = f->vkCreateDevice(physDev, &devInfo, nullptr, &dev); + if (err != VK_SUCCESS) { + qWarning("Failed to create device: %d", err); + return false; + } + } + + df = inst->deviceFunctions(dev); + + if (!importedCmdPool) { + VkCommandPoolCreateInfo poolInfo; + memset(&poolInfo, 0, sizeof(poolInfo)); + poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + poolInfo.queueFamilyIndex = gfxQueueFamilyIdx; + VkResult err = df->vkCreateCommandPool(dev, &poolInfo, nullptr, &cmdPool); + if (err != VK_SUCCESS) { + qWarning("Failed to create command pool: %d", err); + return false; + } + } + + if (gfxQueueFamilyIdx != -1) { + // Will use one queue always, including when multiple QRhis use the + // same device. This has significant consequences, and cannot easily be + // changed (e.g. think pipeline barriers which create a dependency + // between commands submitted to a queue - with multiple queues + // additional synchronization would be needed) + + if (!gfxQueue) + df->vkGetDeviceQueue(dev, gfxQueueFamilyIdx, 0, &gfxQueue); + + if (queueFamilyProps.isEmpty()) + queryQueueFamilyProps(); + + timestampValidBits = queueFamilyProps[gfxQueueFamilyIdx].timestampValidBits; + } + + f->vkGetPhysicalDeviceProperties(physDev, &physDevProperties); + ubufAlign = physDevProperties.limits.minUniformBufferOffsetAlignment; + // helps little with an optimal offset of 1 (on some drivers) when the spec + // elsewhere states that the minimum bufferOffset is 4... + texbufAlign = qMax(4, physDevProperties.limits.optimalBufferCopyOffsetAlignment); + + if (!importedAllocator) { + VmaVulkanFunctions afuncs; + afuncs.vkGetPhysicalDeviceProperties = wrap_vkGetPhysicalDeviceProperties; + afuncs.vkGetPhysicalDeviceMemoryProperties = wrap_vkGetPhysicalDeviceMemoryProperties; + afuncs.vkAllocateMemory = wrap_vkAllocateMemory; + afuncs.vkFreeMemory = wrap_vkFreeMemory; + afuncs.vkMapMemory = wrap_vkMapMemory; + afuncs.vkUnmapMemory = wrap_vkUnmapMemory; + afuncs.vkFlushMappedMemoryRanges = wrap_vkFlushMappedMemoryRanges; + afuncs.vkInvalidateMappedMemoryRanges = wrap_vkInvalidateMappedMemoryRanges; + afuncs.vkBindBufferMemory = wrap_vkBindBufferMemory; + afuncs.vkBindImageMemory = wrap_vkBindImageMemory; + afuncs.vkGetBufferMemoryRequirements = wrap_vkGetBufferMemoryRequirements; + afuncs.vkGetImageMemoryRequirements = wrap_vkGetImageMemoryRequirements; + afuncs.vkCreateBuffer = wrap_vkCreateBuffer; + afuncs.vkDestroyBuffer = wrap_vkDestroyBuffer; + afuncs.vkCreateImage = wrap_vkCreateImage; + afuncs.vkDestroyImage = wrap_vkDestroyImage; + + VmaAllocatorCreateInfo allocatorInfo; + memset(&allocatorInfo, 0, sizeof(allocatorInfo)); + allocatorInfo.physicalDevice = physDev; + allocatorInfo.device = dev; + allocatorInfo.pVulkanFunctions = &afuncs; + VmaAllocator vmaallocator; + VkResult err = vmaCreateAllocator(&allocatorInfo, &vmaallocator); + if (err != VK_SUCCESS) { + qWarning("Failed to create allocator: %d", err); + return false; + } + allocator = vmaallocator; + } + + inst->installDebugOutputFilter(qvk_debug_filter); + + VkDescriptorPool pool; + VkResult err = createDescriptorPool(&pool); + if (err == VK_SUCCESS) + descriptorPools.append(pool); + else + qWarning("Failed to create initial descriptor pool: %d", err); + + VkQueryPoolCreateInfo timestampQueryPoolInfo; + memset(×tampQueryPoolInfo, 0, sizeof(timestampQueryPoolInfo)); + timestampQueryPoolInfo.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO; + timestampQueryPoolInfo.queryType = VK_QUERY_TYPE_TIMESTAMP; + timestampQueryPoolInfo.queryCount = QVK_MAX_ACTIVE_TIMESTAMP_PAIRS * 2; + err = df->vkCreateQueryPool(dev, ×tampQueryPoolInfo, nullptr, ×tampQueryPool); + if (err != VK_SUCCESS) { + qWarning("Failed to create timestamp query pool: %d", err); + return false; + } + timestampQueryPoolMap.resize(QVK_MAX_ACTIVE_TIMESTAMP_PAIRS); // 1 bit per pair + timestampQueryPoolMap.fill(false); + + if (debugMarkersAvailable) { + vkCmdDebugMarkerBegin = reinterpret_cast(f->vkGetDeviceProcAddr(dev, "vkCmdDebugMarkerBeginEXT")); + vkCmdDebugMarkerEnd = reinterpret_cast(f->vkGetDeviceProcAddr(dev, "vkCmdDebugMarkerEndEXT")); + vkCmdDebugMarkerInsert = reinterpret_cast(f->vkGetDeviceProcAddr(dev, "vkCmdDebugMarkerInsertEXT")); + vkDebugMarkerSetObjectName = reinterpret_cast(f->vkGetDeviceProcAddr(dev, "vkDebugMarkerSetObjectNameEXT")); + } + + nativeHandlesStruct.physDev = physDev; + nativeHandlesStruct.dev = dev; + nativeHandlesStruct.gfxQueueFamilyIdx = gfxQueueFamilyIdx; + nativeHandlesStruct.gfxQueue = gfxQueue; + nativeHandlesStruct.cmdPool = cmdPool; + nativeHandlesStruct.vmemAllocator = allocator; + + return true; +} + +void QRhiVulkan::destroy() +{ + if (!df) + return; + + df->vkDeviceWaitIdle(dev); + + executeDeferredReleases(true); + finishActiveReadbacks(true); + + if (ofr.cmdFence) { + df->vkDestroyFence(dev, ofr.cmdFence, nullptr); + ofr.cmdFence = VK_NULL_HANDLE; + } + + if (ofr.cbWrapper.cb) { + df->vkFreeCommandBuffers(dev, cmdPool, 1, &ofr.cbWrapper.cb); + ofr.cbWrapper.cb = VK_NULL_HANDLE; + } + + if (pipelineCache) { + df->vkDestroyPipelineCache(dev, pipelineCache, nullptr); + pipelineCache = VK_NULL_HANDLE; + } + + for (const DescriptorPoolData &pool : descriptorPools) + df->vkDestroyDescriptorPool(dev, pool.pool, nullptr); + + descriptorPools.clear(); + + if (timestampQueryPool) { + df->vkDestroyQueryPool(dev, timestampQueryPool, nullptr); + timestampQueryPool = VK_NULL_HANDLE; + } + + if (!importedAllocator && allocator) { + vmaDestroyAllocator(toVmaAllocator(allocator)); + allocator = nullptr; + } + + if (!importedCmdPool && cmdPool) { + df->vkDestroyCommandPool(dev, cmdPool, nullptr); + cmdPool = VK_NULL_HANDLE; + } + + if (!importedDevice && dev) { + df->vkDestroyDevice(dev, nullptr); + inst->resetDeviceFunctions(dev); + dev = VK_NULL_HANDLE; + } + + f = nullptr; + df = nullptr; +} + +VkResult QRhiVulkan::createDescriptorPool(VkDescriptorPool *pool) +{ + VkDescriptorPoolSize descPoolSizes[] = { + { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, QVK_UNIFORM_BUFFERS_PER_POOL }, + { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, QVK_UNIFORM_BUFFERS_PER_POOL }, + { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, QVK_COMBINED_IMAGE_SAMPLERS_PER_POOL } + }; + VkDescriptorPoolCreateInfo descPoolInfo; + memset(&descPoolInfo, 0, sizeof(descPoolInfo)); + descPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + // Do not enable vkFreeDescriptorSets - sets are never freed on their own + // (good so no trouble with fragmentation), they just deref their pool + // which is then reset at some point (or not). + descPoolInfo.flags = 0; + descPoolInfo.maxSets = QVK_DESC_SETS_PER_POOL; + descPoolInfo.poolSizeCount = sizeof(descPoolSizes) / sizeof(descPoolSizes[0]); + descPoolInfo.pPoolSizes = descPoolSizes; + return df->vkCreateDescriptorPool(dev, &descPoolInfo, nullptr, pool); +} + +bool QRhiVulkan::allocateDescriptorSet(VkDescriptorSetAllocateInfo *allocInfo, VkDescriptorSet *result, int *resultPoolIndex) +{ + auto tryAllocate = [this, allocInfo, result](int poolIndex) { + allocInfo->descriptorPool = descriptorPools[poolIndex].pool; + VkResult r = df->vkAllocateDescriptorSets(dev, allocInfo, result); + if (r == VK_SUCCESS) + descriptorPools[poolIndex].refCount += 1; + return r; + }; + + int lastPoolIdx = descriptorPools.count() - 1; + for (int i = lastPoolIdx; i >= 0; --i) { + if (descriptorPools[i].refCount == 0) { + df->vkResetDescriptorPool(dev, descriptorPools[i].pool, 0); + descriptorPools[i].allocedDescSets = 0; + } + if (descriptorPools[i].allocedDescSets + allocInfo->descriptorSetCount <= QVK_DESC_SETS_PER_POOL) { + VkResult err = tryAllocate(i); + if (err == VK_SUCCESS) { + descriptorPools[i].allocedDescSets += allocInfo->descriptorSetCount; + *resultPoolIndex = i; + return true; + } + } + } + + VkDescriptorPool newPool; + VkResult poolErr = createDescriptorPool(&newPool); + if (poolErr == VK_SUCCESS) { + descriptorPools.append(newPool); + lastPoolIdx = descriptorPools.count() - 1; + VkResult err = tryAllocate(lastPoolIdx); + if (err != VK_SUCCESS) { + qWarning("Failed to allocate descriptor set from new pool too, giving up: %d", err); + return false; + } + descriptorPools[lastPoolIdx].allocedDescSets += allocInfo->descriptorSetCount; + *resultPoolIndex = lastPoolIdx; + return true; + } else { + qWarning("Failed to allocate new descriptor pool: %d", poolErr); + return false; + } +} + +static inline VkFormat toVkTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags) +{ + const bool srgb = flags.testFlag(QRhiTexture::sRGB); + switch (format) { + case QRhiTexture::RGBA8: + return srgb ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM; + case QRhiTexture::BGRA8: + return srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM; + case QRhiTexture::R8: + return srgb ? VK_FORMAT_R8_SRGB : VK_FORMAT_R8_UNORM; + case QRhiTexture::R16: + return VK_FORMAT_R16_UNORM; + case QRhiTexture::RED_OR_ALPHA8: + return VK_FORMAT_R8_UNORM; + + case QRhiTexture::RGBA16F: + return VK_FORMAT_R16G16B16A16_SFLOAT; + case QRhiTexture::RGBA32F: + return VK_FORMAT_R32G32B32A32_SFLOAT; + + case QRhiTexture::D16: + return VK_FORMAT_D16_UNORM; + case QRhiTexture::D32F: + return VK_FORMAT_D32_SFLOAT; + + case QRhiTexture::BC1: + return srgb ? VK_FORMAT_BC1_RGB_SRGB_BLOCK : VK_FORMAT_BC1_RGB_UNORM_BLOCK; + case QRhiTexture::BC2: + return srgb ? VK_FORMAT_BC2_SRGB_BLOCK : VK_FORMAT_BC2_UNORM_BLOCK; + case QRhiTexture::BC3: + return srgb ? VK_FORMAT_BC3_SRGB_BLOCK : VK_FORMAT_BC3_UNORM_BLOCK; + case QRhiTexture::BC4: + return VK_FORMAT_BC4_UNORM_BLOCK; + case QRhiTexture::BC5: + return VK_FORMAT_BC5_UNORM_BLOCK; + case QRhiTexture::BC6H: + return VK_FORMAT_BC6H_UFLOAT_BLOCK; + case QRhiTexture::BC7: + return srgb ? VK_FORMAT_BC7_SRGB_BLOCK : VK_FORMAT_BC7_UNORM_BLOCK; + + case QRhiTexture::ETC2_RGB8: + return srgb ? VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK : VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK; + case QRhiTexture::ETC2_RGB8A1: + return srgb ? VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK : VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK; + case QRhiTexture::ETC2_RGBA8: + return srgb ? VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK : VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK; + + case QRhiTexture::ASTC_4x4: + return srgb ? VK_FORMAT_ASTC_4x4_SRGB_BLOCK : VK_FORMAT_ASTC_4x4_UNORM_BLOCK; + case QRhiTexture::ASTC_5x4: + return srgb ? VK_FORMAT_ASTC_5x4_SRGB_BLOCK : VK_FORMAT_ASTC_5x4_UNORM_BLOCK; + case QRhiTexture::ASTC_5x5: + return srgb ? VK_FORMAT_ASTC_5x5_SRGB_BLOCK : VK_FORMAT_ASTC_5x5_UNORM_BLOCK; + case QRhiTexture::ASTC_6x5: + return srgb ? VK_FORMAT_ASTC_6x5_SRGB_BLOCK : VK_FORMAT_ASTC_6x5_UNORM_BLOCK; + case QRhiTexture::ASTC_6x6: + return srgb ? VK_FORMAT_ASTC_6x6_SRGB_BLOCK : VK_FORMAT_ASTC_6x6_UNORM_BLOCK; + case QRhiTexture::ASTC_8x5: + return srgb ? VK_FORMAT_ASTC_8x5_SRGB_BLOCK : VK_FORMAT_ASTC_8x5_UNORM_BLOCK; + case QRhiTexture::ASTC_8x6: + return srgb ? VK_FORMAT_ASTC_8x6_SRGB_BLOCK : VK_FORMAT_ASTC_8x6_UNORM_BLOCK; + case QRhiTexture::ASTC_8x8: + return srgb ? VK_FORMAT_ASTC_8x8_SRGB_BLOCK : VK_FORMAT_ASTC_8x8_UNORM_BLOCK; + case QRhiTexture::ASTC_10x5: + return srgb ? VK_FORMAT_ASTC_10x5_SRGB_BLOCK : VK_FORMAT_ASTC_10x5_UNORM_BLOCK; + case QRhiTexture::ASTC_10x6: + return srgb ? VK_FORMAT_ASTC_10x6_SRGB_BLOCK : VK_FORMAT_ASTC_10x6_UNORM_BLOCK; + case QRhiTexture::ASTC_10x8: + return srgb ? VK_FORMAT_ASTC_10x8_SRGB_BLOCK : VK_FORMAT_ASTC_10x8_UNORM_BLOCK; + case QRhiTexture::ASTC_10x10: + return srgb ? VK_FORMAT_ASTC_10x10_SRGB_BLOCK : VK_FORMAT_ASTC_10x10_UNORM_BLOCK; + case QRhiTexture::ASTC_12x10: + return srgb ? VK_FORMAT_ASTC_12x10_SRGB_BLOCK : VK_FORMAT_ASTC_12x10_UNORM_BLOCK; + case QRhiTexture::ASTC_12x12: + return srgb ? VK_FORMAT_ASTC_12x12_SRGB_BLOCK : VK_FORMAT_ASTC_12x12_UNORM_BLOCK; + + default: + Q_UNREACHABLE(); + return VK_FORMAT_R8G8B8A8_UNORM; + } +} + +static inline QRhiTexture::Format colorTextureFormatFromVkFormat(VkFormat format, QRhiTexture::Flags *flags) +{ + switch (format) { + case VK_FORMAT_R8G8B8A8_UNORM: + return QRhiTexture::RGBA8; + case VK_FORMAT_R8G8B8A8_SRGB: + if (flags) + (*flags) |= QRhiTexture::sRGB; + return QRhiTexture::RGBA8; + case VK_FORMAT_B8G8R8A8_UNORM: + return QRhiTexture::BGRA8; + case VK_FORMAT_B8G8R8A8_SRGB: + if (flags) + (*flags) |= QRhiTexture::sRGB; + return QRhiTexture::BGRA8; + case VK_FORMAT_R8_UNORM: + return QRhiTexture::R8; + case VK_FORMAT_R8_SRGB: + if (flags) + (*flags) |= QRhiTexture::sRGB; + return QRhiTexture::R8; + case VK_FORMAT_R16_UNORM: + return QRhiTexture::R16; + default: // this cannot assert, must warn and return unknown + qWarning("VkFormat %d is not a recognized uncompressed color format", format); + break; + } + return QRhiTexture::UnknownFormat; +} + +static inline bool isDepthTextureFormat(QRhiTexture::Format format) +{ + switch (format) { + case QRhiTexture::Format::D16: + Q_FALLTHROUGH(); + case QRhiTexture::Format::D32F: + return true; + + default: + return false; + } +} + +// Transient images ("render buffers") backed by lazily allocated memory are +// managed manually without going through vk_mem_alloc since it does not offer +// any support for such images. This should be ok since in practice there +// should be very few of such images. + +uint32_t QRhiVulkan::chooseTransientImageMemType(VkImage img, uint32_t startIndex) +{ + VkPhysicalDeviceMemoryProperties physDevMemProps; + f->vkGetPhysicalDeviceMemoryProperties(physDev, &physDevMemProps); + + VkMemoryRequirements memReq; + df->vkGetImageMemoryRequirements(dev, img, &memReq); + uint32_t memTypeIndex = uint32_t(-1); + + if (memReq.memoryTypeBits) { + // Find a device local + lazily allocated, or at least device local memtype. + const VkMemoryType *memType = physDevMemProps.memoryTypes; + bool foundDevLocal = false; + for (uint32_t i = startIndex; i < physDevMemProps.memoryTypeCount; ++i) { + if (memReq.memoryTypeBits & (1 << i)) { + if (memType[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) { + if (!foundDevLocal) { + foundDevLocal = true; + memTypeIndex = i; + } + if (memType[i].propertyFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) { + memTypeIndex = i; + break; + } + } + } + } + } + + return memTypeIndex; +} + +bool QRhiVulkan::createTransientImage(VkFormat format, + const QSize &pixelSize, + VkImageUsageFlags usage, + VkImageAspectFlags aspectMask, + VkSampleCountFlagBits samples, + VkDeviceMemory *mem, + VkImage *images, + VkImageView *views, + int count) +{ + VkMemoryRequirements memReq; + VkResult err; + + for (int i = 0; i < count; ++i) { + VkImageCreateInfo imgInfo; + memset(&imgInfo, 0, sizeof(imgInfo)); + imgInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imgInfo.imageType = VK_IMAGE_TYPE_2D; + imgInfo.format = format; + imgInfo.extent.width = pixelSize.width(); + imgInfo.extent.height = pixelSize.height(); + imgInfo.extent.depth = 1; + imgInfo.mipLevels = imgInfo.arrayLayers = 1; + imgInfo.samples = samples; + imgInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imgInfo.usage = usage | VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT; + imgInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + err = df->vkCreateImage(dev, &imgInfo, nullptr, images + i); + if (err != VK_SUCCESS) { + qWarning("Failed to create image: %d", err); + return false; + } + + // Assume the reqs are the same since the images are same in every way. + // Still, call GetImageMemReq for every image, in order to prevent the + // validation layer from complaining. + df->vkGetImageMemoryRequirements(dev, images[i], &memReq); + } + + VkMemoryAllocateInfo memInfo; + memset(&memInfo, 0, sizeof(memInfo)); + memInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memInfo.allocationSize = aligned(memReq.size, memReq.alignment) * count; + + uint32_t startIndex = 0; + do { + memInfo.memoryTypeIndex = chooseTransientImageMemType(images[0], startIndex); + if (memInfo.memoryTypeIndex == uint32_t(-1)) { + qWarning("No suitable memory type found"); + return false; + } + startIndex = memInfo.memoryTypeIndex + 1; + err = df->vkAllocateMemory(dev, &memInfo, nullptr, mem); + if (err != VK_SUCCESS && err != VK_ERROR_OUT_OF_DEVICE_MEMORY) { + qWarning("Failed to allocate image memory: %d", err); + return false; + } + } while (err != VK_SUCCESS); + + VkDeviceSize ofs = 0; + for (int i = 0; i < count; ++i) { + err = df->vkBindImageMemory(dev, images[i], *mem, ofs); + if (err != VK_SUCCESS) { + qWarning("Failed to bind image memory: %d", err); + return false; + } + ofs += aligned(memReq.size, memReq.alignment); + + VkImageViewCreateInfo imgViewInfo; + memset(&imgViewInfo, 0, sizeof(imgViewInfo)); + imgViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + imgViewInfo.image = images[i]; + imgViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + imgViewInfo.format = format; + imgViewInfo.components.r = VK_COMPONENT_SWIZZLE_R; + imgViewInfo.components.g = VK_COMPONENT_SWIZZLE_G; + imgViewInfo.components.b = VK_COMPONENT_SWIZZLE_B; + imgViewInfo.components.a = VK_COMPONENT_SWIZZLE_A; + imgViewInfo.subresourceRange.aspectMask = aspectMask; + imgViewInfo.subresourceRange.levelCount = imgViewInfo.subresourceRange.layerCount = 1; + + err = df->vkCreateImageView(dev, &imgViewInfo, nullptr, views + i); + if (err != VK_SUCCESS) { + qWarning("Failed to create image view: %d", err); + return false; + } + } + + return true; +} + +VkFormat QRhiVulkan::optimalDepthStencilFormat() +{ + if (optimalDsFormat != VK_FORMAT_UNDEFINED) + return optimalDsFormat; + + const VkFormat dsFormatCandidates[] = { + VK_FORMAT_D24_UNORM_S8_UINT, + VK_FORMAT_D32_SFLOAT_S8_UINT, + VK_FORMAT_D16_UNORM_S8_UINT + }; + const int dsFormatCandidateCount = sizeof(dsFormatCandidates) / sizeof(VkFormat); + int dsFormatIdx = 0; + while (dsFormatIdx < dsFormatCandidateCount) { + optimalDsFormat = dsFormatCandidates[dsFormatIdx]; + VkFormatProperties fmtProp; + f->vkGetPhysicalDeviceFormatProperties(physDev, optimalDsFormat, &fmtProp); + if (fmtProp.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) + break; + ++dsFormatIdx; + } + if (dsFormatIdx == dsFormatCandidateCount) + qWarning("Failed to find an optimal depth-stencil format"); + + return optimalDsFormat; +} + +bool QRhiVulkan::createDefaultRenderPass(VkRenderPass *rp, bool hasDepthStencil, VkSampleCountFlagBits samples, VkFormat colorFormat) +{ + VkAttachmentDescription attDesc[3]; + memset(attDesc, 0, sizeof(attDesc)); + + // attachment list layout is color (1), ds (0-1), resolve (0-1) + + attDesc[0].format = colorFormat; + attDesc[0].samples = samples; + attDesc[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attDesc[0].storeOp = samples > VK_SAMPLE_COUNT_1_BIT ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE; + attDesc[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attDesc[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attDesc[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attDesc[0].finalLayout = samples > VK_SAMPLE_COUNT_1_BIT ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + // clear on load + no store + lazy alloc + transient image should play + // nicely with tiled GPUs (no physical backing necessary for ds buffer) + attDesc[1].format = optimalDepthStencilFormat(); + attDesc[1].samples = samples; + attDesc[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attDesc[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attDesc[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attDesc[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attDesc[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attDesc[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + if (samples > VK_SAMPLE_COUNT_1_BIT) { + attDesc[2].format = colorFormat; + attDesc[2].samples = VK_SAMPLE_COUNT_1_BIT; + attDesc[2].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attDesc[2].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attDesc[2].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attDesc[2].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attDesc[2].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attDesc[2].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + } + + VkAttachmentReference colorRef = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; + VkAttachmentReference dsRef = { 1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL }; + VkAttachmentReference resolveRef = { 2, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; + + VkSubpassDescription subpassDesc; + memset(&subpassDesc, 0, sizeof(subpassDesc)); + subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpassDesc.colorAttachmentCount = 1; + subpassDesc.pColorAttachments = &colorRef; + subpassDesc.pDepthStencilAttachment = hasDepthStencil ? &dsRef : nullptr; + + // Replace the first implicit dep (TOP_OF_PIPE / ALL_COMMANDS) with our own. + VkSubpassDependency subpassDep; + memset(&subpassDep, 0, sizeof(subpassDep)); + subpassDep.srcSubpass = VK_SUBPASS_EXTERNAL; + subpassDep.dstSubpass = 0; + subpassDep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + subpassDep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + subpassDep.srcAccessMask = 0; + subpassDep.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + VkRenderPassCreateInfo rpInfo; + memset(&rpInfo, 0, sizeof(rpInfo)); + rpInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + rpInfo.attachmentCount = 1; + rpInfo.pAttachments = attDesc; + rpInfo.subpassCount = 1; + rpInfo.pSubpasses = &subpassDesc; + rpInfo.dependencyCount = 1; + rpInfo.pDependencies = &subpassDep; + + if (hasDepthStencil) + rpInfo.attachmentCount += 1; + + if (samples > VK_SAMPLE_COUNT_1_BIT) { + rpInfo.attachmentCount += 1; + subpassDesc.pResolveAttachments = &resolveRef; + } + + VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, rp); + if (err != VK_SUCCESS) { + qWarning("Failed to create renderpass: %d", err); + return false; + } + + return true; +} + +bool QRhiVulkan::createOffscreenRenderPass(VkRenderPass *rp, + const QVector &colorAttachments, + bool preserveColor, + bool preserveDs, + QRhiRenderBuffer *depthStencilBuffer, + QRhiTexture *depthTexture) +{ + QVarLengthArray attDescs; + QVarLengthArray colorRefs; + QVarLengthArray resolveRefs; + const int colorAttCount = colorAttachments.count(); + + // attachment list layout is color (0-8), ds (0-1), resolve (0-8) + + for (int i = 0; i < colorAttCount; ++i) { + QVkTexture *texD = QRHI_RES(QVkTexture, colorAttachments[i].texture()); + QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, colorAttachments[i].renderBuffer()); + Q_ASSERT(texD || rbD); + const VkFormat vkformat = texD ? texD->vkformat : rbD->vkformat; + const VkSampleCountFlagBits samples = texD ? texD->samples : rbD->samples; + + VkAttachmentDescription attDesc; + memset(&attDesc, 0, sizeof(attDesc)); + attDesc.format = vkformat; + attDesc.samples = samples; + attDesc.loadOp = preserveColor ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_CLEAR; + attDesc.storeOp = colorAttachments[i].resolveTexture() ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE; + attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + // this has to interact correctly with activateTextureRenderTarget(), hence leaving in COLOR_ATT + attDesc.initialLayout = preserveColor ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED; + attDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attDescs.append(attDesc); + + const VkAttachmentReference ref = { uint32_t(attDescs.count() - 1), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; + colorRefs.append(ref); + } + + const bool hasDepthStencil = depthStencilBuffer || depthTexture; + if (hasDepthStencil) { + const VkFormat dsFormat = depthTexture ? QRHI_RES(QVkTexture, depthTexture)->vkformat + : QRHI_RES(QVkRenderBuffer, depthStencilBuffer)->vkformat; + const VkSampleCountFlagBits samples = depthTexture ? QRHI_RES(QVkTexture, depthTexture)->samples + : QRHI_RES(QVkRenderBuffer, depthStencilBuffer)->samples; + const VkAttachmentLoadOp loadOp = preserveDs ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_CLEAR; + const VkAttachmentStoreOp storeOp = depthTexture ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE; + VkAttachmentDescription attDesc; + memset(&attDesc, 0, sizeof(attDesc)); + attDesc.format = dsFormat; + attDesc.samples = samples; + attDesc.loadOp = loadOp; + attDesc.storeOp = storeOp; + attDesc.stencilLoadOp = loadOp; + attDesc.stencilStoreOp = storeOp; + attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attDesc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + attDescs.append(attDesc); + } + VkAttachmentReference dsRef = { uint32_t(attDescs.count() - 1), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL }; + + for (int i = 0; i < colorAttCount; ++i) { + if (colorAttachments[i].resolveTexture()) { + QVkTexture *rtexD = QRHI_RES(QVkTexture, colorAttachments[i].resolveTexture()); + if (rtexD->samples > VK_SAMPLE_COUNT_1_BIT) + qWarning("Resolving into a multisample texture is not supported"); + + VkAttachmentDescription attDesc; + memset(&attDesc, 0, sizeof(attDesc)); + attDesc.format = rtexD->vkformat; + attDesc.samples = VK_SAMPLE_COUNT_1_BIT; + attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; // ignored + attDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attDescs.append(attDesc); + + const VkAttachmentReference ref = { uint32_t(attDescs.count() - 1), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; + resolveRefs.append(ref); + } else { + const VkAttachmentReference ref = { VK_ATTACHMENT_UNUSED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; + resolveRefs.append(ref); + } + } + + VkSubpassDescription subpassDesc; + memset(&subpassDesc, 0, sizeof(subpassDesc)); + subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpassDesc.colorAttachmentCount = colorRefs.count(); + Q_ASSERT(colorRefs.count() == resolveRefs.count()); + subpassDesc.pColorAttachments = !colorRefs.isEmpty() ? colorRefs.constData() : nullptr; + subpassDesc.pDepthStencilAttachment = hasDepthStencil ? &dsRef : nullptr; + subpassDesc.pResolveAttachments = !resolveRefs.isEmpty() ? resolveRefs.constData() : nullptr; + + VkRenderPassCreateInfo rpInfo; + memset(&rpInfo, 0, sizeof(rpInfo)); + rpInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + rpInfo.attachmentCount = attDescs.count(); + rpInfo.pAttachments = attDescs.constData(); + rpInfo.subpassCount = 1; + rpInfo.pSubpasses = &subpassDesc; + // don't yet know the correct initial/final access and stage stuff for the + // implicit deps at this point, so leave it to the resource tracking to + // generate barriers + + VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, rp); + if (err != VK_SUCCESS) { + qWarning("Failed to create renderpass: %d", err); + return false; + } + + return true; +} + +bool QRhiVulkan::recreateSwapChain(QRhiSwapChain *swapChain) +{ + QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain); + if (swapChainD->pixelSize.isEmpty()) { + qWarning("Surface size is 0, cannot create swapchain"); + return false; + } + + df->vkDeviceWaitIdle(dev); + + if (!vkCreateSwapchainKHR) { + vkCreateSwapchainKHR = reinterpret_cast(f->vkGetDeviceProcAddr(dev, "vkCreateSwapchainKHR")); + vkDestroySwapchainKHR = reinterpret_cast(f->vkGetDeviceProcAddr(dev, "vkDestroySwapchainKHR")); + vkGetSwapchainImagesKHR = reinterpret_cast(f->vkGetDeviceProcAddr(dev, "vkGetSwapchainImagesKHR")); + vkAcquireNextImageKHR = reinterpret_cast(f->vkGetDeviceProcAddr(dev, "vkAcquireNextImageKHR")); + vkQueuePresentKHR = reinterpret_cast(f->vkGetDeviceProcAddr(dev, "vkQueuePresentKHR")); + if (!vkCreateSwapchainKHR || !vkDestroySwapchainKHR || !vkGetSwapchainImagesKHR || !vkAcquireNextImageKHR || !vkQueuePresentKHR) { + qWarning("Swapchain functions not available"); + return false; + } + } + + VkSurfaceCapabilitiesKHR surfaceCaps; + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physDev, swapChainD->surface, &surfaceCaps); + quint32 reqBufferCount; + if (swapChainD->m_flags.testFlag(QRhiSwapChain::MinimalBufferCount)) { + reqBufferCount = qMax(2, surfaceCaps.minImageCount); + } else { + const quint32 maxBuffers = QVkSwapChain::MAX_BUFFER_COUNT; + if (surfaceCaps.maxImageCount) + reqBufferCount = qMax(qMin(surfaceCaps.maxImageCount, maxBuffers), surfaceCaps.minImageCount); + else + reqBufferCount = qMax(2, surfaceCaps.minImageCount); + } + + VkSurfaceTransformFlagBitsKHR preTransform = + (surfaceCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) + ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR + : surfaceCaps.currentTransform; + + VkCompositeAlphaFlagBitsKHR compositeAlpha = + (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) + ? VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR + : VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + + if (swapChainD->m_flags.testFlag(QRhiSwapChain::SurfaceHasPreMulAlpha) + && (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR)) + { + compositeAlpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR; + } + + if (swapChainD->m_flags.testFlag(QRhiSwapChain::SurfaceHasNonPreMulAlpha) + && (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR)) + { + compositeAlpha = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR; + } + + VkImageUsageFlags usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + swapChainD->supportsReadback = (surfaceCaps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT); + if (swapChainD->supportsReadback && swapChainD->m_flags.testFlag(QRhiSwapChain::UsedAsTransferSource)) + usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + + VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR; + if (swapChainD->m_flags.testFlag(QRhiSwapChain::NoVSync)) { + if (swapChainD->supportedPresentationModes.contains(VK_PRESENT_MODE_MAILBOX_KHR)) + presentMode = VK_PRESENT_MODE_MAILBOX_KHR; + else if (swapChainD->supportedPresentationModes.contains(VK_PRESENT_MODE_IMMEDIATE_KHR)) + presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; + } + + // If the surface is different than before, then passing in the old + // swapchain associated with the old surface can fail the swapchain + // creation. (for example, Android loses the surface when backgrounding and + // restoring applications, and it also enforces failing swapchain creation + // 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); + + VkSwapchainCreateInfoKHR swapChainInfo; + memset(&swapChainInfo, 0, sizeof(swapChainInfo)); + swapChainInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + swapChainInfo.surface = swapChainD->surface; + swapChainInfo.minImageCount = reqBufferCount; + swapChainInfo.imageFormat = swapChainD->colorFormat; + swapChainInfo.imageColorSpace = swapChainD->colorSpace; + swapChainInfo.imageExtent = VkExtent2D { uint32_t(swapChainD->pixelSize.width()), uint32_t(swapChainD->pixelSize.height()) }; + swapChainInfo.imageArrayLayers = 1; + swapChainInfo.imageUsage = usage; + swapChainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + swapChainInfo.preTransform = preTransform; + swapChainInfo.compositeAlpha = compositeAlpha; + swapChainInfo.presentMode = presentMode; + swapChainInfo.clipped = true; + swapChainInfo.oldSwapchain = reuseExisting ? swapChainD->sc : VK_NULL_HANDLE; + + VkSwapchainKHR newSwapChain; + VkResult err = vkCreateSwapchainKHR(dev, &swapChainInfo, nullptr, &newSwapChain); + if (err != VK_SUCCESS) { + qWarning("Failed to create swapchain: %d", err); + return false; + } + + if (swapChainD->sc) + releaseSwapChainResources(swapChain); + + swapChainD->sc = newSwapChain; + swapChainD->lastConnectedSurface = swapChainD->surface; + + quint32 actualSwapChainBufferCount = 0; + err = vkGetSwapchainImagesKHR(dev, swapChainD->sc, &actualSwapChainBufferCount, nullptr); + if (err != VK_SUCCESS || actualSwapChainBufferCount < 2) { + qWarning("Failed to get swapchain images: %d (count=%u)", err, actualSwapChainBufferCount); + return false; + } + + if (actualSwapChainBufferCount > QVkSwapChain::MAX_BUFFER_COUNT) { + qWarning("Too many swapchain buffers (%u)", actualSwapChainBufferCount); + return false; + } + if (actualSwapChainBufferCount != reqBufferCount) + qDebug("Actual swapchain buffer count is %u", actualSwapChainBufferCount); + swapChainD->bufferCount = actualSwapChainBufferCount; + + VkImage swapChainImages[QVkSwapChain::MAX_BUFFER_COUNT]; + err = vkGetSwapchainImagesKHR(dev, swapChainD->sc, &actualSwapChainBufferCount, swapChainImages); + if (err != VK_SUCCESS) { + qWarning("Failed to get swapchain images: %d", err); + return false; + } + + VkImage msaaImages[QVkSwapChain::MAX_BUFFER_COUNT]; + VkImageView msaaViews[QVkSwapChain::MAX_BUFFER_COUNT]; + if (swapChainD->samples > VK_SAMPLE_COUNT_1_BIT) { + if (!createTransientImage(swapChainD->colorFormat, + swapChainD->pixelSize, + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + VK_IMAGE_ASPECT_COLOR_BIT, + swapChainD->samples, + &swapChainD->msaaImageMem, + msaaImages, + msaaViews, + swapChainD->bufferCount)) + { + qWarning("Failed to create transient image for MSAA color buffer"); + return false; + } + } + + VkFenceCreateInfo fenceInfo; + memset(&fenceInfo, 0, sizeof(fenceInfo)); + fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; + + for (int i = 0; i < swapChainD->bufferCount; ++i) { + QVkSwapChain::ImageResources &image(swapChainD->imageRes[i]); + image.image = swapChainImages[i]; + if (swapChainD->samples > VK_SAMPLE_COUNT_1_BIT) { + image.msaaImage = msaaImages[i]; + image.msaaImageView = msaaViews[i]; + } + + VkImageViewCreateInfo imgViewInfo; + memset(&imgViewInfo, 0, sizeof(imgViewInfo)); + imgViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + imgViewInfo.image = swapChainImages[i]; + imgViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + imgViewInfo.format = swapChainD->colorFormat; + imgViewInfo.components.r = VK_COMPONENT_SWIZZLE_R; + imgViewInfo.components.g = VK_COMPONENT_SWIZZLE_G; + imgViewInfo.components.b = VK_COMPONENT_SWIZZLE_B; + imgViewInfo.components.a = VK_COMPONENT_SWIZZLE_A; + imgViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imgViewInfo.subresourceRange.levelCount = imgViewInfo.subresourceRange.layerCount = 1; + err = df->vkCreateImageView(dev, &imgViewInfo, nullptr, &image.imageView); + if (err != VK_SUCCESS) { + qWarning("Failed to create swapchain image view %d: %d", i, err); + return false; + } + } + + swapChainD->currentImageIndex = 0; + + VkSemaphoreCreateInfo semInfo; + memset(&semInfo, 0, sizeof(semInfo)); + semInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + + for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) { + QVkSwapChain::FrameResources &frame(swapChainD->frameRes[i]); + + frame.imageAcquired = false; + frame.imageSemWaitable = false; + + df->vkCreateFence(dev, &fenceInfo, nullptr, &frame.imageFence); + frame.imageFenceWaitable = true; // fence was created in signaled state + + df->vkCreateSemaphore(dev, &semInfo, nullptr, &frame.imageSem); + df->vkCreateSemaphore(dev, &semInfo, nullptr, &frame.drawSem); + + err = df->vkCreateFence(dev, &fenceInfo, nullptr, &frame.cmdFence); + if (err != VK_SUCCESS) { + qWarning("Failed to create command buffer fence: %d", err); + return false; + } + frame.cmdFenceWaitable = true; // fence was created in signaled state + } + + swapChainD->currentFrameSlot = 0; + + return true; +} + +void QRhiVulkan::releaseSwapChainResources(QRhiSwapChain *swapChain) +{ + QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain); + + if (swapChainD->sc == VK_NULL_HANDLE) + return; + + df->vkDeviceWaitIdle(dev); + + for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) { + QVkSwapChain::FrameResources &frame(swapChainD->frameRes[i]); + if (frame.cmdFence) { + if (frame.cmdFenceWaitable) + df->vkWaitForFences(dev, 1, &frame.cmdFence, VK_TRUE, UINT64_MAX); + df->vkDestroyFence(dev, frame.cmdFence, nullptr); + frame.cmdFence = VK_NULL_HANDLE; + frame.cmdFenceWaitable = false; + } + if (frame.imageFence) { + if (frame.imageFenceWaitable) + df->vkWaitForFences(dev, 1, &frame.imageFence, VK_TRUE, UINT64_MAX); + df->vkDestroyFence(dev, frame.imageFence, nullptr); + frame.imageFence = VK_NULL_HANDLE; + frame.imageFenceWaitable = false; + } + if (frame.imageSem) { + df->vkDestroySemaphore(dev, frame.imageSem, nullptr); + frame.imageSem = VK_NULL_HANDLE; + } + if (frame.drawSem) { + df->vkDestroySemaphore(dev, frame.drawSem, nullptr); + frame.drawSem = VK_NULL_HANDLE; + } + if (frame.cmdBuf) { + df->vkFreeCommandBuffers(dev, cmdPool, 1, &frame.cmdBuf); + frame.cmdBuf = VK_NULL_HANDLE; + } + } + + for (int i = 0; i < swapChainD->bufferCount; ++i) { + QVkSwapChain::ImageResources &image(swapChainD->imageRes[i]); + if (image.fb) { + df->vkDestroyFramebuffer(dev, image.fb, nullptr); + image.fb = VK_NULL_HANDLE; + } + if (image.imageView) { + df->vkDestroyImageView(dev, image.imageView, nullptr); + image.imageView = VK_NULL_HANDLE; + } + if (image.msaaImageView) { + df->vkDestroyImageView(dev, image.msaaImageView, nullptr); + image.msaaImageView = VK_NULL_HANDLE; + } + if (image.msaaImage) { + df->vkDestroyImage(dev, image.msaaImage, nullptr); + image.msaaImage = VK_NULL_HANDLE; + } + } + + if (swapChainD->msaaImageMem) { + df->vkFreeMemory(dev, swapChainD->msaaImageMem, nullptr); + swapChainD->msaaImageMem = VK_NULL_HANDLE; + } + + vkDestroySwapchainKHR(dev, swapChainD->sc, nullptr); + swapChainD->sc = VK_NULL_HANDLE; + + // NB! surface and similar must remain intact +} + +static inline bool checkDeviceLost(VkResult err) +{ + if (err == VK_ERROR_DEVICE_LOST) { + qWarning("Device lost"); + return true; + } + return false; +} + +QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) +{ + Q_UNUSED(flags); + QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain); + QVkSwapChain::FrameResources &frame(swapChainD->frameRes[swapChainD->currentFrameSlot]); + QRhiProfilerPrivate *rhiP = profilerPrivateOrNull(); + + if (!frame.imageAcquired) { + // Wait if we are too far ahead, i.e. the thread gets throttled based on the presentation rate + // (note that we are using FIFO mode -> vsync) + if (frame.imageFenceWaitable) { + df->vkWaitForFences(dev, 1, &frame.imageFence, VK_TRUE, UINT64_MAX); + df->vkResetFences(dev, 1, &frame.imageFence); + frame.imageFenceWaitable = false; + } + + // move on to next swapchain image + VkResult err = vkAcquireNextImageKHR(dev, swapChainD->sc, UINT64_MAX, + frame.imageSem, frame.imageFence, &frame.imageIndex); + if (err == VK_SUCCESS || err == VK_SUBOPTIMAL_KHR) { + swapChainD->currentImageIndex = frame.imageIndex; + frame.imageSemWaitable = true; + frame.imageAcquired = true; + frame.imageFenceWaitable = true; + } else if (err == VK_ERROR_OUT_OF_DATE_KHR) { + return QRhi::FrameOpSwapChainOutOfDate; + } else { + if (checkDeviceLost(err)) + return QRhi::FrameOpDeviceLost; + else + qWarning("Failed to acquire next swapchain image: %d", err); + return QRhi::FrameOpError; + } + } + + // Make sure the previous commands for the same image have finished. (note + // that this is based on the fence from the command buffer submit, nothing + // to do with the Present) + // + // Do this also for any other swapchain's commands with the same frame slot + // While this reduces concurrency, it keeps resource usage safe: swapchain + // A starting its frame 0, followed by swapchain B starting its own frame 0 + // will make B wait for A's frame 0 commands, so if a resource is written + // in B's frame or when B checks for pending resource releases, that won't + // mess up A's in-flight commands (as they are not in flight anymore). + waitCommandCompletion(swapChainD->currentFrameSlot); + + // Now is the time to read the timestamps for the previous frame for this slot. + if (frame.timestampQueryIndex >= 0) { + quint64 timestamp[2] = { 0, 0 }; + VkResult err = df->vkGetQueryPoolResults(dev, timestampQueryPool, frame.timestampQueryIndex, 2, + 2 * sizeof(quint64), timestamp, sizeof(quint64), VK_QUERY_RESULT_64_BIT); + timestampQueryPoolMap.clearBit(frame.timestampQueryIndex / 2); + frame.timestampQueryIndex = -1; + if (err == VK_SUCCESS) { + quint64 mask = 0; + for (quint64 i = 0; i < timestampValidBits; i += 8) + mask |= 0xFFULL << i; + const quint64 ts0 = timestamp[0] & mask; + const quint64 ts1 = timestamp[1] & mask; + const float nsecsPerTick = physDevProperties.limits.timestampPeriod; + if (!qFuzzyIsNull(nsecsPerTick)) { + const float elapsedMs = float(ts1 - ts0) * nsecsPerTick / 1000000.0f; + // now we have the gpu time for the previous frame for this slot, report it + // (does not matter that it is not for this frame) + QRHI_PROF_F(swapChainFrameGpuTime(swapChain, elapsedMs)); + } + } else { + qWarning("Failed to query timestamp: %d", err); + } + } + + // build new draw command buffer + QRhi::FrameOpResult cbres = startCommandBuffer(&frame.cmdBuf); + if (cbres != QRhi::FrameOpSuccess) + return cbres; + + // when profiling is enabled, pick a free query (pair) from the pool + int timestampQueryIdx = -1; + if (profilerPrivateOrNull()) { + for (int i = 0; i < timestampQueryPoolMap.count(); ++i) { + if (!timestampQueryPoolMap.testBit(i)) { + timestampQueryPoolMap.setBit(i); + timestampQueryIdx = i * 2; + break; + } + } + } + if (timestampQueryIdx >= 0) { + df->vkCmdResetQueryPool(frame.cmdBuf, timestampQueryPool, timestampQueryIdx, 2); + // record timestamp at the start of the command buffer + df->vkCmdWriteTimestamp(frame.cmdBuf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + timestampQueryPool, timestampQueryIdx); + frame.timestampQueryIndex = timestampQueryIdx; + } + + swapChainD->cbWrapper.cb = frame.cmdBuf; + QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]); + swapChainD->rtWrapper.d.fb = image.fb; + + currentFrameSlot = swapChainD->currentFrameSlot; + currentSwapChain = swapChainD; + if (swapChainD->ds) + swapChainD->ds->lastActiveFrameSlot = currentFrameSlot; + + QRHI_PROF_F(beginSwapChainFrame(swapChain)); + + prepareNewFrame(&swapChainD->cbWrapper); + + return QRhi::FrameOpSuccess; +} + +QRhi::FrameOpResult QRhiVulkan::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) +{ + Q_ASSERT(inFrame); + inFrame = false; + + QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain); + Q_ASSERT(currentSwapChain == swapChainD); + + recordCommandBuffer(&swapChainD->cbWrapper); + + QVkSwapChain::FrameResources &frame(swapChainD->frameRes[swapChainD->currentFrameSlot]); + QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]); + + if (image.transferSource) { + // was used in a readback as transfer source, go back to presentable layout + VkImageMemoryBarrier presTrans; + memset(&presTrans, 0, sizeof(presTrans)); + presTrans.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + presTrans.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + presTrans.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + presTrans.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + presTrans.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + presTrans.image = image.image; + presTrans.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + presTrans.subresourceRange.levelCount = presTrans.subresourceRange.layerCount = 1; + df->vkCmdPipelineBarrier(frame.cmdBuf, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + 0, 0, nullptr, 0, nullptr, + 1, &presTrans); + image.transferSource = false; + } + + // record another timestamp, when enabled + if (frame.timestampQueryIndex >= 0) { + df->vkCmdWriteTimestamp(frame.cmdBuf, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, + timestampQueryPool, frame.timestampQueryIndex + 1); + } + + // stop recording and submit to the queue + Q_ASSERT(!frame.cmdFenceWaitable); + const bool needsPresent = !flags.testFlag(QRhi::SkipPresent); + QRhi::FrameOpResult submitres = endAndSubmitCommandBuffer(frame.cmdBuf, + frame.cmdFence, + frame.imageSemWaitable ? &frame.imageSem : nullptr, + needsPresent ? &frame.drawSem : nullptr); + if (submitres != QRhi::FrameOpSuccess) + return submitres; + + frame.imageSemWaitable = false; + frame.cmdFenceWaitable = true; + + QRhiProfilerPrivate *rhiP = profilerPrivateOrNull(); + // this must be done before the Present + QRHI_PROF_F(endSwapChainFrame(swapChain, swapChainD->frameCount + 1)); + + if (needsPresent) { + // add the Present to the queue + VkPresentInfoKHR presInfo; + memset(&presInfo, 0, sizeof(presInfo)); + presInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + presInfo.swapchainCount = 1; + presInfo.pSwapchains = &swapChainD->sc; + presInfo.pImageIndices = &swapChainD->currentImageIndex; + presInfo.waitSemaphoreCount = 1; + presInfo.pWaitSemaphores = &frame.drawSem; // gfxQueueFamilyIdx == presQueueFamilyIdx ? &frame.drawSem : &frame.presTransSem; + + VkResult err = vkQueuePresentKHR(gfxQueue, &presInfo); + if (err != VK_SUCCESS) { + if (err == VK_ERROR_OUT_OF_DATE_KHR) { + return QRhi::FrameOpSwapChainOutOfDate; + } else if (err != VK_SUBOPTIMAL_KHR) { + if (checkDeviceLost(err)) + return QRhi::FrameOpDeviceLost; + else + qWarning("Failed to present: %d", err); + return QRhi::FrameOpError; + } + } + + // mark the current swapchain buffer as unused from our side + frame.imageAcquired = false; + // and move on to the next buffer + swapChainD->currentFrameSlot = (swapChainD->currentFrameSlot + 1) % QVK_FRAMES_IN_FLIGHT; + } + + swapChainD->frameCount += 1; + currentSwapChain = nullptr; + return QRhi::FrameOpSuccess; +} + +void QRhiVulkan::prepareNewFrame(QRhiCommandBuffer *cb) +{ + Q_ASSERT(!inFrame); + inFrame = true; + + // Now is the time to do things for frame N-F, where N is the current one, + // F is QVK_FRAMES_IN_FLIGHT, because only here it is guaranteed that that + // frame has completed on the GPU (due to the fence wait in beginFrame). To + // decide if something is safe to handle now a simple "lastActiveFrameSlot + // == currentFrameSlot" is sufficient (remember that e.g. with F==2 + // currentFrameSlot goes 0, 1, 0, 1, 0, ...) + // + // With multiple swapchains on the same QRhi things get more convoluted + // (and currentFrameSlot strictly alternating is not true anymore) but + // beginNonWrapperFrame() solves that by blocking as necessary so the rest + // here is safe regardless. + + executeDeferredReleases(); + + QRHI_RES(QVkCommandBuffer, cb)->resetState(); + + finishActiveReadbacks(); // last, in case the readback-completed callback issues rhi calls +} + +QRhi::FrameOpResult QRhiVulkan::startCommandBuffer(VkCommandBuffer *cb) +{ + if (*cb) { + df->vkFreeCommandBuffers(dev, cmdPool, 1, cb); + *cb = VK_NULL_HANDLE; + } + + VkCommandBufferAllocateInfo cmdBufInfo; + memset(&cmdBufInfo, 0, sizeof(cmdBufInfo)); + cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + cmdBufInfo.commandPool = cmdPool; + cmdBufInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + cmdBufInfo.commandBufferCount = 1; + + VkResult err = df->vkAllocateCommandBuffers(dev, &cmdBufInfo, cb); + if (err != VK_SUCCESS) { + if (checkDeviceLost(err)) + return QRhi::FrameOpDeviceLost; + else + qWarning("Failed to allocate frame command buffer: %d", err); + return QRhi::FrameOpError; + } + + VkCommandBufferBeginInfo cmdBufBeginInfo; + memset(&cmdBufBeginInfo, 0, sizeof(cmdBufBeginInfo)); + cmdBufBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + + err = df->vkBeginCommandBuffer(*cb, &cmdBufBeginInfo); + if (err != VK_SUCCESS) { + if (checkDeviceLost(err)) + return QRhi::FrameOpDeviceLost; + else + qWarning("Failed to begin frame command buffer: %d", err); + return QRhi::FrameOpError; + } + + return QRhi::FrameOpSuccess; +} + +QRhi::FrameOpResult QRhiVulkan::endAndSubmitCommandBuffer(VkCommandBuffer cb, VkFence cmdFence, + VkSemaphore *waitSem, VkSemaphore *signalSem) +{ + VkResult err = df->vkEndCommandBuffer(cb); + if (err != VK_SUCCESS) { + if (checkDeviceLost(err)) + return QRhi::FrameOpDeviceLost; + else + qWarning("Failed to end frame command buffer: %d", err); + return QRhi::FrameOpError; + } + + VkSubmitInfo submitInfo; + memset(&submitInfo, 0, sizeof(submitInfo)); + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &cb; + if (waitSem) { + submitInfo.waitSemaphoreCount = 1; + submitInfo.pWaitSemaphores = waitSem; + } + if (signalSem) { + submitInfo.signalSemaphoreCount = 1; + submitInfo.pSignalSemaphores = signalSem; + } + VkPipelineStageFlags psf = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + submitInfo.pWaitDstStageMask = &psf; + + err = df->vkQueueSubmit(gfxQueue, 1, &submitInfo, cmdFence); + if (err != VK_SUCCESS) { + if (checkDeviceLost(err)) + return QRhi::FrameOpDeviceLost; + else + qWarning("Failed to submit to graphics queue: %d", err); + return QRhi::FrameOpError; + } + + return QRhi::FrameOpSuccess; +} + +void QRhiVulkan::waitCommandCompletion(int frameSlot) +{ + for (QVkSwapChain *sc : qAsConst(swapchains)) { + QVkSwapChain::FrameResources &frame(sc->frameRes[frameSlot]); + if (frame.cmdFenceWaitable) { + df->vkWaitForFences(dev, 1, &frame.cmdFence, VK_TRUE, UINT64_MAX); + df->vkResetFences(dev, 1, &frame.cmdFence); + frame.cmdFenceWaitable = false; + } + } +} + +QRhi::FrameOpResult QRhiVulkan::beginOffscreenFrame(QRhiCommandBuffer **cb) +{ + QRhi::FrameOpResult cbres = startCommandBuffer(&ofr.cbWrapper.cb); + if (cbres != QRhi::FrameOpSuccess) + return cbres; + + // Switch to the next slot manually. Swapchains do not know about this + // which is good. So for example a - unusual but possible - onscreen, + // onscreen, offscreen, onscreen, onscreen, onscreen sequence of + // begin/endFrame leads to 0, 1, 0, 0, 1, 0. This works because the + // offscreen frame is synchronous in the sense that we wait for execution + // to complete in endFrame, and so no resources used in that frame are busy + // anymore in the next frame. + currentFrameSlot = (currentFrameSlot + 1) % QVK_FRAMES_IN_FLIGHT; + // except that this gets complicated with multiple swapchains so make sure + // any pending commands have finished for the frame slot we are going to use + if (swapchains.count() > 1) + waitCommandCompletion(currentFrameSlot); + + prepareNewFrame(&ofr.cbWrapper); + ofr.active = true; + + *cb = &ofr.cbWrapper; + return QRhi::FrameOpSuccess; +} + +QRhi::FrameOpResult QRhiVulkan::endOffscreenFrame() +{ + Q_ASSERT(inFrame); + inFrame = false; + Q_ASSERT(ofr.active); + ofr.active = false; + + recordCommandBuffer(&ofr.cbWrapper); + + if (!ofr.cmdFence) { + VkFenceCreateInfo fenceInfo; + memset(&fenceInfo, 0, sizeof(fenceInfo)); + fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + VkResult err = df->vkCreateFence(dev, &fenceInfo, nullptr, &ofr.cmdFence); + if (err != VK_SUCCESS) { + qWarning("Failed to create command buffer fence: %d", err); + return QRhi::FrameOpError; + } + } + + QRhi::FrameOpResult submitres = endAndSubmitCommandBuffer(ofr.cbWrapper.cb, ofr.cmdFence, nullptr, nullptr); + if (submitres != QRhi::FrameOpSuccess) + return submitres; + + // wait for completion + df->vkWaitForFences(dev, 1, &ofr.cmdFence, VK_TRUE, UINT64_MAX); + df->vkResetFences(dev, 1, &ofr.cmdFence); + + // Here we know that executing the host-side reads for this (or any + // previous) frame is safe since we waited for completion above. + finishActiveReadbacks(true); + + return QRhi::FrameOpSuccess; +} + +QRhi::FrameOpResult QRhiVulkan::finish() +{ + Q_ASSERT(!inPass); + + QVkSwapChain *swapChainD = nullptr; + if (inFrame) { + // There is either a swapchain or an offscreen frame on-going. + // End command recording and submit what we have. + VkCommandBuffer cb; + if (ofr.active) { + Q_ASSERT(!currentSwapChain); + recordCommandBuffer(&ofr.cbWrapper); + cb = ofr.cbWrapper.cb; + } else { + Q_ASSERT(currentSwapChain); + swapChainD = currentSwapChain; + recordCommandBuffer(&swapChainD->cbWrapper); + cb = swapChainD->cbWrapper.cb; + } + QRhi::FrameOpResult submitres = endAndSubmitCommandBuffer(cb, VK_NULL_HANDLE, nullptr, nullptr); + if (submitres != QRhi::FrameOpSuccess) + return submitres; + } + + df->vkQueueWaitIdle(gfxQueue); + + if (inFrame) { + // Allocate and begin recording on a new command buffer. + if (ofr.active) + startCommandBuffer(&ofr.cbWrapper.cb); + else + startCommandBuffer(&swapChainD->frameRes[swapChainD->currentFrameSlot].cmdBuf); + } + + executeDeferredReleases(true); + finishActiveReadbacks(true); + + return QRhi::FrameOpSuccess; +} + +static inline QRhiPassResourceTracker::UsageState toPassTrackerUsageState(const QVkBuffer::UsageState &bufUsage) +{ + QRhiPassResourceTracker::UsageState u; + u.layout = 0; // unused with buffers + u.access = bufUsage.access; + u.stage = bufUsage.stage; + return u; +} + +static inline QRhiPassResourceTracker::UsageState toPassTrackerUsageState(const QVkTexture::UsageState &texUsage) +{ + QRhiPassResourceTracker::UsageState u; + u.layout = texUsage.layout; + u.access = texUsage.access; + u.stage = texUsage.stage; + return u; +} + +void QRhiVulkan::activateTextureRenderTarget(QVkCommandBuffer *cbD, QVkTextureRenderTarget *rtD) +{ + rtD->lastActiveFrameSlot = currentFrameSlot; + rtD->d.rp->lastActiveFrameSlot = currentFrameSlot; + QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]); + const QVector colorAttachments = rtD->m_desc.colorAttachments(); + for (const QRhiColorAttachment &colorAttachment : colorAttachments) { + QVkTexture *texD = QRHI_RES(QVkTexture, colorAttachment.texture()); + QVkTexture *resolveTexD = QRHI_RES(QVkTexture, colorAttachment.resolveTexture()); + QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, colorAttachment.renderBuffer()); + if (texD) { + trackedRegisterTexture(&passResTracker, texD, + QRhiPassResourceTracker::TexColorOutput, + QRhiPassResourceTracker::TexColorOutputStage); + texD->lastActiveFrameSlot = currentFrameSlot; + } else if (rbD) { + // Won't register rbD->backingTexture because it cannot be used for + // anything in a renderpass, its use makes only sense in + // combination with a resolveTexture. + rbD->lastActiveFrameSlot = currentFrameSlot; + } + if (resolveTexD) { + trackedRegisterTexture(&passResTracker, resolveTexD, + QRhiPassResourceTracker::TexColorOutput, + QRhiPassResourceTracker::TexColorOutputStage); + resolveTexD->lastActiveFrameSlot = currentFrameSlot; + } + } + if (rtD->m_desc.depthStencilBuffer()) + QRHI_RES(QVkRenderBuffer, rtD->m_desc.depthStencilBuffer())->lastActiveFrameSlot = currentFrameSlot; + if (rtD->m_desc.depthTexture()) { + QVkTexture *depthTexD = QRHI_RES(QVkTexture, rtD->m_desc.depthTexture()); + trackedRegisterTexture(&passResTracker, depthTexD, + QRhiPassResourceTracker::TexDepthOutput, + QRhiPassResourceTracker::TexDepthOutputStage); + depthTexD->lastActiveFrameSlot = currentFrameSlot; + } +} + +void QRhiVulkan::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) +{ + Q_ASSERT(inFrame && !inPass); + + enqueueResourceUpdates(QRHI_RES(QVkCommandBuffer, cb), resourceUpdates); +} + +void QRhiVulkan::beginPass(QRhiCommandBuffer *cb, + QRhiRenderTarget *rt, + const QColor &colorClearValue, + const QRhiDepthStencilClearValue &depthStencilClearValue, + QRhiResourceUpdateBatch *resourceUpdates) +{ + Q_ASSERT(inFrame && !inPass); + QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); + + if (resourceUpdates) + enqueueResourceUpdates(cbD, resourceUpdates); + + // Insert a TransitionPassResources into the command stream, pointing to + // the tracker this pass is going to use. That's how we generate the + // barriers later during recording the real VkCommandBuffer, right before + // the vkCmdBeginRenderPass. + enqueueTransitionPassResources(cbD); + + QVkRenderTargetData *rtD = nullptr; + switch (rt->resourceType()) { + case QRhiResource::RenderTarget: + rtD = &QRHI_RES(QVkReferenceRenderTarget, rt)->d; + rtD->rp->lastActiveFrameSlot = currentFrameSlot; + break; + case QRhiResource::TextureRenderTarget: + { + QVkTextureRenderTarget *rtTex = QRHI_RES(QVkTextureRenderTarget, rt); + rtD = &rtTex->d; + activateTextureRenderTarget(cbD, rtTex); + } + break; + default: + Q_UNREACHABLE(); + break; + } + + cbD->currentTarget = rt; + + // No copy operations or image layout transitions allowed after this point + // (up until endPass) as we are going to begin the renderpass. + + VkRenderPassBeginInfo rpBeginInfo; + memset(&rpBeginInfo, 0, sizeof(rpBeginInfo)); + rpBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + rpBeginInfo.renderPass = rtD->rp->rp; + rpBeginInfo.framebuffer = rtD->fb; + rpBeginInfo.renderArea.extent.width = rtD->pixelSize.width(); + rpBeginInfo.renderArea.extent.height = rtD->pixelSize.height(); + + QVarLengthArray cvs; + for (int i = 0; i < rtD->colorAttCount; ++i) { + VkClearValue cv; + cv.color = { { float(colorClearValue.redF()), float(colorClearValue.greenF()), float(colorClearValue.blueF()), + float(colorClearValue.alphaF()) } }; + cvs.append(cv); + } + for (int i = 0; i < rtD->dsAttCount; ++i) { + VkClearValue cv; + cv.depthStencil = { depthStencilClearValue.depthClearValue(), depthStencilClearValue.stencilClearValue() }; + cvs.append(cv); + } + for (int i = 0; i < rtD->resolveAttCount; ++i) { + VkClearValue cv; + cv.color = { { float(colorClearValue.redF()), float(colorClearValue.greenF()), float(colorClearValue.blueF()), + float(colorClearValue.alphaF()) } }; + cvs.append(cv); + } + rpBeginInfo.clearValueCount = cvs.count(); + + QVkCommandBuffer::Command cmd; + cmd.cmd = QVkCommandBuffer::Command::BeginRenderPass; + cmd.args.beginRenderPass.desc = rpBeginInfo; + cmd.args.beginRenderPass.clearValueIndex = cbD->pools.clearValue.count(); + cbD->pools.clearValue.append(cvs.constData(), cvs.count()); + cbD->commands.append(cmd); + + inPass = true; +} + +void QRhiVulkan::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) +{ + Q_ASSERT(inPass); + QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); + + QVkCommandBuffer::Command cmd; + cmd.cmd = QVkCommandBuffer::Command::EndRenderPass; + cbD->commands.append(cmd); + + inPass = false; + cbD->currentTarget = nullptr; + + if (resourceUpdates) + enqueueResourceUpdates(cbD, resourceUpdates); +} + +VkShaderModule QRhiVulkan::createShader(const QByteArray &spirv) +{ + VkShaderModuleCreateInfo shaderInfo; + memset(&shaderInfo, 0, sizeof(shaderInfo)); + shaderInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + shaderInfo.codeSize = spirv.size(); + shaderInfo.pCode = reinterpret_cast(spirv.constData()); + VkShaderModule shaderModule; + VkResult err = df->vkCreateShaderModule(dev, &shaderInfo, nullptr, &shaderModule); + if (err != VK_SUCCESS) { + qWarning("Failed to create shader module: %d", err); + return VK_NULL_HANDLE; + } + return shaderModule; +} + +bool QRhiVulkan::ensurePipelineCache() +{ + if (pipelineCache) + return true; + + VkPipelineCacheCreateInfo pipelineCacheInfo; + memset(&pipelineCacheInfo, 0, sizeof(pipelineCacheInfo)); + pipelineCacheInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; + VkResult err = df->vkCreatePipelineCache(dev, &pipelineCacheInfo, nullptr, &pipelineCache); + if (err != VK_SUCCESS) { + qWarning("Failed to create pipeline cache: %d", err); + return false; + } + return true; +} + +void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, int descSetIdx) +{ + QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, srb); + + QVarLengthArray bufferInfos; + QVarLengthArray imageInfos; + QVarLengthArray writeInfos; + + const bool updateAll = descSetIdx < 0; + int frameSlot = updateAll ? 0 : descSetIdx; + while (frameSlot < (updateAll ? QVK_FRAMES_IN_FLIGHT : descSetIdx + 1)) { + srbD->boundResourceData[frameSlot].resize(srbD->sortedBindings.count()); + for (int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) { + const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&srbD->sortedBindings[i]); + QVkShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[frameSlot][i]); + + VkWriteDescriptorSet writeInfo; + memset(&writeInfo, 0, sizeof(writeInfo)); + writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writeInfo.dstSet = srbD->descSets[frameSlot]; + writeInfo.dstBinding = b->binding; + writeInfo.descriptorCount = 1; + + switch (b->type) { + case QRhiShaderResourceBinding::UniformBuffer: + { + writeInfo.descriptorType = b->u.ubuf.hasDynamicOffset ? VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC + : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + QRhiBuffer *buf = b->u.ubuf.buf; + QVkBuffer *bufD = QRHI_RES(QVkBuffer, buf); + bd.ubuf.id = bufD->m_id; + bd.ubuf.generation = bufD->generation; + VkDescriptorBufferInfo bufInfo; + bufInfo.buffer = bufD->m_type == QRhiBuffer::Dynamic ? bufD->buffers[frameSlot] : bufD->buffers[0]; + bufInfo.offset = b->u.ubuf.offset; + bufInfo.range = b->u.ubuf.maybeSize ? b->u.ubuf.maybeSize : bufD->m_size; + // be nice and assert when we know the vulkan device would die a horrible death due to non-aligned reads + Q_ASSERT(aligned(bufInfo.offset, ubufAlign) == bufInfo.offset); + bufferInfos.append(bufInfo); + writeInfo.pBufferInfo = &bufferInfos.last(); + } + break; + case QRhiShaderResourceBinding::SampledTexture: + { + QVkTexture *texD = QRHI_RES(QVkTexture, b->u.stex.tex); + QVkSampler *samplerD = QRHI_RES(QVkSampler, b->u.stex.sampler); + writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + bd.stex.texId = texD->m_id; + bd.stex.texGeneration = texD->generation; + bd.stex.samplerId = samplerD->m_id; + bd.stex.samplerGeneration = samplerD->generation; + VkDescriptorImageInfo imageInfo; + imageInfo.sampler = samplerD->sampler; + imageInfo.imageView = texD->imageView; + imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + imageInfos.append(imageInfo); + writeInfo.pImageInfo = &imageInfos.last(); + } + break; + default: + continue; + } + + writeInfos.append(writeInfo); + } + ++frameSlot; + } + + df->vkUpdateDescriptorSets(dev, writeInfos.count(), writeInfos.constData(), 0, nullptr); +} + +static inline bool accessIsWrite(VkAccessFlags access) +{ + return (access & VK_ACCESS_SHADER_WRITE_BIT) != 0 + || (access & VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT) != 0 + || (access & VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT) != 0 + || (access & VK_ACCESS_TRANSFER_WRITE_BIT) != 0 + || (access & VK_ACCESS_HOST_WRITE_BIT) != 0 + || (access & VK_ACCESS_MEMORY_WRITE_BIT) != 0; +} + +void QRhiVulkan::trackedBufferBarrier(QVkCommandBuffer *cbD, QVkBuffer *bufD, int slot, + VkAccessFlags access, VkPipelineStageFlags stage) +{ + Q_ASSERT(!inPass); + Q_ASSERT(access && stage); + QVkBuffer::UsageState &s(bufD->usageState[slot]); + if (!s.stage) { + s.access = access; + s.stage = stage; + return; + } + + if (s.access == access && s.stage == stage) { + // No need to flood with unnecessary read-after-read barriers. + // Write-after-write is a different matter, however. + if (!accessIsWrite(access)) + return; + } + + VkBufferMemoryBarrier bufMemBarrier; + memset(&bufMemBarrier, 0, sizeof(bufMemBarrier)); + bufMemBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; + bufMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + bufMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + bufMemBarrier.srcAccessMask = s.access; + bufMemBarrier.dstAccessMask = access; + bufMemBarrier.buffer = bufD->buffers[slot]; + bufMemBarrier.size = VK_WHOLE_SIZE; + + QVkCommandBuffer::Command cmd; + cmd.cmd = QVkCommandBuffer::Command::BufferBarrier; + cmd.args.bufferBarrier.srcStageMask = s.stage; + cmd.args.bufferBarrier.dstStageMask = stage; + cmd.args.bufferBarrier.desc = bufMemBarrier; + cbD->commands.append(cmd); + + s.access = access; + s.stage = stage; +} + +void QRhiVulkan::trackedImageBarrier(QVkCommandBuffer *cbD, QVkTexture *texD, + VkImageLayout layout, VkAccessFlags access, VkPipelineStageFlags stage) +{ + Q_ASSERT(!inPass); + Q_ASSERT(layout && access && stage); + QVkTexture::UsageState &s(texD->usageState); + if (s.access == access && s.stage == stage && s.layout == layout) { + if (!accessIsWrite(access)) + return; + } + + VkImageMemoryBarrier barrier; + memset(&barrier, 0, sizeof(barrier)); + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.subresourceRange.aspectMask = !isDepthTextureFormat(texD->m_format) + ? VK_IMAGE_ASPECT_COLOR_BIT : VK_IMAGE_ASPECT_DEPTH_BIT; + barrier.subresourceRange.baseMipLevel = 0; + barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + barrier.oldLayout = s.layout; // new textures have this set to PREINITIALIZED + barrier.newLayout = layout; + barrier.srcAccessMask = s.access; // may be 0 but that's fine + barrier.dstAccessMask = access; + barrier.image = texD->image; + + VkPipelineStageFlags srcStage = s.stage; + // stage mask cannot be 0 + if (!srcStage) + srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + + QVkCommandBuffer::Command cmd; + cmd.cmd = QVkCommandBuffer::Command::ImageBarrier; + cmd.args.imageBarrier.srcStageMask = srcStage; + cmd.args.imageBarrier.dstStageMask = stage; + cmd.args.imageBarrier.desc = barrier; + cbD->commands.append(cmd); + + s.layout = layout; + s.access = access; + s.stage = stage; +} + +void QRhiVulkan::subresourceBarrier(QVkCommandBuffer *cbD, VkImage image, + VkImageLayout oldLayout, VkImageLayout newLayout, + VkAccessFlags srcAccess, VkAccessFlags dstAccess, + VkPipelineStageFlags srcStage, VkPipelineStageFlags dstStage, + int startLayer, int layerCount, + int startLevel, int levelCount) +{ + Q_ASSERT(!inPass); + VkImageMemoryBarrier barrier; + memset(&barrier, 0, sizeof(barrier)); + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.baseMipLevel = startLevel; + barrier.subresourceRange.levelCount = levelCount; + barrier.subresourceRange.baseArrayLayer = startLayer; + barrier.subresourceRange.layerCount = layerCount; + barrier.oldLayout = oldLayout; + barrier.newLayout = newLayout; + barrier.srcAccessMask = srcAccess; + barrier.dstAccessMask = dstAccess; + barrier.image = image; + + QVkCommandBuffer::Command cmd; + cmd.cmd = QVkCommandBuffer::Command::ImageBarrier; + cmd.args.imageBarrier.srcStageMask = srcStage; + cmd.args.imageBarrier.dstStageMask = dstStage; + cmd.args.imageBarrier.desc = barrier; + cbD->commands.append(cmd); +} + +VkDeviceSize QRhiVulkan::subresUploadByteSize(const QRhiTextureSubresourceUploadDescription &subresDesc) const +{ + VkDeviceSize size = 0; + const qsizetype imageSizeBytes = subresDesc.image().isNull() ? + subresDesc.data().size() : subresDesc.image().sizeInBytes(); + if (imageSizeBytes > 0) + size += aligned(imageSizeBytes, texbufAlign); + return size; +} + +void QRhiVulkan::prepareUploadSubres(QVkTexture *texD, int layer, int level, + const QRhiTextureSubresourceUploadDescription &subresDesc, + size_t *curOfs, void *mp, + BufferImageCopyList *copyInfos) +{ + qsizetype copySizeBytes = 0; + qsizetype imageSizeBytes = 0; + const void *src = nullptr; + + VkBufferImageCopy copyInfo; + memset(©Info, 0, sizeof(copyInfo)); + copyInfo.bufferOffset = *curOfs; + copyInfo.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copyInfo.imageSubresource.mipLevel = level; + copyInfo.imageSubresource.baseArrayLayer = layer; + copyInfo.imageSubresource.layerCount = 1; + copyInfo.imageExtent.depth = 1; + + const QByteArray rawData = subresDesc.data(); + const QPoint dp = subresDesc.destinationTopLeft(); + QImage image = subresDesc.image(); + if (!image.isNull()) { + copySizeBytes = imageSizeBytes = image.sizeInBytes(); + QSize size = image.size(); + src = image.constBits(); + // Scanlines in QImage are 4 byte aligned so bpl must + // be taken into account for bufferRowLength. + int bpc = qMax(1, image.depth() / 8); + // this is in pixels, not bytes, to make it more complicated... + copyInfo.bufferRowLength = image.bytesPerLine() / bpc; + if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) { + const int sx = subresDesc.sourceTopLeft().x(); + const int sy = subresDesc.sourceTopLeft().y(); + if (!subresDesc.sourceSize().isEmpty()) + size = subresDesc.sourceSize(); + if (image.depth() == 32) { + // The staging buffer will get the full image + // regardless, just adjust the vk + // buffer-to-image copy start offset. + copyInfo.bufferOffset += sy * image.bytesPerLine() + sx * 4; + // bufferRowLength remains set to the original image's width + } else { + image = image.copy(sx, sy, size.width(), size.height()); + src = image.constBits(); + // The staging buffer gets the slice only. The rest of the + // space reserved for this mip will be unused. + copySizeBytes = image.sizeInBytes(); + bpc = qMax(1, image.depth() / 8); + copyInfo.bufferRowLength = image.bytesPerLine() / bpc; + } + } + copyInfo.imageOffset.x = dp.x(); + copyInfo.imageOffset.y = dp.y(); + copyInfo.imageExtent.width = size.width(); + copyInfo.imageExtent.height = size.height(); + copyInfos->append(copyInfo); + } else if (!rawData.isEmpty() && isCompressedFormat(texD->m_format)) { + copySizeBytes = imageSizeBytes = rawData.size(); + src = rawData.constData(); + QSize size = q->sizeForMipLevel(level, texD->m_pixelSize); + const int subresw = size.width(); + const int subresh = size.height(); + if (!subresDesc.sourceSize().isEmpty()) + size = subresDesc.sourceSize(); + const int w = size.width(); + const int h = size.height(); + QSize blockDim; + compressedFormatInfo(texD->m_format, QSize(w, h), nullptr, nullptr, &blockDim); + // x and y must be multiples of the block width and height + copyInfo.imageOffset.x = aligned(dp.x(), blockDim.width()); + copyInfo.imageOffset.y = aligned(dp.y(), blockDim.height()); + // width and height must be multiples of the block width and height + // or x + width and y + height must equal the subresource width and height + copyInfo.imageExtent.width = dp.x() + w == subresw ? w : aligned(w, blockDim.width()); + copyInfo.imageExtent.height = dp.y() + h == subresh ? h : aligned(h, blockDim.height()); + copyInfos->append(copyInfo); + } else if (!rawData.isEmpty()) { + copySizeBytes = imageSizeBytes = rawData.size(); + src = rawData.constData(); + QSize size = q->sizeForMipLevel(level, texD->m_pixelSize); + if (!subresDesc.sourceSize().isEmpty()) + size = subresDesc.sourceSize(); + copyInfo.imageOffset.x = dp.x(); + copyInfo.imageOffset.y = dp.y(); + copyInfo.imageExtent.width = size.width(); + copyInfo.imageExtent.height = size.height(); + copyInfos->append(copyInfo); + } else { + qWarning("Invalid texture upload for %p layer=%d mip=%d", texD, layer, level); + } + + memcpy(reinterpret_cast(mp) + *curOfs, src, copySizeBytes); + *curOfs += aligned(imageSizeBytes, texbufAlign); +} + +void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdateBatch *resourceUpdates) +{ + QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates); + QRhiProfilerPrivate *rhiP = profilerPrivateOrNull(); + + for (const QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate &u : ud->dynamicBufferUpdates) { + QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf); + Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic); + for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) + bufD->pendingDynamicUpdates[i].append(u); + } + + for (const QRhiResourceUpdateBatchPrivate::StaticBufferUpload &u : ud->staticBufferUploads) { + QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf); + Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic); + Q_ASSERT(u.offset + u.data.size() <= bufD->m_size); + + if (!bufD->stagingBuffers[currentFrameSlot]) { + VkBufferCreateInfo bufferInfo; + memset(&bufferInfo, 0, sizeof(bufferInfo)); + bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + // must cover the entire buffer - this way multiple, partial updates per frame + // are supported even when the staging buffer is reused (Static) + bufferInfo.size = bufD->m_size; + bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + + VmaAllocationCreateInfo allocInfo; + memset(&allocInfo, 0, sizeof(allocInfo)); + allocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY; + + VmaAllocation allocation; + VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo, + &bufD->stagingBuffers[currentFrameSlot], &allocation, nullptr); + if (err == VK_SUCCESS) { + bufD->stagingAllocations[currentFrameSlot] = allocation; + QRHI_PROF_F(newBufferStagingArea(bufD, currentFrameSlot, bufD->m_size)); + } else { + qWarning("Failed to create staging buffer of size %d: %d", bufD->m_size, err); + continue; + } + } + + void *p = nullptr; + VmaAllocation a = toVmaAllocation(bufD->stagingAllocations[currentFrameSlot]); + VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p); + if (err != VK_SUCCESS) { + qWarning("Failed to map buffer: %d", err); + continue; + } + memcpy(static_cast(p) + u.offset, u.data.constData(), u.data.size()); + vmaUnmapMemory(toVmaAllocator(allocator), a); + vmaFlushAllocation(toVmaAllocator(allocator), a, u.offset, u.data.size()); + + trackedBufferBarrier(cbD, bufD, 0, + VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); + + VkBufferCopy copyInfo; + memset(©Info, 0, sizeof(copyInfo)); + copyInfo.srcOffset = u.offset; + copyInfo.dstOffset = u.offset; + copyInfo.size = u.data.size(); + + QVkCommandBuffer::Command cmd; + cmd.cmd = QVkCommandBuffer::Command::CopyBuffer; + cmd.args.copyBuffer.src = bufD->stagingBuffers[currentFrameSlot]; + cmd.args.copyBuffer.dst = bufD->buffers[0]; + cmd.args.copyBuffer.desc = copyInfo; + cbD->commands.append(cmd); + + // Where's the barrier for read-after-write? (assuming the common case + // of binding this buffer as vertex/index, or, less likely, as uniform + // buffer, in a renderpass later on) That is handled by the pass + // resource tracking: the appropriate pipeline barrier will be + // generated and recorded right before the renderpass, that binds this + // buffer in one of its commands, gets its BeginRenderPass recorded. + + bufD->lastActiveFrameSlot = currentFrameSlot; + + if (bufD->m_type == QRhiBuffer::Immutable) { + QRhiVulkan::DeferredReleaseEntry e; + e.type = QRhiVulkan::DeferredReleaseEntry::StagingBuffer; + e.lastActiveFrameSlot = currentFrameSlot; + e.stagingBuffer.stagingBuffer = bufD->stagingBuffers[currentFrameSlot]; + e.stagingBuffer.stagingAllocation = bufD->stagingAllocations[currentFrameSlot]; + bufD->stagingBuffers[currentFrameSlot] = VK_NULL_HANDLE; + bufD->stagingAllocations[currentFrameSlot] = nullptr; + releaseQueue.append(e); + QRHI_PROF_F(releaseBufferStagingArea(bufD, currentFrameSlot)); + } + } + + for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) { + if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) { + QVkTexture *utexD = QRHI_RES(QVkTexture, u.upload.tex); + // batch into a single staging buffer and a single CopyBufferToImage with multiple copyInfos + VkDeviceSize stagingSize = 0; + for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) { + for (int level = 0; level < QRhi::MAX_LEVELS; ++level) { + for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.upload.subresDesc[layer][level])) + stagingSize += subresUploadByteSize(subresDesc); + } + } + + Q_ASSERT(!utexD->stagingBuffers[currentFrameSlot]); + VkBufferCreateInfo bufferInfo; + memset(&bufferInfo, 0, sizeof(bufferInfo)); + bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferInfo.size = stagingSize; + bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + + VmaAllocationCreateInfo allocInfo; + memset(&allocInfo, 0, sizeof(allocInfo)); + allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU; + + VmaAllocation allocation; + VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo, + &utexD->stagingBuffers[currentFrameSlot], &allocation, nullptr); + if (err != VK_SUCCESS) { + qWarning("Failed to create image staging buffer of size %d: %d", int(stagingSize), err); + continue; + } + utexD->stagingAllocations[currentFrameSlot] = allocation; + QRHI_PROF_F(newTextureStagingArea(utexD, currentFrameSlot, stagingSize)); + + BufferImageCopyList copyInfos; + size_t curOfs = 0; + void *mp = nullptr; + VmaAllocation a = toVmaAllocation(utexD->stagingAllocations[currentFrameSlot]); + err = vmaMapMemory(toVmaAllocator(allocator), a, &mp); + if (err != VK_SUCCESS) { + qWarning("Failed to map image data: %d", err); + continue; + } + + for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) { + for (int level = 0; level < QRhi::MAX_LEVELS; ++level) { + const QVector &srd(u.upload.subresDesc[layer][level]); + if (srd.isEmpty()) + continue; + for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(srd)) { + prepareUploadSubres(utexD, layer, level, + subresDesc, &curOfs, mp, ©Infos); + } + } + } + vmaUnmapMemory(toVmaAllocator(allocator), a); + vmaFlushAllocation(toVmaAllocator(allocator), a, 0, stagingSize); + + trackedImageBarrier(cbD, utexD, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); + + QVkCommandBuffer::Command cmd; + cmd.cmd = QVkCommandBuffer::Command::CopyBufferToImage; + cmd.args.copyBufferToImage.src = utexD->stagingBuffers[currentFrameSlot]; + cmd.args.copyBufferToImage.dst = utexD->image; + cmd.args.copyBufferToImage.dstLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + cmd.args.copyBufferToImage.count = copyInfos.count(); + cmd.args.copyBufferToImage.bufferImageCopyIndex = cbD->pools.bufferImageCopy.count(); + cbD->pools.bufferImageCopy.append(copyInfos.constData(), copyInfos.count()); + cbD->commands.append(cmd); + + // no reuse of staging, this is intentional + QRhiVulkan::DeferredReleaseEntry e; + e.type = QRhiVulkan::DeferredReleaseEntry::StagingBuffer; + e.lastActiveFrameSlot = currentFrameSlot; + e.stagingBuffer.stagingBuffer = utexD->stagingBuffers[currentFrameSlot]; + e.stagingBuffer.stagingAllocation = utexD->stagingAllocations[currentFrameSlot]; + utexD->stagingBuffers[currentFrameSlot] = VK_NULL_HANDLE; + utexD->stagingAllocations[currentFrameSlot] = nullptr; + releaseQueue.append(e); + QRHI_PROF_F(releaseTextureStagingArea(utexD, currentFrameSlot)); + + // Similarly to buffers, transitioning away from DST is done later, + // when a renderpass using the texture is encountered. + + utexD->lastActiveFrameSlot = currentFrameSlot; + } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) { + Q_ASSERT(u.copy.src && u.copy.dst); + if (u.copy.src == u.copy.dst) { + qWarning("Texture copy with matching source and destination is not supported"); + continue; + } + QVkTexture *srcD = QRHI_RES(QVkTexture, u.copy.src); + QVkTexture *dstD = QRHI_RES(QVkTexture, u.copy.dst); + + VkImageCopy region; + memset(®ion, 0, sizeof(region)); + + region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.srcSubresource.mipLevel = u.copy.desc.sourceLevel(); + region.srcSubresource.baseArrayLayer = u.copy.desc.sourceLayer(); + region.srcSubresource.layerCount = 1; + + region.srcOffset.x = u.copy.desc.sourceTopLeft().x(); + region.srcOffset.y = u.copy.desc.sourceTopLeft().y(); + + region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.dstSubresource.mipLevel = u.copy.desc.destinationLevel(); + region.dstSubresource.baseArrayLayer = u.copy.desc.destinationLayer(); + region.dstSubresource.layerCount = 1; + + region.dstOffset.x = u.copy.desc.destinationTopLeft().x(); + region.dstOffset.y = u.copy.desc.destinationTopLeft().y(); + + const QSize size = u.copy.desc.pixelSize().isEmpty() ? srcD->m_pixelSize : u.copy.desc.pixelSize(); + region.extent.width = size.width(); + region.extent.height = size.height(); + region.extent.depth = 1; + + trackedImageBarrier(cbD, srcD, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); + trackedImageBarrier(cbD, dstD, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); + + QVkCommandBuffer::Command cmd; + cmd.cmd = QVkCommandBuffer::Command::CopyImage; + cmd.args.copyImage.src = srcD->image; + cmd.args.copyImage.srcLayout = srcD->usageState.layout; + cmd.args.copyImage.dst = dstD->image; + cmd.args.copyImage.dstLayout = dstD->usageState.layout; + cmd.args.copyImage.desc = region; + cbD->commands.append(cmd); + + srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot; + } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) { + ActiveReadback aRb; + aRb.activeFrameSlot = currentFrameSlot; + aRb.desc = u.read.rb; + aRb.result = u.read.result; + + QVkTexture *texD = QRHI_RES(QVkTexture, u.read.rb.texture()); + QVkSwapChain *swapChainD = nullptr; + if (texD) { + if (texD->samples > VK_SAMPLE_COUNT_1_BIT) { + qWarning("Multisample texture cannot be read back"); + continue; + } + aRb.pixelSize = u.read.rb.level() > 0 ? q->sizeForMipLevel(u.read.rb.level(), texD->m_pixelSize) + : texD->m_pixelSize; + aRb.format = texD->m_format; + texD->lastActiveFrameSlot = currentFrameSlot; + } else { + Q_ASSERT(currentSwapChain); + swapChainD = QRHI_RES(QVkSwapChain, currentSwapChain); + if (!swapChainD->supportsReadback) { + qWarning("Swapchain does not support readback"); + continue; + } + aRb.pixelSize = swapChainD->pixelSize; + aRb.format = colorTextureFormatFromVkFormat(swapChainD->colorFormat, nullptr); + if (aRb.format == QRhiTexture::UnknownFormat) + continue; + + // Multisample swapchains need nothing special since resolving + // happens when ending a renderpass. + } + textureFormatInfo(aRb.format, aRb.pixelSize, nullptr, &aRb.bufSize); + + // Create a host visible buffer. + VkBufferCreateInfo bufferInfo; + memset(&bufferInfo, 0, sizeof(bufferInfo)); + bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferInfo.size = aRb.bufSize; + bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT; + + VmaAllocationCreateInfo allocInfo; + memset(&allocInfo, 0, sizeof(allocInfo)); + allocInfo.usage = VMA_MEMORY_USAGE_GPU_TO_CPU; + + VmaAllocation allocation; + VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo, &aRb.buf, &allocation, nullptr); + if (err == VK_SUCCESS) { + aRb.bufAlloc = allocation; + QRHI_PROF_F(newReadbackBuffer(quint64(aRb.buf), + texD ? static_cast(texD) : static_cast(swapChainD), + aRb.bufSize)); + } else { + qWarning("Failed to create readback buffer of size %u: %d", aRb.bufSize, err); + continue; + } + + // Copy from the (optimal and not host visible) image into the buffer. + VkBufferImageCopy copyDesc; + memset(©Desc, 0, sizeof(copyDesc)); + copyDesc.bufferOffset = 0; + copyDesc.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copyDesc.imageSubresource.mipLevel = u.read.rb.level(); + copyDesc.imageSubresource.baseArrayLayer = u.read.rb.layer(); + copyDesc.imageSubresource.layerCount = 1; + copyDesc.imageExtent.width = aRb.pixelSize.width(); + copyDesc.imageExtent.height = aRb.pixelSize.height(); + copyDesc.imageExtent.depth = 1; + + if (texD) { + trackedImageBarrier(cbD, texD, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); + QVkCommandBuffer::Command cmd; + cmd.cmd = QVkCommandBuffer::Command::CopyImageToBuffer; + cmd.args.copyImageToBuffer.src = texD->image; + cmd.args.copyImageToBuffer.srcLayout = texD->usageState.layout; + cmd.args.copyImageToBuffer.dst = aRb.buf; + cmd.args.copyImageToBuffer.desc = copyDesc; + cbD->commands.append(cmd); + } else { + // use the swapchain image + VkImage image = swapChainD->imageRes[swapChainD->currentImageIndex].image; + if (!swapChainD->imageRes[swapChainD->currentImageIndex].transferSource) { + subresourceBarrier(cbD, image, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_ACCESS_MEMORY_READ_BIT, VK_ACCESS_TRANSFER_READ_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, 1, + 0, 1); + swapChainD->imageRes[swapChainD->currentImageIndex].transferSource = true; + } + + QVkCommandBuffer::Command cmd; + cmd.cmd = QVkCommandBuffer::Command::CopyImageToBuffer; + cmd.args.copyImageToBuffer.src = image; + cmd.args.copyImageToBuffer.srcLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + cmd.args.copyImageToBuffer.dst = aRb.buf; + cmd.args.copyImageToBuffer.desc = copyDesc; + cbD->commands.append(cmd); + } + + activeReadbacks.append(aRb); + } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::MipGen) { + QVkTexture *utexD = QRHI_RES(QVkTexture, u.mipgen.tex); + Q_ASSERT(utexD->m_flags.testFlag(QRhiTexture::UsedWithGenerateMips)); + int w = utexD->m_pixelSize.width(); + int h = utexD->m_pixelSize.height(); + + VkImageLayout origLayout = utexD->usageState.layout; + VkAccessFlags origAccess = utexD->usageState.access; + VkPipelineStageFlags origStage = utexD->usageState.stage; + if (!origStage) + origStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + + for (uint level = 1; level < utexD->mipLevelCount; ++level) { + if (level == 1) { + subresourceBarrier(cbD, utexD->image, + origLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + origAccess, VK_ACCESS_TRANSFER_READ_BIT, + origStage, VK_PIPELINE_STAGE_TRANSFER_BIT, + u.mipgen.layer, 1, + level - 1, 1); + } else { + subresourceBarrier(cbD, utexD->image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + u.mipgen.layer, 1, + level - 1, 1); + } + + subresourceBarrier(cbD, utexD->image, + origLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + origAccess, VK_ACCESS_TRANSFER_WRITE_BIT, + origStage, VK_PIPELINE_STAGE_TRANSFER_BIT, + u.mipgen.layer, 1, + level, 1); + + VkImageBlit region; + memset(®ion, 0, sizeof(region)); + + region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.srcSubresource.mipLevel = level - 1; + region.srcSubresource.baseArrayLayer = u.mipgen.layer; + region.srcSubresource.layerCount = 1; + + region.srcOffsets[1].x = qMax(1, w); + region.srcOffsets[1].y = qMax(1, h); + region.srcOffsets[1].z = 1; + + region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.dstSubresource.mipLevel = level; + region.dstSubresource.baseArrayLayer = u.mipgen.layer; + region.dstSubresource.layerCount = 1; + + region.dstOffsets[1].x = qMax(1, w >> 1); + region.dstOffsets[1].y = qMax(1, h >> 1); + region.dstOffsets[1].z = 1; + + QVkCommandBuffer::Command cmd; + cmd.cmd = QVkCommandBuffer::Command::BlitImage; + cmd.args.blitImage.src = utexD->image; + cmd.args.blitImage.srcLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + cmd.args.blitImage.dst = utexD->image; + cmd.args.blitImage.dstLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + cmd.args.blitImage.filter = VK_FILTER_LINEAR; + cmd.args.blitImage.desc = region; + cbD->commands.append(cmd); + + w >>= 1; + h >>= 1; + } + + if (utexD->mipLevelCount > 1) { + subresourceBarrier(cbD, utexD->image, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, origLayout, + VK_ACCESS_TRANSFER_READ_BIT, origAccess, + VK_PIPELINE_STAGE_TRANSFER_BIT, origStage, + u.mipgen.layer, 1, + 0, utexD->mipLevelCount - 1); + subresourceBarrier(cbD, utexD->image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, origLayout, + VK_ACCESS_TRANSFER_WRITE_BIT, origAccess, + VK_PIPELINE_STAGE_TRANSFER_BIT, origStage, + u.mipgen.layer, 1, + utexD->mipLevelCount - 1, 1); + } + + utexD->lastActiveFrameSlot = currentFrameSlot; + } + } + + ud->free(); +} + +void QRhiVulkan::executeBufferHostWritesForCurrentFrame(QVkBuffer *bufD) +{ + QVector &updates(bufD->pendingDynamicUpdates[currentFrameSlot]); + if (updates.isEmpty()) + return; + + Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic); + void *p = nullptr; + VmaAllocation a = toVmaAllocation(bufD->allocations[currentFrameSlot]); + // The vmaMap/Unmap are basically a no-op when persistently mapped since it + // refcounts; this is great because we don't need to care if the allocation + // was created as persistently mapped or not. + VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p); + if (err != VK_SUCCESS) { + qWarning("Failed to map buffer: %d", err); + return; + } + int changeBegin = -1; + int changeEnd = -1; + for (const QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate &u : updates) { + Q_ASSERT(bufD == QRHI_RES(QVkBuffer, u.buf)); + memcpy(static_cast(p) + u.offset, u.data.constData(), u.data.size()); + if (changeBegin == -1 || u.offset < changeBegin) + changeBegin = u.offset; + if (changeEnd == -1 || u.offset + u.data.size() > changeEnd) + changeEnd = u.offset + u.data.size(); + } + vmaUnmapMemory(toVmaAllocator(allocator), a); + if (changeBegin >= 0) + vmaFlushAllocation(toVmaAllocator(allocator), a, changeBegin, changeEnd - changeBegin); + + updates.clear(); +} + +static void qrhivk_releaseBuffer(const QRhiVulkan::DeferredReleaseEntry &e, void *allocator) +{ + for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) { + vmaDestroyBuffer(toVmaAllocator(allocator), e.buffer.buffers[i], toVmaAllocation(e.buffer.allocations[i])); + vmaDestroyBuffer(toVmaAllocator(allocator), e.buffer.stagingBuffers[i], toVmaAllocation(e.buffer.stagingAllocations[i])); + } +} + +static void qrhivk_releaseRenderBuffer(const QRhiVulkan::DeferredReleaseEntry &e, VkDevice dev, QVulkanDeviceFunctions *df) +{ + df->vkDestroyImageView(dev, e.renderBuffer.imageView, nullptr); + df->vkDestroyImage(dev, e.renderBuffer.image, nullptr); + df->vkFreeMemory(dev, e.renderBuffer.memory, nullptr); +} + +static void qrhivk_releaseTexture(const QRhiVulkan::DeferredReleaseEntry &e, VkDevice dev, QVulkanDeviceFunctions *df, void *allocator) +{ + df->vkDestroyImageView(dev, e.texture.imageView, nullptr); + vmaDestroyImage(toVmaAllocator(allocator), e.texture.image, toVmaAllocation(e.texture.allocation)); + for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) + vmaDestroyBuffer(toVmaAllocator(allocator), e.texture.stagingBuffers[i], toVmaAllocation(e.texture.stagingAllocations[i])); +} + +static void qrhivk_releaseSampler(const QRhiVulkan::DeferredReleaseEntry &e, VkDevice dev, QVulkanDeviceFunctions *df) +{ + df->vkDestroySampler(dev, e.sampler.sampler, nullptr); +} + +void QRhiVulkan::executeDeferredReleases(bool forced) +{ + for (int i = releaseQueue.count() - 1; i >= 0; --i) { + const QRhiVulkan::DeferredReleaseEntry &e(releaseQueue[i]); + if (forced || currentFrameSlot == e.lastActiveFrameSlot || e.lastActiveFrameSlot < 0) { + switch (e.type) { + case QRhiVulkan::DeferredReleaseEntry::Pipeline: + df->vkDestroyPipeline(dev, e.pipelineState.pipeline, nullptr); + df->vkDestroyPipelineLayout(dev, e.pipelineState.layout, nullptr); + break; + case QRhiVulkan::DeferredReleaseEntry::ShaderResourceBindings: + df->vkDestroyDescriptorSetLayout(dev, e.shaderResourceBindings.layout, nullptr); + if (e.shaderResourceBindings.poolIndex >= 0) { + descriptorPools[e.shaderResourceBindings.poolIndex].refCount -= 1; + Q_ASSERT(descriptorPools[e.shaderResourceBindings.poolIndex].refCount >= 0); + } + break; + case QRhiVulkan::DeferredReleaseEntry::Buffer: + qrhivk_releaseBuffer(e, allocator); + break; + case QRhiVulkan::DeferredReleaseEntry::RenderBuffer: + qrhivk_releaseRenderBuffer(e, dev, df); + break; + case QRhiVulkan::DeferredReleaseEntry::Texture: + qrhivk_releaseTexture(e, dev, df, allocator); + break; + case QRhiVulkan::DeferredReleaseEntry::Sampler: + qrhivk_releaseSampler(e, dev, df); + break; + case QRhiVulkan::DeferredReleaseEntry::TextureRenderTarget: + df->vkDestroyFramebuffer(dev, e.textureRenderTarget.fb, nullptr); + for (int att = 0; att < QVkRenderTargetData::MAX_COLOR_ATTACHMENTS; ++att) { + df->vkDestroyImageView(dev, e.textureRenderTarget.rtv[att], nullptr); + df->vkDestroyImageView(dev, e.textureRenderTarget.resrtv[att], nullptr); + } + break; + case QRhiVulkan::DeferredReleaseEntry::RenderPass: + df->vkDestroyRenderPass(dev, e.renderPass.rp, nullptr); + break; + case QRhiVulkan::DeferredReleaseEntry::StagingBuffer: + vmaDestroyBuffer(toVmaAllocator(allocator), e.stagingBuffer.stagingBuffer, toVmaAllocation(e.stagingBuffer.stagingAllocation)); + break; + default: + Q_UNREACHABLE(); + break; + } + releaseQueue.removeAt(i); + } + } +} + +void QRhiVulkan::finishActiveReadbacks(bool forced) +{ + QVarLengthArray, 4> completedCallbacks; + QRhiProfilerPrivate *rhiP = profilerPrivateOrNull(); + + for (int i = activeReadbacks.count() - 1; i >= 0; --i) { + const QRhiVulkan::ActiveReadback &aRb(activeReadbacks[i]); + if (forced || currentFrameSlot == aRb.activeFrameSlot || aRb.activeFrameSlot < 0) { + aRb.result->format = aRb.format; + aRb.result->pixelSize = aRb.pixelSize; + aRb.result->data.resize(aRb.bufSize); + void *p = nullptr; + VmaAllocation a = toVmaAllocation(aRb.bufAlloc); + VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p); + if (err != VK_SUCCESS) { + qWarning("Failed to map readback buffer: %d", err); + continue; + } + memcpy(aRb.result->data.data(), p, aRb.bufSize); + vmaUnmapMemory(toVmaAllocator(allocator), a); + + vmaDestroyBuffer(toVmaAllocator(allocator), aRb.buf, a); + QRHI_PROF_F(releaseReadbackBuffer(quint64(aRb.buf))); + + if (aRb.result->completed) + completedCallbacks.append(aRb.result->completed); + + activeReadbacks.removeAt(i); + } + } + + for (auto f : completedCallbacks) + f(); +} + +static struct { + VkSampleCountFlagBits mask; + int count; +} qvk_sampleCounts[] = { + // keep this sorted by 'count' + { VK_SAMPLE_COUNT_1_BIT, 1 }, + { VK_SAMPLE_COUNT_2_BIT, 2 }, + { VK_SAMPLE_COUNT_4_BIT, 4 }, + { VK_SAMPLE_COUNT_8_BIT, 8 }, + { VK_SAMPLE_COUNT_16_BIT, 16 }, + { VK_SAMPLE_COUNT_32_BIT, 32 }, + { VK_SAMPLE_COUNT_64_BIT, 64 } +}; + +QVector QRhiVulkan::supportedSampleCounts() const +{ + const VkPhysicalDeviceLimits *limits = &physDevProperties.limits; + VkSampleCountFlags color = limits->framebufferColorSampleCounts; + VkSampleCountFlags depth = limits->framebufferDepthSampleCounts; + VkSampleCountFlags stencil = limits->framebufferStencilSampleCounts; + QVector result; + + for (size_t i = 0; i < sizeof(qvk_sampleCounts) / sizeof(qvk_sampleCounts[0]); ++i) { + if ((color & qvk_sampleCounts[i].mask) + && (depth & qvk_sampleCounts[i].mask) + && (stencil & qvk_sampleCounts[i].mask)) + { + result.append(qvk_sampleCounts[i].count); + } + } + + return result; +} + +VkSampleCountFlagBits QRhiVulkan::effectiveSampleCount(int sampleCount) +{ + // Stay compatible with QSurfaceFormat and friends where samples == 0 means the same as 1. + sampleCount = qBound(1, sampleCount, 64); + + if (!supportedSampleCounts().contains(sampleCount)) { + qWarning("Attempted to set unsupported sample count %d", sampleCount); + return VK_SAMPLE_COUNT_1_BIT; + } + + for (size_t i = 0; i < sizeof(qvk_sampleCounts) / sizeof(qvk_sampleCounts[0]); ++i) { + if (qvk_sampleCounts[i].count == sampleCount) + return qvk_sampleCounts[i].mask; + } + + Q_UNREACHABLE(); + return VK_SAMPLE_COUNT_1_BIT; +} + +void QRhiVulkan::enqueueTransitionPassResources(QVkCommandBuffer *cbD) +{ + cbD->passResTrackers.append(QRhiPassResourceTracker()); + QVkCommandBuffer::Command cmd; + cmd.cmd = QVkCommandBuffer::Command::TransitionPassResources; + cmd.args.transitionResources.trackerIndex = cbD->passResTrackers.count() - 1; + cbD->commands.append(cmd); + cbD->currentPassResTrackerIndex = cbD->passResTrackers.count() - 1; +} + +void QRhiVulkan::recordCommandBuffer(QVkCommandBuffer *cbD) +{ + for (QVkCommandBuffer::Command &cmd : cbD->commands) { + switch (cmd.cmd) { + case QVkCommandBuffer::Command::CopyBuffer: + df->vkCmdCopyBuffer(cbD->cb, cmd.args.copyBuffer.src, cmd.args.copyBuffer.dst, + 1, &cmd.args.copyBuffer.desc); + break; + case QVkCommandBuffer::Command::CopyBufferToImage: + df->vkCmdCopyBufferToImage(cbD->cb, cmd.args.copyBufferToImage.src, cmd.args.copyBufferToImage.dst, + cmd.args.copyBufferToImage.dstLayout, + cmd.args.copyBufferToImage.count, + cbD->pools.bufferImageCopy.constData() + cmd.args.copyBufferToImage.bufferImageCopyIndex); + break; + case QVkCommandBuffer::Command::CopyImage: + df->vkCmdCopyImage(cbD->cb, cmd.args.copyImage.src, cmd.args.copyImage.srcLayout, + cmd.args.copyImage.dst, cmd.args.copyImage.dstLayout, + 1, &cmd.args.copyImage.desc); + break; + case QVkCommandBuffer::Command::CopyImageToBuffer: + df->vkCmdCopyImageToBuffer(cbD->cb, cmd.args.copyImageToBuffer.src, cmd.args.copyImageToBuffer.srcLayout, + cmd.args.copyImageToBuffer.dst, + 1, &cmd.args.copyImageToBuffer.desc); + break; + case QVkCommandBuffer::Command::ImageBarrier: + df->vkCmdPipelineBarrier(cbD->cb, cmd.args.imageBarrier.srcStageMask, cmd.args.imageBarrier.dstStageMask, + 0, 0, nullptr, 0, nullptr, + 1, &cmd.args.imageBarrier.desc); + break; + case QVkCommandBuffer::Command::BufferBarrier: + df->vkCmdPipelineBarrier(cbD->cb, cmd.args.bufferBarrier.srcStageMask, cmd.args.bufferBarrier.dstStageMask, + 0, 0, nullptr, + 1, &cmd.args.bufferBarrier.desc, + 0, nullptr); + break; + case QVkCommandBuffer::Command::BlitImage: + df->vkCmdBlitImage(cbD->cb, cmd.args.blitImage.src, cmd.args.blitImage.srcLayout, + cmd.args.blitImage.dst, cmd.args.blitImage.dstLayout, + 1, &cmd.args.blitImage.desc, + cmd.args.blitImage.filter); + break; + case QVkCommandBuffer::Command::BeginRenderPass: + cmd.args.beginRenderPass.desc.pClearValues = cbD->pools.clearValue.constData() + cmd.args.beginRenderPass.clearValueIndex; + df->vkCmdBeginRenderPass(cbD->cb, &cmd.args.beginRenderPass.desc, VK_SUBPASS_CONTENTS_INLINE); + break; + case QVkCommandBuffer::Command::EndRenderPass: + df->vkCmdEndRenderPass(cbD->cb); + break; + case QVkCommandBuffer::Command::BindPipeline: + df->vkCmdBindPipeline(cbD->cb, cmd.args.bindPipeline.bindPoint, cmd.args.bindPipeline.pipeline); + break; + case QVkCommandBuffer::Command::BindDescriptorSet: + { + const uint32_t *offsets = nullptr; + if (cmd.args.bindDescriptorSet.dynamicOffsetCount > 0) + offsets = cbD->pools.dynamicOffset.constData() + cmd.args.bindDescriptorSet.dynamicOffsetIndex; + df->vkCmdBindDescriptorSets(cbD->cb, cmd.args.bindDescriptorSet.bindPoint, + cmd.args.bindDescriptorSet.pipelineLayout, + 0, 1, &cmd.args.bindDescriptorSet.descSet, + cmd.args.bindDescriptorSet.dynamicOffsetCount, + offsets); + } + break; + case QVkCommandBuffer::Command::BindVertexBuffer: + df->vkCmdBindVertexBuffers(cbD->cb, cmd.args.bindVertexBuffer.startBinding, + cmd.args.bindVertexBuffer.count, + cbD->pools.vertexBuffer.constData() + cmd.args.bindVertexBuffer.vertexBufferIndex, + cbD->pools.vertexBufferOffset.constData() + cmd.args.bindVertexBuffer.vertexBufferOffsetIndex); + break; + case QVkCommandBuffer::Command::BindIndexBuffer: + df->vkCmdBindIndexBuffer(cbD->cb, cmd.args.bindIndexBuffer.buf, + cmd.args.bindIndexBuffer.ofs, cmd.args.bindIndexBuffer.type); + break; + case QVkCommandBuffer::Command::SetViewport: + df->vkCmdSetViewport(cbD->cb, 0, 1, &cmd.args.setViewport.viewport); + break; + case QVkCommandBuffer::Command::SetScissor: + df->vkCmdSetScissor(cbD->cb, 0, 1, &cmd.args.setScissor.scissor); + break; + case QVkCommandBuffer::Command::SetBlendConstants: + df->vkCmdSetBlendConstants(cbD->cb, cmd.args.setBlendConstants.c); + break; + case QVkCommandBuffer::Command::SetStencilRef: + df->vkCmdSetStencilReference(cbD->cb, VK_STENCIL_FRONT_AND_BACK, cmd.args.setStencilRef.ref); + break; + case QVkCommandBuffer::Command::Draw: + df->vkCmdDraw(cbD->cb, cmd.args.draw.vertexCount, cmd.args.draw.instanceCount, + cmd.args.draw.firstVertex, cmd.args.draw.firstInstance); + break; + case QVkCommandBuffer::Command::DrawIndexed: + df->vkCmdDrawIndexed(cbD->cb, cmd.args.drawIndexed.indexCount, cmd.args.drawIndexed.instanceCount, + cmd.args.drawIndexed.firstIndex, cmd.args.drawIndexed.vertexOffset, + cmd.args.drawIndexed.firstInstance); + break; + case QVkCommandBuffer::Command::DebugMarkerBegin: + cmd.args.debugMarkerBegin.marker.pMarkerName = + cbD->pools.debugMarkerName[cmd.args.debugMarkerBegin.markerNameIndex].constData(); + vkCmdDebugMarkerBegin(cbD->cb, &cmd.args.debugMarkerBegin.marker); + break; + case QVkCommandBuffer::Command::DebugMarkerEnd: + vkCmdDebugMarkerEnd(cbD->cb); + break; + case QVkCommandBuffer::Command::DebugMarkerInsert: + vkCmdDebugMarkerInsert(cbD->cb, &cmd.args.debugMarkerInsert.marker); + break; + case QVkCommandBuffer::Command::TransitionPassResources: + recordTransitionPassResources(cbD, cbD->passResTrackers[cmd.args.transitionResources.trackerIndex]); + break; + default: + break; + } + } + + cbD->resetCommands(); +} + +static inline VkAccessFlags toVkAccess(QRhiPassResourceTracker::BufferAccess access) +{ + switch (access) { + case QRhiPassResourceTracker::BufVertexInput: + return VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT; + case QRhiPassResourceTracker::BufIndexRead: + return VK_ACCESS_INDEX_READ_BIT; + case QRhiPassResourceTracker::BufUniformRead: + return VK_ACCESS_UNIFORM_READ_BIT; + default: + Q_UNREACHABLE(); + break; + } + return 0; +} + +static inline VkPipelineStageFlags toVkPipelineStage(QRhiPassResourceTracker::BufferStage stage) +{ + switch (stage) { + case QRhiPassResourceTracker::BufVertexInputStage: + return VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; + case QRhiPassResourceTracker::BufVertexStage: + return VK_PIPELINE_STAGE_VERTEX_SHADER_BIT; + case QRhiPassResourceTracker::BufFragmentStage: + return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + default: + Q_UNREACHABLE(); + break; + } + return 0; +} + +static inline QVkBuffer::UsageState toVkBufferUsageState(QRhiPassResourceTracker::UsageState usage) +{ + QVkBuffer::UsageState u; + u.access = usage.access; + u.stage = usage.stage; + return u; +} + +static inline VkImageLayout toVkLayout(QRhiPassResourceTracker::TextureAccess access) +{ + switch (access) { + case QRhiPassResourceTracker::TexSample: + return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + case QRhiPassResourceTracker::TexColorOutput: + return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + case QRhiPassResourceTracker::TexDepthOutput: + return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + default: + Q_UNREACHABLE(); + break; + } + return VK_IMAGE_LAYOUT_GENERAL; +} + +static inline VkAccessFlags toVkAccess(QRhiPassResourceTracker::TextureAccess access) +{ + switch (access) { + case QRhiPassResourceTracker::TexSample: + return VK_ACCESS_SHADER_READ_BIT; + case QRhiPassResourceTracker::TexColorOutput: + return VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + case QRhiPassResourceTracker::TexDepthOutput: + return VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + default: + Q_UNREACHABLE(); + break; + } + return 0; +} + +static inline VkPipelineStageFlags toVkPipelineStage(QRhiPassResourceTracker::TextureStage stage) +{ + switch (stage) { + case QRhiPassResourceTracker::TexVertexStage: + return VK_PIPELINE_STAGE_VERTEX_SHADER_BIT; + case QRhiPassResourceTracker::TexFragmentStage: + return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + case QRhiPassResourceTracker::TexColorOutputStage: + return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + case QRhiPassResourceTracker::TexDepthOutputStage: + return VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + default: + Q_UNREACHABLE(); + break; + } + return 0; +} + +static inline QVkTexture::UsageState toVkTextureUsageState(QRhiPassResourceTracker::UsageState usage) +{ + QVkTexture::UsageState u; + u.layout = VkImageLayout(usage.layout); + u.access = usage.access; + u.stage = usage.stage; + return u; +} + +void QRhiVulkan::trackedRegisterBuffer(QRhiPassResourceTracker *passResTracker, + QVkBuffer *bufD, + int slot, + QRhiPassResourceTracker::BufferAccess access, + QRhiPassResourceTracker::BufferStage stage) +{ + QVkBuffer::UsageState &u(bufD->usageState[slot]); + // The last arg will get ignored if this buffer was already used in the + // same pass; that's good because u is not the state at pass start anymore + // at that point. + passResTracker->registerBufferOnce(bufD, slot, access, stage, toPassTrackerUsageState(u)); + u.access = toVkAccess(access); + u.stage = toVkPipelineStage(stage); +} + +void QRhiVulkan::trackedRegisterTexture(QRhiPassResourceTracker *passResTracker, + QVkTexture *texD, + QRhiPassResourceTracker::TextureAccess access, + QRhiPassResourceTracker::TextureStage stage) +{ + QVkTexture::UsageState &u(texD->usageState); + // The last arg will get ignored if this buffer was already used in the + // same pass; that's good because u is not the state at pass start anymore + // at that point. + passResTracker->registerTextureOnce(texD, access, stage, toPassTrackerUsageState(u)); + u.layout = toVkLayout(access); + u.access = toVkAccess(access); + u.stage = toVkPipelineStage(stage); +} + +void QRhiVulkan::recordTransitionPassResources(QVkCommandBuffer *cbD, const QRhiPassResourceTracker &tracker) +{ + if (tracker.isEmpty()) + return; + + const QVector *buffers = tracker.buffers(); + for (const QRhiPassResourceTracker::Buffer &b : *buffers) { + QVkBuffer *bufD = QRHI_RES(QVkBuffer, b.buf); + VkAccessFlags access = toVkAccess(b.access); + VkPipelineStageFlags stage = toVkPipelineStage(b.stage); + QVkBuffer::UsageState s = toVkBufferUsageState(b.stateAtPassBegin); + if (!s.stage) + continue; + if (s.access == access && s.stage == stage) { + if (!accessIsWrite(access)) + continue; + } + VkBufferMemoryBarrier bufMemBarrier; + memset(&bufMemBarrier, 0, sizeof(bufMemBarrier)); + bufMemBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; + bufMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + bufMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + bufMemBarrier.srcAccessMask = s.access; + bufMemBarrier.dstAccessMask = access; + bufMemBarrier.buffer = bufD->buffers[b.slot]; + bufMemBarrier.size = VK_WHOLE_SIZE; + df->vkCmdPipelineBarrier(cbD->cb, s.stage, stage, 0, + 0, nullptr, + 1, &bufMemBarrier, + 0, nullptr); + } + + const QVector *textures = tracker.textures(); + for (const QRhiPassResourceTracker::Texture &t : *textures) { + QVkTexture *texD = QRHI_RES(QVkTexture, t.tex); + VkImageLayout layout = toVkLayout(t.access); + VkAccessFlags access = toVkAccess(t.access); + VkPipelineStageFlags stage = toVkPipelineStage(t.stage); + QVkTexture::UsageState s = toVkTextureUsageState(t.stateAtPassBegin); + if (s.access == access && s.stage == stage && s.layout == layout) { + if (!accessIsWrite(access)) + continue; + } + VkImageMemoryBarrier barrier; + memset(&barrier, 0, sizeof(barrier)); + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.subresourceRange.aspectMask = !isDepthTextureFormat(texD->m_format) + ? VK_IMAGE_ASPECT_COLOR_BIT : VK_IMAGE_ASPECT_DEPTH_BIT; + barrier.subresourceRange.baseMipLevel = 0; + barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + barrier.oldLayout = s.layout; // new textures have this set to PREINITIALIZED + barrier.newLayout = layout; + barrier.srcAccessMask = s.access; // may be 0 but that's fine + barrier.dstAccessMask = access; + barrier.image = texD->image; + VkPipelineStageFlags srcStage = s.stage; + // stage mask cannot be 0 + if (!srcStage) + srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + df->vkCmdPipelineBarrier(cbD->cb, srcStage, stage, 0, + 0, nullptr, + 0, nullptr, + 1, &barrier); + } +} + +QRhiSwapChain *QRhiVulkan::createSwapChain() +{ + return new QVkSwapChain(this); +} + +QRhiBuffer *QRhiVulkan::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, int size) +{ + return new QVkBuffer(this, type, usage, size); +} + +int QRhiVulkan::ubufAlignment() const +{ + return ubufAlign; // typically 256 (bytes) +} + +bool QRhiVulkan::isYUpInFramebuffer() const +{ + return false; +} + +bool QRhiVulkan::isYUpInNDC() const +{ + return false; +} + +bool QRhiVulkan::isClipDepthZeroToOne() const +{ + return true; +} + +QMatrix4x4 QRhiVulkan::clipSpaceCorrMatrix() const +{ + // See https://matthewwellings.com/blog/the-new-vulkan-coordinate-system/ + + static QMatrix4x4 m; + if (m.isIdentity()) { + // NB the ctor takes row-major + m = QMatrix4x4(1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, -1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.5f, 0.5f, + 0.0f, 0.0f, 0.0f, 1.0f); + } + return m; +} + +bool QRhiVulkan::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const +{ + VkPhysicalDeviceFeatures features; + f->vkGetPhysicalDeviceFeatures(physDev, &features); + + // Note that with some SDKs the validation layer gives an odd warning about + // BC not being supported, even when our check here succeeds. Not much we + // can do about that. + if (format >= QRhiTexture::BC1 && format <= QRhiTexture::BC7) { + if (!features.textureCompressionBC) + return false; + } + + if (format >= QRhiTexture::ETC2_RGB8 && format <= QRhiTexture::ETC2_RGBA8) { + if (!features.textureCompressionETC2) + return false; + } + + if (format >= QRhiTexture::ASTC_4x4 && format <= QRhiTexture::ASTC_12x12) { + if (!features.textureCompressionASTC_LDR) + return false; + } + + VkFormat vkformat = toVkTextureFormat(format, flags); + VkFormatProperties props; + f->vkGetPhysicalDeviceFormatProperties(physDev, vkformat, &props); + return (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) != 0; +} + +bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const +{ + switch (feature) { + case QRhi::MultisampleTexture: + return true; + case QRhi::MultisampleRenderBuffer: + return true; + case QRhi::DebugMarkers: + return debugMarkersAvailable; + case QRhi::Timestamps: + return timestampValidBits != 0; + case QRhi::Instancing: + return true; + case QRhi::CustomInstanceStepRate: + return vertexAttribDivisorAvailable; + case QRhi::PrimitiveRestart: + return true; + case QRhi::NonDynamicUniformBuffers: + return true; + case QRhi::NonFourAlignedEffectiveIndexBufferOffset: + return true; + case QRhi::NPOTTextureRepeat: + return true; + case QRhi::RedOrAlpha8IsRed: + return true; + case QRhi::ElementIndexUint: + return true; + default: + Q_UNREACHABLE(); + return false; + } +} + +int QRhiVulkan::resourceLimit(QRhi::ResourceLimit limit) const +{ + switch (limit) { + case QRhi::TextureSizeMin: + return 1; + case QRhi::TextureSizeMax: + return physDevProperties.limits.maxImageDimension2D; + case QRhi::MaxColorAttachments: + return physDevProperties.limits.maxColorAttachments; + case QRhi::FramesInFlight: + return QVK_FRAMES_IN_FLIGHT; + default: + Q_UNREACHABLE(); + return 0; + } +} + +const QRhiNativeHandles *QRhiVulkan::nativeHandles() +{ + return &nativeHandlesStruct; +} + +void QRhiVulkan::sendVMemStatsToProfiler() +{ + QRhiProfilerPrivate *rhiP = profilerPrivateOrNull(); + if (!rhiP) + return; + + VmaStats stats; + vmaCalculateStats(toVmaAllocator(allocator), &stats); + QRHI_PROF_F(vmemStat(stats.total.blockCount, stats.total.allocationCount, + stats.total.usedBytes, stats.total.unusedBytes)); +} + +QRhiRenderBuffer *QRhiVulkan::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize, + int sampleCount, QRhiRenderBuffer::Flags flags) +{ + return new QVkRenderBuffer(this, type, pixelSize, sampleCount, flags); +} + +QRhiTexture *QRhiVulkan::createTexture(QRhiTexture::Format format, const QSize &pixelSize, + int sampleCount, QRhiTexture::Flags flags) +{ + return new QVkTexture(this, format, pixelSize, sampleCount, flags); +} + +QRhiSampler *QRhiVulkan::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, + QRhiSampler::Filter mipmapMode, + QRhiSampler::AddressMode u, QRhiSampler::AddressMode v) +{ + return new QVkSampler(this, magFilter, minFilter, mipmapMode, u, v); +} + +QRhiTextureRenderTarget *QRhiVulkan::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc, + QRhiTextureRenderTarget::Flags flags) +{ + return new QVkTextureRenderTarget(this, desc, flags); +} + +QRhiGraphicsPipeline *QRhiVulkan::createGraphicsPipeline() +{ + return new QVkGraphicsPipeline(this); +} + +QRhiShaderResourceBindings *QRhiVulkan::createShaderResourceBindings() +{ + return new QVkShaderResourceBindings(this); +} + +void QRhiVulkan::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps) +{ + Q_ASSERT(inPass); + QVkGraphicsPipeline *psD = QRHI_RES(QVkGraphicsPipeline, ps); + Q_ASSERT(psD->pipeline); + QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); + + if (cbD->currentPipeline != ps || cbD->currentPipelineGeneration != psD->generation) { + QVkCommandBuffer::Command cmd; + cmd.cmd = QVkCommandBuffer::Command::BindPipeline; + cmd.args.bindPipeline.bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + cmd.args.bindPipeline.pipeline = psD->pipeline; + cbD->commands.append(cmd); + + cbD->currentPipeline = ps; + cbD->currentPipelineGeneration = psD->generation; + } + + 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; + + 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; + + Q_UNREACHABLE(); + return QRhiPassResourceTracker::TexVertexStage; +} + +void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb, + int dynamicOffsetCount, + const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) +{ + Q_ASSERT(inPass); + + QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); + Q_ASSERT(cbD->currentPipeline); + QVkGraphicsPipeline *psD = QRHI_RES(QVkGraphicsPipeline, cbD->currentPipeline); + + if (!srb) + srb = psD->m_shaderResourceBindings; + + QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, srb); + bool hasSlottedResourceInSrb = false; + bool hasDynamicOffsetInSrb = false; + + for (const QRhiShaderResourceBinding &binding : qAsConst(srbD->sortedBindings)) { + const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&binding); + switch (b->type) { + case QRhiShaderResourceBinding::UniformBuffer: + if (QRHI_RES(QVkBuffer, b->u.ubuf.buf)->m_type == QRhiBuffer::Dynamic) + hasSlottedResourceInSrb = true; + if (b->u.ubuf.hasDynamicOffset) + hasDynamicOffsetInSrb = true; + break; + default: + break; + } + } + + const int descSetIdx = hasSlottedResourceInSrb ? currentFrameSlot : 0; + bool rewriteDescSet = false; + + // Do host writes and mark referenced shader resources as in-use. + // Also prepare to ensure the descriptor set we are going to bind refers to up-to-date Vk objects. + for (int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) { + const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&srbD->sortedBindings[i]); + QVkShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[descSetIdx][i]); + QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]); + switch (b->type) { + case QRhiShaderResourceBinding::UniformBuffer: + { + QVkBuffer *bufD = QRHI_RES(QVkBuffer, b->u.ubuf.buf); + Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)); + + if (bufD->m_type == QRhiBuffer::Dynamic) + executeBufferHostWritesForCurrentFrame(bufD); + + bufD->lastActiveFrameSlot = currentFrameSlot; + trackedRegisterBuffer(&passResTracker, bufD, currentFrameSlot, + QRhiPassResourceTracker::BufUniformRead, + toPassTrackerBufferStage(b->stage)); + + // Check both the "local" id (the generation counter) and the + // global id. The latter is relevant when a newly allocated + // QRhiResource ends up with the same pointer as a previous one. + // (and that previous one could have been in an srb...) + if (bufD->generation != bd.ubuf.generation || bufD->m_id != bd.ubuf.id) { + rewriteDescSet = true; + bd.ubuf.id = bufD->m_id; + bd.ubuf.generation = bufD->generation; + } + } + break; + case QRhiShaderResourceBinding::SampledTexture: + { + QVkTexture *texD = QRHI_RES(QVkTexture, b->u.stex.tex); + QVkSampler *samplerD = QRHI_RES(QVkSampler, b->u.stex.sampler); + texD->lastActiveFrameSlot = currentFrameSlot; + samplerD->lastActiveFrameSlot = currentFrameSlot; + trackedRegisterTexture(&passResTracker, texD, + QRhiPassResourceTracker::TexSample, + toPassTrackerTextureStage(b->stage)); + + if (texD->generation != bd.stex.texGeneration + || texD->m_id != bd.stex.texId + || samplerD->generation != bd.stex.samplerGeneration + || samplerD->m_id != bd.stex.samplerId) + { + rewriteDescSet = true; + bd.stex.texId = texD->m_id; + bd.stex.texGeneration = texD->generation; + bd.stex.samplerId = samplerD->m_id; + bd.stex.samplerGeneration = samplerD->generation; + } + } + break; + default: + Q_UNREACHABLE(); + break; + } + } + + // write descriptor sets, if needed + if (rewriteDescSet) + updateShaderResourceBindings(srb, descSetIdx); + + // make sure the descriptors for the correct slot will get bound. + // also, dynamic offsets always need a bind. + const bool forceRebind = (hasSlottedResourceInSrb && cbD->currentDescSetSlot != descSetIdx) || hasDynamicOffsetInSrb; + + if (forceRebind || rewriteDescSet || cbD->currentSrb != srb || cbD->currentSrbGeneration != srbD->generation) { + QVarLengthArray dynOfs; + if (hasDynamicOffsetInSrb) { + // Filling out dynOfs based on the sorted bindings is important + // because dynOfs has to be ordered based on the binding numbers, + // and neither srb nor dynamicOffsets has any such ordering + // requirement. + for (const QRhiShaderResourceBinding &binding : qAsConst(srbD->sortedBindings)) { + const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&binding); + if (b->type == QRhiShaderResourceBinding::UniformBuffer && b->u.ubuf.hasDynamicOffset) { + uint32_t offset = 0; + for (int i = 0; i < dynamicOffsetCount; ++i) { + const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]); + if (dynOfs.first == b->binding) { + offset = dynOfs.second; + break; + } + } + dynOfs.append(offset); // use 0 if dynamicOffsets did not contain this binding + } + } + } + + QVkCommandBuffer::Command cmd; + cmd.cmd = QVkCommandBuffer::Command::BindDescriptorSet; + cmd.args.bindDescriptorSet.bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + cmd.args.bindDescriptorSet.pipelineLayout = psD->layout; + cmd.args.bindDescriptorSet.descSet = srbD->descSets[descSetIdx]; + cmd.args.bindDescriptorSet.dynamicOffsetCount = dynOfs.count(); + cmd.args.bindDescriptorSet.dynamicOffsetIndex = cbD->pools.dynamicOffset.count(); + cbD->pools.dynamicOffset.append(dynOfs.constData(), dynOfs.count()); + cbD->commands.append(cmd); + + cbD->currentSrb = srb; + cbD->currentSrbGeneration = srbD->generation; + cbD->currentDescSetSlot = descSetIdx; + } + + srbD->lastActiveFrameSlot = currentFrameSlot; +} + +void QRhiVulkan::setVertexInput(QRhiCommandBuffer *cb, + int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings, + QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat) +{ + Q_ASSERT(inPass); + QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); + QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]); + + bool needsBindVBuf = false; + for (int i = 0; i < bindingCount; ++i) { + const int inputSlot = startBinding + i; + QVkBuffer *bufD = QRHI_RES(QVkBuffer, bindings[i].first); + Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::VertexBuffer)); + bufD->lastActiveFrameSlot = currentFrameSlot; + if (bufD->m_type == QRhiBuffer::Dynamic) + executeBufferHostWritesForCurrentFrame(bufD); + + const VkBuffer vkvertexbuf = bufD->buffers[bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0]; + if (cbD->currentVertexBuffers[inputSlot] != vkvertexbuf + || cbD->currentVertexOffsets[inputSlot] != bindings[i].second) + { + needsBindVBuf = true; + cbD->currentVertexBuffers[inputSlot] = vkvertexbuf; + cbD->currentVertexOffsets[inputSlot] = bindings[i].second; + } + } + + if (needsBindVBuf) { + QVarLengthArray bufs; + QVarLengthArray ofs; + for (int i = 0; i < bindingCount; ++i) { + QVkBuffer *bufD = QRHI_RES(QVkBuffer, bindings[i].first); + const int slot = bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0; + bufs.append(bufD->buffers[slot]); + ofs.append(bindings[i].second); + trackedRegisterBuffer(&passResTracker, bufD, slot, + QRhiPassResourceTracker::BufVertexInput, + QRhiPassResourceTracker::BufVertexInputStage); + } + + QVkCommandBuffer::Command cmd; + cmd.cmd = QVkCommandBuffer::Command::BindVertexBuffer; + cmd.args.bindVertexBuffer.startBinding = startBinding; + cmd.args.bindVertexBuffer.count = bufs.count(); + cmd.args.bindVertexBuffer.vertexBufferIndex = cbD->pools.vertexBuffer.count(); + cbD->pools.vertexBuffer.append(bufs.constData(), bufs.count()); + cmd.args.bindVertexBuffer.vertexBufferOffsetIndex = cbD->pools.vertexBufferOffset.count(); + cbD->pools.vertexBufferOffset.append(ofs.constData(), ofs.count()); + cbD->commands.append(cmd); + } + + if (indexBuf) { + QVkBuffer *ibufD = QRHI_RES(QVkBuffer, indexBuf); + Q_ASSERT(ibufD->m_usage.testFlag(QRhiBuffer::IndexBuffer)); + ibufD->lastActiveFrameSlot = currentFrameSlot; + if (ibufD->m_type == QRhiBuffer::Dynamic) + executeBufferHostWritesForCurrentFrame(ibufD); + + const int slot = ibufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0; + const VkBuffer vkindexbuf = ibufD->buffers[slot]; + const VkIndexType type = indexFormat == QRhiCommandBuffer::IndexUInt16 ? VK_INDEX_TYPE_UINT16 + : VK_INDEX_TYPE_UINT32; + + if (cbD->currentIndexBuffer != vkindexbuf + || cbD->currentIndexOffset != indexOffset + || cbD->currentIndexFormat != type) + { + cbD->currentIndexBuffer = vkindexbuf; + cbD->currentIndexOffset = indexOffset; + cbD->currentIndexFormat = type; + + QVkCommandBuffer::Command cmd; + cmd.cmd = QVkCommandBuffer::Command::BindIndexBuffer; + cmd.args.bindIndexBuffer.buf = vkindexbuf; + cmd.args.bindIndexBuffer.ofs = indexOffset; + cmd.args.bindIndexBuffer.type = type; + cbD->commands.append(cmd); + + trackedRegisterBuffer(&passResTracker, ibufD, slot, + QRhiPassResourceTracker::BufIndexRead, + QRhiPassResourceTracker::BufVertexInputStage); + } + } +} + +void QRhiVulkan::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) +{ + Q_ASSERT(inPass); + QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); + Q_ASSERT(cbD->currentPipeline && cbD->currentTarget); + const QSize outputSize = cbD->currentTarget->pixelSize(); + + // x,y is top-left in VkViewport but bottom-left in QRhiViewport + float x, y, w, h; + if (!qrhi_toTopLeftRenderTargetRect(outputSize, viewport.viewport(), &x, &y, &w, &h)) + return; + + QVkCommandBuffer::Command cmd; + cmd.cmd = QVkCommandBuffer::Command::SetViewport; + VkViewport *vp = &cmd.args.setViewport.viewport; + vp->x = x; + vp->y = y; + vp->width = w; + vp->height = h; + vp->minDepth = viewport.minDepth(); + vp->maxDepth = viewport.maxDepth(); + cbD->commands.append(cmd); + + if (!QRHI_RES(QVkGraphicsPipeline, cbD->currentPipeline)->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)) { + cmd.cmd = QVkCommandBuffer::Command::SetScissor; + VkRect2D *s = &cmd.args.setScissor.scissor; + s->offset.x = x; + s->offset.y = y; + s->extent.width = w; + s->extent.height = h; + cbD->commands.append(cmd); + } +} + +void QRhiVulkan::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) +{ + Q_ASSERT(inPass); + QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); + Q_ASSERT(cbD->currentPipeline && cbD->currentTarget); + Q_ASSERT(QRHI_RES(QVkGraphicsPipeline, cbD->currentPipeline)->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)); + const QSize outputSize = cbD->currentTarget->pixelSize(); + + // x,y is top-left in VkRect2D but bottom-left in QRhiScissor + int x, y, w, h; + if (!qrhi_toTopLeftRenderTargetRect(outputSize, scissor.scissor(), &x, &y, &w, &h)) + return; + + QVkCommandBuffer::Command cmd; + cmd.cmd = QVkCommandBuffer::Command::SetScissor; + VkRect2D *s = &cmd.args.setScissor.scissor; + s->offset.x = x; + s->offset.y = y; + s->extent.width = w; + s->extent.height = h; + cbD->commands.append(cmd); +} + +void QRhiVulkan::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) +{ + Q_ASSERT(inPass); + QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); + QVkCommandBuffer::Command cmd; + cmd.cmd = QVkCommandBuffer::Command::SetBlendConstants; + cmd.args.setBlendConstants.c[0] = c.redF(); + cmd.args.setBlendConstants.c[1] = c.greenF(); + cmd.args.setBlendConstants.c[2] = c.blueF(); + cmd.args.setBlendConstants.c[3] = c.alphaF(); + cbD->commands.append(cmd); +} + +void QRhiVulkan::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) +{ + Q_ASSERT(inPass); + QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); + QVkCommandBuffer::Command cmd; + cmd.cmd = QVkCommandBuffer::Command::SetStencilRef; + cmd.args.setStencilRef.ref = refValue; + cbD->commands.append(cmd); +} + +void QRhiVulkan::draw(QRhiCommandBuffer *cb, quint32 vertexCount, + quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) +{ + Q_ASSERT(inPass); + QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); + QVkCommandBuffer::Command cmd; + cmd.cmd = QVkCommandBuffer::Command::Draw; + cmd.args.draw.vertexCount = vertexCount; + cmd.args.draw.instanceCount = instanceCount; + cmd.args.draw.firstVertex = firstVertex; + cmd.args.draw.firstInstance = firstInstance; + cbD->commands.append(cmd); +} + +void QRhiVulkan::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, + quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance) +{ + Q_ASSERT(inPass); + QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); + QVkCommandBuffer::Command cmd; + cmd.cmd = QVkCommandBuffer::Command::DrawIndexed; + cmd.args.drawIndexed.indexCount = indexCount; + cmd.args.drawIndexed.instanceCount = instanceCount; + cmd.args.drawIndexed.firstIndex = firstIndex; + cmd.args.drawIndexed.vertexOffset = vertexOffset; + cmd.args.drawIndexed.firstInstance = firstInstance; + cbD->commands.append(cmd); +} + +void QRhiVulkan::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) +{ + if (!debugMarkers || !debugMarkersAvailable) + return; + + VkDebugMarkerMarkerInfoEXT marker; + memset(&marker, 0, sizeof(marker)); + marker.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT; + + QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); + QVkCommandBuffer::Command cmd; + cmd.cmd = QVkCommandBuffer::Command::DebugMarkerBegin; + cmd.args.debugMarkerBegin.marker = marker; + cmd.args.debugMarkerBegin.markerNameIndex = cbD->pools.debugMarkerName.count(); + cbD->pools.debugMarkerName.append(name); + cbD->commands.append(cmd); +} + +void QRhiVulkan::debugMarkEnd(QRhiCommandBuffer *cb) +{ + if (!debugMarkers || !debugMarkersAvailable) + return; + + QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); + QVkCommandBuffer::Command cmd; + cmd.cmd = QVkCommandBuffer::Command::DebugMarkerEnd; + cbD->commands.append(cmd); +} + +void QRhiVulkan::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) +{ + if (!debugMarkers || !debugMarkersAvailable) + return; + + VkDebugMarkerMarkerInfoEXT marker; + memset(&marker, 0, sizeof(marker)); + marker.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT; + marker.pMarkerName = msg.constData(); + + QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); + QVkCommandBuffer::Command cmd; + cmd.cmd = QVkCommandBuffer::Command::DebugMarkerInsert; + cmd.args.debugMarkerInsert.marker = marker; + cbD->commands.append(cmd); +} + +const QRhiNativeHandles *QRhiVulkan::nativeHandles(QRhiCommandBuffer *cb) +{ + return QRHI_RES(QVkCommandBuffer, cb)->nativeHandles(); +} + +void QRhiVulkan::beginExternal(QRhiCommandBuffer *cb) +{ + Q_UNUSED(cb); +} + +void QRhiVulkan::endExternal(QRhiCommandBuffer *cb) +{ + QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); + cbD->resetCachedState(); +} + +void QRhiVulkan::setObjectName(uint64_t object, VkDebugReportObjectTypeEXT type, const QByteArray &name, int slot) +{ + if (!debugMarkers || !debugMarkersAvailable || name.isEmpty()) + return; + + VkDebugMarkerObjectNameInfoEXT nameInfo; + memset(&nameInfo, 0, sizeof(nameInfo)); + nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT; + nameInfo.objectType = type; + nameInfo.object = object; + QByteArray decoratedName = name; + if (slot >= 0) { + decoratedName += '/'; + decoratedName += QByteArray::number(slot); + } + nameInfo.pObjectName = decoratedName.constData(); + vkDebugMarkerSetObjectName(dev, &nameInfo); +} + +static inline VkBufferUsageFlagBits toVkBufferUsage(QRhiBuffer::UsageFlags usage) +{ + int u = 0; + if (usage.testFlag(QRhiBuffer::VertexBuffer)) + u |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; + if (usage.testFlag(QRhiBuffer::IndexBuffer)) + u |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT; + if (usage.testFlag(QRhiBuffer::UniformBuffer)) + u |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; + return VkBufferUsageFlagBits(u); +} + +static inline VkFilter toVkFilter(QRhiSampler::Filter f) +{ + switch (f) { + case QRhiSampler::Nearest: + return VK_FILTER_NEAREST; + case QRhiSampler::Linear: + return VK_FILTER_LINEAR; + default: + Q_UNREACHABLE(); + return VK_FILTER_NEAREST; + } +} + +static inline VkSamplerMipmapMode toVkMipmapMode(QRhiSampler::Filter f) +{ + switch (f) { + case QRhiSampler::None: + return VK_SAMPLER_MIPMAP_MODE_NEAREST; + case QRhiSampler::Nearest: + return VK_SAMPLER_MIPMAP_MODE_NEAREST; + case QRhiSampler::Linear: + return VK_SAMPLER_MIPMAP_MODE_LINEAR; + default: + Q_UNREACHABLE(); + return VK_SAMPLER_MIPMAP_MODE_NEAREST; + } +} + +static inline VkSamplerAddressMode toVkAddressMode(QRhiSampler::AddressMode m) +{ + switch (m) { + case QRhiSampler::Repeat: + return VK_SAMPLER_ADDRESS_MODE_REPEAT; + case QRhiSampler::ClampToEdge: + return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + case QRhiSampler::Border: + return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; + case QRhiSampler::Mirror: + return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT; + case QRhiSampler::MirrorOnce: + return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE; + default: + Q_UNREACHABLE(); + return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + } +} + +static inline VkShaderStageFlagBits toVkShaderStage(QRhiGraphicsShaderStage::Type type) +{ + switch (type) { + case QRhiGraphicsShaderStage::Vertex: + return VK_SHADER_STAGE_VERTEX_BIT; + case QRhiGraphicsShaderStage::Fragment: + return VK_SHADER_STAGE_FRAGMENT_BIT; + default: + Q_UNREACHABLE(); + return VK_SHADER_STAGE_VERTEX_BIT; + } +} + +static inline VkFormat toVkAttributeFormat(QRhiVertexInputAttribute::Format format) +{ + switch (format) { + case QRhiVertexInputAttribute::Float4: + return VK_FORMAT_R32G32B32A32_SFLOAT; + case QRhiVertexInputAttribute::Float3: + return VK_FORMAT_R32G32B32_SFLOAT; + case QRhiVertexInputAttribute::Float2: + return VK_FORMAT_R32G32_SFLOAT; + case QRhiVertexInputAttribute::Float: + return VK_FORMAT_R32_SFLOAT; + case QRhiVertexInputAttribute::UNormByte4: + return VK_FORMAT_R8G8B8A8_UNORM; + case QRhiVertexInputAttribute::UNormByte2: + return VK_FORMAT_R8G8_UNORM; + case QRhiVertexInputAttribute::UNormByte: + return VK_FORMAT_R8_UNORM; + default: + Q_UNREACHABLE(); + return VK_FORMAT_R32G32B32A32_SFLOAT; + } +} + +static inline VkPrimitiveTopology toVkTopology(QRhiGraphicsPipeline::Topology t) +{ + switch (t) { + case QRhiGraphicsPipeline::Triangles: + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + case QRhiGraphicsPipeline::TriangleStrip: + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; + case QRhiGraphicsPipeline::Lines: + return VK_PRIMITIVE_TOPOLOGY_LINE_LIST; + case QRhiGraphicsPipeline::LineStrip: + return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP; + case QRhiGraphicsPipeline::Points: + return VK_PRIMITIVE_TOPOLOGY_POINT_LIST; + default: + Q_UNREACHABLE(); + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + } +} + +static inline VkCullModeFlags toVkCullMode(QRhiGraphicsPipeline::CullMode c) +{ + switch (c) { + case QRhiGraphicsPipeline::None: + return VK_CULL_MODE_NONE; + case QRhiGraphicsPipeline::Front: + return VK_CULL_MODE_FRONT_BIT; + case QRhiGraphicsPipeline::Back: + return VK_CULL_MODE_BACK_BIT; + default: + Q_UNREACHABLE(); + return VK_CULL_MODE_NONE; + } +} + +static inline VkFrontFace toVkFrontFace(QRhiGraphicsPipeline::FrontFace f) +{ + switch (f) { + case QRhiGraphicsPipeline::CCW: + return VK_FRONT_FACE_COUNTER_CLOCKWISE; + case QRhiGraphicsPipeline::CW: + return VK_FRONT_FACE_CLOCKWISE; + default: + Q_UNREACHABLE(); + return VK_FRONT_FACE_COUNTER_CLOCKWISE; + } +} + +static inline VkColorComponentFlags toVkColorComponents(QRhiGraphicsPipeline::ColorMask c) +{ + int f = 0; + if (c.testFlag(QRhiGraphicsPipeline::R)) + f |= VK_COLOR_COMPONENT_R_BIT; + if (c.testFlag(QRhiGraphicsPipeline::G)) + f |= VK_COLOR_COMPONENT_G_BIT; + if (c.testFlag(QRhiGraphicsPipeline::B)) + f |= VK_COLOR_COMPONENT_B_BIT; + if (c.testFlag(QRhiGraphicsPipeline::A)) + f |= VK_COLOR_COMPONENT_A_BIT; + return VkColorComponentFlags(f); +} + +static inline VkBlendFactor toVkBlendFactor(QRhiGraphicsPipeline::BlendFactor f) +{ + switch (f) { + case QRhiGraphicsPipeline::Zero: + return VK_BLEND_FACTOR_ZERO; + case QRhiGraphicsPipeline::One: + return VK_BLEND_FACTOR_ONE; + case QRhiGraphicsPipeline::SrcColor: + return VK_BLEND_FACTOR_SRC_COLOR; + case QRhiGraphicsPipeline::OneMinusSrcColor: + return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR; + case QRhiGraphicsPipeline::DstColor: + return VK_BLEND_FACTOR_DST_COLOR; + case QRhiGraphicsPipeline::OneMinusDstColor: + return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR; + case QRhiGraphicsPipeline::SrcAlpha: + return VK_BLEND_FACTOR_SRC_ALPHA; + case QRhiGraphicsPipeline::OneMinusSrcAlpha: + return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + case QRhiGraphicsPipeline::DstAlpha: + return VK_BLEND_FACTOR_DST_ALPHA; + case QRhiGraphicsPipeline::OneMinusDstAlpha: + return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA; + case QRhiGraphicsPipeline::ConstantColor: + return VK_BLEND_FACTOR_CONSTANT_COLOR; + case QRhiGraphicsPipeline::OneMinusConstantColor: + return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR; + case QRhiGraphicsPipeline::ConstantAlpha: + return VK_BLEND_FACTOR_CONSTANT_ALPHA; + case QRhiGraphicsPipeline::OneMinusConstantAlpha: + return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA; + case QRhiGraphicsPipeline::SrcAlphaSaturate: + return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE; + case QRhiGraphicsPipeline::Src1Color: + return VK_BLEND_FACTOR_SRC1_COLOR; + case QRhiGraphicsPipeline::OneMinusSrc1Color: + return VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR; + case QRhiGraphicsPipeline::Src1Alpha: + return VK_BLEND_FACTOR_SRC1_ALPHA; + case QRhiGraphicsPipeline::OneMinusSrc1Alpha: + return VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA; + default: + Q_UNREACHABLE(); + return VK_BLEND_FACTOR_ZERO; + } +} + +static inline VkBlendOp toVkBlendOp(QRhiGraphicsPipeline::BlendOp op) +{ + switch (op) { + case QRhiGraphicsPipeline::Add: + return VK_BLEND_OP_ADD; + case QRhiGraphicsPipeline::Subtract: + return VK_BLEND_OP_SUBTRACT; + case QRhiGraphicsPipeline::ReverseSubtract: + return VK_BLEND_OP_REVERSE_SUBTRACT; + case QRhiGraphicsPipeline::Min: + return VK_BLEND_OP_MIN; + case QRhiGraphicsPipeline::Max: + return VK_BLEND_OP_MAX; + default: + Q_UNREACHABLE(); + return VK_BLEND_OP_ADD; + } +} + +static inline VkCompareOp toVkCompareOp(QRhiGraphicsPipeline::CompareOp op) +{ + switch (op) { + case QRhiGraphicsPipeline::Never: + return VK_COMPARE_OP_NEVER; + case QRhiGraphicsPipeline::Less: + return VK_COMPARE_OP_LESS; + case QRhiGraphicsPipeline::Equal: + return VK_COMPARE_OP_EQUAL; + case QRhiGraphicsPipeline::LessOrEqual: + return VK_COMPARE_OP_LESS_OR_EQUAL; + case QRhiGraphicsPipeline::Greater: + return VK_COMPARE_OP_GREATER; + case QRhiGraphicsPipeline::NotEqual: + return VK_COMPARE_OP_NOT_EQUAL; + case QRhiGraphicsPipeline::GreaterOrEqual: + return VK_COMPARE_OP_GREATER_OR_EQUAL; + case QRhiGraphicsPipeline::Always: + return VK_COMPARE_OP_ALWAYS; + default: + Q_UNREACHABLE(); + return VK_COMPARE_OP_ALWAYS; + } +} + +static inline VkStencilOp toVkStencilOp(QRhiGraphicsPipeline::StencilOp op) +{ + switch (op) { + case QRhiGraphicsPipeline::StencilZero: + return VK_STENCIL_OP_ZERO; + case QRhiGraphicsPipeline::Keep: + return VK_STENCIL_OP_KEEP; + case QRhiGraphicsPipeline::Replace: + return VK_STENCIL_OP_REPLACE; + case QRhiGraphicsPipeline::IncrementAndClamp: + return VK_STENCIL_OP_INCREMENT_AND_CLAMP; + case QRhiGraphicsPipeline::DecrementAndClamp: + return VK_STENCIL_OP_DECREMENT_AND_CLAMP; + case QRhiGraphicsPipeline::Invert: + return VK_STENCIL_OP_INVERT; + case QRhiGraphicsPipeline::IncrementAndWrap: + return VK_STENCIL_OP_INCREMENT_AND_WRAP; + case QRhiGraphicsPipeline::DecrementAndWrap: + return VK_STENCIL_OP_DECREMENT_AND_WRAP; + default: + Q_UNREACHABLE(); + return VK_STENCIL_OP_KEEP; + } +} + +static inline void fillVkStencilOpState(VkStencilOpState *dst, const QRhiGraphicsPipeline::StencilOpState &src) +{ + dst->failOp = toVkStencilOp(src.failOp); + dst->passOp = toVkStencilOp(src.passOp); + dst->depthFailOp = toVkStencilOp(src.depthFailOp); + dst->compareOp = toVkCompareOp(src.compareOp); +} + +static inline VkDescriptorType toVkDescriptorType(const QRhiShaderResourceBindingPrivate *b) +{ + switch (b->type) { + case QRhiShaderResourceBinding::UniformBuffer: + return b->u.ubuf.hasDynamicOffset ? VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC + : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + case QRhiShaderResourceBinding::SampledTexture: + return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + default: + Q_UNREACHABLE(); + return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + } +} + +static inline VkShaderStageFlags toVkShaderStageFlags(QRhiShaderResourceBinding::StageFlags stage) +{ + int s = 0; + if (stage.testFlag(QRhiShaderResourceBinding::VertexStage)) + s |= VK_SHADER_STAGE_VERTEX_BIT; + if (stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) + s |= VK_SHADER_STAGE_FRAGMENT_BIT; + return VkShaderStageFlags(s); +} + +static inline VkCompareOp toVkTextureCompareOp(QRhiSampler::CompareOp op) +{ + switch (op) { + case QRhiSampler::Never: + return VK_COMPARE_OP_NEVER; + case QRhiSampler::Less: + return VK_COMPARE_OP_LESS; + case QRhiSampler::Equal: + return VK_COMPARE_OP_EQUAL; + case QRhiSampler::LessOrEqual: + return VK_COMPARE_OP_LESS_OR_EQUAL; + case QRhiSampler::Greater: + return VK_COMPARE_OP_GREATER; + case QRhiSampler::NotEqual: + return VK_COMPARE_OP_NOT_EQUAL; + case QRhiSampler::GreaterOrEqual: + return VK_COMPARE_OP_GREATER_OR_EQUAL; + case QRhiSampler::Always: + return VK_COMPARE_OP_ALWAYS; + default: + Q_UNREACHABLE(); + return VK_COMPARE_OP_NEVER; + } +} + +QVkBuffer::QVkBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size) + : QRhiBuffer(rhi, type, usage, size) +{ + for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) { + buffers[i] = stagingBuffers[i] = VK_NULL_HANDLE; + allocations[i] = stagingAllocations[i] = nullptr; + } +} + +QVkBuffer::~QVkBuffer() +{ + release(); +} + +void QVkBuffer::release() +{ + if (!buffers[0]) + return; + + QRhiVulkan::DeferredReleaseEntry e; + e.type = QRhiVulkan::DeferredReleaseEntry::Buffer; + e.lastActiveFrameSlot = lastActiveFrameSlot; + + for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) { + e.buffer.buffers[i] = buffers[i]; + e.buffer.allocations[i] = allocations[i]; + e.buffer.stagingBuffers[i] = stagingBuffers[i]; + e.buffer.stagingAllocations[i] = stagingAllocations[i]; + + buffers[i] = VK_NULL_HANDLE; + allocations[i] = nullptr; + stagingBuffers[i] = VK_NULL_HANDLE; + stagingAllocations[i] = nullptr; + pendingDynamicUpdates[i].clear(); + } + + QRHI_RES_RHI(QRhiVulkan); + rhiD->releaseQueue.append(e); + + QRHI_PROF; + QRHI_PROF_F(releaseBuffer(this)); + + rhiD->unregisterResource(this); +} + +bool QVkBuffer::build() +{ + if (buffers[0]) + release(); + + const int nonZeroSize = m_size <= 0 ? 256 : m_size; + + VkBufferCreateInfo bufferInfo; + memset(&bufferInfo, 0, sizeof(bufferInfo)); + bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferInfo.size = nonZeroSize; + bufferInfo.usage = toVkBufferUsage(m_usage); + + VmaAllocationCreateInfo allocInfo; + memset(&allocInfo, 0, sizeof(allocInfo)); + + if (m_type == Dynamic) { +#ifndef Q_OS_DARWIN // not for MoltenVK + // Keep mapped all the time. Essential f.ex. with some mobile GPUs, + // where mapping and unmapping an entire allocation every time updating + // a suballocated buffer presents a significant perf. hit. + allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT; +#endif + // host visible, frequent changes + allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU; + } else { + allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; + bufferInfo.usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; + } + + QRHI_RES_RHI(QRhiVulkan); + VkResult err = VK_SUCCESS; + for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) { + buffers[i] = VK_NULL_HANDLE; + allocations[i] = nullptr; + usageState[i].access = usageState[i].stage = 0; + if (i == 0 || m_type == Dynamic) { + VmaAllocation allocation; + err = vmaCreateBuffer(toVmaAllocator(rhiD->allocator), &bufferInfo, &allocInfo, &buffers[i], &allocation, nullptr); + if (err != VK_SUCCESS) + break; + + allocations[i] = allocation; + if (m_type == Dynamic) + pendingDynamicUpdates[i].reserve(16); + + rhiD->setObjectName(uint64_t(buffers[i]), VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT, m_objectName, + m_type == Dynamic ? i : -1); + } + } + + if (err != VK_SUCCESS) { + qWarning("Failed to create buffer: %d", err); + return false; + } + + QRHI_PROF; + QRHI_PROF_F(newBuffer(this, nonZeroSize, m_type != Dynamic ? 1 : QVK_FRAMES_IN_FLIGHT, 0)); + + lastActiveFrameSlot = -1; + generation += 1; + rhiD->registerResource(this); + return true; +} + +QVkRenderBuffer::QVkRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize, + int sampleCount, Flags flags) + : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags) +{ +} + +QVkRenderBuffer::~QVkRenderBuffer() +{ + release(); + delete backingTexture; +} + +void QVkRenderBuffer::release() +{ + if (!memory && !backingTexture) + return; + + QRhiVulkan::DeferredReleaseEntry e; + e.type = QRhiVulkan::DeferredReleaseEntry::RenderBuffer; + e.lastActiveFrameSlot = lastActiveFrameSlot; + + e.renderBuffer.memory = memory; + e.renderBuffer.image = image; + e.renderBuffer.imageView = imageView; + + memory = VK_NULL_HANDLE; + image = VK_NULL_HANDLE; + imageView = VK_NULL_HANDLE; + + if (backingTexture) { + Q_ASSERT(backingTexture->lastActiveFrameSlot == -1); + backingTexture->lastActiveFrameSlot = e.lastActiveFrameSlot; + backingTexture->release(); + } + + QRHI_RES_RHI(QRhiVulkan); + rhiD->releaseQueue.append(e); + + QRHI_PROF; + QRHI_PROF_F(releaseRenderBuffer(this)); + + rhiD->unregisterResource(this); +} + +bool QVkRenderBuffer::build() +{ + if (memory || backingTexture) + release(); + + if (m_pixelSize.isEmpty()) + return false; + + QRHI_RES_RHI(QRhiVulkan); + QRHI_PROF; + samples = rhiD->effectiveSampleCount(m_sampleCount); + + switch (m_type) { + case QRhiRenderBuffer::Color: + { + if (!backingTexture) { + backingTexture = QRHI_RES(QVkTexture, rhiD->createTexture(QRhiTexture::RGBA8, + m_pixelSize, + m_sampleCount, + QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource)); + } else { + backingTexture->setPixelSize(m_pixelSize); + backingTexture->setSampleCount(m_sampleCount); + } + backingTexture->setName(m_objectName); + if (!backingTexture->build()) + return false; + vkformat = backingTexture->vkformat; + QRHI_PROF_F(newRenderBuffer(this, false, false, samples)); + } + break; + case QRhiRenderBuffer::DepthStencil: + vkformat = rhiD->optimalDepthStencilFormat(); + if (!rhiD->createTransientImage(vkformat, + m_pixelSize, + VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, + VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, + samples, + &memory, + &image, + &imageView, + 1)) + { + return false; + } + rhiD->setObjectName(uint64_t(image), VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, m_objectName); + QRHI_PROF_F(newRenderBuffer(this, true, false, samples)); + break; + default: + Q_UNREACHABLE(); + break; + } + + lastActiveFrameSlot = -1; + rhiD->registerResource(this); + return true; +} + +QRhiTexture::Format QVkRenderBuffer::backingFormat() const +{ + return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat; +} + +QVkTexture::QVkTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, + int sampleCount, Flags flags) + : QRhiTexture(rhi, format, pixelSize, sampleCount, flags) +{ + for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) { + stagingBuffers[i] = VK_NULL_HANDLE; + stagingAllocations[i] = nullptr; + } +} + +QVkTexture::~QVkTexture() +{ + release(); +} + +void QVkTexture::release() +{ + if (!image) + return; + + QRhiVulkan::DeferredReleaseEntry e; + e.type = QRhiVulkan::DeferredReleaseEntry::Texture; + e.lastActiveFrameSlot = lastActiveFrameSlot; + + e.texture.image = owns ? image : VK_NULL_HANDLE; + e.texture.imageView = imageView; + e.texture.allocation = owns ? imageAlloc : nullptr; + + for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) { + e.texture.stagingBuffers[i] = stagingBuffers[i]; + e.texture.stagingAllocations[i] = stagingAllocations[i]; + + stagingBuffers[i] = VK_NULL_HANDLE; + stagingAllocations[i] = nullptr; + } + + image = VK_NULL_HANDLE; + imageView = VK_NULL_HANDLE; + imageAlloc = nullptr; + nativeHandlesStruct.image = VK_NULL_HANDLE; + + QRHI_RES_RHI(QRhiVulkan); + rhiD->releaseQueue.append(e); + + QRHI_PROF; + QRHI_PROF_F(releaseTexture(this)); + + rhiD->unregisterResource(this); +} + +bool QVkTexture::prepareBuild(QSize *adjustedSize) +{ + if (image) + release(); + + QRHI_RES_RHI(QRhiVulkan); + vkformat = toVkTextureFormat(m_format, m_flags); + VkFormatProperties props; + rhiD->f->vkGetPhysicalDeviceFormatProperties(rhiD->physDev, vkformat, &props); + const bool canSampleOptimal = (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT); + if (!canSampleOptimal) { + qWarning("Texture sampling with optimal tiling for format %d not supported", vkformat); + return false; + } + + const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize; + const bool isCube = m_flags.testFlag(CubeMap); + const bool hasMipMaps = m_flags.testFlag(MipMapped); + + mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1; + const int maxLevels = QRhi::MAX_LEVELS; + if (mipLevelCount > maxLevels) { + qWarning("Too many mip levels (%d, max is %d), truncating mip chain", mipLevelCount, maxLevels); + mipLevelCount = maxLevels; + } + samples = rhiD->effectiveSampleCount(m_sampleCount); + if (samples > VK_SAMPLE_COUNT_1_BIT) { + if (isCube) { + qWarning("Cubemap texture cannot be multisample"); + return false; + } + if (hasMipMaps) { + qWarning("Multisample texture cannot have mipmaps"); + return false; + } + } + + usageState.layout = VK_IMAGE_LAYOUT_PREINITIALIZED; + usageState.access = 0; + usageState.stage = 0; + + if (adjustedSize) + *adjustedSize = size; + + return true; +} + +bool QVkTexture::finishBuild() +{ + QRHI_RES_RHI(QRhiVulkan); + + const bool isDepth = isDepthTextureFormat(m_format); + const bool isCube = m_flags.testFlag(CubeMap); + + VkImageViewCreateInfo viewInfo; + memset(&viewInfo, 0, sizeof(viewInfo)); + viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewInfo.image = image; + viewInfo.viewType = isCube ? VK_IMAGE_VIEW_TYPE_CUBE : VK_IMAGE_VIEW_TYPE_2D; + viewInfo.format = vkformat; + viewInfo.components.r = VK_COMPONENT_SWIZZLE_R; + viewInfo.components.g = VK_COMPONENT_SWIZZLE_G; + viewInfo.components.b = VK_COMPONENT_SWIZZLE_B; + viewInfo.components.a = VK_COMPONENT_SWIZZLE_A; + viewInfo.subresourceRange.aspectMask = isDepth ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT; + viewInfo.subresourceRange.levelCount = mipLevelCount; + viewInfo.subresourceRange.layerCount = isCube ? 6 : 1; + + VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &imageView); + if (err != VK_SUCCESS) { + qWarning("Failed to create image view: %d", err); + return false; + } + + nativeHandlesStruct.image = image; + + lastActiveFrameSlot = -1; + generation += 1; + + return true; +} + +bool QVkTexture::build() +{ + QSize size; + if (!prepareBuild(&size)) + return false; + + const bool isRenderTarget = m_flags.testFlag(QRhiTexture::RenderTarget); + const bool isDepth = isDepthTextureFormat(m_format); + const bool isCube = m_flags.testFlag(CubeMap); + + VkImageCreateInfo imageInfo; + memset(&imageInfo, 0, sizeof(imageInfo)); + imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageInfo.flags = isCube ? VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT : 0; + imageInfo.imageType = VK_IMAGE_TYPE_2D; + imageInfo.format = vkformat; + imageInfo.extent.width = size.width(); + imageInfo.extent.height = size.height(); + imageInfo.extent.depth = 1; + imageInfo.mipLevels = mipLevelCount; + imageInfo.arrayLayers = isCube ? 6 : 1; + imageInfo.samples = samples; + imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; + + imageInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + if (isRenderTarget) { + if (isDepth) + imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + else + imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + } + if (m_flags.testFlag(QRhiTexture::UsedAsTransferSource)) + imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + if (m_flags.testFlag(QRhiTexture::UsedWithGenerateMips)) + imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + + VmaAllocationCreateInfo allocInfo; + memset(&allocInfo, 0, sizeof(allocInfo)); + allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; + + QRHI_RES_RHI(QRhiVulkan); + VmaAllocation allocation; + VkResult err = vmaCreateImage(toVmaAllocator(rhiD->allocator), &imageInfo, &allocInfo, &image, &allocation, nullptr); + if (err != VK_SUCCESS) { + qWarning("Failed to create image: %d", err); + return false; + } + imageAlloc = allocation; + + if (!finishBuild()) + return false; + + rhiD->setObjectName(uint64_t(image), VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, m_objectName); + + QRHI_PROF; + QRHI_PROF_F(newTexture(this, true, mipLevelCount, isCube ? 6 : 1, samples)); + + owns = true; + rhiD->registerResource(this); + return true; +} + +bool QVkTexture::buildFrom(const QRhiNativeHandles *src) +{ + const QRhiVulkanTextureNativeHandles *h = static_cast(src); + if (!h || !h->image) + return false; + + if (!prepareBuild()) + return false; + + image = h->image; + + if (!finishBuild()) + return false; + + QRHI_PROF; + QRHI_PROF_F(newTexture(this, false, mipLevelCount, m_flags.testFlag(CubeMap) ? 6 : 1, samples)); + + usageState.layout = h->layout; + + owns = false; + QRHI_RES_RHI(QRhiVulkan); + rhiD->registerResource(this); + return true; +} + +const QRhiNativeHandles *QVkTexture::nativeHandles() +{ + nativeHandlesStruct.layout = usageState.layout; + return &nativeHandlesStruct; +} + +QVkSampler::QVkSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode, + AddressMode u, AddressMode v) + : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v) +{ +} + +QVkSampler::~QVkSampler() +{ + release(); +} + +void QVkSampler::release() +{ + if (!sampler) + return; + + QRhiVulkan::DeferredReleaseEntry e; + e.type = QRhiVulkan::DeferredReleaseEntry::Sampler; + e.lastActiveFrameSlot = lastActiveFrameSlot; + + e.sampler.sampler = sampler; + sampler = VK_NULL_HANDLE; + + QRHI_RES_RHI(QRhiVulkan); + rhiD->releaseQueue.append(e); + rhiD->unregisterResource(this); +} + +bool QVkSampler::build() +{ + if (sampler) + release(); + + VkSamplerCreateInfo samplerInfo; + memset(&samplerInfo, 0, sizeof(samplerInfo)); + samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + samplerInfo.magFilter = toVkFilter(m_magFilter); + samplerInfo.minFilter = toVkFilter(m_minFilter); + samplerInfo.mipmapMode = toVkMipmapMode(m_mipmapMode); + samplerInfo.addressModeU = toVkAddressMode(m_addressU); + samplerInfo.addressModeV = toVkAddressMode(m_addressV); + samplerInfo.addressModeW = toVkAddressMode(m_addressW); + samplerInfo.maxAnisotropy = 1.0f; + samplerInfo.compareEnable = m_compareOp != Never; + samplerInfo.compareOp = toVkTextureCompareOp(m_compareOp); + samplerInfo.maxLod = m_mipmapMode == None ? 0.25f : 1000.0f; + + QRHI_RES_RHI(QRhiVulkan); + VkResult err = rhiD->df->vkCreateSampler(rhiD->dev, &samplerInfo, nullptr, &sampler); + if (err != VK_SUCCESS) { + qWarning("Failed to create sampler: %d", err); + return false; + } + + lastActiveFrameSlot = -1; + generation += 1; + rhiD->registerResource(this); + return true; +} + +QVkRenderPassDescriptor::QVkRenderPassDescriptor(QRhiImplementation *rhi) + : QRhiRenderPassDescriptor(rhi) +{ +} + +QVkRenderPassDescriptor::~QVkRenderPassDescriptor() +{ + release(); +} + +void QVkRenderPassDescriptor::release() +{ + if (!rp) + return; + + if (!ownsRp) { + rp = VK_NULL_HANDLE; + return; + } + + QRhiVulkan::DeferredReleaseEntry e; + e.type = QRhiVulkan::DeferredReleaseEntry::RenderPass; + e.lastActiveFrameSlot = lastActiveFrameSlot; + + e.renderPass.rp = rp; + + rp = VK_NULL_HANDLE; + + QRHI_RES_RHI(QRhiVulkan); + rhiD->releaseQueue.append(e); + + rhiD->unregisterResource(this); +} + +QVkReferenceRenderTarget::QVkReferenceRenderTarget(QRhiImplementation *rhi) + : QRhiRenderTarget(rhi) +{ +} + +QVkReferenceRenderTarget::~QVkReferenceRenderTarget() +{ + release(); +} + +void QVkReferenceRenderTarget::release() +{ + // nothing to do here +} + +QSize QVkReferenceRenderTarget::pixelSize() const +{ + return d.pixelSize; +} + +float QVkReferenceRenderTarget::devicePixelRatio() const +{ + return d.dpr; +} + +int QVkReferenceRenderTarget::sampleCount() const +{ + return d.sampleCount; +} + +QVkTextureRenderTarget::QVkTextureRenderTarget(QRhiImplementation *rhi, + const QRhiTextureRenderTargetDescription &desc, + Flags flags) + : QRhiTextureRenderTarget(rhi, desc, flags) +{ + for (int att = 0; att < QVkRenderTargetData::MAX_COLOR_ATTACHMENTS; ++att) { + rtv[att] = VK_NULL_HANDLE; + resrtv[att] = VK_NULL_HANDLE; + } +} + +QVkTextureRenderTarget::~QVkTextureRenderTarget() +{ + release(); +} + +void QVkTextureRenderTarget::release() +{ + if (!d.fb) + return; + + QRhiVulkan::DeferredReleaseEntry e; + e.type = QRhiVulkan::DeferredReleaseEntry::TextureRenderTarget; + e.lastActiveFrameSlot = lastActiveFrameSlot; + + e.textureRenderTarget.fb = d.fb; + d.fb = VK_NULL_HANDLE; + + for (int att = 0; att < QVkRenderTargetData::MAX_COLOR_ATTACHMENTS; ++att) { + e.textureRenderTarget.rtv[att] = rtv[att]; + e.textureRenderTarget.resrtv[att] = resrtv[att]; + rtv[att] = VK_NULL_HANDLE; + resrtv[att] = VK_NULL_HANDLE; + } + + QRHI_RES_RHI(QRhiVulkan); + rhiD->releaseQueue.append(e); + + rhiD->unregisterResource(this); +} + +QRhiRenderPassDescriptor *QVkTextureRenderTarget::newCompatibleRenderPassDescriptor() +{ + // not yet built so cannot rely on data computed in build() + + QRHI_RES_RHI(QRhiVulkan); + QVkRenderPassDescriptor *rp = new QVkRenderPassDescriptor(m_rhi); + if (!rhiD->createOffscreenRenderPass(&rp->rp, + m_desc.colorAttachments(), + m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents), + m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents), + m_desc.depthStencilBuffer(), + m_desc.depthTexture())) + { + delete rp; + return nullptr; + } + + rp->ownsRp = true; + rhiD->registerResource(rp); + return rp; +} + +bool QVkTextureRenderTarget::build() +{ + if (d.fb) + release(); + + const QVector colorAttachments = m_desc.colorAttachments(); + Q_ASSERT(!colorAttachments.isEmpty() || m_desc.depthTexture()); + Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture()); + const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture(); + + QRHI_RES_RHI(QRhiVulkan); + QVarLengthArray views; + + d.colorAttCount = colorAttachments.count(); + for (int i = 0; i < d.colorAttCount; ++i) { + QVkTexture *texD = QRHI_RES(QVkTexture, colorAttachments[i].texture()); + QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, colorAttachments[i].renderBuffer()); + Q_ASSERT(texD || rbD); + if (texD) { + Q_ASSERT(texD->flags().testFlag(QRhiTexture::RenderTarget)); + VkImageViewCreateInfo viewInfo; + memset(&viewInfo, 0, sizeof(viewInfo)); + viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewInfo.image = texD->image; + viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewInfo.format = texD->vkformat; + viewInfo.components.r = VK_COMPONENT_SWIZZLE_R; + viewInfo.components.g = VK_COMPONENT_SWIZZLE_G; + viewInfo.components.b = VK_COMPONENT_SWIZZLE_B; + viewInfo.components.a = VK_COMPONENT_SWIZZLE_A; + viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + viewInfo.subresourceRange.baseMipLevel = colorAttachments[i].level(); + viewInfo.subresourceRange.levelCount = 1; + viewInfo.subresourceRange.baseArrayLayer = colorAttachments[i].layer(); + viewInfo.subresourceRange.layerCount = 1; + VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &rtv[i]); + if (err != VK_SUCCESS) { + qWarning("Failed to create render target image view: %d", err); + return false; + } + views.append(rtv[i]); + if (i == 0) { + d.pixelSize = texD->pixelSize(); + d.sampleCount = texD->samples; + } + } else if (rbD) { + Q_ASSERT(rbD->backingTexture); + views.append(rbD->backingTexture->imageView); + if (i == 0) { + d.pixelSize = rbD->pixelSize(); + d.sampleCount = rbD->samples; + } + } + } + d.dpr = 1; + + if (hasDepthStencil) { + if (m_desc.depthTexture()) { + QVkTexture *depthTexD = QRHI_RES(QVkTexture, m_desc.depthTexture()); + views.append(depthTexD->imageView); + if (d.colorAttCount == 0) { + d.pixelSize = depthTexD->pixelSize(); + d.sampleCount = depthTexD->samples; + } + } else { + QVkRenderBuffer *depthRbD = QRHI_RES(QVkRenderBuffer, m_desc.depthStencilBuffer()); + views.append(depthRbD->imageView); + if (d.colorAttCount == 0) { + d.pixelSize = depthRbD->pixelSize(); + d.sampleCount = depthRbD->samples; + } + } + d.dsAttCount = 1; + } else { + d.dsAttCount = 0; + } + + d.resolveAttCount = 0; + for (int i = 0; i < d.colorAttCount; ++i) { + if (colorAttachments[i].resolveTexture()) { + QVkTexture *resTexD = QRHI_RES(QVkTexture, colorAttachments[i].resolveTexture()); + Q_ASSERT(resTexD->flags().testFlag(QRhiTexture::RenderTarget)); + d.resolveAttCount += 1; + + VkImageViewCreateInfo viewInfo; + memset(&viewInfo, 0, sizeof(viewInfo)); + viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewInfo.image = resTexD->image; + viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewInfo.format = resTexD->vkformat; + viewInfo.components.r = VK_COMPONENT_SWIZZLE_R; + viewInfo.components.g = VK_COMPONENT_SWIZZLE_G; + viewInfo.components.b = VK_COMPONENT_SWIZZLE_B; + viewInfo.components.a = VK_COMPONENT_SWIZZLE_A; + viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + viewInfo.subresourceRange.baseMipLevel = colorAttachments[i].resolveLevel(); + viewInfo.subresourceRange.levelCount = 1; + viewInfo.subresourceRange.baseArrayLayer = colorAttachments[i].resolveLayer(); + viewInfo.subresourceRange.layerCount = 1; + VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &resrtv[i]); + if (err != VK_SUCCESS) { + qWarning("Failed to create render target resolve image view: %d", err); + return false; + } + views.append(resrtv[i]); + } + } + + if (!m_renderPassDesc) + qWarning("QVkTextureRenderTarget: No renderpass descriptor set. See newCompatibleRenderPassDescriptor() and setRenderPassDescriptor()."); + + d.rp = QRHI_RES(QVkRenderPassDescriptor, m_renderPassDesc); + Q_ASSERT(d.rp && d.rp->rp); + + VkFramebufferCreateInfo fbInfo; + memset(&fbInfo, 0, sizeof(fbInfo)); + fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + fbInfo.renderPass = d.rp->rp; + fbInfo.attachmentCount = d.colorAttCount + d.dsAttCount + d.resolveAttCount; + fbInfo.pAttachments = views.constData(); + fbInfo.width = d.pixelSize.width(); + fbInfo.height = d.pixelSize.height(); + fbInfo.layers = 1; + + VkResult err = rhiD->df->vkCreateFramebuffer(rhiD->dev, &fbInfo, nullptr, &d.fb); + if (err != VK_SUCCESS) { + qWarning("Failed to create framebuffer: %d", err); + return false; + } + + lastActiveFrameSlot = -1; + rhiD->registerResource(this); + return true; +} + +QSize QVkTextureRenderTarget::pixelSize() const +{ + return d.pixelSize; +} + +float QVkTextureRenderTarget::devicePixelRatio() const +{ + return d.dpr; +} + +int QVkTextureRenderTarget::sampleCount() const +{ + return d.sampleCount; +} + +QVkShaderResourceBindings::QVkShaderResourceBindings(QRhiImplementation *rhi) + : QRhiShaderResourceBindings(rhi) +{ +} + +QVkShaderResourceBindings::~QVkShaderResourceBindings() +{ + release(); +} + +void QVkShaderResourceBindings::release() +{ + if (!layout) + return; + + sortedBindings.clear(); + + QRhiVulkan::DeferredReleaseEntry e; + e.type = QRhiVulkan::DeferredReleaseEntry::ShaderResourceBindings; + e.lastActiveFrameSlot = lastActiveFrameSlot; + + e.shaderResourceBindings.poolIndex = poolIndex; + e.shaderResourceBindings.layout = layout; + + poolIndex = -1; + layout = VK_NULL_HANDLE; + for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) + descSets[i] = VK_NULL_HANDLE; + + QRHI_RES_RHI(QRhiVulkan); + rhiD->releaseQueue.append(e); + + rhiD->unregisterResource(this); +} + +bool QVkShaderResourceBindings::build() +{ + if (layout) + release(); + + for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) + descSets[i] = VK_NULL_HANDLE; + + sortedBindings = m_bindings; + std::sort(sortedBindings.begin(), sortedBindings.end(), + [](const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b) + { + return QRhiShaderResourceBindingPrivate::get(&a)->binding < QRhiShaderResourceBindingPrivate::get(&b)->binding; + }); + + QVarLengthArray vkbindings; + for (const QRhiShaderResourceBinding &binding : qAsConst(sortedBindings)) { + const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&binding); + VkDescriptorSetLayoutBinding vkbinding; + memset(&vkbinding, 0, sizeof(vkbinding)); + vkbinding.binding = b->binding; + vkbinding.descriptorType = toVkDescriptorType(b); + vkbinding.descriptorCount = 1; // no array support yet + vkbinding.stageFlags = toVkShaderStageFlags(b->stage); + vkbindings.append(vkbinding); + } + + VkDescriptorSetLayoutCreateInfo layoutInfo; + memset(&layoutInfo, 0, sizeof(layoutInfo)); + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = uint32_t(vkbindings.count()); + layoutInfo.pBindings = vkbindings.constData(); + + QRHI_RES_RHI(QRhiVulkan); + VkResult err = rhiD->df->vkCreateDescriptorSetLayout(rhiD->dev, &layoutInfo, nullptr, &layout); + if (err != VK_SUCCESS) { + qWarning("Failed to create descriptor set layout: %d", err); + return false; + } + + VkDescriptorSetAllocateInfo allocInfo; + memset(&allocInfo, 0, sizeof(allocInfo)); + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorSetCount = QVK_FRAMES_IN_FLIGHT; + VkDescriptorSetLayout layouts[QVK_FRAMES_IN_FLIGHT]; + for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) + layouts[i] = layout; + allocInfo.pSetLayouts = layouts; + if (!rhiD->allocateDescriptorSet(&allocInfo, descSets, &poolIndex)) + return false; + + rhiD->updateShaderResourceBindings(this); + + lastActiveFrameSlot = -1; + generation += 1; + rhiD->registerResource(this); + return true; +} + +QVkGraphicsPipeline::QVkGraphicsPipeline(QRhiImplementation *rhi) + : QRhiGraphicsPipeline(rhi) +{ +} + +QVkGraphicsPipeline::~QVkGraphicsPipeline() +{ + release(); +} + +void QVkGraphicsPipeline::release() +{ + if (!pipeline && !layout) + return; + + QRhiVulkan::DeferredReleaseEntry e; + e.type = QRhiVulkan::DeferredReleaseEntry::Pipeline; + e.lastActiveFrameSlot = lastActiveFrameSlot; + + e.pipelineState.pipeline = pipeline; + e.pipelineState.layout = layout; + + pipeline = VK_NULL_HANDLE; + layout = VK_NULL_HANDLE; + + QRHI_RES_RHI(QRhiVulkan); + rhiD->releaseQueue.append(e); + + rhiD->unregisterResource(this); +} + +bool QVkGraphicsPipeline::build() +{ + if (pipeline) + release(); + + QRHI_RES_RHI(QRhiVulkan); + if (!rhiD->ensurePipelineCache()) + return false; + + VkPipelineLayoutCreateInfo pipelineLayoutInfo; + memset(&pipelineLayoutInfo, 0, sizeof(pipelineLayoutInfo)); + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = 1; + QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, m_shaderResourceBindings); + Q_ASSERT(m_shaderResourceBindings && srbD->layout); + pipelineLayoutInfo.pSetLayouts = &srbD->layout; + VkResult err = rhiD->df->vkCreatePipelineLayout(rhiD->dev, &pipelineLayoutInfo, nullptr, &layout); + if (err != VK_SUCCESS) { + qWarning("Failed to create pipeline layout: %d", err); + return false; + } + + VkGraphicsPipelineCreateInfo pipelineInfo; + memset(&pipelineInfo, 0, sizeof(pipelineInfo)); + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + + QVarLengthArray shaders; + QVarLengthArray shaderStageCreateInfos; + for (const QRhiGraphicsShaderStage &shaderStage : m_shaderStages) { + const QShader bakedShader = shaderStage.shader(); + const QShaderCode spirv = bakedShader.shader({ QShader::SpirvShader, 100, shaderStage.shaderVariant() }); + if (spirv.shader().isEmpty()) { + qWarning() << "No SPIR-V 1.0 shader code found in baked shader" << bakedShader; + return false; + } + VkShaderModule shader = rhiD->createShader(spirv.shader()); + if (shader) { + shaders.append(shader); + VkPipelineShaderStageCreateInfo shaderInfo; + memset(&shaderInfo, 0, sizeof(shaderInfo)); + shaderInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shaderInfo.stage = toVkShaderStage(shaderStage.type()); + shaderInfo.module = shader; + shaderInfo.pName = spirv.entryPoint().constData(); + shaderStageCreateInfos.append(shaderInfo); + } + } + pipelineInfo.stageCount = shaderStageCreateInfos.count(); + pipelineInfo.pStages = shaderStageCreateInfos.constData(); + + const QVector bindings = m_vertexInputLayout.bindings(); + QVarLengthArray vertexBindings; + QVarLengthArray nonOneStepRates; + for (int i = 0, ie = bindings.count(); i != ie; ++i) { + const QRhiVertexInputBinding &binding(bindings[i]); + VkVertexInputBindingDescription bindingInfo = { + uint32_t(i), + binding.stride(), + binding.classification() == QRhiVertexInputBinding::PerVertex + ? VK_VERTEX_INPUT_RATE_VERTEX : VK_VERTEX_INPUT_RATE_INSTANCE + }; + if (binding.classification() == QRhiVertexInputBinding::PerInstance + && binding.instanceStepRate() != 1) + { + if (rhiD->vertexAttribDivisorAvailable) { + nonOneStepRates.append({ uint32_t(i), uint32_t(binding.instanceStepRate()) }); + } else { + qWarning("QRhiVulkan: Instance step rates other than 1 not supported without " + "VK_EXT_vertex_attribute_divisor on the device and " + "VK_KHR_get_physical_device_properties2 on the instance"); + } + } + vertexBindings.append(bindingInfo); + } + const QVector attributes = m_vertexInputLayout.attributes(); + QVarLengthArray vertexAttributes; + for (const QRhiVertexInputAttribute &attribute : attributes) { + VkVertexInputAttributeDescription attributeInfo = { + uint32_t(attribute.location()), + uint32_t(attribute.binding()), + toVkAttributeFormat(attribute.format()), + attribute.offset() + }; + vertexAttributes.append(attributeInfo); + } + VkPipelineVertexInputStateCreateInfo vertexInputInfo; + memset(&vertexInputInfo, 0, sizeof(vertexInputInfo)); + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputInfo.vertexBindingDescriptionCount = vertexBindings.count(); + vertexInputInfo.pVertexBindingDescriptions = vertexBindings.constData(); + vertexInputInfo.vertexAttributeDescriptionCount = vertexAttributes.count(); + vertexInputInfo.pVertexAttributeDescriptions = vertexAttributes.constData(); + VkPipelineVertexInputDivisorStateCreateInfoEXT divisorInfo; + if (!nonOneStepRates.isEmpty()) { + memset(&divisorInfo, 0, sizeof(divisorInfo)); + divisorInfo.sType = VkStructureType(1000190001); // VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT + divisorInfo.vertexBindingDivisorCount = nonOneStepRates.count(); + divisorInfo.pVertexBindingDivisors = nonOneStepRates.constData(); + vertexInputInfo.pNext = &divisorInfo; + } + pipelineInfo.pVertexInputState = &vertexInputInfo; + + QVarLengthArray dynEnable; + dynEnable << VK_DYNAMIC_STATE_VIEWPORT; + dynEnable << VK_DYNAMIC_STATE_SCISSOR; // ignore UsesScissor - Vulkan requires a scissor for the viewport always + if (m_flags.testFlag(QRhiGraphicsPipeline::UsesBlendConstants)) + dynEnable << VK_DYNAMIC_STATE_BLEND_CONSTANTS; + if (m_flags.testFlag(QRhiGraphicsPipeline::UsesStencilRef)) + dynEnable << VK_DYNAMIC_STATE_STENCIL_REFERENCE; + + VkPipelineDynamicStateCreateInfo dynamicInfo; + memset(&dynamicInfo, 0, sizeof(dynamicInfo)); + dynamicInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamicInfo.dynamicStateCount = dynEnable.count(); + dynamicInfo.pDynamicStates = dynEnable.constData(); + pipelineInfo.pDynamicState = &dynamicInfo; + + VkPipelineViewportStateCreateInfo viewportInfo; + memset(&viewportInfo, 0, sizeof(viewportInfo)); + viewportInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportInfo.viewportCount = viewportInfo.scissorCount = 1; + pipelineInfo.pViewportState = &viewportInfo; + + VkPipelineInputAssemblyStateCreateInfo inputAsmInfo; + memset(&inputAsmInfo, 0, sizeof(inputAsmInfo)); + inputAsmInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAsmInfo.topology = toVkTopology(m_topology); + inputAsmInfo.primitiveRestartEnable = (m_topology == TriangleStrip || m_topology == LineStrip); + pipelineInfo.pInputAssemblyState = &inputAsmInfo; + + VkPipelineRasterizationStateCreateInfo rastInfo; + memset(&rastInfo, 0, sizeof(rastInfo)); + rastInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rastInfo.cullMode = toVkCullMode(m_cullMode); + rastInfo.frontFace = toVkFrontFace(m_frontFace); + rastInfo.lineWidth = 1.0f; + pipelineInfo.pRasterizationState = &rastInfo; + + VkPipelineMultisampleStateCreateInfo msInfo; + memset(&msInfo, 0, sizeof(msInfo)); + msInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + msInfo.rasterizationSamples = rhiD->effectiveSampleCount(m_sampleCount); + pipelineInfo.pMultisampleState = &msInfo; + + VkPipelineDepthStencilStateCreateInfo dsInfo; + memset(&dsInfo, 0, sizeof(dsInfo)); + dsInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + dsInfo.depthTestEnable = m_depthTest; + dsInfo.depthWriteEnable = m_depthWrite; + dsInfo.depthCompareOp = toVkCompareOp(m_depthOp); + dsInfo.stencilTestEnable = m_stencilTest; + if (m_stencilTest) { + fillVkStencilOpState(&dsInfo.front, m_stencilFront); + dsInfo.front.compareMask = m_stencilReadMask; + dsInfo.front.writeMask = m_stencilWriteMask; + fillVkStencilOpState(&dsInfo.back, m_stencilBack); + dsInfo.back.compareMask = m_stencilReadMask; + dsInfo.back.writeMask = m_stencilWriteMask; + } + pipelineInfo.pDepthStencilState = &dsInfo; + + VkPipelineColorBlendStateCreateInfo blendInfo; + memset(&blendInfo, 0, sizeof(blendInfo)); + blendInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + QVarLengthArray vktargetBlends; + for (const QRhiGraphicsPipeline::TargetBlend &b : qAsConst(m_targetBlends)) { + VkPipelineColorBlendAttachmentState blend; + memset(&blend, 0, sizeof(blend)); + blend.blendEnable = b.enable; + blend.srcColorBlendFactor = toVkBlendFactor(b.srcColor); + blend.dstColorBlendFactor = toVkBlendFactor(b.dstColor); + blend.colorBlendOp = toVkBlendOp(b.opColor); + blend.srcAlphaBlendFactor = toVkBlendFactor(b.srcAlpha); + blend.dstAlphaBlendFactor = toVkBlendFactor(b.dstAlpha); + blend.alphaBlendOp = toVkBlendOp(b.opAlpha); + blend.colorWriteMask = toVkColorComponents(b.colorWrite); + vktargetBlends.append(blend); + } + if (vktargetBlends.isEmpty()) { + VkPipelineColorBlendAttachmentState blend; + memset(&blend, 0, sizeof(blend)); + blend.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT + | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + vktargetBlends.append(blend); + } + blendInfo.attachmentCount = vktargetBlends.count(); + blendInfo.pAttachments = vktargetBlends.constData(); + pipelineInfo.pColorBlendState = &blendInfo; + + pipelineInfo.layout = layout; + + Q_ASSERT(m_renderPassDesc && QRHI_RES(const QVkRenderPassDescriptor, m_renderPassDesc)->rp); + pipelineInfo.renderPass = QRHI_RES(const QVkRenderPassDescriptor, m_renderPassDesc)->rp; + + err = rhiD->df->vkCreateGraphicsPipelines(rhiD->dev, rhiD->pipelineCache, 1, &pipelineInfo, nullptr, &pipeline); + + for (VkShaderModule shader : shaders) + rhiD->df->vkDestroyShaderModule(rhiD->dev, shader, nullptr); + + if (err != VK_SUCCESS) { + qWarning("Failed to create graphics pipeline: %d", err); + return false; + } + + lastActiveFrameSlot = -1; + generation += 1; + rhiD->registerResource(this); + return true; +} + +QVkCommandBuffer::QVkCommandBuffer(QRhiImplementation *rhi) + : QRhiCommandBuffer(rhi) +{ + resetState(); +} + +QVkCommandBuffer::~QVkCommandBuffer() +{ + release(); +} + +void QVkCommandBuffer::release() +{ + // nothing to do here, cb is not owned by us +} + +QVkSwapChain::QVkSwapChain(QRhiImplementation *rhi) + : QRhiSwapChain(rhi), + rtWrapper(rhi), + cbWrapper(rhi) +{ +} + +QVkSwapChain::~QVkSwapChain() +{ + release(); +} + +void QVkSwapChain::release() +{ + if (sc == VK_NULL_HANDLE) + return; + + QRHI_RES_RHI(QRhiVulkan); + rhiD->swapchains.remove(this); + rhiD->releaseSwapChainResources(this); + surface = lastConnectedSurface = VK_NULL_HANDLE; + + QRHI_PROF; + QRHI_PROF_F(releaseSwapChain(this)); + + rhiD->unregisterResource(this); +} + +QRhiCommandBuffer *QVkSwapChain::currentFrameCommandBuffer() +{ + return &cbWrapper; +} + +QRhiRenderTarget *QVkSwapChain::currentFrameRenderTarget() +{ + return &rtWrapper; +} + +QSize QVkSwapChain::surfacePixelSize() +{ + if (!ensureSurface()) + return QSize(); + + // The size from the QWindow may not exactly match the surface... so if a + // size is reported from the surface, use that. + VkSurfaceCapabilitiesKHR surfaceCaps; + memset(&surfaceCaps, 0, sizeof(surfaceCaps)); + QRHI_RES_RHI(QRhiVulkan); + rhiD->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(rhiD->physDev, surface, &surfaceCaps); + VkExtent2D bufferSize = surfaceCaps.currentExtent; + if (bufferSize.width == quint32(-1)) { + Q_ASSERT(bufferSize.height == quint32(-1)); + return m_window->size() * m_window->devicePixelRatio(); + } + return QSize(bufferSize.width, bufferSize.height); +} + +QRhiRenderPassDescriptor *QVkSwapChain::newCompatibleRenderPassDescriptor() +{ + // not yet built so cannot rely on data computed in buildOrResize() + + if (!ensureSurface()) // make sure sampleCount and colorFormat reflect what was requested + return nullptr; + + QRHI_RES_RHI(QRhiVulkan); + QVkRenderPassDescriptor *rp = new QVkRenderPassDescriptor(m_rhi); + if (!rhiD->createDefaultRenderPass(&rp->rp, + m_depthStencil != nullptr, + samples, + colorFormat)) + { + delete rp; + return nullptr; + } + + rp->ownsRp = true; + rhiD->registerResource(rp); + return rp; +} + +static inline bool isSrgbFormat(VkFormat format) +{ + switch (format) { + case VK_FORMAT_R8_SRGB: + Q_FALLTHROUGH(); + case VK_FORMAT_R8G8_SRGB: + Q_FALLTHROUGH(); + case VK_FORMAT_R8G8B8_SRGB: + Q_FALLTHROUGH(); + case VK_FORMAT_B8G8R8_SRGB: + Q_FALLTHROUGH(); + case VK_FORMAT_R8G8B8A8_SRGB: + Q_FALLTHROUGH(); + case VK_FORMAT_B8G8R8A8_SRGB: + Q_FALLTHROUGH(); + case VK_FORMAT_A8B8G8R8_SRGB_PACK32: + return true; + default: + return false; + } +} + +bool QVkSwapChain::ensureSurface() +{ + // Do nothing when already done, however window may change so check the + // surface is still the same. Some of the queries below are very expensive + // with some implementations so it is important to do the rest only once + // per surface. + + Q_ASSERT(m_window); + VkSurfaceKHR surf = QVulkanInstance::surfaceForWindow(m_window); + if (!surf) { + qWarning("Failed to get surface for window"); + return false; + } + if (surface == surf) + return true; + + surface = surf; + + QRHI_RES_RHI(QRhiVulkan); + if (rhiD->gfxQueueFamilyIdx != -1) { + if (!rhiD->inst->supportsPresent(rhiD->physDev, rhiD->gfxQueueFamilyIdx, m_window)) { + qWarning("Presenting not supported on this window"); + return false; + } + } + + if (!rhiD->vkGetPhysicalDeviceSurfaceCapabilitiesKHR) { + rhiD->vkGetPhysicalDeviceSurfaceCapabilitiesKHR = reinterpret_cast( + rhiD->inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceCapabilitiesKHR")); + rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR = reinterpret_cast( + rhiD->inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceFormatsKHR")); + rhiD->vkGetPhysicalDeviceSurfacePresentModesKHR = reinterpret_cast( + rhiD->inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfacePresentModesKHR")); + if (!rhiD->vkGetPhysicalDeviceSurfaceCapabilitiesKHR + || !rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR + || !rhiD->vkGetPhysicalDeviceSurfacePresentModesKHR) + { + qWarning("Physical device surface queries not available"); + return false; + } + } + + quint32 formatCount = 0; + rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR(rhiD->physDev, surface, &formatCount, nullptr); + QVector formats(formatCount); + if (formatCount) + rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR(rhiD->physDev, surface, &formatCount, formats.data()); + + const bool srgbRequested = m_flags.testFlag(sRGB); + for (quint32 i = 0; i < formatCount; ++i) { + if (formats[i].format != VK_FORMAT_UNDEFINED && srgbRequested == isSrgbFormat(formats[i].format)) { + colorFormat = formats[i].format; + colorSpace = formats[i].colorSpace; + break; + } + } + + samples = rhiD->effectiveSampleCount(m_sampleCount); + + quint32 presModeCount = 0; + rhiD->vkGetPhysicalDeviceSurfacePresentModesKHR(rhiD->physDev, surface, &presModeCount, nullptr); + QVector presModes(presModeCount); + rhiD->vkGetPhysicalDeviceSurfacePresentModesKHR(rhiD->physDev, surface, &presModeCount, presModes.data()); + supportedPresentationModes = presModes; + + return true; +} + +bool QVkSwapChain::buildOrResize() +{ + QRHI_RES_RHI(QRhiVulkan); + const bool needsRegistration = !window || window != m_window; + + // Can be called multiple times due to window resizes - that is not the + // same as a simple release+build (as with other resources). Thus no + // release() here. See recreateSwapChain(). + + // except if the window actually changes + if (window && window != m_window) + release(); + + window = m_window; + m_currentPixelSize = surfacePixelSize(); + pixelSize = m_currentPixelSize; + + if (!rhiD->recreateSwapChain(this)) { + qWarning("Failed to create new swapchain"); + return false; + } + + if (needsRegistration) + rhiD->swapchains.insert(this); + + if (m_depthStencil && m_depthStencil->sampleCount() != m_sampleCount) { + qWarning("Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.", + m_depthStencil->sampleCount(), m_sampleCount); + } + if (m_depthStencil && m_depthStencil->pixelSize() != pixelSize) { + qWarning("Depth-stencil buffer's size (%dx%d) does not match the surface size (%dx%d). Expect problems.", + m_depthStencil->pixelSize().width(), m_depthStencil->pixelSize().height(), + pixelSize.width(), pixelSize.height()); + } + + if (!m_renderPassDesc) + qWarning("QVkSwapChain: No renderpass descriptor set. See newCompatibleRenderPassDescriptor() and setRenderPassDescriptor()."); + + rtWrapper.d.rp = QRHI_RES(QVkRenderPassDescriptor, m_renderPassDesc); + Q_ASSERT(rtWrapper.d.rp && rtWrapper.d.rp->rp); + + rtWrapper.d.pixelSize = pixelSize; + rtWrapper.d.dpr = window->devicePixelRatio(); + rtWrapper.d.sampleCount = samples; + rtWrapper.d.colorAttCount = 1; + if (m_depthStencil) { + rtWrapper.d.dsAttCount = 1; + ds = QRHI_RES(QVkRenderBuffer, m_depthStencil); + } else { + rtWrapper.d.dsAttCount = 0; + ds = nullptr; + } + if (samples > VK_SAMPLE_COUNT_1_BIT) + rtWrapper.d.resolveAttCount = 1; + else + rtWrapper.d.resolveAttCount = 0; + + for (int i = 0; i < bufferCount; ++i) { + QVkSwapChain::ImageResources &image(imageRes[i]); + VkImageView views[3] = { // color, ds, resolve + samples > VK_SAMPLE_COUNT_1_BIT ? image.msaaImageView : image.imageView, + ds ? ds->imageView : VK_NULL_HANDLE, + samples > VK_SAMPLE_COUNT_1_BIT ? image.imageView : VK_NULL_HANDLE + }; + + VkFramebufferCreateInfo fbInfo; + memset(&fbInfo, 0, sizeof(fbInfo)); + fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + fbInfo.renderPass = rtWrapper.d.rp->rp; + fbInfo.attachmentCount = rtWrapper.d.colorAttCount + rtWrapper.d.dsAttCount + rtWrapper.d.resolveAttCount; + fbInfo.pAttachments = views; + fbInfo.width = pixelSize.width(); + fbInfo.height = pixelSize.height(); + fbInfo.layers = 1; + + VkResult err = rhiD->df->vkCreateFramebuffer(rhiD->dev, &fbInfo, nullptr, &image.fb); + if (err != VK_SUCCESS) { + qWarning("Failed to create framebuffer: %d", err); + return false; + } + } + + frameCount = 0; + + QRHI_PROF; + QRHI_PROF_F(resizeSwapChain(this, QVK_FRAMES_IN_FLIGHT, samples > VK_SAMPLE_COUNT_1_BIT ? QVK_FRAMES_IN_FLIGHT : 0, samples)); + + if (needsRegistration) + rhiD->registerResource(this); + + return true; +} + +QT_END_NAMESPACE diff --git a/src/gui/rhi/qrhivulkan_p.h b/src/gui/rhi/qrhivulkan_p.h new file mode 100644 index 0000000000..545ef5ad72 --- /dev/null +++ b/src/gui/rhi/qrhivulkan_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Gui module +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QRHIVULKAN_H +#define QRHIVULKAN_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include // this is where vulkan.h gets pulled in + +QT_BEGIN_NAMESPACE + +struct Q_GUI_EXPORT QRhiVulkanInitParams : public QRhiInitParams +{ + QVulkanInstance *inst = nullptr; + QWindow *window = nullptr; +}; + +struct Q_GUI_EXPORT QRhiVulkanNativeHandles : public QRhiNativeHandles +{ + VkPhysicalDevice physDev = VK_NULL_HANDLE; + VkDevice dev = VK_NULL_HANDLE; + int gfxQueueFamilyIdx = -1; + VkQueue gfxQueue = VK_NULL_HANDLE; + VkCommandPool cmdPool = VK_NULL_HANDLE; + void *vmemAllocator = nullptr; +}; + +struct Q_GUI_EXPORT QRhiVulkanTextureNativeHandles : public QRhiNativeHandles +{ + VkImage image = VK_NULL_HANDLE; + VkImageLayout layout = VK_IMAGE_LAYOUT_GENERAL; +}; + +struct Q_GUI_EXPORT QRhiVulkanCommandBufferNativeHandles : public QRhiNativeHandles +{ + VkCommandBuffer commandBuffer = VK_NULL_HANDLE; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/rhi/qrhivulkan_p_p.h b/src/gui/rhi/qrhivulkan_p_p.h new file mode 100644 index 0000000000..01acc40d58 --- /dev/null +++ b/src/gui/rhi/qrhivulkan_p_p.h @@ -0,0 +1,859 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Gui module +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QRHIVULKAN_P_H +#define QRHIVULKAN_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qrhivulkan_p.h" +#include "qrhi_p_p.h" + +QT_BEGIN_NAMESPACE + +class QVulkanFunctions; +class QVulkanDeviceFunctions; + +static const int QVK_FRAMES_IN_FLIGHT = 2; + +static const int QVK_DESC_SETS_PER_POOL = 128; +static const int QVK_UNIFORM_BUFFERS_PER_POOL = 256; +static const int QVK_COMBINED_IMAGE_SAMPLERS_PER_POOL = 256; + +static const int QVK_MAX_ACTIVE_TIMESTAMP_PAIRS = 16; + +// no vk_mem_alloc.h available here, void* is good enough +typedef void * QVkAlloc; +typedef void * QVkAllocator; + +struct QVkBuffer : public QRhiBuffer +{ + QVkBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size); + ~QVkBuffer(); + void release() override; + bool build() override; + + VkBuffer buffers[QVK_FRAMES_IN_FLIGHT]; + QVkAlloc allocations[QVK_FRAMES_IN_FLIGHT]; + QVector pendingDynamicUpdates[QVK_FRAMES_IN_FLIGHT]; + VkBuffer stagingBuffers[QVK_FRAMES_IN_FLIGHT]; + QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT]; + struct UsageState { + VkAccessFlags access = 0; + VkPipelineStageFlags stage = 0; + }; + UsageState usageState[QVK_FRAMES_IN_FLIGHT]; + int lastActiveFrameSlot = -1; + uint generation = 0; + friend class QRhiVulkan; +}; + +struct QVkTexture; + +struct QVkRenderBuffer : public QRhiRenderBuffer +{ + QVkRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize, + int sampleCount, Flags flags); + ~QVkRenderBuffer(); + void release() override; + bool build() override; + QRhiTexture::Format backingFormat() const override; + + VkDeviceMemory memory = VK_NULL_HANDLE; + VkImage image = VK_NULL_HANDLE; + VkImageView imageView = VK_NULL_HANDLE; + VkSampleCountFlagBits samples; + QVkTexture *backingTexture = nullptr; + VkFormat vkformat; + int lastActiveFrameSlot = -1; + friend class QRhiVulkan; +}; + +struct QVkTexture : public QRhiTexture +{ + QVkTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, + int sampleCount, Flags flags); + ~QVkTexture(); + void release() override; + bool build() override; + bool buildFrom(const QRhiNativeHandles *src) override; + const QRhiNativeHandles *nativeHandles() override; + + bool prepareBuild(QSize *adjustedSize = nullptr); + bool finishBuild(); + + VkImage image = VK_NULL_HANDLE; + VkImageView imageView = VK_NULL_HANDLE; + QVkAlloc imageAlloc = nullptr; + VkBuffer stagingBuffers[QVK_FRAMES_IN_FLIGHT]; + QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT]; + bool owns = true; + QRhiVulkanTextureNativeHandles nativeHandlesStruct; + struct UsageState { + // no tracking of subresource layouts (some operations can keep + // subresources in different layouts for some time, but that does not + // need to be kept track of) + VkImageLayout layout; + VkAccessFlags access; + VkPipelineStageFlags stage; + }; + UsageState usageState; + VkFormat vkformat; + uint mipLevelCount = 0; + VkSampleCountFlagBits samples; + int lastActiveFrameSlot = -1; + uint generation = 0; + friend class QRhiVulkan; +}; + +struct QVkSampler : public QRhiSampler +{ + QVkSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode, + AddressMode u, AddressMode v); + ~QVkSampler(); + void release() override; + bool build() override; + + VkSampler sampler = VK_NULL_HANDLE; + int lastActiveFrameSlot = -1; + uint generation = 0; + friend class QRhiVulkan; +}; + +struct QVkRenderPassDescriptor : public QRhiRenderPassDescriptor +{ + QVkRenderPassDescriptor(QRhiImplementation *rhi); + ~QVkRenderPassDescriptor(); + void release() override; + + VkRenderPass rp = VK_NULL_HANDLE; + bool ownsRp = false; + int lastActiveFrameSlot = -1; +}; + +struct QVkRenderTargetData +{ + VkFramebuffer fb = VK_NULL_HANDLE; + QVkRenderPassDescriptor *rp = nullptr; + QSize pixelSize; + float dpr = 1; + int sampleCount = 1; + int colorAttCount = 0; + int dsAttCount = 0; + int resolveAttCount = 0; + static const int MAX_COLOR_ATTACHMENTS = 8; +}; + +struct QVkReferenceRenderTarget : public QRhiRenderTarget +{ + QVkReferenceRenderTarget(QRhiImplementation *rhi); + ~QVkReferenceRenderTarget(); + void release() override; + + QSize pixelSize() const override; + float devicePixelRatio() const override; + int sampleCount() const override; + + QVkRenderTargetData d; +}; + +struct QVkTextureRenderTarget : public QRhiTextureRenderTarget +{ + QVkTextureRenderTarget(QRhiImplementation *rhi, const QRhiTextureRenderTargetDescription &desc, Flags flags); + ~QVkTextureRenderTarget(); + void release() override; + + QSize pixelSize() const override; + float devicePixelRatio() const override; + int sampleCount() const override; + + QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override; + bool build() override; + + QVkRenderTargetData d; + VkImageView rtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS]; + VkImageView resrtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS]; + int lastActiveFrameSlot = -1; + friend class QRhiVulkan; +}; + +struct QVkShaderResourceBindings : public QRhiShaderResourceBindings +{ + QVkShaderResourceBindings(QRhiImplementation *rhi); + ~QVkShaderResourceBindings(); + void release() override; + bool build() override; + + QVector sortedBindings; + int poolIndex = -1; + VkDescriptorSetLayout layout = VK_NULL_HANDLE; + VkDescriptorSet descSets[QVK_FRAMES_IN_FLIGHT]; // multiple sets to support dynamic buffers + int lastActiveFrameSlot = -1; + uint generation = 0; + + // Keep track of the generation number of each referenced QRhi* to be able + // to detect that the underlying descriptor set became out of date and they + // need to be written again with the up-to-date VkBuffer etc. objects. + struct BoundUniformBufferData { + quint64 id; + uint generation; + }; + struct BoundSampledTextureData { + quint64 texId; + uint texGeneration; + quint64 samplerId; + uint samplerGeneration; + }; + struct BoundResourceData { + union { + BoundUniformBufferData ubuf; + BoundSampledTextureData stex; + }; + }; + QVector boundResourceData[QVK_FRAMES_IN_FLIGHT]; + + friend class QRhiVulkan; +}; + +Q_DECLARE_TYPEINFO(QVkShaderResourceBindings::BoundResourceData, Q_MOVABLE_TYPE); + +struct QVkGraphicsPipeline : public QRhiGraphicsPipeline +{ + QVkGraphicsPipeline(QRhiImplementation *rhi); + ~QVkGraphicsPipeline(); + void release() override; + bool build() override; + + VkPipelineLayout layout = VK_NULL_HANDLE; + VkPipeline pipeline = VK_NULL_HANDLE; + int lastActiveFrameSlot = -1; + uint generation = 0; + friend class QRhiVulkan; +}; + +struct QVkCommandBuffer : public QRhiCommandBuffer +{ + QVkCommandBuffer(QRhiImplementation *rhi); + ~QVkCommandBuffer(); + void release() override; + + VkCommandBuffer cb = VK_NULL_HANDLE; + QRhiVulkanCommandBufferNativeHandles nativeHandlesStruct; + + const QRhiNativeHandles *nativeHandles() { + nativeHandlesStruct.commandBuffer = cb; + return &nativeHandlesStruct; + } + + void resetState() { + resetCommands(); + currentTarget = nullptr; + resetCachedState(); + } + + void resetCachedState() { + currentPipeline = nullptr; + currentPipelineGeneration = 0; + currentSrb = nullptr; + currentSrbGeneration = 0; + currentDescSetSlot = -1; + currentIndexBuffer = VK_NULL_HANDLE; + currentIndexOffset = 0; + currentIndexFormat = VK_INDEX_TYPE_UINT16; + memset(currentVertexBuffers, 0, sizeof(currentVertexBuffers)); + memset(currentVertexOffsets, 0, sizeof(currentVertexOffsets)); + } + + QRhiRenderTarget *currentTarget; + QRhiGraphicsPipeline *currentPipeline; + uint currentPipelineGeneration; + QRhiShaderResourceBindings *currentSrb; + uint currentSrbGeneration; + int currentDescSetSlot; + VkBuffer currentIndexBuffer; + quint32 currentIndexOffset; + VkIndexType currentIndexFormat; + static const int VERTEX_INPUT_RESOURCE_SLOT_COUNT = 32; + VkBuffer currentVertexBuffers[VERTEX_INPUT_RESOURCE_SLOT_COUNT]; + quint32 currentVertexOffsets[VERTEX_INPUT_RESOURCE_SLOT_COUNT]; + + struct Command { + enum Cmd { + CopyBuffer, + CopyBufferToImage, + CopyImage, + CopyImageToBuffer, + ImageBarrier, + BufferBarrier, + BlitImage, + BeginRenderPass, + EndRenderPass, + BindPipeline, + BindDescriptorSet, + BindVertexBuffer, + BindIndexBuffer, + SetViewport, + SetScissor, + SetBlendConstants, + SetStencilRef, + Draw, + DrawIndexed, + DebugMarkerBegin, + DebugMarkerEnd, + DebugMarkerInsert, + TransitionPassResources + }; + Cmd cmd; + + union Args { + struct { + VkBuffer src; + VkBuffer dst; + VkBufferCopy desc; + } copyBuffer; + struct { + VkBuffer src; + VkImage dst; + VkImageLayout dstLayout; + int count; + int bufferImageCopyIndex; + } copyBufferToImage; + struct { + VkImage src; + VkImageLayout srcLayout; + VkImage dst; + VkImageLayout dstLayout; + VkImageCopy desc; + } copyImage; + struct { + VkImage src; + VkImageLayout srcLayout; + VkBuffer dst; + VkBufferImageCopy desc; + } copyImageToBuffer; + struct { + VkPipelineStageFlags srcStageMask; + VkPipelineStageFlags dstStageMask; + VkImageMemoryBarrier desc; + } imageBarrier; + struct { + VkPipelineStageFlags srcStageMask; + VkPipelineStageFlags dstStageMask; + VkBufferMemoryBarrier desc; + } bufferBarrier; + struct { + VkImage src; + VkImageLayout srcLayout; + VkImage dst; + VkImageLayout dstLayout; + VkFilter filter; + VkImageBlit desc; + } blitImage; + struct { + VkRenderPassBeginInfo desc; + int clearValueIndex; + } beginRenderPass; + struct { + } endRenderPass; + struct { + VkPipelineBindPoint bindPoint; + VkPipeline pipeline; + } bindPipeline; + struct { + VkPipelineBindPoint bindPoint; + VkPipelineLayout pipelineLayout; + VkDescriptorSet descSet; + int dynamicOffsetCount; + int dynamicOffsetIndex; + } bindDescriptorSet; + struct { + int startBinding; + int count; + int vertexBufferIndex; + int vertexBufferOffsetIndex; + } bindVertexBuffer; + struct { + VkBuffer buf; + VkDeviceSize ofs; + VkIndexType type; + } bindIndexBuffer; + struct { + VkViewport viewport; + } setViewport; + struct { + VkRect2D scissor; + } setScissor; + struct { + float c[4]; + } setBlendConstants; + struct { + uint32_t ref; + } setStencilRef; + struct { + uint32_t vertexCount; + uint32_t instanceCount; + uint32_t firstVertex; + uint32_t firstInstance; + } draw; + struct { + uint32_t indexCount; + uint32_t instanceCount; + uint32_t firstIndex; + int32_t vertexOffset; + uint32_t firstInstance; + } drawIndexed; + struct { + VkDebugMarkerMarkerInfoEXT marker; + int markerNameIndex; + } debugMarkerBegin; + struct { + } debugMarkerEnd; + struct { + VkDebugMarkerMarkerInfoEXT marker; + } debugMarkerInsert; + struct { + int trackerIndex; + } transitionResources; + } args; + }; + QVector commands; + QVarLengthArray passResTrackers; + int currentPassResTrackerIndex; + + void resetCommands() { + commands.clear(); + passResTrackers.clear(); + currentPassResTrackerIndex = -1; + resetPools(); + } + + void resetPools() { + pools.clearValue.clear(); + pools.bufferImageCopy.clear(); + pools.dynamicOffset.clear(); + pools.vertexBuffer.clear(); + pools.vertexBufferOffset.clear(); + pools.debugMarkerName.clear(); + } + + struct { + QVarLengthArray clearValue; + QVarLengthArray bufferImageCopy; + QVarLengthArray dynamicOffset; + QVarLengthArray vertexBuffer; + QVarLengthArray vertexBufferOffset; + QVarLengthArray debugMarkerName; + } pools; + + friend class QRhiVulkan; +}; + +Q_DECLARE_TYPEINFO(QVkCommandBuffer::Command, Q_MOVABLE_TYPE); + +struct QVkSwapChain : public QRhiSwapChain +{ + QVkSwapChain(QRhiImplementation *rhi); + ~QVkSwapChain(); + void release() override; + + QRhiCommandBuffer *currentFrameCommandBuffer() override; + QRhiRenderTarget *currentFrameRenderTarget() override; + + QSize surfacePixelSize() override; + + QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override; + bool buildOrResize() override; + + bool ensureSurface(); + + static const quint32 MAX_BUFFER_COUNT = 3; + + QWindow *window = nullptr; + QSize pixelSize; + bool supportsReadback = false; + VkSwapchainKHR sc = VK_NULL_HANDLE; + int bufferCount = 0; + VkSurfaceKHR surface = VK_NULL_HANDLE; + VkSurfaceKHR lastConnectedSurface = VK_NULL_HANDLE; + VkFormat colorFormat = VK_FORMAT_B8G8R8A8_UNORM; + VkColorSpaceKHR colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; + QVkRenderBuffer *ds = nullptr; + VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT; + QVector supportedPresentationModes; + VkDeviceMemory msaaImageMem = VK_NULL_HANDLE; + QVkReferenceRenderTarget rtWrapper; + QVkCommandBuffer cbWrapper; + + struct ImageResources { + VkImage image = VK_NULL_HANDLE; + VkImageView imageView = VK_NULL_HANDLE; + VkFramebuffer fb = VK_NULL_HANDLE; + VkImage msaaImage = VK_NULL_HANDLE; + VkImageView msaaImageView = VK_NULL_HANDLE; + bool transferSource = false; + } imageRes[MAX_BUFFER_COUNT]; + + struct FrameResources { + VkFence imageFence = VK_NULL_HANDLE; + bool imageFenceWaitable = false; + VkSemaphore imageSem = VK_NULL_HANDLE; + VkSemaphore drawSem = VK_NULL_HANDLE; + bool imageAcquired = false; + bool imageSemWaitable = false; + quint32 imageIndex = 0; + VkCommandBuffer cmdBuf = VK_NULL_HANDLE; + VkFence cmdFence = VK_NULL_HANDLE; + bool cmdFenceWaitable = false; + int timestampQueryIndex = -1; + } frameRes[QVK_FRAMES_IN_FLIGHT]; + + quint32 currentImageIndex = 0; // index in imageRes + quint32 currentFrameSlot = 0; // index in frameRes + int frameCount = 0; + + friend class QRhiVulkan; +}; + +class QRhiVulkan : public QRhiImplementation +{ +public: + QRhiVulkan(QRhiVulkanInitParams *params, QRhiVulkanNativeHandles *importDevice = nullptr); + + bool create(QRhi::Flags flags) override; + void destroy() override; + + QRhiGraphicsPipeline *createGraphicsPipeline() override; + QRhiShaderResourceBindings *createShaderResourceBindings() override; + QRhiBuffer *createBuffer(QRhiBuffer::Type type, + QRhiBuffer::UsageFlags usage, + int size) override; + QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type, + const QSize &pixelSize, + int sampleCount, + QRhiRenderBuffer::Flags flags) override; + QRhiTexture *createTexture(QRhiTexture::Format format, + const QSize &pixelSize, + int sampleCount, + QRhiTexture::Flags flags) override; + QRhiSampler *createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, + QRhiSampler::Filter mipmapMode, + QRhiSampler:: AddressMode u, QRhiSampler::AddressMode v) override; + + QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc, + QRhiTextureRenderTarget::Flags flags) override; + + QRhiSwapChain *createSwapChain() override; + QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override; + QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override; + QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override; + QRhi::FrameOpResult endOffscreenFrame() override; + QRhi::FrameOpResult finish() override; + + void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override; + + void beginPass(QRhiCommandBuffer *cb, + QRhiRenderTarget *rt, + const QColor &colorClearValue, + const QRhiDepthStencilClearValue &depthStencilClearValue, + QRhiResourceUpdateBatch *resourceUpdates) override; + void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override; + + void setGraphicsPipeline(QRhiCommandBuffer *cb, + QRhiGraphicsPipeline *ps) override; + + void setShaderResources(QRhiCommandBuffer *cb, + QRhiShaderResourceBindings *srb, + int dynamicOffsetCount, + const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) override; + + void setVertexInput(QRhiCommandBuffer *cb, + int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings, + QRhiBuffer *indexBuf, quint32 indexOffset, + QRhiCommandBuffer::IndexFormat indexFormat) override; + + void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) override; + void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override; + void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override; + void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override; + + void draw(QRhiCommandBuffer *cb, quint32 vertexCount, + quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override; + + void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, + quint32 instanceCount, quint32 firstIndex, + qint32 vertexOffset, quint32 firstInstance) override; + + void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) override; + void debugMarkEnd(QRhiCommandBuffer *cb) override; + void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) override; + + const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) override; + void beginExternal(QRhiCommandBuffer *cb) override; + void endExternal(QRhiCommandBuffer *cb) override; + + QVector supportedSampleCounts() const override; + int ubufAlignment() const override; + bool isYUpInFramebuffer() const override; + bool isYUpInNDC() const override; + bool isClipDepthZeroToOne() const override; + QMatrix4x4 clipSpaceCorrMatrix() const override; + bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const override; + bool isFeatureSupported(QRhi::Feature feature) const override; + int resourceLimit(QRhi::ResourceLimit limit) const override; + const QRhiNativeHandles *nativeHandles() override; + void sendVMemStatsToProfiler() override; + + VkResult createDescriptorPool(VkDescriptorPool *pool); + bool allocateDescriptorSet(VkDescriptorSetAllocateInfo *allocInfo, VkDescriptorSet *result, int *resultPoolIndex); + uint32_t chooseTransientImageMemType(VkImage img, uint32_t startIndex); + bool createTransientImage(VkFormat format, const QSize &pixelSize, VkImageUsageFlags usage, + VkImageAspectFlags aspectMask, VkSampleCountFlagBits samples, + VkDeviceMemory *mem, VkImage *images, VkImageView *views, int count); + + bool recreateSwapChain(QRhiSwapChain *swapChain); + void releaseSwapChainResources(QRhiSwapChain *swapChain); + + VkFormat optimalDepthStencilFormat(); + VkSampleCountFlagBits effectiveSampleCount(int sampleCount); + bool createDefaultRenderPass(VkRenderPass *rp, + bool hasDepthStencil, + VkSampleCountFlagBits samples, + VkFormat colorFormat); + bool createOffscreenRenderPass(VkRenderPass *rp, + const QVector &colorAttachments, + bool preserveColor, + bool preserveDs, + QRhiRenderBuffer *depthStencilBuffer, + QRhiTexture *depthTexture); + bool ensurePipelineCache(); + VkShaderModule createShader(const QByteArray &spirv); + + void prepareNewFrame(QRhiCommandBuffer *cb); + QRhi::FrameOpResult startCommandBuffer(VkCommandBuffer *cb); + QRhi::FrameOpResult endAndSubmitCommandBuffer(VkCommandBuffer cb, VkFence cmdFence, + VkSemaphore *waitSem, VkSemaphore *signalSem); + void waitCommandCompletion(int frameSlot); + VkDeviceSize subresUploadByteSize(const QRhiTextureSubresourceUploadDescription &subresDesc) const; + using BufferImageCopyList = QVarLengthArray; + void prepareUploadSubres(QVkTexture *texD, int layer, int level, + const QRhiTextureSubresourceUploadDescription &subresDesc, + size_t *curOfs, void *mp, + BufferImageCopyList *copyInfos); + void enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdateBatch *resourceUpdates); + void executeBufferHostWritesForCurrentFrame(QVkBuffer *bufD); + void enqueueTransitionPassResources(QVkCommandBuffer *cbD); + void recordCommandBuffer(QVkCommandBuffer *cbD); + void trackedRegisterBuffer(QRhiPassResourceTracker *passResTracker, + QVkBuffer *bufD, + int slot, + QRhiPassResourceTracker::BufferAccess access, + QRhiPassResourceTracker::BufferStage stage); + void trackedRegisterTexture(QRhiPassResourceTracker *passResTracker, + QVkTexture *texD, + QRhiPassResourceTracker::TextureAccess access, + QRhiPassResourceTracker::TextureStage stage); + void recordTransitionPassResources(QVkCommandBuffer *cbD, const QRhiPassResourceTracker &tracker); + void activateTextureRenderTarget(QVkCommandBuffer *cbD, QVkTextureRenderTarget *rtD); + void executeDeferredReleases(bool forced = false); + void finishActiveReadbacks(bool forced = false); + + void setObjectName(uint64_t object, VkDebugReportObjectTypeEXT type, const QByteArray &name, int slot = -1); + void trackedBufferBarrier(QVkCommandBuffer *cbD, QVkBuffer *bufD, int slot, + VkAccessFlags access, VkPipelineStageFlags stage); + void trackedImageBarrier(QVkCommandBuffer *cbD, QVkTexture *texD, + VkImageLayout layout, VkAccessFlags access, VkPipelineStageFlags stage); + void subresourceBarrier(QVkCommandBuffer *cbD, VkImage image, + VkImageLayout oldLayout, VkImageLayout newLayout, + VkAccessFlags srcAccess, VkAccessFlags dstAccess, + VkPipelineStageFlags srcStage, VkPipelineStageFlags dstStage, + int startLayer, int layerCount, + int startLevel, int levelCount); + void updateShaderResourceBindings(QRhiShaderResourceBindings *srb, int descSetIdx = -1); + + QVulkanInstance *inst = nullptr; + QWindow *maybeWindow = nullptr; + bool importedDevice = false; + VkPhysicalDevice physDev = VK_NULL_HANDLE; + VkDevice dev = VK_NULL_HANDLE; + bool importedCmdPool = false; + VkCommandPool cmdPool = VK_NULL_HANDLE; + int gfxQueueFamilyIdx = -1; + VkQueue gfxQueue = VK_NULL_HANDLE; + quint32 timestampValidBits = 0; + bool importedAllocator = false; + QVkAllocator allocator = nullptr; + QVulkanFunctions *f = nullptr; + QVulkanDeviceFunctions *df = nullptr; + VkPhysicalDeviceProperties physDevProperties; + VkDeviceSize ubufAlign; + VkDeviceSize texbufAlign; + + bool debugMarkersAvailable = false; + bool vertexAttribDivisorAvailable = false; + PFN_vkCmdDebugMarkerBeginEXT vkCmdDebugMarkerBegin = nullptr; + PFN_vkCmdDebugMarkerEndEXT vkCmdDebugMarkerEnd = nullptr; + PFN_vkCmdDebugMarkerInsertEXT vkCmdDebugMarkerInsert = nullptr; + PFN_vkDebugMarkerSetObjectNameEXT vkDebugMarkerSetObjectName = nullptr; + + PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR = nullptr; + PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR; + PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR; + PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR; + PFN_vkQueuePresentKHR vkQueuePresentKHR; + PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR = nullptr; + PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR; + PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR; + + VkPipelineCache pipelineCache = VK_NULL_HANDLE; + struct DescriptorPoolData { + DescriptorPoolData() { } + DescriptorPoolData(VkDescriptorPool pool_) + : pool(pool_) + { } + VkDescriptorPool pool = VK_NULL_HANDLE; + int refCount = 0; + int allocedDescSets = 0; + }; + QVector descriptorPools; + + VkQueryPool timestampQueryPool = VK_NULL_HANDLE; + QBitArray timestampQueryPoolMap; + + VkFormat optimalDsFormat = VK_FORMAT_UNDEFINED; + QMatrix4x4 clipCorrectMatrix; + + bool inFrame = false; + bool inPass = false; + QVkSwapChain *currentSwapChain = nullptr; + QSet swapchains; + QRhiVulkanNativeHandles nativeHandlesStruct; + + struct OffscreenFrame { + OffscreenFrame(QRhiImplementation *rhi) : cbWrapper(rhi) { } + bool active = false; + QVkCommandBuffer cbWrapper; + VkFence cmdFence = VK_NULL_HANDLE; + } ofr; + + struct ActiveReadback { + int activeFrameSlot = -1; + QRhiReadbackDescription desc; + QRhiReadbackResult *result; + VkBuffer buf; + QVkAlloc bufAlloc; + quint32 bufSize; + QSize pixelSize; + QRhiTexture::Format format; + }; + QVector activeReadbacks; + + struct DeferredReleaseEntry { + enum Type { + Pipeline, + ShaderResourceBindings, + Buffer, + RenderBuffer, + Texture, + Sampler, + TextureRenderTarget, + RenderPass, + StagingBuffer + }; + Type type; + int lastActiveFrameSlot; // -1 if not used otherwise 0..FRAMES_IN_FLIGHT-1 + union { + struct { + VkPipeline pipeline; + VkPipelineLayout layout; + } pipelineState; + struct { + int poolIndex; + VkDescriptorSetLayout layout; + } shaderResourceBindings; + struct { + VkBuffer buffers[QVK_FRAMES_IN_FLIGHT]; + QVkAlloc allocations[QVK_FRAMES_IN_FLIGHT]; + VkBuffer stagingBuffers[QVK_FRAMES_IN_FLIGHT]; + QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT]; + } buffer; + struct { + VkDeviceMemory memory; + VkImage image; + VkImageView imageView; + } renderBuffer; + struct { + VkImage image; + VkImageView imageView; + QVkAlloc allocation; + VkBuffer stagingBuffers[QVK_FRAMES_IN_FLIGHT]; + QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT]; + } texture; + struct { + VkSampler sampler; + } sampler; + struct { + VkFramebuffer fb; + VkImageView rtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS]; + VkImageView resrtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS]; + } textureRenderTarget; + struct { + VkRenderPass rp; + } renderPass; + struct { + VkBuffer stagingBuffer; + QVkAlloc stagingAllocation; + } stagingBuffer; + }; + }; + QVector releaseQueue; +}; + +Q_DECLARE_TYPEINFO(QRhiVulkan::DescriptorPoolData, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(QRhiVulkan::DeferredReleaseEntry, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(QRhiVulkan::ActiveReadback, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/rhi/qrhivulkanext_p.h b/src/gui/rhi/qrhivulkanext_p.h new file mode 100644 index 0000000000..67a63e07e0 --- /dev/null +++ b/src/gui/rhi/qrhivulkanext_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt RHI module +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QRHIVULKANEXT_P_H +#define QRHIVULKANEXT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qrhivulkan_p.h" + +QT_BEGIN_NAMESPACE + +#ifndef VK_EXT_vertex_attribute_divisor +#define VK_EXT_vertex_attribute_divisor 1 +#define VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_SPEC_VERSION 2 +#define VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME "VK_EXT_vertex_attribute_divisor" + +typedef struct VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT { + VkStructureType sType; + void* pNext; + uint32_t maxVertexAttribDivisor; +} VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT; + +typedef struct VkVertexInputBindingDivisorDescriptionEXT { + uint32_t binding; + uint32_t divisor; +} VkVertexInputBindingDivisorDescriptionEXT; + +typedef struct VkPipelineVertexInputDivisorStateCreateInfoEXT { + VkStructureType sType; + const void* pNext; + uint32_t vertexBindingDivisorCount; + const VkVertexInputBindingDivisorDescriptionEXT* pVertexBindingDivisors; +} VkPipelineVertexInputDivisorStateCreateInfoEXT; +#endif // VK_EXT_vertex_attribute_divisor + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/rhi/qshader.cpp b/src/gui/rhi/qshader.cpp new file mode 100644 index 0000000000..5569aad639 --- /dev/null +++ b/src/gui/rhi/qshader.cpp @@ -0,0 +1,586 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Gui module +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qshader_p_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QShader + \inmodule QtRhi + \since 5.14 + + \brief Contains multiple versions of a shader translated to multiple shading languages, + together with reflection metadata. + + QShader is the entry point to shader code in the graphics API agnostic + Qt world. Instead of using GLSL shader sources, as was the custom with Qt + 5.x, new graphics systems with backends for multiple graphics APIs, such + as, Vulkan, Metal, Direct3D, and OpenGL, take QShader as their input + whenever a shader needs to be specified. + + A QShader instance is empty and thus invalid by default. To get a useful + instance, the two typical methods are: + + \list + + \li Generate the contents offline, during build time or earlier, using the + \c qsb command line tool. The result is a binary file that is shipped with + the application, read via QIODevice::readAll(), and then deserialized via + fromSerialized(). For more information, see QShaderBaker. + + \li Generate at run time via QShaderBaker. This is an expensive operation, + but allows applications to use user-provided or dynamically generated + shader source strings. + + \endlist + + When used together with the Qt Rendering Hardware Interface and its + classes, like QRhiGraphicsPipeline, no further action is needed from the + application's side as these classes are prepared to consume a QShader + whenever a shader needs to be specified for a given stage of the graphics + pipeline. + + Alternatively, applications can access + + \list + + \li the source or byte code for any of the shading language versions that + are included in the QShader, + + \li the name of the entry point for the shader, + + \li the reflection metadata containing a description of the shader's + inputs, outputs and resources like uniform blocks. This is essential when + an application or framework needs to discover the inputs of a shader at + runtime due to not having advance knowledge of the vertex attributes or the + layout of the uniform buffers used by the shader. + + \endlist + + QShader makes no assumption about the shading language that was used + as the source for generating the various versions and variants that are + included in it. + + QShader uses implicit sharing similarly to many core Qt types, and so + can be returned or passed by value. Detach happens implicitly when calling + a setter. + + For reference, QRhi expects that a QShader suitable for all its + backends contains at least the following: + + \list + + \li SPIR-V 1.0 bytecode suitable for Vulkan 1.0 or newer + + \li GLSL/ES 100 source code suitable for OpenGL ES 2.0 or newer + + \li GLSL 120 source code suitable for OpenGL 2.1 + + \li HLSL Shader Model 5.0 source code or the corresponding DXBC bytecode suitable for Direct3D 11 + + \li Metal Shading Language 1.2 source code or the corresponding bytecode suitable for Metal + + \endlist + + \sa QShaderBaker + */ + +/*! + \enum QShader::Stage + Describes the stage of the graphics pipeline the shader is suitable for. + + \value VertexStage Vertex shader + \value TessellationControlStage Tessellation control (hull) shader + \value TessellationEvaluationStage Tessellation evaluation (domain) shader + \value GeometryStage Geometry shader + \value FragmentStage Fragment (pixel) shader + \value ComputeStage Compute shader + */ + +/*! + \class QShaderVersion + \inmodule QtRhi + + \brief Specifies the shading language version. + + While languages like SPIR-V or the Metal Shading Language use traditional + version numbers, shaders for other APIs can use slightly different + versioning schemes. All those are mapped to a single version number in + here, however. For HLSL, the version refers to the Shader Model version, + like 5.0, 5.1, or 6.0. For GLSL an additional flag is needed to choose + between GLSL and GLSL/ES. + + Below is a list with the most common examples of shader versions for + different graphics APIs: + + \list + + \li Vulkan (SPIR-V): 100 + \li OpenGL: 120, 330, 440, etc. + \li OpenGL ES: 100 with GlslEs, 300 with GlslEs, etc. + \li Direct3D: 50, 51, 60 + \li Metal: 12, 20 + \endlist + + A default constructed QShaderVersion contains a version of 100 and no + flags set. + */ + +/*! + \enum QShaderVersion::Flag + + Describes the flags that can be set. + + \value GlslEs Indicates that GLSL/ES is meant in combination with GlslShader + */ + +/*! + \class QShaderKey + \inmodule QtRhi + + \brief Specifies the shading language, the version with flags, and the variant. + + A default constructed QShaderKey has source set to SpirvShader and + sourceVersion set to 100. sourceVariant defaults to StandardShader. + */ + +/*! + \enum QShader::Source + Describes what kind of shader code an entry contains. + + \value SpirvShader SPIR-V + \value GlslShader GLSL + \value HlslShader HLSL + \value DxbcShader Direct3D bytecode (HLSL compiled by \c fxc) + \value MslShader Metal Shading Language + \value DxilShader Direct3D bytecode (HLSL compiled by \c dxc) + \value MetalLibShader Pre-compiled Metal bytecode + */ + +/*! + \enum QShader::Variant + Describes what kind of shader code an entry contains. + + \value StandardShader The normal, unmodified version of the shader code. + \value BatchableVertexShader Vertex shader rewritten to be suitable for Qt Quick scenegraph batching. + */ + +/*! + \class QShaderCode + \inmodule QtRhi + + \brief Contains source or binary code for a shader and additional metadata. + + When shader() is empty after retrieving a QShaderCode instance from + QShader, it indicates no shader code was found for the requested key. + */ + +static const int QSB_VERSION = 1; + +/*! + Constructs a new, empty (and thus invalid) QShader instance. + */ +QShader::QShader() + : d(new QShaderPrivate) +{ +} + +/*! + \internal + */ +void QShader::detach() +{ + qAtomicDetach(d); +} + +/*! + \internal + */ +QShader::QShader(const QShader &other) + : d(other.d) +{ + d->ref.ref(); +} + +/*! + \internal + */ +QShader &QShader::operator=(const QShader &other) +{ + qAtomicAssign(d, other.d); + return *this; +} + +/*! + Destructor. + */ +QShader::~QShader() +{ + if (!d->ref.deref()) + delete d; +} + +/*! + \return true if the QShader contains at least one shader version. + */ +bool QShader::isValid() const +{ + return !d->shaders.isEmpty(); +} + +/*! + \return the pipeline stage the shader is meant for. + */ +QShader::Stage QShader::stage() const +{ + return d->stage; +} + +/*! + Sets the pipeline \a stage. + */ +void QShader::setStage(Stage stage) +{ + if (stage != d->stage) { + detach(); + d->stage = stage; + } +} + +/*! + \return the reflection metadata for the shader. + */ +QShaderDescription QShader::description() const +{ + return d->desc; +} + +/*! + Sets the reflection metadata to \a desc. + */ +void QShader::setDescription(const QShaderDescription &desc) +{ + detach(); + d->desc = desc; +} + +/*! + \return the list of available shader versions + */ +QVector QShader::availableShaders() const +{ + return d->shaders.keys().toVector(); +} + +/*! + \return the source or binary code for a given shader version specified by \a key. + */ +QShaderCode QShader::shader(const QShaderKey &key) const +{ + return d->shaders.value(key); +} + +/*! + Stores the source or binary \a shader code for a given shader version specified by \a key. + */ +void QShader::setShader(const QShaderKey &key, const QShaderCode &shader) +{ + if (d->shaders.value(key) == shader) + return; + + detach(); + d->shaders[key] = shader; +} + +/*! + Removes the source or binary shader code for a given \a key. + Does nothing when not found. + */ +void QShader::removeShader(const QShaderKey &key) +{ + auto it = d->shaders.find(key); + if (it == d->shaders.end()) + return; + + detach(); + d->shaders.erase(it); +} + +/*! + \return a serialized binary version of all the data held by the + QShader, suitable for writing to files or other I/O devices. + + \sa fromSerialized() + */ +QByteArray QShader::serialized() const +{ + QBuffer buf; + QDataStream ds(&buf); + ds.setVersion(QDataStream::Qt_5_10); + if (!buf.open(QIODevice::WriteOnly)) + return QByteArray(); + + ds << QSB_VERSION; + ds << d->stage; + ds << d->desc.toBinaryJson(); + ds << d->shaders.count(); + for (auto it = d->shaders.cbegin(), itEnd = d->shaders.cend(); it != itEnd; ++it) { + const QShaderKey &k(it.key()); + ds << k.source(); + ds << k.sourceVersion().version(); + ds << k.sourceVersion().flags(); + ds << k.sourceVariant(); + const QShaderCode &shader(d->shaders.value(k)); + ds << shader.shader(); + ds << shader.entryPoint(); + } + + return qCompress(buf.buffer()); +} + +/*! + Creates a new QShader instance from the given \a data. + + \sa serialized() + */ +QShader QShader::fromSerialized(const QByteArray &data) +{ + QByteArray udata = qUncompress(data); + QBuffer buf(&udata); + QDataStream ds(&buf); + ds.setVersion(QDataStream::Qt_5_10); + if (!buf.open(QIODevice::ReadOnly)) + return QShader(); + + QShader bs; + QShaderPrivate *d = QShaderPrivate::get(&bs); + Q_ASSERT(d->ref.load() == 1); // must be detached + int intVal; + ds >> intVal; + if (intVal != QSB_VERSION) + return QShader(); + + ds >> intVal; + d->stage = Stage(intVal); + QByteArray descBin; + ds >> descBin; + d->desc = QShaderDescription::fromBinaryJson(descBin); + int count; + ds >> count; + for (int i = 0; i < count; ++i) { + QShaderKey k; + ds >> intVal; + k.setSource(Source(intVal)); + QShaderVersion ver; + ds >> intVal; + ver.setVersion(intVal); + ds >> intVal; + ver.setFlags(QShaderVersion::Flags(intVal)); + k.setSourceVersion(ver); + ds >> intVal; + k.setSourceVariant(Variant(intVal)); + QShaderCode shader; + QByteArray s; + ds >> s; + shader.setShader(s); + ds >> s; + shader.setEntryPoint(s); + d->shaders[k] = shader; + } + + return bs; +} + +QShaderVersion::QShaderVersion(int v, Flags f) + : m_version(v), m_flags(f) +{ +} + +QShaderCode::QShaderCode(const QByteArray &code, const QByteArray &entry) + : m_shader(code), m_entryPoint(entry) +{ +} + +QShaderKey::QShaderKey(QShader::Source s, + const QShaderVersion &sver, + QShader::Variant svar) + : m_source(s), + m_sourceVersion(sver), + m_sourceVariant(svar) +{ +} + +/*! + Returns \c true if the two QShader objects \a a and \a b are equal, + meaning they are for the same stage with matching sets of shader source or + binary code. + + \relates QShader + */ +bool operator==(const QShader &lhs, const QShader &rhs) Q_DECL_NOTHROW +{ + return lhs.d->stage == rhs.d->stage + && lhs.d->shaders == rhs.d->shaders; + // do not bother with desc, if the shader code is the same, the description must match too +} + +/*! + \fn bool operator!=(const QShader &lhs, const QShader &rhs) + + Returns \c false if the values in the two QShader objects \a a and \a b + are equal; otherwise returns \c true. + + \relates QShader + */ + +/*! + Returns the hash value for \a s, using \a seed to seed the calculation. + + \relates QShader + */ +uint qHash(const QShader &s, uint seed) Q_DECL_NOTHROW +{ + uint h = s.stage(); + for (auto it = s.d->shaders.constBegin(), itEnd = s.d->shaders.constEnd(); it != itEnd; ++it) + h += qHash(it.key(), seed) + qHash(it.value().shader(), seed); + return h; +} + +/*! + Returns \c true if the two QShaderVersion objects \a a and \a b are + equal. + + \relates QShaderVersion + */ +bool operator==(const QShaderVersion &lhs, const QShaderVersion &rhs) Q_DECL_NOTHROW +{ + return lhs.version() == rhs.version() && lhs.flags() == rhs.flags(); +} + +/*! + \fn bool operator!=(const QShaderVersion &lhs, const QShaderVersion &rhs) + + Returns \c false if the values in the two QShaderVersion objects \a a + and \a b are equal; otherwise returns \c true. + + \relates QShaderVersion + */ + +/*! + Returns \c true if the two QShaderKey objects \a a and \a b are equal. + + \relates QShaderKey + */ +bool operator==(const QShaderKey &lhs, const QShaderKey &rhs) Q_DECL_NOTHROW +{ + return lhs.source() == rhs.source() && lhs.sourceVersion() == rhs.sourceVersion() + && lhs.sourceVariant() == rhs.sourceVariant(); +} + +/*! + \fn bool operator!=(const QShaderKey &lhs, const QShaderKey &rhs) + + Returns \c false if the values in the two QShaderKey objects \a a + and \a b are equal; otherwise returns \c true. + + \relates QShaderKey + */ + +/*! + Returns the hash value for \a k, using \a seed to seed the calculation. + + \relates QShaderKey + */ +uint qHash(const QShaderKey &k, uint seed) Q_DECL_NOTHROW +{ + return seed + 10 * k.source() + k.sourceVersion().version() + k.sourceVersion().flags() + k.sourceVariant(); +} + +/*! + Returns \c true if the two QShaderCode objects \a a and \a b are equal. + + \relates QShaderCode + */ +bool operator==(const QShaderCode &lhs, const QShaderCode &rhs) Q_DECL_NOTHROW +{ + return lhs.shader() == rhs.shader() && lhs.entryPoint() == rhs.entryPoint(); +} + +/*! + \fn bool operator!=(const QShaderCode &lhs, const QShaderCode &rhs) + + Returns \c false if the values in the two QShaderCode objects \a a + and \a b are equal; otherwise returns \c true. + + \relates QShaderCode + */ + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QShader &bs) +{ + const QShaderPrivate *d = bs.d; + QDebugStateSaver saver(dbg); + + dbg.nospace() << "QShader(" + << "stage=" << d->stage + << " shaders=" << d->shaders.keys() + << " desc.isValid=" << d->desc.isValid() + << ')'; + + return dbg; +} + +QDebug operator<<(QDebug dbg, const QShaderKey &k) +{ + QDebugStateSaver saver(dbg); + dbg.nospace() << "ShaderKey(" << k.source() + << " " << k.sourceVersion() + << " " << k.sourceVariant() << ")"; + return dbg; +} + +QDebug operator<<(QDebug dbg, const QShaderVersion &v) +{ + QDebugStateSaver saver(dbg); + dbg.nospace() << "Version(" << v.version() << " " << v.flags() << ")"; + return dbg; +} +#endif // QT_NO_DEBUG_STREAM + +QT_END_NAMESPACE diff --git a/src/gui/rhi/qshader_p.h b/src/gui/rhi/qshader_p.h new file mode 100644 index 0000000000..12a0a26e12 --- /dev/null +++ b/src/gui/rhi/qshader_p.h @@ -0,0 +1,227 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Gui module +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSHADER_P_H +#define QSHADER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +struct QShaderPrivate; +class QShaderKey; + +class Q_GUI_EXPORT QShaderVersion +{ +public: + enum Flag { + GlslEs = 0x01 + }; + Q_DECLARE_FLAGS(Flags, Flag) + + QShaderVersion() = default; + QShaderVersion(int v, Flags f = Flags()); + + int version() const { return m_version; } + void setVersion(int v) { m_version = v; } + + Flags flags() const { return m_flags; } + void setFlags(Flags f) { m_flags = f; } + +private: + int m_version = 100; + Flags m_flags; + Q_DECL_UNUSED_MEMBER quint64 m_reserved = 0; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QShaderVersion::Flags) +Q_DECLARE_TYPEINFO(QShaderVersion, Q_MOVABLE_TYPE); + +class Q_GUI_EXPORT QShaderCode +{ +public: + QShaderCode() = default; + QShaderCode(const QByteArray &code, const QByteArray &entry = QByteArray()); + + QByteArray shader() const { return m_shader; } + void setShader(const QByteArray &code) { m_shader = code; } + + QByteArray entryPoint() const { return m_entryPoint; } + void setEntryPoint(const QByteArray &entry) { m_entryPoint = entry; } + +private: + QByteArray m_shader; + QByteArray m_entryPoint; + Q_DECL_UNUSED_MEMBER quint64 m_reserved = 0; +}; + +Q_DECLARE_TYPEINFO(QShaderCode, Q_MOVABLE_TYPE); + +class Q_GUI_EXPORT QShader +{ +public: + enum Stage { + VertexStage = 0, + TessellationControlStage, + TessellationEvaluationStage, + GeometryStage, + FragmentStage, + ComputeStage + }; + + enum Source { + SpirvShader = 0, + GlslShader, + HlslShader, + DxbcShader, // fxc + MslShader, + DxilShader, // dxc + MetalLibShader // xcrun metal + xcrun metallib + }; + + enum Variant { + StandardShader = 0, + BatchableVertexShader + }; + + QShader(); + QShader(const QShader &other); + QShader &operator=(const QShader &other); + ~QShader(); + void detach(); + + bool isValid() const; + + Stage stage() const; + void setStage(Stage stage); + + QShaderDescription description() const; + void setDescription(const QShaderDescription &desc); + + QVector availableShaders() const; + QShaderCode shader(const QShaderKey &key) const; + void setShader(const QShaderKey &key, const QShaderCode &shader); + void removeShader(const QShaderKey &key); + + QByteArray serialized() const; + static QShader fromSerialized(const QByteArray &data); + +private: + QShaderPrivate *d; + friend struct QShaderPrivate; + friend Q_GUI_EXPORT bool operator==(const QShader &, const QShader &) Q_DECL_NOTHROW; + friend Q_GUI_EXPORT uint qHash(const QShader &, uint) Q_DECL_NOTHROW; +#ifndef QT_NO_DEBUG_STREAM + friend Q_GUI_EXPORT QDebug operator<<(QDebug, const QShader &); +#endif +}; + +class Q_GUI_EXPORT QShaderKey +{ +public: + QShaderKey() = default; + QShaderKey(QShader::Source s, + const QShaderVersion &sver, + QShader::Variant svar = QShader::StandardShader); + + QShader::Source source() const { return m_source; } + void setSource(QShader::Source s) { m_source = s; } + + QShaderVersion sourceVersion() const { return m_sourceVersion; } + void setSourceVersion(const QShaderVersion &sver) { m_sourceVersion = sver; } + + QShader::Variant sourceVariant() const { return m_sourceVariant; } + void setSourceVariant(QShader::Variant svar) { m_sourceVariant = svar; } + +private: + QShader::Source m_source = QShader::SpirvShader; + QShaderVersion m_sourceVersion; + QShader::Variant m_sourceVariant = QShader::StandardShader; + Q_DECL_UNUSED_MEMBER quint64 m_reserved = 0; +}; + +Q_DECLARE_TYPEINFO(QShaderKey, Q_MOVABLE_TYPE); + +Q_GUI_EXPORT bool operator==(const QShader &lhs, const QShader &rhs) Q_DECL_NOTHROW; +Q_GUI_EXPORT uint qHash(const QShader &s, uint seed = 0) Q_DECL_NOTHROW; + +inline bool operator!=(const QShader &lhs, const QShader &rhs) Q_DECL_NOTHROW +{ + return !(lhs == rhs); +} + +Q_GUI_EXPORT bool operator==(const QShaderVersion &lhs, const QShaderVersion &rhs) Q_DECL_NOTHROW; +Q_GUI_EXPORT bool operator==(const QShaderKey &lhs, const QShaderKey &rhs) Q_DECL_NOTHROW; +Q_GUI_EXPORT bool operator==(const QShaderCode &lhs, const QShaderCode &rhs) Q_DECL_NOTHROW; + +inline bool operator!=(const QShaderVersion &lhs, const QShaderVersion &rhs) Q_DECL_NOTHROW +{ + return !(lhs == rhs); +} + +inline bool operator!=(const QShaderKey &lhs, const QShaderKey &rhs) Q_DECL_NOTHROW +{ + return !(lhs == rhs); +} + +inline bool operator!=(const QShaderCode &lhs, const QShaderCode &rhs) Q_DECL_NOTHROW +{ + return !(lhs == rhs); +} + +Q_GUI_EXPORT uint qHash(const QShaderKey &k, uint seed = 0) Q_DECL_NOTHROW; + +#ifndef QT_NO_DEBUG_STREAM +Q_GUI_EXPORT QDebug operator<<(QDebug, const QShader &); +Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QShaderKey &k); +Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QShaderVersion &v); +#endif + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/rhi/qshader_p_p.h b/src/gui/rhi/qshader_p_p.h new file mode 100644 index 0000000000..6473590e95 --- /dev/null +++ b/src/gui/rhi/qshader_p_p.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Gui module +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSHADER_P_P_H +#define QSHADER_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qshader_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +struct Q_GUI_EXPORT QShaderPrivate +{ + QShaderPrivate() + : ref(1) + { + } + + QShaderPrivate(const QShaderPrivate *other) + : ref(1), + stage(other->stage), + desc(other->desc), + shaders(other->shaders) + { + } + + static QShaderPrivate *get(QShader *s) { return s->d; } + static const QShaderPrivate *get(const QShader *s) { return s->d; } + + QAtomicInt ref; + QShader::Stage stage = QShader::VertexStage; + QShaderDescription desc; + QHash shaders; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/rhi/qshaderdescription.cpp b/src/gui/rhi/qshaderdescription.cpp new file mode 100644 index 0000000000..ed549b083f --- /dev/null +++ b/src/gui/rhi/qshaderdescription.cpp @@ -0,0 +1,1088 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Gui module +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qshaderdescription_p_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QShaderDescription + \inmodule QtRhi + \since 5.14 + + \brief Describes the interface of a shader. + + A shader typically has a set of inputs and outputs. A vertex shader for + example has a number of input variables and may use one or more uniform + buffers to access data (e.g. a modelview matrix) provided by the + application. The shader for the fragment stage receives data from the + vertex stage (in a simple setup) and may also rely on data from uniform + buffers, images, and samplers. + + When it comes to vertex inputs and the layout of the uniform buffers (what + are the names of the members? what is there size, offset, and so on), + applications and frameworks may need to discover this dynamically at run + time. This is typical when the shader is not built-in but provided by an + external entity, like the user. + + Modern and lean graphics APIs may no longer provide a way to query shader + reflection information at run time. Therefore, such data is now + automatically generated by QShaderBaker and is provided as a + QShaderDescription object for each and every QShader. + + \section2 Example + + Take the following vertex shader: + + \badcode + #version 440 + + layout(location = 0) in vec4 position; + layout(location = 1) in vec3 color; + layout(location = 0) out vec3 v_color; + + layout(std140, binding = 0) uniform buf { + mat4 mvp; + float opacity; + } ubuf; + + out gl_PerVertex { vec4 gl_Position; }; + + void main() + { + v_color = color; + gl_Position = ubuf.mvp * position; + } + \endcode + + This shader has two inputs: \c position at location 0 with a type of \c + vec4, and \c color at location 1 with a type of \c vec3. It has one output: + \c v_color, although this is typically not interesting for applications. + What is more important, there is a uniform block at binding 0 with a size + of 68 bytes and two members, a 4x4 matrix named \c mvp at offset 0, and a + float \c opacity at offset 64. + + All this is described by a QShaderDescription object. QShaderDescription + can also be serialized to JSON and binary JSON, and can be deserialized + from binary JSON. In practice this is rarely needed since QShader + takes care of the associated QShaderDescription automatically, but if the + QShaderDescription of the above shader would be written out as JSON, it + would look like the following: + + \badcode + { + "inputs": [ + { + "location": 1, + "name": "color", + "type": "vec3" + }, + { + "location": 0, + "name": "position", + "type": "vec4" + } + ], + "outputs": [ + { + "location": 0, + "name": "v_color", + "type": "vec3" + } + ], + "uniformBlocks": [ + { + "binding": 0, + "blockName": "buf", + "members": [ + { + "matrixStride": 16, + "name": "mvp", + "offset": 0, + "size": 64, + "type": "mat4" + }, + { + "name": "opacity", + "offset": 64, + "size": 4, + "type": "float" + } + ], + "set": 0, + "size": 68, + "structName": "ubuf" + } + ] + } + \endcode + + The C++ API allows accessing a data structure like the above. For + simplicity the inner structs only contain public data members, also + considering that their layout is unlikely to change in the future. + + \sa QShaderBaker, QShader + */ + +/*! + \enum QShaderDescription::VariableType + Represents the type of a variable or block member. + + \value Unknown + \value Float + \value Vec2 + \value Vec3 + \value Vec4 + \value Mat2 + \value Mat2x3 + \value Mat2x4 + \value Mat3 + \value Mat3x2 + \value Mat3x4 + \value Mat4 + \value Mat4x2 + \value Mat4x3 + \value Int + \value Int2 + \value Int3 + \value Int4 + \value Uint + \value Uint2 + \value Uint3 + \value Uint4 + \value Bool + \value Bool2 + \value Bool3 + \value Bool4 + \value Double + \value Double2 + \value Double3 + \value Double4 + \value DMat2 + \value DMat2x3 + \value DMat2x4 + \value DMat3 + \value DMat3x2 + \value DMat3x4 + \value DMat4 + \value DMat4x2 + \value DMat4x3 + \value Sampler1D + \value Sampler2D + \value Sampler2DMS + \value Sampler3D + \value SamplerCube + \value Sampler1DArray + \value Sampler2DArray + \value Sampler2DMSArray + \value Sampler3DArray + \value SamplerCubeArray + \value SamplerRect + \value SamplerBuffer + \value Image1D + \value Image2D + \value Image2DMS + \value Image3D + \value ImageCube + \value Image1DArray + \value Image2DArray + \value Image2DMSArray + \value Image3DArray + \value ImageCubeArray + \value ImageRect + \value ImageBuffer + \value Struct + */ + +/*! + \class QShaderDescription::InOutVariable + \inmodule QtRhi + + \brief Describes an input or output variable in the shader. + */ + +/*! + \class QShaderDescription::BlockVariable + \inmodule QtRhi + + \brief Describes a member of a uniform or push constant block. + */ + +/*! + \class QShaderDescription::UniformBlock + \inmodule QtRhi + + \brief Describes a uniform block. + + \note When translating to shading languages without uniform block support + (like GLSL 120 or GLSL/ES 100), uniform blocks are replaced with ordinary + uniforms in a struct. The name of the struct, and so the prefix for the + uniforms generated from the block members, is given by structName. + */ + +/*! + \class QShaderDescription::PushConstantBlock + \inmodule QtRhi + + \brief Describes a push constant block. + */ + +/*! + \class QShaderDescription::StorageBlock + \inmodule QtRhi + + \brief Describes a shader storage block. + */ + +/*! + Constructs a new, empty QShaderDescription. + + \note Being empty implies that isValid() returns \c false for the + newly constructed instance. + */ +QShaderDescription::QShaderDescription() + : d(new QShaderDescriptionPrivate) +{ +} + +/*! + \internal + */ +void QShaderDescription::detach() +{ + qAtomicDetach(d); +} + +/*! + \internal + */ +QShaderDescription::QShaderDescription(const QShaderDescription &other) + : d(other.d) +{ + d->ref.ref(); +} + +/*! + \internal + */ +QShaderDescription &QShaderDescription::operator=(const QShaderDescription &other) +{ + qAtomicAssign(d, other.d); + return *this; +} + +/*! + Destructor. + */ +QShaderDescription::~QShaderDescription() +{ + if (!d->ref.deref()) + delete d; +} + +/*! + \return true if the QShaderDescription contains at least one entry in one of + the variable and block lists. + */ +bool QShaderDescription::isValid() const +{ + return !d->inVars.isEmpty() || !d->outVars.isEmpty() + || !d->uniformBlocks.isEmpty() || !d->pushConstantBlocks.isEmpty() || !d->storageBlocks.isEmpty() + || !d->combinedImageSamplers.isEmpty() || !d->storageImages.isEmpty(); +} + +/*! + \return a serialized binary version of the data. + + \sa toJson() + */ +QByteArray QShaderDescription::toBinaryJson() const +{ + return d->makeDoc().toBinaryData(); +} + +/*! + \return a serialized JSON text version of the data. + + \note There is no deserialization method provided for JSON text. + + \sa toBinaryJson() + */ +QByteArray QShaderDescription::toJson() const +{ + return d->makeDoc().toJson(); +} + +/*! + Deserializes the given binary JSON \a data and returns a new + QShaderDescription. + */ +QShaderDescription QShaderDescription::fromBinaryJson(const QByteArray &data) +{ + QShaderDescription desc; + QShaderDescriptionPrivate::get(&desc)->loadDoc(QJsonDocument::fromBinaryData(data)); + return desc; +} + +/*! + \return the list of input variables. This includes vertex inputs (sometimes + called attributes) for the vertex stage, and inputs for other stages + (sometimes called varyings). + */ +QVector QShaderDescription::inputVariables() const +{ + return d->inVars; +} + +/*! + \return the list of output variables. + */ +QVector QShaderDescription::outputVariables() const +{ + return d->outVars; +} + +/*! + \return the list of uniform blocks. + */ +QVector QShaderDescription::uniformBlocks() const +{ + return d->uniformBlocks; +} + +/*! + \return the list of push constant blocks. + + \note Avoid relying on push constant blocks for shaders that are to be used + in combination with the Qt Rendering Hardware Interface since that + currently has no support for them. + */ +QVector QShaderDescription::pushConstantBlocks() const +{ + return d->pushConstantBlocks; +} + +/*! + \return the list of shader storage blocks. + + For example, with GLSL/Vulkan shaders as source, the declaration + + \badcode + struct Stuff { + vec2 a; + vec2 b; + }; + layout(std140, binding = 0) buffer StuffSsbo { + vec4 whatever; + Stuff stuff[]; + } buf; + \endcode + + generates the following: (shown as textual JSON here) + + \badcode + "storageBlocks": [ { + "binding": 0, + "blockName": "StuffSsbo", + "instanceName": "buf", + "knownSize": 16, + "members": [ + { + "name": "whatever", + "offset": 0, + "size": 16, + "type": "vec4" + }, + { + "arrayDims": [ + 0 + ], + "name": "stuff", + "offset": 16, + "size": 0, + "structMembers": [ + { + "name": "a", + "offset": 0, + "size": 8, + "type": "vec2" + }, + { + "name": "b", + "offset": 8, + "size": 8, + "type": "vec2" + } + ], + "type": "struct" + } + ], + "set": 0 + } ] + \endcode + + \note The size of the last member in the storage block is undefined. This shows + up as \c size 0 and an array dimension of \c{[0]}. The storage block's \c knownSize + excludes the size of the last member since that will only be known at run time. + + \note SSBOs are not available with some graphics APIs, such as, OpenGL 2.x or + OpenGL ES older than 3.1. + */ +QVector QShaderDescription::storageBlocks() const +{ + return d->storageBlocks; +} + +/*! + \return the list of combined image samplers + + With GLSL/Vulkan shaders as source a \c{layout(binding = 1) uniform sampler2D tex;} + uniform generates the following: (shown as textual JSON here) + + \badcode + "combinedImageSamplers": [ + { + "binding": 1, + "name": "tex", + "set": 0, + "type": "sampler2D" + } + ] + \endcode + + This does not mean that other language versions of the shader must also use + a combined image sampler, especially considering that the concept may not + exist everywhere. For instance, a HLSL version will likely just use a + Texture2D and SamplerState object with registers t1 and s1, respectively. + */ +QVector QShaderDescription::combinedImageSamplers() const +{ + return d->combinedImageSamplers; +} + +/*! + \return the list of image variables. + + These will likely occur in compute shaders. For example, + \c{layout (binding = 0, rgba8) uniform readonly image2D inputImage;} + generates the following: (shown as textual JSON here) + + \badcode + "storageImages": [ + { + "binding": 0, + "imageFormat": "rgba8", + "name": "inputImage", + "set": 0, + "type": "image2D" + } + ] + \endcode + + \note Separate image objects are not compatible with some graphics APIs, + such as, OpenGL 2.x or OpenGL ES older than 3.1. + */ +QVector QShaderDescription::storageImages() const +{ + return d->storageImages; +} + +static struct TypeTab { + QString k; + QShaderDescription::VariableType v; +} typeTab[] = { + { QLatin1String("float"), QShaderDescription::Float }, + { QLatin1String("vec2"), QShaderDescription::Vec2 }, + { QLatin1String("vec3"), QShaderDescription::Vec3 }, + { QLatin1String("vec4"), QShaderDescription::Vec4 }, + { QLatin1String("mat2"), QShaderDescription::Mat2 }, + { QLatin1String("mat3"), QShaderDescription::Mat3 }, + { QLatin1String("mat4"), QShaderDescription::Mat4 }, + + { QLatin1String("struct"), QShaderDescription::Struct }, + + { QLatin1String("sampler1D"), QShaderDescription::Sampler1D }, + { QLatin1String("sampler2D"), QShaderDescription::Sampler2D }, + { QLatin1String("sampler2DMS"), QShaderDescription::Sampler2DMS }, + { QLatin1String("sampler3D"), QShaderDescription::Sampler3D }, + { QLatin1String("samplerCube"), QShaderDescription::SamplerCube }, + { QLatin1String("sampler1DArray"), QShaderDescription::Sampler1DArray }, + { QLatin1String("sampler2DArray"), QShaderDescription::Sampler2DArray }, + { QLatin1String("sampler2DMSArray"), QShaderDescription::Sampler2DMSArray }, + { QLatin1String("sampler3DArray"), QShaderDescription::Sampler3DArray }, + { QLatin1String("samplerCubeArray"), QShaderDescription::SamplerCubeArray }, + { QLatin1String("samplerRect"), QShaderDescription::SamplerRect }, + { QLatin1String("samplerBuffer"), QShaderDescription::SamplerBuffer }, + + { QLatin1String("mat2x3"), QShaderDescription::Mat2x3 }, + { QLatin1String("mat2x4"), QShaderDescription::Mat2x4 }, + { QLatin1String("mat3x2"), QShaderDescription::Mat3x2 }, + { QLatin1String("mat3x4"), QShaderDescription::Mat3x4 }, + { QLatin1String("mat4x2"), QShaderDescription::Mat4x2 }, + { QLatin1String("mat4x3"), QShaderDescription::Mat4x3 }, + + { QLatin1String("int"), QShaderDescription::Int }, + { QLatin1String("ivec2"), QShaderDescription::Int2 }, + { QLatin1String("ivec3"), QShaderDescription::Int3 }, + { QLatin1String("ivec4"), QShaderDescription::Int4 }, + + { QLatin1String("uint"), QShaderDescription::Uint }, + { QLatin1String("uvec2"), QShaderDescription::Uint2 }, + { QLatin1String("uvec3"), QShaderDescription::Uint3 }, + { QLatin1String("uvec4"), QShaderDescription::Uint4 }, + + { QLatin1String("bool"), QShaderDescription::Bool }, + { QLatin1String("bvec2"), QShaderDescription::Bool2 }, + { QLatin1String("bvec3"), QShaderDescription::Bool3 }, + { QLatin1String("bvec4"), QShaderDescription::Bool4 }, + + { QLatin1String("double"), QShaderDescription::Double }, + { QLatin1String("dvec2"), QShaderDescription::Double2 }, + { QLatin1String("dvec3"), QShaderDescription::Double3 }, + { QLatin1String("dvec4"), QShaderDescription::Double4 }, + { QLatin1String("dmat2"), QShaderDescription::DMat2 }, + { QLatin1String("dmat3"), QShaderDescription::DMat3 }, + { QLatin1String("dmat4"), QShaderDescription::DMat4 }, + { QLatin1String("dmat2x3"), QShaderDescription::DMat2x3 }, + { QLatin1String("dmat2x4"), QShaderDescription::DMat2x4 }, + { QLatin1String("dmat3x2"), QShaderDescription::DMat3x2 }, + { QLatin1String("dmat3x4"), QShaderDescription::DMat3x4 }, + { QLatin1String("dmat4x2"), QShaderDescription::DMat4x2 }, + { QLatin1String("dmat4x3"), QShaderDescription::DMat4x3 }, + + { QLatin1String("image1D"), QShaderDescription::Image1D }, + { QLatin1String("image2D"), QShaderDescription::Image2D }, + { QLatin1String("image2DMS"), QShaderDescription::Image2DMS }, + { QLatin1String("image3D"), QShaderDescription::Image3D }, + { QLatin1String("imageCube"), QShaderDescription::ImageCube }, + { QLatin1String("image1DArray"), QShaderDescription::Image1DArray }, + { QLatin1String("image2DArray"), QShaderDescription::Image2DArray }, + { QLatin1String("image2DMSArray"), QShaderDescription::Image2DMSArray }, + { QLatin1String("image3DArray"), QShaderDescription::Image3DArray }, + { QLatin1String("imageCubeArray"), QShaderDescription::ImageCubeArray }, + { QLatin1String("imageRect"), QShaderDescription::ImageRect }, + { QLatin1String("imageBuffer"), QShaderDescription::ImageBuffer } +}; + +static QString typeStr(const QShaderDescription::VariableType &t) +{ + for (size_t i = 0; i < sizeof(typeTab) / sizeof(TypeTab); ++i) { + if (typeTab[i].v == t) + return typeTab[i].k; + } + return QString(); +} + +static QShaderDescription::VariableType mapType(const QString &t) +{ + for (size_t i = 0; i < sizeof(typeTab) / sizeof(TypeTab); ++i) { + if (typeTab[i].k == t) + return typeTab[i].v; + } + return QShaderDescription::Unknown; +} + +static struct ImageFormatTab { + QString k; + QShaderDescription::ImageFormat v; +} imageFormatTab[] { + { QLatin1String("unknown"), QShaderDescription::ImageFormatUnknown }, + { QLatin1String("rgba32f"), QShaderDescription::ImageFormatRgba32f }, + { QLatin1String("rgba16"), QShaderDescription::ImageFormatRgba16f }, + { QLatin1String("r32f"), QShaderDescription::ImageFormatR32f }, + { QLatin1String("rgba8"), QShaderDescription::ImageFormatRgba8 }, + { QLatin1String("rgba8_snorm"), QShaderDescription::ImageFormatRgba8Snorm }, + { QLatin1String("rg32f"), QShaderDescription::ImageFormatRg32f }, + { QLatin1String("rg16f"), QShaderDescription::ImageFormatRg16f }, + { QLatin1String("r11f_g11f_b10f"), QShaderDescription::ImageFormatR11fG11fB10f }, + { QLatin1String("r16f"), QShaderDescription::ImageFormatR16f }, + { QLatin1String("rgba16"), QShaderDescription::ImageFormatRgba16 }, + { QLatin1String("rgb10_a2"), QShaderDescription::ImageFormatRgb10A2 }, + { QLatin1String("rg16"), QShaderDescription::ImageFormatRg16 }, + { QLatin1String("rg8"), QShaderDescription::ImageFormatRg8 }, + { QLatin1String("r16"), QShaderDescription::ImageFormatR16 }, + { QLatin1String("r8"), QShaderDescription::ImageFormatR8 }, + { QLatin1String("rgba16_snorm"), QShaderDescription::ImageFormatRgba16Snorm }, + { QLatin1String("rg16_snorm"), QShaderDescription::ImageFormatRg16Snorm }, + { QLatin1String("rg8_snorm"), QShaderDescription::ImageFormatRg8Snorm }, + { QLatin1String("r16_snorm"), QShaderDescription::ImageFormatR16Snorm }, + { QLatin1String("r8_snorm"), QShaderDescription::ImageFormatR8Snorm }, + { QLatin1String("rgba32i"), QShaderDescription::ImageFormatRgba32i }, + { QLatin1String("rgba16i"), QShaderDescription::ImageFormatRgba16i }, + { QLatin1String("rgba8i"), QShaderDescription::ImageFormatRgba8i }, + { QLatin1String("r32i"), QShaderDescription::ImageFormatR32i }, + { QLatin1String("rg32i"), QShaderDescription::ImageFormatRg32i }, + { QLatin1String("rg16i"), QShaderDescription::ImageFormatRg16i }, + { QLatin1String("rg8i"), QShaderDescription::ImageFormatRg8i }, + { QLatin1String("r16i"), QShaderDescription::ImageFormatR16i }, + { QLatin1String("r8i"), QShaderDescription::ImageFormatR8i }, + { QLatin1String("rgba32ui"), QShaderDescription::ImageFormatRgba32ui }, + { QLatin1String("rgba16ui"), QShaderDescription::ImageFormatRgba16ui }, + { QLatin1String("rgba8ui"), QShaderDescription::ImageFormatRgba8ui }, + { QLatin1String("r32ui"), QShaderDescription::ImageFormatR32ui }, + { QLatin1String("rgb10_a2ui"), QShaderDescription::ImageFormatRgb10a2ui }, + { QLatin1String("rg32ui"), QShaderDescription::ImageFormatRg32ui }, + { QLatin1String("rg16ui"), QShaderDescription::ImageFormatRg16ui }, + { QLatin1String("rg8ui"), QShaderDescription::ImageFormatRg8ui }, + { QLatin1String("r16ui"), QShaderDescription::ImageFormatR16ui }, + { QLatin1String("r8ui"), QShaderDescription::ImageFormatR8ui } +}; + +static QString imageFormatStr(const QShaderDescription::ImageFormat &f) +{ + for (size_t i = 0; i < sizeof(imageFormatTab) / sizeof(ImageFormatTab); ++i) { + if (imageFormatTab[i].v == f) + return imageFormatTab[i].k; + } + return QString(); +} + +static QShaderDescription::ImageFormat mapImageFormat(const QString &f) +{ + for (size_t i = 0; i < sizeof(imageFormatTab) / sizeof(ImageFormatTab); ++i) { + if (imageFormatTab[i].k == f) + return imageFormatTab[i].v; + } + return QShaderDescription::ImageFormatUnknown; +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QShaderDescription &sd) +{ + const QShaderDescriptionPrivate *d = sd.d; + QDebugStateSaver saver(dbg); + + if (sd.isValid()) { + dbg.nospace() << "QShaderDescription(" + << "inVars " << d->inVars + << " outVars " << d->outVars + << " uniformBlocks " << d->uniformBlocks + << " pcBlocks " << d->pushConstantBlocks + << " storageBlocks " << d->storageBlocks + << " combinedSamplers " << d->combinedImageSamplers + << " images " << d->storageImages + << ')'; + } else { + dbg.nospace() << "QShaderDescription(null)"; + } + + return dbg; +} + +QDebug operator<<(QDebug dbg, const QShaderDescription::InOutVariable &var) +{ + QDebugStateSaver saver(dbg); + dbg.nospace() << "InOutVariable(" << typeStr(var.type) << ' ' << var.name; + if (var.location >= 0) + dbg.nospace() << " location=" << var.location; + if (var.binding >= 0) + dbg.nospace() << " binding=" << var.binding; + if (var.descriptorSet >= 0) + dbg.nospace() << " set=" << var.descriptorSet; + if (var.imageFormat != QShaderDescription::ImageFormatUnknown) + dbg.nospace() << " imageFormat=" << imageFormatStr(var.imageFormat); + if (var.imageFlags) + dbg.nospace() << " imageFlags=" << var.imageFlags; + dbg.nospace() << ')'; + return dbg; +} + +QDebug operator<<(QDebug dbg, const QShaderDescription::BlockVariable &var) +{ + QDebugStateSaver saver(dbg); + dbg.nospace() << "BlockVariable(" << typeStr(var.type) << ' ' << var.name + << " offset=" << var.offset << " size=" << var.size; + if (!var.arrayDims.isEmpty()) + dbg.nospace() << " array=" << var.arrayDims; + if (var.arrayStride) + dbg.nospace() << " arrayStride=" << var.arrayStride; + if (var.matrixStride) + dbg.nospace() << " matrixStride=" << var.matrixStride; + if (var.matrixIsRowMajor) + dbg.nospace() << " [rowmaj]"; + if (!var.structMembers.isEmpty()) + dbg.nospace() << " structMembers=" << var.structMembers; + dbg.nospace() << ')'; + return dbg; +} + +QDebug operator<<(QDebug dbg, const QShaderDescription::UniformBlock &blk) +{ + QDebugStateSaver saver(dbg); + dbg.nospace() << "UniformBlock(" << blk.blockName << ' ' << blk.structName << " size=" << blk.size; + if (blk.binding >= 0) + dbg.nospace() << " binding=" << blk.binding; + if (blk.descriptorSet >= 0) + dbg.nospace() << " set=" << blk.descriptorSet; + dbg.nospace() << ' ' << blk.members << ')'; + return dbg; +} + +QDebug operator<<(QDebug dbg, const QShaderDescription::PushConstantBlock &blk) +{ + QDebugStateSaver saver(dbg); + dbg.nospace() << "PushConstantBlock(" << blk.name << " size=" << blk.size << ' ' << blk.members << ')'; + return dbg; +} + +QDebug operator<<(QDebug dbg, const QShaderDescription::StorageBlock &blk) +{ + QDebugStateSaver saver(dbg); + dbg.nospace() << "StorageBlock(" << blk.blockName << ' ' << blk.instanceName << " knownSize=" << blk.knownSize; + if (blk.binding >= 0) + dbg.nospace() << " binding=" << blk.binding; + if (blk.descriptorSet >= 0) + dbg.nospace() << " set=" << blk.descriptorSet; + dbg.nospace() << ' ' << blk.members << ')'; + return dbg; +} +#endif + +static const QString nameKey = QLatin1String("name"); +static const QString typeKey = QLatin1String("type"); +static const QString locationKey = QLatin1String("location"); +static const QString bindingKey = QLatin1String("binding"); +static const QString setKey = QLatin1String("set"); +static const QString imageFormatKey = QLatin1String("imageFormat"); +static const QString imageFlagsKey = QLatin1String("imageFlags"); +static const QString offsetKey = QLatin1String("offset"); +static const QString arrayDimsKey = QLatin1String("arrayDims"); +static const QString arrayStrideKey = QLatin1String("arrayStride"); +static const QString matrixStrideKey = QLatin1String("matrixStride"); +static const QString matrixRowMajorKey = QLatin1String("matrixRowMajor"); +static const QString structMembersKey = QLatin1String("structMembers"); +static const QString membersKey = QLatin1String("members"); +static const QString inputsKey = QLatin1String("inputs"); +static const QString outputsKey = QLatin1String("outputs"); +static const QString uniformBlocksKey = QLatin1String("uniformBlocks"); +static const QString blockNameKey = QLatin1String("blockName"); +static const QString structNameKey = QLatin1String("structName"); +static const QString instanceNameKey = QLatin1String("instanceName"); +static const QString sizeKey = QLatin1String("size"); +static const QString knownSizeKey = QLatin1String("knownSize"); +static const QString pushConstantBlocksKey = QLatin1String("pushConstantBlocks"); +static const QString storageBlocksKey = QLatin1String("storageBlocks"); +static const QString combinedImageSamplersKey = QLatin1String("combinedImageSamplers"); +static const QString storageImagesKey = QLatin1String("storageImages"); + +static void addDeco(QJsonObject *obj, const QShaderDescription::InOutVariable &v) +{ + if (v.location >= 0) + (*obj)[locationKey] = v.location; + if (v.binding >= 0) + (*obj)[bindingKey] = v.binding; + if (v.descriptorSet >= 0) + (*obj)[setKey] = v.descriptorSet; + if (v.imageFormat != QShaderDescription::ImageFormatUnknown) + (*obj)[imageFormatKey] = imageFormatStr(v.imageFormat); + if (v.imageFlags) + (*obj)[imageFlagsKey] = int(v.imageFlags); +} + +static QJsonObject inOutObject(const QShaderDescription::InOutVariable &v) +{ + QJsonObject obj; + obj[nameKey] = v.name; + obj[typeKey] = typeStr(v.type); + addDeco(&obj, v); + return obj; +} + +static QJsonObject blockMemberObject(const QShaderDescription::BlockVariable &v) +{ + QJsonObject obj; + obj[nameKey] = v.name; + obj[typeKey] = typeStr(v.type); + obj[offsetKey] = v.offset; + obj[sizeKey] = v.size; + if (!v.arrayDims.isEmpty()) { + QJsonArray dimArr; + for (int dim : v.arrayDims) + dimArr.append(dim); + obj[arrayDimsKey] = dimArr; + } + if (v.arrayStride) + obj[arrayStrideKey] = v.arrayStride; + if (v.matrixStride) + obj[matrixStrideKey] = v.matrixStride; + if (v.matrixIsRowMajor) + obj[matrixRowMajorKey] = true; + if (!v.structMembers.isEmpty()) { + QJsonArray arr; + for (const QShaderDescription::BlockVariable &sv : v.structMembers) + arr.append(blockMemberObject(sv)); + obj[structMembersKey] = arr; + } + return obj; +} + +QJsonDocument QShaderDescriptionPrivate::makeDoc() +{ + QJsonObject root; + + QJsonArray jinputs; + for (const QShaderDescription::InOutVariable &v : qAsConst(inVars)) + jinputs.append(inOutObject(v)); + if (!jinputs.isEmpty()) + root[inputsKey] = jinputs; + + QJsonArray joutputs; + for (const QShaderDescription::InOutVariable &v : qAsConst(outVars)) + joutputs.append(inOutObject(v)); + if (!joutputs.isEmpty()) + root[outputsKey] = joutputs; + + QJsonArray juniformBlocks; + for (const QShaderDescription::UniformBlock &b : uniformBlocks) { + QJsonObject juniformBlock; + juniformBlock[blockNameKey] = b.blockName; + juniformBlock[structNameKey] = b.structName; + juniformBlock[sizeKey] = b.size; + if (b.binding >= 0) + juniformBlock[bindingKey] = b.binding; + if (b.descriptorSet >= 0) + juniformBlock[setKey] = b.descriptorSet; + QJsonArray members; + for (const QShaderDescription::BlockVariable &v : b.members) + members.append(blockMemberObject(v)); + juniformBlock[membersKey] = members; + juniformBlocks.append(juniformBlock); + } + if (!juniformBlocks.isEmpty()) + root[uniformBlocksKey] = juniformBlocks; + + QJsonArray jpushConstantBlocks; + for (const QShaderDescription::PushConstantBlock &b : pushConstantBlocks) { + QJsonObject jpushConstantBlock; + jpushConstantBlock[nameKey] = b.name; + jpushConstantBlock[sizeKey] = b.size; + QJsonArray members; + for (const QShaderDescription::BlockVariable &v : b.members) + members.append(blockMemberObject(v)); + jpushConstantBlock[membersKey] = members; + jpushConstantBlocks.append(jpushConstantBlock); + } + if (!jpushConstantBlocks.isEmpty()) + root[pushConstantBlocksKey] = jpushConstantBlocks; + + QJsonArray jstorageBlocks; + for (const QShaderDescription::StorageBlock &b : storageBlocks) { + QJsonObject jstorageBlock; + jstorageBlock[blockNameKey] = b.blockName; + jstorageBlock[instanceNameKey] = b.instanceName; + jstorageBlock[knownSizeKey] = b.knownSize; + if (b.binding >= 0) + jstorageBlock[bindingKey] = b.binding; + if (b.descriptorSet >= 0) + jstorageBlock[setKey] = b.descriptorSet; + QJsonArray members; + for (const QShaderDescription::BlockVariable &v : b.members) + members.append(blockMemberObject(v)); + jstorageBlock[membersKey] = members; + jstorageBlocks.append(jstorageBlock); + } + if (!jstorageBlocks.isEmpty()) + root[storageBlocksKey] = jstorageBlocks; + + QJsonArray jcombinedSamplers; + for (const QShaderDescription::InOutVariable &v : qAsConst(combinedImageSamplers)) { + QJsonObject sampler; + sampler[nameKey] = v.name; + sampler[typeKey] = typeStr(v.type); + addDeco(&sampler, v); + jcombinedSamplers.append(sampler); + } + if (!jcombinedSamplers.isEmpty()) + root[combinedImageSamplersKey] = jcombinedSamplers; + + QJsonArray jstorageImages; + for (const QShaderDescription::InOutVariable &v : qAsConst(storageImages)) { + QJsonObject image; + image[nameKey] = v.name; + image[typeKey] = typeStr(v.type); + addDeco(&image, v); + jstorageImages.append(image); + } + if (!jstorageImages.isEmpty()) + root[storageImagesKey] = jstorageImages; + + return QJsonDocument(root); +} + +static QShaderDescription::InOutVariable inOutVar(const QJsonObject &obj) +{ + QShaderDescription::InOutVariable var; + var.name = obj[nameKey].toString(); + var.type = mapType(obj[typeKey].toString()); + if (obj.contains(locationKey)) + var.location = obj[locationKey].toInt(); + if (obj.contains(bindingKey)) + var.binding = obj[bindingKey].toInt(); + if (obj.contains(setKey)) + var.descriptorSet = obj[setKey].toInt(); + if (obj.contains(imageFormatKey)) + var.imageFormat = mapImageFormat(obj[imageFormatKey].toString()); + if (obj.contains(imageFlagsKey)) + var.imageFlags = QShaderDescription::ImageFlags(obj[imageFlagsKey].toInt()); + return var; +} + +static QShaderDescription::BlockVariable blockVar(const QJsonObject &obj) +{ + QShaderDescription::BlockVariable var; + var.name = obj[nameKey].toString(); + var.type = mapType(obj[typeKey].toString()); + var.offset = obj[offsetKey].toInt(); + var.size = obj[sizeKey].toInt(); + if (obj.contains(arrayDimsKey)) { + QJsonArray dimArr = obj[arrayDimsKey].toArray(); + for (int i = 0; i < dimArr.count(); ++i) + var.arrayDims.append(dimArr.at(i).toInt()); + } + if (obj.contains(arrayStrideKey)) + var.arrayStride = obj[arrayStrideKey].toInt(); + if (obj.contains(matrixStrideKey)) + var.matrixStride = obj[matrixStrideKey].toInt(); + if (obj.contains(matrixRowMajorKey)) + var.matrixIsRowMajor = obj[matrixRowMajorKey].toBool(); + if (obj.contains(structMembersKey)) { + QJsonArray arr = obj[structMembersKey].toArray(); + for (int i = 0; i < arr.count(); ++i) + var.structMembers.append(blockVar(arr.at(i).toObject())); + } + return var; +} + +void QShaderDescriptionPrivate::loadDoc(const QJsonDocument &doc) +{ + if (doc.isNull()) { + qWarning("QShaderDescription: JSON document is empty"); + return; + } + + Q_ASSERT(ref.load() == 1); // must be detached + + inVars.clear(); + outVars.clear(); + uniformBlocks.clear(); + pushConstantBlocks.clear(); + storageBlocks.clear(); + combinedImageSamplers.clear(); + storageImages.clear(); + + QJsonObject root = doc.object(); + + if (root.contains(inputsKey)) { + QJsonArray inputs = root[inputsKey].toArray(); + for (int i = 0; i < inputs.count(); ++i) + inVars.append(inOutVar(inputs[i].toObject())); + } + + if (root.contains(outputsKey)) { + QJsonArray outputs = root[outputsKey].toArray(); + for (int i = 0; i < outputs.count(); ++i) + outVars.append(inOutVar(outputs[i].toObject())); + } + + if (root.contains(uniformBlocksKey)) { + QJsonArray ubs = root[uniformBlocksKey].toArray(); + for (int i = 0; i < ubs.count(); ++i) { + QJsonObject ubObj = ubs[i].toObject(); + QShaderDescription::UniformBlock ub; + ub.blockName = ubObj[blockNameKey].toString(); + ub.structName = ubObj[structNameKey].toString(); + ub.size = ubObj[sizeKey].toInt(); + if (ubObj.contains(bindingKey)) + ub.binding = ubObj[bindingKey].toInt(); + if (ubObj.contains(setKey)) + ub.descriptorSet = ubObj[setKey].toInt(); + QJsonArray members = ubObj[membersKey].toArray(); + for (const QJsonValue &member : members) + ub.members.append(blockVar(member.toObject())); + uniformBlocks.append(ub); + } + } + + if (root.contains(pushConstantBlocksKey)) { + QJsonArray pcs = root[pushConstantBlocksKey].toArray(); + for (int i = 0; i < pcs.count(); ++i) { + QJsonObject pcObj = pcs[i].toObject(); + QShaderDescription::PushConstantBlock pc; + pc.name = pcObj[nameKey].toString(); + pc.size = pcObj[sizeKey].toInt(); + QJsonArray members = pcObj[membersKey].toArray(); + for (const QJsonValue &member : members) + pc.members.append(blockVar(member.toObject())); + pushConstantBlocks.append(pc); + } + } + + if (root.contains(storageBlocksKey)) { + QJsonArray ubs = root[storageBlocksKey].toArray(); + for (int i = 0; i < ubs.count(); ++i) { + QJsonObject sbObj = ubs[i].toObject(); + QShaderDescription::StorageBlock sb; + sb.blockName = sbObj[blockNameKey].toString(); + sb.instanceName = sbObj[instanceNameKey].toString(); + sb.knownSize = sbObj[knownSizeKey].toInt(); + if (sbObj.contains(bindingKey)) + sb.binding = sbObj[bindingKey].toInt(); + if (sbObj.contains(setKey)) + sb.descriptorSet = sbObj[setKey].toInt(); + QJsonArray members = sbObj[membersKey].toArray(); + for (const QJsonValue &member : members) + sb.members.append(blockVar(member.toObject())); + storageBlocks.append(sb); + } + } + + if (root.contains(combinedImageSamplersKey)) { + QJsonArray samplers = root[combinedImageSamplersKey].toArray(); + for (int i = 0; i < samplers.count(); ++i) + combinedImageSamplers.append(inOutVar(samplers[i].toObject())); + } + + if (root.contains(storageImagesKey)) { + QJsonArray images = root[storageImagesKey].toArray(); + for (int i = 0; i < images.count(); ++i) + storageImages.append(inOutVar(images[i].toObject())); + } +} + +QT_END_NAMESPACE diff --git a/src/gui/rhi/qshaderdescription_p.h b/src/gui/rhi/qshaderdescription_p.h new file mode 100644 index 0000000000..43d4256a63 --- /dev/null +++ b/src/gui/rhi/qshaderdescription_p.h @@ -0,0 +1,278 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Gui module +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSHADERDESCRIPTION_H +#define QSHADERDESCRIPTION_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +struct QShaderDescriptionPrivate; + +class Q_GUI_EXPORT QShaderDescription +{ +public: + QShaderDescription(); + QShaderDescription(const QShaderDescription &other); + QShaderDescription &operator=(const QShaderDescription &other); + ~QShaderDescription(); + void detach(); + + bool isValid() const; + + QByteArray toBinaryJson() const; + QByteArray toJson() const; + + static QShaderDescription fromBinaryJson(const QByteArray &data); + + enum VariableType { + Unknown = 0, + + // do not reorder + Float, + Vec2, + Vec3, + Vec4, + Mat2, + Mat2x3, + Mat2x4, + Mat3, + Mat3x2, + Mat3x4, + Mat4, + Mat4x2, + Mat4x3, + + Int, + Int2, + Int3, + Int4, + + Uint, + Uint2, + Uint3, + Uint4, + + Bool, + Bool2, + Bool3, + Bool4, + + Double, + Double2, + Double3, + Double4, + DMat2, + DMat2x3, + DMat2x4, + DMat3, + DMat3x2, + DMat3x4, + DMat4, + DMat4x2, + DMat4x3, + + Sampler1D, + Sampler2D, + Sampler2DMS, + Sampler3D, + SamplerCube, + Sampler1DArray, + Sampler2DArray, + Sampler2DMSArray, + Sampler3DArray, + SamplerCubeArray, + SamplerRect, + SamplerBuffer, + + Image1D, + Image2D, + Image2DMS, + Image3D, + ImageCube, + Image1DArray, + Image2DArray, + Image2DMSArray, + Image3DArray, + ImageCubeArray, + ImageRect, + ImageBuffer, + + Struct + }; + + enum ImageFormat { + // must match SPIR-V's ImageFormat + ImageFormatUnknown = 0, + ImageFormatRgba32f = 1, + ImageFormatRgba16f = 2, + ImageFormatR32f = 3, + ImageFormatRgba8 = 4, + ImageFormatRgba8Snorm = 5, + ImageFormatRg32f = 6, + ImageFormatRg16f = 7, + ImageFormatR11fG11fB10f = 8, + ImageFormatR16f = 9, + ImageFormatRgba16 = 10, + ImageFormatRgb10A2 = 11, + ImageFormatRg16 = 12, + ImageFormatRg8 = 13, + ImageFormatR16 = 14, + ImageFormatR8 = 15, + ImageFormatRgba16Snorm = 16, + ImageFormatRg16Snorm = 17, + ImageFormatRg8Snorm = 18, + ImageFormatR16Snorm = 19, + ImageFormatR8Snorm = 20, + ImageFormatRgba32i = 21, + ImageFormatRgba16i = 22, + ImageFormatRgba8i = 23, + ImageFormatR32i = 24, + ImageFormatRg32i = 25, + ImageFormatRg16i = 26, + ImageFormatRg8i = 27, + ImageFormatR16i = 28, + ImageFormatR8i = 29, + ImageFormatRgba32ui = 30, + ImageFormatRgba16ui = 31, + ImageFormatRgba8ui = 32, + ImageFormatR32ui = 33, + ImageFormatRgb10a2ui = 34, + ImageFormatRg32ui = 35, + ImageFormatRg16ui = 36, + ImageFormatRg8ui = 37, + ImageFormatR16ui = 38, + ImageFormatR8ui = 39 + }; + + enum ImageFlag { + ReadOnlyImage = 1 << 0, + WriteOnlyImage = 1 << 1 + }; + Q_DECLARE_FLAGS(ImageFlags, ImageFlag) + + // Optional data (like decorations) usually default to an otherwise invalid value (-1 or 0). This is intentional. + + struct InOutVariable { + QString name; + VariableType type = Unknown; + int location = -1; + int binding = -1; + int descriptorSet = -1; + ImageFormat imageFormat = ImageFormatUnknown; + ImageFlags imageFlags; + }; + + struct BlockVariable { + QString name; + VariableType type = Unknown; + int offset = 0; + int size = 0; + QVector arrayDims; + int arrayStride = 0; + int matrixStride = 0; + bool matrixIsRowMajor = false; + QVector structMembers; + }; + + struct UniformBlock { + QString blockName; + QString structName; // instanceName + int size = 0; + int binding = -1; + int descriptorSet = -1; + QVector members; + }; + + struct PushConstantBlock { + QString name; + int size = 0; + QVector members; + }; + + struct StorageBlock { + QString blockName; + QString instanceName; + int knownSize = 0; + int binding = -1; + int descriptorSet = -1; + QVector members; + }; + + QVector inputVariables() const; + QVector outputVariables() const; + QVector uniformBlocks() const; + QVector pushConstantBlocks() const; + QVector storageBlocks() const; + QVector combinedImageSamplers() const; + QVector storageImages() const; + +private: + QShaderDescriptionPrivate *d; + friend struct QShaderDescriptionPrivate; +#ifndef QT_NO_DEBUG_STREAM + friend Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription &); +#endif +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QShaderDescription::ImageFlags) + +#ifndef QT_NO_DEBUG_STREAM +Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription &); +Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::InOutVariable &); +Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::BlockVariable &); +Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::UniformBlock &); +Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::PushConstantBlock &); +Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::StorageBlock &); +#endif + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/rhi/qshaderdescription_p_p.h b/src/gui/rhi/qshaderdescription_p_p.h new file mode 100644 index 0000000000..dbe68d1060 --- /dev/null +++ b/src/gui/rhi/qshaderdescription_p_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Gui module +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSHADERDESCRIPTION_P_H +#define QSHADERDESCRIPTION_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qshaderdescription_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +struct Q_GUI_EXPORT QShaderDescriptionPrivate +{ + QShaderDescriptionPrivate() + : ref(1) + { + } + + QShaderDescriptionPrivate(const QShaderDescriptionPrivate *other) + : ref(1), + inVars(other->inVars), + outVars(other->outVars), + uniformBlocks(other->uniformBlocks), + pushConstantBlocks(other->pushConstantBlocks), + storageBlocks(other->storageBlocks), + combinedImageSamplers(other->combinedImageSamplers), + storageImages(other->storageImages) + { + } + + static QShaderDescriptionPrivate *get(QShaderDescription *desc) { return desc->d; } + static const QShaderDescriptionPrivate *get(const QShaderDescription *desc) { return desc->d; } + + QJsonDocument makeDoc(); + void loadDoc(const QJsonDocument &doc); + + QAtomicInt ref; + QVector inVars; + QVector outVars; + QVector uniformBlocks; + QVector pushConstantBlocks; + QVector storageBlocks; + QVector combinedImageSamplers; + QVector storageImages; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/rhi/rhi.pri b/src/gui/rhi/rhi.pri new file mode 100644 index 0000000000..d8607f1024 --- /dev/null +++ b/src/gui/rhi/rhi.pri @@ -0,0 +1,57 @@ +HEADERS += \ + rhi/qrhi_p.h \ + rhi/qrhi_p_p.h \ + rhi/qrhiprofiler_p.h \ + rhi/qrhiprofiler_p_p.h \ + rhi/qrhinull_p.h \ + rhi/qrhinull_p_p.h \ + rhi/qshader_p.h \ + rhi/qshader_p_p.h \ + rhi/qshaderdescription_p.h \ + rhi/qshaderdescription_p_p.h + +SOURCES += \ + rhi/qrhi.cpp \ + rhi/qrhiprofiler.cpp \ + rhi/qrhinull.cpp \ + rhi/qshaderdescription.cpp \ + rhi/qshader.cpp + +qtConfig(opengl) { + HEADERS += \ + rhi/qrhigles2_p.h \ + rhi/qrhigles2_p_p.h + SOURCES += \ + rhi/qrhigles2.cpp +} + +qtConfig(vulkan) { + HEADERS += \ + rhi/qrhivulkan_p.h \ + rhi/qrhivulkan_p_p.h + SOURCES += \ + rhi/qrhivulkan.cpp +} + +win32 { + HEADERS += \ + rhi/qrhid3d11_p.h \ + rhi/qrhid3d11_p_p.h + SOURCES += \ + rhi/qrhid3d11.cpp + + LIBS += -ld3d11 -ldxgi -ldxguid -ld3dcompiler +} + +# darwin { +macos { + HEADERS += \ + rhi/qrhimetal_p.h \ + rhi/qrhimetal_p_p.h + SOURCES += \ + rhi/qrhimetal.mm + + LIBS += -framework AppKit -framework Metal +} + +include($$PWD/../../3rdparty/VulkanMemoryAllocator.pri) -- cgit v1.2.3 From 7f7eaf1cad2681e361d365ce3c602105338603d1 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 12 Jun 2019 12:40:08 +0200 Subject: Add VulkanMemoryAllocator The Vulkan backend of QRhi relies on vk_mem_alloc.h from AMD in order to get a stable, performant, and tested GPU memory allocator. It is not unthinkable that we will move away from this in the future, especially considering that a potential future D3D12 backend may need a similar solution, but until then this will do. Change-Id: I198a898f216d0795b4bf339ccea80b0cd2efbabc Reviewed-by: Lars Knoll --- src/3rdparty/VulkanMemoryAllocator.pri | 1 + src/3rdparty/VulkanMemoryAllocator/LICENSE.txt | 19 + .../patches/0001-Avoid-compiler-warnings.patch | 402 + .../patches/0002-Fix-gcc8-warning.patch | 14 + .../VulkanMemoryAllocator/qt_attribution.json | 16 + src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h | 16790 +++++++++++++++++++ 6 files changed, 17242 insertions(+) create mode 100644 src/3rdparty/VulkanMemoryAllocator.pri create mode 100644 src/3rdparty/VulkanMemoryAllocator/LICENSE.txt create mode 100644 src/3rdparty/VulkanMemoryAllocator/patches/0001-Avoid-compiler-warnings.patch create mode 100644 src/3rdparty/VulkanMemoryAllocator/patches/0002-Fix-gcc8-warning.patch create mode 100644 src/3rdparty/VulkanMemoryAllocator/qt_attribution.json create mode 100644 src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h (limited to 'src') diff --git a/src/3rdparty/VulkanMemoryAllocator.pri b/src/3rdparty/VulkanMemoryAllocator.pri new file mode 100644 index 0000000000..7466200dfc --- /dev/null +++ b/src/3rdparty/VulkanMemoryAllocator.pri @@ -0,0 +1 @@ +INCLUDEPATH += $$PWD/VulkanMemoryAllocator diff --git a/src/3rdparty/VulkanMemoryAllocator/LICENSE.txt b/src/3rdparty/VulkanMemoryAllocator/LICENSE.txt new file mode 100644 index 0000000000..dbfe253391 --- /dev/null +++ b/src/3rdparty/VulkanMemoryAllocator/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2017-2018 Advanced Micro Devices, Inc. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/src/3rdparty/VulkanMemoryAllocator/patches/0001-Avoid-compiler-warnings.patch b/src/3rdparty/VulkanMemoryAllocator/patches/0001-Avoid-compiler-warnings.patch new file mode 100644 index 0000000000..f459db6c7a --- /dev/null +++ b/src/3rdparty/VulkanMemoryAllocator/patches/0001-Avoid-compiler-warnings.patch @@ -0,0 +1,402 @@ +diff --git a/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h b/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h +index a2f7a1b..fbe6f9e 100644 +--- a/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h ++++ b/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h +@@ -3661,7 +3661,7 @@ static void VmaWriteMagicValue(void* pData, VkDeviceSize offset) + { + uint32_t* pDst = (uint32_t*)((char*)pData + offset); + const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t); +- for(size_t i = 0; i < numberCount; ++i, ++pDst) ++ for(size_t i = 0; i != numberCount; ++i, ++pDst) + { + *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE; + } +@@ -3671,7 +3671,7 @@ static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset) + { + const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset); + const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t); +- for(size_t i = 0; i < numberCount; ++i, ++pSrc) ++ for(size_t i = 0; i != numberCount; ++i, ++pSrc) + { + if(*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE) + { +@@ -3866,7 +3866,7 @@ public: + template VmaStlAllocator(const VmaStlAllocator& src) : m_pCallbacks(src.m_pCallbacks) { } + + T* allocate(size_t n) { return VmaAllocateArray(m_pCallbacks, n); } +- void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); } ++ void deallocate(T* p, size_t /*n*/) { VmaFree(m_pCallbacks, p); } + + template + bool operator==(const VmaStlAllocator& rhs) const +@@ -5214,7 +5214,7 @@ public: + virtual void FreeAtOffset(VkDeviceSize offset) = 0; + + // Tries to resize (grow or shrink) space for given allocation, in place. +- virtual bool ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize) { return false; } ++ virtual bool ResizeAllocation(const VmaAllocation /*alloc*/, VkDeviceSize /*newSize*/) { return false; } + + protected: + const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; } +@@ -5574,7 +5574,7 @@ public: + + virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount); + +- virtual VkResult CheckCorruption(const void* pBlockData) { return VK_ERROR_FEATURE_NOT_PRESENT; } ++ virtual VkResult CheckCorruption(const void* /*pBlockData*/) { return VK_ERROR_FEATURE_NOT_PRESENT; } + + virtual void Alloc( + const VmaAllocationRequest& request, +@@ -6133,7 +6133,7 @@ public: + bool overlappingMoveSupported); + virtual ~VmaDefragmentationAlgorithm_Fast(); + +- virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) { ++m_AllocationCount; } ++ virtual void AddAllocation(VmaAllocation /*hAlloc*/, VkBool32* /*pChanged*/) { ++m_AllocationCount; } + virtual void AddAll() { m_AllAllocations = true; } + + virtual VkResult Defragment( +@@ -6318,7 +6318,7 @@ private: + // Redundant, for convenience not to fetch from m_hCustomPool->m_BlockVector or m_hAllocator->m_pBlockVectors. + VmaBlockVector* const m_pBlockVector; + const uint32_t m_CurrFrameIndex; +- const uint32_t m_AlgorithmFlags; ++ /*const uint32_t m_AlgorithmFlags;*/ + // Owner of this object. + VmaDefragmentationAlgorithm* m_pAlgorithm; + +@@ -7073,6 +7073,7 @@ void VmaJsonWriter::BeginValue(bool isString) + if(currItem.type == COLLECTION_TYPE_OBJECT && + currItem.valueCount % 2 == 0) + { ++ (void) isString; + VMA_ASSERT(isString); + } + +@@ -7660,7 +7661,9 @@ bool VmaBlockMetadata_Generic::Validate() const + } + + // Margin required between allocations - every free space must be at least that large. ++#if VMA_DEBUG_MARGIN + VMA_VALIDATE(subAlloc.size >= VMA_DEBUG_MARGIN); ++#endif + } + else + { +@@ -7806,6 +7809,7 @@ bool VmaBlockMetadata_Generic::CreateAllocationRequest( + { + VMA_ASSERT(allocSize > 0); + VMA_ASSERT(!upperAddress); ++ (void) upperAddress; + VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE); + VMA_ASSERT(pAllocationRequest != VMA_NULL); + VMA_HEAVY_ASSERT(Validate()); +@@ -8033,6 +8037,7 @@ void VmaBlockMetadata_Generic::Alloc( + VmaAllocation hAllocation) + { + VMA_ASSERT(!upperAddress); ++ (void) upperAddress; + VMA_ASSERT(request.item != m_Suballocations.end()); + VmaSuballocation& suballoc = *request.item; + // Given suballocation is a free block. +@@ -9609,7 +9614,7 @@ bool VmaBlockMetadata_Linear::CreateAllocationRequest( + bool upperAddress, + VmaSuballocationType allocType, + bool canMakeOtherLost, +- uint32_t strategy, ++ uint32_t /*strategy*/, + VmaAllocationRequest* pAllocationRequest) + { + VMA_ASSERT(allocSize > 0); +@@ -9651,10 +9656,12 @@ bool VmaBlockMetadata_Linear::CreateAllocationRequest( + // Apply VMA_DEBUG_MARGIN at the end. + if(VMA_DEBUG_MARGIN > 0) + { ++#if VMA_DEBUG_MARGIN + if(resultOffset < VMA_DEBUG_MARGIN) + { + return false; + } ++#endif + resultOffset -= VMA_DEBUG_MARGIN; + } + +@@ -10542,18 +10549,19 @@ void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json) const + #endif // #if VMA_STATS_STRING_ENABLED + + bool VmaBlockMetadata_Buddy::CreateAllocationRequest( +- uint32_t currentFrameIndex, +- uint32_t frameInUseCount, ++ uint32_t /*currentFrameIndex*/, ++ uint32_t /*frameInUseCount*/, + VkDeviceSize bufferImageGranularity, + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, +- bool canMakeOtherLost, +- uint32_t strategy, ++ bool /*canMakeOtherLost*/, ++ uint32_t /*strategy*/, + VmaAllocationRequest* pAllocationRequest) + { + VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm."); ++ (void) upperAddress; + + // Simple way to respect bufferImageGranularity. May be optimized some day. + // Whenever it might be an OPTIMAL image... +@@ -10593,8 +10601,8 @@ bool VmaBlockMetadata_Buddy::CreateAllocationRequest( + } + + bool VmaBlockMetadata_Buddy::MakeRequestedAllocationsLost( +- uint32_t currentFrameIndex, +- uint32_t frameInUseCount, ++ uint32_t /*currentFrameIndex*/, ++ uint32_t /*frameInUseCount*/, + VmaAllocationRequest* pAllocationRequest) + { + /* +@@ -10604,7 +10612,7 @@ bool VmaBlockMetadata_Buddy::MakeRequestedAllocationsLost( + return pAllocationRequest->itemsToMakeLostCount == 0; + } + +-uint32_t VmaBlockMetadata_Buddy::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) ++uint32_t VmaBlockMetadata_Buddy::MakeAllocationsLost(uint32_t /*currentFrameIndex*/, uint32_t /*frameInUseCount*/) + { + /* + Lost allocations are not supported in buddy allocator at the moment. +@@ -10615,9 +10623,9 @@ uint32_t VmaBlockMetadata_Buddy::MakeAllocationsLost(uint32_t currentFrameIndex, + + void VmaBlockMetadata_Buddy::Alloc( + const VmaAllocationRequest& request, +- VmaSuballocationType type, ++ VmaSuballocationType /*type*/, + VkDeviceSize allocSize, +- bool upperAddress, ++ bool /*upperAddress*/, + VmaAllocation hAllocation) + { + const uint32_t targetLevel = AllocSizeToLevel(allocSize); +@@ -10941,7 +10949,7 @@ void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, con + //////////////////////////////////////////////////////////////////////////////// + // class VmaDeviceMemoryBlock + +-VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator) : ++VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator /*hAllocator*/) : + m_pMetadata(VMA_NULL), + m_MemoryTypeIndex(UINT32_MAX), + m_Id(0), +@@ -11691,6 +11699,7 @@ VkResult VmaBlockVector::AllocatePage( + if(IsCorruptionDetectionEnabled()) + { + VkResult res = pBestRequestBlock->WriteMagicValueAroundAllocation(m_hAllocator, bestRequest.offset, size); ++ (void) res; + VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value."); + } + return VK_SUCCESS; +@@ -11729,6 +11738,7 @@ void VmaBlockVector::Free( + if(IsCorruptionDetectionEnabled()) + { + VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize()); ++ (void) res; + VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value."); + } + +@@ -11894,6 +11904,7 @@ VkResult VmaBlockVector::AllocateFromBlock( + if(IsCorruptionDetectionEnabled()) + { + VkResult res = pBlock->WriteMagicValueAroundAllocation(m_hAllocator, currRequest.offset, size); ++ (void) res; + VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value."); + } + return VK_SUCCESS; +@@ -11903,7 +11914,8 @@ VkResult VmaBlockVector::AllocateFromBlock( + + VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex) + { +- VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; ++ VkMemoryAllocateInfo allocInfo = {}; ++ allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocInfo.memoryTypeIndex = m_MemoryTypeIndex; + allocInfo.allocationSize = blockSize; + VkDeviceMemory mem = VK_NULL_HANDLE; +@@ -11991,7 +12003,8 @@ void VmaBlockVector::ApplyDefragmentationMovesCpu( + if(pDefragCtx->res == VK_SUCCESS) + { + const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize; +- VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE }; ++ VkMappedMemoryRange memRange = {}; ++ memRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + + for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex) + { +@@ -12076,7 +12089,8 @@ void VmaBlockVector::ApplyDefragmentationMovesGpu( + + // Go over all blocks. Create and bind buffer for whole block if necessary. + { +- VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; ++ VkBufferCreateInfo bufCreateInfo = {}; ++ bufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT; + +@@ -12101,8 +12115,9 @@ void VmaBlockVector::ApplyDefragmentationMovesGpu( + // Go over all moves. Post data transfer commands to command buffer. + if(pDefragCtx->res == VK_SUCCESS) + { +- const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize; +- VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE }; ++ /*const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize; ++ VkMappedMemoryRange memRange = {}; ++ memRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;*/ + + for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex) + { +@@ -12435,10 +12450,10 @@ VmaDefragmentationAlgorithm_Generic::VmaDefragmentationAlgorithm_Generic( + VmaAllocator hAllocator, + VmaBlockVector* pBlockVector, + uint32_t currentFrameIndex, +- bool overlappingMoveSupported) : ++ bool /*overlappingMoveSupported*/) : + VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex), +- m_AllAllocations(false), + m_AllocationCount(0), ++ m_AllAllocations(false), + m_BytesMoved(0), + m_AllocationsMoved(0), + m_Blocks(VmaStlAllocator(hAllocator->GetAllocationCallbacks())) +@@ -12813,7 +12828,7 @@ VkResult VmaDefragmentationAlgorithm_Fast::Defragment( + size_t freeSpaceOrigBlockIndex = m_BlockInfos[freeSpaceInfoIndex].origBlockIndex; + VmaDeviceMemoryBlock* pFreeSpaceBlock = m_pBlockVector->GetBlock(freeSpaceOrigBlockIndex); + VmaBlockMetadata_Generic* pFreeSpaceMetadata = (VmaBlockMetadata_Generic*)pFreeSpaceBlock->m_pMetadata; +- VkDeviceSize freeSpaceBlockSize = pFreeSpaceMetadata->GetSize(); ++ /*VkDeviceSize freeSpaceBlockSize = pFreeSpaceMetadata->GetSize();*/ + + // Same block + if(freeSpaceInfoIndex == srcBlockInfoIndex) +@@ -13098,7 +13113,7 @@ VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext( + VmaPool hCustomPool, + VmaBlockVector* pBlockVector, + uint32_t currFrameIndex, +- uint32_t algorithmFlags) : ++ uint32_t /*algorithmFlags*/) : + res(VK_SUCCESS), + mutexLocked(false), + blockContexts(VmaStlAllocator(hAllocator->GetAllocationCallbacks())), +@@ -13106,7 +13121,7 @@ VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext( + m_hCustomPool(hCustomPool), + m_pBlockVector(pBlockVector), + m_CurrFrameIndex(currFrameIndex), +- m_AlgorithmFlags(algorithmFlags), ++ /*m_AlgorithmFlags(algorithmFlags),*/ + m_pAlgorithm(VMA_NULL), + m_Allocations(VmaStlAllocator(hAllocator->GetAllocationCallbacks())), + m_AllAllocations(false) +@@ -14311,19 +14326,21 @@ VkResult VmaAllocator_T::AllocateDedicatedMemory( + bool map, + bool isUserDataString, + void* pUserData, +- VkBuffer dedicatedBuffer, +- VkImage dedicatedImage, ++ VkBuffer /*dedicatedBuffer*/, ++ VkImage /*dedicatedImage*/, + size_t allocationCount, + VmaAllocation* pAllocations) + { + VMA_ASSERT(allocationCount > 0 && pAllocations); + +- VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; ++ VkMemoryAllocateInfo allocInfo = {}; ++ allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocInfo.memoryTypeIndex = memTypeIndex; + allocInfo.allocationSize = size; + + #if VMA_DEDICATED_ALLOCATION +- VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR }; ++ VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = {}; ++ dedicatedAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR; + if(m_UseKhrDedicatedAllocation) + { + if(dedicatedBuffer != VK_NULL_HANDLE) +@@ -14341,7 +14358,7 @@ VkResult VmaAllocator_T::AllocateDedicatedMemory( + #endif // #if VMA_DEDICATED_ALLOCATION + + size_t allocIndex; +- VkResult res; ++ VkResult res = VK_SUCCESS; + for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex) + { + res = AllocateDedicatedMemoryPage( +@@ -14460,12 +14477,15 @@ void VmaAllocator_T::GetBufferMemoryRequirements( + #if VMA_DEDICATED_ALLOCATION + if(m_UseKhrDedicatedAllocation) + { +- VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR }; ++ VkBufferMemoryRequirementsInfo2KHR memReqInfo = {}; ++ memReqInfo.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR; + memReqInfo.buffer = hBuffer; + +- VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR }; ++ VkMemoryDedicatedRequirementsKHR memDedicatedReq = {}; ++ memDedicatedReq.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR; + +- VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR }; ++ VkMemoryRequirements2KHR memReq2 = {}; ++ memReq2.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR; + memReq2.pNext = &memDedicatedReq; + + (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2); +@@ -14492,12 +14512,15 @@ void VmaAllocator_T::GetImageMemoryRequirements( + #if VMA_DEDICATED_ALLOCATION + if(m_UseKhrDedicatedAllocation) + { +- VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR }; ++ VkImageMemoryRequirementsInfo2KHR memReqInfo = {}; ++ memReqInfo.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR; + memReqInfo.image = hImage; + +- VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR }; ++ VkMemoryDedicatedRequirementsKHR memDedicatedReq = {}; ++ memDedicatedReq.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR; + +- VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR }; ++ VkMemoryRequirements2KHR memReq2 = {}; ++ memReq2.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR; + memReq2.pNext = &memDedicatedReq; + + (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2); +@@ -14734,7 +14757,7 @@ VkResult VmaAllocator_T::ResizeAllocation( + } + else + { +- return VK_ERROR_OUT_OF_POOL_MEMORY; ++ return VkResult(-1000069000); // VK_ERROR_OUT_OF_POOL_MEMORY + } + default: + VMA_ASSERT(0); +@@ -15000,6 +15023,7 @@ void VmaAllocator_T::DestroyPool(VmaPool pool) + { + VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex); + bool success = VmaVectorRemoveSorted(m_Pools, pool); ++ (void) success; + VMA_ASSERT(success && "Pool not found in Allocator."); + } + +@@ -15248,7 +15272,8 @@ void VmaAllocator_T::FlushOrInvalidateAllocation( + + const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize; + +- VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE }; ++ VkMappedMemoryRange memRange = {}; ++ memRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + memRange.memory = hAllocation->GetMemory(); + + switch(hAllocation->GetType()) +@@ -15321,6 +15346,7 @@ void VmaAllocator_T::FreeDedicatedMemory(VmaAllocation allocation) + AllocationVectorType* const pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex]; + VMA_ASSERT(pDedicatedAllocations); + bool success = VmaVectorRemoveSorted(*pDedicatedAllocations, allocation); ++ (void) success; + VMA_ASSERT(success); + } + diff --git a/src/3rdparty/VulkanMemoryAllocator/patches/0002-Fix-gcc8-warning.patch b/src/3rdparty/VulkanMemoryAllocator/patches/0002-Fix-gcc8-warning.patch new file mode 100644 index 0000000000..57a2f1a0f1 --- /dev/null +++ b/src/3rdparty/VulkanMemoryAllocator/patches/0002-Fix-gcc8-warning.patch @@ -0,0 +1,14 @@ +diff --git a/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h b/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h +index fbe6f9e3e8..f043bdc289 100644 +--- a/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h ++++ b/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h +@@ -12074,7 +12074,8 @@ void VmaBlockVector::ApplyDefragmentationMovesGpu( + const size_t blockCount = m_Blocks.size(); + + pDefragCtx->blockContexts.resize(blockCount); +- memset(pDefragCtx->blockContexts.data(), 0, blockCount * sizeof(VmaBlockDefragmentationContext)); ++ for (size_t i = 0; i < blockCount; ++i) ++ pDefragCtx->blockContexts[i] = VmaBlockDefragmentationContext(); + + // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED. + const size_t moveCount = moves.size(); diff --git a/src/3rdparty/VulkanMemoryAllocator/qt_attribution.json b/src/3rdparty/VulkanMemoryAllocator/qt_attribution.json new file mode 100644 index 0000000000..2548856ca7 --- /dev/null +++ b/src/3rdparty/VulkanMemoryAllocator/qt_attribution.json @@ -0,0 +1,16 @@ +[ + { + "Id": "VulkanMemoryAllocator", + "Name": "Vulkan Memory Allocator", + "QDocModule": "qtrhi", + "Description": "Vulkan Memory Allocator", + "QtUsage": "Memory management for the Vulkan backend of QRhi.", + + "Homepage": "https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator", + "Version": "2.2.0", + "License": "MIT License", + "LicenseId": "MIT", + "LicenseFile": "LICENSE.txt", + "Copyright": "Copyright (c) 2017-2018 Advanced Micro Devices, Inc. All rights reserved." + } +] diff --git a/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h b/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h new file mode 100644 index 0000000000..f043bdc289 --- /dev/null +++ b/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h @@ -0,0 +1,16790 @@ +// +// Copyright (c) 2017-2018 Advanced Micro Devices, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#ifndef AMD_VULKAN_MEMORY_ALLOCATOR_H +#define AMD_VULKAN_MEMORY_ALLOCATOR_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** \mainpage Vulkan Memory Allocator + +Version 2.2.0 (2018-12-13) + +Copyright (c) 2017-2018 Advanced Micro Devices, Inc. All rights reserved. \n +License: MIT + +Documentation of all members: vk_mem_alloc.h + +\section main_table_of_contents Table of contents + +- User guide + - \subpage quick_start + - [Project setup](@ref quick_start_project_setup) + - [Initialization](@ref quick_start_initialization) + - [Resource allocation](@ref quick_start_resource_allocation) + - \subpage choosing_memory_type + - [Usage](@ref choosing_memory_type_usage) + - [Required and preferred flags](@ref choosing_memory_type_required_preferred_flags) + - [Explicit memory types](@ref choosing_memory_type_explicit_memory_types) + - [Custom memory pools](@ref choosing_memory_type_custom_memory_pools) + - \subpage memory_mapping + - [Mapping functions](@ref memory_mapping_mapping_functions) + - [Persistently mapped memory](@ref memory_mapping_persistently_mapped_memory) + - [Cache control](@ref memory_mapping_cache_control) + - [Finding out if memory is mappable](@ref memory_mapping_finding_if_memory_mappable) + - \subpage custom_memory_pools + - [Choosing memory type index](@ref custom_memory_pools_MemTypeIndex) + - [Linear allocation algorithm](@ref linear_algorithm) + - [Free-at-once](@ref linear_algorithm_free_at_once) + - [Stack](@ref linear_algorithm_stack) + - [Double stack](@ref linear_algorithm_double_stack) + - [Ring buffer](@ref linear_algorithm_ring_buffer) + - [Buddy allocation algorithm](@ref buddy_algorithm) + - \subpage defragmentation + - [Defragmenting CPU memory](@ref defragmentation_cpu) + - [Defragmenting GPU memory](@ref defragmentation_gpu) + - [Additional notes](@ref defragmentation_additional_notes) + - [Writing custom allocation algorithm](@ref defragmentation_custom_algorithm) + - \subpage lost_allocations + - \subpage statistics + - [Numeric statistics](@ref statistics_numeric_statistics) + - [JSON dump](@ref statistics_json_dump) + - \subpage allocation_annotation + - [Allocation user data](@ref allocation_user_data) + - [Allocation names](@ref allocation_names) + - \subpage debugging_memory_usage + - [Memory initialization](@ref debugging_memory_usage_initialization) + - [Margins](@ref debugging_memory_usage_margins) + - [Corruption detection](@ref debugging_memory_usage_corruption_detection) + - \subpage record_and_replay +- \subpage usage_patterns + - [Simple patterns](@ref usage_patterns_simple) + - [Advanced patterns](@ref usage_patterns_advanced) +- \subpage configuration + - [Pointers to Vulkan functions](@ref config_Vulkan_functions) + - [Custom host memory allocator](@ref custom_memory_allocator) + - [Device memory allocation callbacks](@ref allocation_callbacks) + - [Device heap memory limit](@ref heap_memory_limit) + - \subpage vk_khr_dedicated_allocation +- \subpage general_considerations + - [Thread safety](@ref general_considerations_thread_safety) + - [Validation layer warnings](@ref general_considerations_validation_layer_warnings) + - [Allocation algorithm](@ref general_considerations_allocation_algorithm) + - [Features not supported](@ref general_considerations_features_not_supported) + +\section main_see_also See also + +- [Product page on GPUOpen](https://gpuopen.com/gaming-product/vulkan-memory-allocator/) +- [Source repository on GitHub](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator) + + + + +\page quick_start Quick start + +\section quick_start_project_setup Project setup + +Vulkan Memory Allocator comes in form of a single header file. +You don't need to build it as a separate library project. +You can add this file directly to your project and submit it to code repository next to your other source files. + +"Single header" doesn't mean that everything is contained in C/C++ declarations, +like it tends to be in case of inline functions or C++ templates. +It means that implementation is bundled with interface in a single file and needs to be extracted using preprocessor macro. +If you don't do it properly, you will get linker errors. + +To do it properly: + +-# Include "vk_mem_alloc.h" file in each CPP file where you want to use the library. + This includes declarations of all members of the library. +-# In exacly one CPP file define following macro before this include. + It enables also internal definitions. + +\code +#define VMA_IMPLEMENTATION +#include "vk_mem_alloc.h" +\endcode + +It may be a good idea to create dedicated CPP file just for this purpose. + +Note on language: This library is written in C++, but has C-compatible interface. +Thus you can include and use vk_mem_alloc.h in C or C++ code, but full +implementation with `VMA_IMPLEMENTATION` macro must be compiled as C++, NOT as C. + +Please note that this library includes header ``, which in turn +includes `` on Windows. If you need some specific macros defined +before including these headers (like `WIN32_LEAN_AND_MEAN` or +`WINVER` for Windows, `VK_USE_PLATFORM_WIN32_KHR` for Vulkan), you must define +them before every `#include` of this library. + + +\section quick_start_initialization Initialization + +At program startup: + +-# Initialize Vulkan to have `VkPhysicalDevice` and `VkDevice` object. +-# Fill VmaAllocatorCreateInfo structure and create #VmaAllocator object by + calling vmaCreateAllocator(). + +\code +VmaAllocatorCreateInfo allocatorInfo = {}; +allocatorInfo.physicalDevice = physicalDevice; +allocatorInfo.device = device; + +VmaAllocator allocator; +vmaCreateAllocator(&allocatorInfo, &allocator); +\endcode + +\section quick_start_resource_allocation Resource allocation + +When you want to create a buffer or image: + +-# Fill `VkBufferCreateInfo` / `VkImageCreateInfo` structure. +-# Fill VmaAllocationCreateInfo structure. +-# Call vmaCreateBuffer() / vmaCreateImage() to get `VkBuffer`/`VkImage` with memory + already allocated and bound to it. + +\code +VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufferInfo.size = 65536; +bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + +VmaAllocationCreateInfo allocInfo = {}; +allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; + +VkBuffer buffer; +VmaAllocation allocation; +vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr); +\endcode + +Don't forget to destroy your objects when no longer needed: + +\code +vmaDestroyBuffer(allocator, buffer, allocation); +vmaDestroyAllocator(allocator); +\endcode + + +\page choosing_memory_type Choosing memory type + +Physical devices in Vulkan support various combinations of memory heaps and +types. Help with choosing correct and optimal memory type for your specific +resource is one of the key features of this library. You can use it by filling +appropriate members of VmaAllocationCreateInfo structure, as described below. +You can also combine multiple methods. + +-# If you just want to find memory type index that meets your requirements, you + can use function vmaFindMemoryTypeIndex(). +-# If you want to allocate a region of device memory without association with any + specific image or buffer, you can use function vmaAllocateMemory(). Usage of + this function is not recommended and usually not needed. +-# If you already have a buffer or an image created, you want to allocate memory + for it and then you will bind it yourself, you can use function + vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(). + For binding you should use functions: vmaBindBufferMemory(), vmaBindImageMemory(). +-# If you want to create a buffer or an image, allocate memory for it and bind + them together, all in one call, you can use function vmaCreateBuffer(), + vmaCreateImage(). This is the recommended way to use this library. + +When using 3. or 4., the library internally queries Vulkan for memory types +supported for that buffer or image (function `vkGetBufferMemoryRequirements()`) +and uses only one of these types. + +If no memory type can be found that meets all the requirements, these functions +return `VK_ERROR_FEATURE_NOT_PRESENT`. + +You can leave VmaAllocationCreateInfo structure completely filled with zeros. +It means no requirements are specified for memory type. +It is valid, although not very useful. + +\section choosing_memory_type_usage Usage + +The easiest way to specify memory requirements is to fill member +VmaAllocationCreateInfo::usage using one of the values of enum #VmaMemoryUsage. +It defines high level, common usage types. +For more details, see description of this enum. + +For example, if you want to create a uniform buffer that will be filled using +transfer only once or infrequently and used for rendering every frame, you can +do it using following code: + +\code +VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufferInfo.size = 65536; +bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + +VmaAllocationCreateInfo allocInfo = {}; +allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; + +VkBuffer buffer; +VmaAllocation allocation; +vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr); +\endcode + +\section choosing_memory_type_required_preferred_flags Required and preferred flags + +You can specify more detailed requirements by filling members +VmaAllocationCreateInfo::requiredFlags and VmaAllocationCreateInfo::preferredFlags +with a combination of bits from enum `VkMemoryPropertyFlags`. For example, +if you want to create a buffer that will be persistently mapped on host (so it +must be `HOST_VISIBLE`) and preferably will also be `HOST_COHERENT` and `HOST_CACHED`, +use following code: + +\code +VmaAllocationCreateInfo allocInfo = {}; +allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; +allocInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT; +allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT; + +VkBuffer buffer; +VmaAllocation allocation; +vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr); +\endcode + +A memory type is chosen that has all the required flags and as many preferred +flags set as possible. + +If you use VmaAllocationCreateInfo::usage, it is just internally converted to +a set of required and preferred flags. + +\section choosing_memory_type_explicit_memory_types Explicit memory types + +If you inspected memory types available on the physical device and you have +a preference for memory types that you want to use, you can fill member +VmaAllocationCreateInfo::memoryTypeBits. It is a bit mask, where each bit set +means that a memory type with that index is allowed to be used for the +allocation. Special value 0, just like `UINT32_MAX`, means there are no +restrictions to memory type index. + +Please note that this member is NOT just a memory type index. +Still you can use it to choose just one, specific memory type. +For example, if you already determined that your buffer should be created in +memory type 2, use following code: + +\code +uint32_t memoryTypeIndex = 2; + +VmaAllocationCreateInfo allocInfo = {}; +allocInfo.memoryTypeBits = 1u << memoryTypeIndex; + +VkBuffer buffer; +VmaAllocation allocation; +vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr); +\endcode + +\section choosing_memory_type_custom_memory_pools Custom memory pools + +If you allocate from custom memory pool, all the ways of specifying memory +requirements described above are not applicable and the aforementioned members +of VmaAllocationCreateInfo structure are ignored. Memory type is selected +explicitly when creating the pool and then used to make all the allocations from +that pool. For further details, see \ref custom_memory_pools. + + +\page memory_mapping Memory mapping + +To "map memory" in Vulkan means to obtain a CPU pointer to `VkDeviceMemory`, +to be able to read from it or write to it in CPU code. +Mapping is possible only of memory allocated from a memory type that has +`VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag. +Functions `vkMapMemory()`, `vkUnmapMemory()` are designed for this purpose. +You can use them directly with memory allocated by this library, +but it is not recommended because of following issue: +Mapping the same `VkDeviceMemory` block multiple times is illegal - only one mapping at a time is allowed. +This includes mapping disjoint regions. Mapping is not reference-counted internally by Vulkan. +Because of this, Vulkan Memory Allocator provides following facilities: + +\section memory_mapping_mapping_functions Mapping functions + +The library provides following functions for mapping of a specific #VmaAllocation: vmaMapMemory(), vmaUnmapMemory(). +They are safer and more convenient to use than standard Vulkan functions. +You can map an allocation multiple times simultaneously - mapping is reference-counted internally. +You can also map different allocations simultaneously regardless of whether they use the same `VkDeviceMemory` block. +The way it's implemented is that the library always maps entire memory block, not just region of the allocation. +For further details, see description of vmaMapMemory() function. +Example: + +\code +// Having these objects initialized: + +struct ConstantBuffer +{ + ... +}; +ConstantBuffer constantBufferData; + +VmaAllocator allocator; +VkBuffer constantBuffer; +VmaAllocation constantBufferAllocation; + +// You can map and fill your buffer using following code: + +void* mappedData; +vmaMapMemory(allocator, constantBufferAllocation, &mappedData); +memcpy(mappedData, &constantBufferData, sizeof(constantBufferData)); +vmaUnmapMemory(allocator, constantBufferAllocation); +\endcode + +When mapping, you may see a warning from Vulkan validation layer similar to this one: + +Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used. + +It happens because the library maps entire `VkDeviceMemory` block, where different +types of images and buffers may end up together, especially on GPUs with unified memory like Intel. +You can safely ignore it if you are sure you access only memory of the intended +object that you wanted to map. + + +\section memory_mapping_persistently_mapped_memory Persistently mapped memory + +Kepping your memory persistently mapped is generally OK in Vulkan. +You don't need to unmap it before using its data on the GPU. +The library provides a special feature designed for that: +Allocations made with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag set in +VmaAllocationCreateInfo::flags stay mapped all the time, +so you can just access CPU pointer to it any time +without a need to call any "map" or "unmap" function. +Example: + +\code +VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufCreateInfo.size = sizeof(ConstantBuffer); +bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY; +allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT; + +VkBuffer buf; +VmaAllocation alloc; +VmaAllocationInfo allocInfo; +vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo); + +// Buffer is already mapped. You can access its memory. +memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData)); +\endcode + +There are some exceptions though, when you should consider mapping memory only for a short period of time: + +- When operating system is Windows 7 or 8.x (Windows 10 is not affected because it uses WDDM2), + device is discrete AMD GPU, + and memory type is the special 256 MiB pool of `DEVICE_LOCAL + HOST_VISIBLE` memory + (selected when you use #VMA_MEMORY_USAGE_CPU_TO_GPU), + then whenever a memory block allocated from this memory type stays mapped + for the time of any call to `vkQueueSubmit()` or `vkQueuePresentKHR()`, this + block is migrated by WDDM to system RAM, which degrades performance. It doesn't + matter if that particular memory block is actually used by the command buffer + being submitted. +- On Mac/MoltenVK there is a known bug - [Issue #175](https://github.com/KhronosGroup/MoltenVK/issues/175) + which requires unmapping before GPU can see updated texture. +- Keeping many large memory blocks mapped may impact performance or stability of some debugging tools. + +\section memory_mapping_cache_control Cache control + +Memory in Vulkan doesn't need to be unmapped before using it on GPU, +but unless a memory types has `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT` flag set, +you need to manually invalidate cache before reading of mapped pointer +and flush cache after writing to mapped pointer. +Vulkan provides following functions for this purpose `vkFlushMappedMemoryRanges()`, +`vkInvalidateMappedMemoryRanges()`, but this library provides more convenient +functions that refer to given allocation object: vmaFlushAllocation(), +vmaInvalidateAllocation(). + +Regions of memory specified for flush/invalidate must be aligned to +`VkPhysicalDeviceLimits::nonCoherentAtomSize`. This is automatically ensured by the library. +In any memory type that is `HOST_VISIBLE` but not `HOST_COHERENT`, all allocations +within blocks are aligned to this value, so their offsets are always multiply of +`nonCoherentAtomSize` and two different allocations never share same "line" of this size. + +Please note that memory allocated with #VMA_MEMORY_USAGE_CPU_ONLY is guaranteed to be `HOST_COHERENT`. + +Also, Windows drivers from all 3 PC GPU vendors (AMD, Intel, NVIDIA) +currently provide `HOST_COHERENT` flag on all memory types that are +`HOST_VISIBLE`, so on this platform you may not need to bother. + +\section memory_mapping_finding_if_memory_mappable Finding out if memory is mappable + +It may happen that your allocation ends up in memory that is `HOST_VISIBLE` (available for mapping) +despite it wasn't explicitly requested. +For example, application may work on integrated graphics with unified memory (like Intel) or +allocation from video memory might have failed, so the library chose system memory as fallback. + +You can detect this case and map such allocation to access its memory on CPU directly, +instead of launching a transfer operation. +In order to do that: inspect `allocInfo.memoryType`, call vmaGetMemoryTypeProperties(), +and look for `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag in properties of that memory type. + +\code +VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufCreateInfo.size = sizeof(ConstantBuffer); +bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; +allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + +VkBuffer buf; +VmaAllocation alloc; +VmaAllocationInfo allocInfo; +vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo); + +VkMemoryPropertyFlags memFlags; +vmaGetMemoryTypeProperties(allocator, allocInfo.memoryType, &memFlags); +if((memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) +{ + // Allocation ended up in mappable memory. You can map it and access it directly. + void* mappedData; + vmaMapMemory(allocator, alloc, &mappedData); + memcpy(mappedData, &constantBufferData, sizeof(constantBufferData)); + vmaUnmapMemory(allocator, alloc); +} +else +{ + // Allocation ended up in non-mappable memory. + // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer. +} +\endcode + +You can even use #VMA_ALLOCATION_CREATE_MAPPED_BIT flag while creating allocations +that are not necessarily `HOST_VISIBLE` (e.g. using #VMA_MEMORY_USAGE_GPU_ONLY). +If the allocation ends up in memory type that is `HOST_VISIBLE`, it will be persistently mapped and you can use it directly. +If not, the flag is just ignored. +Example: + +\code +VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufCreateInfo.size = sizeof(ConstantBuffer); +bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; +allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT; + +VkBuffer buf; +VmaAllocation alloc; +VmaAllocationInfo allocInfo; +vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo); + +if(allocInfo.pUserData != nullptr) +{ + // Allocation ended up in mappable memory. + // It's persistently mapped. You can access it directly. + memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData)); +} +else +{ + // Allocation ended up in non-mappable memory. + // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer. +} +\endcode + + +\page custom_memory_pools Custom memory pools + +A memory pool contains a number of `VkDeviceMemory` blocks. +The library automatically creates and manages default pool for each memory type available on the device. +Default memory pool automatically grows in size. +Size of allocated blocks is also variable and managed automatically. + +You can create custom pool and allocate memory out of it. +It can be useful if you want to: + +- Keep certain kind of allocations separate from others. +- Enforce particular, fixed size of Vulkan memory blocks. +- Limit maximum amount of Vulkan memory allocated for that pool. +- Reserve minimum or fixed amount of Vulkan memory always preallocated for that pool. + +To use custom memory pools: + +-# Fill VmaPoolCreateInfo structure. +-# Call vmaCreatePool() to obtain #VmaPool handle. +-# When making an allocation, set VmaAllocationCreateInfo::pool to this handle. + You don't need to specify any other parameters of this structure, like `usage`. + +Example: + +\code +// Create a pool that can have at most 2 blocks, 128 MiB each. +VmaPoolCreateInfo poolCreateInfo = {}; +poolCreateInfo.memoryTypeIndex = ... +poolCreateInfo.blockSize = 128ull * 1024 * 1024; +poolCreateInfo.maxBlockCount = 2; + +VmaPool pool; +vmaCreatePool(allocator, &poolCreateInfo, &pool); + +// Allocate a buffer out of it. +VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufCreateInfo.size = 1024; +bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.pool = pool; + +VkBuffer buf; +VmaAllocation alloc; +VmaAllocationInfo allocInfo; +vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo); +\endcode + +You have to free all allocations made from this pool before destroying it. + +\code +vmaDestroyBuffer(allocator, buf, alloc); +vmaDestroyPool(allocator, pool); +\endcode + +\section custom_memory_pools_MemTypeIndex Choosing memory type index + +When creating a pool, you must explicitly specify memory type index. +To find the one suitable for your buffers or images, you can use helper functions +vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo(). +You need to provide structures with example parameters of buffers or images +that you are going to create in that pool. + +\code +VkBufferCreateInfo exampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +exampleBufCreateInfo.size = 1024; // Whatever. +exampleBufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; // Change if needed. + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; // Change if needed. + +uint32_t memTypeIndex; +vmaFindMemoryTypeIndexForBufferInfo(allocator, &exampleBufCreateInfo, &allocCreateInfo, &memTypeIndex); + +VmaPoolCreateInfo poolCreateInfo = {}; +poolCreateInfo.memoryTypeIndex = memTypeIndex; +// ... +\endcode + +When creating buffers/images allocated in that pool, provide following parameters: + +- `VkBufferCreateInfo`: Prefer to pass same parameters as above. + Otherwise you risk creating resources in a memory type that is not suitable for them, which may result in undefined behavior. + Using different `VK_BUFFER_USAGE_` flags may work, but you shouldn't create images in a pool intended for buffers + or the other way around. +- VmaAllocationCreateInfo: You don't need to pass same parameters. Fill only `pool` member. + Other members are ignored anyway. + +\section linear_algorithm Linear allocation algorithm + +Each Vulkan memory block managed by this library has accompanying metadata that +keeps track of used and unused regions. By default, the metadata structure and +algorithm tries to find best place for new allocations among free regions to +optimize memory usage. This way you can allocate and free objects in any order. + +![Default allocation algorithm](../gfx/Linear_allocator_1_algo_default.png) + +Sometimes there is a need to use simpler, linear allocation algorithm. You can +create custom pool that uses such algorithm by adding flag +#VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating +#VmaPool object. Then an alternative metadata management is used. It always +creates new allocations after last one and doesn't reuse free regions after +allocations freed in the middle. It results in better allocation performance and +less memory consumed by metadata. + +![Linear allocation algorithm](../gfx/Linear_allocator_2_algo_linear.png) + +With this one flag, you can create a custom pool that can be used in many ways: +free-at-once, stack, double stack, and ring buffer. See below for details. + +\subsection linear_algorithm_free_at_once Free-at-once + +In a pool that uses linear algorithm, you still need to free all the allocations +individually, e.g. by using vmaFreeMemory() or vmaDestroyBuffer(). You can free +them in any order. New allocations are always made after last one - free space +in the middle is not reused. However, when you release all the allocation and +the pool becomes empty, allocation starts from the beginning again. This way you +can use linear algorithm to speed up creation of allocations that you are going +to release all at once. + +![Free-at-once](../gfx/Linear_allocator_3_free_at_once.png) + +This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount +value that allows multiple memory blocks. + +\subsection linear_algorithm_stack Stack + +When you free an allocation that was created last, its space can be reused. +Thanks to this, if you always release allocations in the order opposite to their +creation (LIFO - Last In First Out), you can achieve behavior of a stack. + +![Stack](../gfx/Linear_allocator_4_stack.png) + +This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount +value that allows multiple memory blocks. + +\subsection linear_algorithm_double_stack Double stack + +The space reserved by a custom pool with linear algorithm may be used by two +stacks: + +- First, default one, growing up from offset 0. +- Second, "upper" one, growing down from the end towards lower offsets. + +To make allocation from upper stack, add flag #VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT +to VmaAllocationCreateInfo::flags. + +![Double stack](../gfx/Linear_allocator_7_double_stack.png) + +Double stack is available only in pools with one memory block - +VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined. + +When the two stacks' ends meet so there is not enough space between them for a +new allocation, such allocation fails with usual +`VK_ERROR_OUT_OF_DEVICE_MEMORY` error. + +\subsection linear_algorithm_ring_buffer Ring buffer + +When you free some allocations from the beginning and there is not enough free space +for a new one at the end of a pool, allocator's "cursor" wraps around to the +beginning and starts allocation there. Thanks to this, if you always release +allocations in the same order as you created them (FIFO - First In First Out), +you can achieve behavior of a ring buffer / queue. + +![Ring buffer](../gfx/Linear_allocator_5_ring_buffer.png) + +Pools with linear algorithm support [lost allocations](@ref lost_allocations) when used as ring buffer. +If there is not enough free space for a new allocation, but existing allocations +from the front of the queue can become lost, they become lost and the allocation +succeeds. + +![Ring buffer with lost allocations](../gfx/Linear_allocator_6_ring_buffer_lost.png) + +Ring buffer is available only in pools with one memory block - +VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined. + +\section buddy_algorithm Buddy allocation algorithm + +There is another allocation algorithm that can be used with custom pools, called +"buddy". Its internal data structure is based on a tree of blocks, each having +size that is a power of two and a half of its parent's size. When you want to +allocate memory of certain size, a free node in the tree is located. If it's too +large, it is recursively split into two halves (called "buddies"). However, if +requested allocation size is not a power of two, the size of a tree node is +aligned up to the nearest power of two and the remaining space is wasted. When +two buddy nodes become free, they are merged back into one larger node. + +![Buddy allocator](../gfx/Buddy_allocator.png) + +The advantage of buddy allocation algorithm over default algorithm is faster +allocation and deallocation, as well as smaller external fragmentation. The +disadvantage is more wasted space (internal fragmentation). + +For more information, please read ["Buddy memory allocation" on Wikipedia](https://en.wikipedia.org/wiki/Buddy_memory_allocation) +or other sources that describe this concept in general. + +To use buddy allocation algorithm with a custom pool, add flag +#VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating +#VmaPool object. + +Several limitations apply to pools that use buddy algorithm: + +- It is recommended to use VmaPoolCreateInfo::blockSize that is a power of two. + Otherwise, only largest power of two smaller than the size is used for + allocations. The remaining space always stays unused. +- [Margins](@ref debugging_memory_usage_margins) and + [corruption detection](@ref debugging_memory_usage_corruption_detection) + don't work in such pools. +- [Lost allocations](@ref lost_allocations) don't work in such pools. You can + use them, but they never become lost. Support may be added in the future. +- [Defragmentation](@ref defragmentation) doesn't work with allocations made from + such pool. + +\page defragmentation Defragmentation + +Interleaved allocations and deallocations of many objects of varying size can +cause fragmentation over time, which can lead to a situation where the library is unable +to find a continuous range of free memory for a new allocation despite there is +enough free space, just scattered across many small free ranges between existing +allocations. + +To mitigate this problem, you can use defragmentation feature: +structure #VmaDefragmentationInfo2, function vmaDefragmentationBegin(), vmaDefragmentationEnd(). +Given set of allocations, +this function can move them to compact used memory, ensure more continuous free +space and possibly also free some `VkDeviceMemory` blocks. + +What the defragmentation does is: + +- Updates #VmaAllocation objects to point to new `VkDeviceMemory` and offset. + After allocation has been moved, its VmaAllocationInfo::deviceMemory and/or + VmaAllocationInfo::offset changes. You must query them again using + vmaGetAllocationInfo() if you need them. +- Moves actual data in memory. + +What it doesn't do, so you need to do it yourself: + +- Recreate buffers and images that were bound to allocations that were defragmented and + bind them with their new places in memory. + You must use `vkDestroyBuffer()`, `vkDestroyImage()`, + `vkCreateBuffer()`, `vkCreateImage()` for that purpose and NOT vmaDestroyBuffer(), + vmaDestroyImage(), vmaCreateBuffer(), vmaCreateImage(), because you don't need to + destroy or create allocation objects! +- Recreate views and update descriptors that point to these buffers and images. + +\section defragmentation_cpu Defragmenting CPU memory + +Following example demonstrates how you can run defragmentation on CPU. +Only allocations created in memory types that are `HOST_VISIBLE` can be defragmented. +Others are ignored. + +The way it works is: + +- It temporarily maps entire memory blocks when necessary. +- It moves data using `memmove()` function. + +\code +// Given following variables already initialized: +VkDevice device; +VmaAllocator allocator; +std::vector buffers; +std::vector allocations; + + +const uint32_t allocCount = (uint32_t)allocations.size(); +std::vector allocationsChanged(allocCount); + +VmaDefragmentationInfo2 defragInfo = {}; +defragInfo.allocationCount = allocCount; +defragInfo.pAllocations = allocations.data(); +defragInfo.pAllocationsChanged = allocationsChanged.data(); +defragInfo.maxCpuBytesToMove = VK_WHOLE_SIZE; // No limit. +defragInfo.maxCpuAllocationsToMove = UINT32_MAX; // No limit. + +VmaDefragmentationContext defragCtx; +vmaDefragmentationBegin(allocator, &defragInfo, nullptr, &defragCtx); +vmaDefragmentationEnd(allocator, defragCtx); + +for(uint32_t i = 0; i < allocCount; ++i) +{ + if(allocationsChanged[i]) + { + // Destroy buffer that is immutably bound to memory region which is no longer valid. + vkDestroyBuffer(device, buffers[i], nullptr); + + // Create new buffer with same parameters. + VkBufferCreateInfo bufferInfo = ...; + vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]); + + // You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning. + + // Bind new buffer to new memory region. Data contained in it is already moved. + VmaAllocationInfo allocInfo; + vmaGetAllocationInfo(allocator, allocations[i], &allocInfo); + vkBindBufferMemory(device, buffers[i], allocInfo.deviceMemory, allocInfo.offset); + } +} +\endcode + +Setting VmaDefragmentationInfo2::pAllocationsChanged is optional. +This output array tells whether particular allocation in VmaDefragmentationInfo2::pAllocations at the same index +has been modified during defragmentation. +You can pass null, but you then need to query every allocation passed to defragmentation +for new parameters using vmaGetAllocationInfo() if you might need to recreate and rebind a buffer or image associated with it. + +If you use [Custom memory pools](@ref choosing_memory_type_custom_memory_pools), +you can fill VmaDefragmentationInfo2::poolCount and VmaDefragmentationInfo2::pPools +instead of VmaDefragmentationInfo2::allocationCount and VmaDefragmentationInfo2::pAllocations +to defragment all allocations in given pools. +You cannot use VmaDefragmentationInfo2::pAllocationsChanged in that case. +You can also combine both methods. + +\section defragmentation_gpu Defragmenting GPU memory + +It is also possible to defragment allocations created in memory types that are not `HOST_VISIBLE`. +To do that, you need to pass a command buffer that meets requirements as described in +VmaDefragmentationInfo2::commandBuffer. The way it works is: + +- It creates temporary buffers and binds them to entire memory blocks when necessary. +- It issues `vkCmdCopyBuffer()` to passed command buffer. + +Example: + +\code +// Given following variables already initialized: +VkDevice device; +VmaAllocator allocator; +VkCommandBuffer commandBuffer; +std::vector buffers; +std::vector allocations; + + +const uint32_t allocCount = (uint32_t)allocations.size(); +std::vector allocationsChanged(allocCount); + +VkCommandBufferBeginInfo cmdBufBeginInfo = ...; +vkBeginCommandBuffer(commandBuffer, &cmdBufBeginInfo); + +VmaDefragmentationInfo2 defragInfo = {}; +defragInfo.allocationCount = allocCount; +defragInfo.pAllocations = allocations.data(); +defragInfo.pAllocationsChanged = allocationsChanged.data(); +defragInfo.maxGpuBytesToMove = VK_WHOLE_SIZE; // Notice it's "GPU" this time. +defragInfo.maxGpuAllocationsToMove = UINT32_MAX; // Notice it's "GPU" this time. +defragInfo.commandBuffer = commandBuffer; + +VmaDefragmentationContext defragCtx; +vmaDefragmentationBegin(allocator, &defragInfo, nullptr, &defragCtx); + +vkEndCommandBuffer(commandBuffer); + +// Submit commandBuffer. +// Wait for a fence that ensures commandBuffer execution finished. + +vmaDefragmentationEnd(allocator, defragCtx); + +for(uint32_t i = 0; i < allocCount; ++i) +{ + if(allocationsChanged[i]) + { + // Destroy buffer that is immutably bound to memory region which is no longer valid. + vkDestroyBuffer(device, buffers[i], nullptr); + + // Create new buffer with same parameters. + VkBufferCreateInfo bufferInfo = ...; + vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]); + + // You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning. + + // Bind new buffer to new memory region. Data contained in it is already moved. + VmaAllocationInfo allocInfo; + vmaGetAllocationInfo(allocator, allocations[i], &allocInfo); + vkBindBufferMemory(device, buffers[i], allocInfo.deviceMemory, allocInfo.offset); + } +} +\endcode + +You can combine these two methods by specifying non-zero `maxGpu*` as well as `maxCpu*` parameters. +The library automatically chooses best method to defragment each memory pool. + +You may try not to block your entire program to wait until defragmentation finishes, +but do it in the background, as long as you carefully fullfill requirements described +in function vmaDefragmentationBegin(). + +\section defragmentation_additional_notes Additional notes + +While using defragmentation, you may experience validation layer warnings, which you just need to ignore. +See [Validation layer warnings](@ref general_considerations_validation_layer_warnings). + +If you defragment allocations bound to images, these images should be created with +`VK_IMAGE_CREATE_ALIAS_BIT` flag, to make sure that new image created with same +parameters and pointing to data copied to another memory region will interpret +its contents consistently. Otherwise you may experience corrupted data on some +implementations, e.g. due to different pixel swizzling used internally by the graphics driver. + +If you defragment allocations bound to images, new images to be bound to new +memory region after defragmentation should be created with `VK_IMAGE_LAYOUT_PREINITIALIZED` +and then transitioned to their original layout from before defragmentation using +an image memory barrier. + +Please don't expect memory to be fully compacted after defragmentation. +Algorithms inside are based on some heuristics that try to maximize number of Vulkan +memory blocks to make totally empty to release them, as well as to maximimze continuous +empty space inside remaining blocks, while minimizing the number and size of allocations that +need to be moved. Some fragmentation may still remain - this is normal. + +\section defragmentation_custom_algorithm Writing custom defragmentation algorithm + +If you want to implement your own, custom defragmentation algorithm, +there is infrastructure prepared for that, +but it is not exposed through the library API - you need to hack its source code. +Here are steps needed to do this: + +-# Main thing you need to do is to define your own class derived from base abstract + class `VmaDefragmentationAlgorithm` and implement your version of its pure virtual methods. + See definition and comments of this class for details. +-# Your code needs to interact with device memory block metadata. + If you need more access to its data than it's provided by its public interface, + declare your new class as a friend class e.g. in class `VmaBlockMetadata_Generic`. +-# If you want to create a flag that would enable your algorithm or pass some additional + flags to configure it, add them to `VmaDefragmentationFlagBits` and use them in + VmaDefragmentationInfo2::flags. +-# Modify function `VmaBlockVectorDefragmentationContext::Begin` to create object + of your new class whenever needed. + + +\page lost_allocations Lost allocations + +If your game oversubscribes video memory, if may work OK in previous-generation +graphics APIs (DirectX 9, 10, 11, OpenGL) because resources are automatically +paged to system RAM. In Vulkan you can't do it because when you run out of +memory, an allocation just fails. If you have more data (e.g. textures) that can +fit into VRAM and you don't need it all at once, you may want to upload them to +GPU on demand and "push out" ones that are not used for a long time to make room +for the new ones, effectively using VRAM (or a cartain memory pool) as a form of +cache. Vulkan Memory Allocator can help you with that by supporting a concept of +"lost allocations". + +To create an allocation that can become lost, include #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT +flag in VmaAllocationCreateInfo::flags. Before using a buffer or image bound to +such allocation in every new frame, you need to query it if it's not lost. +To check it, call vmaTouchAllocation(). +If the allocation is lost, you should not use it or buffer/image bound to it. +You mustn't forget to destroy this allocation and this buffer/image. +vmaGetAllocationInfo() can also be used for checking status of the allocation. +Allocation is lost when returned VmaAllocationInfo::deviceMemory == `VK_NULL_HANDLE`. + +To create an allocation that can make some other allocations lost to make room +for it, use #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag. You will +usually use both flags #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT and +#VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT at the same time. + +Warning! Current implementation uses quite naive, brute force algorithm, +which can make allocation calls that use #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT +flag quite slow. A new, more optimal algorithm and data structure to speed this +up is planned for the future. + +Q: When interleaving creation of new allocations with usage of existing ones, +how do you make sure that an allocation won't become lost while it's used in the +current frame? + +It is ensured because vmaTouchAllocation() / vmaGetAllocationInfo() not only returns allocation +status/parameters and checks whether it's not lost, but when it's not, it also +atomically marks it as used in the current frame, which makes it impossible to +become lost in that frame. It uses lockless algorithm, so it works fast and +doesn't involve locking any internal mutex. + +Q: What if my allocation may still be in use by the GPU when it's rendering a +previous frame while I already submit new frame on the CPU? + +You can make sure that allocations "touched" by vmaTouchAllocation() / vmaGetAllocationInfo() will not +become lost for a number of additional frames back from the current one by +specifying this number as VmaAllocatorCreateInfo::frameInUseCount (for default +memory pool) and VmaPoolCreateInfo::frameInUseCount (for custom pool). + +Q: How do you inform the library when new frame starts? + +You need to call function vmaSetCurrentFrameIndex(). + +Example code: + +\code +struct MyBuffer +{ + VkBuffer m_Buf = nullptr; + VmaAllocation m_Alloc = nullptr; + + // Called when the buffer is really needed in the current frame. + void EnsureBuffer(); +}; + +void MyBuffer::EnsureBuffer() +{ + // Buffer has been created. + if(m_Buf != VK_NULL_HANDLE) + { + // Check if its allocation is not lost + mark it as used in current frame. + if(vmaTouchAllocation(allocator, m_Alloc)) + { + // It's all OK - safe to use m_Buf. + return; + } + } + + // Buffer not yet exists or lost - destroy and recreate it. + + vmaDestroyBuffer(allocator, m_Buf, m_Alloc); + + VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; + bufCreateInfo.size = 1024; + bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + + VmaAllocationCreateInfo allocCreateInfo = {}; + allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; + allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT | + VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT; + + vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &m_Buf, &m_Alloc, nullptr); +} +\endcode + +When using lost allocations, you may see some Vulkan validation layer warnings +about overlapping regions of memory bound to different kinds of buffers and +images. This is still valid as long as you implement proper handling of lost +allocations (like in the example above) and don't use them. + +You can create an allocation that is already in lost state from the beginning using function +vmaCreateLostAllocation(). It may be useful if you need a "dummy" allocation that is not null. + +You can call function vmaMakePoolAllocationsLost() to set all eligible allocations +in a specified custom pool to lost state. +Allocations that have been "touched" in current frame or VmaPoolCreateInfo::frameInUseCount frames back +cannot become lost. + +Q: Can I touch allocation that cannot become lost? + +Yes, although it has no visible effect. +Calls to vmaGetAllocationInfo() and vmaTouchAllocation() update last use frame index +also for allocations that cannot become lost, but the only way to observe it is to dump +internal allocator state using vmaBuildStatsString(). +You can use this feature for debugging purposes to explicitly mark allocations that you use +in current frame and then analyze JSON dump to see for how long each allocation stays unused. + + +\page statistics Statistics + +This library contains functions that return information about its internal state, +especially the amount of memory allocated from Vulkan. +Please keep in mind that these functions need to traverse all internal data structures +to gather these information, so they may be quite time-consuming. +Don't call them too often. + +\section statistics_numeric_statistics Numeric statistics + +You can query for overall statistics of the allocator using function vmaCalculateStats(). +Information are returned using structure #VmaStats. +It contains #VmaStatInfo - number of allocated blocks, number of allocations +(occupied ranges in these blocks), number of unused (free) ranges in these blocks, +number of bytes used and unused (but still allocated from Vulkan) and other information. +They are summed across memory heaps, memory types and total for whole allocator. + +You can query for statistics of a custom pool using function vmaGetPoolStats(). +Information are returned using structure #VmaPoolStats. + +You can query for information about specific allocation using function vmaGetAllocationInfo(). +It fill structure #VmaAllocationInfo. + +\section statistics_json_dump JSON dump + +You can dump internal state of the allocator to a string in JSON format using function vmaBuildStatsString(). +The result is guaranteed to be correct JSON. +It uses ANSI encoding. +Any strings provided by user (see [Allocation names](@ref allocation_names)) +are copied as-is and properly escaped for JSON, so if they use UTF-8, ISO-8859-2 or any other encoding, +this JSON string can be treated as using this encoding. +It must be freed using function vmaFreeStatsString(). + +The format of this JSON string is not part of official documentation of the library, +but it will not change in backward-incompatible way without increasing library major version number +and appropriate mention in changelog. + +The JSON string contains all the data that can be obtained using vmaCalculateStats(). +It can also contain detailed map of allocated memory blocks and their regions - +free and occupied by allocations. +This allows e.g. to visualize the memory or assess fragmentation. + + +\page allocation_annotation Allocation names and user data + +\section allocation_user_data Allocation user data + +You can annotate allocations with your own information, e.g. for debugging purposes. +To do that, fill VmaAllocationCreateInfo::pUserData field when creating +an allocation. It's an opaque `void*` pointer. You can use it e.g. as a pointer, +some handle, index, key, ordinal number or any other value that would associate +the allocation with your custom metadata. + +\code +VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +// Fill bufferInfo... + +MyBufferMetadata* pMetadata = CreateBufferMetadata(); + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; +allocCreateInfo.pUserData = pMetadata; + +VkBuffer buffer; +VmaAllocation allocation; +vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, nullptr); +\endcode + +The pointer may be later retrieved as VmaAllocationInfo::pUserData: + +\code +VmaAllocationInfo allocInfo; +vmaGetAllocationInfo(allocator, allocation, &allocInfo); +MyBufferMetadata* pMetadata = (MyBufferMetadata*)allocInfo.pUserData; +\endcode + +It can also be changed using function vmaSetAllocationUserData(). + +Values of (non-zero) allocations' `pUserData` are printed in JSON report created by +vmaBuildStatsString(), in hexadecimal form. + +\section allocation_names Allocation names + +There is alternative mode available where `pUserData` pointer is used to point to +a null-terminated string, giving a name to the allocation. To use this mode, +set #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT flag in VmaAllocationCreateInfo::flags. +Then `pUserData` passed as VmaAllocationCreateInfo::pUserData or argument to +vmaSetAllocationUserData() must be either null or pointer to a null-terminated string. +The library creates internal copy of the string, so the pointer you pass doesn't need +to be valid for whole lifetime of the allocation. You can free it after the call. + +\code +VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; +// Fill imageInfo... + +std::string imageName = "Texture: "; +imageName += fileName; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; +allocCreateInfo.flags = VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT; +allocCreateInfo.pUserData = imageName.c_str(); + +VkImage image; +VmaAllocation allocation; +vmaCreateImage(allocator, &imageInfo, &allocCreateInfo, &image, &allocation, nullptr); +\endcode + +The value of `pUserData` pointer of the allocation will be different than the one +you passed when setting allocation's name - pointing to a buffer managed +internally that holds copy of the string. + +\code +VmaAllocationInfo allocInfo; +vmaGetAllocationInfo(allocator, allocation, &allocInfo); +const char* imageName = (const char*)allocInfo.pUserData; +printf("Image name: %s\n", imageName); +\endcode + +That string is also printed in JSON report created by vmaBuildStatsString(). + + +\page debugging_memory_usage Debugging incorrect memory usage + +If you suspect a bug with memory usage, like usage of uninitialized memory or +memory being overwritten out of bounds of an allocation, +you can use debug features of this library to verify this. + +\section debugging_memory_usage_initialization Memory initialization + +If you experience a bug with incorrect and nondeterministic data in your program and you suspect uninitialized memory to be used, +you can enable automatic memory initialization to verify this. +To do it, define macro `VMA_DEBUG_INITIALIZE_ALLOCATIONS` to 1. + +\code +#define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1 +#include "vk_mem_alloc.h" +\endcode + +It makes memory of all new allocations initialized to bit pattern `0xDCDCDCDC`. +Before an allocation is destroyed, its memory is filled with bit pattern `0xEFEFEFEF`. +Memory is automatically mapped and unmapped if necessary. + +If you find these values while debugging your program, good chances are that you incorrectly +read Vulkan memory that is allocated but not initialized, or already freed, respectively. + +Memory initialization works only with memory types that are `HOST_VISIBLE`. +It works also with dedicated allocations. +It doesn't work with allocations created with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag, +as they cannot be mapped. + +\section debugging_memory_usage_margins Margins + +By default, allocations are laid out in memory blocks next to each other if possible +(considering required alignment, `bufferImageGranularity`, and `nonCoherentAtomSize`). + +![Allocations without margin](../gfx/Margins_1.png) + +Define macro `VMA_DEBUG_MARGIN` to some non-zero value (e.g. 16) to enforce specified +number of bytes as a margin before and after every allocation. + +\code +#define VMA_DEBUG_MARGIN 16 +#include "vk_mem_alloc.h" +\endcode + +![Allocations with margin](../gfx/Margins_2.png) + +If your bug goes away after enabling margins, it means it may be caused by memory +being overwritten outside of allocation boundaries. It is not 100% certain though. +Change in application behavior may also be caused by different order and distribution +of allocations across memory blocks after margins are applied. + +The margin is applied also before first and after last allocation in a block. +It may occur only once between two adjacent allocations. + +Margins work with all types of memory. + +Margin is applied only to allocations made out of memory blocks and not to dedicated +allocations, which have their own memory block of specific size. +It is thus not applied to allocations made using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT flag +or those automatically decided to put into dedicated allocations, e.g. due to its +large size or recommended by VK_KHR_dedicated_allocation extension. +Margins are also not active in custom pools created with #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag. + +Margins appear in [JSON dump](@ref statistics_json_dump) as part of free space. + +Note that enabling margins increases memory usage and fragmentation. + +\section debugging_memory_usage_corruption_detection Corruption detection + +You can additionally define macro `VMA_DEBUG_DETECT_CORRUPTION` to 1 to enable validation +of contents of the margins. + +\code +#define VMA_DEBUG_MARGIN 16 +#define VMA_DEBUG_DETECT_CORRUPTION 1 +#include "vk_mem_alloc.h" +\endcode + +When this feature is enabled, number of bytes specified as `VMA_DEBUG_MARGIN` +(it must be multiply of 4) before and after every allocation is filled with a magic number. +This idea is also know as "canary". +Memory is automatically mapped and unmapped if necessary. + +This number is validated automatically when the allocation is destroyed. +If it's not equal to the expected value, `VMA_ASSERT()` is executed. +It clearly means that either CPU or GPU overwritten the memory outside of boundaries of the allocation, +which indicates a serious bug. + +You can also explicitly request checking margins of all allocations in all memory blocks +that belong to specified memory types by using function vmaCheckCorruption(), +or in memory blocks that belong to specified custom pool, by using function +vmaCheckPoolCorruption(). + +Margin validation (corruption detection) works only for memory types that are +`HOST_VISIBLE` and `HOST_COHERENT`. + + +\page record_and_replay Record and replay + +\section record_and_replay_introduction Introduction + +While using the library, sequence of calls to its functions together with their +parameters can be recorded to a file and later replayed using standalone player +application. It can be useful to: + +- Test correctness - check if same sequence of calls will not cause crash or + failures on a target platform. +- Gather statistics - see number of allocations, peak memory usage, number of + calls etc. +- Benchmark performance - see how much time it takes to replay the whole + sequence. + +\section record_and_replay_usage Usage + +To record sequence of calls to a file: Fill in +VmaAllocatorCreateInfo::pRecordSettings member while creating #VmaAllocator +object. File is opened and written during whole lifetime of the allocator. + +To replay file: Use VmaReplay - standalone command-line program. +Precompiled binary can be found in "bin" directory. +Its source can be found in "src/VmaReplay" directory. +Its project is generated by Premake. +Command line syntax is printed when the program is launched without parameters. +Basic usage: + + VmaReplay.exe MyRecording.csv + +Documentation of file format can be found in file: "docs/Recording file format.md". +It's a human-readable, text file in CSV format (Comma Separated Values). + +\section record_and_replay_additional_considerations Additional considerations + +- Replaying file that was recorded on a different GPU (with different parameters + like `bufferImageGranularity`, `nonCoherentAtomSize`, and especially different + set of memory heaps and types) may give different performance and memory usage + results, as well as issue some warnings and errors. +- Current implementation of recording in VMA, as well as VmaReplay application, is + coded and tested only on Windows. Inclusion of recording code is driven by + `VMA_RECORDING_ENABLED` macro. Support for other platforms should be easy to + add. Contributions are welcomed. +- Currently calls to vmaDefragment() function are not recorded. + + +\page usage_patterns Recommended usage patterns + +See also slides from talk: +[Sawicki, Adam. Advanced Graphics Techniques Tutorial: Memory management in Vulkan and DX12. Game Developers Conference, 2018](https://www.gdcvault.com/play/1025458/Advanced-Graphics-Techniques-Tutorial-New) + + +\section usage_patterns_simple Simple patterns + +\subsection usage_patterns_simple_render_targets Render targets + +When: +Any resources that you frequently write and read on GPU, +e.g. images used as color attachments (aka "render targets"), depth-stencil attachments, +images/buffers used as storage image/buffer (aka "Unordered Access View (UAV)"). + +What to do: +Create them in video memory that is fastest to access from GPU using +#VMA_MEMORY_USAGE_GPU_ONLY. + +Consider using [VK_KHR_dedicated_allocation](@ref vk_khr_dedicated_allocation) extension +and/or manually creating them as dedicated allocations using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT, +especially if they are large or if you plan to destroy and recreate them e.g. when +display resolution changes. +Prefer to create such resources first and all other GPU resources (like textures and vertex buffers) later. + +\subsection usage_patterns_simple_immutable_resources Immutable resources + +When: +Any resources that you fill on CPU only once (aka "immutable") or infrequently +and then read frequently on GPU, +e.g. textures, vertex and index buffers, constant buffers that don't change often. + +What to do: +Create them in video memory that is fastest to access from GPU using +#VMA_MEMORY_USAGE_GPU_ONLY. + +To initialize content of such resource, create a CPU-side (aka "staging") copy of it +in system memory - #VMA_MEMORY_USAGE_CPU_ONLY, map it, fill it, +and submit a transfer from it to the GPU resource. +You can keep the staging copy if you need it for another upload transfer in the future. +If you don't, you can destroy it or reuse this buffer for uploading different resource +after the transfer finishes. + +Prefer to create just buffers in system memory rather than images, even for uploading textures. +Use `vkCmdCopyBufferToImage()`. +Dont use images with `VK_IMAGE_TILING_LINEAR`. + +\subsection usage_patterns_dynamic_resources Dynamic resources + +When: +Any resources that change frequently (aka "dynamic"), e.g. every frame or every draw call, +written on CPU, read on GPU. + +What to do: +Create them using #VMA_MEMORY_USAGE_CPU_TO_GPU. +You can map it and write to it directly on CPU, as well as read from it on GPU. + +This is a more complex situation. Different solutions are possible, +and the best one depends on specific GPU type, but you can use this simple approach for the start. +Prefer to write to such resource sequentially (e.g. using `memcpy`). +Don't perform random access or any reads from it on CPU, as it may be very slow. + +\subsection usage_patterns_readback Readback + +When: +Resources that contain data written by GPU that you want to read back on CPU, +e.g. results of some computations. + +What to do: +Create them using #VMA_MEMORY_USAGE_GPU_TO_CPU. +You can write to them directly on GPU, as well as map and read them on CPU. + +\section usage_patterns_advanced Advanced patterns + +\subsection usage_patterns_integrated_graphics Detecting integrated graphics + +You can support integrated graphics (like Intel HD Graphics, AMD APU) better +by detecting it in Vulkan. +To do it, call `vkGetPhysicalDeviceProperties()`, inspect +`VkPhysicalDeviceProperties::deviceType` and look for `VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU`. +When you find it, you can assume that memory is unified and all memory types are comparably fast +to access from GPU, regardless of `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`. + +You can then sum up sizes of all available memory heaps and treat them as useful for +your GPU resources, instead of only `DEVICE_LOCAL` ones. +You can also prefer to create your resources in memory types that are `HOST_VISIBLE` to map them +directly instead of submitting explicit transfer (see below). + +\subsection usage_patterns_direct_vs_transfer Direct access versus transfer + +For resources that you frequently write on CPU and read on GPU, many solutions are possible: + +-# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY, + second copy in system memory using #VMA_MEMORY_USAGE_CPU_ONLY and submit explicit tranfer each time. +-# Create just single copy using #VMA_MEMORY_USAGE_CPU_TO_GPU, map it and fill it on CPU, + read it directly on GPU. +-# Create just single copy using #VMA_MEMORY_USAGE_CPU_ONLY, map it and fill it on CPU, + read it directly on GPU. + +Which solution is the most efficient depends on your resource and especially on the GPU. +It is best to measure it and then make the decision. +Some general recommendations: + +- On integrated graphics use (2) or (3) to avoid unnecesary time and memory overhead + related to using a second copy and making transfer. +- For small resources (e.g. constant buffers) use (2). + Discrete AMD cards have special 256 MiB pool of video memory that is directly mappable. + Even if the resource ends up in system memory, its data may be cached on GPU after first + fetch over PCIe bus. +- For larger resources (e.g. textures), decide between (1) and (2). + You may want to differentiate NVIDIA and AMD, e.g. by looking for memory type that is + both `DEVICE_LOCAL` and `HOST_VISIBLE`. When you find it, use (2), otherwise use (1). + +Similarly, for resources that you frequently write on GPU and read on CPU, multiple +solutions are possible: + +-# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY, + second copy in system memory using #VMA_MEMORY_USAGE_GPU_TO_CPU and submit explicit tranfer each time. +-# Create just single copy using #VMA_MEMORY_USAGE_GPU_TO_CPU, write to it directly on GPU, + map it and read it on CPU. + +You should take some measurements to decide which option is faster in case of your specific +resource. + +If you don't want to specialize your code for specific types of GPUs, you can still make +an simple optimization for cases when your resource ends up in mappable memory to use it +directly in this case instead of creating CPU-side staging copy. +For details see [Finding out if memory is mappable](@ref memory_mapping_finding_if_memory_mappable). + + +\page configuration Configuration + +Please check "CONFIGURATION SECTION" in the code to find macros that you can define +before each include of this file or change directly in this file to provide +your own implementation of basic facilities like assert, `min()` and `max()` functions, +mutex, atomic etc. +The library uses its own implementation of containers by default, but you can switch to using +STL containers instead. + +\section config_Vulkan_functions Pointers to Vulkan functions + +The library uses Vulkan functions straight from the `vulkan.h` header by default. +If you want to provide your own pointers to these functions, e.g. fetched using +`vkGetInstanceProcAddr()` and `vkGetDeviceProcAddr()`: + +-# Define `VMA_STATIC_VULKAN_FUNCTIONS 0`. +-# Provide valid pointers through VmaAllocatorCreateInfo::pVulkanFunctions. + +\section custom_memory_allocator Custom host memory allocator + +If you use custom allocator for CPU memory rather than default operator `new` +and `delete` from C++, you can make this library using your allocator as well +by filling optional member VmaAllocatorCreateInfo::pAllocationCallbacks. These +functions will be passed to Vulkan, as well as used by the library itself to +make any CPU-side allocations. + +\section allocation_callbacks Device memory allocation callbacks + +The library makes calls to `vkAllocateMemory()` and `vkFreeMemory()` internally. +You can setup callbacks to be informed about these calls, e.g. for the purpose +of gathering some statistics. To do it, fill optional member +VmaAllocatorCreateInfo::pDeviceMemoryCallbacks. + +\section heap_memory_limit Device heap memory limit + +If you want to test how your program behaves with limited amount of Vulkan device +memory available without switching your graphics card to one that really has +smaller VRAM, you can use a feature of this library intended for this purpose. +To do it, fill optional member VmaAllocatorCreateInfo::pHeapSizeLimit. + + + +\page vk_khr_dedicated_allocation VK_KHR_dedicated_allocation + +VK_KHR_dedicated_allocation is a Vulkan extension which can be used to improve +performance on some GPUs. It augments Vulkan API with possibility to query +driver whether it prefers particular buffer or image to have its own, dedicated +allocation (separate `VkDeviceMemory` block) for better efficiency - to be able +to do some internal optimizations. + +The extension is supported by this library. It will be used automatically when +enabled. To enable it: + +1 . When creating Vulkan device, check if following 2 device extensions are +supported (call `vkEnumerateDeviceExtensionProperties()`). +If yes, enable them (fill `VkDeviceCreateInfo::ppEnabledExtensionNames`). + +- VK_KHR_get_memory_requirements2 +- VK_KHR_dedicated_allocation + +If you enabled these extensions: + +2 . Use #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag when creating +your #VmaAllocator`to inform the library that you enabled required extensions +and you want the library to use them. + +\code +allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT; + +vmaCreateAllocator(&allocatorInfo, &allocator); +\endcode + +That's all. The extension will be automatically used whenever you create a +buffer using vmaCreateBuffer() or image using vmaCreateImage(). + +When using the extension together with Vulkan Validation Layer, you will receive +warnings like this: + + vkBindBufferMemory(): Binding memory to buffer 0x33 but vkGetBufferMemoryRequirements() has not been called on that buffer. + +It is OK, you should just ignore it. It happens because you use function +`vkGetBufferMemoryRequirements2KHR()` instead of standard +`vkGetBufferMemoryRequirements()`, while the validation layer seems to be +unaware of it. + +To learn more about this extension, see: + +- [VK_KHR_dedicated_allocation in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.0-extensions/html/vkspec.html#VK_KHR_dedicated_allocation) +- [VK_KHR_dedicated_allocation unofficial manual](http://asawicki.info/articles/VK_KHR_dedicated_allocation.php5) + + + +\page general_considerations General considerations + +\section general_considerations_thread_safety Thread safety + +- The library has no global state, so separate #VmaAllocator objects can be used + independently. + There should be no need to create multiple such objects though - one per `VkDevice` is enough. +- By default, all calls to functions that take #VmaAllocator as first parameter + are safe to call from multiple threads simultaneously because they are + synchronized internally when needed. +- When the allocator is created with #VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT + flag, calls to functions that take such #VmaAllocator object must be + synchronized externally. +- Access to a #VmaAllocation object must be externally synchronized. For example, + you must not call vmaGetAllocationInfo() and vmaMapMemory() from different + threads at the same time if you pass the same #VmaAllocation object to these + functions. + +\section general_considerations_validation_layer_warnings Validation layer warnings + +When using this library, you can meet following types of warnings issued by +Vulkan validation layer. They don't necessarily indicate a bug, so you may need +to just ignore them. + +- *vkBindBufferMemory(): Binding memory to buffer 0xeb8e4 but vkGetBufferMemoryRequirements() has not been called on that buffer.* + - It happens when VK_KHR_dedicated_allocation extension is enabled. + `vkGetBufferMemoryRequirements2KHR` function is used instead, while validation layer seems to be unaware of it. +- *Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used.* + - It happens when you map a buffer or image, because the library maps entire + `VkDeviceMemory` block, where different types of images and buffers may end + up together, especially on GPUs with unified memory like Intel. +- *Non-linear image 0xebc91 is aliased with linear buffer 0xeb8e4 which may indicate a bug.* + - It happens when you use lost allocations, and a new image or buffer is + created in place of an existing object that bacame lost. + - It may happen also when you use [defragmentation](@ref defragmentation). + +\section general_considerations_allocation_algorithm Allocation algorithm + +The library uses following algorithm for allocation, in order: + +-# Try to find free range of memory in existing blocks. +-# If failed, try to create a new block of `VkDeviceMemory`, with preferred block size. +-# If failed, try to create such block with size/2, size/4, size/8. +-# If failed and #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag was + specified, try to find space in existing blocks, possilby making some other + allocations lost. +-# If failed, try to allocate separate `VkDeviceMemory` for this allocation, + just like when you use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. +-# If failed, choose other memory type that meets the requirements specified in + VmaAllocationCreateInfo and go to point 1. +-# If failed, return `VK_ERROR_OUT_OF_DEVICE_MEMORY`. + +\section general_considerations_features_not_supported Features not supported + +Features deliberately excluded from the scope of this library: + +- Data transfer. Uploading (straming) and downloading data of buffers and images + between CPU and GPU memory and related synchronization is responsibility of the user. +- Allocations for imported/exported external memory. They tend to require + explicit memory type index and dedicated allocation anyway, so they don't + interact with main features of this library. Such special purpose allocations + should be made manually, using `vkCreateBuffer()` and `vkAllocateMemory()`. +- Recreation of buffers and images. Although the library has functions for + buffer and image creation (vmaCreateBuffer(), vmaCreateImage()), you need to + recreate these objects yourself after defragmentation. That's because the big + structures `VkBufferCreateInfo`, `VkImageCreateInfo` are not stored in + #VmaAllocation object. +- Handling CPU memory allocation failures. When dynamically creating small C++ + objects in CPU memory (not Vulkan memory), allocation failures are not checked + and handled gracefully, because that would complicate code significantly and + is usually not needed in desktop PC applications anyway. +- Code free of any compiler warnings. Maintaining the library to compile and + work correctly on so many different platforms is hard enough. Being free of + any warnings, on any version of any compiler, is simply not feasible. +- This is a C++ library with C interface. + Bindings or ports to any other programming languages are welcomed as external projects and + are not going to be included into this repository. + +*/ + +/* +Define this macro to 0/1 to disable/enable support for recording functionality, +available through VmaAllocatorCreateInfo::pRecordSettings. +*/ +#ifndef VMA_RECORDING_ENABLED + #ifdef _WIN32 + #define VMA_RECORDING_ENABLED 1 + #else + #define VMA_RECORDING_ENABLED 0 + #endif +#endif + +#ifndef NOMINMAX + #define NOMINMAX // For windows.h +#endif + +#ifndef VULKAN_H_ + #include +#endif + +#if VMA_RECORDING_ENABLED + #include +#endif + +#if !defined(VMA_DEDICATED_ALLOCATION) + #if VK_KHR_get_memory_requirements2 && VK_KHR_dedicated_allocation + #define VMA_DEDICATED_ALLOCATION 1 + #else + #define VMA_DEDICATED_ALLOCATION 0 + #endif +#endif + +/** \struct VmaAllocator +\brief Represents main object of this library initialized. + +Fill structure #VmaAllocatorCreateInfo and call function vmaCreateAllocator() to create it. +Call function vmaDestroyAllocator() to destroy it. + +It is recommended to create just one object of this type per `VkDevice` object, +right after Vulkan is initialized and keep it alive until before Vulkan device is destroyed. +*/ +VK_DEFINE_HANDLE(VmaAllocator) + +/// Callback function called after successful vkAllocateMemory. +typedef void (VKAPI_PTR *PFN_vmaAllocateDeviceMemoryFunction)( + VmaAllocator allocator, + uint32_t memoryType, + VkDeviceMemory memory, + VkDeviceSize size); +/// Callback function called before vkFreeMemory. +typedef void (VKAPI_PTR *PFN_vmaFreeDeviceMemoryFunction)( + VmaAllocator allocator, + uint32_t memoryType, + VkDeviceMemory memory, + VkDeviceSize size); + +/** \brief Set of callbacks that the library will call for `vkAllocateMemory` and `vkFreeMemory`. + +Provided for informative purpose, e.g. to gather statistics about number of +allocations or total amount of memory allocated in Vulkan. + +Used in VmaAllocatorCreateInfo::pDeviceMemoryCallbacks. +*/ +typedef struct VmaDeviceMemoryCallbacks { + /// Optional, can be null. + PFN_vmaAllocateDeviceMemoryFunction pfnAllocate; + /// Optional, can be null. + PFN_vmaFreeDeviceMemoryFunction pfnFree; +} VmaDeviceMemoryCallbacks; + +/// Flags for created #VmaAllocator. +typedef enum VmaAllocatorCreateFlagBits { + /** \brief Allocator and all objects created from it will not be synchronized internally, so you must guarantee they are used from only one thread at a time or synchronized externally by you. + + Using this flag may increase performance because internal mutexes are not used. + */ + VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT = 0x00000001, + /** \brief Enables usage of VK_KHR_dedicated_allocation extension. + + Using this extenion will automatically allocate dedicated blocks of memory for + some buffers and images instead of suballocating place for them out of bigger + memory blocks (as if you explicitly used #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT + flag) when it is recommended by the driver. It may improve performance on some + GPUs. + + You may set this flag only if you found out that following device extensions are + supported, you enabled them while creating Vulkan device passed as + VmaAllocatorCreateInfo::device, and you want them to be used internally by this + library: + + - VK_KHR_get_memory_requirements2 + - VK_KHR_dedicated_allocation + +When this flag is set, you can experience following warnings reported by Vulkan +validation layer. You can ignore them. + +> vkBindBufferMemory(): Binding memory to buffer 0x2d but vkGetBufferMemoryRequirements() has not been called on that buffer. + */ + VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT = 0x00000002, + + VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VmaAllocatorCreateFlagBits; +typedef VkFlags VmaAllocatorCreateFlags; + +/** \brief Pointers to some Vulkan functions - a subset used by the library. + +Used in VmaAllocatorCreateInfo::pVulkanFunctions. +*/ +typedef struct VmaVulkanFunctions { + PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties; + PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties; + PFN_vkAllocateMemory vkAllocateMemory; + PFN_vkFreeMemory vkFreeMemory; + PFN_vkMapMemory vkMapMemory; + PFN_vkUnmapMemory vkUnmapMemory; + PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges; + PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges; + PFN_vkBindBufferMemory vkBindBufferMemory; + PFN_vkBindImageMemory vkBindImageMemory; + PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements; + PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements; + PFN_vkCreateBuffer vkCreateBuffer; + PFN_vkDestroyBuffer vkDestroyBuffer; + PFN_vkCreateImage vkCreateImage; + PFN_vkDestroyImage vkDestroyImage; + PFN_vkCmdCopyBuffer vkCmdCopyBuffer; +#if VMA_DEDICATED_ALLOCATION + PFN_vkGetBufferMemoryRequirements2KHR vkGetBufferMemoryRequirements2KHR; + PFN_vkGetImageMemoryRequirements2KHR vkGetImageMemoryRequirements2KHR; +#endif +} VmaVulkanFunctions; + +/// Flags to be used in VmaRecordSettings::flags. +typedef enum VmaRecordFlagBits { + /** \brief Enables flush after recording every function call. + + Enable it if you expect your application to crash, which may leave recording file truncated. + It may degrade performance though. + */ + VMA_RECORD_FLUSH_AFTER_CALL_BIT = 0x00000001, + + VMA_RECORD_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VmaRecordFlagBits; +typedef VkFlags VmaRecordFlags; + +/// Parameters for recording calls to VMA functions. To be used in VmaAllocatorCreateInfo::pRecordSettings. +typedef struct VmaRecordSettings +{ + /// Flags for recording. Use #VmaRecordFlagBits enum. + VmaRecordFlags flags; + /** \brief Path to the file that should be written by the recording. + + Suggested extension: "csv". + If the file already exists, it will be overwritten. + It will be opened for the whole time #VmaAllocator object is alive. + If opening this file fails, creation of the whole allocator object fails. + */ + const char* pFilePath; +} VmaRecordSettings; + +/// Description of a Allocator to be created. +typedef struct VmaAllocatorCreateInfo +{ + /// Flags for created allocator. Use #VmaAllocatorCreateFlagBits enum. + VmaAllocatorCreateFlags flags; + /// Vulkan physical device. + /** It must be valid throughout whole lifetime of created allocator. */ + VkPhysicalDevice physicalDevice; + /// Vulkan device. + /** It must be valid throughout whole lifetime of created allocator. */ + VkDevice device; + /// Preferred size of a single `VkDeviceMemory` block to be allocated from large heaps > 1 GiB. Optional. + /** Set to 0 to use default, which is currently 256 MiB. */ + VkDeviceSize preferredLargeHeapBlockSize; + /// Custom CPU memory allocation callbacks. Optional. + /** Optional, can be null. When specified, will also be used for all CPU-side memory allocations. */ + const VkAllocationCallbacks* pAllocationCallbacks; + /// Informative callbacks for `vkAllocateMemory`, `vkFreeMemory`. Optional. + /** Optional, can be null. */ + const VmaDeviceMemoryCallbacks* pDeviceMemoryCallbacks; + /** \brief Maximum number of additional frames that are in use at the same time as current frame. + + This value is used only when you make allocations with + VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become + lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount. + + For example, if you double-buffer your command buffers, so resources used for + rendering in previous frame may still be in use by the GPU at the moment you + allocate resources needed for the current frame, set this value to 1. + + If you want to allow any allocations other than used in the current frame to + become lost, set this value to 0. + */ + uint32_t frameInUseCount; + /** \brief Either null or a pointer to an array of limits on maximum number of bytes that can be allocated out of particular Vulkan memory heap. + + If not NULL, it must be a pointer to an array of + `VkPhysicalDeviceMemoryProperties::memoryHeapCount` elements, defining limit on + maximum number of bytes that can be allocated out of particular Vulkan memory + heap. + + Any of the elements may be equal to `VK_WHOLE_SIZE`, which means no limit on that + heap. This is also the default in case of `pHeapSizeLimit` = NULL. + + If there is a limit defined for a heap: + + - If user tries to allocate more memory from that heap using this allocator, + the allocation fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`. + - If the limit is smaller than heap size reported in `VkMemoryHeap::size`, the + value of this limit will be reported instead when using vmaGetMemoryProperties(). + + Warning! Using this feature may not be equivalent to installing a GPU with + smaller amount of memory, because graphics driver doesn't necessary fail new + allocations with `VK_ERROR_OUT_OF_DEVICE_MEMORY` result when memory capacity is + exceeded. It may return success and just silently migrate some device memory + blocks to system RAM. This driver behavior can also be controlled using + VK_AMD_memory_overallocation_behavior extension. + */ + const VkDeviceSize* pHeapSizeLimit; + /** \brief Pointers to Vulkan functions. Can be null if you leave define `VMA_STATIC_VULKAN_FUNCTIONS 1`. + + If you leave define `VMA_STATIC_VULKAN_FUNCTIONS 1` in configuration section, + you can pass null as this member, because the library will fetch pointers to + Vulkan functions internally in a static way, like: + + vulkanFunctions.vkAllocateMemory = &vkAllocateMemory; + + Fill this member if you want to provide your own pointers to Vulkan functions, + e.g. fetched using `vkGetInstanceProcAddr()` and `vkGetDeviceProcAddr()`. + */ + const VmaVulkanFunctions* pVulkanFunctions; + /** \brief Parameters for recording of VMA calls. Can be null. + + If not null, it enables recording of calls to VMA functions to a file. + If support for recording is not enabled using `VMA_RECORDING_ENABLED` macro, + creation of the allocator object fails with `VK_ERROR_FEATURE_NOT_PRESENT`. + */ + const VmaRecordSettings* pRecordSettings; +} VmaAllocatorCreateInfo; + +/// Creates Allocator object. +VkResult vmaCreateAllocator( + const VmaAllocatorCreateInfo* pCreateInfo, + VmaAllocator* pAllocator); + +/// Destroys allocator object. +void vmaDestroyAllocator( + VmaAllocator allocator); + +/** +PhysicalDeviceProperties are fetched from physicalDevice by the allocator. +You can access it here, without fetching it again on your own. +*/ +void vmaGetPhysicalDeviceProperties( + VmaAllocator allocator, + const VkPhysicalDeviceProperties** ppPhysicalDeviceProperties); + +/** +PhysicalDeviceMemoryProperties are fetched from physicalDevice by the allocator. +You can access it here, without fetching it again on your own. +*/ +void vmaGetMemoryProperties( + VmaAllocator allocator, + const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties); + +/** +\brief Given Memory Type Index, returns Property Flags of this memory type. + +This is just a convenience function. Same information can be obtained using +vmaGetMemoryProperties(). +*/ +void vmaGetMemoryTypeProperties( + VmaAllocator allocator, + uint32_t memoryTypeIndex, + VkMemoryPropertyFlags* pFlags); + +/** \brief Sets index of the current frame. + +This function must be used if you make allocations with +#VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT and +#VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flags to inform the allocator +when a new frame begins. Allocations queried using vmaGetAllocationInfo() cannot +become lost in the current frame. +*/ +void vmaSetCurrentFrameIndex( + VmaAllocator allocator, + uint32_t frameIndex); + +/** \brief Calculated statistics of memory usage in entire allocator. +*/ +typedef struct VmaStatInfo +{ + /// Number of `VkDeviceMemory` Vulkan memory blocks allocated. + uint32_t blockCount; + /// Number of #VmaAllocation allocation objects allocated. + uint32_t allocationCount; + /// Number of free ranges of memory between allocations. + uint32_t unusedRangeCount; + /// Total number of bytes occupied by all allocations. + VkDeviceSize usedBytes; + /// Total number of bytes occupied by unused ranges. + VkDeviceSize unusedBytes; + VkDeviceSize allocationSizeMin, allocationSizeAvg, allocationSizeMax; + VkDeviceSize unusedRangeSizeMin, unusedRangeSizeAvg, unusedRangeSizeMax; +} VmaStatInfo; + +/// General statistics from current state of Allocator. +typedef struct VmaStats +{ + VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES]; + VmaStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS]; + VmaStatInfo total; +} VmaStats; + +/// Retrieves statistics from current state of the Allocator. +void vmaCalculateStats( + VmaAllocator allocator, + VmaStats* pStats); + +#define VMA_STATS_STRING_ENABLED 1 + +#if VMA_STATS_STRING_ENABLED + +/// Builds and returns statistics as string in JSON format. +/** @param[out] ppStatsString Must be freed using vmaFreeStatsString() function. +*/ +void vmaBuildStatsString( + VmaAllocator allocator, + char** ppStatsString, + VkBool32 detailedMap); + +void vmaFreeStatsString( + VmaAllocator allocator, + char* pStatsString); + +#endif // #if VMA_STATS_STRING_ENABLED + +/** \struct VmaPool +\brief Represents custom memory pool + +Fill structure VmaPoolCreateInfo and call function vmaCreatePool() to create it. +Call function vmaDestroyPool() to destroy it. + +For more information see [Custom memory pools](@ref choosing_memory_type_custom_memory_pools). +*/ +VK_DEFINE_HANDLE(VmaPool) + +typedef enum VmaMemoryUsage +{ + /** No intended memory usage specified. + Use other members of VmaAllocationCreateInfo to specify your requirements. + */ + VMA_MEMORY_USAGE_UNKNOWN = 0, + /** Memory will be used on device only, so fast access from the device is preferred. + It usually means device-local GPU (video) memory. + No need to be mappable on host. + It is roughly equivalent of `D3D12_HEAP_TYPE_DEFAULT`. + + Usage: + + - Resources written and read by device, e.g. images used as attachments. + - Resources transferred from host once (immutable) or infrequently and read by + device multiple times, e.g. textures to be sampled, vertex buffers, uniform + (constant) buffers, and majority of other types of resources used on GPU. + + Allocation may still end up in `HOST_VISIBLE` memory on some implementations. + In such case, you are free to map it. + You can use #VMA_ALLOCATION_CREATE_MAPPED_BIT with this usage type. + */ + VMA_MEMORY_USAGE_GPU_ONLY = 1, + /** Memory will be mappable on host. + It usually means CPU (system) memory. + Guarantees to be `HOST_VISIBLE` and `HOST_COHERENT`. + CPU access is typically uncached. Writes may be write-combined. + Resources created in this pool may still be accessible to the device, but access to them can be slow. + It is roughly equivalent of `D3D12_HEAP_TYPE_UPLOAD`. + + Usage: Staging copy of resources used as transfer source. + */ + VMA_MEMORY_USAGE_CPU_ONLY = 2, + /** + Memory that is both mappable on host (guarantees to be `HOST_VISIBLE`) and preferably fast to access by GPU. + CPU access is typically uncached. Writes may be write-combined. + + Usage: Resources written frequently by host (dynamic), read by device. E.g. textures, vertex buffers, uniform buffers updated every frame or every draw call. + */ + VMA_MEMORY_USAGE_CPU_TO_GPU = 3, + /** Memory mappable on host (guarantees to be `HOST_VISIBLE`) and cached. + It is roughly equivalent of `D3D12_HEAP_TYPE_READBACK`. + + Usage: + + - Resources written by device, read by host - results of some computations, e.g. screen capture, average scene luminance for HDR tone mapping. + - Any resources read or accessed randomly on host, e.g. CPU-side copy of vertex buffer used as source of transfer, but also used for collision detection. + */ + VMA_MEMORY_USAGE_GPU_TO_CPU = 4, + VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF +} VmaMemoryUsage; + +/// Flags to be passed as VmaAllocationCreateInfo::flags. +typedef enum VmaAllocationCreateFlagBits { + /** \brief Set this flag if the allocation should have its own memory block. + + Use it for special, big resources, like fullscreen images used as attachments. + + This flag must also be used for host visible resources that you want to map + simultaneously because otherwise they might end up as regions of the same + `VkDeviceMemory`, while mapping same `VkDeviceMemory` multiple times + simultaneously is illegal. + + You should not use this flag if VmaAllocationCreateInfo::pool is not null. + */ + VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT = 0x00000001, + + /** \brief Set this flag to only try to allocate from existing `VkDeviceMemory` blocks and never create new such block. + + If new allocation cannot be placed in any of the existing blocks, allocation + fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY` error. + + You should not use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT and + #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT at the same time. It makes no sense. + + If VmaAllocationCreateInfo::pool is not null, this flag is implied and ignored. */ + VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT = 0x00000002, + /** \brief Set this flag to use a memory that will be persistently mapped and retrieve pointer to it. + + Pointer to mapped memory will be returned through VmaAllocationInfo::pMappedData. + + Is it valid to use this flag for allocation made from memory type that is not + `HOST_VISIBLE`. This flag is then ignored and memory is not mapped. This is + useful if you need an allocation that is efficient to use on GPU + (`DEVICE_LOCAL`) and still want to map it directly if possible on platforms that + support it (e.g. Intel GPU). + + You should not use this flag together with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT. + */ + VMA_ALLOCATION_CREATE_MAPPED_BIT = 0x00000004, + /** Allocation created with this flag can become lost as a result of another + allocation with #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag, so you + must check it before use. + + To check if allocation is not lost, call vmaGetAllocationInfo() and check if + VmaAllocationInfo::deviceMemory is not `VK_NULL_HANDLE`. + + For details about supporting lost allocations, see Lost Allocations + chapter of User Guide on Main Page. + + You should not use this flag together with #VMA_ALLOCATION_CREATE_MAPPED_BIT. + */ + VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT = 0x00000008, + /** While creating allocation using this flag, other allocations that were + created with flag #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT can become lost. + + For details about supporting lost allocations, see Lost Allocations + chapter of User Guide on Main Page. + */ + VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT = 0x00000010, + /** Set this flag to treat VmaAllocationCreateInfo::pUserData as pointer to a + null-terminated string. Instead of copying pointer value, a local copy of the + string is made and stored in allocation's `pUserData`. The string is automatically + freed together with the allocation. It is also used in vmaBuildStatsString(). + */ + VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT = 0x00000020, + /** Allocation will be created from upper stack in a double stack pool. + + This flag is only allowed for custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT flag. + */ + VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = 0x00000040, + + /** Allocation strategy that chooses smallest possible free range for the + allocation. + */ + VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT = 0x00010000, + /** Allocation strategy that chooses biggest possible free range for the + allocation. + */ + VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT = 0x00020000, + /** Allocation strategy that chooses first suitable free range for the + allocation. + + "First" doesn't necessarily means the one with smallest offset in memory, + but rather the one that is easiest and fastest to find. + */ + VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT = 0x00040000, + + /** Allocation strategy that tries to minimize memory usage. + */ + VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT, + /** Allocation strategy that tries to minimize allocation time. + */ + VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT, + /** Allocation strategy that tries to minimize memory fragmentation. + */ + VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT, + + /** A bit mask to extract only `STRATEGY` bits from entire set of flags. + */ + VMA_ALLOCATION_CREATE_STRATEGY_MASK = + VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT | + VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT | + VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT, + + VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VmaAllocationCreateFlagBits; +typedef VkFlags VmaAllocationCreateFlags; + +typedef struct VmaAllocationCreateInfo +{ + /// Use #VmaAllocationCreateFlagBits enum. + VmaAllocationCreateFlags flags; + /** \brief Intended usage of memory. + + You can leave #VMA_MEMORY_USAGE_UNKNOWN if you specify memory requirements in other way. \n + If `pool` is not null, this member is ignored. + */ + VmaMemoryUsage usage; + /** \brief Flags that must be set in a Memory Type chosen for an allocation. + + Leave 0 if you specify memory requirements in other way. \n + If `pool` is not null, this member is ignored.*/ + VkMemoryPropertyFlags requiredFlags; + /** \brief Flags that preferably should be set in a memory type chosen for an allocation. + + Set to 0 if no additional flags are prefered. \n + If `pool` is not null, this member is ignored. */ + VkMemoryPropertyFlags preferredFlags; + /** \brief Bitmask containing one bit set for every memory type acceptable for this allocation. + + Value 0 is equivalent to `UINT32_MAX` - it means any memory type is accepted if + it meets other requirements specified by this structure, with no further + restrictions on memory type index. \n + If `pool` is not null, this member is ignored. + */ + uint32_t memoryTypeBits; + /** \brief Pool that this allocation should be created in. + + Leave `VK_NULL_HANDLE` to allocate from default pool. If not null, members: + `usage`, `requiredFlags`, `preferredFlags`, `memoryTypeBits` are ignored. + */ + VmaPool pool; + /** \brief Custom general-purpose pointer that will be stored in #VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData(). + + If #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is used, it must be either + null or pointer to a null-terminated string. The string will be then copied to + internal buffer, so it doesn't need to be valid after allocation call. + */ + void* pUserData; +} VmaAllocationCreateInfo; + +/** +\brief Helps to find memoryTypeIndex, given memoryTypeBits and VmaAllocationCreateInfo. + +This algorithm tries to find a memory type that: + +- Is allowed by memoryTypeBits. +- Contains all the flags from pAllocationCreateInfo->requiredFlags. +- Matches intended usage. +- Has as many flags from pAllocationCreateInfo->preferredFlags as possible. + +\return Returns VK_ERROR_FEATURE_NOT_PRESENT if not found. Receiving such result +from this function or any other allocating function probably means that your +device doesn't support any memory type with requested features for the specific +type of resource you want to use it for. Please check parameters of your +resource, like image layout (OPTIMAL versus LINEAR) or mip level count. +*/ +VkResult vmaFindMemoryTypeIndex( + VmaAllocator allocator, + uint32_t memoryTypeBits, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + uint32_t* pMemoryTypeIndex); + +/** +\brief Helps to find memoryTypeIndex, given VkBufferCreateInfo and VmaAllocationCreateInfo. + +It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex. +It internally creates a temporary, dummy buffer that never has memory bound. +It is just a convenience function, equivalent to calling: + +- `vkCreateBuffer` +- `vkGetBufferMemoryRequirements` +- `vmaFindMemoryTypeIndex` +- `vkDestroyBuffer` +*/ +VkResult vmaFindMemoryTypeIndexForBufferInfo( + VmaAllocator allocator, + const VkBufferCreateInfo* pBufferCreateInfo, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + uint32_t* pMemoryTypeIndex); + +/** +\brief Helps to find memoryTypeIndex, given VkImageCreateInfo and VmaAllocationCreateInfo. + +It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex. +It internally creates a temporary, dummy image that never has memory bound. +It is just a convenience function, equivalent to calling: + +- `vkCreateImage` +- `vkGetImageMemoryRequirements` +- `vmaFindMemoryTypeIndex` +- `vkDestroyImage` +*/ +VkResult vmaFindMemoryTypeIndexForImageInfo( + VmaAllocator allocator, + const VkImageCreateInfo* pImageCreateInfo, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + uint32_t* pMemoryTypeIndex); + +/// Flags to be passed as VmaPoolCreateInfo::flags. +typedef enum VmaPoolCreateFlagBits { + /** \brief Use this flag if you always allocate only buffers and linear images or only optimal images out of this pool and so Buffer-Image Granularity can be ignored. + + This is an optional optimization flag. + + If you always allocate using vmaCreateBuffer(), vmaCreateImage(), + vmaAllocateMemoryForBuffer(), then you don't need to use it because allocator + knows exact type of your allocations so it can handle Buffer-Image Granularity + in the optimal way. + + If you also allocate using vmaAllocateMemoryForImage() or vmaAllocateMemory(), + exact type of such allocations is not known, so allocator must be conservative + in handling Buffer-Image Granularity, which can lead to suboptimal allocation + (wasted memory). In that case, if you can make sure you always allocate only + buffers and linear images or only optimal images out of this pool, use this flag + to make allocator disregard Buffer-Image Granularity and so make allocations + faster and more optimal. + */ + VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT = 0x00000002, + + /** \brief Enables alternative, linear allocation algorithm in this pool. + + Specify this flag to enable linear allocation algorithm, which always creates + new allocations after last one and doesn't reuse space from allocations freed in + between. It trades memory consumption for simplified algorithm and data + structure, which has better performance and uses less memory for metadata. + + By using this flag, you can achieve behavior of free-at-once, stack, + ring buffer, and double stack. For details, see documentation chapter + \ref linear_algorithm. + + When using this flag, you must specify VmaPoolCreateInfo::maxBlockCount == 1 (or 0 for default). + + For more details, see [Linear allocation algorithm](@ref linear_algorithm). + */ + VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT = 0x00000004, + + /** \brief Enables alternative, buddy allocation algorithm in this pool. + + It operates on a tree of blocks, each having size that is a power of two and + a half of its parent's size. Comparing to default algorithm, this one provides + faster allocation and deallocation and decreased external fragmentation, + at the expense of more memory wasted (internal fragmentation). + + For more details, see [Buddy allocation algorithm](@ref buddy_algorithm). + */ + VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT = 0x00000008, + + /** Bit mask to extract only `ALGORITHM` bits from entire set of flags. + */ + VMA_POOL_CREATE_ALGORITHM_MASK = + VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT | + VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT, + + VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VmaPoolCreateFlagBits; +typedef VkFlags VmaPoolCreateFlags; + +/** \brief Describes parameter of created #VmaPool. +*/ +typedef struct VmaPoolCreateInfo { + /** \brief Vulkan memory type index to allocate this pool from. + */ + uint32_t memoryTypeIndex; + /** \brief Use combination of #VmaPoolCreateFlagBits. + */ + VmaPoolCreateFlags flags; + /** \brief Size of a single `VkDeviceMemory` block to be allocated as part of this pool, in bytes. Optional. + + Specify nonzero to set explicit, constant size of memory blocks used by this + pool. + + Leave 0 to use default and let the library manage block sizes automatically. + Sizes of particular blocks may vary. + */ + VkDeviceSize blockSize; + /** \brief Minimum number of blocks to be always allocated in this pool, even if they stay empty. + + Set to 0 to have no preallocated blocks and allow the pool be completely empty. + */ + size_t minBlockCount; + /** \brief Maximum number of blocks that can be allocated in this pool. Optional. + + Set to 0 to use default, which is `SIZE_MAX`, which means no limit. + + Set to same value as VmaPoolCreateInfo::minBlockCount to have fixed amount of memory allocated + throughout whole lifetime of this pool. + */ + size_t maxBlockCount; + /** \brief Maximum number of additional frames that are in use at the same time as current frame. + + This value is used only when you make allocations with + #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become + lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount. + + For example, if you double-buffer your command buffers, so resources used for + rendering in previous frame may still be in use by the GPU at the moment you + allocate resources needed for the current frame, set this value to 1. + + If you want to allow any allocations other than used in the current frame to + become lost, set this value to 0. + */ + uint32_t frameInUseCount; +} VmaPoolCreateInfo; + +/** \brief Describes parameter of existing #VmaPool. +*/ +typedef struct VmaPoolStats { + /** \brief Total amount of `VkDeviceMemory` allocated from Vulkan for this pool, in bytes. + */ + VkDeviceSize size; + /** \brief Total number of bytes in the pool not used by any #VmaAllocation. + */ + VkDeviceSize unusedSize; + /** \brief Number of #VmaAllocation objects created from this pool that were not destroyed or lost. + */ + size_t allocationCount; + /** \brief Number of continuous memory ranges in the pool not used by any #VmaAllocation. + */ + size_t unusedRangeCount; + /** \brief Size of the largest continuous free memory region available for new allocation. + + Making a new allocation of that size is not guaranteed to succeed because of + possible additional margin required to respect alignment and buffer/image + granularity. + */ + VkDeviceSize unusedRangeSizeMax; + /** \brief Number of `VkDeviceMemory` blocks allocated for this pool. + */ + size_t blockCount; +} VmaPoolStats; + +/** \brief Allocates Vulkan device memory and creates #VmaPool object. + +@param allocator Allocator object. +@param pCreateInfo Parameters of pool to create. +@param[out] pPool Handle to created pool. +*/ +VkResult vmaCreatePool( + VmaAllocator allocator, + const VmaPoolCreateInfo* pCreateInfo, + VmaPool* pPool); + +/** \brief Destroys #VmaPool object and frees Vulkan device memory. +*/ +void vmaDestroyPool( + VmaAllocator allocator, + VmaPool pool); + +/** \brief Retrieves statistics of existing #VmaPool object. + +@param allocator Allocator object. +@param pool Pool object. +@param[out] pPoolStats Statistics of specified pool. +*/ +void vmaGetPoolStats( + VmaAllocator allocator, + VmaPool pool, + VmaPoolStats* pPoolStats); + +/** \brief Marks all allocations in given pool as lost if they are not used in current frame or VmaPoolCreateInfo::frameInUseCount back from now. + +@param allocator Allocator object. +@param pool Pool. +@param[out] pLostAllocationCount Number of allocations marked as lost. Optional - pass null if you don't need this information. +*/ +void vmaMakePoolAllocationsLost( + VmaAllocator allocator, + VmaPool pool, + size_t* pLostAllocationCount); + +/** \brief Checks magic number in margins around all allocations in given memory pool in search for corruptions. + +Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero, +`VMA_DEBUG_MARGIN` is defined to nonzero and the pool is created in memory type that is +`HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection). + +Possible return values: + +- `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for specified pool. +- `VK_SUCCESS` - corruption detection has been performed and succeeded. +- `VK_ERROR_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations. + `VMA_ASSERT` is also fired in that case. +- Other value: Error returned by Vulkan, e.g. memory mapping failure. +*/ +VkResult vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool); + +/** \struct VmaAllocation +\brief Represents single memory allocation. + +It may be either dedicated block of `VkDeviceMemory` or a specific region of a bigger block of this type +plus unique offset. + +There are multiple ways to create such object. +You need to fill structure VmaAllocationCreateInfo. +For more information see [Choosing memory type](@ref choosing_memory_type). + +Although the library provides convenience functions that create Vulkan buffer or image, +allocate memory for it and bind them together, +binding of the allocation to a buffer or an image is out of scope of the allocation itself. +Allocation object can exist without buffer/image bound, +binding can be done manually by the user, and destruction of it can be done +independently of destruction of the allocation. + +The object also remembers its size and some other information. +To retrieve this information, use function vmaGetAllocationInfo() and inspect +returned structure VmaAllocationInfo. + +Some kinds allocations can be in lost state. +For more information, see [Lost allocations](@ref lost_allocations). +*/ +VK_DEFINE_HANDLE(VmaAllocation) + +/** \brief Parameters of #VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo(). +*/ +typedef struct VmaAllocationInfo { + /** \brief Memory type index that this allocation was allocated from. + + It never changes. + */ + uint32_t memoryType; + /** \brief Handle to Vulkan memory object. + + Same memory object can be shared by multiple allocations. + + It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost. + + If the allocation is lost, it is equal to `VK_NULL_HANDLE`. + */ + VkDeviceMemory deviceMemory; + /** \brief Offset into deviceMemory object to the beginning of this allocation, in bytes. (deviceMemory, offset) pair is unique to this allocation. + + It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost. + */ + VkDeviceSize offset; + /** \brief Size of this allocation, in bytes. + + It never changes, unless allocation is lost. + */ + VkDeviceSize size; + /** \brief Pointer to the beginning of this allocation as mapped data. + + If the allocation hasn't been mapped using vmaMapMemory() and hasn't been + created with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag, this value null. + + It can change after call to vmaMapMemory(), vmaUnmapMemory(). + It can also change after call to vmaDefragment() if this allocation is passed to the function. + */ + void* pMappedData; + /** \brief Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vmaSetAllocationUserData(). + + It can change after call to vmaSetAllocationUserData() for this allocation. + */ + void* pUserData; +} VmaAllocationInfo; + +/** \brief General purpose memory allocation. + +@param[out] pAllocation Handle to allocated memory. +@param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo(). + +You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages(). + +It is recommended to use vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(), +vmaCreateBuffer(), vmaCreateImage() instead whenever possible. +*/ +VkResult vmaAllocateMemory( + VmaAllocator allocator, + const VkMemoryRequirements* pVkMemoryRequirements, + const VmaAllocationCreateInfo* pCreateInfo, + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo); + +/** \brief General purpose memory allocation for multiple allocation objects at once. + +@param allocator Allocator object. +@param pVkMemoryRequirements Memory requirements for each allocation. +@param pCreateInfo Creation parameters for each alloction. +@param allocationCount Number of allocations to make. +@param[out] pAllocations Pointer to array that will be filled with handles to created allocations. +@param[out] pAllocationInfo Optional. Pointer to array that will be filled with parameters of created allocations. + +You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages(). + +Word "pages" is just a suggestion to use this function to allocate pieces of memory needed for sparse binding. +It is just a general purpose allocation function able to make multiple allocations at once. +It may be internally optimized to be more efficient than calling vmaAllocateMemory() `allocationCount` times. + +All allocations are made using same parameters. All of them are created out of the same memory pool and type. +If any allocation fails, all allocations already made within this function call are also freed, so that when +returned result is not `VK_SUCCESS`, `pAllocation` array is always entirely filled with `VK_NULL_HANDLE`. +*/ +VkResult vmaAllocateMemoryPages( + VmaAllocator allocator, + const VkMemoryRequirements* pVkMemoryRequirements, + const VmaAllocationCreateInfo* pCreateInfo, + size_t allocationCount, + VmaAllocation* pAllocations, + VmaAllocationInfo* pAllocationInfo); + +/** +@param[out] pAllocation Handle to allocated memory. +@param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo(). + +You should free the memory using vmaFreeMemory(). +*/ +VkResult vmaAllocateMemoryForBuffer( + VmaAllocator allocator, + VkBuffer buffer, + const VmaAllocationCreateInfo* pCreateInfo, + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo); + +/// Function similar to vmaAllocateMemoryForBuffer(). +VkResult vmaAllocateMemoryForImage( + VmaAllocator allocator, + VkImage image, + const VmaAllocationCreateInfo* pCreateInfo, + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo); + +/** \brief Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage(). + +Passing `VK_NULL_HANDLE` as `allocation` is valid. Such function call is just skipped. +*/ +void vmaFreeMemory( + VmaAllocator allocator, + VmaAllocation allocation); + +/** \brief Frees memory and destroys multiple allocations. + +Word "pages" is just a suggestion to use this function to free pieces of memory used for sparse binding. +It is just a general purpose function to free memory and destroy allocations made using e.g. vmaAllocateMemory(), +vmaAllocateMemoryPages() and other functions. +It may be internally optimized to be more efficient than calling vmaFreeMemory() `allocationCount` times. + +Allocations in `pAllocations` array can come from any memory pools and types. +Passing `VK_NULL_HANDLE` as elements of `pAllocations` array is valid. Such entries are just skipped. +*/ +void vmaFreeMemoryPages( + VmaAllocator allocator, + size_t allocationCount, + VmaAllocation* pAllocations); + +/** \brief Tries to resize an allocation in place, if there is enough free memory after it. + +Tries to change allocation's size without moving or reallocating it. +You can both shrink and grow allocation size. +When growing, it succeeds only when the allocation belongs to a memory block with enough +free space after it. + +Returns `VK_SUCCESS` if allocation's size has been successfully changed. +Returns `VK_ERROR_OUT_OF_POOL_MEMORY` if allocation's size could not be changed. + +After successful call to this function, VmaAllocationInfo::size of this allocation changes. +All other parameters stay the same: memory pool and type, alignment, offset, mapped pointer. + +- Calling this function on allocation that is in lost state fails with result `VK_ERROR_VALIDATION_FAILED_EXT`. +- Calling this function with `newSize` same as current allocation size does nothing and returns `VK_SUCCESS`. +- Resizing dedicated allocations, as well as allocations created in pools that use linear + or buddy algorithm, is not supported. + The function returns `VK_ERROR_FEATURE_NOT_PRESENT` in such cases. + Support may be added in the future. +*/ +VkResult vmaResizeAllocation( + VmaAllocator allocator, + VmaAllocation allocation, + VkDeviceSize newSize); + +/** \brief Returns current information about specified allocation and atomically marks it as used in current frame. + +Current paramters of given allocation are returned in `pAllocationInfo`. + +This function also atomically "touches" allocation - marks it as used in current frame, +just like vmaTouchAllocation(). +If the allocation is in lost state, `pAllocationInfo->deviceMemory == VK_NULL_HANDLE`. + +Although this function uses atomics and doesn't lock any mutex, so it should be quite efficient, +you can avoid calling it too often. + +- You can retrieve same VmaAllocationInfo structure while creating your resource, from function + vmaCreateBuffer(), vmaCreateImage(). You can remember it if you are sure parameters don't change + (e.g. due to defragmentation or allocation becoming lost). +- If you just want to check if allocation is not lost, vmaTouchAllocation() will work faster. +*/ +void vmaGetAllocationInfo( + VmaAllocator allocator, + VmaAllocation allocation, + VmaAllocationInfo* pAllocationInfo); + +/** \brief Returns `VK_TRUE` if allocation is not lost and atomically marks it as used in current frame. + +If the allocation has been created with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag, +this function returns `VK_TRUE` if it's not in lost state, so it can still be used. +It then also atomically "touches" the allocation - marks it as used in current frame, +so that you can be sure it won't become lost in current frame or next `frameInUseCount` frames. + +If the allocation is in lost state, the function returns `VK_FALSE`. +Memory of such allocation, as well as buffer or image bound to it, should not be used. +Lost allocation and the buffer/image still need to be destroyed. + +If the allocation has been created without #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag, +this function always returns `VK_TRUE`. +*/ +VkBool32 vmaTouchAllocation( + VmaAllocator allocator, + VmaAllocation allocation); + +/** \brief Sets pUserData in given allocation to new value. + +If the allocation was created with VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT, +pUserData must be either null, or pointer to a null-terminated string. The function +makes local copy of the string and sets it as allocation's `pUserData`. String +passed as pUserData doesn't need to be valid for whole lifetime of the allocation - +you can free it after this call. String previously pointed by allocation's +pUserData is freed from memory. + +If the flag was not used, the value of pointer `pUserData` is just copied to +allocation's `pUserData`. It is opaque, so you can use it however you want - e.g. +as a pointer, ordinal number or some handle to you own data. +*/ +void vmaSetAllocationUserData( + VmaAllocator allocator, + VmaAllocation allocation, + void* pUserData); + +/** \brief Creates new allocation that is in lost state from the beginning. + +It can be useful if you need a dummy, non-null allocation. + +You still need to destroy created object using vmaFreeMemory(). + +Returned allocation is not tied to any specific memory pool or memory type and +not bound to any image or buffer. It has size = 0. It cannot be turned into +a real, non-empty allocation. +*/ +void vmaCreateLostAllocation( + VmaAllocator allocator, + VmaAllocation* pAllocation); + +/** \brief Maps memory represented by given allocation and returns pointer to it. + +Maps memory represented by given allocation to make it accessible to CPU code. +When succeeded, `*ppData` contains pointer to first byte of this memory. +If the allocation is part of bigger `VkDeviceMemory` block, the pointer is +correctly offseted to the beginning of region assigned to this particular +allocation. + +Mapping is internally reference-counted and synchronized, so despite raw Vulkan +function `vkMapMemory()` cannot be used to map same block of `VkDeviceMemory` +multiple times simultaneously, it is safe to call this function on allocations +assigned to the same memory block. Actual Vulkan memory will be mapped on first +mapping and unmapped on last unmapping. + +If the function succeeded, you must call vmaUnmapMemory() to unmap the +allocation when mapping is no longer needed or before freeing the allocation, at +the latest. + +It also safe to call this function multiple times on the same allocation. You +must call vmaUnmapMemory() same number of times as you called vmaMapMemory(). + +It is also safe to call this function on allocation created with +#VMA_ALLOCATION_CREATE_MAPPED_BIT flag. Its memory stays mapped all the time. +You must still call vmaUnmapMemory() same number of times as you called +vmaMapMemory(). You must not call vmaUnmapMemory() additional time to free the +"0-th" mapping made automatically due to #VMA_ALLOCATION_CREATE_MAPPED_BIT flag. + +This function fails when used on allocation made in memory type that is not +`HOST_VISIBLE`. + +This function always fails when called for allocation that was created with +#VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocations cannot be +mapped. +*/ +VkResult vmaMapMemory( + VmaAllocator allocator, + VmaAllocation allocation, + void** ppData); + +/** \brief Unmaps memory represented by given allocation, mapped previously using vmaMapMemory(). + +For details, see description of vmaMapMemory(). +*/ +void vmaUnmapMemory( + VmaAllocator allocator, + VmaAllocation allocation); + +/** \brief Flushes memory of given allocation. + +Calls `vkFlushMappedMemoryRanges()` for memory associated with given range of given allocation. + +- `offset` must be relative to the beginning of allocation. +- `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation. +- `offset` and `size` don't have to be aligned. + They are internally rounded down/up to multiply of `nonCoherentAtomSize`. +- If `size` is 0, this call is ignored. +- If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`, + this call is ignored. +*/ +void vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size); + +/** \brief Invalidates memory of given allocation. + +Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given range of given allocation. + +- `offset` must be relative to the beginning of allocation. +- `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation. +- `offset` and `size` don't have to be aligned. + They are internally rounded down/up to multiply of `nonCoherentAtomSize`. +- If `size` is 0, this call is ignored. +- If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`, + this call is ignored. +*/ +void vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size); + +/** \brief Checks magic number in margins around all allocations in given memory types (in both default and custom pools) in search for corruptions. + +@param memoryTypeBits Bit mask, where each bit set means that a memory type with that index should be checked. + +Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero, +`VMA_DEBUG_MARGIN` is defined to nonzero and only for memory types that are +`HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection). + +Possible return values: + +- `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for any of specified memory types. +- `VK_SUCCESS` - corruption detection has been performed and succeeded. +- `VK_ERROR_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations. + `VMA_ASSERT` is also fired in that case. +- Other value: Error returned by Vulkan, e.g. memory mapping failure. +*/ +VkResult vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits); + +/** \struct VmaDefragmentationContext +\brief Represents Opaque object that represents started defragmentation process. + +Fill structure #VmaDefragmentationInfo2 and call function vmaDefragmentationBegin() to create it. +Call function vmaDefragmentationEnd() to destroy it. +*/ +VK_DEFINE_HANDLE(VmaDefragmentationContext) + +/// Flags to be used in vmaDefragmentationBegin(). None at the moment. Reserved for future use. +typedef enum VmaDefragmentationFlagBits { + VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VmaDefragmentationFlagBits; +typedef VkFlags VmaDefragmentationFlags; + +/** \brief Parameters for defragmentation. + +To be used with function vmaDefragmentationBegin(). +*/ +typedef struct VmaDefragmentationInfo2 { + /** \brief Reserved for future use. Should be 0. + */ + VmaDefragmentationFlags flags; + /** \brief Number of allocations in `pAllocations` array. + */ + uint32_t allocationCount; + /** \brief Pointer to array of allocations that can be defragmented. + + The array should have `allocationCount` elements. + The array should not contain nulls. + Elements in the array should be unique - same allocation cannot occur twice. + It is safe to pass allocations that are in the lost state - they are ignored. + All allocations not present in this array are considered non-moveable during this defragmentation. + */ + VmaAllocation* pAllocations; + /** \brief Optional, output. Pointer to array that will be filled with information whether the allocation at certain index has been changed during defragmentation. + + The array should have `allocationCount` elements. + You can pass null if you are not interested in this information. + */ + VkBool32* pAllocationsChanged; + /** \brief Numer of pools in `pPools` array. + */ + uint32_t poolCount; + /** \brief Either null or pointer to array of pools to be defragmented. + + All the allocations in the specified pools can be moved during defragmentation + and there is no way to check if they were really moved as in `pAllocationsChanged`, + so you must query all the allocations in all these pools for new `VkDeviceMemory` + and offset using vmaGetAllocationInfo() if you might need to recreate buffers + and images bound to them. + + The array should have `poolCount` elements. + The array should not contain nulls. + Elements in the array should be unique - same pool cannot occur twice. + + Using this array is equivalent to specifying all allocations from the pools in `pAllocations`. + It might be more efficient. + */ + VmaPool* pPools; + /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on CPU side, like `memcpy()`, `memmove()`. + + `VK_WHOLE_SIZE` means no limit. + */ + VkDeviceSize maxCpuBytesToMove; + /** \brief Maximum number of allocations that can be moved to a different place using transfers on CPU side, like `memcpy()`, `memmove()`. + + `UINT32_MAX` means no limit. + */ + uint32_t maxCpuAllocationsToMove; + /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on GPU side, posted to `commandBuffer`. + + `VK_WHOLE_SIZE` means no limit. + */ + VkDeviceSize maxGpuBytesToMove; + /** \brief Maximum number of allocations that can be moved to a different place using transfers on GPU side, posted to `commandBuffer`. + + `UINT32_MAX` means no limit. + */ + uint32_t maxGpuAllocationsToMove; + /** \brief Optional. Command buffer where GPU copy commands will be posted. + + If not null, it must be a valid command buffer handle that supports Transfer queue type. + It must be in the recording state and outside of a render pass instance. + You need to submit it and make sure it finished execution before calling vmaDefragmentationEnd(). + + Passing null means that only CPU defragmentation will be performed. + */ + VkCommandBuffer commandBuffer; +} VmaDefragmentationInfo2; + +/** \brief Deprecated. Optional configuration parameters to be passed to function vmaDefragment(). + +\deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead. +*/ +typedef struct VmaDefragmentationInfo { + /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places. + + Default is `VK_WHOLE_SIZE`, which means no limit. + */ + VkDeviceSize maxBytesToMove; + /** \brief Maximum number of allocations that can be moved to different place. + + Default is `UINT32_MAX`, which means no limit. + */ + uint32_t maxAllocationsToMove; +} VmaDefragmentationInfo; + +/** \brief Statistics returned by function vmaDefragment(). */ +typedef struct VmaDefragmentationStats { + /// Total number of bytes that have been copied while moving allocations to different places. + VkDeviceSize bytesMoved; + /// Total number of bytes that have been released to the system by freeing empty `VkDeviceMemory` objects. + VkDeviceSize bytesFreed; + /// Number of allocations that have been moved to different places. + uint32_t allocationsMoved; + /// Number of empty `VkDeviceMemory` objects that have been released to the system. + uint32_t deviceMemoryBlocksFreed; +} VmaDefragmentationStats; + +/** \brief Begins defragmentation process. + +@param allocator Allocator object. +@param pInfo Structure filled with parameters of defragmentation. +@param[out] pStats Optional. Statistics of defragmentation. You can pass null if you are not interested in this information. +@param[out] pContext Context object that must be passed to vmaDefragmentationEnd() to finish defragmentation. +@return `VK_SUCCESS` and `*pContext == null` if defragmentation finished within this function call. `VK_NOT_READY` and `*pContext != null` if defragmentation has been started and you need to call vmaDefragmentationEnd() to finish it. Negative value in case of error. + +Use this function instead of old, deprecated vmaDefragment(). + +Warning! Between the call to vmaDefragmentationBegin() and vmaDefragmentationEnd(): + +- You should not use any of allocations passed as `pInfo->pAllocations` or + any allocations that belong to pools passed as `pInfo->pPools`, + including calling vmaGetAllocationInfo(), vmaTouchAllocation(), or access + their data. +- Some mutexes protecting internal data structures may be locked, so trying to + make or free any allocations, bind buffers or images, map memory, or launch + another simultaneous defragmentation in between may cause stall (when done on + another thread) or deadlock (when done on the same thread), unless you are + 100% sure that defragmented allocations are in different pools. +- Information returned via `pStats` and `pInfo->pAllocationsChanged` are undefined. + They become valid after call to vmaDefragmentationEnd(). +- If `pInfo->commandBuffer` is not null, you must submit that command buffer + and make sure it finished execution before calling vmaDefragmentationEnd(). +*/ +VkResult vmaDefragmentationBegin( + VmaAllocator allocator, + const VmaDefragmentationInfo2* pInfo, + VmaDefragmentationStats* pStats, + VmaDefragmentationContext *pContext); + +/** \brief Ends defragmentation process. + +Use this function to finish defragmentation started by vmaDefragmentationBegin(). +It is safe to pass `context == null`. The function then does nothing. +*/ +VkResult vmaDefragmentationEnd( + VmaAllocator allocator, + VmaDefragmentationContext context); + +/** \brief Deprecated. Compacts memory by moving allocations. + +@param pAllocations Array of allocations that can be moved during this compation. +@param allocationCount Number of elements in pAllocations and pAllocationsChanged arrays. +@param[out] pAllocationsChanged Array of boolean values that will indicate whether matching allocation in pAllocations array has been moved. This parameter is optional. Pass null if you don't need this information. +@param pDefragmentationInfo Configuration parameters. Optional - pass null to use default values. +@param[out] pDefragmentationStats Statistics returned by the function. Optional - pass null if you don't need this information. +@return `VK_SUCCESS` if completed, negative error code in case of error. + +\deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead. + +This function works by moving allocations to different places (different +`VkDeviceMemory` objects and/or different offsets) in order to optimize memory +usage. Only allocations that are in `pAllocations` array can be moved. All other +allocations are considered nonmovable in this call. Basic rules: + +- Only allocations made in memory types that have + `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` and `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT` + flags can be compacted. You may pass other allocations but it makes no sense - + these will never be moved. +- Custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT or + #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag are not defragmented. Allocations + passed to this function that come from such pools are ignored. +- Allocations created with #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT or + created as dedicated allocations for any other reason are also ignored. +- Both allocations made with or without #VMA_ALLOCATION_CREATE_MAPPED_BIT + flag can be compacted. If not persistently mapped, memory will be mapped + temporarily inside this function if needed. +- You must not pass same #VmaAllocation object multiple times in `pAllocations` array. + +The function also frees empty `VkDeviceMemory` blocks. + +Warning: This function may be time-consuming, so you shouldn't call it too often +(like after every resource creation/destruction). +You can call it on special occasions (like when reloading a game level or +when you just destroyed a lot of objects). Calling it every frame may be OK, but +you should measure that on your platform. + +For more information, see [Defragmentation](@ref defragmentation) chapter. +*/ +VkResult vmaDefragment( + VmaAllocator allocator, + VmaAllocation* pAllocations, + size_t allocationCount, + VkBool32* pAllocationsChanged, + const VmaDefragmentationInfo *pDefragmentationInfo, + VmaDefragmentationStats* pDefragmentationStats); + +/** \brief Binds buffer to allocation. + +Binds specified buffer to region of memory represented by specified allocation. +Gets `VkDeviceMemory` handle and offset from the allocation. +If you want to create a buffer, allocate memory for it and bind them together separately, +you should use this function for binding instead of standard `vkBindBufferMemory()`, +because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple +allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously +(which is illegal in Vulkan). + +It is recommended to use function vmaCreateBuffer() instead of this one. +*/ +VkResult vmaBindBufferMemory( + VmaAllocator allocator, + VmaAllocation allocation, + VkBuffer buffer); + +/** \brief Binds image to allocation. + +Binds specified image to region of memory represented by specified allocation. +Gets `VkDeviceMemory` handle and offset from the allocation. +If you want to create an image, allocate memory for it and bind them together separately, +you should use this function for binding instead of standard `vkBindImageMemory()`, +because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple +allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously +(which is illegal in Vulkan). + +It is recommended to use function vmaCreateImage() instead of this one. +*/ +VkResult vmaBindImageMemory( + VmaAllocator allocator, + VmaAllocation allocation, + VkImage image); + +/** +@param[out] pBuffer Buffer that was created. +@param[out] pAllocation Allocation that was created. +@param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo(). + +This function automatically: + +-# Creates buffer. +-# Allocates appropriate memory for it. +-# Binds the buffer with the memory. + +If any of these operations fail, buffer and allocation are not created, +returned value is negative error code, *pBuffer and *pAllocation are null. + +If the function succeeded, you must destroy both buffer and allocation when you +no longer need them using either convenience function vmaDestroyBuffer() or +separately, using `vkDestroyBuffer()` and vmaFreeMemory(). + +If VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag was used, +VK_KHR_dedicated_allocation extension is used internally to query driver whether +it requires or prefers the new buffer to have dedicated allocation. If yes, +and if dedicated allocation is possible (VmaAllocationCreateInfo::pool is null +and VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT is not used), it creates dedicated +allocation for this buffer, just like when using +VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. +*/ +VkResult vmaCreateBuffer( + VmaAllocator allocator, + const VkBufferCreateInfo* pBufferCreateInfo, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + VkBuffer* pBuffer, + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo); + +/** \brief Destroys Vulkan buffer and frees allocated memory. + +This is just a convenience function equivalent to: + +\code +vkDestroyBuffer(device, buffer, allocationCallbacks); +vmaFreeMemory(allocator, allocation); +\endcode + +It it safe to pass null as buffer and/or allocation. +*/ +void vmaDestroyBuffer( + VmaAllocator allocator, + VkBuffer buffer, + VmaAllocation allocation); + +/// Function similar to vmaCreateBuffer(). +VkResult vmaCreateImage( + VmaAllocator allocator, + const VkImageCreateInfo* pImageCreateInfo, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + VkImage* pImage, + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo); + +/** \brief Destroys Vulkan image and frees allocated memory. + +This is just a convenience function equivalent to: + +\code +vkDestroyImage(device, image, allocationCallbacks); +vmaFreeMemory(allocator, allocation); +\endcode + +It it safe to pass null as image and/or allocation. +*/ +void vmaDestroyImage( + VmaAllocator allocator, + VkImage image, + VmaAllocation allocation); + +#ifdef __cplusplus +} +#endif + +#endif // AMD_VULKAN_MEMORY_ALLOCATOR_H + +// For Visual Studio IntelliSense. +#if defined(__cplusplus) && defined(__INTELLISENSE__) +#define VMA_IMPLEMENTATION +#endif + +#ifdef VMA_IMPLEMENTATION +#undef VMA_IMPLEMENTATION + +#include +#include +#include + +/******************************************************************************* +CONFIGURATION SECTION + +Define some of these macros before each #include of this header or change them +here if you need other then default behavior depending on your environment. +*/ + +/* +Define this macro to 1 to make the library fetch pointers to Vulkan functions +internally, like: + + vulkanFunctions.vkAllocateMemory = &vkAllocateMemory; + +Define to 0 if you are going to provide you own pointers to Vulkan functions via +VmaAllocatorCreateInfo::pVulkanFunctions. +*/ +#if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES) +#define VMA_STATIC_VULKAN_FUNCTIONS 1 +#endif + +// Define this macro to 1 to make the library use STL containers instead of its own implementation. +//#define VMA_USE_STL_CONTAINERS 1 + +/* Set this macro to 1 to make the library including and using STL containers: +std::pair, std::vector, std::list, std::unordered_map. + +Set it to 0 or undefined to make the library using its own implementation of +the containers. +*/ +#if VMA_USE_STL_CONTAINERS + #define VMA_USE_STL_VECTOR 1 + #define VMA_USE_STL_UNORDERED_MAP 1 + #define VMA_USE_STL_LIST 1 +#endif + +#ifndef VMA_USE_STL_SHARED_MUTEX + // Minimum Visual Studio 2015 Update 2 + #if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918 + #define VMA_USE_STL_SHARED_MUTEX 1 + #endif +#endif + +#if VMA_USE_STL_VECTOR + #include +#endif + +#if VMA_USE_STL_UNORDERED_MAP + #include +#endif + +#if VMA_USE_STL_LIST + #include +#endif + +/* +Following headers are used in this CONFIGURATION section only, so feel free to +remove them if not needed. +*/ +#include // for assert +#include // for min, max +#include +#include // for std::atomic + +#ifndef VMA_NULL + // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0. + #define VMA_NULL nullptr +#endif + +#if defined(__ANDROID_API__) && (__ANDROID_API__ < 16) +#include +void *aligned_alloc(size_t alignment, size_t size) +{ + // alignment must be >= sizeof(void*) + if(alignment < sizeof(void*)) + { + alignment = sizeof(void*); + } + + return memalign(alignment, size); +} +#elif defined(__APPLE__) || defined(__ANDROID__) +#include +void *aligned_alloc(size_t alignment, size_t size) +{ + // alignment must be >= sizeof(void*) + if(alignment < sizeof(void*)) + { + alignment = sizeof(void*); + } + + void *pointer; + if(posix_memalign(&pointer, alignment, size) == 0) + return pointer; + return VMA_NULL; +} +#endif + +// If your compiler is not compatible with C++11 and definition of +// aligned_alloc() function is missing, uncommeting following line may help: + +//#include + +// Normal assert to check for programmer's errors, especially in Debug configuration. +#ifndef VMA_ASSERT + #ifdef _DEBUG + #define VMA_ASSERT(expr) assert(expr) + #else + #define VMA_ASSERT(expr) + #endif +#endif + +// Assert that will be called very often, like inside data structures e.g. operator[]. +// Making it non-empty can make program slow. +#ifndef VMA_HEAVY_ASSERT + #ifdef _DEBUG + #define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr) + #else + #define VMA_HEAVY_ASSERT(expr) + #endif +#endif + +#ifndef VMA_ALIGN_OF + #define VMA_ALIGN_OF(type) (__alignof(type)) +#endif + +#ifndef VMA_SYSTEM_ALIGNED_MALLOC + #if defined(_WIN32) + #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (_aligned_malloc((size), (alignment))) + #else + #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (aligned_alloc((alignment), (size) )) + #endif +#endif + +#ifndef VMA_SYSTEM_FREE + #if defined(_WIN32) + #define VMA_SYSTEM_FREE(ptr) _aligned_free(ptr) + #else + #define VMA_SYSTEM_FREE(ptr) free(ptr) + #endif +#endif + +#ifndef VMA_MIN + #define VMA_MIN(v1, v2) (std::min((v1), (v2))) +#endif + +#ifndef VMA_MAX + #define VMA_MAX(v1, v2) (std::max((v1), (v2))) +#endif + +#ifndef VMA_SWAP + #define VMA_SWAP(v1, v2) std::swap((v1), (v2)) +#endif + +#ifndef VMA_SORT + #define VMA_SORT(beg, end, cmp) std::sort(beg, end, cmp) +#endif + +#ifndef VMA_DEBUG_LOG + #define VMA_DEBUG_LOG(format, ...) + /* + #define VMA_DEBUG_LOG(format, ...) do { \ + printf(format, __VA_ARGS__); \ + printf("\n"); \ + } while(false) + */ +#endif + +// Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString. +#if VMA_STATS_STRING_ENABLED + static inline void VmaUint32ToStr(char* outStr, size_t strLen, uint32_t num) + { + snprintf(outStr, strLen, "%u", static_cast(num)); + } + static inline void VmaUint64ToStr(char* outStr, size_t strLen, uint64_t num) + { + snprintf(outStr, strLen, "%llu", static_cast(num)); + } + static inline void VmaPtrToStr(char* outStr, size_t strLen, const void* ptr) + { + snprintf(outStr, strLen, "%p", ptr); + } +#endif + +#ifndef VMA_MUTEX + class VmaMutex + { + public: + void Lock() { m_Mutex.lock(); } + void Unlock() { m_Mutex.unlock(); } + private: + std::mutex m_Mutex; + }; + #define VMA_MUTEX VmaMutex +#endif + +// Read-write mutex, where "read" is shared access, "write" is exclusive access. +#ifndef VMA_RW_MUTEX + #if VMA_USE_STL_SHARED_MUTEX + // Use std::shared_mutex from C++17. + #include + class VmaRWMutex + { + public: + void LockRead() { m_Mutex.lock_shared(); } + void UnlockRead() { m_Mutex.unlock_shared(); } + void LockWrite() { m_Mutex.lock(); } + void UnlockWrite() { m_Mutex.unlock(); } + private: + std::shared_mutex m_Mutex; + }; + #define VMA_RW_MUTEX VmaRWMutex + #elif defined(_WIN32) + // Use SRWLOCK from WinAPI. + class VmaRWMutex + { + public: + VmaRWMutex() { InitializeSRWLock(&m_Lock); } + void LockRead() { AcquireSRWLockShared(&m_Lock); } + void UnlockRead() { ReleaseSRWLockShared(&m_Lock); } + void LockWrite() { AcquireSRWLockExclusive(&m_Lock); } + void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); } + private: + SRWLOCK m_Lock; + }; + #define VMA_RW_MUTEX VmaRWMutex + #else + // Less efficient fallback: Use normal mutex. + class VmaRWMutex + { + public: + void LockRead() { m_Mutex.Lock(); } + void UnlockRead() { m_Mutex.Unlock(); } + void LockWrite() { m_Mutex.Lock(); } + void UnlockWrite() { m_Mutex.Unlock(); } + private: + VMA_MUTEX m_Mutex; + }; + #define VMA_RW_MUTEX VmaRWMutex + #endif // #if VMA_USE_STL_SHARED_MUTEX +#endif // #ifndef VMA_RW_MUTEX + +/* +If providing your own implementation, you need to implement a subset of std::atomic: + +- Constructor(uint32_t desired) +- uint32_t load() const +- void store(uint32_t desired) +- bool compare_exchange_weak(uint32_t& expected, uint32_t desired) +*/ +#ifndef VMA_ATOMIC_UINT32 + #define VMA_ATOMIC_UINT32 std::atomic +#endif + +#ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY + /** + Every allocation will have its own memory block. + Define to 1 for debugging purposes only. + */ + #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0) +#endif + +#ifndef VMA_DEBUG_ALIGNMENT + /** + Minimum alignment of all allocations, in bytes. + Set to more than 1 for debugging purposes only. Must be power of two. + */ + #define VMA_DEBUG_ALIGNMENT (1) +#endif + +#ifndef VMA_DEBUG_MARGIN + /** + Minimum margin before and after every allocation, in bytes. + Set nonzero for debugging purposes only. + */ + #define VMA_DEBUG_MARGIN (0) +#endif + +#ifndef VMA_DEBUG_INITIALIZE_ALLOCATIONS + /** + Define this macro to 1 to automatically fill new allocations and destroyed + allocations with some bit pattern. + */ + #define VMA_DEBUG_INITIALIZE_ALLOCATIONS (0) +#endif + +#ifndef VMA_DEBUG_DETECT_CORRUPTION + /** + Define this macro to 1 together with non-zero value of VMA_DEBUG_MARGIN to + enable writing magic value to the margin before and after every allocation and + validating it, so that memory corruptions (out-of-bounds writes) are detected. + */ + #define VMA_DEBUG_DETECT_CORRUPTION (0) +#endif + +#ifndef VMA_DEBUG_GLOBAL_MUTEX + /** + Set this to 1 for debugging purposes only, to enable single mutex protecting all + entry calls to the library. Can be useful for debugging multithreading issues. + */ + #define VMA_DEBUG_GLOBAL_MUTEX (0) +#endif + +#ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY + /** + Minimum value for VkPhysicalDeviceLimits::bufferImageGranularity. + Set to more than 1 for debugging purposes only. Must be power of two. + */ + #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1) +#endif + +#ifndef VMA_SMALL_HEAP_MAX_SIZE + /// Maximum size of a memory heap in Vulkan to consider it "small". + #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024) +#endif + +#ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE + /// Default size of a block allocated as single VkDeviceMemory from a "large" heap. + #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024) +#endif + +#ifndef VMA_CLASS_NO_COPY + #define VMA_CLASS_NO_COPY(className) \ + private: \ + className(const className&) = delete; \ + className& operator=(const className&) = delete; +#endif + +static const uint32_t VMA_FRAME_INDEX_LOST = UINT32_MAX; + +// Decimal 2139416166, float NaN, little-endian binary 66 E6 84 7F. +static const uint32_t VMA_CORRUPTION_DETECTION_MAGIC_VALUE = 0x7F84E666; + +static const uint8_t VMA_ALLOCATION_FILL_PATTERN_CREATED = 0xDC; +static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF; + +/******************************************************************************* +END OF CONFIGURATION +*/ + +static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u; + +static VkAllocationCallbacks VmaEmptyAllocationCallbacks = { + VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL }; + +// Returns number of bits set to 1 in (v). +static inline uint32_t VmaCountBitsSet(uint32_t v) +{ + uint32_t c = v - ((v >> 1) & 0x55555555); + c = ((c >> 2) & 0x33333333) + (c & 0x33333333); + c = ((c >> 4) + c) & 0x0F0F0F0F; + c = ((c >> 8) + c) & 0x00FF00FF; + c = ((c >> 16) + c) & 0x0000FFFF; + return c; +} + +// Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16. +// Use types like uint32_t, uint64_t as T. +template +static inline T VmaAlignUp(T val, T align) +{ + return (val + align - 1) / align * align; +} +// Aligns given value down to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 8. +// Use types like uint32_t, uint64_t as T. +template +static inline T VmaAlignDown(T val, T align) +{ + return val / align * align; +} + +// Division with mathematical rounding to nearest number. +template +static inline T VmaRoundDiv(T x, T y) +{ + return (x + (y / (T)2)) / y; +} + +/* +Returns true if given number is a power of two. +T must be unsigned integer number or signed integer but always nonnegative. +For 0 returns true. +*/ +template +inline bool VmaIsPow2(T x) +{ + return (x & (x-1)) == 0; +} + +// Returns smallest power of 2 greater or equal to v. +static inline uint32_t VmaNextPow2(uint32_t v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +} +static inline uint64_t VmaNextPow2(uint64_t v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v |= v >> 32; + v++; + return v; +} + +// Returns largest power of 2 less or equal to v. +static inline uint32_t VmaPrevPow2(uint32_t v) +{ + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v = v ^ (v >> 1); + return v; +} +static inline uint64_t VmaPrevPow2(uint64_t v) +{ + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v |= v >> 32; + v = v ^ (v >> 1); + return v; +} + +static inline bool VmaStrIsEmpty(const char* pStr) +{ + return pStr == VMA_NULL || *pStr == '\0'; +} + +static const char* VmaAlgorithmToStr(uint32_t algorithm) +{ + switch(algorithm) + { + case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT: + return "Linear"; + case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT: + return "Buddy"; + case 0: + return "Default"; + default: + VMA_ASSERT(0); + return ""; + } +} + +#ifndef VMA_SORT + +template +Iterator VmaQuickSortPartition(Iterator beg, Iterator end, Compare cmp) +{ + Iterator centerValue = end; --centerValue; + Iterator insertIndex = beg; + for(Iterator memTypeIndex = beg; memTypeIndex < centerValue; ++memTypeIndex) + { + if(cmp(*memTypeIndex, *centerValue)) + { + if(insertIndex != memTypeIndex) + { + VMA_SWAP(*memTypeIndex, *insertIndex); + } + ++insertIndex; + } + } + if(insertIndex != centerValue) + { + VMA_SWAP(*insertIndex, *centerValue); + } + return insertIndex; +} + +template +void VmaQuickSort(Iterator beg, Iterator end, Compare cmp) +{ + if(beg < end) + { + Iterator it = VmaQuickSortPartition(beg, end, cmp); + VmaQuickSort(beg, it, cmp); + VmaQuickSort(it + 1, end, cmp); + } +} + +#define VMA_SORT(beg, end, cmp) VmaQuickSort(beg, end, cmp) + +#endif // #ifndef VMA_SORT + +/* +Returns true if two memory blocks occupy overlapping pages. +ResourceA must be in less memory offset than ResourceB. + +Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)" +chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity". +*/ +static inline bool VmaBlocksOnSamePage( + VkDeviceSize resourceAOffset, + VkDeviceSize resourceASize, + VkDeviceSize resourceBOffset, + VkDeviceSize pageSize) +{ + VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0); + VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1; + VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1); + VkDeviceSize resourceBStart = resourceBOffset; + VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1); + return resourceAEndPage == resourceBStartPage; +} + +enum VmaSuballocationType +{ + VMA_SUBALLOCATION_TYPE_FREE = 0, + VMA_SUBALLOCATION_TYPE_UNKNOWN = 1, + VMA_SUBALLOCATION_TYPE_BUFFER = 2, + VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3, + VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4, + VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5, + VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF +}; + +/* +Returns true if given suballocation types could conflict and must respect +VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer +or linear image and another one is optimal image. If type is unknown, behave +conservatively. +*/ +static inline bool VmaIsBufferImageGranularityConflict( + VmaSuballocationType suballocType1, + VmaSuballocationType suballocType2) +{ + if(suballocType1 > suballocType2) + { + VMA_SWAP(suballocType1, suballocType2); + } + + switch(suballocType1) + { + case VMA_SUBALLOCATION_TYPE_FREE: + return false; + case VMA_SUBALLOCATION_TYPE_UNKNOWN: + return true; + case VMA_SUBALLOCATION_TYPE_BUFFER: + return + suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN || + suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL; + case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN: + return + suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN || + suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR || + suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL; + case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR: + return + suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL; + case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL: + return false; + default: + VMA_ASSERT(0); + return true; + } +} + +static void VmaWriteMagicValue(void* pData, VkDeviceSize offset) +{ + uint32_t* pDst = (uint32_t*)((char*)pData + offset); + const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t); + for(size_t i = 0; i != numberCount; ++i, ++pDst) + { + *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE; + } +} + +static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset) +{ + const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset); + const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t); + for(size_t i = 0; i != numberCount; ++i, ++pSrc) + { + if(*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE) + { + return false; + } + } + return true; +} + +// Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope). +struct VmaMutexLock +{ + VMA_CLASS_NO_COPY(VmaMutexLock) +public: + VmaMutexLock(VMA_MUTEX& mutex, bool useMutex) : + m_pMutex(useMutex ? &mutex : VMA_NULL) + { if(m_pMutex) { m_pMutex->Lock(); } } + ~VmaMutexLock() + { if(m_pMutex) { m_pMutex->Unlock(); } } +private: + VMA_MUTEX* m_pMutex; +}; + +// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading. +struct VmaMutexLockRead +{ + VMA_CLASS_NO_COPY(VmaMutexLockRead) +public: + VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) : + m_pMutex(useMutex ? &mutex : VMA_NULL) + { if(m_pMutex) { m_pMutex->LockRead(); } } + ~VmaMutexLockRead() { if(m_pMutex) { m_pMutex->UnlockRead(); } } +private: + VMA_RW_MUTEX* m_pMutex; +}; + +// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing. +struct VmaMutexLockWrite +{ + VMA_CLASS_NO_COPY(VmaMutexLockWrite) +public: + VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex) : + m_pMutex(useMutex ? &mutex : VMA_NULL) + { if(m_pMutex) { m_pMutex->LockWrite(); } } + ~VmaMutexLockWrite() { if(m_pMutex) { m_pMutex->UnlockWrite(); } } +private: + VMA_RW_MUTEX* m_pMutex; +}; + +#if VMA_DEBUG_GLOBAL_MUTEX + static VMA_MUTEX gDebugGlobalMutex; + #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true); +#else + #define VMA_DEBUG_GLOBAL_MUTEX_LOCK +#endif + +// Minimum size of a free suballocation to register it in the free suballocation collection. +static const VkDeviceSize VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16; + +/* +Performs binary search and returns iterator to first element that is greater or +equal to (key), according to comparison (cmp). + +Cmp should return true if first argument is less than second argument. + +Returned value is the found element, if present in the collection or place where +new element with value (key) should be inserted. +*/ +template +static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, CmpLess cmp) +{ + size_t down = 0, up = (end - beg); + while(down < up) + { + const size_t mid = (down + up) / 2; + if(cmp(*(beg+mid), key)) + { + down = mid + 1; + } + else + { + up = mid; + } + } + return beg + down; +} + +/* +Returns true if all pointers in the array are not-null and unique. +Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT. +T must be pointer type, e.g. VmaAllocation, VmaPool. +*/ +template +static bool VmaValidatePointerArray(uint32_t count, const T* arr) +{ + for(uint32_t i = 0; i < count; ++i) + { + const T iPtr = arr[i]; + if(iPtr == VMA_NULL) + { + return false; + } + for(uint32_t j = i + 1; j < count; ++j) + { + if(iPtr == arr[j]) + { + return false; + } + } + } + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// Memory allocation + +static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment) +{ + if((pAllocationCallbacks != VMA_NULL) && + (pAllocationCallbacks->pfnAllocation != VMA_NULL)) + { + return (*pAllocationCallbacks->pfnAllocation)( + pAllocationCallbacks->pUserData, + size, + alignment, + VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); + } + else + { + return VMA_SYSTEM_ALIGNED_MALLOC(size, alignment); + } +} + +static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr) +{ + if((pAllocationCallbacks != VMA_NULL) && + (pAllocationCallbacks->pfnFree != VMA_NULL)) + { + (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr); + } + else + { + VMA_SYSTEM_FREE(ptr); + } +} + +template +static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks) +{ + return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T)); +} + +template +static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count) +{ + return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T)); +} + +#define vma_new(allocator, type) new(VmaAllocate(allocator))(type) + +#define vma_new_array(allocator, type, count) new(VmaAllocateArray((allocator), (count)))(type) + +template +static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr) +{ + ptr->~T(); + VmaFree(pAllocationCallbacks, ptr); +} + +template +static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count) +{ + if(ptr != VMA_NULL) + { + for(size_t i = count; i--; ) + { + ptr[i].~T(); + } + VmaFree(pAllocationCallbacks, ptr); + } +} + +// STL-compatible allocator. +template +class VmaStlAllocator +{ +public: + const VkAllocationCallbacks* const m_pCallbacks; + typedef T value_type; + + VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) { } + template VmaStlAllocator(const VmaStlAllocator& src) : m_pCallbacks(src.m_pCallbacks) { } + + T* allocate(size_t n) { return VmaAllocateArray(m_pCallbacks, n); } + void deallocate(T* p, size_t /*n*/) { VmaFree(m_pCallbacks, p); } + + template + bool operator==(const VmaStlAllocator& rhs) const + { + return m_pCallbacks == rhs.m_pCallbacks; + } + template + bool operator!=(const VmaStlAllocator& rhs) const + { + return m_pCallbacks != rhs.m_pCallbacks; + } + + VmaStlAllocator& operator=(const VmaStlAllocator& x) = delete; +}; + +#if VMA_USE_STL_VECTOR + +#define VmaVector std::vector + +template +static void VmaVectorInsert(std::vector& vec, size_t index, const T& item) +{ + vec.insert(vec.begin() + index, item); +} + +template +static void VmaVectorRemove(std::vector& vec, size_t index) +{ + vec.erase(vec.begin() + index); +} + +#else // #if VMA_USE_STL_VECTOR + +/* Class with interface compatible with subset of std::vector. +T must be POD because constructors and destructors are not called and memcpy is +used for these objects. */ +template +class VmaVector +{ +public: + typedef T value_type; + + VmaVector(const AllocatorT& allocator) : + m_Allocator(allocator), + m_pArray(VMA_NULL), + m_Count(0), + m_Capacity(0) + { + } + + VmaVector(size_t count, const AllocatorT& allocator) : + m_Allocator(allocator), + m_pArray(count ? (T*)VmaAllocateArray(allocator.m_pCallbacks, count) : VMA_NULL), + m_Count(count), + m_Capacity(count) + { + } + + VmaVector(const VmaVector& src) : + m_Allocator(src.m_Allocator), + m_pArray(src.m_Count ? (T*)VmaAllocateArray(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL), + m_Count(src.m_Count), + m_Capacity(src.m_Count) + { + if(m_Count != 0) + { + memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T)); + } + } + + ~VmaVector() + { + VmaFree(m_Allocator.m_pCallbacks, m_pArray); + } + + VmaVector& operator=(const VmaVector& rhs) + { + if(&rhs != this) + { + resize(rhs.m_Count); + if(m_Count != 0) + { + memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T)); + } + } + return *this; + } + + bool empty() const { return m_Count == 0; } + size_t size() const { return m_Count; } + T* data() { return m_pArray; } + const T* data() const { return m_pArray; } + + T& operator[](size_t index) + { + VMA_HEAVY_ASSERT(index < m_Count); + return m_pArray[index]; + } + const T& operator[](size_t index) const + { + VMA_HEAVY_ASSERT(index < m_Count); + return m_pArray[index]; + } + + T& front() + { + VMA_HEAVY_ASSERT(m_Count > 0); + return m_pArray[0]; + } + const T& front() const + { + VMA_HEAVY_ASSERT(m_Count > 0); + return m_pArray[0]; + } + T& back() + { + VMA_HEAVY_ASSERT(m_Count > 0); + return m_pArray[m_Count - 1]; + } + const T& back() const + { + VMA_HEAVY_ASSERT(m_Count > 0); + return m_pArray[m_Count - 1]; + } + + void reserve(size_t newCapacity, bool freeMemory = false) + { + newCapacity = VMA_MAX(newCapacity, m_Count); + + if((newCapacity < m_Capacity) && !freeMemory) + { + newCapacity = m_Capacity; + } + + if(newCapacity != m_Capacity) + { + T* const newArray = newCapacity ? VmaAllocateArray(m_Allocator, newCapacity) : VMA_NULL; + if(m_Count != 0) + { + memcpy(newArray, m_pArray, m_Count * sizeof(T)); + } + VmaFree(m_Allocator.m_pCallbacks, m_pArray); + m_Capacity = newCapacity; + m_pArray = newArray; + } + } + + void resize(size_t newCount, bool freeMemory = false) + { + size_t newCapacity = m_Capacity; + if(newCount > m_Capacity) + { + newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8)); + } + else if(freeMemory) + { + newCapacity = newCount; + } + + if(newCapacity != m_Capacity) + { + T* const newArray = newCapacity ? VmaAllocateArray(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL; + const size_t elementsToCopy = VMA_MIN(m_Count, newCount); + if(elementsToCopy != 0) + { + memcpy(newArray, m_pArray, elementsToCopy * sizeof(T)); + } + VmaFree(m_Allocator.m_pCallbacks, m_pArray); + m_Capacity = newCapacity; + m_pArray = newArray; + } + + m_Count = newCount; + } + + void clear(bool freeMemory = false) + { + resize(0, freeMemory); + } + + void insert(size_t index, const T& src) + { + VMA_HEAVY_ASSERT(index <= m_Count); + const size_t oldCount = size(); + resize(oldCount + 1); + if(index < oldCount) + { + memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T)); + } + m_pArray[index] = src; + } + + void remove(size_t index) + { + VMA_HEAVY_ASSERT(index < m_Count); + const size_t oldCount = size(); + if(index < oldCount - 1) + { + memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T)); + } + resize(oldCount - 1); + } + + void push_back(const T& src) + { + const size_t newIndex = size(); + resize(newIndex + 1); + m_pArray[newIndex] = src; + } + + void pop_back() + { + VMA_HEAVY_ASSERT(m_Count > 0); + resize(size() - 1); + } + + void push_front(const T& src) + { + insert(0, src); + } + + void pop_front() + { + VMA_HEAVY_ASSERT(m_Count > 0); + remove(0); + } + + typedef T* iterator; + + iterator begin() { return m_pArray; } + iterator end() { return m_pArray + m_Count; } + +private: + AllocatorT m_Allocator; + T* m_pArray; + size_t m_Count; + size_t m_Capacity; +}; + +template +static void VmaVectorInsert(VmaVector& vec, size_t index, const T& item) +{ + vec.insert(index, item); +} + +template +static void VmaVectorRemove(VmaVector& vec, size_t index) +{ + vec.remove(index); +} + +#endif // #if VMA_USE_STL_VECTOR + +template +size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value) +{ + const size_t indexToInsert = VmaBinaryFindFirstNotLess( + vector.data(), + vector.data() + vector.size(), + value, + CmpLess()) - vector.data(); + VmaVectorInsert(vector, indexToInsert, value); + return indexToInsert; +} + +template +bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value) +{ + CmpLess comparator; + typename VectorT::iterator it = VmaBinaryFindFirstNotLess( + vector.begin(), + vector.end(), + value, + comparator); + if((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it)) + { + size_t indexToRemove = it - vector.begin(); + VmaVectorRemove(vector, indexToRemove); + return true; + } + return false; +} + +template +IterT VmaVectorFindSorted(const IterT& beg, const IterT& end, const KeyT& value) +{ + CmpLess comparator; + IterT it = VmaBinaryFindFirstNotLess( + beg, end, value, comparator); + if(it == end || + (!comparator(*it, value) && !comparator(value, *it))) + { + return it; + } + return end; +} + +//////////////////////////////////////////////////////////////////////////////// +// class VmaPoolAllocator + +/* +Allocator for objects of type T using a list of arrays (pools) to speed up +allocation. Number of elements that can be allocated is not bounded because +allocator can create multiple blocks. +*/ +template +class VmaPoolAllocator +{ + VMA_CLASS_NO_COPY(VmaPoolAllocator) +public: + VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock); + ~VmaPoolAllocator(); + void Clear(); + T* Alloc(); + void Free(T* ptr); + +private: + union Item + { + uint32_t NextFreeIndex; + T Value; + }; + + struct ItemBlock + { + Item* pItems; + uint32_t FirstFreeIndex; + }; + + const VkAllocationCallbacks* m_pAllocationCallbacks; + size_t m_ItemsPerBlock; + VmaVector< ItemBlock, VmaStlAllocator > m_ItemBlocks; + + ItemBlock& CreateNewBlock(); +}; + +template +VmaPoolAllocator::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock) : + m_pAllocationCallbacks(pAllocationCallbacks), + m_ItemsPerBlock(itemsPerBlock), + m_ItemBlocks(VmaStlAllocator(pAllocationCallbacks)) +{ + VMA_ASSERT(itemsPerBlock > 0); +} + +template +VmaPoolAllocator::~VmaPoolAllocator() +{ + Clear(); +} + +template +void VmaPoolAllocator::Clear() +{ + for(size_t i = m_ItemBlocks.size(); i--; ) + vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemsPerBlock); + m_ItemBlocks.clear(); +} + +template +T* VmaPoolAllocator::Alloc() +{ + for(size_t i = m_ItemBlocks.size(); i--; ) + { + ItemBlock& block = m_ItemBlocks[i]; + // This block has some free items: Use first one. + if(block.FirstFreeIndex != UINT32_MAX) + { + Item* const pItem = &block.pItems[block.FirstFreeIndex]; + block.FirstFreeIndex = pItem->NextFreeIndex; + return &pItem->Value; + } + } + + // No block has free item: Create new one and use it. + ItemBlock& newBlock = CreateNewBlock(); + Item* const pItem = &newBlock.pItems[0]; + newBlock.FirstFreeIndex = pItem->NextFreeIndex; + return &pItem->Value; +} + +template +void VmaPoolAllocator::Free(T* ptr) +{ + // Search all memory blocks to find ptr. + for(size_t i = 0; i < m_ItemBlocks.size(); ++i) + { + ItemBlock& block = m_ItemBlocks[i]; + + // Casting to union. + Item* pItemPtr; + memcpy(&pItemPtr, &ptr, sizeof(pItemPtr)); + + // Check if pItemPtr is in address range of this block. + if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + m_ItemsPerBlock)) + { + const uint32_t index = static_cast(pItemPtr - block.pItems); + pItemPtr->NextFreeIndex = block.FirstFreeIndex; + block.FirstFreeIndex = index; + return; + } + } + VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool."); +} + +template +typename VmaPoolAllocator::ItemBlock& VmaPoolAllocator::CreateNewBlock() +{ + ItemBlock newBlock = { + vma_new_array(m_pAllocationCallbacks, Item, m_ItemsPerBlock), 0 }; + + m_ItemBlocks.push_back(newBlock); + + // Setup singly-linked list of all free items in this block. + for(uint32_t i = 0; i < m_ItemsPerBlock - 1; ++i) + newBlock.pItems[i].NextFreeIndex = i + 1; + newBlock.pItems[m_ItemsPerBlock - 1].NextFreeIndex = UINT32_MAX; + return m_ItemBlocks.back(); +} + +//////////////////////////////////////////////////////////////////////////////// +// class VmaRawList, VmaList + +#if VMA_USE_STL_LIST + +#define VmaList std::list + +#else // #if VMA_USE_STL_LIST + +template +struct VmaListItem +{ + VmaListItem* pPrev; + VmaListItem* pNext; + T Value; +}; + +// Doubly linked list. +template +class VmaRawList +{ + VMA_CLASS_NO_COPY(VmaRawList) +public: + typedef VmaListItem ItemType; + + VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks); + ~VmaRawList(); + void Clear(); + + size_t GetCount() const { return m_Count; } + bool IsEmpty() const { return m_Count == 0; } + + ItemType* Front() { return m_pFront; } + const ItemType* Front() const { return m_pFront; } + ItemType* Back() { return m_pBack; } + const ItemType* Back() const { return m_pBack; } + + ItemType* PushBack(); + ItemType* PushFront(); + ItemType* PushBack(const T& value); + ItemType* PushFront(const T& value); + void PopBack(); + void PopFront(); + + // Item can be null - it means PushBack. + ItemType* InsertBefore(ItemType* pItem); + // Item can be null - it means PushFront. + ItemType* InsertAfter(ItemType* pItem); + + ItemType* InsertBefore(ItemType* pItem, const T& value); + ItemType* InsertAfter(ItemType* pItem, const T& value); + + void Remove(ItemType* pItem); + +private: + const VkAllocationCallbacks* const m_pAllocationCallbacks; + VmaPoolAllocator m_ItemAllocator; + ItemType* m_pFront; + ItemType* m_pBack; + size_t m_Count; +}; + +template +VmaRawList::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) : + m_pAllocationCallbacks(pAllocationCallbacks), + m_ItemAllocator(pAllocationCallbacks, 128), + m_pFront(VMA_NULL), + m_pBack(VMA_NULL), + m_Count(0) +{ +} + +template +VmaRawList::~VmaRawList() +{ + // Intentionally not calling Clear, because that would be unnecessary + // computations to return all items to m_ItemAllocator as free. +} + +template +void VmaRawList::Clear() +{ + if(IsEmpty() == false) + { + ItemType* pItem = m_pBack; + while(pItem != VMA_NULL) + { + ItemType* const pPrevItem = pItem->pPrev; + m_ItemAllocator.Free(pItem); + pItem = pPrevItem; + } + m_pFront = VMA_NULL; + m_pBack = VMA_NULL; + m_Count = 0; + } +} + +template +VmaListItem* VmaRawList::PushBack() +{ + ItemType* const pNewItem = m_ItemAllocator.Alloc(); + pNewItem->pNext = VMA_NULL; + if(IsEmpty()) + { + pNewItem->pPrev = VMA_NULL; + m_pFront = pNewItem; + m_pBack = pNewItem; + m_Count = 1; + } + else + { + pNewItem->pPrev = m_pBack; + m_pBack->pNext = pNewItem; + m_pBack = pNewItem; + ++m_Count; + } + return pNewItem; +} + +template +VmaListItem* VmaRawList::PushFront() +{ + ItemType* const pNewItem = m_ItemAllocator.Alloc(); + pNewItem->pPrev = VMA_NULL; + if(IsEmpty()) + { + pNewItem->pNext = VMA_NULL; + m_pFront = pNewItem; + m_pBack = pNewItem; + m_Count = 1; + } + else + { + pNewItem->pNext = m_pFront; + m_pFront->pPrev = pNewItem; + m_pFront = pNewItem; + ++m_Count; + } + return pNewItem; +} + +template +VmaListItem* VmaRawList::PushBack(const T& value) +{ + ItemType* const pNewItem = PushBack(); + pNewItem->Value = value; + return pNewItem; +} + +template +VmaListItem* VmaRawList::PushFront(const T& value) +{ + ItemType* const pNewItem = PushFront(); + pNewItem->Value = value; + return pNewItem; +} + +template +void VmaRawList::PopBack() +{ + VMA_HEAVY_ASSERT(m_Count > 0); + ItemType* const pBackItem = m_pBack; + ItemType* const pPrevItem = pBackItem->pPrev; + if(pPrevItem != VMA_NULL) + { + pPrevItem->pNext = VMA_NULL; + } + m_pBack = pPrevItem; + m_ItemAllocator.Free(pBackItem); + --m_Count; +} + +template +void VmaRawList::PopFront() +{ + VMA_HEAVY_ASSERT(m_Count > 0); + ItemType* const pFrontItem = m_pFront; + ItemType* const pNextItem = pFrontItem->pNext; + if(pNextItem != VMA_NULL) + { + pNextItem->pPrev = VMA_NULL; + } + m_pFront = pNextItem; + m_ItemAllocator.Free(pFrontItem); + --m_Count; +} + +template +void VmaRawList::Remove(ItemType* pItem) +{ + VMA_HEAVY_ASSERT(pItem != VMA_NULL); + VMA_HEAVY_ASSERT(m_Count > 0); + + if(pItem->pPrev != VMA_NULL) + { + pItem->pPrev->pNext = pItem->pNext; + } + else + { + VMA_HEAVY_ASSERT(m_pFront == pItem); + m_pFront = pItem->pNext; + } + + if(pItem->pNext != VMA_NULL) + { + pItem->pNext->pPrev = pItem->pPrev; + } + else + { + VMA_HEAVY_ASSERT(m_pBack == pItem); + m_pBack = pItem->pPrev; + } + + m_ItemAllocator.Free(pItem); + --m_Count; +} + +template +VmaListItem* VmaRawList::InsertBefore(ItemType* pItem) +{ + if(pItem != VMA_NULL) + { + ItemType* const prevItem = pItem->pPrev; + ItemType* const newItem = m_ItemAllocator.Alloc(); + newItem->pPrev = prevItem; + newItem->pNext = pItem; + pItem->pPrev = newItem; + if(prevItem != VMA_NULL) + { + prevItem->pNext = newItem; + } + else + { + VMA_HEAVY_ASSERT(m_pFront == pItem); + m_pFront = newItem; + } + ++m_Count; + return newItem; + } + else + return PushBack(); +} + +template +VmaListItem* VmaRawList::InsertAfter(ItemType* pItem) +{ + if(pItem != VMA_NULL) + { + ItemType* const nextItem = pItem->pNext; + ItemType* const newItem = m_ItemAllocator.Alloc(); + newItem->pNext = nextItem; + newItem->pPrev = pItem; + pItem->pNext = newItem; + if(nextItem != VMA_NULL) + { + nextItem->pPrev = newItem; + } + else + { + VMA_HEAVY_ASSERT(m_pBack == pItem); + m_pBack = newItem; + } + ++m_Count; + return newItem; + } + else + return PushFront(); +} + +template +VmaListItem* VmaRawList::InsertBefore(ItemType* pItem, const T& value) +{ + ItemType* const newItem = InsertBefore(pItem); + newItem->Value = value; + return newItem; +} + +template +VmaListItem* VmaRawList::InsertAfter(ItemType* pItem, const T& value) +{ + ItemType* const newItem = InsertAfter(pItem); + newItem->Value = value; + return newItem; +} + +template +class VmaList +{ + VMA_CLASS_NO_COPY(VmaList) +public: + class iterator + { + public: + iterator() : + m_pList(VMA_NULL), + m_pItem(VMA_NULL) + { + } + + T& operator*() const + { + VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); + return m_pItem->Value; + } + T* operator->() const + { + VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); + return &m_pItem->Value; + } + + iterator& operator++() + { + VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); + m_pItem = m_pItem->pNext; + return *this; + } + iterator& operator--() + { + if(m_pItem != VMA_NULL) + { + m_pItem = m_pItem->pPrev; + } + else + { + VMA_HEAVY_ASSERT(!m_pList->IsEmpty()); + m_pItem = m_pList->Back(); + } + return *this; + } + + iterator operator++(int) + { + iterator result = *this; + ++*this; + return result; + } + iterator operator--(int) + { + iterator result = *this; + --*this; + return result; + } + + bool operator==(const iterator& rhs) const + { + VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); + return m_pItem == rhs.m_pItem; + } + bool operator!=(const iterator& rhs) const + { + VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); + return m_pItem != rhs.m_pItem; + } + + private: + VmaRawList* m_pList; + VmaListItem* m_pItem; + + iterator(VmaRawList* pList, VmaListItem* pItem) : + m_pList(pList), + m_pItem(pItem) + { + } + + friend class VmaList; + }; + + class const_iterator + { + public: + const_iterator() : + m_pList(VMA_NULL), + m_pItem(VMA_NULL) + { + } + + const_iterator(const iterator& src) : + m_pList(src.m_pList), + m_pItem(src.m_pItem) + { + } + + const T& operator*() const + { + VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); + return m_pItem->Value; + } + const T* operator->() const + { + VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); + return &m_pItem->Value; + } + + const_iterator& operator++() + { + VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); + m_pItem = m_pItem->pNext; + return *this; + } + const_iterator& operator--() + { + if(m_pItem != VMA_NULL) + { + m_pItem = m_pItem->pPrev; + } + else + { + VMA_HEAVY_ASSERT(!m_pList->IsEmpty()); + m_pItem = m_pList->Back(); + } + return *this; + } + + const_iterator operator++(int) + { + const_iterator result = *this; + ++*this; + return result; + } + const_iterator operator--(int) + { + const_iterator result = *this; + --*this; + return result; + } + + bool operator==(const const_iterator& rhs) const + { + VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); + return m_pItem == rhs.m_pItem; + } + bool operator!=(const const_iterator& rhs) const + { + VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); + return m_pItem != rhs.m_pItem; + } + + private: + const_iterator(const VmaRawList* pList, const VmaListItem* pItem) : + m_pList(pList), + m_pItem(pItem) + { + } + + const VmaRawList* m_pList; + const VmaListItem* m_pItem; + + friend class VmaList; + }; + + VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) { } + + bool empty() const { return m_RawList.IsEmpty(); } + size_t size() const { return m_RawList.GetCount(); } + + iterator begin() { return iterator(&m_RawList, m_RawList.Front()); } + iterator end() { return iterator(&m_RawList, VMA_NULL); } + + const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); } + const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); } + + void clear() { m_RawList.Clear(); } + void push_back(const T& value) { m_RawList.PushBack(value); } + void erase(iterator it) { m_RawList.Remove(it.m_pItem); } + iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); } + +private: + VmaRawList m_RawList; +}; + +#endif // #if VMA_USE_STL_LIST + +//////////////////////////////////////////////////////////////////////////////// +// class VmaMap + +// Unused in this version. +#if 0 + +#if VMA_USE_STL_UNORDERED_MAP + +#define VmaPair std::pair + +#define VMA_MAP_TYPE(KeyT, ValueT) \ + std::unordered_map< KeyT, ValueT, std::hash, std::equal_to, VmaStlAllocator< std::pair > > + +#else // #if VMA_USE_STL_UNORDERED_MAP + +template +struct VmaPair +{ + T1 first; + T2 second; + + VmaPair() : first(), second() { } + VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) { } +}; + +/* Class compatible with subset of interface of std::unordered_map. +KeyT, ValueT must be POD because they will be stored in VmaVector. +*/ +template +class VmaMap +{ +public: + typedef VmaPair PairType; + typedef PairType* iterator; + + VmaMap(const VmaStlAllocator& allocator) : m_Vector(allocator) { } + + iterator begin() { return m_Vector.begin(); } + iterator end() { return m_Vector.end(); } + + void insert(const PairType& pair); + iterator find(const KeyT& key); + void erase(iterator it); + +private: + VmaVector< PairType, VmaStlAllocator > m_Vector; +}; + +#define VMA_MAP_TYPE(KeyT, ValueT) VmaMap + +template +struct VmaPairFirstLess +{ + bool operator()(const VmaPair& lhs, const VmaPair& rhs) const + { + return lhs.first < rhs.first; + } + bool operator()(const VmaPair& lhs, const FirstT& rhsFirst) const + { + return lhs.first < rhsFirst; + } +}; + +template +void VmaMap::insert(const PairType& pair) +{ + const size_t indexToInsert = VmaBinaryFindFirstNotLess( + m_Vector.data(), + m_Vector.data() + m_Vector.size(), + pair, + VmaPairFirstLess()) - m_Vector.data(); + VmaVectorInsert(m_Vector, indexToInsert, pair); +} + +template +VmaPair* VmaMap::find(const KeyT& key) +{ + PairType* it = VmaBinaryFindFirstNotLess( + m_Vector.data(), + m_Vector.data() + m_Vector.size(), + key, + VmaPairFirstLess()); + if((it != m_Vector.end()) && (it->first == key)) + { + return it; + } + else + { + return m_Vector.end(); + } +} + +template +void VmaMap::erase(iterator it) +{ + VmaVectorRemove(m_Vector, it - m_Vector.begin()); +} + +#endif // #if VMA_USE_STL_UNORDERED_MAP + +#endif // #if 0 + +//////////////////////////////////////////////////////////////////////////////// + +class VmaDeviceMemoryBlock; + +enum VMA_CACHE_OPERATION { VMA_CACHE_FLUSH, VMA_CACHE_INVALIDATE }; + +struct VmaAllocation_T +{ + VMA_CLASS_NO_COPY(VmaAllocation_T) +private: + static const uint8_t MAP_COUNT_FLAG_PERSISTENT_MAP = 0x80; + + enum FLAGS + { + FLAG_USER_DATA_STRING = 0x01, + }; + +public: + enum ALLOCATION_TYPE + { + ALLOCATION_TYPE_NONE, + ALLOCATION_TYPE_BLOCK, + ALLOCATION_TYPE_DEDICATED, + }; + + VmaAllocation_T(uint32_t currentFrameIndex, bool userDataString) : + m_Alignment(1), + m_Size(0), + m_pUserData(VMA_NULL), + m_LastUseFrameIndex(currentFrameIndex), + m_Type((uint8_t)ALLOCATION_TYPE_NONE), + m_SuballocationType((uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN), + m_MapCount(0), + m_Flags(userDataString ? (uint8_t)FLAG_USER_DATA_STRING : 0) + { +#if VMA_STATS_STRING_ENABLED + m_CreationFrameIndex = currentFrameIndex; + m_BufferImageUsage = 0; +#endif + } + + ~VmaAllocation_T() + { + VMA_ASSERT((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) == 0 && "Allocation was not unmapped before destruction."); + + // Check if owned string was freed. + VMA_ASSERT(m_pUserData == VMA_NULL); + } + + void InitBlockAllocation( + VmaPool hPool, + VmaDeviceMemoryBlock* block, + VkDeviceSize offset, + VkDeviceSize alignment, + VkDeviceSize size, + VmaSuballocationType suballocationType, + bool mapped, + bool canBecomeLost) + { + VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE); + VMA_ASSERT(block != VMA_NULL); + m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK; + m_Alignment = alignment; + m_Size = size; + m_MapCount = mapped ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0; + m_SuballocationType = (uint8_t)suballocationType; + m_BlockAllocation.m_hPool = hPool; + m_BlockAllocation.m_Block = block; + m_BlockAllocation.m_Offset = offset; + m_BlockAllocation.m_CanBecomeLost = canBecomeLost; + } + + void InitLost() + { + VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE); + VMA_ASSERT(m_LastUseFrameIndex.load() == VMA_FRAME_INDEX_LOST); + m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK; + m_BlockAllocation.m_hPool = VK_NULL_HANDLE; + m_BlockAllocation.m_Block = VMA_NULL; + m_BlockAllocation.m_Offset = 0; + m_BlockAllocation.m_CanBecomeLost = true; + } + + void ChangeBlockAllocation( + VmaAllocator hAllocator, + VmaDeviceMemoryBlock* block, + VkDeviceSize offset); + + void ChangeSize(VkDeviceSize newSize); + void ChangeOffset(VkDeviceSize newOffset); + + // pMappedData not null means allocation is created with MAPPED flag. + void InitDedicatedAllocation( + uint32_t memoryTypeIndex, + VkDeviceMemory hMemory, + VmaSuballocationType suballocationType, + void* pMappedData, + VkDeviceSize size) + { + VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE); + VMA_ASSERT(hMemory != VK_NULL_HANDLE); + m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED; + m_Alignment = 0; + m_Size = size; + m_SuballocationType = (uint8_t)suballocationType; + m_MapCount = (pMappedData != VMA_NULL) ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0; + m_DedicatedAllocation.m_MemoryTypeIndex = memoryTypeIndex; + m_DedicatedAllocation.m_hMemory = hMemory; + m_DedicatedAllocation.m_pMappedData = pMappedData; + } + + ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; } + VkDeviceSize GetAlignment() const { return m_Alignment; } + VkDeviceSize GetSize() const { return m_Size; } + bool IsUserDataString() const { return (m_Flags & FLAG_USER_DATA_STRING) != 0; } + void* GetUserData() const { return m_pUserData; } + void SetUserData(VmaAllocator hAllocator, void* pUserData); + VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; } + + VmaDeviceMemoryBlock* GetBlock() const + { + VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK); + return m_BlockAllocation.m_Block; + } + VkDeviceSize GetOffset() const; + VkDeviceMemory GetMemory() const; + uint32_t GetMemoryTypeIndex() const; + bool IsPersistentMap() const { return (m_MapCount & MAP_COUNT_FLAG_PERSISTENT_MAP) != 0; } + void* GetMappedData() const; + bool CanBecomeLost() const; + VmaPool GetPool() const; + + uint32_t GetLastUseFrameIndex() const + { + return m_LastUseFrameIndex.load(); + } + bool CompareExchangeLastUseFrameIndex(uint32_t& expected, uint32_t desired) + { + return m_LastUseFrameIndex.compare_exchange_weak(expected, desired); + } + /* + - If hAllocation.LastUseFrameIndex + frameInUseCount < allocator.CurrentFrameIndex, + makes it lost by setting LastUseFrameIndex = VMA_FRAME_INDEX_LOST and returns true. + - Else, returns false. + + If hAllocation is already lost, assert - you should not call it then. + If hAllocation was not created with CAN_BECOME_LOST_BIT, assert. + */ + bool MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount); + + void DedicatedAllocCalcStatsInfo(VmaStatInfo& outInfo) + { + VMA_ASSERT(m_Type == ALLOCATION_TYPE_DEDICATED); + outInfo.blockCount = 1; + outInfo.allocationCount = 1; + outInfo.unusedRangeCount = 0; + outInfo.usedBytes = m_Size; + outInfo.unusedBytes = 0; + outInfo.allocationSizeMin = outInfo.allocationSizeMax = m_Size; + outInfo.unusedRangeSizeMin = UINT64_MAX; + outInfo.unusedRangeSizeMax = 0; + } + + void BlockAllocMap(); + void BlockAllocUnmap(); + VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData); + void DedicatedAllocUnmap(VmaAllocator hAllocator); + +#if VMA_STATS_STRING_ENABLED + uint32_t GetCreationFrameIndex() const { return m_CreationFrameIndex; } + uint32_t GetBufferImageUsage() const { return m_BufferImageUsage; } + + void InitBufferImageUsage(uint32_t bufferImageUsage) + { + VMA_ASSERT(m_BufferImageUsage == 0); + m_BufferImageUsage = bufferImageUsage; + } + + void PrintParameters(class VmaJsonWriter& json) const; +#endif + +private: + VkDeviceSize m_Alignment; + VkDeviceSize m_Size; + void* m_pUserData; + VMA_ATOMIC_UINT32 m_LastUseFrameIndex; + uint8_t m_Type; // ALLOCATION_TYPE + uint8_t m_SuballocationType; // VmaSuballocationType + // Bit 0x80 is set when allocation was created with VMA_ALLOCATION_CREATE_MAPPED_BIT. + // Bits with mask 0x7F are reference counter for vmaMapMemory()/vmaUnmapMemory(). + uint8_t m_MapCount; + uint8_t m_Flags; // enum FLAGS + + // Allocation out of VmaDeviceMemoryBlock. + struct BlockAllocation + { + VmaPool m_hPool; // Null if belongs to general memory. + VmaDeviceMemoryBlock* m_Block; + VkDeviceSize m_Offset; + bool m_CanBecomeLost; + }; + + // Allocation for an object that has its own private VkDeviceMemory. + struct DedicatedAllocation + { + uint32_t m_MemoryTypeIndex; + VkDeviceMemory m_hMemory; + void* m_pMappedData; // Not null means memory is mapped. + }; + + union + { + // Allocation out of VmaDeviceMemoryBlock. + BlockAllocation m_BlockAllocation; + // Allocation for an object that has its own private VkDeviceMemory. + DedicatedAllocation m_DedicatedAllocation; + }; + +#if VMA_STATS_STRING_ENABLED + uint32_t m_CreationFrameIndex; + uint32_t m_BufferImageUsage; // 0 if unknown. +#endif + + void FreeUserDataString(VmaAllocator hAllocator); +}; + +/* +Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as +allocated memory block or free. +*/ +struct VmaSuballocation +{ + VkDeviceSize offset; + VkDeviceSize size; + VmaAllocation hAllocation; + VmaSuballocationType type; +}; + +// Comparator for offsets. +struct VmaSuballocationOffsetLess +{ + bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const + { + return lhs.offset < rhs.offset; + } +}; +struct VmaSuballocationOffsetGreater +{ + bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const + { + return lhs.offset > rhs.offset; + } +}; + +typedef VmaList< VmaSuballocation, VmaStlAllocator > VmaSuballocationList; + +// Cost of one additional allocation lost, as equivalent in bytes. +static const VkDeviceSize VMA_LOST_ALLOCATION_COST = 1048576; + +/* +Parameters of planned allocation inside a VmaDeviceMemoryBlock. + +If canMakeOtherLost was false: +- item points to a FREE suballocation. +- itemsToMakeLostCount is 0. + +If canMakeOtherLost was true: +- item points to first of sequence of suballocations, which are either FREE, + or point to VmaAllocations that can become lost. +- itemsToMakeLostCount is the number of VmaAllocations that need to be made lost for + the requested allocation to succeed. +*/ +struct VmaAllocationRequest +{ + VkDeviceSize offset; + VkDeviceSize sumFreeSize; // Sum size of free items that overlap with proposed allocation. + VkDeviceSize sumItemSize; // Sum size of items to make lost that overlap with proposed allocation. + VmaSuballocationList::iterator item; + size_t itemsToMakeLostCount; + void* customData; + + VkDeviceSize CalcCost() const + { + return sumItemSize + itemsToMakeLostCount * VMA_LOST_ALLOCATION_COST; + } +}; + +/* +Data structure used for bookkeeping of allocations and unused ranges of memory +in a single VkDeviceMemory block. +*/ +class VmaBlockMetadata +{ +public: + VmaBlockMetadata(VmaAllocator hAllocator); + virtual ~VmaBlockMetadata() { } + virtual void Init(VkDeviceSize size) { m_Size = size; } + + // Validates all data structures inside this object. If not valid, returns false. + virtual bool Validate() const = 0; + VkDeviceSize GetSize() const { return m_Size; } + virtual size_t GetAllocationCount() const = 0; + virtual VkDeviceSize GetSumFreeSize() const = 0; + virtual VkDeviceSize GetUnusedRangeSizeMax() const = 0; + // Returns true if this block is empty - contains only single free suballocation. + virtual bool IsEmpty() const = 0; + + virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const = 0; + // Shouldn't modify blockCount. + virtual void AddPoolStats(VmaPoolStats& inoutStats) const = 0; + +#if VMA_STATS_STRING_ENABLED + virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0; +#endif + + // Tries to find a place for suballocation with given parameters inside this block. + // If succeeded, fills pAllocationRequest and returns true. + // If failed, returns false. + virtual bool CreateAllocationRequest( + uint32_t currentFrameIndex, + uint32_t frameInUseCount, + VkDeviceSize bufferImageGranularity, + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + bool canMakeOtherLost, + // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags. + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) = 0; + + virtual bool MakeRequestedAllocationsLost( + uint32_t currentFrameIndex, + uint32_t frameInUseCount, + VmaAllocationRequest* pAllocationRequest) = 0; + + virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) = 0; + + virtual VkResult CheckCorruption(const void* pBlockData) = 0; + + // Makes actual allocation based on request. Request must already be checked and valid. + virtual void Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + VkDeviceSize allocSize, + bool upperAddress, + VmaAllocation hAllocation) = 0; + + // Frees suballocation assigned to given memory region. + virtual void Free(const VmaAllocation allocation) = 0; + virtual void FreeAtOffset(VkDeviceSize offset) = 0; + + // Tries to resize (grow or shrink) space for given allocation, in place. + virtual bool ResizeAllocation(const VmaAllocation /*alloc*/, VkDeviceSize /*newSize*/) { return false; } + +protected: + const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; } + +#if VMA_STATS_STRING_ENABLED + void PrintDetailedMap_Begin(class VmaJsonWriter& json, + VkDeviceSize unusedBytes, + size_t allocationCount, + size_t unusedRangeCount) const; + void PrintDetailedMap_Allocation(class VmaJsonWriter& json, + VkDeviceSize offset, + VmaAllocation hAllocation) const; + void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json, + VkDeviceSize offset, + VkDeviceSize size) const; + void PrintDetailedMap_End(class VmaJsonWriter& json) const; +#endif + +private: + VkDeviceSize m_Size; + const VkAllocationCallbacks* m_pAllocationCallbacks; +}; + +#define VMA_VALIDATE(cond) do { if(!(cond)) { \ + VMA_ASSERT(0 && "Validation failed: " #cond); \ + return false; \ + } } while(false) + +class VmaBlockMetadata_Generic : public VmaBlockMetadata +{ + VMA_CLASS_NO_COPY(VmaBlockMetadata_Generic) +public: + VmaBlockMetadata_Generic(VmaAllocator hAllocator); + virtual ~VmaBlockMetadata_Generic(); + virtual void Init(VkDeviceSize size); + + virtual bool Validate() const; + virtual size_t GetAllocationCount() const { return m_Suballocations.size() - m_FreeCount; } + virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; } + virtual VkDeviceSize GetUnusedRangeSizeMax() const; + virtual bool IsEmpty() const; + + virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const; + virtual void AddPoolStats(VmaPoolStats& inoutStats) const; + +#if VMA_STATS_STRING_ENABLED + virtual void PrintDetailedMap(class VmaJsonWriter& json) const; +#endif + + virtual bool CreateAllocationRequest( + uint32_t currentFrameIndex, + uint32_t frameInUseCount, + VkDeviceSize bufferImageGranularity, + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + bool canMakeOtherLost, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest); + + virtual bool MakeRequestedAllocationsLost( + uint32_t currentFrameIndex, + uint32_t frameInUseCount, + VmaAllocationRequest* pAllocationRequest); + + virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount); + + virtual VkResult CheckCorruption(const void* pBlockData); + + virtual void Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + VkDeviceSize allocSize, + bool upperAddress, + VmaAllocation hAllocation); + + virtual void Free(const VmaAllocation allocation); + virtual void FreeAtOffset(VkDeviceSize offset); + + virtual bool ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize); + + //////////////////////////////////////////////////////////////////////////////// + // For defragmentation + + bool IsBufferImageGranularityConflictPossible( + VkDeviceSize bufferImageGranularity, + VmaSuballocationType& inOutPrevSuballocType) const; + +private: + friend class VmaDefragmentationAlgorithm_Generic; + friend class VmaDefragmentationAlgorithm_Fast; + + uint32_t m_FreeCount; + VkDeviceSize m_SumFreeSize; + VmaSuballocationList m_Suballocations; + // Suballocations that are free and have size greater than certain threshold. + // Sorted by size, ascending. + VmaVector< VmaSuballocationList::iterator, VmaStlAllocator< VmaSuballocationList::iterator > > m_FreeSuballocationsBySize; + + bool ValidateFreeSuballocationList() const; + + // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem. + // If yes, fills pOffset and returns true. If no, returns false. + bool CheckAllocation( + uint32_t currentFrameIndex, + uint32_t frameInUseCount, + VkDeviceSize bufferImageGranularity, + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + VmaSuballocationType allocType, + VmaSuballocationList::const_iterator suballocItem, + bool canMakeOtherLost, + VkDeviceSize* pOffset, + size_t* itemsToMakeLostCount, + VkDeviceSize* pSumFreeSize, + VkDeviceSize* pSumItemSize) const; + // Given free suballocation, it merges it with following one, which must also be free. + void MergeFreeWithNext(VmaSuballocationList::iterator item); + // Releases given suballocation, making it free. + // Merges it with adjacent free suballocations if applicable. + // Returns iterator to new free suballocation at this place. + VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem); + // Given free suballocation, it inserts it into sorted list of + // m_FreeSuballocationsBySize if it's suitable. + void RegisterFreeSuballocation(VmaSuballocationList::iterator item); + // Given free suballocation, it removes it from sorted list of + // m_FreeSuballocationsBySize if it's suitable. + void UnregisterFreeSuballocation(VmaSuballocationList::iterator item); +}; + +/* +Allocations and their references in internal data structure look like this: + +if(m_2ndVectorMode == SECOND_VECTOR_EMPTY): + + 0 +-------+ + | | + | | + | | + +-------+ + | Alloc | 1st[m_1stNullItemsBeginCount] + +-------+ + | Alloc | 1st[m_1stNullItemsBeginCount + 1] + +-------+ + | ... | + +-------+ + | Alloc | 1st[1st.size() - 1] + +-------+ + | | + | | + | | +GetSize() +-------+ + +if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER): + + 0 +-------+ + | Alloc | 2nd[0] + +-------+ + | Alloc | 2nd[1] + +-------+ + | ... | + +-------+ + | Alloc | 2nd[2nd.size() - 1] + +-------+ + | | + | | + | | + +-------+ + | Alloc | 1st[m_1stNullItemsBeginCount] + +-------+ + | Alloc | 1st[m_1stNullItemsBeginCount + 1] + +-------+ + | ... | + +-------+ + | Alloc | 1st[1st.size() - 1] + +-------+ + | | +GetSize() +-------+ + +if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK): + + 0 +-------+ + | | + | | + | | + +-------+ + | Alloc | 1st[m_1stNullItemsBeginCount] + +-------+ + | Alloc | 1st[m_1stNullItemsBeginCount + 1] + +-------+ + | ... | + +-------+ + | Alloc | 1st[1st.size() - 1] + +-------+ + | | + | | + | | + +-------+ + | Alloc | 2nd[2nd.size() - 1] + +-------+ + | ... | + +-------+ + | Alloc | 2nd[1] + +-------+ + | Alloc | 2nd[0] +GetSize() +-------+ + +*/ +class VmaBlockMetadata_Linear : public VmaBlockMetadata +{ + VMA_CLASS_NO_COPY(VmaBlockMetadata_Linear) +public: + VmaBlockMetadata_Linear(VmaAllocator hAllocator); + virtual ~VmaBlockMetadata_Linear(); + virtual void Init(VkDeviceSize size); + + virtual bool Validate() const; + virtual size_t GetAllocationCount() const; + virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; } + virtual VkDeviceSize GetUnusedRangeSizeMax() const; + virtual bool IsEmpty() const { return GetAllocationCount() == 0; } + + virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const; + virtual void AddPoolStats(VmaPoolStats& inoutStats) const; + +#if VMA_STATS_STRING_ENABLED + virtual void PrintDetailedMap(class VmaJsonWriter& json) const; +#endif + + virtual bool CreateAllocationRequest( + uint32_t currentFrameIndex, + uint32_t frameInUseCount, + VkDeviceSize bufferImageGranularity, + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + bool canMakeOtherLost, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest); + + virtual bool MakeRequestedAllocationsLost( + uint32_t currentFrameIndex, + uint32_t frameInUseCount, + VmaAllocationRequest* pAllocationRequest); + + virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount); + + virtual VkResult CheckCorruption(const void* pBlockData); + + virtual void Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + VkDeviceSize allocSize, + bool upperAddress, + VmaAllocation hAllocation); + + virtual void Free(const VmaAllocation allocation); + virtual void FreeAtOffset(VkDeviceSize offset); + +private: + /* + There are two suballocation vectors, used in ping-pong way. + The one with index m_1stVectorIndex is called 1st. + The one with index (m_1stVectorIndex ^ 1) is called 2nd. + 2nd can be non-empty only when 1st is not empty. + When 2nd is not empty, m_2ndVectorMode indicates its mode of operation. + */ + typedef VmaVector< VmaSuballocation, VmaStlAllocator > SuballocationVectorType; + + enum SECOND_VECTOR_MODE + { + SECOND_VECTOR_EMPTY, + /* + Suballocations in 2nd vector are created later than the ones in 1st, but they + all have smaller offset. + */ + SECOND_VECTOR_RING_BUFFER, + /* + Suballocations in 2nd vector are upper side of double stack. + They all have offsets higher than those in 1st vector. + Top of this stack means smaller offsets, but higher indices in this vector. + */ + SECOND_VECTOR_DOUBLE_STACK, + }; + + VkDeviceSize m_SumFreeSize; + SuballocationVectorType m_Suballocations0, m_Suballocations1; + uint32_t m_1stVectorIndex; + SECOND_VECTOR_MODE m_2ndVectorMode; + + SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; } + SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; } + const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; } + const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; } + + // Number of items in 1st vector with hAllocation = null at the beginning. + size_t m_1stNullItemsBeginCount; + // Number of other items in 1st vector with hAllocation = null somewhere in the middle. + size_t m_1stNullItemsMiddleCount; + // Number of items in 2nd vector with hAllocation = null. + size_t m_2ndNullItemsCount; + + bool ShouldCompact1st() const; + void CleanupAfterFree(); +}; + +/* +- GetSize() is the original size of allocated memory block. +- m_UsableSize is this size aligned down to a power of two. + All allocations and calculations happen relative to m_UsableSize. +- GetUnusableSize() is the difference between them. + It is repoted as separate, unused range, not available for allocations. + +Node at level 0 has size = m_UsableSize. +Each next level contains nodes with size 2 times smaller than current level. +m_LevelCount is the maximum number of levels to use in the current object. +*/ +class VmaBlockMetadata_Buddy : public VmaBlockMetadata +{ + VMA_CLASS_NO_COPY(VmaBlockMetadata_Buddy) +public: + VmaBlockMetadata_Buddy(VmaAllocator hAllocator); + virtual ~VmaBlockMetadata_Buddy(); + virtual void Init(VkDeviceSize size); + + virtual bool Validate() const; + virtual size_t GetAllocationCount() const { return m_AllocationCount; } + virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize + GetUnusableSize(); } + virtual VkDeviceSize GetUnusedRangeSizeMax() const; + virtual bool IsEmpty() const { return m_Root->type == Node::TYPE_FREE; } + + virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const; + virtual void AddPoolStats(VmaPoolStats& inoutStats) const; + +#if VMA_STATS_STRING_ENABLED + virtual void PrintDetailedMap(class VmaJsonWriter& json) const; +#endif + + virtual bool CreateAllocationRequest( + uint32_t currentFrameIndex, + uint32_t frameInUseCount, + VkDeviceSize bufferImageGranularity, + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + bool canMakeOtherLost, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest); + + virtual bool MakeRequestedAllocationsLost( + uint32_t currentFrameIndex, + uint32_t frameInUseCount, + VmaAllocationRequest* pAllocationRequest); + + virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount); + + virtual VkResult CheckCorruption(const void* /*pBlockData*/) { return VK_ERROR_FEATURE_NOT_PRESENT; } + + virtual void Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + VkDeviceSize allocSize, + bool upperAddress, + VmaAllocation hAllocation); + + virtual void Free(const VmaAllocation allocation) { FreeAtOffset(allocation, allocation->GetOffset()); } + virtual void FreeAtOffset(VkDeviceSize offset) { FreeAtOffset(VMA_NULL, offset); } + +private: + static const VkDeviceSize MIN_NODE_SIZE = 32; + static const size_t MAX_LEVELS = 30; + + struct ValidationContext + { + size_t calculatedAllocationCount; + size_t calculatedFreeCount; + VkDeviceSize calculatedSumFreeSize; + + ValidationContext() : + calculatedAllocationCount(0), + calculatedFreeCount(0), + calculatedSumFreeSize(0) { } + }; + + struct Node + { + VkDeviceSize offset; + enum TYPE + { + TYPE_FREE, + TYPE_ALLOCATION, + TYPE_SPLIT, + TYPE_COUNT + } type; + Node* parent; + Node* buddy; + + union + { + struct + { + Node* prev; + Node* next; + } free; + struct + { + VmaAllocation alloc; + } allocation; + struct + { + Node* leftChild; + } split; + }; + }; + + // Size of the memory block aligned down to a power of two. + VkDeviceSize m_UsableSize; + uint32_t m_LevelCount; + + Node* m_Root; + struct { + Node* front; + Node* back; + } m_FreeList[MAX_LEVELS]; + // Number of nodes in the tree with type == TYPE_ALLOCATION. + size_t m_AllocationCount; + // Number of nodes in the tree with type == TYPE_FREE. + size_t m_FreeCount; + // This includes space wasted due to internal fragmentation. Doesn't include unusable size. + VkDeviceSize m_SumFreeSize; + + VkDeviceSize GetUnusableSize() const { return GetSize() - m_UsableSize; } + void DeleteNode(Node* node); + bool ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const; + uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const; + inline VkDeviceSize LevelToNodeSize(uint32_t level) const { return m_UsableSize >> level; } + // Alloc passed just for validation. Can be null. + void FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset); + void CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const; + // Adds node to the front of FreeList at given level. + // node->type must be FREE. + // node->free.prev, next can be undefined. + void AddToFreeListFront(uint32_t level, Node* node); + // Removes node from FreeList at given level. + // node->type must be FREE. + // node->free.prev, next stay untouched. + void RemoveFromFreeList(uint32_t level, Node* node); + +#if VMA_STATS_STRING_ENABLED + void PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const; +#endif +}; + +/* +Represents a single block of device memory (`VkDeviceMemory`) with all the +data about its regions (aka suballocations, #VmaAllocation), assigned and free. + +Thread-safety: This class must be externally synchronized. +*/ +class VmaDeviceMemoryBlock +{ + VMA_CLASS_NO_COPY(VmaDeviceMemoryBlock) +public: + VmaBlockMetadata* m_pMetadata; + + VmaDeviceMemoryBlock(VmaAllocator hAllocator); + + ~VmaDeviceMemoryBlock() + { + VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped."); + VMA_ASSERT(m_hMemory == VK_NULL_HANDLE); + } + + // Always call after construction. + void Init( + VmaAllocator hAllocator, + uint32_t newMemoryTypeIndex, + VkDeviceMemory newMemory, + VkDeviceSize newSize, + uint32_t id, + uint32_t algorithm); + // Always call before destruction. + void Destroy(VmaAllocator allocator); + + VkDeviceMemory GetDeviceMemory() const { return m_hMemory; } + uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; } + uint32_t GetId() const { return m_Id; } + void* GetMappedData() const { return m_pMappedData; } + + // Validates all data structures inside this object. If not valid, returns false. + bool Validate() const; + + VkResult CheckCorruption(VmaAllocator hAllocator); + + // ppData can be null. + VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData); + void Unmap(VmaAllocator hAllocator, uint32_t count); + + VkResult WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize); + VkResult ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize); + + VkResult BindBufferMemory( + const VmaAllocator hAllocator, + const VmaAllocation hAllocation, + VkBuffer hBuffer); + VkResult BindImageMemory( + const VmaAllocator hAllocator, + const VmaAllocation hAllocation, + VkImage hImage); + +private: + uint32_t m_MemoryTypeIndex; + uint32_t m_Id; + VkDeviceMemory m_hMemory; + + /* + Protects access to m_hMemory so it's not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory. + Also protects m_MapCount, m_pMappedData. + Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex. + */ + VMA_MUTEX m_Mutex; + uint32_t m_MapCount; + void* m_pMappedData; +}; + +struct VmaPointerLess +{ + bool operator()(const void* lhs, const void* rhs) const + { + return lhs < rhs; + } +}; + +struct VmaDefragmentationMove +{ + size_t srcBlockIndex; + size_t dstBlockIndex; + VkDeviceSize srcOffset; + VkDeviceSize dstOffset; + VkDeviceSize size; +}; + +class VmaDefragmentationAlgorithm; + +/* +Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific +Vulkan memory type. + +Synchronized internally with a mutex. +*/ +struct VmaBlockVector +{ + VMA_CLASS_NO_COPY(VmaBlockVector) +public: + VmaBlockVector( + VmaAllocator hAllocator, + uint32_t memoryTypeIndex, + VkDeviceSize preferredBlockSize, + size_t minBlockCount, + size_t maxBlockCount, + VkDeviceSize bufferImageGranularity, + uint32_t frameInUseCount, + bool isCustomPool, + bool explicitBlockSize, + uint32_t algorithm); + ~VmaBlockVector(); + + VkResult CreateMinBlocks(); + + uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; } + VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; } + VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; } + uint32_t GetFrameInUseCount() const { return m_FrameInUseCount; } + uint32_t GetAlgorithm() const { return m_Algorithm; } + + void GetPoolStats(VmaPoolStats* pStats); + + bool IsEmpty() const { return m_Blocks.empty(); } + bool IsCorruptionDetectionEnabled() const; + + VkResult Allocate( + VmaPool hCurrentPool, + uint32_t currentFrameIndex, + VkDeviceSize size, + VkDeviceSize alignment, + const VmaAllocationCreateInfo& createInfo, + VmaSuballocationType suballocType, + size_t allocationCount, + VmaAllocation* pAllocations); + + void Free( + VmaAllocation hAllocation); + + // Adds statistics of this BlockVector to pStats. + void AddStats(VmaStats* pStats); + +#if VMA_STATS_STRING_ENABLED + void PrintDetailedMap(class VmaJsonWriter& json); +#endif + + void MakePoolAllocationsLost( + uint32_t currentFrameIndex, + size_t* pLostAllocationCount); + VkResult CheckCorruption(); + + // Saves results in pCtx->res. + void Defragment( + class VmaBlockVectorDefragmentationContext* pCtx, + VmaDefragmentationStats* pStats, + VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove, + VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove, + VkCommandBuffer commandBuffer); + void DefragmentationEnd( + class VmaBlockVectorDefragmentationContext* pCtx, + VmaDefragmentationStats* pStats); + + //////////////////////////////////////////////////////////////////////////////// + // To be used only while the m_Mutex is locked. Used during defragmentation. + + size_t GetBlockCount() const { return m_Blocks.size(); } + VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; } + size_t CalcAllocationCount() const; + bool IsBufferImageGranularityConflictPossible() const; + +private: + friend class VmaDefragmentationAlgorithm_Generic; + + const VmaAllocator m_hAllocator; + const uint32_t m_MemoryTypeIndex; + const VkDeviceSize m_PreferredBlockSize; + const size_t m_MinBlockCount; + const size_t m_MaxBlockCount; + const VkDeviceSize m_BufferImageGranularity; + const uint32_t m_FrameInUseCount; + const bool m_IsCustomPool; + const bool m_ExplicitBlockSize; + const uint32_t m_Algorithm; + /* There can be at most one allocation that is completely empty - a + hysteresis to avoid pessimistic case of alternating creation and destruction + of a VkDeviceMemory. */ + bool m_HasEmptyBlock; + VMA_RW_MUTEX m_Mutex; + // Incrementally sorted by sumFreeSize, ascending. + VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator > m_Blocks; + uint32_t m_NextBlockId; + + VkDeviceSize CalcMaxBlockSize() const; + + // Finds and removes given block from vector. + void Remove(VmaDeviceMemoryBlock* pBlock); + + // Performs single step in sorting m_Blocks. They may not be fully sorted + // after this call. + void IncrementallySortBlocks(); + + VkResult AllocatePage( + VmaPool hCurrentPool, + uint32_t currentFrameIndex, + VkDeviceSize size, + VkDeviceSize alignment, + const VmaAllocationCreateInfo& createInfo, + VmaSuballocationType suballocType, + VmaAllocation* pAllocation); + + // To be used only without CAN_MAKE_OTHER_LOST flag. + VkResult AllocateFromBlock( + VmaDeviceMemoryBlock* pBlock, + VmaPool hCurrentPool, + uint32_t currentFrameIndex, + VkDeviceSize size, + VkDeviceSize alignment, + VmaAllocationCreateFlags allocFlags, + void* pUserData, + VmaSuballocationType suballocType, + uint32_t strategy, + VmaAllocation* pAllocation); + + VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex); + + // Saves result to pCtx->res. + void ApplyDefragmentationMovesCpu( + class VmaBlockVectorDefragmentationContext* pDefragCtx, + const VmaVector< VmaDefragmentationMove, VmaStlAllocator >& moves); + // Saves result to pCtx->res. + void ApplyDefragmentationMovesGpu( + class VmaBlockVectorDefragmentationContext* pDefragCtx, + const VmaVector< VmaDefragmentationMove, VmaStlAllocator >& moves, + VkCommandBuffer commandBuffer); + + /* + Used during defragmentation. pDefragmentationStats is optional. It's in/out + - updated with new data. + */ + void FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats); +}; + +struct VmaPool_T +{ + VMA_CLASS_NO_COPY(VmaPool_T) +public: + VmaBlockVector m_BlockVector; + + VmaPool_T( + VmaAllocator hAllocator, + const VmaPoolCreateInfo& createInfo, + VkDeviceSize preferredBlockSize); + ~VmaPool_T(); + + uint32_t GetId() const { return m_Id; } + void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; } + +#if VMA_STATS_STRING_ENABLED + //void PrintDetailedMap(class VmaStringBuilder& sb); +#endif + +private: + uint32_t m_Id; +}; + +/* +Performs defragmentation: + +- Updates `pBlockVector->m_pMetadata`. +- Updates allocations by calling ChangeBlockAllocation() or ChangeOffset(). +- Does not move actual data, only returns requested moves as `moves`. +*/ +class VmaDefragmentationAlgorithm +{ + VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm) +public: + VmaDefragmentationAlgorithm( + VmaAllocator hAllocator, + VmaBlockVector* pBlockVector, + uint32_t currentFrameIndex) : + m_hAllocator(hAllocator), + m_pBlockVector(pBlockVector), + m_CurrentFrameIndex(currentFrameIndex) + { + } + virtual ~VmaDefragmentationAlgorithm() + { + } + + virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) = 0; + virtual void AddAll() = 0; + + virtual VkResult Defragment( + VmaVector< VmaDefragmentationMove, VmaStlAllocator >& moves, + VkDeviceSize maxBytesToMove, + uint32_t maxAllocationsToMove) = 0; + + virtual VkDeviceSize GetBytesMoved() const = 0; + virtual uint32_t GetAllocationsMoved() const = 0; + +protected: + VmaAllocator const m_hAllocator; + VmaBlockVector* const m_pBlockVector; + const uint32_t m_CurrentFrameIndex; + + struct AllocationInfo + { + VmaAllocation m_hAllocation; + VkBool32* m_pChanged; + + AllocationInfo() : + m_hAllocation(VK_NULL_HANDLE), + m_pChanged(VMA_NULL) + { + } + AllocationInfo(VmaAllocation hAlloc, VkBool32* pChanged) : + m_hAllocation(hAlloc), + m_pChanged(pChanged) + { + } + }; +}; + +class VmaDefragmentationAlgorithm_Generic : public VmaDefragmentationAlgorithm +{ + VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Generic) +public: + VmaDefragmentationAlgorithm_Generic( + VmaAllocator hAllocator, + VmaBlockVector* pBlockVector, + uint32_t currentFrameIndex, + bool overlappingMoveSupported); + virtual ~VmaDefragmentationAlgorithm_Generic(); + + virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged); + virtual void AddAll() { m_AllAllocations = true; } + + virtual VkResult Defragment( + VmaVector< VmaDefragmentationMove, VmaStlAllocator >& moves, + VkDeviceSize maxBytesToMove, + uint32_t maxAllocationsToMove); + + virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; } + virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; } + +private: + uint32_t m_AllocationCount; + bool m_AllAllocations; + + VkDeviceSize m_BytesMoved; + uint32_t m_AllocationsMoved; + + struct AllocationInfoSizeGreater + { + bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const + { + return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize(); + } + }; + + struct AllocationInfoOffsetGreater + { + bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const + { + return lhs.m_hAllocation->GetOffset() > rhs.m_hAllocation->GetOffset(); + } + }; + + struct BlockInfo + { + size_t m_OriginalBlockIndex; + VmaDeviceMemoryBlock* m_pBlock; + bool m_HasNonMovableAllocations; + VmaVector< AllocationInfo, VmaStlAllocator > m_Allocations; + + BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) : + m_OriginalBlockIndex(SIZE_MAX), + m_pBlock(VMA_NULL), + m_HasNonMovableAllocations(true), + m_Allocations(pAllocationCallbacks) + { + } + + void CalcHasNonMovableAllocations() + { + const size_t blockAllocCount = m_pBlock->m_pMetadata->GetAllocationCount(); + const size_t defragmentAllocCount = m_Allocations.size(); + m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount; + } + + void SortAllocationsBySizeDescending() + { + VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater()); + } + + void SortAllocationsByOffsetDescending() + { + VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoOffsetGreater()); + } + }; + + struct BlockPointerLess + { + bool operator()(const BlockInfo* pLhsBlockInfo, const VmaDeviceMemoryBlock* pRhsBlock) const + { + return pLhsBlockInfo->m_pBlock < pRhsBlock; + } + bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const + { + return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock; + } + }; + + // 1. Blocks with some non-movable allocations go first. + // 2. Blocks with smaller sumFreeSize go first. + struct BlockInfoCompareMoveDestination + { + bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const + { + if(pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations) + { + return true; + } + if(!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations) + { + return false; + } + if(pLhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize() < pRhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize()) + { + return true; + } + return false; + } + }; + + typedef VmaVector< BlockInfo*, VmaStlAllocator > BlockInfoVector; + BlockInfoVector m_Blocks; + + VkResult DefragmentRound( + VmaVector< VmaDefragmentationMove, VmaStlAllocator >& moves, + VkDeviceSize maxBytesToMove, + uint32_t maxAllocationsToMove); + + size_t CalcBlocksWithNonMovableCount() const; + + static bool MoveMakesSense( + size_t dstBlockIndex, VkDeviceSize dstOffset, + size_t srcBlockIndex, VkDeviceSize srcOffset); +}; + +class VmaDefragmentationAlgorithm_Fast : public VmaDefragmentationAlgorithm +{ + VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Fast) +public: + VmaDefragmentationAlgorithm_Fast( + VmaAllocator hAllocator, + VmaBlockVector* pBlockVector, + uint32_t currentFrameIndex, + bool overlappingMoveSupported); + virtual ~VmaDefragmentationAlgorithm_Fast(); + + virtual void AddAllocation(VmaAllocation /*hAlloc*/, VkBool32* /*pChanged*/) { ++m_AllocationCount; } + virtual void AddAll() { m_AllAllocations = true; } + + virtual VkResult Defragment( + VmaVector< VmaDefragmentationMove, VmaStlAllocator >& moves, + VkDeviceSize maxBytesToMove, + uint32_t maxAllocationsToMove); + + virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; } + virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; } + +private: + struct BlockInfo + { + size_t origBlockIndex; + }; + + class FreeSpaceDatabase + { + public: + FreeSpaceDatabase() + { + FreeSpace s = {}; + s.blockInfoIndex = SIZE_MAX; + for(size_t i = 0; i < MAX_COUNT; ++i) + { + m_FreeSpaces[i] = s; + } + } + + void Register(size_t blockInfoIndex, VkDeviceSize offset, VkDeviceSize size) + { + if(size < VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER) + { + return; + } + + // Find first invalid or the smallest structure. + size_t bestIndex = SIZE_MAX; + for(size_t i = 0; i < MAX_COUNT; ++i) + { + // Empty structure. + if(m_FreeSpaces[i].blockInfoIndex == SIZE_MAX) + { + bestIndex = i; + break; + } + if(m_FreeSpaces[i].size < size && + (bestIndex == SIZE_MAX || m_FreeSpaces[bestIndex].size > m_FreeSpaces[i].size)) + { + bestIndex = i; + } + } + + if(bestIndex != SIZE_MAX) + { + m_FreeSpaces[bestIndex].blockInfoIndex = blockInfoIndex; + m_FreeSpaces[bestIndex].offset = offset; + m_FreeSpaces[bestIndex].size = size; + } + } + + bool Fetch(VkDeviceSize alignment, VkDeviceSize size, + size_t& outBlockInfoIndex, VkDeviceSize& outDstOffset) + { + size_t bestIndex = SIZE_MAX; + VkDeviceSize bestFreeSpaceAfter = 0; + for(size_t i = 0; i < MAX_COUNT; ++i) + { + // Structure is valid. + if(m_FreeSpaces[i].blockInfoIndex != SIZE_MAX) + { + const VkDeviceSize dstOffset = VmaAlignUp(m_FreeSpaces[i].offset, alignment); + // Allocation fits into this structure. + if(dstOffset + size <= m_FreeSpaces[i].offset + m_FreeSpaces[i].size) + { + const VkDeviceSize freeSpaceAfter = (m_FreeSpaces[i].offset + m_FreeSpaces[i].size) - + (dstOffset + size); + if(bestIndex == SIZE_MAX || freeSpaceAfter > bestFreeSpaceAfter) + { + bestIndex = i; + bestFreeSpaceAfter = freeSpaceAfter; + } + } + } + } + + if(bestIndex != SIZE_MAX) + { + outBlockInfoIndex = m_FreeSpaces[bestIndex].blockInfoIndex; + outDstOffset = VmaAlignUp(m_FreeSpaces[bestIndex].offset, alignment); + + if(bestFreeSpaceAfter >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER) + { + // Leave this structure for remaining empty space. + const VkDeviceSize alignmentPlusSize = (outDstOffset - m_FreeSpaces[bestIndex].offset) + size; + m_FreeSpaces[bestIndex].offset += alignmentPlusSize; + m_FreeSpaces[bestIndex].size -= alignmentPlusSize; + } + else + { + // This structure becomes invalid. + m_FreeSpaces[bestIndex].blockInfoIndex = SIZE_MAX; + } + + return true; + } + + return false; + } + + private: + static const size_t MAX_COUNT = 4; + + struct FreeSpace + { + size_t blockInfoIndex; // SIZE_MAX means this structure is invalid. + VkDeviceSize offset; + VkDeviceSize size; + } m_FreeSpaces[MAX_COUNT]; + }; + + const bool m_OverlappingMoveSupported; + + uint32_t m_AllocationCount; + bool m_AllAllocations; + + VkDeviceSize m_BytesMoved; + uint32_t m_AllocationsMoved; + + VmaVector< BlockInfo, VmaStlAllocator > m_BlockInfos; + + void PreprocessMetadata(); + void PostprocessMetadata(); + void InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc); +}; + +struct VmaBlockDefragmentationContext +{ + enum BLOCK_FLAG + { + BLOCK_FLAG_USED = 0x00000001, + }; + uint32_t flags; + VkBuffer hBuffer; + + VmaBlockDefragmentationContext() : + flags(0), + hBuffer(VK_NULL_HANDLE) + { + } +}; + +class VmaBlockVectorDefragmentationContext +{ + VMA_CLASS_NO_COPY(VmaBlockVectorDefragmentationContext) +public: + VkResult res; + bool mutexLocked; + VmaVector< VmaBlockDefragmentationContext, VmaStlAllocator > blockContexts; + + VmaBlockVectorDefragmentationContext( + VmaAllocator hAllocator, + VmaPool hCustomPool, // Optional. + VmaBlockVector* pBlockVector, + uint32_t currFrameIndex, + uint32_t flags); + ~VmaBlockVectorDefragmentationContext(); + + VmaPool GetCustomPool() const { return m_hCustomPool; } + VmaBlockVector* GetBlockVector() const { return m_pBlockVector; } + VmaDefragmentationAlgorithm* GetAlgorithm() const { return m_pAlgorithm; } + + void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged); + void AddAll() { m_AllAllocations = true; } + + void Begin(bool overlappingMoveSupported); + +private: + const VmaAllocator m_hAllocator; + // Null if not from custom pool. + const VmaPool m_hCustomPool; + // Redundant, for convenience not to fetch from m_hCustomPool->m_BlockVector or m_hAllocator->m_pBlockVectors. + VmaBlockVector* const m_pBlockVector; + const uint32_t m_CurrFrameIndex; + /*const uint32_t m_AlgorithmFlags;*/ + // Owner of this object. + VmaDefragmentationAlgorithm* m_pAlgorithm; + + struct AllocInfo + { + VmaAllocation hAlloc; + VkBool32* pChanged; + }; + // Used between constructor and Begin. + VmaVector< AllocInfo, VmaStlAllocator > m_Allocations; + bool m_AllAllocations; +}; + +struct VmaDefragmentationContext_T +{ +private: + VMA_CLASS_NO_COPY(VmaDefragmentationContext_T) +public: + VmaDefragmentationContext_T( + VmaAllocator hAllocator, + uint32_t currFrameIndex, + uint32_t flags, + VmaDefragmentationStats* pStats); + ~VmaDefragmentationContext_T(); + + void AddPools(uint32_t poolCount, VmaPool* pPools); + void AddAllocations( + uint32_t allocationCount, + VmaAllocation* pAllocations, + VkBool32* pAllocationsChanged); + + /* + Returns: + - `VK_SUCCESS` if succeeded and object can be destroyed immediately. + - `VK_NOT_READY` if succeeded but the object must remain alive until vmaDefragmentationEnd(). + - Negative value if error occured and object can be destroyed immediately. + */ + VkResult Defragment( + VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove, + VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove, + VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats); + +private: + const VmaAllocator m_hAllocator; + const uint32_t m_CurrFrameIndex; + const uint32_t m_Flags; + VmaDefragmentationStats* const m_pStats; + // Owner of these objects. + VmaBlockVectorDefragmentationContext* m_DefaultPoolContexts[VK_MAX_MEMORY_TYPES]; + // Owner of these objects. + VmaVector< VmaBlockVectorDefragmentationContext*, VmaStlAllocator > m_CustomPoolContexts; +}; + +#if VMA_RECORDING_ENABLED + +class VmaRecorder +{ +public: + VmaRecorder(); + VkResult Init(const VmaRecordSettings& settings, bool useMutex); + void WriteConfiguration( + const VkPhysicalDeviceProperties& devProps, + const VkPhysicalDeviceMemoryProperties& memProps, + bool dedicatedAllocationExtensionEnabled); + ~VmaRecorder(); + + void RecordCreateAllocator(uint32_t frameIndex); + void RecordDestroyAllocator(uint32_t frameIndex); + void RecordCreatePool(uint32_t frameIndex, + const VmaPoolCreateInfo& createInfo, + VmaPool pool); + void RecordDestroyPool(uint32_t frameIndex, VmaPool pool); + void RecordAllocateMemory(uint32_t frameIndex, + const VkMemoryRequirements& vkMemReq, + const VmaAllocationCreateInfo& createInfo, + VmaAllocation allocation); + void RecordAllocateMemoryPages(uint32_t frameIndex, + const VkMemoryRequirements& vkMemReq, + const VmaAllocationCreateInfo& createInfo, + uint64_t allocationCount, + const VmaAllocation* pAllocations); + void RecordAllocateMemoryForBuffer(uint32_t frameIndex, + const VkMemoryRequirements& vkMemReq, + bool requiresDedicatedAllocation, + bool prefersDedicatedAllocation, + const VmaAllocationCreateInfo& createInfo, + VmaAllocation allocation); + void RecordAllocateMemoryForImage(uint32_t frameIndex, + const VkMemoryRequirements& vkMemReq, + bool requiresDedicatedAllocation, + bool prefersDedicatedAllocation, + const VmaAllocationCreateInfo& createInfo, + VmaAllocation allocation); + void RecordFreeMemory(uint32_t frameIndex, + VmaAllocation allocation); + void RecordFreeMemoryPages(uint32_t frameIndex, + uint64_t allocationCount, + const VmaAllocation* pAllocations); + void RecordResizeAllocation( + uint32_t frameIndex, + VmaAllocation allocation, + VkDeviceSize newSize); + void RecordSetAllocationUserData(uint32_t frameIndex, + VmaAllocation allocation, + const void* pUserData); + void RecordCreateLostAllocation(uint32_t frameIndex, + VmaAllocation allocation); + void RecordMapMemory(uint32_t frameIndex, + VmaAllocation allocation); + void RecordUnmapMemory(uint32_t frameIndex, + VmaAllocation allocation); + void RecordFlushAllocation(uint32_t frameIndex, + VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size); + void RecordInvalidateAllocation(uint32_t frameIndex, + VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size); + void RecordCreateBuffer(uint32_t frameIndex, + const VkBufferCreateInfo& bufCreateInfo, + const VmaAllocationCreateInfo& allocCreateInfo, + VmaAllocation allocation); + void RecordCreateImage(uint32_t frameIndex, + const VkImageCreateInfo& imageCreateInfo, + const VmaAllocationCreateInfo& allocCreateInfo, + VmaAllocation allocation); + void RecordDestroyBuffer(uint32_t frameIndex, + VmaAllocation allocation); + void RecordDestroyImage(uint32_t frameIndex, + VmaAllocation allocation); + void RecordTouchAllocation(uint32_t frameIndex, + VmaAllocation allocation); + void RecordGetAllocationInfo(uint32_t frameIndex, + VmaAllocation allocation); + void RecordMakePoolAllocationsLost(uint32_t frameIndex, + VmaPool pool); + void RecordDefragmentationBegin(uint32_t frameIndex, + const VmaDefragmentationInfo2& info, + VmaDefragmentationContext ctx); + void RecordDefragmentationEnd(uint32_t frameIndex, + VmaDefragmentationContext ctx); + +private: + struct CallParams + { + uint32_t threadId; + double time; + }; + + class UserDataString + { + public: + UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData); + const char* GetString() const { return m_Str; } + + private: + char m_PtrStr[17]; + const char* m_Str; + }; + + bool m_UseMutex; + VmaRecordFlags m_Flags; + FILE* m_File; + VMA_MUTEX m_FileMutex; + int64_t m_Freq; + int64_t m_StartCounter; + + void GetBasicParams(CallParams& outParams); + + // T must be a pointer type, e.g. VmaAllocation, VmaPool. + template + void PrintPointerList(uint64_t count, const T* pItems) + { + if(count) + { + fprintf(m_File, "%p", pItems[0]); + for(uint64_t i = 1; i < count; ++i) + { + fprintf(m_File, " %p", pItems[i]); + } + } + } + + void PrintPointerList(uint64_t count, const VmaAllocation* pItems); + void Flush(); +}; + +#endif // #if VMA_RECORDING_ENABLED + +// Main allocator object. +struct VmaAllocator_T +{ + VMA_CLASS_NO_COPY(VmaAllocator_T) +public: + bool m_UseMutex; + bool m_UseKhrDedicatedAllocation; + VkDevice m_hDevice; + bool m_AllocationCallbacksSpecified; + VkAllocationCallbacks m_AllocationCallbacks; + VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks; + + // Number of bytes free out of limit, or VK_WHOLE_SIZE if no limit for that heap. + VkDeviceSize m_HeapSizeLimit[VK_MAX_MEMORY_HEAPS]; + VMA_MUTEX m_HeapSizeLimitMutex; + + VkPhysicalDeviceProperties m_PhysicalDeviceProperties; + VkPhysicalDeviceMemoryProperties m_MemProps; + + // Default pools. + VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES]; + + // Each vector is sorted by memory (handle value). + typedef VmaVector< VmaAllocation, VmaStlAllocator > AllocationVectorType; + AllocationVectorType* m_pDedicatedAllocations[VK_MAX_MEMORY_TYPES]; + VMA_RW_MUTEX m_DedicatedAllocationsMutex[VK_MAX_MEMORY_TYPES]; + + VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo); + VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo); + ~VmaAllocator_T(); + + const VkAllocationCallbacks* GetAllocationCallbacks() const + { + return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : 0; + } + const VmaVulkanFunctions& GetVulkanFunctions() const + { + return m_VulkanFunctions; + } + + VkDeviceSize GetBufferImageGranularity() const + { + return VMA_MAX( + static_cast(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY), + m_PhysicalDeviceProperties.limits.bufferImageGranularity); + } + + uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; } + uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; } + + uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const + { + VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount); + return m_MemProps.memoryTypes[memTypeIndex].heapIndex; + } + // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT. + bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const + { + return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) == + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + } + // Minimum alignment for all allocations in specific memory type. + VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const + { + return IsMemoryTypeNonCoherent(memTypeIndex) ? + VMA_MAX((VkDeviceSize)VMA_DEBUG_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) : + (VkDeviceSize)VMA_DEBUG_ALIGNMENT; + } + + bool IsIntegratedGpu() const + { + return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU; + } + +#if VMA_RECORDING_ENABLED + VmaRecorder* GetRecorder() const { return m_pRecorder; } +#endif + + void GetBufferMemoryRequirements( + VkBuffer hBuffer, + VkMemoryRequirements& memReq, + bool& requiresDedicatedAllocation, + bool& prefersDedicatedAllocation) const; + void GetImageMemoryRequirements( + VkImage hImage, + VkMemoryRequirements& memReq, + bool& requiresDedicatedAllocation, + bool& prefersDedicatedAllocation) const; + + // Main allocation function. + VkResult AllocateMemory( + const VkMemoryRequirements& vkMemReq, + bool requiresDedicatedAllocation, + bool prefersDedicatedAllocation, + VkBuffer dedicatedBuffer, + VkImage dedicatedImage, + const VmaAllocationCreateInfo& createInfo, + VmaSuballocationType suballocType, + size_t allocationCount, + VmaAllocation* pAllocations); + + // Main deallocation function. + void FreeMemory( + size_t allocationCount, + const VmaAllocation* pAllocations); + + VkResult ResizeAllocation( + const VmaAllocation alloc, + VkDeviceSize newSize); + + void CalculateStats(VmaStats* pStats); + +#if VMA_STATS_STRING_ENABLED + void PrintDetailedMap(class VmaJsonWriter& json); +#endif + + VkResult DefragmentationBegin( + const VmaDefragmentationInfo2& info, + VmaDefragmentationStats* pStats, + VmaDefragmentationContext* pContext); + VkResult DefragmentationEnd( + VmaDefragmentationContext context); + + void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo); + bool TouchAllocation(VmaAllocation hAllocation); + + VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool); + void DestroyPool(VmaPool pool); + void GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats); + + void SetCurrentFrameIndex(uint32_t frameIndex); + uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); } + + void MakePoolAllocationsLost( + VmaPool hPool, + size_t* pLostAllocationCount); + VkResult CheckPoolCorruption(VmaPool hPool); + VkResult CheckCorruption(uint32_t memoryTypeBits); + + void CreateLostAllocation(VmaAllocation* pAllocation); + + VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory); + void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory); + + VkResult Map(VmaAllocation hAllocation, void** ppData); + void Unmap(VmaAllocation hAllocation); + + VkResult BindBufferMemory(VmaAllocation hAllocation, VkBuffer hBuffer); + VkResult BindImageMemory(VmaAllocation hAllocation, VkImage hImage); + + void FlushOrInvalidateAllocation( + VmaAllocation hAllocation, + VkDeviceSize offset, VkDeviceSize size, + VMA_CACHE_OPERATION op); + + void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern); + +private: + VkDeviceSize m_PreferredLargeHeapBlockSize; + + VkPhysicalDevice m_PhysicalDevice; + VMA_ATOMIC_UINT32 m_CurrentFrameIndex; + + VMA_RW_MUTEX m_PoolsMutex; + // Protected by m_PoolsMutex. Sorted by pointer value. + VmaVector > m_Pools; + uint32_t m_NextPoolId; + + VmaVulkanFunctions m_VulkanFunctions; + +#if VMA_RECORDING_ENABLED + VmaRecorder* m_pRecorder; +#endif + + void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions); + + VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex); + + VkResult AllocateMemoryOfType( + VkDeviceSize size, + VkDeviceSize alignment, + bool dedicatedAllocation, + VkBuffer dedicatedBuffer, + VkImage dedicatedImage, + const VmaAllocationCreateInfo& createInfo, + uint32_t memTypeIndex, + VmaSuballocationType suballocType, + size_t allocationCount, + VmaAllocation* pAllocations); + + // Helper function only to be used inside AllocateDedicatedMemory. + VkResult AllocateDedicatedMemoryPage( + VkDeviceSize size, + VmaSuballocationType suballocType, + uint32_t memTypeIndex, + const VkMemoryAllocateInfo& allocInfo, + bool map, + bool isUserDataString, + void* pUserData, + VmaAllocation* pAllocation); + + // Allocates and registers new VkDeviceMemory specifically for dedicated allocations. + VkResult AllocateDedicatedMemory( + VkDeviceSize size, + VmaSuballocationType suballocType, + uint32_t memTypeIndex, + bool map, + bool isUserDataString, + void* pUserData, + VkBuffer dedicatedBuffer, + VkImage dedicatedImage, + size_t allocationCount, + VmaAllocation* pAllocations); + + // Tries to free pMemory as Dedicated Memory. Returns true if found and freed. + void FreeDedicatedMemory(VmaAllocation allocation); +}; + +//////////////////////////////////////////////////////////////////////////////// +// Memory allocation #2 after VmaAllocator_T definition + +static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment) +{ + return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment); +} + +static void VmaFree(VmaAllocator hAllocator, void* ptr) +{ + VmaFree(&hAllocator->m_AllocationCallbacks, ptr); +} + +template +static T* VmaAllocate(VmaAllocator hAllocator) +{ + return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T)); +} + +template +static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count) +{ + return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T)); +} + +template +static void vma_delete(VmaAllocator hAllocator, T* ptr) +{ + if(ptr != VMA_NULL) + { + ptr->~T(); + VmaFree(hAllocator, ptr); + } +} + +template +static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count) +{ + if(ptr != VMA_NULL) + { + for(size_t i = count; i--; ) + ptr[i].~T(); + VmaFree(hAllocator, ptr); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// VmaStringBuilder + +#if VMA_STATS_STRING_ENABLED + +class VmaStringBuilder +{ +public: + VmaStringBuilder(VmaAllocator alloc) : m_Data(VmaStlAllocator(alloc->GetAllocationCallbacks())) { } + size_t GetLength() const { return m_Data.size(); } + const char* GetData() const { return m_Data.data(); } + + void Add(char ch) { m_Data.push_back(ch); } + void Add(const char* pStr); + void AddNewLine() { Add('\n'); } + void AddNumber(uint32_t num); + void AddNumber(uint64_t num); + void AddPointer(const void* ptr); + +private: + VmaVector< char, VmaStlAllocator > m_Data; +}; + +void VmaStringBuilder::Add(const char* pStr) +{ + const size_t strLen = strlen(pStr); + if(strLen > 0) + { + const size_t oldCount = m_Data.size(); + m_Data.resize(oldCount + strLen); + memcpy(m_Data.data() + oldCount, pStr, strLen); + } +} + +void VmaStringBuilder::AddNumber(uint32_t num) +{ + char buf[11]; + VmaUint32ToStr(buf, sizeof(buf), num); + Add(buf); +} + +void VmaStringBuilder::AddNumber(uint64_t num) +{ + char buf[21]; + VmaUint64ToStr(buf, sizeof(buf), num); + Add(buf); +} + +void VmaStringBuilder::AddPointer(const void* ptr) +{ + char buf[21]; + VmaPtrToStr(buf, sizeof(buf), ptr); + Add(buf); +} + +#endif // #if VMA_STATS_STRING_ENABLED + +//////////////////////////////////////////////////////////////////////////////// +// VmaJsonWriter + +#if VMA_STATS_STRING_ENABLED + +class VmaJsonWriter +{ + VMA_CLASS_NO_COPY(VmaJsonWriter) +public: + VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb); + ~VmaJsonWriter(); + + void BeginObject(bool singleLine = false); + void EndObject(); + + void BeginArray(bool singleLine = false); + void EndArray(); + + void WriteString(const char* pStr); + void BeginString(const char* pStr = VMA_NULL); + void ContinueString(const char* pStr); + void ContinueString(uint32_t n); + void ContinueString(uint64_t n); + void ContinueString_Pointer(const void* ptr); + void EndString(const char* pStr = VMA_NULL); + + void WriteNumber(uint32_t n); + void WriteNumber(uint64_t n); + void WriteBool(bool b); + void WriteNull(); + +private: + static const char* const INDENT; + + enum COLLECTION_TYPE + { + COLLECTION_TYPE_OBJECT, + COLLECTION_TYPE_ARRAY, + }; + struct StackItem + { + COLLECTION_TYPE type; + uint32_t valueCount; + bool singleLineMode; + }; + + VmaStringBuilder& m_SB; + VmaVector< StackItem, VmaStlAllocator > m_Stack; + bool m_InsideString; + + void BeginValue(bool isString); + void WriteIndent(bool oneLess = false); +}; + +const char* const VmaJsonWriter::INDENT = " "; + +VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) : + m_SB(sb), + m_Stack(VmaStlAllocator(pAllocationCallbacks)), + m_InsideString(false) +{ +} + +VmaJsonWriter::~VmaJsonWriter() +{ + VMA_ASSERT(!m_InsideString); + VMA_ASSERT(m_Stack.empty()); +} + +void VmaJsonWriter::BeginObject(bool singleLine) +{ + VMA_ASSERT(!m_InsideString); + + BeginValue(false); + m_SB.Add('{'); + + StackItem item; + item.type = COLLECTION_TYPE_OBJECT; + item.valueCount = 0; + item.singleLineMode = singleLine; + m_Stack.push_back(item); +} + +void VmaJsonWriter::EndObject() +{ + VMA_ASSERT(!m_InsideString); + + WriteIndent(true); + m_SB.Add('}'); + + VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT); + m_Stack.pop_back(); +} + +void VmaJsonWriter::BeginArray(bool singleLine) +{ + VMA_ASSERT(!m_InsideString); + + BeginValue(false); + m_SB.Add('['); + + StackItem item; + item.type = COLLECTION_TYPE_ARRAY; + item.valueCount = 0; + item.singleLineMode = singleLine; + m_Stack.push_back(item); +} + +void VmaJsonWriter::EndArray() +{ + VMA_ASSERT(!m_InsideString); + + WriteIndent(true); + m_SB.Add(']'); + + VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY); + m_Stack.pop_back(); +} + +void VmaJsonWriter::WriteString(const char* pStr) +{ + BeginString(pStr); + EndString(); +} + +void VmaJsonWriter::BeginString(const char* pStr) +{ + VMA_ASSERT(!m_InsideString); + + BeginValue(true); + m_SB.Add('"'); + m_InsideString = true; + if(pStr != VMA_NULL && pStr[0] != '\0') + { + ContinueString(pStr); + } +} + +void VmaJsonWriter::ContinueString(const char* pStr) +{ + VMA_ASSERT(m_InsideString); + + const size_t strLen = strlen(pStr); + for(size_t i = 0; i < strLen; ++i) + { + char ch = pStr[i]; + if(ch == '\\') + { + m_SB.Add("\\\\"); + } + else if(ch == '"') + { + m_SB.Add("\\\""); + } + else if(ch >= 32) + { + m_SB.Add(ch); + } + else switch(ch) + { + case '\b': + m_SB.Add("\\b"); + break; + case '\f': + m_SB.Add("\\f"); + break; + case '\n': + m_SB.Add("\\n"); + break; + case '\r': + m_SB.Add("\\r"); + break; + case '\t': + m_SB.Add("\\t"); + break; + default: + VMA_ASSERT(0 && "Character not currently supported."); + break; + } + } +} + +void VmaJsonWriter::ContinueString(uint32_t n) +{ + VMA_ASSERT(m_InsideString); + m_SB.AddNumber(n); +} + +void VmaJsonWriter::ContinueString(uint64_t n) +{ + VMA_ASSERT(m_InsideString); + m_SB.AddNumber(n); +} + +void VmaJsonWriter::ContinueString_Pointer(const void* ptr) +{ + VMA_ASSERT(m_InsideString); + m_SB.AddPointer(ptr); +} + +void VmaJsonWriter::EndString(const char* pStr) +{ + VMA_ASSERT(m_InsideString); + if(pStr != VMA_NULL && pStr[0] != '\0') + { + ContinueString(pStr); + } + m_SB.Add('"'); + m_InsideString = false; +} + +void VmaJsonWriter::WriteNumber(uint32_t n) +{ + VMA_ASSERT(!m_InsideString); + BeginValue(false); + m_SB.AddNumber(n); +} + +void VmaJsonWriter::WriteNumber(uint64_t n) +{ + VMA_ASSERT(!m_InsideString); + BeginValue(false); + m_SB.AddNumber(n); +} + +void VmaJsonWriter::WriteBool(bool b) +{ + VMA_ASSERT(!m_InsideString); + BeginValue(false); + m_SB.Add(b ? "true" : "false"); +} + +void VmaJsonWriter::WriteNull() +{ + VMA_ASSERT(!m_InsideString); + BeginValue(false); + m_SB.Add("null"); +} + +void VmaJsonWriter::BeginValue(bool isString) +{ + if(!m_Stack.empty()) + { + StackItem& currItem = m_Stack.back(); + if(currItem.type == COLLECTION_TYPE_OBJECT && + currItem.valueCount % 2 == 0) + { + (void) isString; + VMA_ASSERT(isString); + } + + if(currItem.type == COLLECTION_TYPE_OBJECT && + currItem.valueCount % 2 != 0) + { + m_SB.Add(": "); + } + else if(currItem.valueCount > 0) + { + m_SB.Add(", "); + WriteIndent(); + } + else + { + WriteIndent(); + } + ++currItem.valueCount; + } +} + +void VmaJsonWriter::WriteIndent(bool oneLess) +{ + if(!m_Stack.empty() && !m_Stack.back().singleLineMode) + { + m_SB.AddNewLine(); + + size_t count = m_Stack.size(); + if(count > 0 && oneLess) + { + --count; + } + for(size_t i = 0; i < count; ++i) + { + m_SB.Add(INDENT); + } + } +} + +#endif // #if VMA_STATS_STRING_ENABLED + +//////////////////////////////////////////////////////////////////////////////// + +void VmaAllocation_T::SetUserData(VmaAllocator hAllocator, void* pUserData) +{ + if(IsUserDataString()) + { + VMA_ASSERT(pUserData == VMA_NULL || pUserData != m_pUserData); + + FreeUserDataString(hAllocator); + + if(pUserData != VMA_NULL) + { + const char* const newStrSrc = (char*)pUserData; + const size_t newStrLen = strlen(newStrSrc); + char* const newStrDst = vma_new_array(hAllocator, char, newStrLen + 1); + memcpy(newStrDst, newStrSrc, newStrLen + 1); + m_pUserData = newStrDst; + } + } + else + { + m_pUserData = pUserData; + } +} + +void VmaAllocation_T::ChangeBlockAllocation( + VmaAllocator hAllocator, + VmaDeviceMemoryBlock* block, + VkDeviceSize offset) +{ + VMA_ASSERT(block != VMA_NULL); + VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK); + + // Move mapping reference counter from old block to new block. + if(block != m_BlockAllocation.m_Block) + { + uint32_t mapRefCount = m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP; + if(IsPersistentMap()) + ++mapRefCount; + m_BlockAllocation.m_Block->Unmap(hAllocator, mapRefCount); + block->Map(hAllocator, mapRefCount, VMA_NULL); + } + + m_BlockAllocation.m_Block = block; + m_BlockAllocation.m_Offset = offset; +} + +void VmaAllocation_T::ChangeSize(VkDeviceSize newSize) +{ + VMA_ASSERT(newSize > 0); + m_Size = newSize; +} + +void VmaAllocation_T::ChangeOffset(VkDeviceSize newOffset) +{ + VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK); + m_BlockAllocation.m_Offset = newOffset; +} + +VkDeviceSize VmaAllocation_T::GetOffset() const +{ + switch(m_Type) + { + case ALLOCATION_TYPE_BLOCK: + return m_BlockAllocation.m_Offset; + case ALLOCATION_TYPE_DEDICATED: + return 0; + default: + VMA_ASSERT(0); + return 0; + } +} + +VkDeviceMemory VmaAllocation_T::GetMemory() const +{ + switch(m_Type) + { + case ALLOCATION_TYPE_BLOCK: + return m_BlockAllocation.m_Block->GetDeviceMemory(); + case ALLOCATION_TYPE_DEDICATED: + return m_DedicatedAllocation.m_hMemory; + default: + VMA_ASSERT(0); + return VK_NULL_HANDLE; + } +} + +uint32_t VmaAllocation_T::GetMemoryTypeIndex() const +{ + switch(m_Type) + { + case ALLOCATION_TYPE_BLOCK: + return m_BlockAllocation.m_Block->GetMemoryTypeIndex(); + case ALLOCATION_TYPE_DEDICATED: + return m_DedicatedAllocation.m_MemoryTypeIndex; + default: + VMA_ASSERT(0); + return UINT32_MAX; + } +} + +void* VmaAllocation_T::GetMappedData() const +{ + switch(m_Type) + { + case ALLOCATION_TYPE_BLOCK: + if(m_MapCount != 0) + { + void* pBlockData = m_BlockAllocation.m_Block->GetMappedData(); + VMA_ASSERT(pBlockData != VMA_NULL); + return (char*)pBlockData + m_BlockAllocation.m_Offset; + } + else + { + return VMA_NULL; + } + break; + case ALLOCATION_TYPE_DEDICATED: + VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0)); + return m_DedicatedAllocation.m_pMappedData; + default: + VMA_ASSERT(0); + return VMA_NULL; + } +} + +bool VmaAllocation_T::CanBecomeLost() const +{ + switch(m_Type) + { + case ALLOCATION_TYPE_BLOCK: + return m_BlockAllocation.m_CanBecomeLost; + case ALLOCATION_TYPE_DEDICATED: + return false; + default: + VMA_ASSERT(0); + return false; + } +} + +VmaPool VmaAllocation_T::GetPool() const +{ + VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK); + return m_BlockAllocation.m_hPool; +} + +bool VmaAllocation_T::MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) +{ + VMA_ASSERT(CanBecomeLost()); + + /* + Warning: This is a carefully designed algorithm. + Do not modify unless you really know what you're doing :) + */ + uint32_t localLastUseFrameIndex = GetLastUseFrameIndex(); + for(;;) + { + if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST) + { + VMA_ASSERT(0); + return false; + } + else if(localLastUseFrameIndex + frameInUseCount >= currentFrameIndex) + { + return false; + } + else // Last use time earlier than current time. + { + if(CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, VMA_FRAME_INDEX_LOST)) + { + // Setting hAllocation.LastUseFrameIndex atomic to VMA_FRAME_INDEX_LOST is enough to mark it as LOST. + // Calling code just needs to unregister this allocation in owning VmaDeviceMemoryBlock. + return true; + } + } + } +} + +#if VMA_STATS_STRING_ENABLED + +// Correspond to values of enum VmaSuballocationType. +static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = { + "FREE", + "UNKNOWN", + "BUFFER", + "IMAGE_UNKNOWN", + "IMAGE_LINEAR", + "IMAGE_OPTIMAL", +}; + +void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const +{ + json.WriteString("Type"); + json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]); + + json.WriteString("Size"); + json.WriteNumber(m_Size); + + if(m_pUserData != VMA_NULL) + { + json.WriteString("UserData"); + if(IsUserDataString()) + { + json.WriteString((const char*)m_pUserData); + } + else + { + json.BeginString(); + json.ContinueString_Pointer(m_pUserData); + json.EndString(); + } + } + + json.WriteString("CreationFrameIndex"); + json.WriteNumber(m_CreationFrameIndex); + + json.WriteString("LastUseFrameIndex"); + json.WriteNumber(GetLastUseFrameIndex()); + + if(m_BufferImageUsage != 0) + { + json.WriteString("Usage"); + json.WriteNumber(m_BufferImageUsage); + } +} + +#endif + +void VmaAllocation_T::FreeUserDataString(VmaAllocator hAllocator) +{ + VMA_ASSERT(IsUserDataString()); + if(m_pUserData != VMA_NULL) + { + char* const oldStr = (char*)m_pUserData; + const size_t oldStrLen = strlen(oldStr); + vma_delete_array(hAllocator, oldStr, oldStrLen + 1); + m_pUserData = VMA_NULL; + } +} + +void VmaAllocation_T::BlockAllocMap() +{ + VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK); + + if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F) + { + ++m_MapCount; + } + else + { + VMA_ASSERT(0 && "Allocation mapped too many times simultaneously."); + } +} + +void VmaAllocation_T::BlockAllocUnmap() +{ + VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK); + + if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0) + { + --m_MapCount; + } + else + { + VMA_ASSERT(0 && "Unmapping allocation not previously mapped."); + } +} + +VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData) +{ + VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED); + + if(m_MapCount != 0) + { + if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F) + { + VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL); + *ppData = m_DedicatedAllocation.m_pMappedData; + ++m_MapCount; + return VK_SUCCESS; + } + else + { + VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously."); + return VK_ERROR_MEMORY_MAP_FAILED; + } + } + else + { + VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)( + hAllocator->m_hDevice, + m_DedicatedAllocation.m_hMemory, + 0, // offset + VK_WHOLE_SIZE, + 0, // flags + ppData); + if(result == VK_SUCCESS) + { + m_DedicatedAllocation.m_pMappedData = *ppData; + m_MapCount = 1; + } + return result; + } +} + +void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator) +{ + VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED); + + if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0) + { + --m_MapCount; + if(m_MapCount == 0) + { + m_DedicatedAllocation.m_pMappedData = VMA_NULL; + (*hAllocator->GetVulkanFunctions().vkUnmapMemory)( + hAllocator->m_hDevice, + m_DedicatedAllocation.m_hMemory); + } + } + else + { + VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped."); + } +} + +#if VMA_STATS_STRING_ENABLED + +static void VmaPrintStatInfo(VmaJsonWriter& json, const VmaStatInfo& stat) +{ + json.BeginObject(); + + json.WriteString("Blocks"); + json.WriteNumber(stat.blockCount); + + json.WriteString("Allocations"); + json.WriteNumber(stat.allocationCount); + + json.WriteString("UnusedRanges"); + json.WriteNumber(stat.unusedRangeCount); + + json.WriteString("UsedBytes"); + json.WriteNumber(stat.usedBytes); + + json.WriteString("UnusedBytes"); + json.WriteNumber(stat.unusedBytes); + + if(stat.allocationCount > 1) + { + json.WriteString("AllocationSize"); + json.BeginObject(true); + json.WriteString("Min"); + json.WriteNumber(stat.allocationSizeMin); + json.WriteString("Avg"); + json.WriteNumber(stat.allocationSizeAvg); + json.WriteString("Max"); + json.WriteNumber(stat.allocationSizeMax); + json.EndObject(); + } + + if(stat.unusedRangeCount > 1) + { + json.WriteString("UnusedRangeSize"); + json.BeginObject(true); + json.WriteString("Min"); + json.WriteNumber(stat.unusedRangeSizeMin); + json.WriteString("Avg"); + json.WriteNumber(stat.unusedRangeSizeAvg); + json.WriteString("Max"); + json.WriteNumber(stat.unusedRangeSizeMax); + json.EndObject(); + } + + json.EndObject(); +} + +#endif // #if VMA_STATS_STRING_ENABLED + +struct VmaSuballocationItemSizeLess +{ + bool operator()( + const VmaSuballocationList::iterator lhs, + const VmaSuballocationList::iterator rhs) const + { + return lhs->size < rhs->size; + } + bool operator()( + const VmaSuballocationList::iterator lhs, + VkDeviceSize rhsSize) const + { + return lhs->size < rhsSize; + } +}; + + +//////////////////////////////////////////////////////////////////////////////// +// class VmaBlockMetadata + +VmaBlockMetadata::VmaBlockMetadata(VmaAllocator hAllocator) : + m_Size(0), + m_pAllocationCallbacks(hAllocator->GetAllocationCallbacks()) +{ +} + +#if VMA_STATS_STRING_ENABLED + +void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json, + VkDeviceSize unusedBytes, + size_t allocationCount, + size_t unusedRangeCount) const +{ + json.BeginObject(); + + json.WriteString("TotalBytes"); + json.WriteNumber(GetSize()); + + json.WriteString("UnusedBytes"); + json.WriteNumber(unusedBytes); + + json.WriteString("Allocations"); + json.WriteNumber((uint64_t)allocationCount); + + json.WriteString("UnusedRanges"); + json.WriteNumber((uint64_t)unusedRangeCount); + + json.WriteString("Suballocations"); + json.BeginArray(); +} + +void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json, + VkDeviceSize offset, + VmaAllocation hAllocation) const +{ + json.BeginObject(true); + + json.WriteString("Offset"); + json.WriteNumber(offset); + + hAllocation->PrintParameters(json); + + json.EndObject(); +} + +void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json, + VkDeviceSize offset, + VkDeviceSize size) const +{ + json.BeginObject(true); + + json.WriteString("Offset"); + json.WriteNumber(offset); + + json.WriteString("Type"); + json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]); + + json.WriteString("Size"); + json.WriteNumber(size); + + json.EndObject(); +} + +void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const +{ + json.EndArray(); + json.EndObject(); +} + +#endif // #if VMA_STATS_STRING_ENABLED + +//////////////////////////////////////////////////////////////////////////////// +// class VmaBlockMetadata_Generic + +VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(VmaAllocator hAllocator) : + VmaBlockMetadata(hAllocator), + m_FreeCount(0), + m_SumFreeSize(0), + m_Suballocations(VmaStlAllocator(hAllocator->GetAllocationCallbacks())), + m_FreeSuballocationsBySize(VmaStlAllocator(hAllocator->GetAllocationCallbacks())) +{ +} + +VmaBlockMetadata_Generic::~VmaBlockMetadata_Generic() +{ +} + +void VmaBlockMetadata_Generic::Init(VkDeviceSize size) +{ + VmaBlockMetadata::Init(size); + + m_FreeCount = 1; + m_SumFreeSize = size; + + VmaSuballocation suballoc = {}; + suballoc.offset = 0; + suballoc.size = size; + suballoc.type = VMA_SUBALLOCATION_TYPE_FREE; + suballoc.hAllocation = VK_NULL_HANDLE; + + VMA_ASSERT(size > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER); + m_Suballocations.push_back(suballoc); + VmaSuballocationList::iterator suballocItem = m_Suballocations.end(); + --suballocItem; + m_FreeSuballocationsBySize.push_back(suballocItem); +} + +bool VmaBlockMetadata_Generic::Validate() const +{ + VMA_VALIDATE(!m_Suballocations.empty()); + + // Expected offset of new suballocation as calculated from previous ones. + VkDeviceSize calculatedOffset = 0; + // Expected number of free suballocations as calculated from traversing their list. + uint32_t calculatedFreeCount = 0; + // Expected sum size of free suballocations as calculated from traversing their list. + VkDeviceSize calculatedSumFreeSize = 0; + // Expected number of free suballocations that should be registered in + // m_FreeSuballocationsBySize calculated from traversing their list. + size_t freeSuballocationsToRegister = 0; + // True if previous visited suballocation was free. + bool prevFree = false; + + for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin(); + suballocItem != m_Suballocations.cend(); + ++suballocItem) + { + const VmaSuballocation& subAlloc = *suballocItem; + + // Actual offset of this suballocation doesn't match expected one. + VMA_VALIDATE(subAlloc.offset == calculatedOffset); + + const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE); + // Two adjacent free suballocations are invalid. They should be merged. + VMA_VALIDATE(!prevFree || !currFree); + + VMA_VALIDATE(currFree == (subAlloc.hAllocation == VK_NULL_HANDLE)); + + if(currFree) + { + calculatedSumFreeSize += subAlloc.size; + ++calculatedFreeCount; + if(subAlloc.size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER) + { + ++freeSuballocationsToRegister; + } + + // Margin required between allocations - every free space must be at least that large. +#if VMA_DEBUG_MARGIN + VMA_VALIDATE(subAlloc.size >= VMA_DEBUG_MARGIN); +#endif + } + else + { + VMA_VALIDATE(subAlloc.hAllocation->GetOffset() == subAlloc.offset); + VMA_VALIDATE(subAlloc.hAllocation->GetSize() == subAlloc.size); + + // Margin required between allocations - previous allocation must be free. + VMA_VALIDATE(VMA_DEBUG_MARGIN == 0 || prevFree); + } + + calculatedOffset += subAlloc.size; + prevFree = currFree; + } + + // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't + // match expected one. + VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister); + + VkDeviceSize lastSize = 0; + for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i) + { + VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i]; + + // Only free suballocations can be registered in m_FreeSuballocationsBySize. + VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE); + // They must be sorted by size ascending. + VMA_VALIDATE(suballocItem->size >= lastSize); + + lastSize = suballocItem->size; + } + + // Check if totals match calculacted values. + VMA_VALIDATE(ValidateFreeSuballocationList()); + VMA_VALIDATE(calculatedOffset == GetSize()); + VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize); + VMA_VALIDATE(calculatedFreeCount == m_FreeCount); + + return true; +} + +VkDeviceSize VmaBlockMetadata_Generic::GetUnusedRangeSizeMax() const +{ + if(!m_FreeSuballocationsBySize.empty()) + { + return m_FreeSuballocationsBySize.back()->size; + } + else + { + return 0; + } +} + +bool VmaBlockMetadata_Generic::IsEmpty() const +{ + return (m_Suballocations.size() == 1) && (m_FreeCount == 1); +} + +void VmaBlockMetadata_Generic::CalcAllocationStatInfo(VmaStatInfo& outInfo) const +{ + outInfo.blockCount = 1; + + const uint32_t rangeCount = (uint32_t)m_Suballocations.size(); + outInfo.allocationCount = rangeCount - m_FreeCount; + outInfo.unusedRangeCount = m_FreeCount; + + outInfo.unusedBytes = m_SumFreeSize; + outInfo.usedBytes = GetSize() - outInfo.unusedBytes; + + outInfo.allocationSizeMin = UINT64_MAX; + outInfo.allocationSizeMax = 0; + outInfo.unusedRangeSizeMin = UINT64_MAX; + outInfo.unusedRangeSizeMax = 0; + + for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin(); + suballocItem != m_Suballocations.cend(); + ++suballocItem) + { + const VmaSuballocation& suballoc = *suballocItem; + if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE) + { + outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size); + outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, suballoc.size); + } + else + { + outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, suballoc.size); + outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, suballoc.size); + } + } +} + +void VmaBlockMetadata_Generic::AddPoolStats(VmaPoolStats& inoutStats) const +{ + const uint32_t rangeCount = (uint32_t)m_Suballocations.size(); + + inoutStats.size += GetSize(); + inoutStats.unusedSize += m_SumFreeSize; + inoutStats.allocationCount += rangeCount - m_FreeCount; + inoutStats.unusedRangeCount += m_FreeCount; + inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax()); +} + +#if VMA_STATS_STRING_ENABLED + +void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json) const +{ + PrintDetailedMap_Begin(json, + m_SumFreeSize, // unusedBytes + m_Suballocations.size() - (size_t)m_FreeCount, // allocationCount + m_FreeCount); // unusedRangeCount + + size_t i = 0; + for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin(); + suballocItem != m_Suballocations.cend(); + ++suballocItem, ++i) + { + if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE) + { + PrintDetailedMap_UnusedRange(json, suballocItem->offset, suballocItem->size); + } + else + { + PrintDetailedMap_Allocation(json, suballocItem->offset, suballocItem->hAllocation); + } + } + + PrintDetailedMap_End(json); +} + +#endif // #if VMA_STATS_STRING_ENABLED + +bool VmaBlockMetadata_Generic::CreateAllocationRequest( + uint32_t currentFrameIndex, + uint32_t frameInUseCount, + VkDeviceSize bufferImageGranularity, + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + bool canMakeOtherLost, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) +{ + VMA_ASSERT(allocSize > 0); + VMA_ASSERT(!upperAddress); + (void) upperAddress; + VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE); + VMA_ASSERT(pAllocationRequest != VMA_NULL); + VMA_HEAVY_ASSERT(Validate()); + + // There is not enough total free space in this block to fullfill the request: Early return. + if(canMakeOtherLost == false && + m_SumFreeSize < allocSize + 2 * VMA_DEBUG_MARGIN) + { + return false; + } + + // New algorithm, efficiently searching freeSuballocationsBySize. + const size_t freeSuballocCount = m_FreeSuballocationsBySize.size(); + if(freeSuballocCount > 0) + { + if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT) + { + // Find first free suballocation with size not less than allocSize + 2 * VMA_DEBUG_MARGIN. + VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess( + m_FreeSuballocationsBySize.data(), + m_FreeSuballocationsBySize.data() + freeSuballocCount, + allocSize + 2 * VMA_DEBUG_MARGIN, + VmaSuballocationItemSizeLess()); + size_t index = it - m_FreeSuballocationsBySize.data(); + for(; index < freeSuballocCount; ++index) + { + if(CheckAllocation( + currentFrameIndex, + frameInUseCount, + bufferImageGranularity, + allocSize, + allocAlignment, + allocType, + m_FreeSuballocationsBySize[index], + false, // canMakeOtherLost + &pAllocationRequest->offset, + &pAllocationRequest->itemsToMakeLostCount, + &pAllocationRequest->sumFreeSize, + &pAllocationRequest->sumItemSize)) + { + pAllocationRequest->item = m_FreeSuballocationsBySize[index]; + return true; + } + } + } + else if(strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET) + { + for(VmaSuballocationList::iterator it = m_Suballocations.begin(); + it != m_Suballocations.end(); + ++it) + { + if(it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation( + currentFrameIndex, + frameInUseCount, + bufferImageGranularity, + allocSize, + allocAlignment, + allocType, + it, + false, // canMakeOtherLost + &pAllocationRequest->offset, + &pAllocationRequest->itemsToMakeLostCount, + &pAllocationRequest->sumFreeSize, + &pAllocationRequest->sumItemSize)) + { + pAllocationRequest->item = it; + return true; + } + } + } + else // WORST_FIT, FIRST_FIT + { + // Search staring from biggest suballocations. + for(size_t index = freeSuballocCount; index--; ) + { + if(CheckAllocation( + currentFrameIndex, + frameInUseCount, + bufferImageGranularity, + allocSize, + allocAlignment, + allocType, + m_FreeSuballocationsBySize[index], + false, // canMakeOtherLost + &pAllocationRequest->offset, + &pAllocationRequest->itemsToMakeLostCount, + &pAllocationRequest->sumFreeSize, + &pAllocationRequest->sumItemSize)) + { + pAllocationRequest->item = m_FreeSuballocationsBySize[index]; + return true; + } + } + } + } + + if(canMakeOtherLost) + { + // Brute-force algorithm. TODO: Come up with something better. + + pAllocationRequest->sumFreeSize = VK_WHOLE_SIZE; + pAllocationRequest->sumItemSize = VK_WHOLE_SIZE; + + VmaAllocationRequest tmpAllocRequest = {}; + for(VmaSuballocationList::iterator suballocIt = m_Suballocations.begin(); + suballocIt != m_Suballocations.end(); + ++suballocIt) + { + if(suballocIt->type == VMA_SUBALLOCATION_TYPE_FREE || + suballocIt->hAllocation->CanBecomeLost()) + { + if(CheckAllocation( + currentFrameIndex, + frameInUseCount, + bufferImageGranularity, + allocSize, + allocAlignment, + allocType, + suballocIt, + canMakeOtherLost, + &tmpAllocRequest.offset, + &tmpAllocRequest.itemsToMakeLostCount, + &tmpAllocRequest.sumFreeSize, + &tmpAllocRequest.sumItemSize)) + { + tmpAllocRequest.item = suballocIt; + + if(tmpAllocRequest.CalcCost() < pAllocationRequest->CalcCost() || + strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT) + { + *pAllocationRequest = tmpAllocRequest; + } + } + } + } + + if(pAllocationRequest->sumItemSize != VK_WHOLE_SIZE) + { + return true; + } + } + + return false; +} + +bool VmaBlockMetadata_Generic::MakeRequestedAllocationsLost( + uint32_t currentFrameIndex, + uint32_t frameInUseCount, + VmaAllocationRequest* pAllocationRequest) +{ + while(pAllocationRequest->itemsToMakeLostCount > 0) + { + if(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE) + { + ++pAllocationRequest->item; + } + VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end()); + VMA_ASSERT(pAllocationRequest->item->hAllocation != VK_NULL_HANDLE); + VMA_ASSERT(pAllocationRequest->item->hAllocation->CanBecomeLost()); + if(pAllocationRequest->item->hAllocation->MakeLost(currentFrameIndex, frameInUseCount)) + { + pAllocationRequest->item = FreeSuballocation(pAllocationRequest->item); + --pAllocationRequest->itemsToMakeLostCount; + } + else + { + return false; + } + } + + VMA_HEAVY_ASSERT(Validate()); + VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end()); + VMA_ASSERT(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE); + + return true; +} + +uint32_t VmaBlockMetadata_Generic::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) +{ + uint32_t lostAllocationCount = 0; + for(VmaSuballocationList::iterator it = m_Suballocations.begin(); + it != m_Suballocations.end(); + ++it) + { + if(it->type != VMA_SUBALLOCATION_TYPE_FREE && + it->hAllocation->CanBecomeLost() && + it->hAllocation->MakeLost(currentFrameIndex, frameInUseCount)) + { + it = FreeSuballocation(it); + ++lostAllocationCount; + } + } + return lostAllocationCount; +} + +VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData) +{ + for(VmaSuballocationList::iterator it = m_Suballocations.begin(); + it != m_Suballocations.end(); + ++it) + { + if(it->type != VMA_SUBALLOCATION_TYPE_FREE) + { + if(!VmaValidateMagicValue(pBlockData, it->offset - VMA_DEBUG_MARGIN)) + { + VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!"); + return VK_ERROR_VALIDATION_FAILED_EXT; + } + if(!VmaValidateMagicValue(pBlockData, it->offset + it->size)) + { + VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!"); + return VK_ERROR_VALIDATION_FAILED_EXT; + } + } + } + + return VK_SUCCESS; +} + +void VmaBlockMetadata_Generic::Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + VkDeviceSize allocSize, + bool upperAddress, + VmaAllocation hAllocation) +{ + VMA_ASSERT(!upperAddress); + (void) upperAddress; + VMA_ASSERT(request.item != m_Suballocations.end()); + VmaSuballocation& suballoc = *request.item; + // Given suballocation is a free block. + VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE); + // Given offset is inside this suballocation. + VMA_ASSERT(request.offset >= suballoc.offset); + const VkDeviceSize paddingBegin = request.offset - suballoc.offset; + VMA_ASSERT(suballoc.size >= paddingBegin + allocSize); + const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - allocSize; + + // Unregister this free suballocation from m_FreeSuballocationsBySize and update + // it to become used. + UnregisterFreeSuballocation(request.item); + + suballoc.offset = request.offset; + suballoc.size = allocSize; + suballoc.type = type; + suballoc.hAllocation = hAllocation; + + // If there are any free bytes remaining at the end, insert new free suballocation after current one. + if(paddingEnd) + { + VmaSuballocation paddingSuballoc = {}; + paddingSuballoc.offset = request.offset + allocSize; + paddingSuballoc.size = paddingEnd; + paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE; + VmaSuballocationList::iterator next = request.item; + ++next; + const VmaSuballocationList::iterator paddingEndItem = + m_Suballocations.insert(next, paddingSuballoc); + RegisterFreeSuballocation(paddingEndItem); + } + + // If there are any free bytes remaining at the beginning, insert new free suballocation before current one. + if(paddingBegin) + { + VmaSuballocation paddingSuballoc = {}; + paddingSuballoc.offset = request.offset - paddingBegin; + paddingSuballoc.size = paddingBegin; + paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE; + const VmaSuballocationList::iterator paddingBeginItem = + m_Suballocations.insert(request.item, paddingSuballoc); + RegisterFreeSuballocation(paddingBeginItem); + } + + // Update totals. + m_FreeCount = m_FreeCount - 1; + if(paddingBegin > 0) + { + ++m_FreeCount; + } + if(paddingEnd > 0) + { + ++m_FreeCount; + } + m_SumFreeSize -= allocSize; +} + +void VmaBlockMetadata_Generic::Free(const VmaAllocation allocation) +{ + for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin(); + suballocItem != m_Suballocations.end(); + ++suballocItem) + { + VmaSuballocation& suballoc = *suballocItem; + if(suballoc.hAllocation == allocation) + { + FreeSuballocation(suballocItem); + VMA_HEAVY_ASSERT(Validate()); + return; + } + } + VMA_ASSERT(0 && "Not found!"); +} + +void VmaBlockMetadata_Generic::FreeAtOffset(VkDeviceSize offset) +{ + for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin(); + suballocItem != m_Suballocations.end(); + ++suballocItem) + { + VmaSuballocation& suballoc = *suballocItem; + if(suballoc.offset == offset) + { + FreeSuballocation(suballocItem); + return; + } + } + VMA_ASSERT(0 && "Not found!"); +} + +bool VmaBlockMetadata_Generic::ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize) +{ + typedef VmaSuballocationList::iterator iter_type; + for(iter_type suballocItem = m_Suballocations.begin(); + suballocItem != m_Suballocations.end(); + ++suballocItem) + { + VmaSuballocation& suballoc = *suballocItem; + if(suballoc.hAllocation == alloc) + { + iter_type nextItem = suballocItem; + ++nextItem; + + // Should have been ensured on higher level. + VMA_ASSERT(newSize != alloc->GetSize() && newSize > 0); + + // Shrinking. + if(newSize < alloc->GetSize()) + { + const VkDeviceSize sizeDiff = suballoc.size - newSize; + + // There is next item. + if(nextItem != m_Suballocations.end()) + { + // Next item is free. + if(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE) + { + // Grow this next item backward. + UnregisterFreeSuballocation(nextItem); + nextItem->offset -= sizeDiff; + nextItem->size += sizeDiff; + RegisterFreeSuballocation(nextItem); + } + // Next item is not free. + else + { + // Create free item after current one. + VmaSuballocation newFreeSuballoc; + newFreeSuballoc.hAllocation = VK_NULL_HANDLE; + newFreeSuballoc.offset = suballoc.offset + newSize; + newFreeSuballoc.size = sizeDiff; + newFreeSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE; + iter_type newFreeSuballocIt = m_Suballocations.insert(nextItem, newFreeSuballoc); + RegisterFreeSuballocation(newFreeSuballocIt); + + ++m_FreeCount; + } + } + // This is the last item. + else + { + // Create free item at the end. + VmaSuballocation newFreeSuballoc; + newFreeSuballoc.hAllocation = VK_NULL_HANDLE; + newFreeSuballoc.offset = suballoc.offset + newSize; + newFreeSuballoc.size = sizeDiff; + newFreeSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE; + m_Suballocations.push_back(newFreeSuballoc); + + iter_type newFreeSuballocIt = m_Suballocations.end(); + RegisterFreeSuballocation(--newFreeSuballocIt); + + ++m_FreeCount; + } + + suballoc.size = newSize; + m_SumFreeSize += sizeDiff; + } + // Growing. + else + { + const VkDeviceSize sizeDiff = newSize - suballoc.size; + + // There is next item. + if(nextItem != m_Suballocations.end()) + { + // Next item is free. + if(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE) + { + // There is not enough free space, including margin. + if(nextItem->size < sizeDiff + VMA_DEBUG_MARGIN) + { + return false; + } + + // There is more free space than required. + if(nextItem->size > sizeDiff) + { + // Move and shrink this next item. + UnregisterFreeSuballocation(nextItem); + nextItem->offset += sizeDiff; + nextItem->size -= sizeDiff; + RegisterFreeSuballocation(nextItem); + } + // There is exactly the amount of free space required. + else + { + // Remove this next free item. + UnregisterFreeSuballocation(nextItem); + m_Suballocations.erase(nextItem); + --m_FreeCount; + } + } + // Next item is not free - there is no space to grow. + else + { + return false; + } + } + // This is the last item - there is no space to grow. + else + { + return false; + } + + suballoc.size = newSize; + m_SumFreeSize -= sizeDiff; + } + + // We cannot call Validate() here because alloc object is updated to new size outside of this call. + return true; + } + } + VMA_ASSERT(0 && "Not found!"); + return false; +} + +bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const +{ + VkDeviceSize lastSize = 0; + for(size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i) + { + const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i]; + + VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE); + VMA_VALIDATE(it->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER); + VMA_VALIDATE(it->size >= lastSize); + lastSize = it->size; + } + return true; +} + +bool VmaBlockMetadata_Generic::CheckAllocation( + uint32_t currentFrameIndex, + uint32_t frameInUseCount, + VkDeviceSize bufferImageGranularity, + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + VmaSuballocationType allocType, + VmaSuballocationList::const_iterator suballocItem, + bool canMakeOtherLost, + VkDeviceSize* pOffset, + size_t* itemsToMakeLostCount, + VkDeviceSize* pSumFreeSize, + VkDeviceSize* pSumItemSize) const +{ + VMA_ASSERT(allocSize > 0); + VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE); + VMA_ASSERT(suballocItem != m_Suballocations.cend()); + VMA_ASSERT(pOffset != VMA_NULL); + + *itemsToMakeLostCount = 0; + *pSumFreeSize = 0; + *pSumItemSize = 0; + + if(canMakeOtherLost) + { + if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE) + { + *pSumFreeSize = suballocItem->size; + } + else + { + if(suballocItem->hAllocation->CanBecomeLost() && + suballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex) + { + ++*itemsToMakeLostCount; + *pSumItemSize = suballocItem->size; + } + else + { + return false; + } + } + + // Remaining size is too small for this request: Early return. + if(GetSize() - suballocItem->offset < allocSize) + { + return false; + } + + // Start from offset equal to beginning of this suballocation. + *pOffset = suballocItem->offset; + + // Apply VMA_DEBUG_MARGIN at the beginning. + if(VMA_DEBUG_MARGIN > 0) + { + *pOffset += VMA_DEBUG_MARGIN; + } + + // Apply alignment. + *pOffset = VmaAlignUp(*pOffset, allocAlignment); + + // Check previous suballocations for BufferImageGranularity conflicts. + // Make bigger alignment if necessary. + if(bufferImageGranularity > 1) + { + bool bufferImageGranularityConflict = false; + VmaSuballocationList::const_iterator prevSuballocItem = suballocItem; + while(prevSuballocItem != m_Suballocations.cbegin()) + { + --prevSuballocItem; + const VmaSuballocation& prevSuballoc = *prevSuballocItem; + if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity)) + { + if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType)) + { + bufferImageGranularityConflict = true; + break; + } + } + else + // Already on previous page. + break; + } + if(bufferImageGranularityConflict) + { + *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity); + } + } + + // Now that we have final *pOffset, check if we are past suballocItem. + // If yes, return false - this function should be called for another suballocItem as starting point. + if(*pOffset >= suballocItem->offset + suballocItem->size) + { + return false; + } + + // Calculate padding at the beginning based on current offset. + const VkDeviceSize paddingBegin = *pOffset - suballocItem->offset; + + // Calculate required margin at the end. + const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN; + + const VkDeviceSize totalSize = paddingBegin + allocSize + requiredEndMargin; + // Another early return check. + if(suballocItem->offset + totalSize > GetSize()) + { + return false; + } + + // Advance lastSuballocItem until desired size is reached. + // Update itemsToMakeLostCount. + VmaSuballocationList::const_iterator lastSuballocItem = suballocItem; + if(totalSize > suballocItem->size) + { + VkDeviceSize remainingSize = totalSize - suballocItem->size; + while(remainingSize > 0) + { + ++lastSuballocItem; + if(lastSuballocItem == m_Suballocations.cend()) + { + return false; + } + if(lastSuballocItem->type == VMA_SUBALLOCATION_TYPE_FREE) + { + *pSumFreeSize += lastSuballocItem->size; + } + else + { + VMA_ASSERT(lastSuballocItem->hAllocation != VK_NULL_HANDLE); + if(lastSuballocItem->hAllocation->CanBecomeLost() && + lastSuballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex) + { + ++*itemsToMakeLostCount; + *pSumItemSize += lastSuballocItem->size; + } + else + { + return false; + } + } + remainingSize = (lastSuballocItem->size < remainingSize) ? + remainingSize - lastSuballocItem->size : 0; + } + } + + // Check next suballocations for BufferImageGranularity conflicts. + // If conflict exists, we must mark more allocations lost or fail. + if(bufferImageGranularity > 1) + { + VmaSuballocationList::const_iterator nextSuballocItem = lastSuballocItem; + ++nextSuballocItem; + while(nextSuballocItem != m_Suballocations.cend()) + { + const VmaSuballocation& nextSuballoc = *nextSuballocItem; + if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity)) + { + if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type)) + { + VMA_ASSERT(nextSuballoc.hAllocation != VK_NULL_HANDLE); + if(nextSuballoc.hAllocation->CanBecomeLost() && + nextSuballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex) + { + ++*itemsToMakeLostCount; + } + else + { + return false; + } + } + } + else + { + // Already on next page. + break; + } + ++nextSuballocItem; + } + } + } + else + { + const VmaSuballocation& suballoc = *suballocItem; + VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE); + + *pSumFreeSize = suballoc.size; + + // Size of this suballocation is too small for this request: Early return. + if(suballoc.size < allocSize) + { + return false; + } + + // Start from offset equal to beginning of this suballocation. + *pOffset = suballoc.offset; + + // Apply VMA_DEBUG_MARGIN at the beginning. + if(VMA_DEBUG_MARGIN > 0) + { + *pOffset += VMA_DEBUG_MARGIN; + } + + // Apply alignment. + *pOffset = VmaAlignUp(*pOffset, allocAlignment); + + // Check previous suballocations for BufferImageGranularity conflicts. + // Make bigger alignment if necessary. + if(bufferImageGranularity > 1) + { + bool bufferImageGranularityConflict = false; + VmaSuballocationList::const_iterator prevSuballocItem = suballocItem; + while(prevSuballocItem != m_Suballocations.cbegin()) + { + --prevSuballocItem; + const VmaSuballocation& prevSuballoc = *prevSuballocItem; + if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity)) + { + if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType)) + { + bufferImageGranularityConflict = true; + break; + } + } + else + // Already on previous page. + break; + } + if(bufferImageGranularityConflict) + { + *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity); + } + } + + // Calculate padding at the beginning based on current offset. + const VkDeviceSize paddingBegin = *pOffset - suballoc.offset; + + // Calculate required margin at the end. + const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN; + + // Fail if requested size plus margin before and after is bigger than size of this suballocation. + if(paddingBegin + allocSize + requiredEndMargin > suballoc.size) + { + return false; + } + + // Check next suballocations for BufferImageGranularity conflicts. + // If conflict exists, allocation cannot be made here. + if(bufferImageGranularity > 1) + { + VmaSuballocationList::const_iterator nextSuballocItem = suballocItem; + ++nextSuballocItem; + while(nextSuballocItem != m_Suballocations.cend()) + { + const VmaSuballocation& nextSuballoc = *nextSuballocItem; + if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity)) + { + if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type)) + { + return false; + } + } + else + { + // Already on next page. + break; + } + ++nextSuballocItem; + } + } + } + + // All tests passed: Success. pOffset is already filled. + return true; +} + +void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item) +{ + VMA_ASSERT(item != m_Suballocations.end()); + VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE); + + VmaSuballocationList::iterator nextItem = item; + ++nextItem; + VMA_ASSERT(nextItem != m_Suballocations.end()); + VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE); + + item->size += nextItem->size; + --m_FreeCount; + m_Suballocations.erase(nextItem); +} + +VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem) +{ + // Change this suballocation to be marked as free. + VmaSuballocation& suballoc = *suballocItem; + suballoc.type = VMA_SUBALLOCATION_TYPE_FREE; + suballoc.hAllocation = VK_NULL_HANDLE; + + // Update totals. + ++m_FreeCount; + m_SumFreeSize += suballoc.size; + + // Merge with previous and/or next suballocation if it's also free. + bool mergeWithNext = false; + bool mergeWithPrev = false; + + VmaSuballocationList::iterator nextItem = suballocItem; + ++nextItem; + if((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE)) + { + mergeWithNext = true; + } + + VmaSuballocationList::iterator prevItem = suballocItem; + if(suballocItem != m_Suballocations.begin()) + { + --prevItem; + if(prevItem->type == VMA_SUBALLOCATION_TYPE_FREE) + { + mergeWithPrev = true; + } + } + + if(mergeWithNext) + { + UnregisterFreeSuballocation(nextItem); + MergeFreeWithNext(suballocItem); + } + + if(mergeWithPrev) + { + UnregisterFreeSuballocation(prevItem); + MergeFreeWithNext(prevItem); + RegisterFreeSuballocation(prevItem); + return prevItem; + } + else + { + RegisterFreeSuballocation(suballocItem); + return suballocItem; + } +} + +void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item) +{ + VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE); + VMA_ASSERT(item->size > 0); + + // You may want to enable this validation at the beginning or at the end of + // this function, depending on what do you want to check. + VMA_HEAVY_ASSERT(ValidateFreeSuballocationList()); + + if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER) + { + if(m_FreeSuballocationsBySize.empty()) + { + m_FreeSuballocationsBySize.push_back(item); + } + else + { + VmaVectorInsertSorted(m_FreeSuballocationsBySize, item); + } + } + + //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList()); +} + + +void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item) +{ + VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE); + VMA_ASSERT(item->size > 0); + + // You may want to enable this validation at the beginning or at the end of + // this function, depending on what do you want to check. + VMA_HEAVY_ASSERT(ValidateFreeSuballocationList()); + + if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER) + { + VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess( + m_FreeSuballocationsBySize.data(), + m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(), + item, + VmaSuballocationItemSizeLess()); + for(size_t index = it - m_FreeSuballocationsBySize.data(); + index < m_FreeSuballocationsBySize.size(); + ++index) + { + if(m_FreeSuballocationsBySize[index] == item) + { + VmaVectorRemove(m_FreeSuballocationsBySize, index); + return; + } + VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found."); + } + VMA_ASSERT(0 && "Not found."); + } + + //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList()); +} + +bool VmaBlockMetadata_Generic::IsBufferImageGranularityConflictPossible( + VkDeviceSize bufferImageGranularity, + VmaSuballocationType& inOutPrevSuballocType) const +{ + if(bufferImageGranularity == 1 || IsEmpty()) + { + return false; + } + + VkDeviceSize minAlignment = VK_WHOLE_SIZE; + bool typeConflictFound = false; + for(VmaSuballocationList::const_iterator it = m_Suballocations.cbegin(); + it != m_Suballocations.cend(); + ++it) + { + const VmaSuballocationType suballocType = it->type; + if(suballocType != VMA_SUBALLOCATION_TYPE_FREE) + { + minAlignment = VMA_MIN(minAlignment, it->hAllocation->GetAlignment()); + if(VmaIsBufferImageGranularityConflict(inOutPrevSuballocType, suballocType)) + { + typeConflictFound = true; + } + inOutPrevSuballocType = suballocType; + } + } + + return typeConflictFound || minAlignment >= bufferImageGranularity; +} + +//////////////////////////////////////////////////////////////////////////////// +// class VmaBlockMetadata_Linear + +VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(VmaAllocator hAllocator) : + VmaBlockMetadata(hAllocator), + m_SumFreeSize(0), + m_Suballocations0(VmaStlAllocator(hAllocator->GetAllocationCallbacks())), + m_Suballocations1(VmaStlAllocator(hAllocator->GetAllocationCallbacks())), + m_1stVectorIndex(0), + m_2ndVectorMode(SECOND_VECTOR_EMPTY), + m_1stNullItemsBeginCount(0), + m_1stNullItemsMiddleCount(0), + m_2ndNullItemsCount(0) +{ +} + +VmaBlockMetadata_Linear::~VmaBlockMetadata_Linear() +{ +} + +void VmaBlockMetadata_Linear::Init(VkDeviceSize size) +{ + VmaBlockMetadata::Init(size); + m_SumFreeSize = size; +} + +bool VmaBlockMetadata_Linear::Validate() const +{ + const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY)); + VMA_VALIDATE(!suballocations1st.empty() || + suballocations2nd.empty() || + m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER); + + if(!suballocations1st.empty()) + { + // Null item at the beginning should be accounted into m_1stNullItemsBeginCount. + VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].hAllocation != VK_NULL_HANDLE); + // Null item at the end should be just pop_back(). + VMA_VALIDATE(suballocations1st.back().hAllocation != VK_NULL_HANDLE); + } + if(!suballocations2nd.empty()) + { + // Null item at the end should be just pop_back(). + VMA_VALIDATE(suballocations2nd.back().hAllocation != VK_NULL_HANDLE); + } + + VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size()); + VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size()); + + VkDeviceSize sumUsedSize = 0; + const size_t suballoc1stCount = suballocations1st.size(); + VkDeviceSize offset = VMA_DEBUG_MARGIN; + + if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + const size_t suballoc2ndCount = suballocations2nd.size(); + size_t nullItem2ndCount = 0; + for(size_t i = 0; i < suballoc2ndCount; ++i) + { + const VmaSuballocation& suballoc = suballocations2nd[i]; + const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE); + + VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE)); + VMA_VALIDATE(suballoc.offset >= offset); + + if(!currFree) + { + VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset); + VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size); + sumUsedSize += suballoc.size; + } + else + { + ++nullItem2ndCount; + } + + offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN; + } + + VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount); + } + + for(size_t i = 0; i < m_1stNullItemsBeginCount; ++i) + { + const VmaSuballocation& suballoc = suballocations1st[i]; + VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE && + suballoc.hAllocation == VK_NULL_HANDLE); + } + + size_t nullItem1stCount = m_1stNullItemsBeginCount; + + for(size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i) + { + const VmaSuballocation& suballoc = suballocations1st[i]; + const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE); + + VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE)); + VMA_VALIDATE(suballoc.offset >= offset); + VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree); + + if(!currFree) + { + VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset); + VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size); + sumUsedSize += suballoc.size; + } + else + { + ++nullItem1stCount; + } + + offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN; + } + VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount); + + if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + const size_t suballoc2ndCount = suballocations2nd.size(); + size_t nullItem2ndCount = 0; + for(size_t i = suballoc2ndCount; i--; ) + { + const VmaSuballocation& suballoc = suballocations2nd[i]; + const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE); + + VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE)); + VMA_VALIDATE(suballoc.offset >= offset); + + if(!currFree) + { + VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset); + VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size); + sumUsedSize += suballoc.size; + } + else + { + ++nullItem2ndCount; + } + + offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN; + } + + VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount); + } + + VMA_VALIDATE(offset <= GetSize()); + VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize); + + return true; +} + +size_t VmaBlockMetadata_Linear::GetAllocationCount() const +{ + return AccessSuballocations1st().size() - (m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount) + + AccessSuballocations2nd().size() - m_2ndNullItemsCount; +} + +VkDeviceSize VmaBlockMetadata_Linear::GetUnusedRangeSizeMax() const +{ + const VkDeviceSize size = GetSize(); + + /* + We don't consider gaps inside allocation vectors with freed allocations because + they are not suitable for reuse in linear allocator. We consider only space that + is available for new allocations. + */ + if(IsEmpty()) + { + return size; + } + + const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + + switch(m_2ndVectorMode) + { + case SECOND_VECTOR_EMPTY: + /* + Available space is after end of 1st, as well as before beginning of 1st (which + whould make it a ring buffer). + */ + { + const size_t suballocations1stCount = suballocations1st.size(); + VMA_ASSERT(suballocations1stCount > m_1stNullItemsBeginCount); + const VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount]; + const VmaSuballocation& lastSuballoc = suballocations1st[suballocations1stCount - 1]; + return VMA_MAX( + firstSuballoc.offset, + size - (lastSuballoc.offset + lastSuballoc.size)); + } + break; + + case SECOND_VECTOR_RING_BUFFER: + /* + Available space is only between end of 2nd and beginning of 1st. + */ + { + const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + const VmaSuballocation& lastSuballoc2nd = suballocations2nd.back(); + const VmaSuballocation& firstSuballoc1st = suballocations1st[m_1stNullItemsBeginCount]; + return firstSuballoc1st.offset - (lastSuballoc2nd.offset + lastSuballoc2nd.size); + } + break; + + case SECOND_VECTOR_DOUBLE_STACK: + /* + Available space is only between end of 1st and top of 2nd. + */ + { + const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + const VmaSuballocation& topSuballoc2nd = suballocations2nd.back(); + const VmaSuballocation& lastSuballoc1st = suballocations1st.back(); + return topSuballoc2nd.offset - (lastSuballoc1st.offset + lastSuballoc1st.size); + } + break; + + default: + VMA_ASSERT(0); + return 0; + } +} + +void VmaBlockMetadata_Linear::CalcAllocationStatInfo(VmaStatInfo& outInfo) const +{ + const VkDeviceSize size = GetSize(); + const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + const size_t suballoc1stCount = suballocations1st.size(); + const size_t suballoc2ndCount = suballocations2nd.size(); + + outInfo.blockCount = 1; + outInfo.allocationCount = (uint32_t)GetAllocationCount(); + outInfo.unusedRangeCount = 0; + outInfo.usedBytes = 0; + outInfo.allocationSizeMin = UINT64_MAX; + outInfo.allocationSizeMax = 0; + outInfo.unusedRangeSizeMin = UINT64_MAX; + outInfo.unusedRangeSizeMax = 0; + + VkDeviceSize lastOffset = 0; + + if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset; + size_t nextAlloc2ndIndex = 0; + while(lastOffset < freeSpace2ndTo1stEnd) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while(nextAlloc2ndIndex < suballoc2ndCount && + suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE) + { + ++nextAlloc2ndIndex; + } + + // Found non-null allocation. + if(nextAlloc2ndIndex < suballoc2ndCount) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if(lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + ++outInfo.unusedRangeCount; + outInfo.unusedBytes += unusedRangeSize; + outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize); + outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + outInfo.usedBytes += suballoc.size; + outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size); + outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc2ndIndex; + } + // We are at the end. + else + { + // There is free space from lastOffset to freeSpace2ndTo1stEnd. + if(lastOffset < freeSpace2ndTo1stEnd) + { + const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset; + ++outInfo.unusedRangeCount; + outInfo.unusedBytes += unusedRangeSize; + outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize); + outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize); + } + + // End of loop. + lastOffset = freeSpace2ndTo1stEnd; + } + } + } + + size_t nextAlloc1stIndex = m_1stNullItemsBeginCount; + const VkDeviceSize freeSpace1stTo2ndEnd = + m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size; + while(lastOffset < freeSpace1stTo2ndEnd) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while(nextAlloc1stIndex < suballoc1stCount && + suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE) + { + ++nextAlloc1stIndex; + } + + // Found non-null allocation. + if(nextAlloc1stIndex < suballoc1stCount) + { + const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex]; + + // 1. Process free space before this allocation. + if(lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + ++outInfo.unusedRangeCount; + outInfo.unusedBytes += unusedRangeSize; + outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize); + outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + outInfo.usedBytes += suballoc.size; + outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size); + outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc1stIndex; + } + // We are at the end. + else + { + // There is free space from lastOffset to freeSpace1stTo2ndEnd. + if(lastOffset < freeSpace1stTo2ndEnd) + { + const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset; + ++outInfo.unusedRangeCount; + outInfo.unusedBytes += unusedRangeSize; + outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize); + outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize); + } + + // End of loop. + lastOffset = freeSpace1stTo2ndEnd; + } + } + + if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + size_t nextAlloc2ndIndex = suballocations2nd.size() - 1; + while(lastOffset < size) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while(nextAlloc2ndIndex != SIZE_MAX && + suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE) + { + --nextAlloc2ndIndex; + } + + // Found non-null allocation. + if(nextAlloc2ndIndex != SIZE_MAX) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if(lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + ++outInfo.unusedRangeCount; + outInfo.unusedBytes += unusedRangeSize; + outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize); + outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + outInfo.usedBytes += suballoc.size; + outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size); + outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + --nextAlloc2ndIndex; + } + // We are at the end. + else + { + // There is free space from lastOffset to size. + if(lastOffset < size) + { + const VkDeviceSize unusedRangeSize = size - lastOffset; + ++outInfo.unusedRangeCount; + outInfo.unusedBytes += unusedRangeSize; + outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize); + outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize); + } + + // End of loop. + lastOffset = size; + } + } + } + + outInfo.unusedBytes = size - outInfo.usedBytes; +} + +void VmaBlockMetadata_Linear::AddPoolStats(VmaPoolStats& inoutStats) const +{ + const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + const VkDeviceSize size = GetSize(); + const size_t suballoc1stCount = suballocations1st.size(); + const size_t suballoc2ndCount = suballocations2nd.size(); + + inoutStats.size += size; + + VkDeviceSize lastOffset = 0; + + if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset; + size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount; + while(lastOffset < freeSpace2ndTo1stEnd) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while(nextAlloc2ndIndex < suballoc2ndCount && + suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE) + { + ++nextAlloc2ndIndex; + } + + // Found non-null allocation. + if(nextAlloc2ndIndex < suballoc2ndCount) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if(lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + inoutStats.unusedSize += unusedRangeSize; + ++inoutStats.unusedRangeCount; + inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + ++inoutStats.allocationCount; + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc2ndIndex; + } + // We are at the end. + else + { + if(lastOffset < freeSpace2ndTo1stEnd) + { + // There is free space from lastOffset to freeSpace2ndTo1stEnd. + const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset; + inoutStats.unusedSize += unusedRangeSize; + ++inoutStats.unusedRangeCount; + inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize); + } + + // End of loop. + lastOffset = freeSpace2ndTo1stEnd; + } + } + } + + size_t nextAlloc1stIndex = m_1stNullItemsBeginCount; + const VkDeviceSize freeSpace1stTo2ndEnd = + m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size; + while(lastOffset < freeSpace1stTo2ndEnd) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while(nextAlloc1stIndex < suballoc1stCount && + suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE) + { + ++nextAlloc1stIndex; + } + + // Found non-null allocation. + if(nextAlloc1stIndex < suballoc1stCount) + { + const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex]; + + // 1. Process free space before this allocation. + if(lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + inoutStats.unusedSize += unusedRangeSize; + ++inoutStats.unusedRangeCount; + inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + ++inoutStats.allocationCount; + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc1stIndex; + } + // We are at the end. + else + { + if(lastOffset < freeSpace1stTo2ndEnd) + { + // There is free space from lastOffset to freeSpace1stTo2ndEnd. + const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset; + inoutStats.unusedSize += unusedRangeSize; + ++inoutStats.unusedRangeCount; + inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize); + } + + // End of loop. + lastOffset = freeSpace1stTo2ndEnd; + } + } + + if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + size_t nextAlloc2ndIndex = suballocations2nd.size() - 1; + while(lastOffset < size) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while(nextAlloc2ndIndex != SIZE_MAX && + suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE) + { + --nextAlloc2ndIndex; + } + + // Found non-null allocation. + if(nextAlloc2ndIndex != SIZE_MAX) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if(lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + inoutStats.unusedSize += unusedRangeSize; + ++inoutStats.unusedRangeCount; + inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + ++inoutStats.allocationCount; + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + --nextAlloc2ndIndex; + } + // We are at the end. + else + { + if(lastOffset < size) + { + // There is free space from lastOffset to size. + const VkDeviceSize unusedRangeSize = size - lastOffset; + inoutStats.unusedSize += unusedRangeSize; + ++inoutStats.unusedRangeCount; + inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize); + } + + // End of loop. + lastOffset = size; + } + } + } +} + +#if VMA_STATS_STRING_ENABLED +void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const +{ + const VkDeviceSize size = GetSize(); + const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + const size_t suballoc1stCount = suballocations1st.size(); + const size_t suballoc2ndCount = suballocations2nd.size(); + + // FIRST PASS + + size_t unusedRangeCount = 0; + VkDeviceSize usedBytes = 0; + + VkDeviceSize lastOffset = 0; + + size_t alloc2ndCount = 0; + if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset; + size_t nextAlloc2ndIndex = 0; + while(lastOffset < freeSpace2ndTo1stEnd) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while(nextAlloc2ndIndex < suballoc2ndCount && + suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE) + { + ++nextAlloc2ndIndex; + } + + // Found non-null allocation. + if(nextAlloc2ndIndex < suballoc2ndCount) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if(lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + ++unusedRangeCount; + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + ++alloc2ndCount; + usedBytes += suballoc.size; + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc2ndIndex; + } + // We are at the end. + else + { + if(lastOffset < freeSpace2ndTo1stEnd) + { + // There is free space from lastOffset to freeSpace2ndTo1stEnd. + ++unusedRangeCount; + } + + // End of loop. + lastOffset = freeSpace2ndTo1stEnd; + } + } + } + + size_t nextAlloc1stIndex = m_1stNullItemsBeginCount; + size_t alloc1stCount = 0; + const VkDeviceSize freeSpace1stTo2ndEnd = + m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size; + while(lastOffset < freeSpace1stTo2ndEnd) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while(nextAlloc1stIndex < suballoc1stCount && + suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE) + { + ++nextAlloc1stIndex; + } + + // Found non-null allocation. + if(nextAlloc1stIndex < suballoc1stCount) + { + const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex]; + + // 1. Process free space before this allocation. + if(lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + ++unusedRangeCount; + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + ++alloc1stCount; + usedBytes += suballoc.size; + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc1stIndex; + } + // We are at the end. + else + { + if(lastOffset < size) + { + // There is free space from lastOffset to freeSpace1stTo2ndEnd. + ++unusedRangeCount; + } + + // End of loop. + lastOffset = freeSpace1stTo2ndEnd; + } + } + + if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + size_t nextAlloc2ndIndex = suballocations2nd.size() - 1; + while(lastOffset < size) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while(nextAlloc2ndIndex != SIZE_MAX && + suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE) + { + --nextAlloc2ndIndex; + } + + // Found non-null allocation. + if(nextAlloc2ndIndex != SIZE_MAX) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if(lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + ++unusedRangeCount; + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + ++alloc2ndCount; + usedBytes += suballoc.size; + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + --nextAlloc2ndIndex; + } + // We are at the end. + else + { + if(lastOffset < size) + { + // There is free space from lastOffset to size. + ++unusedRangeCount; + } + + // End of loop. + lastOffset = size; + } + } + } + + const VkDeviceSize unusedBytes = size - usedBytes; + PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount); + + // SECOND PASS + lastOffset = 0; + + if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset; + size_t nextAlloc2ndIndex = 0; + while(lastOffset < freeSpace2ndTo1stEnd) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while(nextAlloc2ndIndex < suballoc2ndCount && + suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE) + { + ++nextAlloc2ndIndex; + } + + // Found non-null allocation. + if(nextAlloc2ndIndex < suballoc2ndCount) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if(lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc2ndIndex; + } + // We are at the end. + else + { + if(lastOffset < freeSpace2ndTo1stEnd) + { + // There is free space from lastOffset to freeSpace2ndTo1stEnd. + const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // End of loop. + lastOffset = freeSpace2ndTo1stEnd; + } + } + } + + nextAlloc1stIndex = m_1stNullItemsBeginCount; + while(lastOffset < freeSpace1stTo2ndEnd) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while(nextAlloc1stIndex < suballoc1stCount && + suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE) + { + ++nextAlloc1stIndex; + } + + // Found non-null allocation. + if(nextAlloc1stIndex < suballoc1stCount) + { + const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex]; + + // 1. Process free space before this allocation. + if(lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc1stIndex; + } + // We are at the end. + else + { + if(lastOffset < freeSpace1stTo2ndEnd) + { + // There is free space from lastOffset to freeSpace1stTo2ndEnd. + const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // End of loop. + lastOffset = freeSpace1stTo2ndEnd; + } + } + + if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + size_t nextAlloc2ndIndex = suballocations2nd.size() - 1; + while(lastOffset < size) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while(nextAlloc2ndIndex != SIZE_MAX && + suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE) + { + --nextAlloc2ndIndex; + } + + // Found non-null allocation. + if(nextAlloc2ndIndex != SIZE_MAX) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if(lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + --nextAlloc2ndIndex; + } + // We are at the end. + else + { + if(lastOffset < size) + { + // There is free space from lastOffset to size. + const VkDeviceSize unusedRangeSize = size - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // End of loop. + lastOffset = size; + } + } + } + + PrintDetailedMap_End(json); +} +#endif // #if VMA_STATS_STRING_ENABLED + +bool VmaBlockMetadata_Linear::CreateAllocationRequest( + uint32_t currentFrameIndex, + uint32_t frameInUseCount, + VkDeviceSize bufferImageGranularity, + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + bool canMakeOtherLost, + uint32_t /*strategy*/, + VmaAllocationRequest* pAllocationRequest) +{ + VMA_ASSERT(allocSize > 0); + VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE); + VMA_ASSERT(pAllocationRequest != VMA_NULL); + VMA_HEAVY_ASSERT(Validate()); + + const VkDeviceSize size = GetSize(); + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + if(upperAddress) + { + if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer."); + return false; + } + + // Try to allocate before 2nd.back(), or end of block if 2nd.empty(). + if(allocSize > size) + { + return false; + } + VkDeviceSize resultBaseOffset = size - allocSize; + if(!suballocations2nd.empty()) + { + const VmaSuballocation& lastSuballoc = suballocations2nd.back(); + resultBaseOffset = lastSuballoc.offset - allocSize; + if(allocSize > lastSuballoc.offset) + { + return false; + } + } + + // Start from offset equal to end of free space. + VkDeviceSize resultOffset = resultBaseOffset; + + // Apply VMA_DEBUG_MARGIN at the end. + if(VMA_DEBUG_MARGIN > 0) + { +#if VMA_DEBUG_MARGIN + if(resultOffset < VMA_DEBUG_MARGIN) + { + return false; + } +#endif + resultOffset -= VMA_DEBUG_MARGIN; + } + + // Apply alignment. + resultOffset = VmaAlignDown(resultOffset, allocAlignment); + + // Check next suballocations from 2nd for BufferImageGranularity conflicts. + // Make bigger alignment if necessary. + if(bufferImageGranularity > 1 && !suballocations2nd.empty()) + { + bool bufferImageGranularityConflict = false; + for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; ) + { + const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex]; + if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity)) + { + if(VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType)) + { + bufferImageGranularityConflict = true; + break; + } + } + else + // Already on previous page. + break; + } + if(bufferImageGranularityConflict) + { + resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity); + } + } + + // There is enough free space. + const VkDeviceSize endOf1st = !suballocations1st.empty() ? + suballocations1st.back().offset + suballocations1st.back().size : + 0; + if(endOf1st + VMA_DEBUG_MARGIN <= resultOffset) + { + // Check previous suballocations for BufferImageGranularity conflicts. + // If conflict exists, allocation cannot be made here. + if(bufferImageGranularity > 1) + { + for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; ) + { + const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex]; + if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity)) + { + if(VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type)) + { + return false; + } + } + else + { + // Already on next page. + break; + } + } + } + + // All tests passed: Success. + pAllocationRequest->offset = resultOffset; + pAllocationRequest->sumFreeSize = resultBaseOffset + allocSize - endOf1st; + pAllocationRequest->sumItemSize = 0; + // pAllocationRequest->item unused. + pAllocationRequest->itemsToMakeLostCount = 0; + return true; + } + } + else // !upperAddress + { + if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + // Try to allocate at the end of 1st vector. + + VkDeviceSize resultBaseOffset = 0; + if(!suballocations1st.empty()) + { + const VmaSuballocation& lastSuballoc = suballocations1st.back(); + resultBaseOffset = lastSuballoc.offset + lastSuballoc.size; + } + + // Start from offset equal to beginning of free space. + VkDeviceSize resultOffset = resultBaseOffset; + + // Apply VMA_DEBUG_MARGIN at the beginning. + if(VMA_DEBUG_MARGIN > 0) + { + resultOffset += VMA_DEBUG_MARGIN; + } + + // Apply alignment. + resultOffset = VmaAlignUp(resultOffset, allocAlignment); + + // Check previous suballocations for BufferImageGranularity conflicts. + // Make bigger alignment if necessary. + if(bufferImageGranularity > 1 && !suballocations1st.empty()) + { + bool bufferImageGranularityConflict = false; + for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; ) + { + const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex]; + if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity)) + { + if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType)) + { + bufferImageGranularityConflict = true; + break; + } + } + else + // Already on previous page. + break; + } + if(bufferImageGranularityConflict) + { + resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity); + } + } + + const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? + suballocations2nd.back().offset : size; + + // There is enough free space at the end after alignment. + if(resultOffset + allocSize + VMA_DEBUG_MARGIN <= freeSpaceEnd) + { + // Check next suballocations for BufferImageGranularity conflicts. + // If conflict exists, allocation cannot be made here. + if(bufferImageGranularity > 1 && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; ) + { + const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex]; + if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity)) + { + if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type)) + { + return false; + } + } + else + { + // Already on previous page. + break; + } + } + } + + // All tests passed: Success. + pAllocationRequest->offset = resultOffset; + pAllocationRequest->sumFreeSize = freeSpaceEnd - resultBaseOffset; + pAllocationRequest->sumItemSize = 0; + // pAllocationRequest->item unused. + pAllocationRequest->itemsToMakeLostCount = 0; + return true; + } + } + + // Wrap-around to end of 2nd vector. Try to allocate there, watching for the + // beginning of 1st vector as the end of free space. + if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + VMA_ASSERT(!suballocations1st.empty()); + + VkDeviceSize resultBaseOffset = 0; + if(!suballocations2nd.empty()) + { + const VmaSuballocation& lastSuballoc = suballocations2nd.back(); + resultBaseOffset = lastSuballoc.offset + lastSuballoc.size; + } + + // Start from offset equal to beginning of free space. + VkDeviceSize resultOffset = resultBaseOffset; + + // Apply VMA_DEBUG_MARGIN at the beginning. + if(VMA_DEBUG_MARGIN > 0) + { + resultOffset += VMA_DEBUG_MARGIN; + } + + // Apply alignment. + resultOffset = VmaAlignUp(resultOffset, allocAlignment); + + // Check previous suballocations for BufferImageGranularity conflicts. + // Make bigger alignment if necessary. + if(bufferImageGranularity > 1 && !suballocations2nd.empty()) + { + bool bufferImageGranularityConflict = false; + for(size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; ) + { + const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex]; + if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity)) + { + if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType)) + { + bufferImageGranularityConflict = true; + break; + } + } + else + // Already on previous page. + break; + } + if(bufferImageGranularityConflict) + { + resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity); + } + } + + pAllocationRequest->itemsToMakeLostCount = 0; + pAllocationRequest->sumItemSize = 0; + size_t index1st = m_1stNullItemsBeginCount; + + if(canMakeOtherLost) + { + while(index1st < suballocations1st.size() && + resultOffset + allocSize + VMA_DEBUG_MARGIN > suballocations1st[index1st].offset) + { + // Next colliding allocation at the beginning of 1st vector found. Try to make it lost. + const VmaSuballocation& suballoc = suballocations1st[index1st]; + if(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE) + { + // No problem. + } + else + { + VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE); + if(suballoc.hAllocation->CanBecomeLost() && + suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex) + { + ++pAllocationRequest->itemsToMakeLostCount; + pAllocationRequest->sumItemSize += suballoc.size; + } + else + { + return false; + } + } + ++index1st; + } + + // Check next suballocations for BufferImageGranularity conflicts. + // If conflict exists, we must mark more allocations lost or fail. + if(bufferImageGranularity > 1) + { + while(index1st < suballocations1st.size()) + { + const VmaSuballocation& suballoc = suballocations1st[index1st]; + if(VmaBlocksOnSamePage(resultOffset, allocSize, suballoc.offset, bufferImageGranularity)) + { + if(suballoc.hAllocation != VK_NULL_HANDLE) + { + // Not checking actual VmaIsBufferImageGranularityConflict(allocType, suballoc.type). + if(suballoc.hAllocation->CanBecomeLost() && + suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex) + { + ++pAllocationRequest->itemsToMakeLostCount; + pAllocationRequest->sumItemSize += suballoc.size; + } + else + { + return false; + } + } + } + else + { + // Already on next page. + break; + } + ++index1st; + } + } + } + + // There is enough free space at the end after alignment. + if((index1st == suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN < size) || + (index1st < suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= suballocations1st[index1st].offset)) + { + // Check next suballocations for BufferImageGranularity conflicts. + // If conflict exists, allocation cannot be made here. + if(bufferImageGranularity > 1) + { + for(size_t nextSuballocIndex = index1st; + nextSuballocIndex < suballocations1st.size(); + nextSuballocIndex++) + { + const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex]; + if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity)) + { + if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type)) + { + return false; + } + } + else + { + // Already on next page. + break; + } + } + } + + // All tests passed: Success. + pAllocationRequest->offset = resultOffset; + pAllocationRequest->sumFreeSize = + (index1st < suballocations1st.size() ? suballocations1st[index1st].offset : size) + - resultBaseOffset + - pAllocationRequest->sumItemSize; + // pAllocationRequest->item unused. + return true; + } + } + } + + return false; +} + +bool VmaBlockMetadata_Linear::MakeRequestedAllocationsLost( + uint32_t currentFrameIndex, + uint32_t frameInUseCount, + VmaAllocationRequest* pAllocationRequest) +{ + if(pAllocationRequest->itemsToMakeLostCount == 0) + { + return true; + } + + VMA_ASSERT(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER); + + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + size_t index1st = m_1stNullItemsBeginCount; + size_t madeLostCount = 0; + while(madeLostCount < pAllocationRequest->itemsToMakeLostCount) + { + VMA_ASSERT(index1st < suballocations1st.size()); + VmaSuballocation& suballoc = suballocations1st[index1st]; + if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE) + { + VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE); + VMA_ASSERT(suballoc.hAllocation->CanBecomeLost()); + if(suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount)) + { + suballoc.type = VMA_SUBALLOCATION_TYPE_FREE; + suballoc.hAllocation = VK_NULL_HANDLE; + m_SumFreeSize += suballoc.size; + ++m_1stNullItemsMiddleCount; + ++madeLostCount; + } + else + { + return false; + } + } + ++index1st; + } + + CleanupAfterFree(); + //VMA_HEAVY_ASSERT(Validate()); // Already called by ClanupAfterFree(). + + return true; +} + +uint32_t VmaBlockMetadata_Linear::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) +{ + uint32_t lostAllocationCount = 0; + + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i) + { + VmaSuballocation& suballoc = suballocations1st[i]; + if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE && + suballoc.hAllocation->CanBecomeLost() && + suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount)) + { + suballoc.type = VMA_SUBALLOCATION_TYPE_FREE; + suballoc.hAllocation = VK_NULL_HANDLE; + ++m_1stNullItemsMiddleCount; + m_SumFreeSize += suballoc.size; + ++lostAllocationCount; + } + } + + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i) + { + VmaSuballocation& suballoc = suballocations2nd[i]; + if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE && + suballoc.hAllocation->CanBecomeLost() && + suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount)) + { + suballoc.type = VMA_SUBALLOCATION_TYPE_FREE; + suballoc.hAllocation = VK_NULL_HANDLE; + ++m_2ndNullItemsCount; + ++lostAllocationCount; + } + } + + if(lostAllocationCount) + { + CleanupAfterFree(); + } + + return lostAllocationCount; +} + +VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData) +{ + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i) + { + const VmaSuballocation& suballoc = suballocations1st[i]; + if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE) + { + if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN)) + { + VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!"); + return VK_ERROR_VALIDATION_FAILED_EXT; + } + if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size)) + { + VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!"); + return VK_ERROR_VALIDATION_FAILED_EXT; + } + } + } + + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i) + { + const VmaSuballocation& suballoc = suballocations2nd[i]; + if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE) + { + if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN)) + { + VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!"); + return VK_ERROR_VALIDATION_FAILED_EXT; + } + if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size)) + { + VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!"); + return VK_ERROR_VALIDATION_FAILED_EXT; + } + } + } + + return VK_SUCCESS; +} + +void VmaBlockMetadata_Linear::Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + VkDeviceSize allocSize, + bool upperAddress, + VmaAllocation hAllocation) +{ + const VmaSuballocation newSuballoc = { request.offset, allocSize, hAllocation, type }; + + if(upperAddress) + { + VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER && + "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer."); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + suballocations2nd.push_back(newSuballoc); + m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK; + } + else + { + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + + // First allocation. + if(suballocations1st.empty()) + { + suballocations1st.push_back(newSuballoc); + } + else + { + // New allocation at the end of 1st vector. + if(request.offset >= suballocations1st.back().offset + suballocations1st.back().size) + { + // Check if it fits before the end of the block. + VMA_ASSERT(request.offset + allocSize <= GetSize()); + suballocations1st.push_back(newSuballoc); + } + // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector. + else if(request.offset + allocSize <= suballocations1st[m_1stNullItemsBeginCount].offset) + { + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + switch(m_2ndVectorMode) + { + case SECOND_VECTOR_EMPTY: + // First allocation from second part ring buffer. + VMA_ASSERT(suballocations2nd.empty()); + m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER; + break; + case SECOND_VECTOR_RING_BUFFER: + // 2-part ring buffer is already started. + VMA_ASSERT(!suballocations2nd.empty()); + break; + case SECOND_VECTOR_DOUBLE_STACK: + VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack."); + break; + default: + VMA_ASSERT(0); + } + + suballocations2nd.push_back(newSuballoc); + } + else + { + VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR."); + } + } + } + + m_SumFreeSize -= newSuballoc.size; +} + +void VmaBlockMetadata_Linear::Free(const VmaAllocation allocation) +{ + FreeAtOffset(allocation->GetOffset()); +} + +void VmaBlockMetadata_Linear::FreeAtOffset(VkDeviceSize offset) +{ + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + if(!suballocations1st.empty()) + { + // First allocation: Mark it as next empty at the beginning. + VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount]; + if(firstSuballoc.offset == offset) + { + firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE; + firstSuballoc.hAllocation = VK_NULL_HANDLE; + m_SumFreeSize += firstSuballoc.size; + ++m_1stNullItemsBeginCount; + CleanupAfterFree(); + return; + } + } + + // Last allocation in 2-part ring buffer or top of upper stack (same logic). + if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER || + m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + VmaSuballocation& lastSuballoc = suballocations2nd.back(); + if(lastSuballoc.offset == offset) + { + m_SumFreeSize += lastSuballoc.size; + suballocations2nd.pop_back(); + CleanupAfterFree(); + return; + } + } + // Last allocation in 1st vector. + else if(m_2ndVectorMode == SECOND_VECTOR_EMPTY) + { + VmaSuballocation& lastSuballoc = suballocations1st.back(); + if(lastSuballoc.offset == offset) + { + m_SumFreeSize += lastSuballoc.size; + suballocations1st.pop_back(); + CleanupAfterFree(); + return; + } + } + + // Item from the middle of 1st vector. + { + VmaSuballocation refSuballoc; + refSuballoc.offset = offset; + // Rest of members stays uninitialized intentionally for better performance. + SuballocationVectorType::iterator it = VmaVectorFindSorted( + suballocations1st.begin() + m_1stNullItemsBeginCount, + suballocations1st.end(), + refSuballoc); + if(it != suballocations1st.end()) + { + it->type = VMA_SUBALLOCATION_TYPE_FREE; + it->hAllocation = VK_NULL_HANDLE; + ++m_1stNullItemsMiddleCount; + m_SumFreeSize += it->size; + CleanupAfterFree(); + return; + } + } + + if(m_2ndVectorMode != SECOND_VECTOR_EMPTY) + { + // Item from the middle of 2nd vector. + VmaSuballocation refSuballoc; + refSuballoc.offset = offset; + // Rest of members stays uninitialized intentionally for better performance. + SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ? + VmaVectorFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc) : + VmaVectorFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc); + if(it != suballocations2nd.end()) + { + it->type = VMA_SUBALLOCATION_TYPE_FREE; + it->hAllocation = VK_NULL_HANDLE; + ++m_2ndNullItemsCount; + m_SumFreeSize += it->size; + CleanupAfterFree(); + return; + } + } + + VMA_ASSERT(0 && "Allocation to free not found in linear allocator!"); +} + +bool VmaBlockMetadata_Linear::ShouldCompact1st() const +{ + const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount; + const size_t suballocCount = AccessSuballocations1st().size(); + return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3; +} + +void VmaBlockMetadata_Linear::CleanupAfterFree() +{ + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + if(IsEmpty()) + { + suballocations1st.clear(); + suballocations2nd.clear(); + m_1stNullItemsBeginCount = 0; + m_1stNullItemsMiddleCount = 0; + m_2ndNullItemsCount = 0; + m_2ndVectorMode = SECOND_VECTOR_EMPTY; + } + else + { + const size_t suballoc1stCount = suballocations1st.size(); + const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount; + VMA_ASSERT(nullItem1stCount <= suballoc1stCount); + + // Find more null items at the beginning of 1st vector. + while(m_1stNullItemsBeginCount < suballoc1stCount && + suballocations1st[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE) + { + ++m_1stNullItemsBeginCount; + --m_1stNullItemsMiddleCount; + } + + // Find more null items at the end of 1st vector. + while(m_1stNullItemsMiddleCount > 0 && + suballocations1st.back().hAllocation == VK_NULL_HANDLE) + { + --m_1stNullItemsMiddleCount; + suballocations1st.pop_back(); + } + + // Find more null items at the end of 2nd vector. + while(m_2ndNullItemsCount > 0 && + suballocations2nd.back().hAllocation == VK_NULL_HANDLE) + { + --m_2ndNullItemsCount; + suballocations2nd.pop_back(); + } + + if(ShouldCompact1st()) + { + const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount; + size_t srcIndex = m_1stNullItemsBeginCount; + for(size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex) + { + while(suballocations1st[srcIndex].hAllocation == VK_NULL_HANDLE) + { + ++srcIndex; + } + if(dstIndex != srcIndex) + { + suballocations1st[dstIndex] = suballocations1st[srcIndex]; + } + ++srcIndex; + } + suballocations1st.resize(nonNullItemCount); + m_1stNullItemsBeginCount = 0; + m_1stNullItemsMiddleCount = 0; + } + + // 2nd vector became empty. + if(suballocations2nd.empty()) + { + m_2ndVectorMode = SECOND_VECTOR_EMPTY; + } + + // 1st vector became empty. + if(suballocations1st.size() - m_1stNullItemsBeginCount == 0) + { + suballocations1st.clear(); + m_1stNullItemsBeginCount = 0; + + if(!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + // Swap 1st with 2nd. Now 2nd is empty. + m_2ndVectorMode = SECOND_VECTOR_EMPTY; + m_1stNullItemsMiddleCount = m_2ndNullItemsCount; + while(m_1stNullItemsBeginCount < suballocations2nd.size() && + suballocations2nd[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE) + { + ++m_1stNullItemsBeginCount; + --m_1stNullItemsMiddleCount; + } + m_2ndNullItemsCount = 0; + m_1stVectorIndex ^= 1; + } + } + } + + VMA_HEAVY_ASSERT(Validate()); +} + + +//////////////////////////////////////////////////////////////////////////////// +// class VmaBlockMetadata_Buddy + +VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(VmaAllocator hAllocator) : + VmaBlockMetadata(hAllocator), + m_Root(VMA_NULL), + m_AllocationCount(0), + m_FreeCount(1), + m_SumFreeSize(0) +{ + memset(m_FreeList, 0, sizeof(m_FreeList)); +} + +VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy() +{ + DeleteNode(m_Root); +} + +void VmaBlockMetadata_Buddy::Init(VkDeviceSize size) +{ + VmaBlockMetadata::Init(size); + + m_UsableSize = VmaPrevPow2(size); + m_SumFreeSize = m_UsableSize; + + // Calculate m_LevelCount. + m_LevelCount = 1; + while(m_LevelCount < MAX_LEVELS && + LevelToNodeSize(m_LevelCount) >= MIN_NODE_SIZE) + { + ++m_LevelCount; + } + + Node* rootNode = vma_new(GetAllocationCallbacks(), Node)(); + rootNode->offset = 0; + rootNode->type = Node::TYPE_FREE; + rootNode->parent = VMA_NULL; + rootNode->buddy = VMA_NULL; + + m_Root = rootNode; + AddToFreeListFront(0, rootNode); +} + +bool VmaBlockMetadata_Buddy::Validate() const +{ + // Validate tree. + ValidationContext ctx; + if(!ValidateNode(ctx, VMA_NULL, m_Root, 0, LevelToNodeSize(0))) + { + VMA_VALIDATE(false && "ValidateNode failed."); + } + VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount); + VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize); + + // Validate free node lists. + for(uint32_t level = 0; level < m_LevelCount; ++level) + { + VMA_VALIDATE(m_FreeList[level].front == VMA_NULL || + m_FreeList[level].front->free.prev == VMA_NULL); + + for(Node* node = m_FreeList[level].front; + node != VMA_NULL; + node = node->free.next) + { + VMA_VALIDATE(node->type == Node::TYPE_FREE); + + if(node->free.next == VMA_NULL) + { + VMA_VALIDATE(m_FreeList[level].back == node); + } + else + { + VMA_VALIDATE(node->free.next->free.prev == node); + } + } + } + + // Validate that free lists ar higher levels are empty. + for(uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level) + { + VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL); + } + + return true; +} + +VkDeviceSize VmaBlockMetadata_Buddy::GetUnusedRangeSizeMax() const +{ + for(uint32_t level = 0; level < m_LevelCount; ++level) + { + if(m_FreeList[level].front != VMA_NULL) + { + return LevelToNodeSize(level); + } + } + return 0; +} + +void VmaBlockMetadata_Buddy::CalcAllocationStatInfo(VmaStatInfo& outInfo) const +{ + const VkDeviceSize unusableSize = GetUnusableSize(); + + outInfo.blockCount = 1; + + outInfo.allocationCount = outInfo.unusedRangeCount = 0; + outInfo.usedBytes = outInfo.unusedBytes = 0; + + outInfo.allocationSizeMax = outInfo.unusedRangeSizeMax = 0; + outInfo.allocationSizeMin = outInfo.unusedRangeSizeMin = UINT64_MAX; + outInfo.allocationSizeAvg = outInfo.unusedRangeSizeAvg = 0; // Unused. + + CalcAllocationStatInfoNode(outInfo, m_Root, LevelToNodeSize(0)); + + if(unusableSize > 0) + { + ++outInfo.unusedRangeCount; + outInfo.unusedBytes += unusableSize; + outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusableSize); + outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusableSize); + } +} + +void VmaBlockMetadata_Buddy::AddPoolStats(VmaPoolStats& inoutStats) const +{ + const VkDeviceSize unusableSize = GetUnusableSize(); + + inoutStats.size += GetSize(); + inoutStats.unusedSize += m_SumFreeSize + unusableSize; + inoutStats.allocationCount += m_AllocationCount; + inoutStats.unusedRangeCount += m_FreeCount; + inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax()); + + if(unusableSize > 0) + { + ++inoutStats.unusedRangeCount; + // Not updating inoutStats.unusedRangeSizeMax with unusableSize because this space is not available for allocations. + } +} + +#if VMA_STATS_STRING_ENABLED + +void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json) const +{ + // TODO optimize + VmaStatInfo stat; + CalcAllocationStatInfo(stat); + + PrintDetailedMap_Begin( + json, + stat.unusedBytes, + stat.allocationCount, + stat.unusedRangeCount); + + PrintDetailedMapNode(json, m_Root, LevelToNodeSize(0)); + + const VkDeviceSize unusableSize = GetUnusableSize(); + if(unusableSize > 0) + { + PrintDetailedMap_UnusedRange(json, + m_UsableSize, // offset + unusableSize); // size + } + + PrintDetailedMap_End(json); +} + +#endif // #if VMA_STATS_STRING_ENABLED + +bool VmaBlockMetadata_Buddy::CreateAllocationRequest( + uint32_t /*currentFrameIndex*/, + uint32_t /*frameInUseCount*/, + VkDeviceSize bufferImageGranularity, + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + bool /*canMakeOtherLost*/, + uint32_t /*strategy*/, + VmaAllocationRequest* pAllocationRequest) +{ + VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm."); + (void) upperAddress; + + // Simple way to respect bufferImageGranularity. May be optimized some day. + // Whenever it might be an OPTIMAL image... + if(allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN || + allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN || + allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL) + { + allocAlignment = VMA_MAX(allocAlignment, bufferImageGranularity); + allocSize = VMA_MAX(allocSize, bufferImageGranularity); + } + + if(allocSize > m_UsableSize) + { + return false; + } + + const uint32_t targetLevel = AllocSizeToLevel(allocSize); + for(uint32_t level = targetLevel + 1; level--; ) + { + for(Node* freeNode = m_FreeList[level].front; + freeNode != VMA_NULL; + freeNode = freeNode->free.next) + { + if(freeNode->offset % allocAlignment == 0) + { + pAllocationRequest->offset = freeNode->offset; + pAllocationRequest->sumFreeSize = LevelToNodeSize(level); + pAllocationRequest->sumItemSize = 0; + pAllocationRequest->itemsToMakeLostCount = 0; + pAllocationRequest->customData = (void*)(uintptr_t)level; + return true; + } + } + } + + return false; +} + +bool VmaBlockMetadata_Buddy::MakeRequestedAllocationsLost( + uint32_t /*currentFrameIndex*/, + uint32_t /*frameInUseCount*/, + VmaAllocationRequest* pAllocationRequest) +{ + /* + Lost allocations are not supported in buddy allocator at the moment. + Support might be added in the future. + */ + return pAllocationRequest->itemsToMakeLostCount == 0; +} + +uint32_t VmaBlockMetadata_Buddy::MakeAllocationsLost(uint32_t /*currentFrameIndex*/, uint32_t /*frameInUseCount*/) +{ + /* + Lost allocations are not supported in buddy allocator at the moment. + Support might be added in the future. + */ + return 0; +} + +void VmaBlockMetadata_Buddy::Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType /*type*/, + VkDeviceSize allocSize, + bool /*upperAddress*/, + VmaAllocation hAllocation) +{ + const uint32_t targetLevel = AllocSizeToLevel(allocSize); + uint32_t currLevel = (uint32_t)(uintptr_t)request.customData; + + Node* currNode = m_FreeList[currLevel].front; + VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE); + while(currNode->offset != request.offset) + { + currNode = currNode->free.next; + VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE); + } + + // Go down, splitting free nodes. + while(currLevel < targetLevel) + { + // currNode is already first free node at currLevel. + // Remove it from list of free nodes at this currLevel. + RemoveFromFreeList(currLevel, currNode); + + const uint32_t childrenLevel = currLevel + 1; + + // Create two free sub-nodes. + Node* leftChild = vma_new(GetAllocationCallbacks(), Node)(); + Node* rightChild = vma_new(GetAllocationCallbacks(), Node)(); + + leftChild->offset = currNode->offset; + leftChild->type = Node::TYPE_FREE; + leftChild->parent = currNode; + leftChild->buddy = rightChild; + + rightChild->offset = currNode->offset + LevelToNodeSize(childrenLevel); + rightChild->type = Node::TYPE_FREE; + rightChild->parent = currNode; + rightChild->buddy = leftChild; + + // Convert current currNode to split type. + currNode->type = Node::TYPE_SPLIT; + currNode->split.leftChild = leftChild; + + // Add child nodes to free list. Order is important! + AddToFreeListFront(childrenLevel, rightChild); + AddToFreeListFront(childrenLevel, leftChild); + + ++m_FreeCount; + //m_SumFreeSize -= LevelToNodeSize(currLevel) % 2; // Useful only when level node sizes can be non power of 2. + ++currLevel; + currNode = m_FreeList[currLevel].front; + + /* + We can be sure that currNode, as left child of node previously split, + also fullfills the alignment requirement. + */ + } + + // Remove from free list. + VMA_ASSERT(currLevel == targetLevel && + currNode != VMA_NULL && + currNode->type == Node::TYPE_FREE); + RemoveFromFreeList(currLevel, currNode); + + // Convert to allocation node. + currNode->type = Node::TYPE_ALLOCATION; + currNode->allocation.alloc = hAllocation; + + ++m_AllocationCount; + --m_FreeCount; + m_SumFreeSize -= allocSize; +} + +void VmaBlockMetadata_Buddy::DeleteNode(Node* node) +{ + if(node->type == Node::TYPE_SPLIT) + { + DeleteNode(node->split.leftChild->buddy); + DeleteNode(node->split.leftChild); + } + + vma_delete(GetAllocationCallbacks(), node); +} + +bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const +{ + VMA_VALIDATE(level < m_LevelCount); + VMA_VALIDATE(curr->parent == parent); + VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL)); + VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr); + switch(curr->type) + { + case Node::TYPE_FREE: + // curr->free.prev, next are validated separately. + ctx.calculatedSumFreeSize += levelNodeSize; + ++ctx.calculatedFreeCount; + break; + case Node::TYPE_ALLOCATION: + ++ctx.calculatedAllocationCount; + ctx.calculatedSumFreeSize += levelNodeSize - curr->allocation.alloc->GetSize(); + VMA_VALIDATE(curr->allocation.alloc != VK_NULL_HANDLE); + break; + case Node::TYPE_SPLIT: + { + const uint32_t childrenLevel = level + 1; + const VkDeviceSize childrenLevelNodeSize = levelNodeSize / 2; + const Node* const leftChild = curr->split.leftChild; + VMA_VALIDATE(leftChild != VMA_NULL); + VMA_VALIDATE(leftChild->offset == curr->offset); + if(!ValidateNode(ctx, curr, leftChild, childrenLevel, childrenLevelNodeSize)) + { + VMA_VALIDATE(false && "ValidateNode for left child failed."); + } + const Node* const rightChild = leftChild->buddy; + VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize); + if(!ValidateNode(ctx, curr, rightChild, childrenLevel, childrenLevelNodeSize)) + { + VMA_VALIDATE(false && "ValidateNode for right child failed."); + } + } + break; + default: + return false; + } + + return true; +} + +uint32_t VmaBlockMetadata_Buddy::AllocSizeToLevel(VkDeviceSize allocSize) const +{ + // I know this could be optimized somehow e.g. by using std::log2p1 from C++20. + uint32_t level = 0; + VkDeviceSize currLevelNodeSize = m_UsableSize; + VkDeviceSize nextLevelNodeSize = currLevelNodeSize >> 1; + while(allocSize <= nextLevelNodeSize && level + 1 < m_LevelCount) + { + ++level; + currLevelNodeSize = nextLevelNodeSize; + nextLevelNodeSize = currLevelNodeSize >> 1; + } + return level; +} + +void VmaBlockMetadata_Buddy::FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset) +{ + // Find node and level. + Node* node = m_Root; + VkDeviceSize nodeOffset = 0; + uint32_t level = 0; + VkDeviceSize levelNodeSize = LevelToNodeSize(0); + while(node->type == Node::TYPE_SPLIT) + { + const VkDeviceSize nextLevelSize = levelNodeSize >> 1; + if(offset < nodeOffset + nextLevelSize) + { + node = node->split.leftChild; + } + else + { + node = node->split.leftChild->buddy; + nodeOffset += nextLevelSize; + } + ++level; + levelNodeSize = nextLevelSize; + } + + VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION); + VMA_ASSERT(alloc == VK_NULL_HANDLE || node->allocation.alloc == alloc); + + ++m_FreeCount; + --m_AllocationCount; + m_SumFreeSize += alloc->GetSize(); + + node->type = Node::TYPE_FREE; + + // Join free nodes if possible. + while(level > 0 && node->buddy->type == Node::TYPE_FREE) + { + RemoveFromFreeList(level, node->buddy); + Node* const parent = node->parent; + + vma_delete(GetAllocationCallbacks(), node->buddy); + vma_delete(GetAllocationCallbacks(), node); + parent->type = Node::TYPE_FREE; + + node = parent; + --level; + //m_SumFreeSize += LevelToNodeSize(level) % 2; // Useful only when level node sizes can be non power of 2. + --m_FreeCount; + } + + AddToFreeListFront(level, node); +} + +void VmaBlockMetadata_Buddy::CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const +{ + switch(node->type) + { + case Node::TYPE_FREE: + ++outInfo.unusedRangeCount; + outInfo.unusedBytes += levelNodeSize; + outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, levelNodeSize); + outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, levelNodeSize); + break; + case Node::TYPE_ALLOCATION: + { + const VkDeviceSize allocSize = node->allocation.alloc->GetSize(); + ++outInfo.allocationCount; + outInfo.usedBytes += allocSize; + outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, allocSize); + outInfo.allocationSizeMin = VMA_MAX(outInfo.allocationSizeMin, allocSize); + + const VkDeviceSize unusedRangeSize = levelNodeSize - allocSize; + if(unusedRangeSize > 0) + { + ++outInfo.unusedRangeCount; + outInfo.unusedBytes += unusedRangeSize; + outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusedRangeSize); + outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, unusedRangeSize); + } + } + break; + case Node::TYPE_SPLIT: + { + const VkDeviceSize childrenNodeSize = levelNodeSize / 2; + const Node* const leftChild = node->split.leftChild; + CalcAllocationStatInfoNode(outInfo, leftChild, childrenNodeSize); + const Node* const rightChild = leftChild->buddy; + CalcAllocationStatInfoNode(outInfo, rightChild, childrenNodeSize); + } + break; + default: + VMA_ASSERT(0); + } +} + +void VmaBlockMetadata_Buddy::AddToFreeListFront(uint32_t level, Node* node) +{ + VMA_ASSERT(node->type == Node::TYPE_FREE); + + // List is empty. + Node* const frontNode = m_FreeList[level].front; + if(frontNode == VMA_NULL) + { + VMA_ASSERT(m_FreeList[level].back == VMA_NULL); + node->free.prev = node->free.next = VMA_NULL; + m_FreeList[level].front = m_FreeList[level].back = node; + } + else + { + VMA_ASSERT(frontNode->free.prev == VMA_NULL); + node->free.prev = VMA_NULL; + node->free.next = frontNode; + frontNode->free.prev = node; + m_FreeList[level].front = node; + } +} + +void VmaBlockMetadata_Buddy::RemoveFromFreeList(uint32_t level, Node* node) +{ + VMA_ASSERT(m_FreeList[level].front != VMA_NULL); + + // It is at the front. + if(node->free.prev == VMA_NULL) + { + VMA_ASSERT(m_FreeList[level].front == node); + m_FreeList[level].front = node->free.next; + } + else + { + Node* const prevFreeNode = node->free.prev; + VMA_ASSERT(prevFreeNode->free.next == node); + prevFreeNode->free.next = node->free.next; + } + + // It is at the back. + if(node->free.next == VMA_NULL) + { + VMA_ASSERT(m_FreeList[level].back == node); + m_FreeList[level].back = node->free.prev; + } + else + { + Node* const nextFreeNode = node->free.next; + VMA_ASSERT(nextFreeNode->free.prev == node); + nextFreeNode->free.prev = node->free.prev; + } +} + +#if VMA_STATS_STRING_ENABLED +void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const +{ + switch(node->type) + { + case Node::TYPE_FREE: + PrintDetailedMap_UnusedRange(json, node->offset, levelNodeSize); + break; + case Node::TYPE_ALLOCATION: + { + PrintDetailedMap_Allocation(json, node->offset, node->allocation.alloc); + const VkDeviceSize allocSize = node->allocation.alloc->GetSize(); + if(allocSize < levelNodeSize) + { + PrintDetailedMap_UnusedRange(json, node->offset + allocSize, levelNodeSize - allocSize); + } + } + break; + case Node::TYPE_SPLIT: + { + const VkDeviceSize childrenNodeSize = levelNodeSize / 2; + const Node* const leftChild = node->split.leftChild; + PrintDetailedMapNode(json, leftChild, childrenNodeSize); + const Node* const rightChild = leftChild->buddy; + PrintDetailedMapNode(json, rightChild, childrenNodeSize); + } + break; + default: + VMA_ASSERT(0); + } +} +#endif // #if VMA_STATS_STRING_ENABLED + + +//////////////////////////////////////////////////////////////////////////////// +// class VmaDeviceMemoryBlock + +VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator /*hAllocator*/) : + m_pMetadata(VMA_NULL), + m_MemoryTypeIndex(UINT32_MAX), + m_Id(0), + m_hMemory(VK_NULL_HANDLE), + m_MapCount(0), + m_pMappedData(VMA_NULL) +{ +} + +void VmaDeviceMemoryBlock::Init( + VmaAllocator hAllocator, + uint32_t newMemoryTypeIndex, + VkDeviceMemory newMemory, + VkDeviceSize newSize, + uint32_t id, + uint32_t algorithm) +{ + VMA_ASSERT(m_hMemory == VK_NULL_HANDLE); + + m_MemoryTypeIndex = newMemoryTypeIndex; + m_Id = id; + m_hMemory = newMemory; + + switch(algorithm) + { + case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT: + m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator); + break; + case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT: + m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Buddy)(hAllocator); + break; + default: + VMA_ASSERT(0); + // Fall-through. + case 0: + m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Generic)(hAllocator); + } + m_pMetadata->Init(newSize); +} + +void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator) +{ + // This is the most important assert in the entire library. + // Hitting it means you have some memory leak - unreleased VmaAllocation objects. + VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!"); + + VMA_ASSERT(m_hMemory != VK_NULL_HANDLE); + allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory); + m_hMemory = VK_NULL_HANDLE; + + vma_delete(allocator, m_pMetadata); + m_pMetadata = VMA_NULL; +} + +bool VmaDeviceMemoryBlock::Validate() const +{ + VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) && + (m_pMetadata->GetSize() != 0)); + + return m_pMetadata->Validate(); +} + +VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator) +{ + void* pData = nullptr; + VkResult res = Map(hAllocator, 1, &pData); + if(res != VK_SUCCESS) + { + return res; + } + + res = m_pMetadata->CheckCorruption(pData); + + Unmap(hAllocator, 1); + + return res; +} + +VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData) +{ + if(count == 0) + { + return VK_SUCCESS; + } + + VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex); + if(m_MapCount != 0) + { + m_MapCount += count; + VMA_ASSERT(m_pMappedData != VMA_NULL); + if(ppData != VMA_NULL) + { + *ppData = m_pMappedData; + } + return VK_SUCCESS; + } + else + { + VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)( + hAllocator->m_hDevice, + m_hMemory, + 0, // offset + VK_WHOLE_SIZE, + 0, // flags + &m_pMappedData); + if(result == VK_SUCCESS) + { + if(ppData != VMA_NULL) + { + *ppData = m_pMappedData; + } + m_MapCount = count; + } + return result; + } +} + +void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count) +{ + if(count == 0) + { + return; + } + + VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex); + if(m_MapCount >= count) + { + m_MapCount -= count; + if(m_MapCount == 0) + { + m_pMappedData = VMA_NULL; + (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory); + } + } + else + { + VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped."); + } +} + +VkResult VmaDeviceMemoryBlock::WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize) +{ + VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION); + VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN); + + void* pData; + VkResult res = Map(hAllocator, 1, &pData); + if(res != VK_SUCCESS) + { + return res; + } + + VmaWriteMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN); + VmaWriteMagicValue(pData, allocOffset + allocSize); + + Unmap(hAllocator, 1); + + return VK_SUCCESS; +} + +VkResult VmaDeviceMemoryBlock::ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize) +{ + VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION); + VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN); + + void* pData; + VkResult res = Map(hAllocator, 1, &pData); + if(res != VK_SUCCESS) + { + return res; + } + + if(!VmaValidateMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN)) + { + VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE FREED ALLOCATION!"); + } + else if(!VmaValidateMagicValue(pData, allocOffset + allocSize)) + { + VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!"); + } + + Unmap(hAllocator, 1); + + return VK_SUCCESS; +} + +VkResult VmaDeviceMemoryBlock::BindBufferMemory( + const VmaAllocator hAllocator, + const VmaAllocation hAllocation, + VkBuffer hBuffer) +{ + VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK && + hAllocation->GetBlock() == this); + // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads. + VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex); + return hAllocator->GetVulkanFunctions().vkBindBufferMemory( + hAllocator->m_hDevice, + hBuffer, + m_hMemory, + hAllocation->GetOffset()); +} + +VkResult VmaDeviceMemoryBlock::BindImageMemory( + const VmaAllocator hAllocator, + const VmaAllocation hAllocation, + VkImage hImage) +{ + VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK && + hAllocation->GetBlock() == this); + // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads. + VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex); + return hAllocator->GetVulkanFunctions().vkBindImageMemory( + hAllocator->m_hDevice, + hImage, + m_hMemory, + hAllocation->GetOffset()); +} + +static void InitStatInfo(VmaStatInfo& outInfo) +{ + memset(&outInfo, 0, sizeof(outInfo)); + outInfo.allocationSizeMin = UINT64_MAX; + outInfo.unusedRangeSizeMin = UINT64_MAX; +} + +// Adds statistics srcInfo into inoutInfo, like: inoutInfo += srcInfo. +static void VmaAddStatInfo(VmaStatInfo& inoutInfo, const VmaStatInfo& srcInfo) +{ + inoutInfo.blockCount += srcInfo.blockCount; + inoutInfo.allocationCount += srcInfo.allocationCount; + inoutInfo.unusedRangeCount += srcInfo.unusedRangeCount; + inoutInfo.usedBytes += srcInfo.usedBytes; + inoutInfo.unusedBytes += srcInfo.unusedBytes; + inoutInfo.allocationSizeMin = VMA_MIN(inoutInfo.allocationSizeMin, srcInfo.allocationSizeMin); + inoutInfo.allocationSizeMax = VMA_MAX(inoutInfo.allocationSizeMax, srcInfo.allocationSizeMax); + inoutInfo.unusedRangeSizeMin = VMA_MIN(inoutInfo.unusedRangeSizeMin, srcInfo.unusedRangeSizeMin); + inoutInfo.unusedRangeSizeMax = VMA_MAX(inoutInfo.unusedRangeSizeMax, srcInfo.unusedRangeSizeMax); +} + +static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo) +{ + inoutInfo.allocationSizeAvg = (inoutInfo.allocationCount > 0) ? + VmaRoundDiv(inoutInfo.usedBytes, inoutInfo.allocationCount) : 0; + inoutInfo.unusedRangeSizeAvg = (inoutInfo.unusedRangeCount > 0) ? + VmaRoundDiv(inoutInfo.unusedBytes, inoutInfo.unusedRangeCount) : 0; +} + +VmaPool_T::VmaPool_T( + VmaAllocator hAllocator, + const VmaPoolCreateInfo& createInfo, + VkDeviceSize preferredBlockSize) : + m_BlockVector( + hAllocator, + createInfo.memoryTypeIndex, + createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize, + createInfo.minBlockCount, + createInfo.maxBlockCount, + (createInfo.flags & VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(), + createInfo.frameInUseCount, + true, // isCustomPool + createInfo.blockSize != 0, // explicitBlockSize + createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK), // algorithm + m_Id(0) +{ +} + +VmaPool_T::~VmaPool_T() +{ +} + +#if VMA_STATS_STRING_ENABLED + +#endif // #if VMA_STATS_STRING_ENABLED + +VmaBlockVector::VmaBlockVector( + VmaAllocator hAllocator, + uint32_t memoryTypeIndex, + VkDeviceSize preferredBlockSize, + size_t minBlockCount, + size_t maxBlockCount, + VkDeviceSize bufferImageGranularity, + uint32_t frameInUseCount, + bool isCustomPool, + bool explicitBlockSize, + uint32_t algorithm) : + m_hAllocator(hAllocator), + m_MemoryTypeIndex(memoryTypeIndex), + m_PreferredBlockSize(preferredBlockSize), + m_MinBlockCount(minBlockCount), + m_MaxBlockCount(maxBlockCount), + m_BufferImageGranularity(bufferImageGranularity), + m_FrameInUseCount(frameInUseCount), + m_IsCustomPool(isCustomPool), + m_ExplicitBlockSize(explicitBlockSize), + m_Algorithm(algorithm), + m_HasEmptyBlock(false), + m_Blocks(VmaStlAllocator(hAllocator->GetAllocationCallbacks())), + m_NextBlockId(0) +{ +} + +VmaBlockVector::~VmaBlockVector() +{ + for(size_t i = m_Blocks.size(); i--; ) + { + m_Blocks[i]->Destroy(m_hAllocator); + vma_delete(m_hAllocator, m_Blocks[i]); + } +} + +VkResult VmaBlockVector::CreateMinBlocks() +{ + for(size_t i = 0; i < m_MinBlockCount; ++i) + { + VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL); + if(res != VK_SUCCESS) + { + return res; + } + } + return VK_SUCCESS; +} + +void VmaBlockVector::GetPoolStats(VmaPoolStats* pStats) +{ + VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex); + + const size_t blockCount = m_Blocks.size(); + + pStats->size = 0; + pStats->unusedSize = 0; + pStats->allocationCount = 0; + pStats->unusedRangeCount = 0; + pStats->unusedRangeSizeMax = 0; + pStats->blockCount = blockCount; + + for(uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex) + { + const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex]; + VMA_ASSERT(pBlock); + VMA_HEAVY_ASSERT(pBlock->Validate()); + pBlock->m_pMetadata->AddPoolStats(*pStats); + } +} + +bool VmaBlockVector::IsCorruptionDetectionEnabled() const +{ + const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + return (VMA_DEBUG_DETECT_CORRUPTION != 0) && + (VMA_DEBUG_MARGIN > 0) && + (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags; +} + +static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32; + +VkResult VmaBlockVector::Allocate( + VmaPool hCurrentPool, + uint32_t currentFrameIndex, + VkDeviceSize size, + VkDeviceSize alignment, + const VmaAllocationCreateInfo& createInfo, + VmaSuballocationType suballocType, + size_t allocationCount, + VmaAllocation* pAllocations) +{ + size_t allocIndex; + VkResult res = VK_SUCCESS; + + { + VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex); + for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex) + { + res = AllocatePage( + hCurrentPool, + currentFrameIndex, + size, + alignment, + createInfo, + suballocType, + pAllocations + allocIndex); + if(res != VK_SUCCESS) + { + break; + } + } + } + + if(res != VK_SUCCESS) + { + // Free all already created allocations. + while(allocIndex--) + { + Free(pAllocations[allocIndex]); + } + memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount); + } + + return res; +} + +VkResult VmaBlockVector::AllocatePage( + VmaPool hCurrentPool, + uint32_t currentFrameIndex, + VkDeviceSize size, + VkDeviceSize alignment, + const VmaAllocationCreateInfo& createInfo, + VmaSuballocationType suballocType, + VmaAllocation* pAllocation) +{ + const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0; + bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0; + const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0; + const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0; + const bool canCreateNewBlock = + ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) && + (m_Blocks.size() < m_MaxBlockCount); + uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK; + + // If linearAlgorithm is used, canMakeOtherLost is available only when used as ring buffer. + // Which in turn is available only when maxBlockCount = 1. + if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT && m_MaxBlockCount > 1) + { + canMakeOtherLost = false; + } + + // Upper address can only be used with linear allocator and within single memory block. + if(isUpperAddress && + (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1)) + { + return VK_ERROR_FEATURE_NOT_PRESENT; + } + + // Validate strategy. + switch(strategy) + { + case 0: + strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT; + break; + case VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT: + case VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT: + case VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT: + break; + default: + return VK_ERROR_FEATURE_NOT_PRESENT; + } + + // Early reject: requested allocation size is larger that maximum block size for this block vector. + if(size + 2 * VMA_DEBUG_MARGIN > m_PreferredBlockSize) + { + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + } + + /* + Under certain condition, this whole section can be skipped for optimization, so + we move on directly to trying to allocate with canMakeOtherLost. That's the case + e.g. for custom pools with linear algorithm. + */ + if(!canMakeOtherLost || canCreateNewBlock) + { + // 1. Search existing allocations. Try to allocate without making other allocations lost. + VmaAllocationCreateFlags allocFlagsCopy = createInfo.flags; + allocFlagsCopy &= ~VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT; + + if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) + { + // Use only last block. + if(!m_Blocks.empty()) + { + VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back(); + VMA_ASSERT(pCurrBlock); + VkResult res = AllocateFromBlock( + pCurrBlock, + hCurrentPool, + currentFrameIndex, + size, + alignment, + allocFlagsCopy, + createInfo.pUserData, + suballocType, + strategy, + pAllocation); + if(res == VK_SUCCESS) + { + VMA_DEBUG_LOG(" Returned from last block #%u", (uint32_t)(m_Blocks.size() - 1)); + return VK_SUCCESS; + } + } + } + else + { + if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT) + { + // Forward order in m_Blocks - prefer blocks with smallest amount of free space. + for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex ) + { + VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex]; + VMA_ASSERT(pCurrBlock); + VkResult res = AllocateFromBlock( + pCurrBlock, + hCurrentPool, + currentFrameIndex, + size, + alignment, + allocFlagsCopy, + createInfo.pUserData, + suballocType, + strategy, + pAllocation); + if(res == VK_SUCCESS) + { + VMA_DEBUG_LOG(" Returned from existing block #%u", (uint32_t)blockIndex); + return VK_SUCCESS; + } + } + } + else // WORST_FIT, FIRST_FIT + { + // Backward order in m_Blocks - prefer blocks with largest amount of free space. + for(size_t blockIndex = m_Blocks.size(); blockIndex--; ) + { + VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex]; + VMA_ASSERT(pCurrBlock); + VkResult res = AllocateFromBlock( + pCurrBlock, + hCurrentPool, + currentFrameIndex, + size, + alignment, + allocFlagsCopy, + createInfo.pUserData, + suballocType, + strategy, + pAllocation); + if(res == VK_SUCCESS) + { + VMA_DEBUG_LOG(" Returned from existing block #%u", (uint32_t)blockIndex); + return VK_SUCCESS; + } + } + } + } + + // 2. Try to create new block. + if(canCreateNewBlock) + { + // Calculate optimal size for new block. + VkDeviceSize newBlockSize = m_PreferredBlockSize; + uint32_t newBlockSizeShift = 0; + const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3; + + if(!m_ExplicitBlockSize) + { + // Allocate 1/8, 1/4, 1/2 as first blocks. + const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize(); + for(uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i) + { + const VkDeviceSize smallerNewBlockSize = newBlockSize / 2; + if(smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2) + { + newBlockSize = smallerNewBlockSize; + ++newBlockSizeShift; + } + else + { + break; + } + } + } + + size_t newBlockIndex = 0; + VkResult res = CreateBlock(newBlockSize, &newBlockIndex); + // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize. + if(!m_ExplicitBlockSize) + { + while(res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX) + { + const VkDeviceSize smallerNewBlockSize = newBlockSize / 2; + if(smallerNewBlockSize >= size) + { + newBlockSize = smallerNewBlockSize; + ++newBlockSizeShift; + res = CreateBlock(newBlockSize, &newBlockIndex); + } + else + { + break; + } + } + } + + if(res == VK_SUCCESS) + { + VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex]; + VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size); + + res = AllocateFromBlock( + pBlock, + hCurrentPool, + currentFrameIndex, + size, + alignment, + allocFlagsCopy, + createInfo.pUserData, + suballocType, + strategy, + pAllocation); + if(res == VK_SUCCESS) + { + VMA_DEBUG_LOG(" Created new block Size=%llu", newBlockSize); + return VK_SUCCESS; + } + else + { + // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment. + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + } + } + } + } + + // 3. Try to allocate from existing blocks with making other allocations lost. + if(canMakeOtherLost) + { + uint32_t tryIndex = 0; + for(; tryIndex < VMA_ALLOCATION_TRY_COUNT; ++tryIndex) + { + VmaDeviceMemoryBlock* pBestRequestBlock = VMA_NULL; + VmaAllocationRequest bestRequest = {}; + VkDeviceSize bestRequestCost = VK_WHOLE_SIZE; + + // 1. Search existing allocations. + if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT) + { + // Forward order in m_Blocks - prefer blocks with smallest amount of free space. + for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex ) + { + VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex]; + VMA_ASSERT(pCurrBlock); + VmaAllocationRequest currRequest = {}; + if(pCurrBlock->m_pMetadata->CreateAllocationRequest( + currentFrameIndex, + m_FrameInUseCount, + m_BufferImageGranularity, + size, + alignment, + (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0, + suballocType, + canMakeOtherLost, + strategy, + &currRequest)) + { + const VkDeviceSize currRequestCost = currRequest.CalcCost(); + if(pBestRequestBlock == VMA_NULL || + currRequestCost < bestRequestCost) + { + pBestRequestBlock = pCurrBlock; + bestRequest = currRequest; + bestRequestCost = currRequestCost; + + if(bestRequestCost == 0) + { + break; + } + } + } + } + } + else // WORST_FIT, FIRST_FIT + { + // Backward order in m_Blocks - prefer blocks with largest amount of free space. + for(size_t blockIndex = m_Blocks.size(); blockIndex--; ) + { + VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex]; + VMA_ASSERT(pCurrBlock); + VmaAllocationRequest currRequest = {}; + if(pCurrBlock->m_pMetadata->CreateAllocationRequest( + currentFrameIndex, + m_FrameInUseCount, + m_BufferImageGranularity, + size, + alignment, + (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0, + suballocType, + canMakeOtherLost, + strategy, + &currRequest)) + { + const VkDeviceSize currRequestCost = currRequest.CalcCost(); + if(pBestRequestBlock == VMA_NULL || + currRequestCost < bestRequestCost || + strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT) + { + pBestRequestBlock = pCurrBlock; + bestRequest = currRequest; + bestRequestCost = currRequestCost; + + if(bestRequestCost == 0 || + strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT) + { + break; + } + } + } + } + } + + if(pBestRequestBlock != VMA_NULL) + { + if(mapped) + { + VkResult res = pBestRequestBlock->Map(m_hAllocator, 1, VMA_NULL); + if(res != VK_SUCCESS) + { + return res; + } + } + + if(pBestRequestBlock->m_pMetadata->MakeRequestedAllocationsLost( + currentFrameIndex, + m_FrameInUseCount, + &bestRequest)) + { + // We no longer have an empty Allocation. + if(pBestRequestBlock->m_pMetadata->IsEmpty()) + { + m_HasEmptyBlock = false; + } + // Allocate from this pBlock. + *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString); + pBestRequestBlock->m_pMetadata->Alloc(bestRequest, suballocType, size, isUpperAddress, *pAllocation); + (*pAllocation)->InitBlockAllocation( + hCurrentPool, + pBestRequestBlock, + bestRequest.offset, + alignment, + size, + suballocType, + mapped, + (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0); + VMA_HEAVY_ASSERT(pBestRequestBlock->Validate()); + VMA_DEBUG_LOG(" Returned from existing allocation #%u", (uint32_t)blockIndex); + (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData); + if(VMA_DEBUG_INITIALIZE_ALLOCATIONS) + { + m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED); + } + if(IsCorruptionDetectionEnabled()) + { + VkResult res = pBestRequestBlock->WriteMagicValueAroundAllocation(m_hAllocator, bestRequest.offset, size); + (void) res; + VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value."); + } + return VK_SUCCESS; + } + // else: Some allocations must have been touched while we are here. Next try. + } + else + { + // Could not find place in any of the blocks - break outer loop. + break; + } + } + /* Maximum number of tries exceeded - a very unlike event when many other + threads are simultaneously touching allocations making it impossible to make + lost at the same time as we try to allocate. */ + if(tryIndex == VMA_ALLOCATION_TRY_COUNT) + { + return VK_ERROR_TOO_MANY_OBJECTS; + } + } + + return VK_ERROR_OUT_OF_DEVICE_MEMORY; +} + +void VmaBlockVector::Free( + VmaAllocation hAllocation) +{ + VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL; + + // Scope for lock. + { + VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex); + + VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock(); + + if(IsCorruptionDetectionEnabled()) + { + VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize()); + (void) res; + VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value."); + } + + if(hAllocation->IsPersistentMap()) + { + pBlock->Unmap(m_hAllocator, 1); + } + + pBlock->m_pMetadata->Free(hAllocation); + VMA_HEAVY_ASSERT(pBlock->Validate()); + + VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u", memTypeIndex); + + // pBlock became empty after this deallocation. + if(pBlock->m_pMetadata->IsEmpty()) + { + // Already has empty Allocation. We don't want to have two, so delete this one. + if(m_HasEmptyBlock && m_Blocks.size() > m_MinBlockCount) + { + pBlockToDelete = pBlock; + Remove(pBlock); + } + // We now have first empty block. + else + { + m_HasEmptyBlock = true; + } + } + // pBlock didn't become empty, but we have another empty block - find and free that one. + // (This is optional, heuristics.) + else if(m_HasEmptyBlock) + { + VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back(); + if(pLastBlock->m_pMetadata->IsEmpty() && m_Blocks.size() > m_MinBlockCount) + { + pBlockToDelete = pLastBlock; + m_Blocks.pop_back(); + m_HasEmptyBlock = false; + } + } + + IncrementallySortBlocks(); + } + + // Destruction of a free Allocation. Deferred until this point, outside of mutex + // lock, for performance reason. + if(pBlockToDelete != VMA_NULL) + { + VMA_DEBUG_LOG(" Deleted empty allocation"); + pBlockToDelete->Destroy(m_hAllocator); + vma_delete(m_hAllocator, pBlockToDelete); + } +} + +VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const +{ + VkDeviceSize result = 0; + for(size_t i = m_Blocks.size(); i--; ) + { + result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize()); + if(result >= m_PreferredBlockSize) + { + break; + } + } + return result; +} + +void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock) +{ + for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex) + { + if(m_Blocks[blockIndex] == pBlock) + { + VmaVectorRemove(m_Blocks, blockIndex); + return; + } + } + VMA_ASSERT(0); +} + +void VmaBlockVector::IncrementallySortBlocks() +{ + if(m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) + { + // Bubble sort only until first swap. + for(size_t i = 1; i < m_Blocks.size(); ++i) + { + if(m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize()) + { + VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]); + return; + } + } + } +} + +VkResult VmaBlockVector::AllocateFromBlock( + VmaDeviceMemoryBlock* pBlock, + VmaPool hCurrentPool, + uint32_t currentFrameIndex, + VkDeviceSize size, + VkDeviceSize alignment, + VmaAllocationCreateFlags allocFlags, + void* pUserData, + VmaSuballocationType suballocType, + uint32_t strategy, + VmaAllocation* pAllocation) +{ + VMA_ASSERT((allocFlags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) == 0); + const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0; + const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0; + const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0; + + VmaAllocationRequest currRequest = {}; + if(pBlock->m_pMetadata->CreateAllocationRequest( + currentFrameIndex, + m_FrameInUseCount, + m_BufferImageGranularity, + size, + alignment, + isUpperAddress, + suballocType, + false, // canMakeOtherLost + strategy, + &currRequest)) + { + // Allocate from pCurrBlock. + VMA_ASSERT(currRequest.itemsToMakeLostCount == 0); + + if(mapped) + { + VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL); + if(res != VK_SUCCESS) + { + return res; + } + } + + // We no longer have an empty Allocation. + if(pBlock->m_pMetadata->IsEmpty()) + { + m_HasEmptyBlock = false; + } + + *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString); + pBlock->m_pMetadata->Alloc(currRequest, suballocType, size, isUpperAddress, *pAllocation); + (*pAllocation)->InitBlockAllocation( + hCurrentPool, + pBlock, + currRequest.offset, + alignment, + size, + suballocType, + mapped, + (allocFlags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0); + VMA_HEAVY_ASSERT(pBlock->Validate()); + (*pAllocation)->SetUserData(m_hAllocator, pUserData); + if(VMA_DEBUG_INITIALIZE_ALLOCATIONS) + { + m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED); + } + if(IsCorruptionDetectionEnabled()) + { + VkResult res = pBlock->WriteMagicValueAroundAllocation(m_hAllocator, currRequest.offset, size); + (void) res; + VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value."); + } + return VK_SUCCESS; + } + return VK_ERROR_OUT_OF_DEVICE_MEMORY; +} + +VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex) +{ + VkMemoryAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocInfo.memoryTypeIndex = m_MemoryTypeIndex; + allocInfo.allocationSize = blockSize; + VkDeviceMemory mem = VK_NULL_HANDLE; + VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem); + if(res < 0) + { + return res; + } + + // New VkDeviceMemory successfully created. + + // Create new Allocation for it. + VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator); + pBlock->Init( + m_hAllocator, + m_MemoryTypeIndex, + mem, + allocInfo.allocationSize, + m_NextBlockId++, + m_Algorithm); + + m_Blocks.push_back(pBlock); + if(pNewBlockIndex != VMA_NULL) + { + *pNewBlockIndex = m_Blocks.size() - 1; + } + + return VK_SUCCESS; +} + +void VmaBlockVector::ApplyDefragmentationMovesCpu( + class VmaBlockVectorDefragmentationContext* pDefragCtx, + const VmaVector< VmaDefragmentationMove, VmaStlAllocator >& moves) +{ + const size_t blockCount = m_Blocks.size(); + const bool isNonCoherent = m_hAllocator->IsMemoryTypeNonCoherent(m_MemoryTypeIndex); + + enum BLOCK_FLAG + { + BLOCK_FLAG_USED = 0x00000001, + BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION = 0x00000002, + }; + + struct BlockInfo + { + uint32_t flags; + void* pMappedData; + }; + VmaVector< BlockInfo, VmaStlAllocator > + blockInfo(blockCount, VmaStlAllocator(m_hAllocator->GetAllocationCallbacks())); + memset(blockInfo.data(), 0, blockCount * sizeof(BlockInfo)); + + // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED. + const size_t moveCount = moves.size(); + for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex) + { + const VmaDefragmentationMove& move = moves[moveIndex]; + blockInfo[move.srcBlockIndex].flags |= BLOCK_FLAG_USED; + blockInfo[move.dstBlockIndex].flags |= BLOCK_FLAG_USED; + } + + VMA_ASSERT(pDefragCtx->res == VK_SUCCESS); + + // Go over all blocks. Get mapped pointer or map if necessary. + for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex) + { + BlockInfo& currBlockInfo = blockInfo[blockIndex]; + VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex]; + if((currBlockInfo.flags & BLOCK_FLAG_USED) != 0) + { + currBlockInfo.pMappedData = pBlock->GetMappedData(); + // It is not originally mapped - map it. + if(currBlockInfo.pMappedData == VMA_NULL) + { + pDefragCtx->res = pBlock->Map(m_hAllocator, 1, &currBlockInfo.pMappedData); + if(pDefragCtx->res == VK_SUCCESS) + { + currBlockInfo.flags |= BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION; + } + } + } + } + + // Go over all moves. Do actual data transfer. + if(pDefragCtx->res == VK_SUCCESS) + { + const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize; + VkMappedMemoryRange memRange = {}; + memRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + + for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex) + { + const VmaDefragmentationMove& move = moves[moveIndex]; + + const BlockInfo& srcBlockInfo = blockInfo[move.srcBlockIndex]; + const BlockInfo& dstBlockInfo = blockInfo[move.dstBlockIndex]; + + VMA_ASSERT(srcBlockInfo.pMappedData && dstBlockInfo.pMappedData); + + // Invalidate source. + if(isNonCoherent) + { + VmaDeviceMemoryBlock* const pSrcBlock = m_Blocks[move.srcBlockIndex]; + memRange.memory = pSrcBlock->GetDeviceMemory(); + memRange.offset = VmaAlignDown(move.srcOffset, nonCoherentAtomSize); + memRange.size = VMA_MIN( + VmaAlignUp(move.size + (move.srcOffset - memRange.offset), nonCoherentAtomSize), + pSrcBlock->m_pMetadata->GetSize() - memRange.offset); + (*m_hAllocator->GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange); + } + + // THE PLACE WHERE ACTUAL DATA COPY HAPPENS. + memmove( + reinterpret_cast(dstBlockInfo.pMappedData) + move.dstOffset, + reinterpret_cast(srcBlockInfo.pMappedData) + move.srcOffset, + static_cast(move.size)); + + if(IsCorruptionDetectionEnabled()) + { + VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset - VMA_DEBUG_MARGIN); + VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset + move.size); + } + + // Flush destination. + if(isNonCoherent) + { + VmaDeviceMemoryBlock* const pDstBlock = m_Blocks[move.dstBlockIndex]; + memRange.memory = pDstBlock->GetDeviceMemory(); + memRange.offset = VmaAlignDown(move.dstOffset, nonCoherentAtomSize); + memRange.size = VMA_MIN( + VmaAlignUp(move.size + (move.dstOffset - memRange.offset), nonCoherentAtomSize), + pDstBlock->m_pMetadata->GetSize() - memRange.offset); + (*m_hAllocator->GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange); + } + } + } + + // Go over all blocks in reverse order. Unmap those that were mapped just for defragmentation. + // Regardless of pCtx->res == VK_SUCCESS. + for(size_t blockIndex = blockCount; blockIndex--; ) + { + const BlockInfo& currBlockInfo = blockInfo[blockIndex]; + if((currBlockInfo.flags & BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION) != 0) + { + VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex]; + pBlock->Unmap(m_hAllocator, 1); + } + } +} + +void VmaBlockVector::ApplyDefragmentationMovesGpu( + class VmaBlockVectorDefragmentationContext* pDefragCtx, + const VmaVector< VmaDefragmentationMove, VmaStlAllocator >& moves, + VkCommandBuffer commandBuffer) +{ + const size_t blockCount = m_Blocks.size(); + + pDefragCtx->blockContexts.resize(blockCount); + for (size_t i = 0; i < blockCount; ++i) + pDefragCtx->blockContexts[i] = VmaBlockDefragmentationContext(); + + // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED. + const size_t moveCount = moves.size(); + for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex) + { + const VmaDefragmentationMove& move = moves[moveIndex]; + pDefragCtx->blockContexts[move.srcBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED; + pDefragCtx->blockContexts[move.dstBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED; + } + + VMA_ASSERT(pDefragCtx->res == VK_SUCCESS); + + // Go over all blocks. Create and bind buffer for whole block if necessary. + { + VkBufferCreateInfo bufCreateInfo = {}; + bufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT; + + for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex) + { + VmaBlockDefragmentationContext& currBlockCtx = pDefragCtx->blockContexts[blockIndex]; + VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex]; + if((currBlockCtx.flags & VmaBlockDefragmentationContext::BLOCK_FLAG_USED) != 0) + { + bufCreateInfo.size = pBlock->m_pMetadata->GetSize(); + pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkCreateBuffer)( + m_hAllocator->m_hDevice, &bufCreateInfo, m_hAllocator->GetAllocationCallbacks(), &currBlockCtx.hBuffer); + if(pDefragCtx->res == VK_SUCCESS) + { + pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkBindBufferMemory)( + m_hAllocator->m_hDevice, currBlockCtx.hBuffer, pBlock->GetDeviceMemory(), 0); + } + } + } + } + + // Go over all moves. Post data transfer commands to command buffer. + if(pDefragCtx->res == VK_SUCCESS) + { + /*const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize; + VkMappedMemoryRange memRange = {}; + memRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;*/ + + for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex) + { + const VmaDefragmentationMove& move = moves[moveIndex]; + + const VmaBlockDefragmentationContext& srcBlockCtx = pDefragCtx->blockContexts[move.srcBlockIndex]; + const VmaBlockDefragmentationContext& dstBlockCtx = pDefragCtx->blockContexts[move.dstBlockIndex]; + + VMA_ASSERT(srcBlockCtx.hBuffer && dstBlockCtx.hBuffer); + + VkBufferCopy region = { + move.srcOffset, + move.dstOffset, + move.size }; + (*m_hAllocator->GetVulkanFunctions().vkCmdCopyBuffer)( + commandBuffer, srcBlockCtx.hBuffer, dstBlockCtx.hBuffer, 1, ®ion); + } + } + + // Save buffers to defrag context for later destruction. + if(pDefragCtx->res == VK_SUCCESS && moveCount > 0) + { + pDefragCtx->res = VK_NOT_READY; + } +} + +void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats) +{ + m_HasEmptyBlock = false; + for(size_t blockIndex = m_Blocks.size(); blockIndex--; ) + { + VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex]; + if(pBlock->m_pMetadata->IsEmpty()) + { + if(m_Blocks.size() > m_MinBlockCount) + { + if(pDefragmentationStats != VMA_NULL) + { + ++pDefragmentationStats->deviceMemoryBlocksFreed; + pDefragmentationStats->bytesFreed += pBlock->m_pMetadata->GetSize(); + } + + VmaVectorRemove(m_Blocks, blockIndex); + pBlock->Destroy(m_hAllocator); + vma_delete(m_hAllocator, pBlock); + } + else + { + m_HasEmptyBlock = true; + } + } + } +} + +#if VMA_STATS_STRING_ENABLED + +void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json) +{ + VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex); + + json.BeginObject(); + + if(m_IsCustomPool) + { + json.WriteString("MemoryTypeIndex"); + json.WriteNumber(m_MemoryTypeIndex); + + json.WriteString("BlockSize"); + json.WriteNumber(m_PreferredBlockSize); + + json.WriteString("BlockCount"); + json.BeginObject(true); + if(m_MinBlockCount > 0) + { + json.WriteString("Min"); + json.WriteNumber((uint64_t)m_MinBlockCount); + } + if(m_MaxBlockCount < SIZE_MAX) + { + json.WriteString("Max"); + json.WriteNumber((uint64_t)m_MaxBlockCount); + } + json.WriteString("Cur"); + json.WriteNumber((uint64_t)m_Blocks.size()); + json.EndObject(); + + if(m_FrameInUseCount > 0) + { + json.WriteString("FrameInUseCount"); + json.WriteNumber(m_FrameInUseCount); + } + + if(m_Algorithm != 0) + { + json.WriteString("Algorithm"); + json.WriteString(VmaAlgorithmToStr(m_Algorithm)); + } + } + else + { + json.WriteString("PreferredBlockSize"); + json.WriteNumber(m_PreferredBlockSize); + } + + json.WriteString("Blocks"); + json.BeginObject(); + for(size_t i = 0; i < m_Blocks.size(); ++i) + { + json.BeginString(); + json.ContinueString(m_Blocks[i]->GetId()); + json.EndString(); + + m_Blocks[i]->m_pMetadata->PrintDetailedMap(json); + } + json.EndObject(); + + json.EndObject(); +} + +#endif // #if VMA_STATS_STRING_ENABLED + +void VmaBlockVector::Defragment( + class VmaBlockVectorDefragmentationContext* pCtx, + VmaDefragmentationStats* pStats, + VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove, + VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove, + VkCommandBuffer commandBuffer) +{ + pCtx->res = VK_SUCCESS; + + const VkMemoryPropertyFlags memPropFlags = + m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags; + const bool isHostVisible = (memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0; + const bool isHostCoherent = (memPropFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0; + + const bool canDefragmentOnCpu = maxCpuBytesToMove > 0 && maxCpuAllocationsToMove > 0 && + isHostVisible; + const bool canDefragmentOnGpu = maxGpuBytesToMove > 0 && maxGpuAllocationsToMove > 0 && + (VMA_DEBUG_DETECT_CORRUPTION == 0 || !(isHostVisible && isHostCoherent)); + + // There are options to defragment this memory type. + if(canDefragmentOnCpu || canDefragmentOnGpu) + { + bool defragmentOnGpu; + // There is only one option to defragment this memory type. + if(canDefragmentOnGpu != canDefragmentOnCpu) + { + defragmentOnGpu = canDefragmentOnGpu; + } + // Both options are available: Heuristics to choose the best one. + else + { + defragmentOnGpu = (memPropFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 || + m_hAllocator->IsIntegratedGpu(); + } + + bool overlappingMoveSupported = !defragmentOnGpu; + + if(m_hAllocator->m_UseMutex) + { + m_Mutex.LockWrite(); + pCtx->mutexLocked = true; + } + + pCtx->Begin(overlappingMoveSupported); + + // Defragment. + + const VkDeviceSize maxBytesToMove = defragmentOnGpu ? maxGpuBytesToMove : maxCpuBytesToMove; + const uint32_t maxAllocationsToMove = defragmentOnGpu ? maxGpuAllocationsToMove : maxCpuAllocationsToMove; + VmaVector< VmaDefragmentationMove, VmaStlAllocator > moves = + VmaVector< VmaDefragmentationMove, VmaStlAllocator >(VmaStlAllocator(m_hAllocator->GetAllocationCallbacks())); + pCtx->res = pCtx->GetAlgorithm()->Defragment(moves, maxBytesToMove, maxAllocationsToMove); + + // Accumulate statistics. + if(pStats != VMA_NULL) + { + const VkDeviceSize bytesMoved = pCtx->GetAlgorithm()->GetBytesMoved(); + const uint32_t allocationsMoved = pCtx->GetAlgorithm()->GetAllocationsMoved(); + pStats->bytesMoved += bytesMoved; + pStats->allocationsMoved += allocationsMoved; + VMA_ASSERT(bytesMoved <= maxBytesToMove); + VMA_ASSERT(allocationsMoved <= maxAllocationsToMove); + if(defragmentOnGpu) + { + maxGpuBytesToMove -= bytesMoved; + maxGpuAllocationsToMove -= allocationsMoved; + } + else + { + maxCpuBytesToMove -= bytesMoved; + maxCpuAllocationsToMove -= allocationsMoved; + } + } + + if(pCtx->res >= VK_SUCCESS) + { + if(defragmentOnGpu) + { + ApplyDefragmentationMovesGpu(pCtx, moves, commandBuffer); + } + else + { + ApplyDefragmentationMovesCpu(pCtx, moves); + } + } + } +} + +void VmaBlockVector::DefragmentationEnd( + class VmaBlockVectorDefragmentationContext* pCtx, + VmaDefragmentationStats* pStats) +{ + // Destroy buffers. + for(size_t blockIndex = pCtx->blockContexts.size(); blockIndex--; ) + { + VmaBlockDefragmentationContext& blockCtx = pCtx->blockContexts[blockIndex]; + if(blockCtx.hBuffer) + { + (*m_hAllocator->GetVulkanFunctions().vkDestroyBuffer)( + m_hAllocator->m_hDevice, blockCtx.hBuffer, m_hAllocator->GetAllocationCallbacks()); + } + } + + if(pCtx->res >= VK_SUCCESS) + { + FreeEmptyBlocks(pStats); + } + + if(pCtx->mutexLocked) + { + VMA_ASSERT(m_hAllocator->m_UseMutex); + m_Mutex.UnlockWrite(); + } +} + +size_t VmaBlockVector::CalcAllocationCount() const +{ + size_t result = 0; + for(size_t i = 0; i < m_Blocks.size(); ++i) + { + result += m_Blocks[i]->m_pMetadata->GetAllocationCount(); + } + return result; +} + +bool VmaBlockVector::IsBufferImageGranularityConflictPossible() const +{ + if(m_BufferImageGranularity == 1) + { + return false; + } + VmaSuballocationType lastSuballocType = VMA_SUBALLOCATION_TYPE_FREE; + for(size_t i = 0, count = m_Blocks.size(); i < count; ++i) + { + VmaDeviceMemoryBlock* const pBlock = m_Blocks[i]; + VMA_ASSERT(m_Algorithm == 0); + VmaBlockMetadata_Generic* const pMetadata = (VmaBlockMetadata_Generic*)pBlock->m_pMetadata; + if(pMetadata->IsBufferImageGranularityConflictPossible(m_BufferImageGranularity, lastSuballocType)) + { + return true; + } + } + return false; +} + +void VmaBlockVector::MakePoolAllocationsLost( + uint32_t currentFrameIndex, + size_t* pLostAllocationCount) +{ + VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex); + size_t lostAllocationCount = 0; + for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex) + { + VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex]; + VMA_ASSERT(pBlock); + lostAllocationCount += pBlock->m_pMetadata->MakeAllocationsLost(currentFrameIndex, m_FrameInUseCount); + } + if(pLostAllocationCount != VMA_NULL) + { + *pLostAllocationCount = lostAllocationCount; + } +} + +VkResult VmaBlockVector::CheckCorruption() +{ + if(!IsCorruptionDetectionEnabled()) + { + return VK_ERROR_FEATURE_NOT_PRESENT; + } + + VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex); + for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex) + { + VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex]; + VMA_ASSERT(pBlock); + VkResult res = pBlock->CheckCorruption(m_hAllocator); + if(res != VK_SUCCESS) + { + return res; + } + } + return VK_SUCCESS; +} + +void VmaBlockVector::AddStats(VmaStats* pStats) +{ + const uint32_t memTypeIndex = m_MemoryTypeIndex; + const uint32_t memHeapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(memTypeIndex); + + VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex); + + for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex) + { + const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex]; + VMA_ASSERT(pBlock); + VMA_HEAVY_ASSERT(pBlock->Validate()); + VmaStatInfo allocationStatInfo; + pBlock->m_pMetadata->CalcAllocationStatInfo(allocationStatInfo); + VmaAddStatInfo(pStats->total, allocationStatInfo); + VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo); + VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// VmaDefragmentationAlgorithm_Generic members definition + +VmaDefragmentationAlgorithm_Generic::VmaDefragmentationAlgorithm_Generic( + VmaAllocator hAllocator, + VmaBlockVector* pBlockVector, + uint32_t currentFrameIndex, + bool /*overlappingMoveSupported*/) : + VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex), + m_AllocationCount(0), + m_AllAllocations(false), + m_BytesMoved(0), + m_AllocationsMoved(0), + m_Blocks(VmaStlAllocator(hAllocator->GetAllocationCallbacks())) +{ + // Create block info for each block. + const size_t blockCount = m_pBlockVector->m_Blocks.size(); + for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex) + { + BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks()); + pBlockInfo->m_OriginalBlockIndex = blockIndex; + pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex]; + m_Blocks.push_back(pBlockInfo); + } + + // Sort them by m_pBlock pointer value. + VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess()); +} + +VmaDefragmentationAlgorithm_Generic::~VmaDefragmentationAlgorithm_Generic() +{ + for(size_t i = m_Blocks.size(); i--; ) + { + vma_delete(m_hAllocator, m_Blocks[i]); + } +} + +void VmaDefragmentationAlgorithm_Generic::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) +{ + // Now as we are inside VmaBlockVector::m_Mutex, we can make final check if this allocation was not lost. + if(hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST) + { + VmaDeviceMemoryBlock* pBlock = hAlloc->GetBlock(); + BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess()); + if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock) + { + AllocationInfo allocInfo = AllocationInfo(hAlloc, pChanged); + (*it)->m_Allocations.push_back(allocInfo); + } + else + { + VMA_ASSERT(0); + } + + ++m_AllocationCount; + } +} + +VkResult VmaDefragmentationAlgorithm_Generic::DefragmentRound( + VmaVector< VmaDefragmentationMove, VmaStlAllocator >& moves, + VkDeviceSize maxBytesToMove, + uint32_t maxAllocationsToMove) +{ + if(m_Blocks.empty()) + { + return VK_SUCCESS; + } + + // This is a choice based on research. + // Option 1: + uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT; + // Option 2: + //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT; + // Option 3: + //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT; + + size_t srcBlockMinIndex = 0; + // When FAST_ALGORITHM, move allocations from only last out of blocks that contain non-movable allocations. + /* + if(m_AlgorithmFlags & VMA_DEFRAGMENTATION_FAST_ALGORITHM_BIT) + { + const size_t blocksWithNonMovableCount = CalcBlocksWithNonMovableCount(); + if(blocksWithNonMovableCount > 0) + { + srcBlockMinIndex = blocksWithNonMovableCount - 1; + } + } + */ + + size_t srcBlockIndex = m_Blocks.size() - 1; + size_t srcAllocIndex = SIZE_MAX; + for(;;) + { + // 1. Find next allocation to move. + // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source". + // 1.2. Then start from last to first m_Allocations. + while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size()) + { + if(m_Blocks[srcBlockIndex]->m_Allocations.empty()) + { + // Finished: no more allocations to process. + if(srcBlockIndex == srcBlockMinIndex) + { + return VK_SUCCESS; + } + else + { + --srcBlockIndex; + srcAllocIndex = SIZE_MAX; + } + } + else + { + srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1; + } + } + + BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex]; + AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex]; + + const VkDeviceSize size = allocInfo.m_hAllocation->GetSize(); + const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset(); + const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment(); + const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType(); + + // 2. Try to find new place for this allocation in preceding or current block. + for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex) + { + BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex]; + VmaAllocationRequest dstAllocRequest; + if(pDstBlockInfo->m_pBlock->m_pMetadata->CreateAllocationRequest( + m_CurrentFrameIndex, + m_pBlockVector->GetFrameInUseCount(), + m_pBlockVector->GetBufferImageGranularity(), + size, + alignment, + false, // upperAddress + suballocType, + false, // canMakeOtherLost + strategy, + &dstAllocRequest) && + MoveMakesSense( + dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset)) + { + VMA_ASSERT(dstAllocRequest.itemsToMakeLostCount == 0); + + // Reached limit on number of allocations or bytes to move. + if((m_AllocationsMoved + 1 > maxAllocationsToMove) || + (m_BytesMoved + size > maxBytesToMove)) + { + return VK_SUCCESS; + } + + VmaDefragmentationMove move; + move.srcBlockIndex = pSrcBlockInfo->m_OriginalBlockIndex; + move.dstBlockIndex = pDstBlockInfo->m_OriginalBlockIndex; + move.srcOffset = srcOffset; + move.dstOffset = dstAllocRequest.offset; + move.size = size; + moves.push_back(move); + + pDstBlockInfo->m_pBlock->m_pMetadata->Alloc( + dstAllocRequest, + suballocType, + size, + false, // upperAddress + allocInfo.m_hAllocation); + pSrcBlockInfo->m_pBlock->m_pMetadata->FreeAtOffset(srcOffset); + + allocInfo.m_hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlockInfo->m_pBlock, dstAllocRequest.offset); + + if(allocInfo.m_pChanged != VMA_NULL) + { + *allocInfo.m_pChanged = VK_TRUE; + } + + ++m_AllocationsMoved; + m_BytesMoved += size; + + VmaVectorRemove(pSrcBlockInfo->m_Allocations, srcAllocIndex); + + break; + } + } + + // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round. + + if(srcAllocIndex > 0) + { + --srcAllocIndex; + } + else + { + if(srcBlockIndex > 0) + { + --srcBlockIndex; + srcAllocIndex = SIZE_MAX; + } + else + { + return VK_SUCCESS; + } + } + } +} + +size_t VmaDefragmentationAlgorithm_Generic::CalcBlocksWithNonMovableCount() const +{ + size_t result = 0; + for(size_t i = 0; i < m_Blocks.size(); ++i) + { + if(m_Blocks[i]->m_HasNonMovableAllocations) + { + ++result; + } + } + return result; +} + +VkResult VmaDefragmentationAlgorithm_Generic::Defragment( + VmaVector< VmaDefragmentationMove, VmaStlAllocator >& moves, + VkDeviceSize maxBytesToMove, + uint32_t maxAllocationsToMove) +{ + if(!m_AllAllocations && m_AllocationCount == 0) + { + return VK_SUCCESS; + } + + const size_t blockCount = m_Blocks.size(); + for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex) + { + BlockInfo* pBlockInfo = m_Blocks[blockIndex]; + + if(m_AllAllocations) + { + VmaBlockMetadata_Generic* pMetadata = (VmaBlockMetadata_Generic*)pBlockInfo->m_pBlock->m_pMetadata; + for(VmaSuballocationList::const_iterator it = pMetadata->m_Suballocations.begin(); + it != pMetadata->m_Suballocations.end(); + ++it) + { + if(it->type != VMA_SUBALLOCATION_TYPE_FREE) + { + AllocationInfo allocInfo = AllocationInfo(it->hAllocation, VMA_NULL); + pBlockInfo->m_Allocations.push_back(allocInfo); + } + } + } + + pBlockInfo->CalcHasNonMovableAllocations(); + + // This is a choice based on research. + // Option 1: + pBlockInfo->SortAllocationsByOffsetDescending(); + // Option 2: + //pBlockInfo->SortAllocationsBySizeDescending(); + } + + // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks. + VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination()); + + // This is a choice based on research. + const uint32_t roundCount = 2; + + // Execute defragmentation rounds (the main part). + VkResult result = VK_SUCCESS; + for(uint32_t round = 0; (round < roundCount) && (result == VK_SUCCESS); ++round) + { + result = DefragmentRound(moves, maxBytesToMove, maxAllocationsToMove); + } + + return result; +} + +bool VmaDefragmentationAlgorithm_Generic::MoveMakesSense( + size_t dstBlockIndex, VkDeviceSize dstOffset, + size_t srcBlockIndex, VkDeviceSize srcOffset) +{ + if(dstBlockIndex < srcBlockIndex) + { + return true; + } + if(dstBlockIndex > srcBlockIndex) + { + return false; + } + if(dstOffset < srcOffset) + { + return true; + } + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// VmaDefragmentationAlgorithm_Fast + +VmaDefragmentationAlgorithm_Fast::VmaDefragmentationAlgorithm_Fast( + VmaAllocator hAllocator, + VmaBlockVector* pBlockVector, + uint32_t currentFrameIndex, + bool overlappingMoveSupported) : + VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex), + m_OverlappingMoveSupported(overlappingMoveSupported), + m_AllocationCount(0), + m_AllAllocations(false), + m_BytesMoved(0), + m_AllocationsMoved(0), + m_BlockInfos(VmaStlAllocator(hAllocator->GetAllocationCallbacks())) +{ + VMA_ASSERT(VMA_DEBUG_MARGIN == 0); + +} + +VmaDefragmentationAlgorithm_Fast::~VmaDefragmentationAlgorithm_Fast() +{ +} + +VkResult VmaDefragmentationAlgorithm_Fast::Defragment( + VmaVector< VmaDefragmentationMove, VmaStlAllocator >& moves, + VkDeviceSize maxBytesToMove, + uint32_t maxAllocationsToMove) +{ + VMA_ASSERT(m_AllAllocations || m_pBlockVector->CalcAllocationCount() == m_AllocationCount); + + const size_t blockCount = m_pBlockVector->GetBlockCount(); + if(blockCount == 0 || maxBytesToMove == 0 || maxAllocationsToMove == 0) + { + return VK_SUCCESS; + } + + PreprocessMetadata(); + + // Sort blocks in order from most destination. + + m_BlockInfos.resize(blockCount); + for(size_t i = 0; i < blockCount; ++i) + { + m_BlockInfos[i].origBlockIndex = i; + } + + VMA_SORT(m_BlockInfos.begin(), m_BlockInfos.end(), [this](const BlockInfo& lhs, const BlockInfo& rhs) -> bool { + return m_pBlockVector->GetBlock(lhs.origBlockIndex)->m_pMetadata->GetSumFreeSize() < + m_pBlockVector->GetBlock(rhs.origBlockIndex)->m_pMetadata->GetSumFreeSize(); + }); + + // THE MAIN ALGORITHM + + FreeSpaceDatabase freeSpaceDb; + + size_t dstBlockInfoIndex = 0; + size_t dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex; + VmaDeviceMemoryBlock* pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex); + VmaBlockMetadata_Generic* pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata; + VkDeviceSize dstBlockSize = pDstMetadata->GetSize(); + VkDeviceSize dstOffset = 0; + + bool end = false; + for(size_t srcBlockInfoIndex = 0; !end && srcBlockInfoIndex < blockCount; ++srcBlockInfoIndex) + { + const size_t srcOrigBlockIndex = m_BlockInfos[srcBlockInfoIndex].origBlockIndex; + VmaDeviceMemoryBlock* const pSrcBlock = m_pBlockVector->GetBlock(srcOrigBlockIndex); + VmaBlockMetadata_Generic* const pSrcMetadata = (VmaBlockMetadata_Generic*)pSrcBlock->m_pMetadata; + for(VmaSuballocationList::iterator srcSuballocIt = pSrcMetadata->m_Suballocations.begin(); + !end && srcSuballocIt != pSrcMetadata->m_Suballocations.end(); ) + { + VmaAllocation_T* const pAlloc = srcSuballocIt->hAllocation; + const VkDeviceSize srcAllocAlignment = pAlloc->GetAlignment(); + const VkDeviceSize srcAllocSize = srcSuballocIt->size; + if(m_AllocationsMoved == maxAllocationsToMove || + m_BytesMoved + srcAllocSize > maxBytesToMove) + { + end = true; + break; + } + const VkDeviceSize srcAllocOffset = srcSuballocIt->offset; + + // Try to place it in one of free spaces from the database. + size_t freeSpaceInfoIndex; + VkDeviceSize dstAllocOffset; + if(freeSpaceDb.Fetch(srcAllocAlignment, srcAllocSize, + freeSpaceInfoIndex, dstAllocOffset)) + { + size_t freeSpaceOrigBlockIndex = m_BlockInfos[freeSpaceInfoIndex].origBlockIndex; + VmaDeviceMemoryBlock* pFreeSpaceBlock = m_pBlockVector->GetBlock(freeSpaceOrigBlockIndex); + VmaBlockMetadata_Generic* pFreeSpaceMetadata = (VmaBlockMetadata_Generic*)pFreeSpaceBlock->m_pMetadata; + /*VkDeviceSize freeSpaceBlockSize = pFreeSpaceMetadata->GetSize();*/ + + // Same block + if(freeSpaceInfoIndex == srcBlockInfoIndex) + { + VMA_ASSERT(dstAllocOffset <= srcAllocOffset); + + // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset. + + VmaSuballocation suballoc = *srcSuballocIt; + suballoc.offset = dstAllocOffset; + suballoc.hAllocation->ChangeOffset(dstAllocOffset); + m_BytesMoved += srcAllocSize; + ++m_AllocationsMoved; + + VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt; + ++nextSuballocIt; + pSrcMetadata->m_Suballocations.erase(srcSuballocIt); + srcSuballocIt = nextSuballocIt; + + InsertSuballoc(pFreeSpaceMetadata, suballoc); + + VmaDefragmentationMove move = { + srcOrigBlockIndex, freeSpaceOrigBlockIndex, + srcAllocOffset, dstAllocOffset, + srcAllocSize }; + moves.push_back(move); + } + // Different block + else + { + // MOVE OPTION 2: Move the allocation to a different block. + + VMA_ASSERT(freeSpaceInfoIndex < srcBlockInfoIndex); + + VmaSuballocation suballoc = *srcSuballocIt; + suballoc.offset = dstAllocOffset; + suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pFreeSpaceBlock, dstAllocOffset); + m_BytesMoved += srcAllocSize; + ++m_AllocationsMoved; + + VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt; + ++nextSuballocIt; + pSrcMetadata->m_Suballocations.erase(srcSuballocIt); + srcSuballocIt = nextSuballocIt; + + InsertSuballoc(pFreeSpaceMetadata, suballoc); + + VmaDefragmentationMove move = { + srcOrigBlockIndex, freeSpaceOrigBlockIndex, + srcAllocOffset, dstAllocOffset, + srcAllocSize }; + moves.push_back(move); + } + } + else + { + dstAllocOffset = VmaAlignUp(dstOffset, srcAllocAlignment); + + // If the allocation doesn't fit before the end of dstBlock, forward to next block. + while(dstBlockInfoIndex < srcBlockInfoIndex && + dstAllocOffset + srcAllocSize > dstBlockSize) + { + // But before that, register remaining free space at the end of dst block. + freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, dstBlockSize - dstOffset); + + ++dstBlockInfoIndex; + dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex; + pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex); + pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata; + dstBlockSize = pDstMetadata->GetSize(); + dstOffset = 0; + dstAllocOffset = 0; + } + + // Same block + if(dstBlockInfoIndex == srcBlockInfoIndex) + { + VMA_ASSERT(dstAllocOffset <= srcAllocOffset); + + const bool overlap = dstAllocOffset + srcAllocSize > srcAllocOffset; + + bool skipOver = overlap; + if(overlap && m_OverlappingMoveSupported && dstAllocOffset < srcAllocOffset) + { + // If destination and source place overlap, skip if it would move it + // by only < 1/64 of its size. + skipOver = (srcAllocOffset - dstAllocOffset) * 64 < srcAllocSize; + } + + if(skipOver) + { + freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, srcAllocOffset - dstOffset); + + dstOffset = srcAllocOffset + srcAllocSize; + ++srcSuballocIt; + } + // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset. + else + { + srcSuballocIt->offset = dstAllocOffset; + srcSuballocIt->hAllocation->ChangeOffset(dstAllocOffset); + dstOffset = dstAllocOffset + srcAllocSize; + m_BytesMoved += srcAllocSize; + ++m_AllocationsMoved; + ++srcSuballocIt; + VmaDefragmentationMove move = { + srcOrigBlockIndex, dstOrigBlockIndex, + srcAllocOffset, dstAllocOffset, + srcAllocSize }; + moves.push_back(move); + } + } + // Different block + else + { + // MOVE OPTION 2: Move the allocation to a different block. + + VMA_ASSERT(dstBlockInfoIndex < srcBlockInfoIndex); + VMA_ASSERT(dstAllocOffset + srcAllocSize <= dstBlockSize); + + VmaSuballocation suballoc = *srcSuballocIt; + suballoc.offset = dstAllocOffset; + suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlock, dstAllocOffset); + dstOffset = dstAllocOffset + srcAllocSize; + m_BytesMoved += srcAllocSize; + ++m_AllocationsMoved; + + VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt; + ++nextSuballocIt; + pSrcMetadata->m_Suballocations.erase(srcSuballocIt); + srcSuballocIt = nextSuballocIt; + + pDstMetadata->m_Suballocations.push_back(suballoc); + + VmaDefragmentationMove move = { + srcOrigBlockIndex, dstOrigBlockIndex, + srcAllocOffset, dstAllocOffset, + srcAllocSize }; + moves.push_back(move); + } + } + } + } + + m_BlockInfos.clear(); + + PostprocessMetadata(); + + return VK_SUCCESS; +} + +void VmaDefragmentationAlgorithm_Fast::PreprocessMetadata() +{ + const size_t blockCount = m_pBlockVector->GetBlockCount(); + for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex) + { + VmaBlockMetadata_Generic* const pMetadata = + (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata; + pMetadata->m_FreeCount = 0; + pMetadata->m_SumFreeSize = pMetadata->GetSize(); + pMetadata->m_FreeSuballocationsBySize.clear(); + for(VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin(); + it != pMetadata->m_Suballocations.end(); ) + { + if(it->type == VMA_SUBALLOCATION_TYPE_FREE) + { + VmaSuballocationList::iterator nextIt = it; + ++nextIt; + pMetadata->m_Suballocations.erase(it); + it = nextIt; + } + else + { + ++it; + } + } + } +} + +void VmaDefragmentationAlgorithm_Fast::PostprocessMetadata() +{ + const size_t blockCount = m_pBlockVector->GetBlockCount(); + for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex) + { + VmaBlockMetadata_Generic* const pMetadata = + (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata; + const VkDeviceSize blockSize = pMetadata->GetSize(); + + // No allocations in this block - entire area is free. + if(pMetadata->m_Suballocations.empty()) + { + pMetadata->m_FreeCount = 1; + //pMetadata->m_SumFreeSize is already set to blockSize. + VmaSuballocation suballoc = { + 0, // offset + blockSize, // size + VMA_NULL, // hAllocation + VMA_SUBALLOCATION_TYPE_FREE }; + pMetadata->m_Suballocations.push_back(suballoc); + pMetadata->RegisterFreeSuballocation(pMetadata->m_Suballocations.begin()); + } + // There are some allocations in this block. + else + { + VkDeviceSize offset = 0; + VmaSuballocationList::iterator it; + for(it = pMetadata->m_Suballocations.begin(); + it != pMetadata->m_Suballocations.end(); + ++it) + { + VMA_ASSERT(it->type != VMA_SUBALLOCATION_TYPE_FREE); + VMA_ASSERT(it->offset >= offset); + + // Need to insert preceding free space. + if(it->offset > offset) + { + ++pMetadata->m_FreeCount; + const VkDeviceSize freeSize = it->offset - offset; + VmaSuballocation suballoc = { + offset, // offset + freeSize, // size + VMA_NULL, // hAllocation + VMA_SUBALLOCATION_TYPE_FREE }; + VmaSuballocationList::iterator precedingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc); + if(freeSize >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER) + { + pMetadata->m_FreeSuballocationsBySize.push_back(precedingFreeIt); + } + } + + pMetadata->m_SumFreeSize -= it->size; + offset = it->offset + it->size; + } + + // Need to insert trailing free space. + if(offset < blockSize) + { + ++pMetadata->m_FreeCount; + const VkDeviceSize freeSize = blockSize - offset; + VmaSuballocation suballoc = { + offset, // offset + freeSize, // size + VMA_NULL, // hAllocation + VMA_SUBALLOCATION_TYPE_FREE }; + VMA_ASSERT(it == pMetadata->m_Suballocations.end()); + VmaSuballocationList::iterator trailingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc); + if(freeSize > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER) + { + pMetadata->m_FreeSuballocationsBySize.push_back(trailingFreeIt); + } + } + + VMA_SORT( + pMetadata->m_FreeSuballocationsBySize.begin(), + pMetadata->m_FreeSuballocationsBySize.end(), + VmaSuballocationItemSizeLess()); + } + + VMA_HEAVY_ASSERT(pMetadata->Validate()); + } +} + +void VmaDefragmentationAlgorithm_Fast::InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc) +{ + // TODO: Optimize somehow. Remember iterator instead of searching for it linearly. + VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin(); + while(it != pMetadata->m_Suballocations.end()) + { + if(it->offset < suballoc.offset) + { + ++it; + } + } + pMetadata->m_Suballocations.insert(it, suballoc); +} + +//////////////////////////////////////////////////////////////////////////////// +// VmaBlockVectorDefragmentationContext + +VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext( + VmaAllocator hAllocator, + VmaPool hCustomPool, + VmaBlockVector* pBlockVector, + uint32_t currFrameIndex, + uint32_t /*algorithmFlags*/) : + res(VK_SUCCESS), + mutexLocked(false), + blockContexts(VmaStlAllocator(hAllocator->GetAllocationCallbacks())), + m_hAllocator(hAllocator), + m_hCustomPool(hCustomPool), + m_pBlockVector(pBlockVector), + m_CurrFrameIndex(currFrameIndex), + /*m_AlgorithmFlags(algorithmFlags),*/ + m_pAlgorithm(VMA_NULL), + m_Allocations(VmaStlAllocator(hAllocator->GetAllocationCallbacks())), + m_AllAllocations(false) +{ +} + +VmaBlockVectorDefragmentationContext::~VmaBlockVectorDefragmentationContext() +{ + vma_delete(m_hAllocator, m_pAlgorithm); +} + +void VmaBlockVectorDefragmentationContext::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) +{ + AllocInfo info = { hAlloc, pChanged }; + m_Allocations.push_back(info); +} + +void VmaBlockVectorDefragmentationContext::Begin(bool overlappingMoveSupported) +{ + const bool allAllocations = m_AllAllocations || + m_Allocations.size() == m_pBlockVector->CalcAllocationCount(); + + /******************************** + HERE IS THE CHOICE OF DEFRAGMENTATION ALGORITHM. + ********************************/ + + /* + Fast algorithm is supported only when certain criteria are met: + - VMA_DEBUG_MARGIN is 0. + - All allocations in this block vector are moveable. + - There is no possibility of image/buffer granularity conflict. + */ + if(VMA_DEBUG_MARGIN == 0 && + allAllocations && + !m_pBlockVector->IsBufferImageGranularityConflictPossible()) + { + m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Fast)( + m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported); + } + else + { + m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Generic)( + m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported); + } + + if(allAllocations) + { + m_pAlgorithm->AddAll(); + } + else + { + for(size_t i = 0, count = m_Allocations.size(); i < count; ++i) + { + m_pAlgorithm->AddAllocation(m_Allocations[i].hAlloc, m_Allocations[i].pChanged); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// VmaDefragmentationContext + +VmaDefragmentationContext_T::VmaDefragmentationContext_T( + VmaAllocator hAllocator, + uint32_t currFrameIndex, + uint32_t flags, + VmaDefragmentationStats* pStats) : + m_hAllocator(hAllocator), + m_CurrFrameIndex(currFrameIndex), + m_Flags(flags), + m_pStats(pStats), + m_CustomPoolContexts(VmaStlAllocator(hAllocator->GetAllocationCallbacks())) +{ + memset(m_DefaultPoolContexts, 0, sizeof(m_DefaultPoolContexts)); +} + +VmaDefragmentationContext_T::~VmaDefragmentationContext_T() +{ + for(size_t i = m_CustomPoolContexts.size(); i--; ) + { + VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[i]; + pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_pStats); + vma_delete(m_hAllocator, pBlockVectorCtx); + } + for(size_t i = m_hAllocator->m_MemProps.memoryTypeCount; i--; ) + { + VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[i]; + if(pBlockVectorCtx) + { + pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_pStats); + vma_delete(m_hAllocator, pBlockVectorCtx); + } + } +} + +void VmaDefragmentationContext_T::AddPools(uint32_t poolCount, VmaPool* pPools) +{ + for(uint32_t poolIndex = 0; poolIndex < poolCount; ++poolIndex) + { + VmaPool pool = pPools[poolIndex]; + VMA_ASSERT(pool); + // Pools with algorithm other than default are not defragmented. + if(pool->m_BlockVector.GetAlgorithm() == 0) + { + VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL; + + for(size_t i = m_CustomPoolContexts.size(); i--; ) + { + if(m_CustomPoolContexts[i]->GetCustomPool() == pool) + { + pBlockVectorDefragCtx = m_CustomPoolContexts[i]; + break; + } + } + + if(!pBlockVectorDefragCtx) + { + pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)( + m_hAllocator, + pool, + &pool->m_BlockVector, + m_CurrFrameIndex, + m_Flags); + m_CustomPoolContexts.push_back(pBlockVectorDefragCtx); + } + + pBlockVectorDefragCtx->AddAll(); + } + } +} + +void VmaDefragmentationContext_T::AddAllocations( + uint32_t allocationCount, + VmaAllocation* pAllocations, + VkBool32* pAllocationsChanged) +{ + // Dispatch pAllocations among defragmentators. Create them when necessary. + for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex) + { + const VmaAllocation hAlloc = pAllocations[allocIndex]; + VMA_ASSERT(hAlloc); + // DedicatedAlloc cannot be defragmented. + if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) && + // Lost allocation cannot be defragmented. + (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)) + { + VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL; + + const VmaPool hAllocPool = hAlloc->GetPool(); + // This allocation belongs to custom pool. + if(hAllocPool != VK_NULL_HANDLE) + { + // Pools with algorithm other than default are not defragmented. + if(hAllocPool->m_BlockVector.GetAlgorithm() == 0) + { + for(size_t i = m_CustomPoolContexts.size(); i--; ) + { + if(m_CustomPoolContexts[i]->GetCustomPool() == hAllocPool) + { + pBlockVectorDefragCtx = m_CustomPoolContexts[i]; + break; + } + } + if(!pBlockVectorDefragCtx) + { + pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)( + m_hAllocator, + hAllocPool, + &hAllocPool->m_BlockVector, + m_CurrFrameIndex, + m_Flags); + m_CustomPoolContexts.push_back(pBlockVectorDefragCtx); + } + } + } + // This allocation belongs to default pool. + else + { + const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex(); + pBlockVectorDefragCtx = m_DefaultPoolContexts[memTypeIndex]; + if(!pBlockVectorDefragCtx) + { + pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)( + m_hAllocator, + VMA_NULL, // hCustomPool + m_hAllocator->m_pBlockVectors[memTypeIndex], + m_CurrFrameIndex, + m_Flags); + m_DefaultPoolContexts[memTypeIndex] = pBlockVectorDefragCtx; + } + } + + if(pBlockVectorDefragCtx) + { + VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ? + &pAllocationsChanged[allocIndex] : VMA_NULL; + pBlockVectorDefragCtx->AddAllocation(hAlloc, pChanged); + } + } + } +} + +VkResult VmaDefragmentationContext_T::Defragment( + VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove, + VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove, + VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats) +{ + if(pStats) + { + memset(pStats, 0, sizeof(VmaDefragmentationStats)); + } + + if(commandBuffer == VK_NULL_HANDLE) + { + maxGpuBytesToMove = 0; + maxGpuAllocationsToMove = 0; + } + + VkResult res = VK_SUCCESS; + + // Process default pools. + for(uint32_t memTypeIndex = 0; + memTypeIndex < m_hAllocator->GetMemoryTypeCount() && res >= VK_SUCCESS; + ++memTypeIndex) + { + VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex]; + if(pBlockVectorCtx) + { + VMA_ASSERT(pBlockVectorCtx->GetBlockVector()); + pBlockVectorCtx->GetBlockVector()->Defragment( + pBlockVectorCtx, + pStats, + maxCpuBytesToMove, maxCpuAllocationsToMove, + maxGpuBytesToMove, maxGpuAllocationsToMove, + commandBuffer); + if(pBlockVectorCtx->res != VK_SUCCESS) + { + res = pBlockVectorCtx->res; + } + } + } + + // Process custom pools. + for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size(); + customCtxIndex < customCtxCount && res >= VK_SUCCESS; + ++customCtxIndex) + { + VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex]; + VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector()); + pBlockVectorCtx->GetBlockVector()->Defragment( + pBlockVectorCtx, + pStats, + maxCpuBytesToMove, maxCpuAllocationsToMove, + maxGpuBytesToMove, maxGpuAllocationsToMove, + commandBuffer); + if(pBlockVectorCtx->res != VK_SUCCESS) + { + res = pBlockVectorCtx->res; + } + } + + return res; +} + +//////////////////////////////////////////////////////////////////////////////// +// VmaRecorder + +#if VMA_RECORDING_ENABLED + +VmaRecorder::VmaRecorder() : + m_UseMutex(true), + m_Flags(0), + m_File(VMA_NULL), + m_Freq(INT64_MAX), + m_StartCounter(INT64_MAX) +{ +} + +VkResult VmaRecorder::Init(const VmaRecordSettings& settings, bool useMutex) +{ + m_UseMutex = useMutex; + m_Flags = settings.flags; + + QueryPerformanceFrequency((LARGE_INTEGER*)&m_Freq); + QueryPerformanceCounter((LARGE_INTEGER*)&m_StartCounter); + + // Open file for writing. + errno_t err = fopen_s(&m_File, settings.pFilePath, "wb"); + if(err != 0) + { + return VK_ERROR_INITIALIZATION_FAILED; + } + + // Write header. + fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording"); + fprintf(m_File, "%s\n", "1,5"); + + return VK_SUCCESS; +} + +VmaRecorder::~VmaRecorder() +{ + if(m_File != VMA_NULL) + { + fclose(m_File); + } +} + +void VmaRecorder::RecordCreateAllocator(uint32_t frameIndex) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaCreateAllocator\n", callParams.threadId, callParams.time, frameIndex); + Flush(); +} + +void VmaRecorder::RecordDestroyAllocator(uint32_t frameIndex) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaDestroyAllocator\n", callParams.threadId, callParams.time, frameIndex); + Flush(); +} + +void VmaRecorder::RecordCreatePool(uint32_t frameIndex, const VmaPoolCreateInfo& createInfo, VmaPool pool) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaCreatePool,%u,%u,%llu,%llu,%llu,%u,%p\n", callParams.threadId, callParams.time, frameIndex, + createInfo.memoryTypeIndex, + createInfo.flags, + createInfo.blockSize, + (uint64_t)createInfo.minBlockCount, + (uint64_t)createInfo.maxBlockCount, + createInfo.frameInUseCount, + pool); + Flush(); +} + +void VmaRecorder::RecordDestroyPool(uint32_t frameIndex, VmaPool pool) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaDestroyPool,%p\n", callParams.threadId, callParams.time, frameIndex, + pool); + Flush(); +} + +void VmaRecorder::RecordAllocateMemory(uint32_t frameIndex, + const VkMemoryRequirements& vkMemReq, + const VmaAllocationCreateInfo& createInfo, + VmaAllocation allocation) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + UserDataString userDataStr(createInfo.flags, createInfo.pUserData); + fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemory,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex, + vkMemReq.size, + vkMemReq.alignment, + vkMemReq.memoryTypeBits, + createInfo.flags, + createInfo.usage, + createInfo.requiredFlags, + createInfo.preferredFlags, + createInfo.memoryTypeBits, + createInfo.pool, + allocation, + userDataStr.GetString()); + Flush(); +} + +void VmaRecorder::RecordAllocateMemoryPages(uint32_t frameIndex, + const VkMemoryRequirements& vkMemReq, + const VmaAllocationCreateInfo& createInfo, + uint64_t allocationCount, + const VmaAllocation* pAllocations) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + UserDataString userDataStr(createInfo.flags, createInfo.pUserData); + fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryPages,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,", callParams.threadId, callParams.time, frameIndex, + vkMemReq.size, + vkMemReq.alignment, + vkMemReq.memoryTypeBits, + createInfo.flags, + createInfo.usage, + createInfo.requiredFlags, + createInfo.preferredFlags, + createInfo.memoryTypeBits, + createInfo.pool); + PrintPointerList(allocationCount, pAllocations); + fprintf(m_File, ",%s\n", userDataStr.GetString()); + Flush(); +} + +void VmaRecorder::RecordAllocateMemoryForBuffer(uint32_t frameIndex, + const VkMemoryRequirements& vkMemReq, + bool requiresDedicatedAllocation, + bool prefersDedicatedAllocation, + const VmaAllocationCreateInfo& createInfo, + VmaAllocation allocation) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + UserDataString userDataStr(createInfo.flags, createInfo.pUserData); + fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryForBuffer,%llu,%llu,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex, + vkMemReq.size, + vkMemReq.alignment, + vkMemReq.memoryTypeBits, + requiresDedicatedAllocation ? 1 : 0, + prefersDedicatedAllocation ? 1 : 0, + createInfo.flags, + createInfo.usage, + createInfo.requiredFlags, + createInfo.preferredFlags, + createInfo.memoryTypeBits, + createInfo.pool, + allocation, + userDataStr.GetString()); + Flush(); +} + +void VmaRecorder::RecordAllocateMemoryForImage(uint32_t frameIndex, + const VkMemoryRequirements& vkMemReq, + bool requiresDedicatedAllocation, + bool prefersDedicatedAllocation, + const VmaAllocationCreateInfo& createInfo, + VmaAllocation allocation) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + UserDataString userDataStr(createInfo.flags, createInfo.pUserData); + fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryForImage,%llu,%llu,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex, + vkMemReq.size, + vkMemReq.alignment, + vkMemReq.memoryTypeBits, + requiresDedicatedAllocation ? 1 : 0, + prefersDedicatedAllocation ? 1 : 0, + createInfo.flags, + createInfo.usage, + createInfo.requiredFlags, + createInfo.preferredFlags, + createInfo.memoryTypeBits, + createInfo.pool, + allocation, + userDataStr.GetString()); + Flush(); +} + +void VmaRecorder::RecordFreeMemory(uint32_t frameIndex, + VmaAllocation allocation) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaFreeMemory,%p\n", callParams.threadId, callParams.time, frameIndex, + allocation); + Flush(); +} + +void VmaRecorder::RecordFreeMemoryPages(uint32_t frameIndex, + uint64_t allocationCount, + const VmaAllocation* pAllocations) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaFreeMemoryPages,", callParams.threadId, callParams.time, frameIndex); + PrintPointerList(allocationCount, pAllocations); + fprintf(m_File, "\n"); + Flush(); +} + +void VmaRecorder::RecordResizeAllocation( + uint32_t frameIndex, + VmaAllocation allocation, + VkDeviceSize newSize) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaResizeAllocation,%p,%llu\n", callParams.threadId, callParams.time, frameIndex, + allocation, newSize); + Flush(); +} + +void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex, + VmaAllocation allocation, + const void* pUserData) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + UserDataString userDataStr( + allocation->IsUserDataString() ? VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT : 0, + pUserData); + fprintf(m_File, "%u,%.3f,%u,vmaSetAllocationUserData,%p,%s\n", callParams.threadId, callParams.time, frameIndex, + allocation, + userDataStr.GetString()); + Flush(); +} + +void VmaRecorder::RecordCreateLostAllocation(uint32_t frameIndex, + VmaAllocation allocation) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaCreateLostAllocation,%p\n", callParams.threadId, callParams.time, frameIndex, + allocation); + Flush(); +} + +void VmaRecorder::RecordMapMemory(uint32_t frameIndex, + VmaAllocation allocation) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaMapMemory,%p\n", callParams.threadId, callParams.time, frameIndex, + allocation); + Flush(); +} + +void VmaRecorder::RecordUnmapMemory(uint32_t frameIndex, + VmaAllocation allocation) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaUnmapMemory,%p\n", callParams.threadId, callParams.time, frameIndex, + allocation); + Flush(); +} + +void VmaRecorder::RecordFlushAllocation(uint32_t frameIndex, + VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaFlushAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex, + allocation, + offset, + size); + Flush(); +} + +void VmaRecorder::RecordInvalidateAllocation(uint32_t frameIndex, + VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaInvalidateAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex, + allocation, + offset, + size); + Flush(); +} + +void VmaRecorder::RecordCreateBuffer(uint32_t frameIndex, + const VkBufferCreateInfo& bufCreateInfo, + const VmaAllocationCreateInfo& allocCreateInfo, + VmaAllocation allocation) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData); + fprintf(m_File, "%u,%.3f,%u,vmaCreateBuffer,%u,%llu,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex, + bufCreateInfo.flags, + bufCreateInfo.size, + bufCreateInfo.usage, + bufCreateInfo.sharingMode, + allocCreateInfo.flags, + allocCreateInfo.usage, + allocCreateInfo.requiredFlags, + allocCreateInfo.preferredFlags, + allocCreateInfo.memoryTypeBits, + allocCreateInfo.pool, + allocation, + userDataStr.GetString()); + Flush(); +} + +void VmaRecorder::RecordCreateImage(uint32_t frameIndex, + const VkImageCreateInfo& imageCreateInfo, + const VmaAllocationCreateInfo& allocCreateInfo, + VmaAllocation allocation) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData); + fprintf(m_File, "%u,%.3f,%u,vmaCreateImage,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex, + imageCreateInfo.flags, + imageCreateInfo.imageType, + imageCreateInfo.format, + imageCreateInfo.extent.width, + imageCreateInfo.extent.height, + imageCreateInfo.extent.depth, + imageCreateInfo.mipLevels, + imageCreateInfo.arrayLayers, + imageCreateInfo.samples, + imageCreateInfo.tiling, + imageCreateInfo.usage, + imageCreateInfo.sharingMode, + imageCreateInfo.initialLayout, + allocCreateInfo.flags, + allocCreateInfo.usage, + allocCreateInfo.requiredFlags, + allocCreateInfo.preferredFlags, + allocCreateInfo.memoryTypeBits, + allocCreateInfo.pool, + allocation, + userDataStr.GetString()); + Flush(); +} + +void VmaRecorder::RecordDestroyBuffer(uint32_t frameIndex, + VmaAllocation allocation) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaDestroyBuffer,%p\n", callParams.threadId, callParams.time, frameIndex, + allocation); + Flush(); +} + +void VmaRecorder::RecordDestroyImage(uint32_t frameIndex, + VmaAllocation allocation) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaDestroyImage,%p\n", callParams.threadId, callParams.time, frameIndex, + allocation); + Flush(); +} + +void VmaRecorder::RecordTouchAllocation(uint32_t frameIndex, + VmaAllocation allocation) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaTouchAllocation,%p\n", callParams.threadId, callParams.time, frameIndex, + allocation); + Flush(); +} + +void VmaRecorder::RecordGetAllocationInfo(uint32_t frameIndex, + VmaAllocation allocation) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaGetAllocationInfo,%p\n", callParams.threadId, callParams.time, frameIndex, + allocation); + Flush(); +} + +void VmaRecorder::RecordMakePoolAllocationsLost(uint32_t frameIndex, + VmaPool pool) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaMakePoolAllocationsLost,%p\n", callParams.threadId, callParams.time, frameIndex, + pool); + Flush(); +} + +void VmaRecorder::RecordDefragmentationBegin(uint32_t frameIndex, + const VmaDefragmentationInfo2& info, + VmaDefragmentationContext ctx) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationBegin,%u,", callParams.threadId, callParams.time, frameIndex, + info.flags); + PrintPointerList(info.allocationCount, info.pAllocations); + fprintf(m_File, ","); + PrintPointerList(info.poolCount, info.pPools); + fprintf(m_File, ",%llu,%u,%llu,%u,%p,%p\n", + info.maxCpuBytesToMove, + info.maxCpuAllocationsToMove, + info.maxGpuBytesToMove, + info.maxGpuAllocationsToMove, + info.commandBuffer, + ctx); + Flush(); +} + +void VmaRecorder::RecordDefragmentationEnd(uint32_t frameIndex, + VmaDefragmentationContext ctx) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationEnd,%p\n", callParams.threadId, callParams.time, frameIndex, + ctx); + Flush(); +} + +VmaRecorder::UserDataString::UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData) +{ + if(pUserData != VMA_NULL) + { + if((allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0) + { + m_Str = (const char*)pUserData; + } + else + { + sprintf_s(m_PtrStr, "%p", pUserData); + m_Str = m_PtrStr; + } + } + else + { + m_Str = ""; + } +} + +void VmaRecorder::WriteConfiguration( + const VkPhysicalDeviceProperties& devProps, + const VkPhysicalDeviceMemoryProperties& memProps, + bool dedicatedAllocationExtensionEnabled) +{ + fprintf(m_File, "Config,Begin\n"); + + fprintf(m_File, "PhysicalDevice,apiVersion,%u\n", devProps.apiVersion); + fprintf(m_File, "PhysicalDevice,driverVersion,%u\n", devProps.driverVersion); + fprintf(m_File, "PhysicalDevice,vendorID,%u\n", devProps.vendorID); + fprintf(m_File, "PhysicalDevice,deviceID,%u\n", devProps.deviceID); + fprintf(m_File, "PhysicalDevice,deviceType,%u\n", devProps.deviceType); + fprintf(m_File, "PhysicalDevice,deviceName,%s\n", devProps.deviceName); + + fprintf(m_File, "PhysicalDeviceLimits,maxMemoryAllocationCount,%u\n", devProps.limits.maxMemoryAllocationCount); + fprintf(m_File, "PhysicalDeviceLimits,bufferImageGranularity,%llu\n", devProps.limits.bufferImageGranularity); + fprintf(m_File, "PhysicalDeviceLimits,nonCoherentAtomSize,%llu\n", devProps.limits.nonCoherentAtomSize); + + fprintf(m_File, "PhysicalDeviceMemory,HeapCount,%u\n", memProps.memoryHeapCount); + for(uint32_t i = 0; i < memProps.memoryHeapCount; ++i) + { + fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,size,%llu\n", i, memProps.memoryHeaps[i].size); + fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,flags,%u\n", i, memProps.memoryHeaps[i].flags); + } + fprintf(m_File, "PhysicalDeviceMemory,TypeCount,%u\n", memProps.memoryTypeCount); + for(uint32_t i = 0; i < memProps.memoryTypeCount; ++i) + { + fprintf(m_File, "PhysicalDeviceMemory,Type,%u,heapIndex,%u\n", i, memProps.memoryTypes[i].heapIndex); + fprintf(m_File, "PhysicalDeviceMemory,Type,%u,propertyFlags,%u\n", i, memProps.memoryTypes[i].propertyFlags); + } + + fprintf(m_File, "Extension,VK_KHR_dedicated_allocation,%u\n", dedicatedAllocationExtensionEnabled ? 1 : 0); + + fprintf(m_File, "Macro,VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,%u\n", VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ? 1 : 0); + fprintf(m_File, "Macro,VMA_DEBUG_ALIGNMENT,%llu\n", (VkDeviceSize)VMA_DEBUG_ALIGNMENT); + fprintf(m_File, "Macro,VMA_DEBUG_MARGIN,%llu\n", (VkDeviceSize)VMA_DEBUG_MARGIN); + fprintf(m_File, "Macro,VMA_DEBUG_INITIALIZE_ALLOCATIONS,%u\n", VMA_DEBUG_INITIALIZE_ALLOCATIONS ? 1 : 0); + fprintf(m_File, "Macro,VMA_DEBUG_DETECT_CORRUPTION,%u\n", VMA_DEBUG_DETECT_CORRUPTION ? 1 : 0); + fprintf(m_File, "Macro,VMA_DEBUG_GLOBAL_MUTEX,%u\n", VMA_DEBUG_GLOBAL_MUTEX ? 1 : 0); + fprintf(m_File, "Macro,VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,%llu\n", (VkDeviceSize)VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY); + fprintf(m_File, "Macro,VMA_SMALL_HEAP_MAX_SIZE,%llu\n", (VkDeviceSize)VMA_SMALL_HEAP_MAX_SIZE); + fprintf(m_File, "Macro,VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,%llu\n", (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE); + + fprintf(m_File, "Config,End\n"); +} + +void VmaRecorder::GetBasicParams(CallParams& outParams) +{ + outParams.threadId = GetCurrentThreadId(); + + LARGE_INTEGER counter; + QueryPerformanceCounter(&counter); + outParams.time = (double)(counter.QuadPart - m_StartCounter) / (double)m_Freq; +} + +void VmaRecorder::PrintPointerList(uint64_t count, const VmaAllocation* pItems) +{ + if(count) + { + fprintf(m_File, "%p", pItems[0]); + for(uint64_t i = 1; i < count; ++i) + { + fprintf(m_File, " %p", pItems[i]); + } + } +} + +void VmaRecorder::Flush() +{ + if((m_Flags & VMA_RECORD_FLUSH_AFTER_CALL_BIT) != 0) + { + fflush(m_File); + } +} + +#endif // #if VMA_RECORDING_ENABLED + +//////////////////////////////////////////////////////////////////////////////// +// VmaAllocator_T + +VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) : + m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0), + m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0), + m_hDevice(pCreateInfo->device), + m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL), + m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ? + *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks), + m_PreferredLargeHeapBlockSize(0), + m_PhysicalDevice(pCreateInfo->physicalDevice), + m_CurrentFrameIndex(0), + m_Pools(VmaStlAllocator(GetAllocationCallbacks())), + m_NextPoolId(0) +#if VMA_RECORDING_ENABLED + ,m_pRecorder(VMA_NULL) +#endif +{ + if(VMA_DEBUG_DETECT_CORRUPTION) + { + // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it. + VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0); + } + + VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device); + +#if !(VMA_DEDICATED_ALLOCATION) + if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0) + { + VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros."); + } +#endif + + memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks)); + memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties)); + memset(&m_MemProps, 0, sizeof(m_MemProps)); + + memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors)); + memset(&m_pDedicatedAllocations, 0, sizeof(m_pDedicatedAllocations)); + + for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i) + { + m_HeapSizeLimit[i] = VK_WHOLE_SIZE; + } + + if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL) + { + m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate; + m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree; + } + + ImportVulkanFunctions(pCreateInfo->pVulkanFunctions); + + (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties); + (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps); + + VMA_ASSERT(VmaIsPow2(VMA_DEBUG_ALIGNMENT)); + VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY)); + VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity)); + VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize)); + + m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ? + pCreateInfo->preferredLargeHeapBlockSize : static_cast(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE); + + if(pCreateInfo->pHeapSizeLimit != VMA_NULL) + { + for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex) + { + const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex]; + if(limit != VK_WHOLE_SIZE) + { + m_HeapSizeLimit[heapIndex] = limit; + if(limit < m_MemProps.memoryHeaps[heapIndex].size) + { + m_MemProps.memoryHeaps[heapIndex].size = limit; + } + } + } + } + + for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) + { + const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex); + + m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)( + this, + memTypeIndex, + preferredBlockSize, + 0, + SIZE_MAX, + GetBufferImageGranularity(), + pCreateInfo->frameInUseCount, + false, // isCustomPool + false, // explicitBlockSize + false); // linearAlgorithm + // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here, + // becase minBlockCount is 0. + m_pDedicatedAllocations[memTypeIndex] = vma_new(this, AllocationVectorType)(VmaStlAllocator(GetAllocationCallbacks())); + + } +} + +VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo) +{ + VkResult res = VK_SUCCESS; + + if(pCreateInfo->pRecordSettings != VMA_NULL && + !VmaStrIsEmpty(pCreateInfo->pRecordSettings->pFilePath)) + { +#if VMA_RECORDING_ENABLED + m_pRecorder = vma_new(this, VmaRecorder)(); + res = m_pRecorder->Init(*pCreateInfo->pRecordSettings, m_UseMutex); + if(res != VK_SUCCESS) + { + return res; + } + m_pRecorder->WriteConfiguration( + m_PhysicalDeviceProperties, + m_MemProps, + m_UseKhrDedicatedAllocation); + m_pRecorder->RecordCreateAllocator(GetCurrentFrameIndex()); +#else + VMA_ASSERT(0 && "VmaAllocatorCreateInfo::pRecordSettings used, but not supported due to VMA_RECORDING_ENABLED not defined to 1."); + return VK_ERROR_FEATURE_NOT_PRESENT; +#endif + } + + return res; +} + +VmaAllocator_T::~VmaAllocator_T() +{ +#if VMA_RECORDING_ENABLED + if(m_pRecorder != VMA_NULL) + { + m_pRecorder->RecordDestroyAllocator(GetCurrentFrameIndex()); + vma_delete(this, m_pRecorder); + } +#endif + + VMA_ASSERT(m_Pools.empty()); + + for(size_t i = GetMemoryTypeCount(); i--; ) + { + vma_delete(this, m_pDedicatedAllocations[i]); + vma_delete(this, m_pBlockVectors[i]); + } +} + +void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions) +{ +#if VMA_STATIC_VULKAN_FUNCTIONS == 1 + m_VulkanFunctions.vkGetPhysicalDeviceProperties = &vkGetPhysicalDeviceProperties; + m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = &vkGetPhysicalDeviceMemoryProperties; + m_VulkanFunctions.vkAllocateMemory = &vkAllocateMemory; + m_VulkanFunctions.vkFreeMemory = &vkFreeMemory; + m_VulkanFunctions.vkMapMemory = &vkMapMemory; + m_VulkanFunctions.vkUnmapMemory = &vkUnmapMemory; + m_VulkanFunctions.vkFlushMappedMemoryRanges = &vkFlushMappedMemoryRanges; + m_VulkanFunctions.vkInvalidateMappedMemoryRanges = &vkInvalidateMappedMemoryRanges; + m_VulkanFunctions.vkBindBufferMemory = &vkBindBufferMemory; + m_VulkanFunctions.vkBindImageMemory = &vkBindImageMemory; + m_VulkanFunctions.vkGetBufferMemoryRequirements = &vkGetBufferMemoryRequirements; + m_VulkanFunctions.vkGetImageMemoryRequirements = &vkGetImageMemoryRequirements; + m_VulkanFunctions.vkCreateBuffer = &vkCreateBuffer; + m_VulkanFunctions.vkDestroyBuffer = &vkDestroyBuffer; + m_VulkanFunctions.vkCreateImage = &vkCreateImage; + m_VulkanFunctions.vkDestroyImage = &vkDestroyImage; + m_VulkanFunctions.vkCmdCopyBuffer = &vkCmdCopyBuffer; +#if VMA_DEDICATED_ALLOCATION + if(m_UseKhrDedicatedAllocation) + { + m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR = + (PFN_vkGetBufferMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetBufferMemoryRequirements2KHR"); + m_VulkanFunctions.vkGetImageMemoryRequirements2KHR = + (PFN_vkGetImageMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetImageMemoryRequirements2KHR"); + } +#endif // #if VMA_DEDICATED_ALLOCATION +#endif // #if VMA_STATIC_VULKAN_FUNCTIONS == 1 + +#define VMA_COPY_IF_NOT_NULL(funcName) \ + if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName; + + if(pVulkanFunctions != VMA_NULL) + { + VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties); + VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties); + VMA_COPY_IF_NOT_NULL(vkAllocateMemory); + VMA_COPY_IF_NOT_NULL(vkFreeMemory); + VMA_COPY_IF_NOT_NULL(vkMapMemory); + VMA_COPY_IF_NOT_NULL(vkUnmapMemory); + VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges); + VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges); + VMA_COPY_IF_NOT_NULL(vkBindBufferMemory); + VMA_COPY_IF_NOT_NULL(vkBindImageMemory); + VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements); + VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements); + VMA_COPY_IF_NOT_NULL(vkCreateBuffer); + VMA_COPY_IF_NOT_NULL(vkDestroyBuffer); + VMA_COPY_IF_NOT_NULL(vkCreateImage); + VMA_COPY_IF_NOT_NULL(vkDestroyImage); + VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer); +#if VMA_DEDICATED_ALLOCATION + VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR); + VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR); +#endif + } + +#undef VMA_COPY_IF_NOT_NULL + + // If these asserts are hit, you must either #define VMA_STATIC_VULKAN_FUNCTIONS 1 + // or pass valid pointers as VmaAllocatorCreateInfo::pVulkanFunctions. + VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL); +#if VMA_DEDICATED_ALLOCATION + if(m_UseKhrDedicatedAllocation) + { + VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL); + } +#endif +} + +VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex) +{ + const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex); + const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size; + const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE; + return isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize; +} + +VkResult VmaAllocator_T::AllocateMemoryOfType( + VkDeviceSize size, + VkDeviceSize alignment, + bool dedicatedAllocation, + VkBuffer dedicatedBuffer, + VkImage dedicatedImage, + const VmaAllocationCreateInfo& createInfo, + uint32_t memTypeIndex, + VmaSuballocationType suballocType, + size_t allocationCount, + VmaAllocation* pAllocations) +{ + VMA_ASSERT(pAllocations != VMA_NULL); + VMA_DEBUG_LOG(" AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, vkMemReq.size); + + VmaAllocationCreateInfo finalCreateInfo = createInfo; + + // If memory type is not HOST_VISIBLE, disable MAPPED. + if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 && + (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) + { + finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT; + } + + VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex]; + VMA_ASSERT(blockVector); + + const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize(); + bool preferDedicatedMemory = + VMA_DEBUG_ALWAYS_DEDICATED_MEMORY || + dedicatedAllocation || + // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size. + size > preferredBlockSize / 2; + + if(preferDedicatedMemory && + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 && + finalCreateInfo.pool == VK_NULL_HANDLE) + { + finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; + } + + if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0) + { + if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0) + { + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + } + else + { + return AllocateDedicatedMemory( + size, + suballocType, + memTypeIndex, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0, + finalCreateInfo.pUserData, + dedicatedBuffer, + dedicatedImage, + allocationCount, + pAllocations); + } + } + else + { + VkResult res = blockVector->Allocate( + VK_NULL_HANDLE, // hCurrentPool + m_CurrentFrameIndex.load(), + size, + alignment, + finalCreateInfo, + suballocType, + allocationCount, + pAllocations); + if(res == VK_SUCCESS) + { + return res; + } + + // 5. Try dedicated memory. + if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0) + { + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + } + else + { + res = AllocateDedicatedMemory( + size, + suballocType, + memTypeIndex, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0, + finalCreateInfo.pUserData, + dedicatedBuffer, + dedicatedImage, + allocationCount, + pAllocations); + if(res == VK_SUCCESS) + { + // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here. + VMA_DEBUG_LOG(" Allocated as DedicatedMemory"); + return VK_SUCCESS; + } + else + { + // Everything failed: Return error code. + VMA_DEBUG_LOG(" vkAllocateMemory FAILED"); + return res; + } + } + } +} + +VkResult VmaAllocator_T::AllocateDedicatedMemory( + VkDeviceSize size, + VmaSuballocationType suballocType, + uint32_t memTypeIndex, + bool map, + bool isUserDataString, + void* pUserData, + VkBuffer /*dedicatedBuffer*/, + VkImage /*dedicatedImage*/, + size_t allocationCount, + VmaAllocation* pAllocations) +{ + VMA_ASSERT(allocationCount > 0 && pAllocations); + + VkMemoryAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocInfo.memoryTypeIndex = memTypeIndex; + allocInfo.allocationSize = size; + +#if VMA_DEDICATED_ALLOCATION + VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = {}; + dedicatedAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR; + if(m_UseKhrDedicatedAllocation) + { + if(dedicatedBuffer != VK_NULL_HANDLE) + { + VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE); + dedicatedAllocInfo.buffer = dedicatedBuffer; + allocInfo.pNext = &dedicatedAllocInfo; + } + else if(dedicatedImage != VK_NULL_HANDLE) + { + dedicatedAllocInfo.image = dedicatedImage; + allocInfo.pNext = &dedicatedAllocInfo; + } + } +#endif // #if VMA_DEDICATED_ALLOCATION + + size_t allocIndex; + VkResult res = VK_SUCCESS; + for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex) + { + res = AllocateDedicatedMemoryPage( + size, + suballocType, + memTypeIndex, + allocInfo, + map, + isUserDataString, + pUserData, + pAllocations + allocIndex); + if(res != VK_SUCCESS) + { + break; + } + } + + if(res == VK_SUCCESS) + { + // Register them in m_pDedicatedAllocations. + { + VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex); + AllocationVectorType* pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex]; + VMA_ASSERT(pDedicatedAllocations); + for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex) + { + VmaVectorInsertSorted(*pDedicatedAllocations, pAllocations[allocIndex]); + } + } + + VMA_DEBUG_LOG(" Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex); + } + else + { + // Free all already created allocations. + while(allocIndex--) + { + VmaAllocation currAlloc = pAllocations[allocIndex]; + VkDeviceMemory hMemory = currAlloc->GetMemory(); + + /* + There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory + before vkFreeMemory. + + if(currAlloc->GetMappedData() != VMA_NULL) + { + (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory); + } + */ + + FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory); + + currAlloc->SetUserData(this, VMA_NULL); + vma_delete(this, currAlloc); + } + + memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount); + } + + return res; +} + +VkResult VmaAllocator_T::AllocateDedicatedMemoryPage( + VkDeviceSize size, + VmaSuballocationType suballocType, + uint32_t memTypeIndex, + const VkMemoryAllocateInfo& allocInfo, + bool map, + bool isUserDataString, + void* pUserData, + VmaAllocation* pAllocation) +{ + VkDeviceMemory hMemory = VK_NULL_HANDLE; + VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory); + if(res < 0) + { + VMA_DEBUG_LOG(" vkAllocateMemory FAILED"); + return res; + } + + void* pMappedData = VMA_NULL; + if(map) + { + res = (*m_VulkanFunctions.vkMapMemory)( + m_hDevice, + hMemory, + 0, + VK_WHOLE_SIZE, + 0, + &pMappedData); + if(res < 0) + { + VMA_DEBUG_LOG(" vkMapMemory FAILED"); + FreeVulkanMemory(memTypeIndex, size, hMemory); + return res; + } + } + + *pAllocation = vma_new(this, VmaAllocation_T)(m_CurrentFrameIndex.load(), isUserDataString); + (*pAllocation)->InitDedicatedAllocation(memTypeIndex, hMemory, suballocType, pMappedData, size); + (*pAllocation)->SetUserData(this, pUserData); + if(VMA_DEBUG_INITIALIZE_ALLOCATIONS) + { + FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED); + } + + return VK_SUCCESS; +} + +void VmaAllocator_T::GetBufferMemoryRequirements( + VkBuffer hBuffer, + VkMemoryRequirements& memReq, + bool& requiresDedicatedAllocation, + bool& prefersDedicatedAllocation) const +{ +#if VMA_DEDICATED_ALLOCATION + if(m_UseKhrDedicatedAllocation) + { + VkBufferMemoryRequirementsInfo2KHR memReqInfo = {}; + memReqInfo.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR; + memReqInfo.buffer = hBuffer; + + VkMemoryDedicatedRequirementsKHR memDedicatedReq = {}; + memDedicatedReq.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR; + + VkMemoryRequirements2KHR memReq2 = {}; + memReq2.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR; + memReq2.pNext = &memDedicatedReq; + + (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2); + + memReq = memReq2.memoryRequirements; + requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE); + prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE); + } + else +#endif // #if VMA_DEDICATED_ALLOCATION + { + (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq); + requiresDedicatedAllocation = false; + prefersDedicatedAllocation = false; + } +} + +void VmaAllocator_T::GetImageMemoryRequirements( + VkImage hImage, + VkMemoryRequirements& memReq, + bool& requiresDedicatedAllocation, + bool& prefersDedicatedAllocation) const +{ +#if VMA_DEDICATED_ALLOCATION + if(m_UseKhrDedicatedAllocation) + { + VkImageMemoryRequirementsInfo2KHR memReqInfo = {}; + memReqInfo.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR; + memReqInfo.image = hImage; + + VkMemoryDedicatedRequirementsKHR memDedicatedReq = {}; + memDedicatedReq.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR; + + VkMemoryRequirements2KHR memReq2 = {}; + memReq2.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR; + memReq2.pNext = &memDedicatedReq; + + (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2); + + memReq = memReq2.memoryRequirements; + requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE); + prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE); + } + else +#endif // #if VMA_DEDICATED_ALLOCATION + { + (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq); + requiresDedicatedAllocation = false; + prefersDedicatedAllocation = false; + } +} + +VkResult VmaAllocator_T::AllocateMemory( + const VkMemoryRequirements& vkMemReq, + bool requiresDedicatedAllocation, + bool prefersDedicatedAllocation, + VkBuffer dedicatedBuffer, + VkImage dedicatedImage, + const VmaAllocationCreateInfo& createInfo, + VmaSuballocationType suballocType, + size_t allocationCount, + VmaAllocation* pAllocations) +{ + memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount); + + VMA_ASSERT(VmaIsPow2(vkMemReq.alignment)); + + if(vkMemReq.size == 0) + { + return VK_ERROR_VALIDATION_FAILED_EXT; + } + if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 && + (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0) + { + VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense."); + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + } + if((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 && + (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0) + { + VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid."); + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + } + if(requiresDedicatedAllocation) + { + if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0) + { + VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required."); + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + } + if(createInfo.pool != VK_NULL_HANDLE) + { + VMA_ASSERT(0 && "Pool specified while dedicated allocation is required."); + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + } + } + if((createInfo.pool != VK_NULL_HANDLE) && + ((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0)) + { + VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid."); + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + } + + if(createInfo.pool != VK_NULL_HANDLE) + { + const VkDeviceSize alignmentForPool = VMA_MAX( + vkMemReq.alignment, + GetMemoryTypeMinAlignment(createInfo.pool->m_BlockVector.GetMemoryTypeIndex())); + return createInfo.pool->m_BlockVector.Allocate( + createInfo.pool, + m_CurrentFrameIndex.load(), + vkMemReq.size, + alignmentForPool, + createInfo, + suballocType, + allocationCount, + pAllocations); + } + else + { + // Bit mask of memory Vulkan types acceptable for this allocation. + uint32_t memoryTypeBits = vkMemReq.memoryTypeBits; + uint32_t memTypeIndex = UINT32_MAX; + VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex); + if(res == VK_SUCCESS) + { + VkDeviceSize alignmentForMemType = VMA_MAX( + vkMemReq.alignment, + GetMemoryTypeMinAlignment(memTypeIndex)); + + res = AllocateMemoryOfType( + vkMemReq.size, + alignmentForMemType, + requiresDedicatedAllocation || prefersDedicatedAllocation, + dedicatedBuffer, + dedicatedImage, + createInfo, + memTypeIndex, + suballocType, + allocationCount, + pAllocations); + // Succeeded on first try. + if(res == VK_SUCCESS) + { + return res; + } + // Allocation from this memory type failed. Try other compatible memory types. + else + { + for(;;) + { + // Remove old memTypeIndex from list of possibilities. + memoryTypeBits &= ~(1u << memTypeIndex); + // Find alternative memTypeIndex. + res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex); + if(res == VK_SUCCESS) + { + alignmentForMemType = VMA_MAX( + vkMemReq.alignment, + GetMemoryTypeMinAlignment(memTypeIndex)); + + res = AllocateMemoryOfType( + vkMemReq.size, + alignmentForMemType, + requiresDedicatedAllocation || prefersDedicatedAllocation, + dedicatedBuffer, + dedicatedImage, + createInfo, + memTypeIndex, + suballocType, + allocationCount, + pAllocations); + // Allocation from this alternative memory type succeeded. + if(res == VK_SUCCESS) + { + return res; + } + // else: Allocation from this memory type failed. Try next one - next loop iteration. + } + // No other matching memory type index could be found. + else + { + // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once. + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + } + } + } + } + // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT. + else + return res; + } +} + +void VmaAllocator_T::FreeMemory( + size_t allocationCount, + const VmaAllocation* pAllocations) +{ + VMA_ASSERT(pAllocations); + + for(size_t allocIndex = allocationCount; allocIndex--; ) + { + VmaAllocation allocation = pAllocations[allocIndex]; + + if(allocation != VK_NULL_HANDLE) + { + if(TouchAllocation(allocation)) + { + if(VMA_DEBUG_INITIALIZE_ALLOCATIONS) + { + FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED); + } + + switch(allocation->GetType()) + { + case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: + { + VmaBlockVector* pBlockVector = VMA_NULL; + VmaPool hPool = allocation->GetPool(); + if(hPool != VK_NULL_HANDLE) + { + pBlockVector = &hPool->m_BlockVector; + } + else + { + const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex(); + pBlockVector = m_pBlockVectors[memTypeIndex]; + } + pBlockVector->Free(allocation); + } + break; + case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: + FreeDedicatedMemory(allocation); + break; + default: + VMA_ASSERT(0); + } + } + + allocation->SetUserData(this, VMA_NULL); + vma_delete(this, allocation); + } + } +} + +VkResult VmaAllocator_T::ResizeAllocation( + const VmaAllocation alloc, + VkDeviceSize newSize) +{ + if(newSize == 0 || alloc->GetLastUseFrameIndex() == VMA_FRAME_INDEX_LOST) + { + return VK_ERROR_VALIDATION_FAILED_EXT; + } + if(newSize == alloc->GetSize()) + { + return VK_SUCCESS; + } + + switch(alloc->GetType()) + { + case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: + return VK_ERROR_FEATURE_NOT_PRESENT; + case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: + if(alloc->GetBlock()->m_pMetadata->ResizeAllocation(alloc, newSize)) + { + alloc->ChangeSize(newSize); + VMA_HEAVY_ASSERT(alloc->GetBlock()->m_pMetadata->Validate()); + return VK_SUCCESS; + } + else + { + return VkResult(-1000069000); // VK_ERROR_OUT_OF_POOL_MEMORY + } + default: + VMA_ASSERT(0); + return VK_ERROR_VALIDATION_FAILED_EXT; + } +} + +void VmaAllocator_T::CalculateStats(VmaStats* pStats) +{ + // Initialize. + InitStatInfo(pStats->total); + for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i) + InitStatInfo(pStats->memoryType[i]); + for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i) + InitStatInfo(pStats->memoryHeap[i]); + + // Process default pools. + for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) + { + VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex]; + VMA_ASSERT(pBlockVector); + pBlockVector->AddStats(pStats); + } + + // Process custom pools. + { + VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex); + for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex) + { + m_Pools[poolIndex]->m_BlockVector.AddStats(pStats); + } + } + + // Process dedicated allocations. + for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) + { + const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex); + VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex); + AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex]; + VMA_ASSERT(pDedicatedAllocVector); + for(size_t allocIndex = 0, allocCount = pDedicatedAllocVector->size(); allocIndex < allocCount; ++allocIndex) + { + VmaStatInfo allocationStatInfo; + (*pDedicatedAllocVector)[allocIndex]->DedicatedAllocCalcStatsInfo(allocationStatInfo); + VmaAddStatInfo(pStats->total, allocationStatInfo); + VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo); + VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo); + } + } + + // Postprocess. + VmaPostprocessCalcStatInfo(pStats->total); + for(size_t i = 0; i < GetMemoryTypeCount(); ++i) + VmaPostprocessCalcStatInfo(pStats->memoryType[i]); + for(size_t i = 0; i < GetMemoryHeapCount(); ++i) + VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]); +} + +static const uint32_t VMA_VENDOR_ID_AMD = 4098; + +VkResult VmaAllocator_T::DefragmentationBegin( + const VmaDefragmentationInfo2& info, + VmaDefragmentationStats* pStats, + VmaDefragmentationContext* pContext) +{ + if(info.pAllocationsChanged != VMA_NULL) + { + memset(info.pAllocationsChanged, 0, info.allocationCount * sizeof(VkBool32)); + } + + *pContext = vma_new(this, VmaDefragmentationContext_T)( + this, m_CurrentFrameIndex.load(), info.flags, pStats); + + (*pContext)->AddPools(info.poolCount, info.pPools); + (*pContext)->AddAllocations( + info.allocationCount, info.pAllocations, info.pAllocationsChanged); + + VkResult res = (*pContext)->Defragment( + info.maxCpuBytesToMove, info.maxCpuAllocationsToMove, + info.maxGpuBytesToMove, info.maxGpuAllocationsToMove, + info.commandBuffer, pStats); + + if(res != VK_NOT_READY) + { + vma_delete(this, *pContext); + *pContext = VMA_NULL; + } + + return res; +} + +VkResult VmaAllocator_T::DefragmentationEnd( + VmaDefragmentationContext context) +{ + vma_delete(this, context); + return VK_SUCCESS; +} + +void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo) +{ + if(hAllocation->CanBecomeLost()) + { + /* + Warning: This is a carefully designed algorithm. + Do not modify unless you really know what you're doing :) + */ + const uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load(); + uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex(); + for(;;) + { + if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST) + { + pAllocationInfo->memoryType = UINT32_MAX; + pAllocationInfo->deviceMemory = VK_NULL_HANDLE; + pAllocationInfo->offset = 0; + pAllocationInfo->size = hAllocation->GetSize(); + pAllocationInfo->pMappedData = VMA_NULL; + pAllocationInfo->pUserData = hAllocation->GetUserData(); + return; + } + else if(localLastUseFrameIndex == localCurrFrameIndex) + { + pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex(); + pAllocationInfo->deviceMemory = hAllocation->GetMemory(); + pAllocationInfo->offset = hAllocation->GetOffset(); + pAllocationInfo->size = hAllocation->GetSize(); + pAllocationInfo->pMappedData = VMA_NULL; + pAllocationInfo->pUserData = hAllocation->GetUserData(); + return; + } + else // Last use time earlier than current time. + { + if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex)) + { + localLastUseFrameIndex = localCurrFrameIndex; + } + } + } + } + else + { +#if VMA_STATS_STRING_ENABLED + uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load(); + uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex(); + for(;;) + { + VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST); + if(localLastUseFrameIndex == localCurrFrameIndex) + { + break; + } + else // Last use time earlier than current time. + { + if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex)) + { + localLastUseFrameIndex = localCurrFrameIndex; + } + } + } +#endif + + pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex(); + pAllocationInfo->deviceMemory = hAllocation->GetMemory(); + pAllocationInfo->offset = hAllocation->GetOffset(); + pAllocationInfo->size = hAllocation->GetSize(); + pAllocationInfo->pMappedData = hAllocation->GetMappedData(); + pAllocationInfo->pUserData = hAllocation->GetUserData(); + } +} + +bool VmaAllocator_T::TouchAllocation(VmaAllocation hAllocation) +{ + // This is a stripped-down version of VmaAllocator_T::GetAllocationInfo. + if(hAllocation->CanBecomeLost()) + { + uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load(); + uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex(); + for(;;) + { + if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST) + { + return false; + } + else if(localLastUseFrameIndex == localCurrFrameIndex) + { + return true; + } + else // Last use time earlier than current time. + { + if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex)) + { + localLastUseFrameIndex = localCurrFrameIndex; + } + } + } + } + else + { +#if VMA_STATS_STRING_ENABLED + uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load(); + uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex(); + for(;;) + { + VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST); + if(localLastUseFrameIndex == localCurrFrameIndex) + { + break; + } + else // Last use time earlier than current time. + { + if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex)) + { + localLastUseFrameIndex = localCurrFrameIndex; + } + } + } +#endif + + return true; + } +} + +VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool) +{ + VMA_DEBUG_LOG(" CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags); + + VmaPoolCreateInfo newCreateInfo = *pCreateInfo; + + if(newCreateInfo.maxBlockCount == 0) + { + newCreateInfo.maxBlockCount = SIZE_MAX; + } + if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount) + { + return VK_ERROR_INITIALIZATION_FAILED; + } + + const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex); + + *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize); + + VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks(); + if(res != VK_SUCCESS) + { + vma_delete(this, *pPool); + *pPool = VMA_NULL; + return res; + } + + // Add to m_Pools. + { + VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex); + (*pPool)->SetId(m_NextPoolId++); + VmaVectorInsertSorted(m_Pools, *pPool); + } + + return VK_SUCCESS; +} + +void VmaAllocator_T::DestroyPool(VmaPool pool) +{ + // Remove from m_Pools. + { + VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex); + bool success = VmaVectorRemoveSorted(m_Pools, pool); + (void) success; + VMA_ASSERT(success && "Pool not found in Allocator."); + } + + vma_delete(this, pool); +} + +void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats) +{ + pool->m_BlockVector.GetPoolStats(pPoolStats); +} + +void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex) +{ + m_CurrentFrameIndex.store(frameIndex); +} + +void VmaAllocator_T::MakePoolAllocationsLost( + VmaPool hPool, + size_t* pLostAllocationCount) +{ + hPool->m_BlockVector.MakePoolAllocationsLost( + m_CurrentFrameIndex.load(), + pLostAllocationCount); +} + +VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool) +{ + return hPool->m_BlockVector.CheckCorruption(); +} + +VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits) +{ + VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT; + + // Process default pools. + for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) + { + if(((1u << memTypeIndex) & memoryTypeBits) != 0) + { + VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex]; + VMA_ASSERT(pBlockVector); + VkResult localRes = pBlockVector->CheckCorruption(); + switch(localRes) + { + case VK_ERROR_FEATURE_NOT_PRESENT: + break; + case VK_SUCCESS: + finalRes = VK_SUCCESS; + break; + default: + return localRes; + } + } + } + + // Process custom pools. + { + VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex); + for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex) + { + if(((1u << m_Pools[poolIndex]->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0) + { + VkResult localRes = m_Pools[poolIndex]->m_BlockVector.CheckCorruption(); + switch(localRes) + { + case VK_ERROR_FEATURE_NOT_PRESENT: + break; + case VK_SUCCESS: + finalRes = VK_SUCCESS; + break; + default: + return localRes; + } + } + } + } + + return finalRes; +} + +void VmaAllocator_T::CreateLostAllocation(VmaAllocation* pAllocation) +{ + *pAllocation = vma_new(this, VmaAllocation_T)(VMA_FRAME_INDEX_LOST, false); + (*pAllocation)->InitLost(); +} + +VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory) +{ + const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex); + + VkResult res; + if(m_HeapSizeLimit[heapIndex] != VK_WHOLE_SIZE) + { + VmaMutexLock lock(m_HeapSizeLimitMutex, m_UseMutex); + if(m_HeapSizeLimit[heapIndex] >= pAllocateInfo->allocationSize) + { + res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory); + if(res == VK_SUCCESS) + { + m_HeapSizeLimit[heapIndex] -= pAllocateInfo->allocationSize; + } + } + else + { + res = VK_ERROR_OUT_OF_DEVICE_MEMORY; + } + } + else + { + res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory); + } + + if(res == VK_SUCCESS && m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL) + { + (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize); + } + + return res; +} + +void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory) +{ + if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL) + { + (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size); + } + + (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks()); + + const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memoryType); + if(m_HeapSizeLimit[heapIndex] != VK_WHOLE_SIZE) + { + VmaMutexLock lock(m_HeapSizeLimitMutex, m_UseMutex); + m_HeapSizeLimit[heapIndex] += size; + } +} + +VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData) +{ + if(hAllocation->CanBecomeLost()) + { + return VK_ERROR_MEMORY_MAP_FAILED; + } + + switch(hAllocation->GetType()) + { + case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: + { + VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock(); + char *pBytes = VMA_NULL; + VkResult res = pBlock->Map(this, 1, (void**)&pBytes); + if(res == VK_SUCCESS) + { + *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset(); + hAllocation->BlockAllocMap(); + } + return res; + } + case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: + return hAllocation->DedicatedAllocMap(this, ppData); + default: + VMA_ASSERT(0); + return VK_ERROR_MEMORY_MAP_FAILED; + } +} + +void VmaAllocator_T::Unmap(VmaAllocation hAllocation) +{ + switch(hAllocation->GetType()) + { + case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: + { + VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock(); + hAllocation->BlockAllocUnmap(); + pBlock->Unmap(this, 1); + } + break; + case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: + hAllocation->DedicatedAllocUnmap(this); + break; + default: + VMA_ASSERT(0); + } +} + +VkResult VmaAllocator_T::BindBufferMemory(VmaAllocation hAllocation, VkBuffer hBuffer) +{ + VkResult res = VK_SUCCESS; + switch(hAllocation->GetType()) + { + case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: + res = GetVulkanFunctions().vkBindBufferMemory( + m_hDevice, + hBuffer, + hAllocation->GetMemory(), + 0); //memoryOffset + break; + case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: + { + VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock(); + VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block. Is the allocation lost?"); + res = pBlock->BindBufferMemory(this, hAllocation, hBuffer); + break; + } + default: + VMA_ASSERT(0); + } + return res; +} + +VkResult VmaAllocator_T::BindImageMemory(VmaAllocation hAllocation, VkImage hImage) +{ + VkResult res = VK_SUCCESS; + switch(hAllocation->GetType()) + { + case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: + res = GetVulkanFunctions().vkBindImageMemory( + m_hDevice, + hImage, + hAllocation->GetMemory(), + 0); //memoryOffset + break; + case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: + { + VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock(); + VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block. Is the allocation lost?"); + res = pBlock->BindImageMemory(this, hAllocation, hImage); + break; + } + default: + VMA_ASSERT(0); + } + return res; +} + +void VmaAllocator_T::FlushOrInvalidateAllocation( + VmaAllocation hAllocation, + VkDeviceSize offset, VkDeviceSize size, + VMA_CACHE_OPERATION op) +{ + const uint32_t memTypeIndex = hAllocation->GetMemoryTypeIndex(); + if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex)) + { + const VkDeviceSize allocationSize = hAllocation->GetSize(); + VMA_ASSERT(offset <= allocationSize); + + const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize; + + VkMappedMemoryRange memRange = {}; + memRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + memRange.memory = hAllocation->GetMemory(); + + switch(hAllocation->GetType()) + { + case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: + memRange.offset = VmaAlignDown(offset, nonCoherentAtomSize); + if(size == VK_WHOLE_SIZE) + { + memRange.size = allocationSize - memRange.offset; + } + else + { + VMA_ASSERT(offset + size <= allocationSize); + memRange.size = VMA_MIN( + VmaAlignUp(size + (offset - memRange.offset), nonCoherentAtomSize), + allocationSize - memRange.offset); + } + break; + + case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: + { + // 1. Still within this allocation. + memRange.offset = VmaAlignDown(offset, nonCoherentAtomSize); + if(size == VK_WHOLE_SIZE) + { + size = allocationSize - offset; + } + else + { + VMA_ASSERT(offset + size <= allocationSize); + } + memRange.size = VmaAlignUp(size + (offset - memRange.offset), nonCoherentAtomSize); + + // 2. Adjust to whole block. + const VkDeviceSize allocationOffset = hAllocation->GetOffset(); + VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0); + const VkDeviceSize blockSize = hAllocation->GetBlock()->m_pMetadata->GetSize(); + memRange.offset += allocationOffset; + memRange.size = VMA_MIN(memRange.size, blockSize - memRange.offset); + + break; + } + + default: + VMA_ASSERT(0); + } + + switch(op) + { + case VMA_CACHE_FLUSH: + (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange); + break; + case VMA_CACHE_INVALIDATE: + (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange); + break; + default: + VMA_ASSERT(0); + } + } + // else: Just ignore this call. +} + +void VmaAllocator_T::FreeDedicatedMemory(VmaAllocation allocation) +{ + VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED); + + const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex(); + { + VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex); + AllocationVectorType* const pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex]; + VMA_ASSERT(pDedicatedAllocations); + bool success = VmaVectorRemoveSorted(*pDedicatedAllocations, allocation); + (void) success; + VMA_ASSERT(success); + } + + VkDeviceMemory hMemory = allocation->GetMemory(); + + /* + There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory + before vkFreeMemory. + + if(allocation->GetMappedData() != VMA_NULL) + { + (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory); + } + */ + + FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory); + + VMA_DEBUG_LOG(" Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex); +} + +void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern) +{ + if(VMA_DEBUG_INITIALIZE_ALLOCATIONS && + !hAllocation->CanBecomeLost() && + (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0) + { + void* pData = VMA_NULL; + VkResult res = Map(hAllocation, &pData); + if(res == VK_SUCCESS) + { + memset(pData, (int)pattern, (size_t)hAllocation->GetSize()); + FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH); + Unmap(hAllocation); + } + else + { + VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation."); + } + } +} + +#if VMA_STATS_STRING_ENABLED + +void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json) +{ + bool dedicatedAllocationsStarted = false; + for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) + { + VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex); + AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex]; + VMA_ASSERT(pDedicatedAllocVector); + if(pDedicatedAllocVector->empty() == false) + { + if(dedicatedAllocationsStarted == false) + { + dedicatedAllocationsStarted = true; + json.WriteString("DedicatedAllocations"); + json.BeginObject(); + } + + json.BeginString("Type "); + json.ContinueString(memTypeIndex); + json.EndString(); + + json.BeginArray(); + + for(size_t i = 0; i < pDedicatedAllocVector->size(); ++i) + { + json.BeginObject(true); + const VmaAllocation hAlloc = (*pDedicatedAllocVector)[i]; + hAlloc->PrintParameters(json); + json.EndObject(); + } + + json.EndArray(); + } + } + if(dedicatedAllocationsStarted) + { + json.EndObject(); + } + + { + bool allocationsStarted = false; + for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) + { + if(m_pBlockVectors[memTypeIndex]->IsEmpty() == false) + { + if(allocationsStarted == false) + { + allocationsStarted = true; + json.WriteString("DefaultPools"); + json.BeginObject(); + } + + json.BeginString("Type "); + json.ContinueString(memTypeIndex); + json.EndString(); + + m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json); + } + } + if(allocationsStarted) + { + json.EndObject(); + } + } + + // Custom pools + { + VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex); + const size_t poolCount = m_Pools.size(); + if(poolCount > 0) + { + json.WriteString("Pools"); + json.BeginObject(); + for(size_t poolIndex = 0; poolIndex < poolCount; ++poolIndex) + { + json.BeginString(); + json.ContinueString(m_Pools[poolIndex]->GetId()); + json.EndString(); + + m_Pools[poolIndex]->m_BlockVector.PrintDetailedMap(json); + } + json.EndObject(); + } + } +} + +#endif // #if VMA_STATS_STRING_ENABLED + +//////////////////////////////////////////////////////////////////////////////// +// Public interface + +VkResult vmaCreateAllocator( + const VmaAllocatorCreateInfo* pCreateInfo, + VmaAllocator* pAllocator) +{ + VMA_ASSERT(pCreateInfo && pAllocator); + VMA_DEBUG_LOG("vmaCreateAllocator"); + *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo); + return (*pAllocator)->Init(pCreateInfo); +} + +void vmaDestroyAllocator( + VmaAllocator allocator) +{ + if(allocator != VK_NULL_HANDLE) + { + VMA_DEBUG_LOG("vmaDestroyAllocator"); + VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks; + vma_delete(&allocationCallbacks, allocator); + } +} + +void vmaGetPhysicalDeviceProperties( + VmaAllocator allocator, + const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties) +{ + VMA_ASSERT(allocator && ppPhysicalDeviceProperties); + *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties; +} + +void vmaGetMemoryProperties( + VmaAllocator allocator, + const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties) +{ + VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties); + *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps; +} + +void vmaGetMemoryTypeProperties( + VmaAllocator allocator, + uint32_t memoryTypeIndex, + VkMemoryPropertyFlags* pFlags) +{ + VMA_ASSERT(allocator && pFlags); + VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount()); + *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags; +} + +void vmaSetCurrentFrameIndex( + VmaAllocator allocator, + uint32_t frameIndex) +{ + VMA_ASSERT(allocator); + VMA_ASSERT(frameIndex != VMA_FRAME_INDEX_LOST); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocator->SetCurrentFrameIndex(frameIndex); +} + +void vmaCalculateStats( + VmaAllocator allocator, + VmaStats* pStats) +{ + VMA_ASSERT(allocator && pStats); + VMA_DEBUG_GLOBAL_MUTEX_LOCK + allocator->CalculateStats(pStats); +} + +#if VMA_STATS_STRING_ENABLED + +void vmaBuildStatsString( + VmaAllocator allocator, + char** ppStatsString, + VkBool32 detailedMap) +{ + VMA_ASSERT(allocator && ppStatsString); + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + VmaStringBuilder sb(allocator); + { + VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb); + json.BeginObject(); + + VmaStats stats; + allocator->CalculateStats(&stats); + + json.WriteString("Total"); + VmaPrintStatInfo(json, stats.total); + + for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex) + { + json.BeginString("Heap "); + json.ContinueString(heapIndex); + json.EndString(); + json.BeginObject(); + + json.WriteString("Size"); + json.WriteNumber(allocator->m_MemProps.memoryHeaps[heapIndex].size); + + json.WriteString("Flags"); + json.BeginArray(true); + if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0) + { + json.WriteString("DEVICE_LOCAL"); + } + json.EndArray(); + + if(stats.memoryHeap[heapIndex].blockCount > 0) + { + json.WriteString("Stats"); + VmaPrintStatInfo(json, stats.memoryHeap[heapIndex]); + } + + for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex) + { + if(allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex) + { + json.BeginString("Type "); + json.ContinueString(typeIndex); + json.EndString(); + + json.BeginObject(); + + json.WriteString("Flags"); + json.BeginArray(true); + VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags; + if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0) + { + json.WriteString("DEVICE_LOCAL"); + } + if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0) + { + json.WriteString("HOST_VISIBLE"); + } + if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0) + { + json.WriteString("HOST_COHERENT"); + } + if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0) + { + json.WriteString("HOST_CACHED"); + } + if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0) + { + json.WriteString("LAZILY_ALLOCATED"); + } + json.EndArray(); + + if(stats.memoryType[typeIndex].blockCount > 0) + { + json.WriteString("Stats"); + VmaPrintStatInfo(json, stats.memoryType[typeIndex]); + } + + json.EndObject(); + } + } + + json.EndObject(); + } + if(detailedMap == VK_TRUE) + { + allocator->PrintDetailedMap(json); + } + + json.EndObject(); + } + + const size_t len = sb.GetLength(); + char* const pChars = vma_new_array(allocator, char, len + 1); + if(len > 0) + { + memcpy(pChars, sb.GetData(), len); + } + pChars[len] = '\0'; + *ppStatsString = pChars; +} + +void vmaFreeStatsString( + VmaAllocator allocator, + char* pStatsString) +{ + if(pStatsString != VMA_NULL) + { + VMA_ASSERT(allocator); + size_t len = strlen(pStatsString); + vma_delete_array(allocator, pStatsString, len + 1); + } +} + +#endif // #if VMA_STATS_STRING_ENABLED + +/* +This function is not protected by any mutex because it just reads immutable data. +*/ +VkResult vmaFindMemoryTypeIndex( + VmaAllocator allocator, + uint32_t memoryTypeBits, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + uint32_t* pMemoryTypeIndex) +{ + VMA_ASSERT(allocator != VK_NULL_HANDLE); + VMA_ASSERT(pAllocationCreateInfo != VMA_NULL); + VMA_ASSERT(pMemoryTypeIndex != VMA_NULL); + + if(pAllocationCreateInfo->memoryTypeBits != 0) + { + memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits; + } + + uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags; + uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags; + + const bool mapped = (pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0; + if(mapped) + { + preferredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + } + + // Convert usage to requiredFlags and preferredFlags. + switch(pAllocationCreateInfo->usage) + { + case VMA_MEMORY_USAGE_UNKNOWN: + break; + case VMA_MEMORY_USAGE_GPU_ONLY: + if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) + { + preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + } + break; + case VMA_MEMORY_USAGE_CPU_ONLY: + requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + break; + case VMA_MEMORY_USAGE_CPU_TO_GPU: + requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) + { + preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + } + break; + case VMA_MEMORY_USAGE_GPU_TO_CPU: + requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + preferredFlags |= VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT; + break; + default: + break; + } + + *pMemoryTypeIndex = UINT32_MAX; + uint32_t minCost = UINT32_MAX; + for(uint32_t memTypeIndex = 0, memTypeBit = 1; + memTypeIndex < allocator->GetMemoryTypeCount(); + ++memTypeIndex, memTypeBit <<= 1) + { + // This memory type is acceptable according to memoryTypeBits bitmask. + if((memTypeBit & memoryTypeBits) != 0) + { + const VkMemoryPropertyFlags currFlags = + allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags; + // This memory type contains requiredFlags. + if((requiredFlags & ~currFlags) == 0) + { + // Calculate cost as number of bits from preferredFlags not present in this memory type. + uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags); + // Remember memory type with lowest cost. + if(currCost < minCost) + { + *pMemoryTypeIndex = memTypeIndex; + if(currCost == 0) + { + return VK_SUCCESS; + } + minCost = currCost; + } + } + } + } + return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT; +} + +VkResult vmaFindMemoryTypeIndexForBufferInfo( + VmaAllocator allocator, + const VkBufferCreateInfo* pBufferCreateInfo, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + uint32_t* pMemoryTypeIndex) +{ + VMA_ASSERT(allocator != VK_NULL_HANDLE); + VMA_ASSERT(pBufferCreateInfo != VMA_NULL); + VMA_ASSERT(pAllocationCreateInfo != VMA_NULL); + VMA_ASSERT(pMemoryTypeIndex != VMA_NULL); + + const VkDevice hDev = allocator->m_hDevice; + VkBuffer hBuffer = VK_NULL_HANDLE; + VkResult res = allocator->GetVulkanFunctions().vkCreateBuffer( + hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer); + if(res == VK_SUCCESS) + { + VkMemoryRequirements memReq = {}; + allocator->GetVulkanFunctions().vkGetBufferMemoryRequirements( + hDev, hBuffer, &memReq); + + res = vmaFindMemoryTypeIndex( + allocator, + memReq.memoryTypeBits, + pAllocationCreateInfo, + pMemoryTypeIndex); + + allocator->GetVulkanFunctions().vkDestroyBuffer( + hDev, hBuffer, allocator->GetAllocationCallbacks()); + } + return res; +} + +VkResult vmaFindMemoryTypeIndexForImageInfo( + VmaAllocator allocator, + const VkImageCreateInfo* pImageCreateInfo, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + uint32_t* pMemoryTypeIndex) +{ + VMA_ASSERT(allocator != VK_NULL_HANDLE); + VMA_ASSERT(pImageCreateInfo != VMA_NULL); + VMA_ASSERT(pAllocationCreateInfo != VMA_NULL); + VMA_ASSERT(pMemoryTypeIndex != VMA_NULL); + + const VkDevice hDev = allocator->m_hDevice; + VkImage hImage = VK_NULL_HANDLE; + VkResult res = allocator->GetVulkanFunctions().vkCreateImage( + hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage); + if(res == VK_SUCCESS) + { + VkMemoryRequirements memReq = {}; + allocator->GetVulkanFunctions().vkGetImageMemoryRequirements( + hDev, hImage, &memReq); + + res = vmaFindMemoryTypeIndex( + allocator, + memReq.memoryTypeBits, + pAllocationCreateInfo, + pMemoryTypeIndex); + + allocator->GetVulkanFunctions().vkDestroyImage( + hDev, hImage, allocator->GetAllocationCallbacks()); + } + return res; +} + +VkResult vmaCreatePool( + VmaAllocator allocator, + const VmaPoolCreateInfo* pCreateInfo, + VmaPool* pPool) +{ + VMA_ASSERT(allocator && pCreateInfo && pPool); + + VMA_DEBUG_LOG("vmaCreatePool"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + VkResult res = allocator->CreatePool(pCreateInfo, pPool); + +#if VMA_RECORDING_ENABLED + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordCreatePool(allocator->GetCurrentFrameIndex(), *pCreateInfo, *pPool); + } +#endif + + return res; +} + +void vmaDestroyPool( + VmaAllocator allocator, + VmaPool pool) +{ + VMA_ASSERT(allocator); + + if(pool == VK_NULL_HANDLE) + { + return; + } + + VMA_DEBUG_LOG("vmaDestroyPool"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + +#if VMA_RECORDING_ENABLED + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordDestroyPool(allocator->GetCurrentFrameIndex(), pool); + } +#endif + + allocator->DestroyPool(pool); +} + +void vmaGetPoolStats( + VmaAllocator allocator, + VmaPool pool, + VmaPoolStats* pPoolStats) +{ + VMA_ASSERT(allocator && pool && pPoolStats); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocator->GetPoolStats(pool, pPoolStats); +} + +void vmaMakePoolAllocationsLost( + VmaAllocator allocator, + VmaPool pool, + size_t* pLostAllocationCount) +{ + VMA_ASSERT(allocator && pool); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + +#if VMA_RECORDING_ENABLED + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordMakePoolAllocationsLost(allocator->GetCurrentFrameIndex(), pool); + } +#endif + + allocator->MakePoolAllocationsLost(pool, pLostAllocationCount); +} + +VkResult vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool) +{ + VMA_ASSERT(allocator && pool); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + VMA_DEBUG_LOG("vmaCheckPoolCorruption"); + + return allocator->CheckPoolCorruption(pool); +} + +VkResult vmaAllocateMemory( + VmaAllocator allocator, + const VkMemoryRequirements* pVkMemoryRequirements, + const VmaAllocationCreateInfo* pCreateInfo, + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo) +{ + VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation); + + VMA_DEBUG_LOG("vmaAllocateMemory"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + VkResult result = allocator->AllocateMemory( + *pVkMemoryRequirements, + false, // requiresDedicatedAllocation + false, // prefersDedicatedAllocation + VK_NULL_HANDLE, // dedicatedBuffer + VK_NULL_HANDLE, // dedicatedImage + *pCreateInfo, + VMA_SUBALLOCATION_TYPE_UNKNOWN, + 1, // allocationCount + pAllocation); + +#if VMA_RECORDING_ENABLED + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordAllocateMemory( + allocator->GetCurrentFrameIndex(), + *pVkMemoryRequirements, + *pCreateInfo, + *pAllocation); + } +#endif + + if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS) + { + allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); + } + + return result; +} + +VkResult vmaAllocateMemoryPages( + VmaAllocator allocator, + const VkMemoryRequirements* pVkMemoryRequirements, + const VmaAllocationCreateInfo* pCreateInfo, + size_t allocationCount, + VmaAllocation* pAllocations, + VmaAllocationInfo* pAllocationInfo) +{ + if(allocationCount == 0) + { + return VK_SUCCESS; + } + + VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations); + + VMA_DEBUG_LOG("vmaAllocateMemoryPages"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + VkResult result = allocator->AllocateMemory( + *pVkMemoryRequirements, + false, // requiresDedicatedAllocation + false, // prefersDedicatedAllocation + VK_NULL_HANDLE, // dedicatedBuffer + VK_NULL_HANDLE, // dedicatedImage + *pCreateInfo, + VMA_SUBALLOCATION_TYPE_UNKNOWN, + allocationCount, + pAllocations); + +#if VMA_RECORDING_ENABLED + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordAllocateMemoryPages( + allocator->GetCurrentFrameIndex(), + *pVkMemoryRequirements, + *pCreateInfo, + (uint64_t)allocationCount, + pAllocations); + } +#endif + + if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS) + { + for(size_t i = 0; i < allocationCount; ++i) + { + allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i); + } + } + + return result; +} + +VkResult vmaAllocateMemoryForBuffer( + VmaAllocator allocator, + VkBuffer buffer, + const VmaAllocationCreateInfo* pCreateInfo, + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo) +{ + VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation); + + VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + VkMemoryRequirements vkMemReq = {}; + bool requiresDedicatedAllocation = false; + bool prefersDedicatedAllocation = false; + allocator->GetBufferMemoryRequirements(buffer, vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation); + + VkResult result = allocator->AllocateMemory( + vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation, + buffer, // dedicatedBuffer + VK_NULL_HANDLE, // dedicatedImage + *pCreateInfo, + VMA_SUBALLOCATION_TYPE_BUFFER, + 1, // allocationCount + pAllocation); + +#if VMA_RECORDING_ENABLED + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordAllocateMemoryForBuffer( + allocator->GetCurrentFrameIndex(), + vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation, + *pCreateInfo, + *pAllocation); + } +#endif + + if(pAllocationInfo && result == VK_SUCCESS) + { + allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); + } + + return result; +} + +VkResult vmaAllocateMemoryForImage( + VmaAllocator allocator, + VkImage image, + const VmaAllocationCreateInfo* pCreateInfo, + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo) +{ + VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation); + + VMA_DEBUG_LOG("vmaAllocateMemoryForImage"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + VkMemoryRequirements vkMemReq = {}; + bool requiresDedicatedAllocation = false; + bool prefersDedicatedAllocation = false; + allocator->GetImageMemoryRequirements(image, vkMemReq, + requiresDedicatedAllocation, prefersDedicatedAllocation); + + VkResult result = allocator->AllocateMemory( + vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation, + VK_NULL_HANDLE, // dedicatedBuffer + image, // dedicatedImage + *pCreateInfo, + VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN, + 1, // allocationCount + pAllocation); + +#if VMA_RECORDING_ENABLED + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordAllocateMemoryForImage( + allocator->GetCurrentFrameIndex(), + vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation, + *pCreateInfo, + *pAllocation); + } +#endif + + if(pAllocationInfo && result == VK_SUCCESS) + { + allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); + } + + return result; +} + +void vmaFreeMemory( + VmaAllocator allocator, + VmaAllocation allocation) +{ + VMA_ASSERT(allocator); + + if(allocation == VK_NULL_HANDLE) + { + return; + } + + VMA_DEBUG_LOG("vmaFreeMemory"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + +#if VMA_RECORDING_ENABLED + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordFreeMemory( + allocator->GetCurrentFrameIndex(), + allocation); + } +#endif + + allocator->FreeMemory( + 1, // allocationCount + &allocation); +} + +void vmaFreeMemoryPages( + VmaAllocator allocator, + size_t allocationCount, + VmaAllocation* pAllocations) +{ + if(allocationCount == 0) + { + return; + } + + VMA_ASSERT(allocator); + + VMA_DEBUG_LOG("vmaFreeMemoryPages"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + +#if VMA_RECORDING_ENABLED + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordFreeMemoryPages( + allocator->GetCurrentFrameIndex(), + (uint64_t)allocationCount, + pAllocations); + } +#endif + + allocator->FreeMemory(allocationCount, pAllocations); +} + +VkResult vmaResizeAllocation( + VmaAllocator allocator, + VmaAllocation allocation, + VkDeviceSize newSize) +{ + VMA_ASSERT(allocator && allocation); + + VMA_DEBUG_LOG("vmaResizeAllocation"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + +#if VMA_RECORDING_ENABLED + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordResizeAllocation( + allocator->GetCurrentFrameIndex(), + allocation, + newSize); + } +#endif + + return allocator->ResizeAllocation(allocation, newSize); +} + +void vmaGetAllocationInfo( + VmaAllocator allocator, + VmaAllocation allocation, + VmaAllocationInfo* pAllocationInfo) +{ + VMA_ASSERT(allocator && allocation && pAllocationInfo); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + +#if VMA_RECORDING_ENABLED + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordGetAllocationInfo( + allocator->GetCurrentFrameIndex(), + allocation); + } +#endif + + allocator->GetAllocationInfo(allocation, pAllocationInfo); +} + +VkBool32 vmaTouchAllocation( + VmaAllocator allocator, + VmaAllocation allocation) +{ + VMA_ASSERT(allocator && allocation); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + +#if VMA_RECORDING_ENABLED + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordTouchAllocation( + allocator->GetCurrentFrameIndex(), + allocation); + } +#endif + + return allocator->TouchAllocation(allocation); +} + +void vmaSetAllocationUserData( + VmaAllocator allocator, + VmaAllocation allocation, + void* pUserData) +{ + VMA_ASSERT(allocator && allocation); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocation->SetUserData(allocator, pUserData); + +#if VMA_RECORDING_ENABLED + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordSetAllocationUserData( + allocator->GetCurrentFrameIndex(), + allocation, + pUserData); + } +#endif +} + +void vmaCreateLostAllocation( + VmaAllocator allocator, + VmaAllocation* pAllocation) +{ + VMA_ASSERT(allocator && pAllocation); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + + allocator->CreateLostAllocation(pAllocation); + +#if VMA_RECORDING_ENABLED + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordCreateLostAllocation( + allocator->GetCurrentFrameIndex(), + *pAllocation); + } +#endif +} + +VkResult vmaMapMemory( + VmaAllocator allocator, + VmaAllocation allocation, + void** ppData) +{ + VMA_ASSERT(allocator && allocation && ppData); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + VkResult res = allocator->Map(allocation, ppData); + +#if VMA_RECORDING_ENABLED + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordMapMemory( + allocator->GetCurrentFrameIndex(), + allocation); + } +#endif + + return res; +} + +void vmaUnmapMemory( + VmaAllocator allocator, + VmaAllocation allocation) +{ + VMA_ASSERT(allocator && allocation); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + +#if VMA_RECORDING_ENABLED + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordUnmapMemory( + allocator->GetCurrentFrameIndex(), + allocation); + } +#endif + + allocator->Unmap(allocation); +} + +void vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size) +{ + VMA_ASSERT(allocator && allocation); + + VMA_DEBUG_LOG("vmaFlushAllocation"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH); + +#if VMA_RECORDING_ENABLED + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordFlushAllocation( + allocator->GetCurrentFrameIndex(), + allocation, offset, size); + } +#endif +} + +void vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size) +{ + VMA_ASSERT(allocator && allocation); + + VMA_DEBUG_LOG("vmaInvalidateAllocation"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE); + +#if VMA_RECORDING_ENABLED + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordInvalidateAllocation( + allocator->GetCurrentFrameIndex(), + allocation, offset, size); + } +#endif +} + +VkResult vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits) +{ + VMA_ASSERT(allocator); + + VMA_DEBUG_LOG("vmaCheckCorruption"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->CheckCorruption(memoryTypeBits); +} + +VkResult vmaDefragment( + VmaAllocator allocator, + VmaAllocation* pAllocations, + size_t allocationCount, + VkBool32* pAllocationsChanged, + const VmaDefragmentationInfo *pDefragmentationInfo, + VmaDefragmentationStats* pDefragmentationStats) +{ + // Deprecated interface, reimplemented using new one. + + VmaDefragmentationInfo2 info2 = {}; + info2.allocationCount = (uint32_t)allocationCount; + info2.pAllocations = pAllocations; + info2.pAllocationsChanged = pAllocationsChanged; + if(pDefragmentationInfo != VMA_NULL) + { + info2.maxCpuAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove; + info2.maxCpuBytesToMove = pDefragmentationInfo->maxBytesToMove; + } + else + { + info2.maxCpuAllocationsToMove = UINT32_MAX; + info2.maxCpuBytesToMove = VK_WHOLE_SIZE; + } + // info2.flags, maxGpuAllocationsToMove, maxGpuBytesToMove, commandBuffer deliberately left zero. + + VmaDefragmentationContext ctx; + VkResult res = vmaDefragmentationBegin(allocator, &info2, pDefragmentationStats, &ctx); + if(res == VK_NOT_READY) + { + res = vmaDefragmentationEnd( allocator, ctx); + } + return res; +} + +VkResult vmaDefragmentationBegin( + VmaAllocator allocator, + const VmaDefragmentationInfo2* pInfo, + VmaDefragmentationStats* pStats, + VmaDefragmentationContext *pContext) +{ + VMA_ASSERT(allocator && pInfo && pContext); + + // Degenerate case: Nothing to defragment. + if(pInfo->allocationCount == 0 && pInfo->poolCount == 0) + { + return VK_SUCCESS; + } + + VMA_ASSERT(pInfo->allocationCount == 0 || pInfo->pAllocations != VMA_NULL); + VMA_ASSERT(pInfo->poolCount == 0 || pInfo->pPools != VMA_NULL); + VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->allocationCount, pInfo->pAllocations)); + VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->poolCount, pInfo->pPools)); + + VMA_DEBUG_LOG("vmaDefragmentationBegin"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + VkResult res = allocator->DefragmentationBegin(*pInfo, pStats, pContext); + +#if VMA_RECORDING_ENABLED + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordDefragmentationBegin( + allocator->GetCurrentFrameIndex(), *pInfo, *pContext); + } +#endif + + return res; +} + +VkResult vmaDefragmentationEnd( + VmaAllocator allocator, + VmaDefragmentationContext context) +{ + VMA_ASSERT(allocator); + + VMA_DEBUG_LOG("vmaDefragmentationEnd"); + + if(context != VK_NULL_HANDLE) + { + VMA_DEBUG_GLOBAL_MUTEX_LOCK + +#if VMA_RECORDING_ENABLED + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordDefragmentationEnd( + allocator->GetCurrentFrameIndex(), context); + } +#endif + + return allocator->DefragmentationEnd(context); + } + else + { + return VK_SUCCESS; + } +} + +VkResult vmaBindBufferMemory( + VmaAllocator allocator, + VmaAllocation allocation, + VkBuffer buffer) +{ + VMA_ASSERT(allocator && allocation && buffer); + + VMA_DEBUG_LOG("vmaBindBufferMemory"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->BindBufferMemory(allocation, buffer); +} + +VkResult vmaBindImageMemory( + VmaAllocator allocator, + VmaAllocation allocation, + VkImage image) +{ + VMA_ASSERT(allocator && allocation && image); + + VMA_DEBUG_LOG("vmaBindImageMemory"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->BindImageMemory(allocation, image); +} + +VkResult vmaCreateBuffer( + VmaAllocator allocator, + const VkBufferCreateInfo* pBufferCreateInfo, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + VkBuffer* pBuffer, + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo) +{ + VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation); + + if(pBufferCreateInfo->size == 0) + { + return VK_ERROR_VALIDATION_FAILED_EXT; + } + + VMA_DEBUG_LOG("vmaCreateBuffer"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + *pBuffer = VK_NULL_HANDLE; + *pAllocation = VK_NULL_HANDLE; + + // 1. Create VkBuffer. + VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)( + allocator->m_hDevice, + pBufferCreateInfo, + allocator->GetAllocationCallbacks(), + pBuffer); + if(res >= 0) + { + // 2. vkGetBufferMemoryRequirements. + VkMemoryRequirements vkMemReq = {}; + bool requiresDedicatedAllocation = false; + bool prefersDedicatedAllocation = false; + allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq, + requiresDedicatedAllocation, prefersDedicatedAllocation); + + // Make sure alignment requirements for specific buffer usages reported + // in Physical Device Properties are included in alignment reported by memory requirements. + if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT) != 0) + { + VMA_ASSERT(vkMemReq.alignment % + allocator->m_PhysicalDeviceProperties.limits.minTexelBufferOffsetAlignment == 0); + } + if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) != 0) + { + VMA_ASSERT(vkMemReq.alignment % + allocator->m_PhysicalDeviceProperties.limits.minUniformBufferOffsetAlignment == 0); + } + if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) != 0) + { + VMA_ASSERT(vkMemReq.alignment % + allocator->m_PhysicalDeviceProperties.limits.minStorageBufferOffsetAlignment == 0); + } + + // 3. Allocate memory using allocator. + res = allocator->AllocateMemory( + vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation, + *pBuffer, // dedicatedBuffer + VK_NULL_HANDLE, // dedicatedImage + *pAllocationCreateInfo, + VMA_SUBALLOCATION_TYPE_BUFFER, + 1, // allocationCount + pAllocation); + +#if VMA_RECORDING_ENABLED + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordCreateBuffer( + allocator->GetCurrentFrameIndex(), + *pBufferCreateInfo, + *pAllocationCreateInfo, + *pAllocation); + } +#endif + + if(res >= 0) + { + // 3. Bind buffer with memory. + res = allocator->BindBufferMemory(*pAllocation, *pBuffer); + if(res >= 0) + { + // All steps succeeded. + #if VMA_STATS_STRING_ENABLED + (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage); + #endif + if(pAllocationInfo != VMA_NULL) + { + allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); + } + + return VK_SUCCESS; + } + allocator->FreeMemory( + 1, // allocationCount + pAllocation); + *pAllocation = VK_NULL_HANDLE; + (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks()); + *pBuffer = VK_NULL_HANDLE; + return res; + } + (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks()); + *pBuffer = VK_NULL_HANDLE; + return res; + } + return res; +} + +void vmaDestroyBuffer( + VmaAllocator allocator, + VkBuffer buffer, + VmaAllocation allocation) +{ + VMA_ASSERT(allocator); + + if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE) + { + return; + } + + VMA_DEBUG_LOG("vmaDestroyBuffer"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + +#if VMA_RECORDING_ENABLED + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordDestroyBuffer( + allocator->GetCurrentFrameIndex(), + allocation); + } +#endif + + if(buffer != VK_NULL_HANDLE) + { + (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks()); + } + + if(allocation != VK_NULL_HANDLE) + { + allocator->FreeMemory( + 1, // allocationCount + &allocation); + } +} + +VkResult vmaCreateImage( + VmaAllocator allocator, + const VkImageCreateInfo* pImageCreateInfo, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + VkImage* pImage, + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo) +{ + VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation); + + if(pImageCreateInfo->extent.width == 0 || + pImageCreateInfo->extent.height == 0 || + pImageCreateInfo->extent.depth == 0 || + pImageCreateInfo->mipLevels == 0 || + pImageCreateInfo->arrayLayers == 0) + { + return VK_ERROR_VALIDATION_FAILED_EXT; + } + + VMA_DEBUG_LOG("vmaCreateImage"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + *pImage = VK_NULL_HANDLE; + *pAllocation = VK_NULL_HANDLE; + + // 1. Create VkImage. + VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)( + allocator->m_hDevice, + pImageCreateInfo, + allocator->GetAllocationCallbacks(), + pImage); + if(res >= 0) + { + VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ? + VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL : + VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR; + + // 2. Allocate memory using allocator. + VkMemoryRequirements vkMemReq = {}; + bool requiresDedicatedAllocation = false; + bool prefersDedicatedAllocation = false; + allocator->GetImageMemoryRequirements(*pImage, vkMemReq, + requiresDedicatedAllocation, prefersDedicatedAllocation); + + res = allocator->AllocateMemory( + vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation, + VK_NULL_HANDLE, // dedicatedBuffer + *pImage, // dedicatedImage + *pAllocationCreateInfo, + suballocType, + 1, // allocationCount + pAllocation); + +#if VMA_RECORDING_ENABLED + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordCreateImage( + allocator->GetCurrentFrameIndex(), + *pImageCreateInfo, + *pAllocationCreateInfo, + *pAllocation); + } +#endif + + if(res >= 0) + { + // 3. Bind image with memory. + res = allocator->BindImageMemory(*pAllocation, *pImage); + if(res >= 0) + { + // All steps succeeded. + #if VMA_STATS_STRING_ENABLED + (*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage); + #endif + if(pAllocationInfo != VMA_NULL) + { + allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); + } + + return VK_SUCCESS; + } + allocator->FreeMemory( + 1, // allocationCount + pAllocation); + *pAllocation = VK_NULL_HANDLE; + (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks()); + *pImage = VK_NULL_HANDLE; + return res; + } + (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks()); + *pImage = VK_NULL_HANDLE; + return res; + } + return res; +} + +void vmaDestroyImage( + VmaAllocator allocator, + VkImage image, + VmaAllocation allocation) +{ + VMA_ASSERT(allocator); + + if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE) + { + return; + } + + VMA_DEBUG_LOG("vmaDestroyImage"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + +#if VMA_RECORDING_ENABLED + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordDestroyImage( + allocator->GetCurrentFrameIndex(), + allocation); + } +#endif + + if(image != VK_NULL_HANDLE) + { + (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks()); + } + if(allocation != VK_NULL_HANDLE) + { + allocator->FreeMemory( + 1, // allocationCount + &allocation); + } +} + +#endif // #ifdef VMA_IMPLEMENTATION -- cgit v1.2.3 From 38f1a36f608f729f47d880eb8389e88159bda7a4 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Fri, 24 May 2019 16:08:18 +0200 Subject: Windows QPA: Fix clang warnings about repetitive type names Fix warning like: warning: use auto when initializing with new/reinterpret_cast to avoid duplicating the type name [modernize-use-auto] Change-Id: Ieb7f052919173f6923e68de9f9e849dee45e36e7 Reviewed-by: Oliver Wolff --- .../direct2d/qwindowsdirect2dintegration.cpp | 4 ++-- .../direct2d/qwindowsdirect2dpaintengine.cpp | 16 ++++++++-------- .../platforms/direct2d/qwindowsdirect2dwindow.cpp | 2 +- src/plugins/platforms/windows/qwin10helpers.cpp | 2 +- .../platforms/windows/qwindowsbackingstore.cpp | 2 +- src/plugins/platforms/windows/qwindowscontext.cpp | 12 ++++++------ src/plugins/platforms/windows/qwindowscursor.cpp | 2 +- .../platforms/windows/qwindowsdialoghelpers.cpp | 12 ++++++------ src/plugins/platforms/windows/qwindowsdrag.cpp | 6 +++--- src/plugins/platforms/windows/qwindowseglcontext.cpp | 8 ++++---- .../platforms/windows/qwindowsgdinativeinterface.cpp | 2 +- src/plugins/platforms/windows/qwindowsglcontext.cpp | 10 +++++----- .../platforms/windows/qwindowsinputcontext.cpp | 2 +- .../platforms/windows/qwindowsintegration.cpp | 4 ++-- src/plugins/platforms/windows/qwindowskeymapper.cpp | 6 +++--- src/plugins/platforms/windows/qwindowsmenu.cpp | 6 +++--- src/plugins/platforms/windows/qwindowsmime.cpp | 20 ++++++++++---------- .../platforms/windows/qwindowsmousehandler.cpp | 6 +++--- .../platforms/windows/qwindowsnativeinterface.cpp | 10 +++++----- src/plugins/platforms/windows/qwindowsole.cpp | 10 +++++----- .../platforms/windows/qwindowsopengltester.cpp | 2 +- .../platforms/windows/qwindowspointerhandler.cpp | 12 ++++++------ src/plugins/platforms/windows/qwindowsscreen.cpp | 4 ++-- src/plugins/platforms/windows/qwindowsservices.cpp | 2 +- src/plugins/platforms/windows/qwindowstheme.cpp | 2 +- src/plugins/platforms/windows/qwindowswindow.cpp | 14 +++++++------- .../windows/uiautomation/qwindowsuiamainprovider.cpp | 2 +- .../windows/uiautomation/qwindowsuiatextprovider.cpp | 6 +++--- .../uiautomation/qwindowsuiatextrangeprovider.cpp | 6 +++--- 29 files changed, 96 insertions(+), 96 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.cpp index 86c863ec50..0cf05cb0ac 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.cpp +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.cpp @@ -91,7 +91,7 @@ static QVersionNumber systemD2DVersion() if (VerQueryValue(info.constData(), __TEXT("\\"), reinterpret_cast(&fi), &size) && size) { - const VS_FIXEDFILEINFO *verInfo = reinterpret_cast(fi); + const auto *verInfo = reinterpret_cast(fi); return QVersionNumber{HIWORD(verInfo->dwFileVersionMS), LOWORD(verInfo->dwFileVersionMS), HIWORD(verInfo->dwFileVersionLS), LOWORD(verInfo->dwFileVersionLS)}; } @@ -140,7 +140,7 @@ QWindowsDirect2DIntegration *QWindowsDirect2DIntegration::create(const QStringLi return nullptr; } - QWindowsDirect2DIntegration *integration = new QWindowsDirect2DIntegration(paramList); + auto *integration = new QWindowsDirect2DIntegration(paramList); if (!integration->init()) { delete integration; diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp index 1b82db0b37..cf3e88d54e 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp @@ -253,7 +253,7 @@ struct D2DVectorPathCache { static void cleanup_func(QPaintEngineEx *engine, void *data) { Q_UNUSED(engine); - D2DVectorPathCache *e = static_cast(data); + auto *e = static_cast(data); delete e; } }; @@ -689,7 +689,7 @@ public: *needsEmulation = true; } else { ComPtr linear; - const QLinearGradient *qlinear = static_cast(newBrush.gradient()); + const auto *qlinear = static_cast(newBrush.gradient()); D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES linearGradientBrushProperties; ComPtr gradientStopCollection; @@ -727,7 +727,7 @@ public: *needsEmulation = true; } else { ComPtr radial; - const QRadialGradient *qradial = static_cast(newBrush.gradient()); + const auto *qradial = static_cast(newBrush.gradient()); D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES radialGradientBrushProperties; ComPtr gradientStopCollection; @@ -807,7 +807,7 @@ public: : nullptr; if (cacheEntry) { - D2DVectorPathCache *e = static_cast(cacheEntry->data); + auto *e = static_cast(cacheEntry->data); if (alias && e->aliased) return e->aliased; else if (!alias && e->antiAliased) @@ -885,7 +885,7 @@ public: if (!cacheEntry) cacheEntry = path.addCacheData(q, new D2DVectorPathCache, D2DVectorPathCache::cleanup_func); - D2DVectorPathCache *e = static_cast(cacheEntry->data); + auto *e = static_cast(cacheEntry->data); if (alias) e->aliased = geometry; else @@ -1481,7 +1481,7 @@ void QWindowsDirect2DPaintEngine::drawPixmap(const QRectF &r, return; } - QWindowsDirect2DPlatformPixmap *pp = static_cast(pm.handle()); + auto *pp = static_cast(pm.handle()); QWindowsDirect2DBitmap *bitmap = pp->bitmap(); ensurePen(); @@ -1593,7 +1593,7 @@ void QWindowsDirect2DPaintEngine::drawTextItem(const QPointF &p, const QTextItem Q_D(QWindowsDirect2DPaintEngine); D2D_TAG(D2DDebugDrawTextItemTag); - const QTextItemInt &ti = static_cast(textItem); + const auto &ti = static_cast(textItem); if (ti.glyphs.numGlyphs == 0) return; @@ -1686,7 +1686,7 @@ void QWindowsDirect2DPaintEngine::rasterFill(const QVectorPath &path, const QBru p.setBrush(state()->brush); p.setPen(state()->pen); - QPaintEngineEx *extended = static_cast(engine); + auto *extended = static_cast(engine); for (const QPainterClipInfo &info : qAsConst(state()->clipInfo)) { extended->state()->matrix = info.matrix; extended->transformChanged(); diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dwindow.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dwindow.cpp index 239e5f3087..c417daaeae 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dwindow.cpp +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dwindow.cpp @@ -310,7 +310,7 @@ void QWindowsDirect2DWindow::setupBitmap() QWindowsDirect2DPaintEngine::Flags flags = QWindowsDirect2DPaintEngine::NoFlag; if (!m_directRendering) flags |= QWindowsDirect2DPaintEngine::TranslucentTopLevelWindow; - QWindowsDirect2DPlatformPixmap *pp = new QWindowsDirect2DPlatformPixmap(QPlatformPixmap::PixmapType, + auto *pp = new QWindowsDirect2DPlatformPixmap(QPlatformPixmap::PixmapType, flags, m_bitmap.data()); m_pixmap.reset(new QPixmap(pp)); diff --git a/src/plugins/platforms/windows/qwin10helpers.cpp b/src/plugins/platforms/windows/qwin10helpers.cpp index cc17d8798f..9a7fce9cd5 100644 --- a/src/plugins/platforms/windows/qwin10helpers.cpp +++ b/src/plugins/platforms/windows/qwin10helpers.cpp @@ -137,7 +137,7 @@ bool qt_windowsIsTabletMode(HWND hwnd) const wchar_t uiViewSettingsId[] = L"Windows.UI.ViewManagement.UIViewSettings"; HSTRING_HEADER uiViewSettingsIdRefHeader; HSTRING uiViewSettingsIdHs = nullptr; - const UINT32 uiViewSettingsIdLen = UINT32(sizeof(uiViewSettingsId) / sizeof(uiViewSettingsId[0]) - 1); + const auto uiViewSettingsIdLen = UINT32(sizeof(uiViewSettingsId) / sizeof(uiViewSettingsId[0]) - 1); if (FAILED(baseComDll.windowsCreateStringReference(uiViewSettingsId, uiViewSettingsIdLen, &uiViewSettingsIdRefHeader, &uiViewSettingsIdHs))) return false; diff --git a/src/plugins/platforms/windows/qwindowsbackingstore.cpp b/src/plugins/platforms/windows/qwindowsbackingstore.cpp index 68807fabdd..bd7bdc55d1 100644 --- a/src/plugins/platforms/windows/qwindowsbackingstore.cpp +++ b/src/plugins/platforms/windows/qwindowsbackingstore.cpp @@ -158,7 +158,7 @@ void QWindowsBackingStore::resize(const QSize &size, const QRegion ®ion) format = qt_maybeAlphaVersionWithSameDepth(format); QWindowsNativeImage *oldwni = m_image.data(); - QWindowsNativeImage *newwni = new QWindowsNativeImage(size.width(), size.height(), format); + auto *newwni = new QWindowsNativeImage(size.width(), size.height(), format); if (oldwni && !region.isEmpty()) { const QImage &oldimg(oldwni->image()); diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index 9be6d4ced7..f7897336c9 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -133,8 +133,8 @@ static inline bool useRTL_Extensions() #if QT_CONFIG(sessionmanager) static inline QWindowsSessionManager *platformSessionManager() { - QGuiApplicationPrivate *guiPrivate = static_cast(QObjectPrivate::get(qApp)); - QSessionManagerPrivate *managerPrivate = static_cast(QObjectPrivate::get(guiPrivate->session_manager)); + auto *guiPrivate = static_cast(QObjectPrivate::get(qApp)); + auto *managerPrivate = static_cast(QObjectPrivate::get(guiPrivate->session_manager)); return static_cast(managerPrivate->platformSessionManager); } @@ -545,7 +545,7 @@ QString QWindowsContext::registerWindowClass(QString cname, // add an instance-specific ID, the address of the window proc. static int classExists = -1; - const HINSTANCE appInstance = static_cast(GetModuleHandle(nullptr)); + const auto appInstance = static_cast(GetModuleHandle(nullptr)); if (classExists == -1) { WNDCLASS wcinfo; classExists = GetClassInfo(appInstance, reinterpret_cast(cname.utf16()), &wcinfo); @@ -598,7 +598,7 @@ QString QWindowsContext::registerWindowClass(QString cname, void QWindowsContext::unregisterWindowClasses() { - const HINSTANCE appInstance = static_cast(GetModuleHandle(nullptr)); + const auto appInstance = static_cast(GetModuleHandle(nullptr)); for (const QString &name : qAsConst(d->m_registeredWindowClassNames)) { if (!UnregisterClass(reinterpret_cast(name.utf16()), appInstance) && QWindowsContext::verbose) @@ -1351,7 +1351,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, sessionManager->blocksInteraction(); sessionManager->clearCancellation(); - QGuiApplicationPrivate *qGuiAppPriv = static_cast(QObjectPrivate::get(qApp)); + auto *qGuiAppPriv = static_cast(QObjectPrivate::get(qApp)); qGuiAppPriv->commitData(); if (lParam & ENDSESSION_LOGOFF) @@ -1369,7 +1369,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, // we receive the message for each toplevel window included internal hidden ones, // but the aboutToQuit signal should be emitted only once. - QGuiApplicationPrivate *qGuiAppPriv = static_cast(QObjectPrivate::get(qApp)); + auto *qGuiAppPriv = static_cast(QObjectPrivate::get(qApp)); if (endsession && !qGuiAppPriv->aboutToQuitEmitted) { qGuiAppPriv->aboutToQuitEmitted = true; int index = QGuiApplication::staticMetaObject.indexOfSignal("aboutToQuit()"); diff --git a/src/plugins/platforms/windows/qwindowscursor.cpp b/src/plugins/platforms/windows/qwindowscursor.cpp index 20a8117304..17e8cffb76 100644 --- a/src/plugins/platforms/windows/qwindowscursor.cpp +++ b/src/plugins/platforms/windows/qwindowscursor.cpp @@ -752,7 +752,7 @@ QPixmap QWindowsCursor::dragDefaultCursor(Qt::DropAction action) const && GetObject(iconInfo.hbmColor, sizeof(BITMAP), &bmColor) && bmColor.bmWidth == bmColor.bmWidthBytes / 4) { const int colorBitsLength = bmColor.bmHeight * bmColor.bmWidthBytes; - uchar *colorBits = new uchar[colorBitsLength]; + auto *colorBits = new uchar[colorBitsLength]; GetBitmapBits(iconInfo.hbmColor, colorBitsLength, colorBits); const QImage colorImage(colorBits, bmColor.bmWidth, bmColor.bmHeight, bmColor.bmWidthBytes, QImage::Format_ARGB32); diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp index 57f19b5ff2..b7ab952a1d 100644 --- a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp +++ b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp @@ -356,7 +356,7 @@ struct FindDialogContext static BOOL QT_WIN_CALLBACK findDialogEnumWindowsProc(HWND hwnd, LPARAM lParam) { - FindDialogContext *context = reinterpret_cast(lParam); + auto *context = reinterpret_cast(lParam); DWORD winPid = 0; GetWindowThreadProcessId(hwnd, &winPid); if (winPid != context->processId) @@ -531,7 +531,7 @@ private: IFileDialogEvents *QWindowsNativeFileDialogEventHandler::create(QWindowsNativeFileDialogBase *nativeFileDialog) { IFileDialogEvents *result; - QWindowsNativeFileDialogEventHandler *eventHandler = new QWindowsNativeFileDialogEventHandler(nativeFileDialog); + auto *eventHandler = new QWindowsNativeFileDialogEventHandler(nativeFileDialog); if (FAILED(eventHandler->QueryInterface(IID_IFileDialogEvents, reinterpret_cast(&result)))) { qErrnoWarning("Unable to obtain IFileDialogEvents"); return nullptr; @@ -1112,7 +1112,7 @@ void QWindowsNativeFileDialogBase::setDefaultSuffixSys(const QString &s) // If this parameter is non-empty, it will be appended by the dialog for the 'Any files' // filter ('*'). If this parameter is non-empty and the current filter has a suffix, // the dialog will append the filter's suffix. - wchar_t *wSuffix = const_cast(reinterpret_cast(s.utf16())); + auto *wSuffix = const_cast(reinterpret_cast(s.utf16())); m_fileDialog->SetDefaultExtension(wSuffix); } @@ -1125,7 +1125,7 @@ static inline IFileDialog2 *getFileDialog2(IFileDialog *fileDialog) void QWindowsNativeFileDialogBase::setLabelText(QFileDialogOptions::DialogLabel l, const QString &text) { - wchar_t *wText = const_cast(reinterpret_cast(text.utf16())); + auto *wText = const_cast(reinterpret_cast(text.utf16())); switch (l) { case QFileDialogOptions::FileName: m_fileDialog->SetFileNameLabel(wText); @@ -1770,7 +1770,7 @@ void QWindowsXpNativeFileDialog::doExec(HWND owner) static int QT_WIN_CALLBACK xpFileDialogGetExistingDirCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData) { - QWindowsXpNativeFileDialog *dialog = reinterpret_cast(lpData); + auto *dialog = reinterpret_cast(lpData); return dialog->existingDirCallback(hwnd, uMsg, lParam); } @@ -1843,7 +1843,7 @@ void QWindowsXpNativeFileDialog::populateOpenFileName(OPENFILENAME *ofn, HWND ow const QList specs = filterSpecs(m_options->nameFilters(), m_options->options() & QFileDialogOptions::HideNameFilterDetails, &totalStringLength); const int size = specs.size(); - wchar_t *ptr = new wchar_t[totalStringLength + 2 * size + 1]; + auto *ptr = new wchar_t[totalStringLength + 2 * size + 1]; ofn->lpstrFilter = ptr; for (const FilterSpec &spec : specs) { ptr += spec.description.toWCharArray(ptr); diff --git a/src/plugins/platforms/windows/qwindowsdrag.cpp b/src/plugins/platforms/windows/qwindowsdrag.cpp index 502c92ef59..3e4c93d47a 100644 --- a/src/plugins/platforms/windows/qwindowsdrag.cpp +++ b/src/plugins/platforms/windows/qwindowsdrag.cpp @@ -627,7 +627,7 @@ QWindowsOleDropTarget::Drop(LPDATAOBJECT pDataObj, DWORD grfKeyState, m_chosenEffect = DROPEFFECT_COPY; HGLOBAL hData = GlobalAlloc(0, sizeof(DWORD)); if (hData) { - DWORD *moveEffect = reinterpret_cast(GlobalLock(hData)); + auto *moveEffect = reinterpret_cast(GlobalLock(hData)); *moveEffect = DROPEFFECT_MOVE; GlobalUnlock(hData); STGMEDIUM medium; @@ -704,9 +704,9 @@ Qt::DropAction QWindowsDrag::drag(QDrag *drag) DWORD resultEffect; QWindowsDrag::m_canceled = false; - QWindowsOleDropSource *windowDropSource = new QWindowsOleDropSource(this); + auto *windowDropSource = new QWindowsOleDropSource(this); windowDropSource->createCursors(); - QWindowsDropDataObject *dropDataObject = new QWindowsDropDataObject(dropData); + auto *dropDataObject = new QWindowsDropDataObject(dropData); const Qt::DropActions possibleActions = drag->supportedActions(); const DWORD allowedEffects = translateToWinDragEffects(possibleActions); qCDebug(lcQpaMime) << '>' << __FUNCTION__ << "possible Actions=0x" diff --git a/src/plugins/platforms/windows/qwindowseglcontext.cpp b/src/plugins/platforms/windows/qwindowseglcontext.cpp index 063e81150e..e9f3dc5189 100644 --- a/src/plugins/platforms/windows/qwindowseglcontext.cpp +++ b/src/plugins/platforms/windows/qwindowseglcontext.cpp @@ -469,10 +469,10 @@ bool QWindowsEGLContext::makeCurrent(QPlatformSurface *surface) QWindowsEGLStaticContext::libEGL.eglBindAPI(m_api); - QWindowsWindow *window = static_cast(surface); + auto *window = static_cast(surface); window->aboutToMakeCurrent(); int err = 0; - EGLSurface eglSurface = static_cast(window->surface(m_eglConfig, &err)); + auto eglSurface = static_cast(window->surface(m_eglConfig, &err)); if (eglSurface == EGL_NO_SURFACE) { if (err == EGL_CONTEXT_LOST) { m_eglContext = EGL_NO_CONTEXT; @@ -531,9 +531,9 @@ void QWindowsEGLContext::doneCurrent() void QWindowsEGLContext::swapBuffers(QPlatformSurface *surface) { QWindowsEGLStaticContext::libEGL.eglBindAPI(m_api); - QWindowsWindow *window = static_cast(surface); + auto *window = static_cast(surface); int err = 0; - EGLSurface eglSurface = static_cast(window->surface(m_eglConfig, &err)); + auto eglSurface = static_cast(window->surface(m_eglConfig, &err)); if (eglSurface == EGL_NO_SURFACE) { if (err == EGL_CONTEXT_LOST) { m_eglContext = EGL_NO_CONTEXT; diff --git a/src/plugins/platforms/windows/qwindowsgdinativeinterface.cpp b/src/plugins/platforms/windows/qwindowsgdinativeinterface.cpp index 08e11c5e39..f2418b0e60 100644 --- a/src/plugins/platforms/windows/qwindowsgdinativeinterface.cpp +++ b/src/plugins/platforms/windows/qwindowsgdinativeinterface.cpp @@ -50,7 +50,7 @@ void *QWindowsGdiNativeInterface::nativeResourceForBackingStore(const QByteArray qWarning("%s: '%s' requested for null backingstore or backingstore without handle.", __FUNCTION__, resource.constData()); return nullptr; } - QWindowsBackingStore *wbs = static_cast(bs->handle()); + auto *wbs = static_cast(bs->handle()); if (resource == "getDC") return wbs->getDC(); qWarning("%s: Invalid key '%s' requested.", __FUNCTION__, resource.constData()); diff --git a/src/plugins/platforms/windows/qwindowsglcontext.cpp b/src/plugins/platforms/windows/qwindowsglcontext.cpp index 66dd32d05e..24526bad2c 100644 --- a/src/plugins/platforms/windows/qwindowsglcontext.cpp +++ b/src/plugins/platforms/windows/qwindowsglcontext.cpp @@ -1009,7 +1009,7 @@ QOpenGLStaticContext *QOpenGLStaticContext::create(bool softwareRendering) QScopedPointer temporaryContext; if (!QOpenGLStaticContext::opengl32.wglGetCurrentContext()) temporaryContext.reset(new QOpenGLTemporaryContext); - QOpenGLStaticContext *result = new QOpenGLStaticContext; + auto *result = new QOpenGLStaticContext; qCDebug(lcQpaGl) << __FUNCTION__ << *result; return result; } @@ -1051,7 +1051,7 @@ QWindowsGLContext::QWindowsGLContext(QOpenGLStaticContext *staticContext, qWarning("QWindowsGLContext: Requires a QWGLNativeContext"); return; } - QWGLNativeContext handle = nativeHandle.value(); + auto handle = nativeHandle.value(); HGLRC wglcontext = handle.context(); HWND wnd = handle.window(); if (!wglcontext || !wnd) { @@ -1233,7 +1233,7 @@ bool QWindowsGLContext::updateObtainedParams(HDC hdc, int *obtainedSwapInterval) hasRobustness = exts && strstr(exts, "GL_ARB_robustness"); } else { typedef const GLubyte * (APIENTRY *glGetStringi_t)(GLenum, GLuint); - glGetStringi_t glGetStringi = reinterpret_cast( + auto glGetStringi = reinterpret_cast( reinterpret_cast(QOpenGLStaticContext::opengl32.wglGetProcAddress("glGetStringi"))); if (glGetStringi) { GLint n = 0; @@ -1305,7 +1305,7 @@ bool QWindowsGLContext::makeCurrent(QPlatformSurface *surface) Q_ASSERT(surface->surface()->supportsOpenGL()); // Do we already have a DC entry for that window? - QWindowsWindow *window = static_cast(surface); + auto *window = static_cast(surface); window->aboutToMakeCurrent(); const HWND hwnd = window->handle(); if (const QOpenGLContextData *contextData = findByHWND(m_windowContexts, hwnd)) { @@ -1374,7 +1374,7 @@ QFunctionPointer QWindowsGLContext::getProcAddress(const char *procName) // Even though we use QFunctionPointer, it does not mean the function can be called. // It will need to be cast to the proper function type with the correct calling // convention. QFunctionPointer is nothing more than a glorified void* here. - QFunctionPointer procAddress = reinterpret_cast(QOpenGLStaticContext::opengl32.wglGetProcAddress(procName)); + auto procAddress = reinterpret_cast(QOpenGLStaticContext::opengl32.wglGetProcAddress(procName)); // We support AllGLFunctionsQueryable, which means this function must be able to // return a function pointer even for functions that are in GL.h and exported diff --git a/src/plugins/platforms/windows/qwindowsinputcontext.cpp b/src/plugins/platforms/windows/qwindowsinputcontext.cpp index 71ed33f85b..e681dbb0cb 100644 --- a/src/plugins/platforms/windows/qwindowsinputcontext.cpp +++ b/src/plugins/platforms/windows/qwindowsinputcontext.cpp @@ -717,7 +717,7 @@ int QWindowsInputContext::reconvertString(RECONVERTSTRING *reconv) reconv->dwCompStrOffset = DWORD(startPos) * sizeof(ushort); // byte count. reconv->dwTargetStrLen = reconv->dwCompStrLen; reconv->dwTargetStrOffset = reconv->dwCompStrOffset; - ushort *pastReconv = reinterpret_cast(reconv + 1); + auto *pastReconv = reinterpret_cast(reconv + 1); std::copy(surroundingText.utf16(), surroundingText.utf16() + surroundingText.size(), QT_MAKE_UNCHECKED_ARRAY_ITERATOR(pastReconv)); return memSize; diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp index f0754e3e57..eccf1c4928 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.cpp +++ b/src/plugins/platforms/windows/qwindowsintegration.cpp @@ -324,7 +324,7 @@ bool QWindowsIntegration::hasCapability(QPlatformIntegration::Capability cap) co QPlatformWindow *QWindowsIntegration::createPlatformWindow(QWindow *window) const { if (window->type() == Qt::Desktop) { - QWindowsDesktopWindow *result = new QWindowsDesktopWindow(window); + auto *result = new QWindowsDesktopWindow(window); qCDebug(lcQpaWindows) << "Desktop window:" << window << Qt::showbase << Qt::hex << result->winId() << Qt::noshowbase << Qt::dec << result->geometry(); return result; @@ -371,7 +371,7 @@ QPlatformWindow *QWindowsIntegration::createForeignWindow(QWindow *window, WId n qWarning("Windows QPA: Invalid foreign window ID %p.", hwnd); return nullptr; } - QWindowsForeignWindow *result = new QWindowsForeignWindow(window, hwnd); + auto *result = new QWindowsForeignWindow(window, hwnd); const QRect obtainedGeometry = result->geometry(); QScreen *screen = nullptr; if (const QPlatformScreen *pScreen = result->screenForGeometry(obtainedGeometry)) diff --git a/src/plugins/platforms/windows/qwindowskeymapper.cpp b/src/plugins/platforms/windows/qwindowskeymapper.cpp index 44668cde78..4b54051e0c 100644 --- a/src/plugins/platforms/windows/qwindowskeymapper.cpp +++ b/src/plugins/platforms/windows/qwindowskeymapper.cpp @@ -100,7 +100,7 @@ QWindowsKeyMapper::QWindowsKeyMapper() : m_useRTLExtensions(false), m_keyGrabber(nullptr) { memset(keyLayout, 0, sizeof(keyLayout)); - QGuiApplication *app = static_cast(QGuiApplication::instance()); + auto *app = static_cast(QGuiApplication::instance()); QObject::connect(app, &QGuiApplication::applicationStateChanged, app, clearKeyRecorderOnApplicationInActive); changeKeyboard(); @@ -950,7 +950,7 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg, const UINT msgType = msg.message; const quint32 scancode = (msg.lParam >> 16) & scancodeBitmask; - quint32 vk_key = quint32(msg.wParam); + auto vk_key = quint32(msg.wParam); quint32 nModifiers = 0; QWindow *receiver = m_keyGrabber ? m_keyGrabber : window; @@ -1182,7 +1182,7 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg, // results, if we map this virtual key-code directly (for eg '?' US layouts). So try // to find the correct key using the current message parameters & keyboard state. if (uch.isNull() && msgType == WM_IME_KEYDOWN) { - const QWindowsInputContext *windowsInputContext = + const auto *windowsInputContext = qobject_cast(QWindowsIntegration::instance()->inputContext()); if (!(windowsInputContext && windowsInputContext->isComposing())) vk_key = ImmGetVirtualKey(reinterpret_cast(window->winId())); diff --git a/src/plugins/platforms/windows/qwindowsmenu.cpp b/src/plugins/platforms/windows/qwindowsmenu.cpp index e55e283fe1..d20edd685e 100644 --- a/src/plugins/platforms/windows/qwindowsmenu.cpp +++ b/src/plugins/platforms/windows/qwindowsmenu.cpp @@ -518,7 +518,7 @@ QWindowsMenu::~QWindowsMenu() void QWindowsMenu::insertMenuItem(QPlatformMenuItem *menuItemIn, QPlatformMenuItem *before) { qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << menuItemIn << ", before=" << before << ')' << this; - QWindowsMenuItem *menuItem = static_cast(menuItemIn); + auto *menuItem = static_cast(menuItemIn); const int index = insertBefore(&m_menuItems, menuItemIn, before); const bool append = index == m_menuItems.size() - 1; menuItem->insertIntoMenu(this, append, index); @@ -689,7 +689,7 @@ void QWindowsPopupMenu::showPopup(const QWindow *parentWindow, const QRect &targ const QPlatformMenuItem *item) { qCDebug(lcQpaMenus) << __FUNCTION__ << '>' << this << parentWindow << targetRect << item; - const QWindowsBaseWindow *window = static_cast(parentWindow->handle()); + const auto *window = static_cast(parentWindow->handle()); const QPoint globalPos = window->mapToGlobal(targetRect.topLeft()); trackPopupMenu(window->handle(), globalPos.x(), globalPos.y()); } @@ -756,7 +756,7 @@ QWindowsMenuBar::~QWindowsMenuBar() void QWindowsMenuBar::insertMenu(QPlatformMenu *menuIn, QPlatformMenu *before) { qCDebug(lcQpaMenus) << __FUNCTION__ << menuIn << "before=" << before; - QWindowsMenu *menu = static_cast(menuIn); + auto *menu = static_cast(menuIn); const int index = insertBefore(&m_menus, menuIn, before); menu->insertIntoMenuBar(this, index == m_menus.size() - 1, index); } diff --git a/src/plugins/platforms/windows/qwindowsmime.cpp b/src/plugins/platforms/windows/qwindowsmime.cpp index 030d8d1e0f..b9d8b191f5 100644 --- a/src/plugins/platforms/windows/qwindowsmime.cpp +++ b/src/plugins/platforms/windows/qwindowsmime.cpp @@ -178,7 +178,7 @@ static bool qt_write_dibv5(QDataStream &s, QImage image) if (image.format() != QImage::Format_ARGB32) image = image.convertToFormat(QImage::Format_ARGB32); - uchar *buf = new uchar[bpl_bmp]; + auto *buf = new uchar[bpl_bmp]; memset(buf, 0, size_t(bpl_bmp)); for (int y=image.height()-1; y>=0; y--) { @@ -264,7 +264,7 @@ static bool qt_read_dibv5(QDataStream &s, QImage &image) const int bpl = image.bytesPerLine(); uchar *data = image.bits(); - uchar *buf24 = new uchar[bpl]; + auto *buf24 = new uchar[bpl]; const int bpl24 = ((w * nbits + 31) / 32) * 4; while (--h >= 0) { @@ -286,7 +286,7 @@ static bool qt_read_dibv5(QDataStream &s, QImage &image) if (bi.bV5Height < 0) { // Flip the image - uchar *buf = new uchar[bpl]; + auto *buf = new uchar[bpl]; h = -bi.bV5Height; for (int y = 0; y < h/2; ++y) { memcpy(buf, data + y * bpl, size_t(bpl)); @@ -772,16 +772,16 @@ bool QWindowsMimeURI::convertFromMime(const FORMATETC &formatetc, const QMimeDat } QByteArray result(size, '\0'); - DROPFILES* d = reinterpret_cast(result.data()); + auto* d = reinterpret_cast(result.data()); d->pFiles = sizeof(DROPFILES); GetCursorPos(&d->pt); // try d->fNC = true; char *files = (reinterpret_cast(d)) + d->pFiles; d->fWide = true; - wchar_t *f = reinterpret_cast(files); + auto *f = reinterpret_cast(files); for (int i=0; i(data.constData()); + const auto *hdrop = reinterpret_cast(data.constData()); if (hdrop->fWide) { - const wchar_t *filesw = reinterpret_cast(data.constData() + hdrop->pFiles); + const auto *filesw = reinterpret_cast(data.constData() + hdrop->pFiles); int i = 0; while (filesw[i]) { QString fileurl = QString::fromWCharArray(filesw + i); @@ -1055,7 +1055,7 @@ QVector QWindowsMimeImage::formatsForMime(const QString &mimeType, co QVector formatetcs; if (mimeData->hasImage() && mimeType == QLatin1String("application/x-qt-image")) { //add DIBV5 if image has alpha channel. Do not add CF_PNG here as it will confuse MS Office (QTBUG47656). - QImage image = qvariant_cast(mimeData->imageData()); + auto image = qvariant_cast(mimeData->imageData()); if (!image.isNull() && image.hasAlphaChannel()) formatetcs += setCf(CF_DIBV5); formatetcs += setCf(CF_DIB); @@ -1097,7 +1097,7 @@ bool QWindowsMimeImage::convertFromMime(const FORMATETC &formatetc, const QMimeD { int cf = getCf(formatetc); if ((cf == CF_DIB || cf == CF_DIBV5 || cf == int(CF_PNG)) && mimeData->hasImage()) { - QImage img = qvariant_cast(mimeData->imageData()); + auto img = qvariant_cast(mimeData->imageData()); if (img.isNull()) return false; QByteArray ba; diff --git a/src/plugins/platforms/windows/qwindowsmousehandler.cpp b/src/plugins/platforms/windows/qwindowsmousehandler.cpp index c15819a65f..6df5e6aa27 100644 --- a/src/plugins/platforms/windows/qwindowsmousehandler.cpp +++ b/src/plugins/platforms/windows/qwindowsmousehandler.cpp @@ -127,7 +127,7 @@ static inline QTouchDevice *createTouchDevice() qCDebug(lcQpaEvents) << "Digitizers:" << Qt::hex << Qt::showbase << (digitizers & ~NID_READY) << "Ready:" << (digitizers & NID_READY) << Qt::dec << Qt::noshowbase << "Tablet PC:" << tabletPc << "Max touch points:" << maxTouchPoints; - QTouchDevice *result = new QTouchDevice; + auto *result = new QTouchDevice; result->setType(digitizers & NID_INTEGRATED_TOUCH ? QTouchDevice::TouchScreen : QTouchDevice::TouchPad); QTouchDevice::Capabilities capabilities = QTouchDevice::Position | QTouchDevice::Area | QTouchDevice::NormalizedPosition; @@ -306,7 +306,7 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, // However, when tablet support is active, extraInfo is a packet serial number. This is not a problem // since we do not want to ignore mouse events coming from a tablet. // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms703320.aspx - const quint64 extraInfo = quint64(GetMessageExtraInfo()); + const auto extraInfo = quint64(GetMessageExtraInfo()); if ((extraInfo & signatureMask) == miWpSignature) { if (extraInfo & 0x80) { // Bit 7 indicates touch event, else tablet pen. source = Qt::MouseEventSynthesizedBySystem; @@ -370,7 +370,7 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, return true; } - QWindowsWindow *platformWindow = static_cast(window->handle()); + auto *platformWindow = static_cast(window->handle()); // If the window was recently resized via mouse doubleclick on the frame or title bar, // we don't get WM_LBUTTONDOWN or WM_LBUTTONDBLCLK for the second click, diff --git a/src/plugins/platforms/windows/qwindowsnativeinterface.cpp b/src/plugins/platforms/windows/qwindowsnativeinterface.cpp index e581b30ced..d1d181d66e 100644 --- a/src/plugins/platforms/windows/qwindowsnativeinterface.cpp +++ b/src/plugins/platforms/windows/qwindowsnativeinterface.cpp @@ -98,7 +98,7 @@ void *QWindowsNativeInterface::nativeResourceForWindow(const QByteArray &resourc qWarning("%s: '%s' requested for null window or window without handle.", __FUNCTION__, resource.constData()); return nullptr; } - QWindowsWindow *bw = static_cast(window->handle()); + auto *bw = static_cast(window->handle()); int type = resourceType(resource); if (type == HandleType) return bw->handle(); @@ -131,7 +131,7 @@ void *QWindowsNativeInterface::nativeResourceForScreen(const QByteArray &resourc qWarning("%s: '%s' requested for null screen or screen without handle.", __FUNCTION__, resource.constData()); return nullptr; } - QWindowsScreen *bs = static_cast(screen->handle()); + auto *bs = static_cast(screen->handle()); int type = resourceType(resource); if (type == HandleType) return bs->handle(); @@ -157,7 +157,7 @@ static const char customMarginPropertyC[] = "WindowsCustomMargins"; QVariant QWindowsNativeInterface::windowProperty(QPlatformWindow *window, const QString &name) const { - QWindowsWindow *platformWindow = static_cast(window); + auto *platformWindow = static_cast(window); if (name == QLatin1String(customMarginPropertyC)) return QVariant::fromValue(platformWindow->customMargins()); return QVariant(); @@ -171,7 +171,7 @@ QVariant QWindowsNativeInterface::windowProperty(QPlatformWindow *window, const void QWindowsNativeInterface::setWindowProperty(QPlatformWindow *window, const QString &name, const QVariant &value) { - QWindowsWindow *platformWindow = static_cast(window); + auto *platformWindow = static_cast(window); if (name == QLatin1String(customMarginPropertyC)) platformWindow->setCustomMargins(qvariant_cast(value)); } @@ -206,7 +206,7 @@ void *QWindowsNativeInterface::nativeResourceForContext(const QByteArray &resour return nullptr; } - QWindowsOpenGLContext *glcontext = static_cast(context->handle()); + auto *glcontext = static_cast(context->handle()); switch (resourceType(resource)) { case RenderingContextType: // Fall through. case EglContextType: diff --git a/src/plugins/platforms/windows/qwindowsole.cpp b/src/plugins/platforms/windows/qwindowsole.cpp index fb6a74581a..f3450e2806 100644 --- a/src/plugins/platforms/windows/qwindowsole.cpp +++ b/src/plugins/platforms/windows/qwindowsole.cpp @@ -155,7 +155,7 @@ QWindowsOleDataObject::SetData(LPFORMATETC pFormatetc, STGMEDIUM *pMedium, BOOL HRESULT hr = ResultFromScode(E_NOTIMPL); if (pFormatetc->cfFormat == CF_PERFORMEDDROPEFFECT && pMedium->tymed == TYMED_HGLOBAL) { - DWORD * val = (DWORD*)GlobalLock(pMedium->hGlobal); + auto * val = (DWORD*)GlobalLock(pMedium->hGlobal); performedEffect = *val; GlobalUnlock(pMedium->hGlobal); if (fRelease) @@ -193,7 +193,7 @@ QWindowsOleDataObject::EnumFormatEtc(DWORD dwDirection, LPENUMFORMATETC FAR* ppe fmtetcs.append(formatetc); } - QWindowsOleEnumFmtEtc *enumFmtEtc = new QWindowsOleEnumFmtEtc(fmtetcs); + auto *enumFmtEtc = new QWindowsOleEnumFmtEtc(fmtetcs); *ppenumFormatEtc = enumFmtEtc; if (enumFmtEtc->isNull()) { delete enumFmtEtc; @@ -237,7 +237,7 @@ QWindowsOleEnumFmtEtc::QWindowsOleEnumFmtEtc(const QVector &fmtetcs) qCDebug(lcQpaMime) << __FUNCTION__ << fmtetcs; m_lpfmtetcs.reserve(fmtetcs.count()); for (int idx = 0; idx < fmtetcs.count(); ++idx) { - LPFORMATETC destetc = new FORMATETC(); + auto destetc = new FORMATETC(); if (copyFormatEtc(destetc, &(fmtetcs.at(idx)))) { m_lpfmtetcs.append(destetc); } else { @@ -255,7 +255,7 @@ QWindowsOleEnumFmtEtc::QWindowsOleEnumFmtEtc(const QVector &lpfmtet m_lpfmtetcs.reserve(lpfmtetcs.count()); for (int idx = 0; idx < lpfmtetcs.count(); ++idx) { LPFORMATETC srcetc = lpfmtetcs.at(idx); - LPFORMATETC destetc = new FORMATETC(); + auto destetc = new FORMATETC(); if (copyFormatEtc(destetc, srcetc)) { m_lpfmtetcs.append(destetc); } else { @@ -357,7 +357,7 @@ QWindowsOleEnumFmtEtc::Clone(LPENUMFORMATETC FAR* newEnum) if (newEnum == nullptr) return ResultFromScode(E_INVALIDARG); - QWindowsOleEnumFmtEtc *result = new QWindowsOleEnumFmtEtc(m_lpfmtetcs); + auto *result = new QWindowsOleEnumFmtEtc(m_lpfmtetcs); result->m_nIndex = m_nIndex; if (result->isNull()) { diff --git a/src/plugins/platforms/windows/qwindowsopengltester.cpp b/src/plugins/platforms/windows/qwindowsopengltester.cpp index ff495c8290..afc1991e2c 100644 --- a/src/plugins/platforms/windows/qwindowsopengltester.cpp +++ b/src/plugins/platforms/windows/qwindowsopengltester.cpp @@ -457,7 +457,7 @@ bool QWindowsOpenGLTester::testDesktopGL() // Check the version. If we got 1.x then it's all hopeless and we can stop right here. typedef const GLubyte * (APIENTRY * GetString_t)(GLenum name); - GetString_t GetString = reinterpret_cast( + auto GetString = reinterpret_cast( reinterpret_cast(::GetProcAddress(lib, "glGetString"))); if (GetString) { if (const char *versionStr = reinterpret_cast(GetString(GL_VERSION))) { diff --git a/src/plugins/platforms/windows/qwindowspointerhandler.cpp b/src/plugins/platforms/windows/qwindowspointerhandler.cpp index 36c614af34..ee24f62281 100644 --- a/src/plugins/platforms/windows/qwindowspointerhandler.cpp +++ b/src/plugins/platforms/windows/qwindowspointerhandler.cpp @@ -318,7 +318,7 @@ static QTouchDevice *createTouchDevice() qCDebug(lcQpaEvents) << "Digitizers:" << Qt::hex << Qt::showbase << (digitizers & ~NID_READY) << "Ready:" << (digitizers & NID_READY) << Qt::dec << Qt::noshowbase << "Tablet PC:" << tabletPc << "Max touch points:" << maxTouchPoints; - QTouchDevice *result = new QTouchDevice; + auto *result = new QTouchDevice; result->setType(digitizers & NID_INTEGRATED_TOUCH ? QTouchDevice::TouchScreen : QTouchDevice::TouchPad); QTouchDevice::Capabilities capabilities = QTouchDevice::Position | QTouchDevice::Area | QTouchDevice::NormalizedPosition; @@ -348,7 +348,7 @@ void QWindowsPointerHandler::handleCaptureRelease(QWindow *window, QEvent::Type eventType, Qt::MouseButtons mouseButtons) { - QWindowsWindow *platformWindow = static_cast(window->handle()); + auto *platformWindow = static_cast(window->handle()); // Qt expects the platform plugin to capture the mouse on any button press until release. if (!platformWindow->hasMouseCapture() && eventType == QEvent::MouseButtonPress) { @@ -384,7 +384,7 @@ void QWindowsPointerHandler::handleEnterLeave(QWindow *window, QWindow *currentWindowUnderPointer, QPoint globalPos) { - QWindowsWindow *platformWindow = static_cast(window->handle()); + auto *platformWindow = static_cast(window->handle()); const bool hasCapture = platformWindow->hasMouseCapture(); // No enter or leave events are sent as long as there is an autocapturing window. @@ -468,7 +468,7 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd, if (!screen) return false; - POINTER_TOUCH_INFO *touchInfo = static_cast(vTouchInfo); + auto *touchInfo = static_cast(vTouchInfo); const QRect screenGeometry = screen->geometry(); @@ -548,13 +548,13 @@ bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWin if (et & QtWindows::NonClientEventFlag) return false; // Let DefWindowProc() handle Non Client messages. - POINTER_PEN_INFO *penInfo = static_cast(vPenInfo); + auto *penInfo = static_cast(vPenInfo); RECT pRect, dRect; if (!QWindowsContext::user32dll.getPointerDeviceRects(penInfo->pointerInfo.sourceDevice, &pRect, &dRect)) return false; - const qint64 sourceDevice = (qint64)penInfo->pointerInfo.sourceDevice; + const auto sourceDevice = (qint64)penInfo->pointerInfo.sourceDevice; const QPoint globalPos = QPoint(penInfo->pointerInfo.ptPixelLocation.x, penInfo->pointerInfo.ptPixelLocation.y); const QPoint localPos = QWindowsGeometryHint::mapFromGlobal(hwnd, globalPos); const QPointF hiResGlobalPos = QPointF(dRect.left + qreal(penInfo->pointerInfo.ptHimetricLocation.x - pRect.left) diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp index e3931b3bb1..8541129c83 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.cpp +++ b/src/plugins/platforms/windows/qwindowsscreen.cpp @@ -120,7 +120,7 @@ BOOL QT_WIN_CALLBACK monitorEnumCallback(HMONITOR hMonitor, HDC, LPRECT, LPARAM { QWindowsScreenData data; if (monitorData(hMonitor, &data)) { - WindowsScreenDataList *result = reinterpret_cast(p); + auto *result = reinterpret_cast(p); // QWindowSystemInterface::handleScreenAdded() documentation specifies that first // added screen will be the primary screen, so order accordingly. // Note that the side effect of this policy is that there is no way to change primary @@ -552,7 +552,7 @@ bool QWindowsScreenManager::handleScreenChanges() if (existingIndex != -1) { m_screens.at(existingIndex)->handleChanges(newData); } else { - QWindowsScreen *newScreen = new QWindowsScreen(newData); + auto *newScreen = new QWindowsScreen(newData); m_screens.push_back(newScreen); QWindowSystemInterface::handleScreenAdded(newScreen, newData.flags & QWindowsScreenData::PrimaryScreen); diff --git a/src/plugins/platforms/windows/qwindowsservices.cpp b/src/plugins/platforms/windows/qwindowsservices.cpp index 9504513a5e..b2b1dee232 100644 --- a/src/plugins/platforms/windows/qwindowsservices.cpp +++ b/src/plugins/platforms/windows/qwindowsservices.cpp @@ -57,7 +57,7 @@ static inline bool shellExecute(const QUrl &url) const QString nativeFilePath = url.isLocalFile() && !url.hasFragment() && !url.hasQuery() ? QDir::toNativeSeparators(url.toLocalFile()) : url.toString(QUrl::FullyEncoded); - const quintptr result = + const auto result = reinterpret_cast(ShellExecute(nullptr, nullptr, reinterpret_cast(nativeFilePath.utf16()), nullptr, nullptr, SW_SHOWNORMAL)); diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp index b75c64c40e..b2074a0795 100644 --- a/src/plugins/platforms/windows/qwindowstheme.cpp +++ b/src/plugins/platforms/windows/qwindowstheme.cpp @@ -583,7 +583,7 @@ Q_GUI_EXPORT QPixmap qt_pixmapFromWinHICON(HICON icon); static QPixmap loadIconFromShell32(int resourceId, QSizeF size) { if (const HMODULE hmod = QSystemLibrary::load(L"shell32")) { - HICON iconHandle = + auto iconHandle = static_cast(LoadImage(hmod, MAKEINTRESOURCE(resourceId), IMAGE_ICON, int(size.width()), int(size.height()), 0)); if (iconHandle) { diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index 3669407765..532837815f 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -764,8 +764,8 @@ QWindowsWindowData if (title.isEmpty() && (result.flags & Qt::WindowTitleHint)) title = topLevel ? qAppName() : w->objectName(); - const wchar_t *titleUtf16 = reinterpret_cast(title.utf16()); - const wchar_t *classNameUtf16 = reinterpret_cast(windowClassName.utf16()); + const auto *titleUtf16 = reinterpret_cast(title.utf16()); + const auto *classNameUtf16 = reinterpret_cast(windowClassName.utf16()); // Capture events before CreateWindowEx() returns. The context is cleared in // the QWindowsWindow constructor. @@ -980,7 +980,7 @@ bool QWindowsGeometryHint::handleCalculateSize(const QMargins &customMargins, co if (!msg.wParam || customMargins.isNull()) return false; *result = DefWindowProc(msg.hwnd, msg.message, msg.wParam, msg.lParam); - NCCALCSIZE_PARAMS *ncp = reinterpret_cast(msg.lParam); + auto *ncp = reinterpret_cast(msg.lParam); const RECT oldClientArea = ncp->rgrc[0]; ncp->rgrc[0].left += customMargins.left(); ncp->rgrc[0].top += customMargins.top(); @@ -1500,7 +1500,7 @@ QWindow *QWindowsWindow::topLevelOf(QWindow *w) w = parent; if (const QPlatformWindow *handle = w->handle()) { - const QWindowsWindow *ww = static_cast(handle); + const auto *ww = static_cast(handle); if (ww->isEmbedded()) { HWND parentHWND = GetAncestor(ww->handle(), GA_PARENT); const HWND desktopHwnd = GetDesktopWindow(); @@ -1574,7 +1574,7 @@ bool QWindowsWindow::isActive() const bool QWindowsWindow::isAncestorOf(const QPlatformWindow *child) const { - const QWindowsWindow *childWindow = static_cast(child); + const auto *childWindow = static_cast(child); return IsChild(m_data.hwnd, childWindow->handle()); } @@ -1723,7 +1723,7 @@ void QWindowsWindow::setParent_sys(const QPlatformWindow *parent) HWND oldParentHWND = parentHwnd(); HWND newParentHWND = nullptr; if (parent) { - const QWindowsWindow *parentW = static_cast(parent); + const auto *parentW = static_cast(parent); newParentHWND = parentW->handle(); } @@ -2366,7 +2366,7 @@ void QWindowsWindow::propagateSizeHints() bool QWindowsWindow::handleGeometryChangingMessage(MSG *message, const QWindow *qWindow, const QMargins &margins) { - WINDOWPOS *windowPos = reinterpret_cast(message->lParam); + auto *windowPos = reinterpret_cast(message->lParam); if ((windowPos->flags & SWP_NOZORDER) == 0) { if (QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(qWindow)) { QWindow *parentWindow = qWindow->parent(); diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp index a427e553f0..b276798316 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp @@ -82,7 +82,7 @@ QWindowsUiaMainProvider *QWindowsUiaMainProvider::providerForAccessible(QAccessi QAccessible::Id id = QAccessible::uniqueId(accessible); QWindowsUiaProviderCache *providerCache = QWindowsUiaProviderCache::instance(); - QWindowsUiaMainProvider *provider = qobject_cast(providerCache->providerForId(id)); + auto *provider = qobject_cast(providerCache->providerForId(id)); if (provider) { provider->AddRef(); diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.cpp index 9d1e72fb78..50ecfc7ecd 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.cpp @@ -100,7 +100,7 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaTextProvider::GetSelection(SAFEARRAY **pRet for (LONG i = 0; i < selCount; ++i) { int startOffset = 0, endOffset = 0; textInterface->selection((int)i, &startOffset, &endOffset); - QWindowsUiaTextRangeProvider *textRangeProvider = new QWindowsUiaTextRangeProvider(id(), startOffset, endOffset); + auto *textRangeProvider = new QWindowsUiaTextRangeProvider(id(), startOffset, endOffset); SafeArrayPutElement(*pRetVal, &i, static_cast(textRangeProvider)); textRangeProvider->Release(); } @@ -110,7 +110,7 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaTextProvider::GetSelection(SAFEARRAY **pRet if ((*pRetVal = SafeArrayCreateVector(VT_UNKNOWN, 0, 1))) { LONG i = 0; int cursorPosition = textInterface->cursorPosition(); - QWindowsUiaTextRangeProvider *textRangeProvider = new QWindowsUiaTextRangeProvider(id(), cursorPosition, cursorPosition); + auto *textRangeProvider = new QWindowsUiaTextRangeProvider(id(), cursorPosition, cursorPosition); SafeArrayPutElement(*pRetVal, &i, static_cast(textRangeProvider)); textRangeProvider->Release(); } @@ -138,7 +138,7 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaTextProvider::GetVisibleRanges(SAFEARRAY ** // Considering the entire text as visible. if ((*pRetVal = SafeArrayCreateVector(VT_UNKNOWN, 0, 1))) { LONG i = 0; - QWindowsUiaTextRangeProvider *textRangeProvider = new QWindowsUiaTextRangeProvider(id(), 0, textInterface->characterCount()); + auto *textRangeProvider = new QWindowsUiaTextRangeProvider(id(), 0, textInterface->characterCount()); SafeArrayPutElement(*pRetVal, &i, static_cast(textRangeProvider)); textRangeProvider->Release(); } diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.cpp index 1be186f6b3..8e395669f8 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.cpp @@ -92,7 +92,7 @@ HRESULT QWindowsUiaTextRangeProvider::Compare(ITextRangeProvider *range, BOOL *p if (!range || !pRetVal) return E_INVALIDARG; - QWindowsUiaTextRangeProvider *targetProvider = static_cast(range); + auto *targetProvider = static_cast(range); *pRetVal = ((targetProvider->m_startOffset == m_startOffset) && (targetProvider->m_endOffset == m_endOffset)); return S_OK; } @@ -110,7 +110,7 @@ HRESULT QWindowsUiaTextRangeProvider::CompareEndpoints(TextPatternRangeEndpoint if (!targetRange || !pRetVal) return E_INVALIDARG; - QWindowsUiaTextRangeProvider *targetProvider = static_cast(targetRange); + auto *targetProvider = static_cast(targetRange); int point = (endpoint == TextPatternRangeEndpoint_Start) ? m_startOffset : m_endOffset; int targetPoint = (targetEndpoint == TextPatternRangeEndpoint_Start) ? @@ -373,7 +373,7 @@ HRESULT QWindowsUiaTextRangeProvider::MoveEndpointByRange(TextPatternRangeEndpoi qCDebug(lcQpaUiAutomation) << __FUNCTION__ << "endpoint=" << endpoint << "targetRange=" << targetRange << "targetEndpoint=" << targetEndpoint << "this: " << this; - QWindowsUiaTextRangeProvider *targetProvider = static_cast(targetRange); + auto *targetProvider = static_cast(targetRange); int targetPoint = (targetEndpoint == TextPatternRangeEndpoint_Start) ? targetProvider->m_startOffset : targetProvider->m_endOffset; -- cgit v1.2.3 From 2f84ec4bcd35855e33140d8c985d640d8ece62bc Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Thu, 6 Jun 2019 10:52:22 +0200 Subject: uic: Introduce nullptr Apply Fixits by Qt Creator. Change-Id: Ic2d65b2604d1d71d910773e02bcdf2466f49e52c Reviewed-by: Jarek Kobus --- src/tools/uic/cpp/cppwriteinitialization.cpp | 7 +++---- src/tools/uic/cpp/cppwriteinitialization.h | 2 +- src/tools/uic/driver.cpp | 2 +- src/tools/uic/main.cpp | 2 +- src/tools/uic/uic.cpp | 8 ++++---- 5 files changed, 10 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/tools/uic/cpp/cppwriteinitialization.cpp b/src/tools/uic/cpp/cppwriteinitialization.cpp index 440758cf41..96801abe6a 100644 --- a/src/tools/uic/cpp/cppwriteinitialization.cpp +++ b/src/tools/uic/cpp/cppwriteinitialization.cpp @@ -706,7 +706,7 @@ void WriteInitialization::acceptWidget(DomWidget *node) addWizardPage(varName, node, parentWidget); } else if (m_uic->customWidgetsInfo()->extends(parentClass, QLatin1String("QToolBox"))) { const DomProperty *plabel = attributes.value(QLatin1String("label")); - DomString *plabelString = plabel ? plabel->elementString() : 0; + DomString *plabelString = plabel ? plabel->elementString() : nullptr; QString icon; if (const DomProperty *picon = attributes.value(QLatin1String("icon"))) icon = QLatin1String(", ") + iconCall(picon); // Side effect: Writes icon definition @@ -729,7 +729,7 @@ void WriteInitialization::acceptWidget(DomWidget *node) } } else if (m_uic->customWidgetsInfo()->extends(parentClass, QLatin1String("QTabWidget"))) { const DomProperty *ptitle = attributes.value(QLatin1String("title")); - DomString *ptitleString = ptitle ? ptitle->elementString() : 0; + DomString *ptitleString = ptitle ? ptitle->elementString() : nullptr; QString icon; if (const DomProperty *picon = attributes.value(QLatin1String("icon"))) icon = QLatin1String(", ") + iconCall(picon); // Side effect: Writes icon definition @@ -844,7 +844,7 @@ void WriteInitialization::addButtonGroup(const DomWidget *buttonNode, const QStr const DomButtonGroup *group = m_driver->findButtonGroup(attributeName); // Legacy feature: Create missing groups on the fly as the UIC button group feature // was present before the actual Designer support (4.5) - const bool createGroupOnTheFly = group == 0; + const bool createGroupOnTheFly = group == nullptr; if (createGroupOnTheFly) { DomButtonGroup *newGroup = new DomButtonGroup; newGroup->setAttributeName(attributeName); @@ -2619,7 +2619,6 @@ static void generateMultiDirectiveEnd(QTextStream &outputStream, const QSet m_children; - Item *m_parent; + Item *m_parent = nullptr; const QString m_itemClassName; const QString m_indent; diff --git a/src/tools/uic/driver.cpp b/src/tools/uic/driver.cpp index eb88032e59..8b9b4806e6 100644 --- a/src/tools/uic/driver.cpp +++ b/src/tools/uic/driver.cpp @@ -256,7 +256,7 @@ bool Driver::uic(const QString &fileName, DomUI *ui, QTextStream *out) QTextStream *oldOutput = m_output; - m_output = out != 0 ? out : &m_stdout; + m_output = out != nullptr ? out : &m_stdout; Uic tool(this); const bool result = tool.write(ui); diff --git a/src/tools/uic/main.cpp b/src/tools/uic/main.cpp index 166fe78ff7..439789d221 100644 --- a/src/tools/uic/main.cpp +++ b/src/tools/uic/main.cpp @@ -137,7 +137,7 @@ int runUic(int argc, char *argv[]) return !driver.printDependencies(inputFile); } - QTextStream *out = 0; + QTextStream *out = nullptr; QFile f; if (!driver.option().outputFile.isEmpty()) { f.setFileName(driver.option().outputFile); diff --git a/src/tools/uic/uic.cpp b/src/tools/uic/uic.cpp index b95f1a784b..207356f28d 100644 --- a/src/tools/uic/uic.cpp +++ b/src/tools/uic/uic.cpp @@ -69,7 +69,7 @@ bool Uic::printDependencies() return false; } - DomUI *ui = 0; + DomUI *ui = nullptr; { QXmlStreamReader reader; reader.setDevice(&f); @@ -178,7 +178,7 @@ static double versionFromUiAttribute(QXmlStreamReader &reader) DomUI *Uic::parseUiFile(QXmlStreamReader &reader) { - DomUI *ui = 0; + DomUI *ui = nullptr; const QString uiElement = QLatin1String("ui"); while (!reader.atEnd()) { @@ -189,7 +189,7 @@ DomUI *Uic::parseUiFile(QXmlStreamReader &reader) if (version < 4.0) { const QString msg = QString::fromLatin1("uic: File generated with too old version of Qt Designer (%1)").arg(version); fprintf(stderr, "%s\n", qPrintable(msg)); - return 0; + return nullptr; } ui = new DomUI(); @@ -201,7 +201,7 @@ DomUI *Uic::parseUiFile(QXmlStreamReader &reader) } if (reader.hasError()) { delete ui; - ui = 0; + ui = nullptr; fprintf(stderr, "%s\n", qPrintable(QString::fromLatin1("uic: Error in line %1, column %2 : %3") .arg(reader.lineNumber()).arg(reader.columnNumber()) .arg(reader.errorString()))); -- cgit v1.2.3 From 5343a7018eadecbe30b698a22a31034adba9b756 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Thu, 6 Jun 2019 10:57:04 +0200 Subject: uic: Replace 'typedef' by 'using' Apply Fixits by Qt Creator with some amendments. Change-Id: I152cb5935ff7d649de297b010b9253c625c7da84 Reviewed-by: Jarek Kobus --- src/tools/uic/cpp/cppwriteincludes.h | 4 ++-- src/tools/uic/cpp/cppwriteinitialization.h | 12 ++++++------ src/tools/uic/customwidgetsinfo.h | 2 +- src/tools/uic/treewalker.h | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/tools/uic/cpp/cppwriteincludes.h b/src/tools/uic/cpp/cppwriteincludes.h index 79cbd41014..aadc6f54fc 100644 --- a/src/tools/uic/cpp/cppwriteincludes.h +++ b/src/tools/uic/cpp/cppwriteincludes.h @@ -82,7 +82,7 @@ private: void add(const QString &className, bool determineHeader = true, const QString &header = QString(), bool global = false); private: - typedef std::set OrderedSet; + using OrderedSet = std::set; void insertIncludeForClass(const QString &className, QString header = QString(), bool global = false); void insertInclude(const QString &header, bool global); void writeHeaders(const OrderedSet &headers, bool global); @@ -97,7 +97,7 @@ private: QSet m_knownClasses; - typedef QMap StringMap; + using StringMap = QMap; StringMap m_classToHeader; StringMap m_oldHeaderToNewHeader; diff --git a/src/tools/uic/cpp/cppwriteinitialization.h b/src/tools/uic/cpp/cppwriteinitialization.h index af8b964777..0ffa5f67af 100644 --- a/src/tools/uic/cpp/cppwriteinitialization.h +++ b/src/tools/uic/cpp/cppwriteinitialization.h @@ -85,8 +85,8 @@ namespace CPP { struct WriteInitialization : public TreeWalker { - typedef QList DomPropertyList; - typedef QHash DomPropertyMap; + using DomPropertyList = QList; + using DomPropertyMap = QHash; WriteInitialization(Uic *uic); @@ -259,13 +259,13 @@ private: QVector m_buddies; QSet m_buttonGroups; - typedef QHash ColorBrushHash; + using ColorBrushHash = QHash; ColorBrushHash m_colorBrushHash; // Map from font properties to font variable name for reuse // Map from size policy to variable for reuse - typedef QMap FontPropertiesNameMap; - typedef QMap IconPropertiesNameMap; - typedef QMap SizePolicyNameMap; + using FontPropertiesNameMap = QMap; + using IconPropertiesNameMap = QMap; + using SizePolicyNameMap = QMap; FontPropertiesNameMap m_fontPropertiesNameMap; IconPropertiesNameMap m_iconPropertiesNameMap; SizePolicyNameMap m_sizePolicyNameMap; diff --git a/src/tools/uic/customwidgetsinfo.h b/src/tools/uic/customwidgetsinfo.h index 8a10999027..a1b24ab042 100644 --- a/src/tools/uic/customwidgetsinfo.h +++ b/src/tools/uic/customwidgetsinfo.h @@ -62,7 +62,7 @@ public: bool isCustomWidgetContainer(const QString &className) const; private: - typedef QMap NameCustomWidgetMap; + using NameCustomWidgetMap = QMap; NameCustomWidgetMap m_customWidgets; }; diff --git a/src/tools/uic/treewalker.h b/src/tools/uic/treewalker.h index 7e8eda57d9..1ad9b51409 100644 --- a/src/tools/uic/treewalker.h +++ b/src/tools/uic/treewalker.h @@ -101,7 +101,7 @@ struct TreeWalker virtual void acceptTime(DomTime *time); virtual void acceptDateTime(DomDateTime *dateTime); virtual void acceptProperty(DomProperty *property); - typedef QVector DomWidgets; + using DomWidgets = QVector; virtual void acceptIncludes(DomIncludes *includes); virtual void acceptInclude(DomInclude *incl); virtual void acceptAction(DomAction *action); -- cgit v1.2.3 From 2e20ae3c1b57169497f6f3904623be4f5e617e12 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Thu, 6 Jun 2019 11:03:01 +0200 Subject: uic: Fix some clang warnings - Use range-based for - Use isEmpty() instead .size(), streamline code - Fix warnings about class definitions, use Q_DISABLE_COPY_MOVE and '= default' for trivial constructors Change-Id: I76255fd9d80c3faffebda9a438e86e918c16d289 Reviewed-by: Jarek Kobus --- src/tools/uic/cpp/cppwriteinitialization.cpp | 7 ++----- src/tools/uic/cpp/cppwriteinitialization.h | 2 +- src/tools/uic/databaseinfo.cpp | 9 +++------ src/tools/uic/treewalker.h | 3 +++ 4 files changed, 9 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/tools/uic/cpp/cppwriteinitialization.cpp b/src/tools/uic/cpp/cppwriteinitialization.cpp index 96801abe6a..85b9a9f60b 100644 --- a/src/tools/uic/cpp/cppwriteinitialization.cpp +++ b/src/tools/uic/cpp/cppwriteinitialization.cpp @@ -498,9 +498,7 @@ void WriteInitialization::acceptUI(DomUI *node) << language::startFunctionDefinition1("setupUi", parameterType, varName, m_option.indent); const QStringList connections = m_uic->databaseInfo()->connections(); - for (int i=0; iattributeName(); if (propertyName == QLatin1String("leftMargin") && p->kind() == DomProperty::Number) left = true; diff --git a/src/tools/uic/cpp/cppwriteinitialization.h b/src/tools/uic/cpp/cppwriteinitialization.h index 0ffa5f67af..3cd0efeaac 100644 --- a/src/tools/uic/cpp/cppwriteinitialization.h +++ b/src/tools/uic/cpp/cppwriteinitialization.h @@ -161,7 +161,7 @@ private: // special initialization // class Item { - Q_DISABLE_COPY(Item) + Q_DISABLE_COPY_MOVE(Item) public: Item(const QString &itemClassName, const QString &indent, QTextStream &setupUiStream, QTextStream &retranslateUiStream, Driver *driver); ~Item(); diff --git a/src/tools/uic/databaseinfo.cpp b/src/tools/uic/databaseinfo.cpp index fa5d5f5df0..9b0d1614ab 100644 --- a/src/tools/uic/databaseinfo.cpp +++ b/src/tools/uic/databaseinfo.cpp @@ -33,9 +33,7 @@ QT_BEGIN_NAMESPACE -DatabaseInfo::DatabaseInfo() -{ -} +DatabaseInfo::DatabaseInfo() = default; void DatabaseInfo::acceptUI(DomUI *node) { @@ -59,10 +57,9 @@ void DatabaseInfo::acceptWidget(DomWidget *node) DomProperty *db = properties.value(QLatin1String("database")); if (db && db->elementStringList()) { QStringList info = db->elementStringList()->elementString(); - - QString connection = info.size() > 0 ? info.at(0) : QString(); - if (connection.isEmpty()) + if (info.isEmpty() || info.constFirst().isEmpty()) return; + const QString &connection = info.constFirst(); m_connections.append(connection); QString table = info.size() > 1 ? info.at(1) : QString(); diff --git a/src/tools/uic/treewalker.h b/src/tools/uic/treewalker.h index 1ad9b51409..3777229517 100644 --- a/src/tools/uic/treewalker.h +++ b/src/tools/uic/treewalker.h @@ -77,6 +77,9 @@ class DomButtonGroup; struct TreeWalker { + Q_DISABLE_COPY_MOVE(TreeWalker) + + TreeWalker() = default; inline virtual ~TreeWalker() = default; virtual void acceptUI(DomUI *ui); -- cgit v1.2.3 From 486c55d7437c247472e287f0f2ccc883399d9f79 Mon Sep 17 00:00:00 2001 From: Andreas Hartmetz Date: Mon, 10 Jun 2019 14:42:53 +0200 Subject: constify, and micro-optimize not detaching Change-Id: I08c3c35e27a2b5e816a1532d0bd7cc09459800ab Reviewed-by: Marc Mutz Reviewed-by: Konstantin Ritt --- src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp b/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp index e545d54ec2..7abf295782 100644 --- a/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp +++ b/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp @@ -721,7 +721,7 @@ QStringList QFontconfigDatabase::fallbacksForFamily(const QString &family, QFont FcValue value; value.type = FcTypeString; - QByteArray cs = family.toUtf8(); + const QByteArray cs = family.toUtf8(); value.u.s = (const FcChar8 *)cs.data(); FcPatternAdd(pattern,FC_FAMILY,value,true); @@ -863,7 +863,7 @@ QString QFontconfigDatabase::resolveFontFamilyAlias(const QString &family) const return family; if (!family.isEmpty()) { - QByteArray cs = family.toUtf8(); + const QByteArray cs = family.toUtf8(); FcPatternAddString(pattern, FC_FAMILY, (const FcChar8 *) cs.constData()); } FcConfigSubstitute(0, pattern, FcMatchPattern); -- cgit v1.2.3 From 6faa4d4a87662a6254542da37b3a6fff528e0a6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Mon, 11 Dec 2017 21:12:10 +0100 Subject: QFuture: Wait for result on iterator advance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wait for the result at the target index if the future is running and the iterator index is past the current result count. Determine if there is a result at the target index after waitForResult() returns, and return -1/end if not. Also support decrementing the end iterator. In this case wait for the future to finish in order to get the final result count. Task-number: QTBUG-59811 Change-Id: I8fcc711bab2e72c3c5196a55b794d25e18bb324d Reviewed-by: Mårten Nordheim --- src/corelib/thread/qfuture.h | 82 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 64 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/corelib/thread/qfuture.h b/src/corelib/thread/qfuture.h index a456dd9139..d3135510b3 100644 --- a/src/corelib/thread/qfuture.h +++ b/src/corelib/thread/qfuture.h @@ -111,33 +111,79 @@ public: typedef const T &reference; inline const_iterator() {} - inline const_iterator(QFuture const * const _future, int _index) : future(_future), index(_index) {} + inline const_iterator(QFuture const * const _future, int _index) + : future(_future), index(advanceIndex(_index, 0)) { } inline const_iterator(const const_iterator &o) : future(o.future), index(o.index) {} inline const_iterator &operator=(const const_iterator &o) { future = o.future; index = o.index; return *this; } inline const T &operator*() const { return future->d.resultReference(index); } inline const T *operator->() const { return future->d.resultPointer(index); } - - inline bool operator!=(const const_iterator &other) const + inline bool operator!=(const const_iterator &other) const { return index != other.index; } + inline bool operator==(const const_iterator &o) const { return !operator!=(o); } + inline const_iterator &operator++() + { index = advanceIndex(index, 1); return *this; } + inline const_iterator &operator--() + { index = advanceIndex(index, -1); return *this; } + inline const_iterator operator++(int) + { + const_iterator r = *this; + index = advanceIndex(index, 1); + return r; + } + inline const_iterator operator--(int) { - if (index == -1 && other.index == -1) // comparing end != end? - return false; - if (other.index == -1) - return (future->isRunning() || (index < future->resultCount())); - return (index != other.index); + const_iterator r = *this; + index = advanceIndex(index, -1); + return r; } + inline const_iterator operator+(int j) const + { return const_iterator(future, advanceIndex(index, j)); } + inline const_iterator operator-(int j) const + { return const_iterator(future, advanceIndex(index, -j)); } + inline const_iterator &operator+=(int j) + { index = advanceIndex(index, j); return *this; } + inline const_iterator &operator-=(int j) + { index = advanceIndex(index, -j); return *this; } + friend inline const_iterator operator+(int j, const_iterator k) + { return const_iterator(k.future, k.advanceIndex(k.index, j)); } - inline bool operator==(const const_iterator &o) const { return !operator!=(o); } - inline const_iterator &operator++() { ++index; return *this; } - inline const_iterator operator++(int) { const_iterator r = *this; ++index; return r; } - inline const_iterator &operator--() { --index; return *this; } - inline const_iterator operator--(int) { const_iterator r = *this; --index; return r; } - inline const_iterator operator+(int j) const { return const_iterator(future, index + j); } - inline const_iterator operator-(int j) const { return const_iterator(future, index - j); } - inline const_iterator &operator+=(int j) { index += j; return *this; } - inline const_iterator &operator-=(int j) { index -= j; return *this; } - friend inline const_iterator operator+(int j, const_iterator k) { return k + j; } private: + /*! \internal + + Advances the iterator index \a idx \a n steps, waits for the + result at the target index, and returns the target index. + + The index may be -1, indicating the end iterator, either + as the argument or as the return value. The end iterator + may be decremented. + + The caller is responsible for not advancing the iterator + before begin() or past end(), with the exception that + attempting to advance a non-end iterator past end() for + a running future is allowed and will return the end iterator. + + Note that n == 0 is valid and will wait for the result + at the given index. + */ + int advanceIndex(int idx, int n) const + { + // The end iterator can be decremented, leave as-is for other cases + if (idx == -1 && n >= 0) + return idx; + + // Special case for decrementing the end iterator: wait for + // finished to get the total result count. + if (idx == -1 && future->isRunning()) + future->d.waitForFinished(); + + // Wait for result at target index + const int targetIndex = (idx == -1) ? future->resultCount() + n : idx + n; + future->d.waitForResult(targetIndex); + + // After waiting there is either a result or the end was reached + return (targetIndex < future->resultCount()) ? targetIndex : -1; + } + QFuture const * future; int index; }; -- cgit v1.2.3 From c4a94edbbc3a59d955bf2721e51b1448d00e0483 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Sat, 25 May 2019 08:58:00 +0200 Subject: QMessagePattern: replace manual memory handling with std::unique_ptr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dealing with 'tokens' is straight-forward. With 'literals', it is not quite so straight-forward, because the ownership chain here is two levels deep. But it's still worthwhile, because it replaces quite error-prone code with code which may be a bit more verbose, but is totally safe. As a drive-by, moved initialization of the fromEnvironment member to the body of the ctor in order to avoid code-churn (I needed to touch the ctor-init-list anyway). The QMessagePattern dtor is now empty and consequently defaulted. Change-Id: Iadb25e7aba1c5a94fd9068be7ae03f17e975328b Reviewed-by: Mårten Nordheim --- src/corelib/global/qlogging.cpp | 37 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/corelib/global/qlogging.cpp b/src/corelib/global/qlogging.cpp index 49411306c2..8db6ab630a 100644 --- a/src/corelib/global/qlogging.cpp +++ b/src/corelib/global/qlogging.cpp @@ -158,6 +158,9 @@ static QT_PREPEND_NAMESPACE(qint64) qt_gettid() #endif // !QT_BOOTSTRAPPED #include +#include +#include +#include #include @@ -1076,8 +1079,8 @@ struct QMessagePattern { void setPattern(const QString &pattern); // 0 terminated arrays of literal tokens / literal or placeholder tokens - const char **literals; - const char **tokens; + std::unique_ptr[]> literals; + std::unique_ptr tokens; QList timeArgs; // timeFormats in sequence of %{time #ifndef QT_BOOTSTRAPPED QElapsedTimer timer; @@ -1100,9 +1103,6 @@ Q_DECLARE_TYPEINFO(QMessagePattern::BacktraceParams, Q_MOVABLE_TYPE); QBasicMutex QMessagePattern::mutex; QMessagePattern::QMessagePattern() - : literals(nullptr) - , tokens(nullptr) - , fromEnvironment(false) { #ifndef QT_BOOTSTRAPPED timer.start(); @@ -1110,6 +1110,7 @@ QMessagePattern::QMessagePattern() const QString envPattern = QString::fromLocal8Bit(qgetenv("QT_MESSAGE_PATTERN")); if (envPattern.isEmpty()) { setPattern(QLatin1String(defaultPattern)); + fromEnvironment = false; } else { setPattern(envPattern); fromEnvironment = true; @@ -1117,23 +1118,10 @@ QMessagePattern::QMessagePattern() } QMessagePattern::~QMessagePattern() -{ - for (int i = 0; literals[i]; ++i) - delete [] literals[i]; - delete [] literals; - literals = nullptr; - delete [] tokens; - tokens = nullptr; -} + = default; void QMessagePattern::setPattern(const QString &pattern) { - if (literals) { - for (int i = 0; literals[i]; ++i) - delete [] literals[i]; - delete [] literals; - } - delete [] tokens; timeArgs.clear(); #ifdef QLOGGING_HAVE_BACKTRACE backtraceArgs.clear(); @@ -1171,8 +1159,8 @@ void QMessagePattern::setPattern(const QString &pattern) lexemes.append(lexeme); // tokenizer - QVarLengthArray literalsVar; - tokens = new const char*[lexemes.size() + 1]; + std::vector> literalsVar; + tokens.reset(new const char*[lexemes.size() + 1]); tokens[lexemes.size()] = nullptr; bool nestedIfError = false; @@ -1267,7 +1255,7 @@ void QMessagePattern::setPattern(const QString &pattern) char *literal = new char[lexeme.size() + 1]; strncpy(literal, lexeme.toLatin1().constData(), lexeme.size()); literal[lexeme.size()] = '\0'; - literalsVar.append(literal); + literalsVar.emplace_back(literal); tokens[i] = literal; } } @@ -1279,9 +1267,8 @@ void QMessagePattern::setPattern(const QString &pattern) if (!error.isEmpty()) qt_message_print(error); - literals = new const char*[literalsVar.size() + 1]; - literals[literalsVar.size()] = nullptr; - memcpy(literals, literalsVar.constData(), literalsVar.size() * sizeof(const char*)); + literals.reset(new std::unique_ptr[literalsVar.size() + 1]); + std::move(literalsVar.begin(), literalsVar.end(), &literals[0]); } #if defined(QLOGGING_HAVE_BACKTRACE) && !defined(QT_BOOTSTRAPPED) -- cgit v1.2.3 From d6cd1a3c2b6d2d67ba58673b9c3dfc35784f0bc4 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Sun, 26 May 2019 10:50:10 +0200 Subject: QUrl: replace manual memory management of QUrlPrivate::Error with unique_ptr Change-Id: I837866dbeff4b3f4ba4eb5b564041fecbd59e70e Reviewed-by: Edward Welbourne --- src/corelib/io/qurl.cpp | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/corelib/io/qurl.cpp b/src/corelib/io/qurl.cpp index 4d0306abde..9bebfff53f 100644 --- a/src/corelib/io/qurl.cpp +++ b/src/corelib/io/qurl.cpp @@ -421,6 +421,7 @@ #include "private/qipaddress_p.h" #include "qurlquery.h" #include "private/qdir_p.h" +#include QT_BEGIN_NAMESPACE @@ -520,7 +521,7 @@ public: bool isEmpty() const { return sectionIsPresent == 0 && port == -1 && path.isEmpty(); } - Error *cloneError() const; + std::unique_ptr cloneError() const; void clearError(); void setError(ErrorCode errorCode, const QString &source, int supplement = -1); ErrorCode validityError(QString *source = nullptr, int *position = nullptr) const; @@ -576,7 +577,7 @@ public: QString query; QString fragment; - Error *error; + std::unique_ptr error; // not used for: // - Port (port == -1 means absence) @@ -591,7 +592,6 @@ public: inline QUrlPrivate::QUrlPrivate() : ref(1), port(-1), - error(nullptr), sectionIsPresent(0), flags(0) { @@ -613,19 +613,16 @@ inline QUrlPrivate::QUrlPrivate(const QUrlPrivate ©) } inline QUrlPrivate::~QUrlPrivate() -{ - delete error; -} + = default; -inline QUrlPrivate::Error *QUrlPrivate::cloneError() const +std::unique_ptr QUrlPrivate::cloneError() const { - return error ? new Error(*error) : nullptr; + return error ? qt_make_unique(*error) : nullptr; } inline void QUrlPrivate::clearError() { - delete error; - error = nullptr; + error.reset(); } inline void QUrlPrivate::setError(ErrorCode errorCode, const QString &source, int supplement) @@ -634,7 +631,7 @@ inline void QUrlPrivate::setError(ErrorCode errorCode, const QString &source, in // don't overwrite an error set in a previous section during parsing return; } - error = new Error; + error = qt_make_unique(); error->code = errorCode; error->source = source; error->position = supplement; -- cgit v1.2.3 From 3f5d27bfda7b6d486d20869b6ce4a4362087d0c9 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Sat, 8 Jun 2019 12:00:05 +0200 Subject: QEvdev: Extract Method updateDeviceCount() The code is noisy and repeats, so wrap it in a function. Change-Id: I5e6e924e22b0bc631eb8176de96c49066b1c9029 Reviewed-by: Allan Sandfeld Jensen --- .../input/evdevkeyboard/qevdevkeyboardmanager.cpp | 12 ++++++++---- .../input/evdevkeyboard/qevdevkeyboardmanager_p.h | 2 ++ src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp | 12 ++++++++---- src/platformsupport/input/evdevmouse/qevdevmousemanager_p.h | 1 + .../input/evdevtablet/qevdevtabletmanager.cpp | 12 ++++++++---- .../input/evdevtablet/qevdevtabletmanager_p.h | 2 ++ 6 files changed, 29 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp index a73d4728a5..d11eabeebb 100644 --- a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp +++ b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp @@ -109,8 +109,7 @@ void QEvdevKeyboardManager::addKeyboard(const QString &deviceNode) keyboard = QEvdevKeyboardHandler::create(deviceNode, m_spec, m_defaultKeymapFile); if (keyboard) { m_keyboards.insert(deviceNode, keyboard); - QInputDeviceManagerPrivate::get(QGuiApplicationPrivate::inputDeviceManager())->setDeviceCount( - QInputDeviceManager::DeviceTypeKeyboard, m_keyboards.count()); + updateDeviceCount(); } else { qWarning("Failed to open keyboard device %ls", qUtf16Printable(deviceNode)); } @@ -122,12 +121,17 @@ void QEvdevKeyboardManager::removeKeyboard(const QString &deviceNode) qCDebug(qLcEvdevKey, "Removing keyboard at %ls", qUtf16Printable(deviceNode)); QEvdevKeyboardHandler *keyboard = m_keyboards.value(deviceNode); m_keyboards.remove(deviceNode); - QInputDeviceManagerPrivate::get(QGuiApplicationPrivate::inputDeviceManager())->setDeviceCount( - QInputDeviceManager::DeviceTypeKeyboard, m_keyboards.count()); + updateDeviceCount(); delete keyboard; } } +void QEvdevKeyboardManager::updateDeviceCount() +{ + QInputDeviceManagerPrivate::get(QGuiApplicationPrivate::inputDeviceManager())->setDeviceCount( + QInputDeviceManager::DeviceTypeKeyboard, m_keyboards.count()); +} + void QEvdevKeyboardManager::loadKeymap(const QString &file) { m_defaultKeymapFile = file; diff --git a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager_p.h b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager_p.h index 01b7e9fc0e..9ce0afb522 100644 --- a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager_p.h +++ b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager_p.h @@ -74,6 +74,8 @@ public: void removeKeyboard(const QString &deviceNode); private: + void updateDeviceCount(); + QString m_spec; QHash m_keyboards; QDeviceDiscovery *m_deviceDiscovery; diff --git a/src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp b/src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp index 038ff7db43..7abca215da 100644 --- a/src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp +++ b/src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp @@ -167,8 +167,7 @@ void QEvdevMouseManager::addMouse(const QString &deviceNode) connect(handler, &QEvdevMouseHandler::handleWheelEvent, this, &QEvdevMouseManager::handleWheelEvent); m_mice.insert(deviceNode, handler); - QInputDeviceManagerPrivate::get(QGuiApplicationPrivate::inputDeviceManager())->setDeviceCount( - QInputDeviceManager::DeviceTypePointer, m_mice.count()); + updateDeviceCount(); } else { qWarning("evdevmouse: Failed to open mouse device %ls", qUtf16Printable(deviceNode)); } @@ -180,10 +179,15 @@ void QEvdevMouseManager::removeMouse(const QString &deviceNode) qCDebug(qLcEvdevMouse, "Removing mouse at %ls", qUtf16Printable(deviceNode)); QEvdevMouseHandler *handler = m_mice.value(deviceNode); m_mice.remove(deviceNode); - QInputDeviceManagerPrivate::get(QGuiApplicationPrivate::inputDeviceManager())->setDeviceCount( - QInputDeviceManager::DeviceTypePointer, m_mice.count()); + updateDeviceCount(); delete handler; } } +void QEvdevMouseManager::updateDeviceCount() +{ + QInputDeviceManagerPrivate::get(QGuiApplicationPrivate::inputDeviceManager())->setDeviceCount( + QInputDeviceManager::DeviceTypePointer, m_mice.count()); +} + QT_END_NAMESPACE diff --git a/src/platformsupport/input/evdevmouse/qevdevmousemanager_p.h b/src/platformsupport/input/evdevmouse/qevdevmousemanager_p.h index c63ca29a71..62b3c1beea 100644 --- a/src/platformsupport/input/evdevmouse/qevdevmousemanager_p.h +++ b/src/platformsupport/input/evdevmouse/qevdevmousemanager_p.h @@ -77,6 +77,7 @@ public: private: void clampPosition(); + void updateDeviceCount(); QString m_spec; QHash m_mice; diff --git a/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp b/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp index da4b6e5172..eedde9a96e 100644 --- a/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp +++ b/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp @@ -109,8 +109,7 @@ void QEvdevTabletManager::addDevice(const QString &deviceNode) handler = new QEvdevTabletHandlerThread(deviceNode, m_spec); if (handler) { m_activeDevices.insert(deviceNode, handler); - QInputDeviceManagerPrivate::get(QGuiApplicationPrivate::inputDeviceManager())->setDeviceCount( - QInputDeviceManager::DeviceTypeTablet, m_activeDevices.count()); + updateDeviceCount(); } else { qWarning("evdevtablet: Failed to open tablet device %ls", qUtf16Printable(deviceNode)); } @@ -122,10 +121,15 @@ void QEvdevTabletManager::removeDevice(const QString &deviceNode) qCDebug(qLcEvdevTablet, "Removing device at %ls", qUtf16Printable(deviceNode)); QEvdevTabletHandlerThread *handler = m_activeDevices.value(deviceNode); m_activeDevices.remove(deviceNode); - QInputDeviceManagerPrivate::get(QGuiApplicationPrivate::inputDeviceManager())->setDeviceCount( - QInputDeviceManager::DeviceTypeTablet, m_activeDevices.count()); + updateDeviceCount(); delete handler; } } +void QEvdevTabletManager::updateDeviceCount() +{ + QInputDeviceManagerPrivate::get(QGuiApplicationPrivate::inputDeviceManager())->setDeviceCount( + QInputDeviceManager::DeviceTypeTablet, m_activeDevices.count()); +} + QT_END_NAMESPACE diff --git a/src/platformsupport/input/evdevtablet/qevdevtabletmanager_p.h b/src/platformsupport/input/evdevtablet/qevdevtabletmanager_p.h index b598156e52..5178e167b0 100644 --- a/src/platformsupport/input/evdevtablet/qevdevtabletmanager_p.h +++ b/src/platformsupport/input/evdevtablet/qevdevtabletmanager_p.h @@ -70,6 +70,8 @@ public: void removeDevice(const QString &deviceNode); private: + void updateDeviceCount(); + QString m_spec; QDeviceDiscovery *m_deviceDiscovery; QHash m_activeDevices; -- cgit v1.2.3 From cbd5a2dcb8969d0bd41f8a02204275e631231a60 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Fri, 26 Aug 2016 15:35:40 +0200 Subject: AtSpiAdaptor: eradicate remaining Q_FOREACH loops ... and replace them by C++11 range-for loops. Change-Id: I6975121f606ec1fcda7a624b02a68edf829bb70b Reviewed-by: Frederik Gladhorn --- src/platformsupport/linuxaccessibility/atspiadaptor.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/platformsupport/linuxaccessibility/atspiadaptor.cpp b/src/platformsupport/linuxaccessibility/atspiadaptor.cpp index 580cf0e31d..8a825f8284 100644 --- a/src/platformsupport/linuxaccessibility/atspiadaptor.cpp +++ b/src/platformsupport/linuxaccessibility/atspiadaptor.cpp @@ -2050,8 +2050,8 @@ QVariantList AtSpiAdaptor::getAttributes(QAccessibleInterface *interface, int of int endOffset; QString joined = interface->textInterface()->attributes(offset, &startOffset, &endOffset); - QStringList attributes = joined.split (QLatin1Char(';'), QString::SkipEmptyParts, Qt::CaseSensitive); - foreach (const QString &attr, attributes) { + const QStringList attributes = joined.split (QLatin1Char(';'), QString::SkipEmptyParts, Qt::CaseSensitive); + for (const QString &attr : attributes) { QStringList items; items = attr.split(QLatin1Char(':'), QString::SkipEmptyParts, Qt::CaseSensitive); AtSpiAttribute attribute = atspiTextAttribute(items[0], items[1]); @@ -2069,14 +2069,13 @@ QVariantList AtSpiAdaptor::getAttributeValue(QAccessibleInterface *interface, in { QString mapped; QString joined; - QStringList attributes; QSpiAttributeSet map; int startOffset; int endOffset; joined = interface->textInterface()->attributes(offset, &startOffset, &endOffset); - attributes = joined.split (QLatin1Char(';'), QString::SkipEmptyParts, Qt::CaseSensitive); - foreach (const QString& attr, attributes) { + const QStringList attributes = joined.split (QLatin1Char(';'), QString::SkipEmptyParts, Qt::CaseSensitive); + for (const QString& attr : attributes) { QStringList items; items = attr.split(QLatin1Char(':'), QString::SkipEmptyParts, Qt::CaseSensitive); AtSpiAttribute attribute = atspiTextAttribute(items[0], items[1]); -- cgit v1.2.3 From a2632f90db2662b1b7cd4b0d0e4015b22aa05025 Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Fri, 14 Jun 2019 14:23:02 +0200 Subject: QGradient: add a last-enum enumerator to the presets It's needed to prepare qtdeclarative for the upcoming patch that won't make QGradient accept illegal presets any more. Change-Id: I4ca929e75214ebe24c7d762d0c37ca254c640c57 Reviewed-by: Marc Mutz --- src/gui/painting/qbrush.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/gui/painting/qbrush.h b/src/gui/painting/qbrush.h index ca51430cf4..43e1eb7472 100644 --- a/src/gui/painting/qbrush.h +++ b/src/gui/painting/qbrush.h @@ -371,6 +371,8 @@ public: GagarinView = 178, FabledSunset = 179, PerfectBlue = 180, + + NumPresets }; Q_ENUM(Preset) -- cgit v1.2.3 From 12938ba70b1277c15cf58eb7f56b58ab37e08849 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Sat, 15 Jun 2019 18:18:13 +0200 Subject: QMimeType: make deep const breakages explicit QExplicitlySharedDataPointer is propagating const in my tree, and I will be proposing this for inclusion into Qt 6, so proactively fix the breakage here. QMimeType is known to be non-reentrant (QTBUG-45684), and this patch doesn't fix it. Change-Id: If68b148c44439d76ab1d95e8db93b90d12650e51 Reviewed-by: David Faure --- src/corelib/mimetypes/qmimetype.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/corelib/mimetypes/qmimetype.cpp b/src/corelib/mimetypes/qmimetype.cpp index cf01c9503b..b60ff8e9df 100644 --- a/src/corelib/mimetypes/qmimetype.cpp +++ b/src/corelib/mimetypes/qmimetype.cpp @@ -253,7 +253,7 @@ QString QMimeType::name() const */ QString QMimeType::comment() const { - QMimeDatabasePrivate::instance()->loadMimeTypePrivate(*d); + QMimeDatabasePrivate::instance()->loadMimeTypePrivate(const_cast(*d)); QStringList languageList; languageList << QLocale().name(); @@ -294,7 +294,7 @@ QString QMimeType::comment() const */ QString QMimeType::genericIconName() const { - QMimeDatabasePrivate::instance()->loadGenericIcon(*d); + QMimeDatabasePrivate::instance()->loadGenericIcon(const_cast(*d)); if (d->genericIconName.isEmpty()) { // From the spec: // If the generic icon name is empty (not specified by the mimetype definition) @@ -322,13 +322,14 @@ QString QMimeType::genericIconName() const */ QString QMimeType::iconName() const { - QMimeDatabasePrivate::instance()->loadIcon(*d); + QMimeDatabasePrivate::instance()->loadIcon(const_cast(*d)); if (d->iconName.isEmpty()) { // Make default icon name from the mimetype name - d->iconName = name(); - const int slashindex = d->iconName.indexOf(QLatin1Char('/')); + QString iconName = name(); + const int slashindex = iconName.indexOf(QLatin1Char('/')); if (slashindex != -1) - d->iconName[slashindex] = QLatin1Char('-'); + iconName[slashindex] = QLatin1Char('-'); + const_cast(this)->d->iconName = std::move(iconName); } return d->iconName; } @@ -342,7 +343,7 @@ QString QMimeType::iconName() const */ QStringList QMimeType::globPatterns() const { - QMimeDatabasePrivate::instance()->loadMimeTypePrivate(*d); + QMimeDatabasePrivate::instance()->loadMimeTypePrivate(const_cast(*d)); return d->globPatterns; } @@ -437,7 +438,7 @@ QStringList QMimeType::aliases() const */ QStringList QMimeType::suffixes() const { - QMimeDatabasePrivate::instance()->loadMimeTypePrivate(*d); + QMimeDatabasePrivate::instance()->loadMimeTypePrivate(const_cast(*d)); QStringList result; for (const QString &pattern : qAsConst(d->globPatterns)) { @@ -480,7 +481,7 @@ QString QMimeType::preferredSuffix() const */ QString QMimeType::filterString() const { - QMimeDatabasePrivate::instance()->loadMimeTypePrivate(*d); + QMimeDatabasePrivate::instance()->loadMimeTypePrivate(const_cast(*d)); QString filter; if (!d->globPatterns.empty()) { -- cgit v1.2.3 From 3bc10fb9bb930c4e1baf52f9d0ba97616e8e77f6 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Sat, 8 Jun 2019 12:00:05 +0200 Subject: QEvdev: Replace manual memory management with unique_ptr Make create() return, and m_mice/m_keyboards/etc store, handlers by unique_ptr. In most cases, we can't use qt_make_unique(), since the ctor we're calling is marked as private. Since QHash can't hold move-only types, use a std::vector<{QString, unique_ptr}> instead. As this pattern repeats in all four QEvdev*Manager classes, create a small class template. Saves almost 6KiB on optimized Linux AMD64 GCC 9.1 builds across all .so's that link to QtInputSupport.a. Change-Id: I8f62b6b629d6e1855314c0a4fb4fc069db9ae0ce Reviewed-by: Allan Sandfeld Jensen --- .../input/evdevkeyboard/qevdevkeyboardhandler.cpp | 6 +- .../input/evdevkeyboard/qevdevkeyboardhandler_p.h | 4 +- .../input/evdevkeyboard/qevdevkeyboardmanager.cpp | 26 +++--- .../input/evdevkeyboard/qevdevkeyboardmanager_p.h | 3 +- .../input/evdevmouse/qevdevmousehandler.cpp | 6 +- .../input/evdevmouse/qevdevmousehandler_p.h | 4 +- .../input/evdevmouse/qevdevmousemanager.cpp | 15 ++-- .../input/evdevmouse/qevdevmousemanager_p.h | 4 +- .../input/evdevtablet/qevdevtabletmanager.cpp | 12 +-- .../input/evdevtablet/qevdevtabletmanager_p.h | 4 +- .../input/evdevtouch/qevdevtouchmanager.cpp | 19 ++--- .../input/evdevtouch/qevdevtouchmanager_p.h | 4 +- .../input/shared/devicehandlerlist_p.h | 95 ++++++++++++++++++++++ src/platformsupport/input/shared/shared.pri | 1 + 14 files changed, 145 insertions(+), 58 deletions(-) create mode 100644 src/platformsupport/input/shared/devicehandlerlist_p.h (limited to 'src') diff --git a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp index 35ee23311d..bff4a2522c 100644 --- a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp +++ b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp @@ -98,7 +98,7 @@ QEvdevKeyboardHandler::~QEvdevKeyboardHandler() unloadKeymap(); } -QEvdevKeyboardHandler *QEvdevKeyboardHandler::create(const QString &device, +std::unique_ptr QEvdevKeyboardHandler::create(const QString &device, const QString &specification, const QString &defaultKeymapFile) { @@ -138,10 +138,10 @@ QEvdevKeyboardHandler *QEvdevKeyboardHandler::create(const QString &device, ::ioctl(fd.get(), EVIOCSREP, kbdrep); } - return new QEvdevKeyboardHandler(device, fd, disableZap, enableCompose, keymapFile); + return std::unique_ptr(new QEvdevKeyboardHandler(device, fd, disableZap, enableCompose, keymapFile)); } else { qErrnoWarning("Cannot open keyboard input device '%ls'", qUtf16Printable(device)); - return 0; + return nullptr; } } diff --git a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler_p.h b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler_p.h index d154c30ed5..f92a2bf704 100644 --- a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler_p.h +++ b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler_p.h @@ -55,6 +55,8 @@ #include #include +#include + QT_BEGIN_NAMESPACE class QSocketNotifier; @@ -168,7 +170,7 @@ public: SwitchConsoleMask = 0x0000007f }; - static QEvdevKeyboardHandler *create(const QString &device, + static std::unique_ptr create(const QString &device, const QString &specification, const QString &defaultKeymapFile = QString()); diff --git a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp index d11eabeebb..5fc080b765 100644 --- a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp +++ b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp @@ -98,17 +98,14 @@ QEvdevKeyboardManager::QEvdevKeyboardManager(const QString &key, const QString & QEvdevKeyboardManager::~QEvdevKeyboardManager() { - qDeleteAll(m_keyboards); - m_keyboards.clear(); } void QEvdevKeyboardManager::addKeyboard(const QString &deviceNode) { qCDebug(qLcEvdevKey, "Adding keyboard at %ls", qUtf16Printable(deviceNode)); - QEvdevKeyboardHandler *keyboard; - keyboard = QEvdevKeyboardHandler::create(deviceNode, m_spec, m_defaultKeymapFile); + auto keyboard = QEvdevKeyboardHandler::create(deviceNode, m_spec, m_defaultKeymapFile); if (keyboard) { - m_keyboards.insert(deviceNode, keyboard); + m_keyboards.add(deviceNode, std::move(keyboard)); updateDeviceCount(); } else { qWarning("Failed to open keyboard device %ls", qUtf16Printable(deviceNode)); @@ -117,12 +114,9 @@ void QEvdevKeyboardManager::addKeyboard(const QString &deviceNode) void QEvdevKeyboardManager::removeKeyboard(const QString &deviceNode) { - if (m_keyboards.contains(deviceNode)) { + if (m_keyboards.remove(deviceNode)) { qCDebug(qLcEvdevKey, "Removing keyboard at %ls", qUtf16Printable(deviceNode)); - QEvdevKeyboardHandler *keyboard = m_keyboards.value(deviceNode); - m_keyboards.remove(deviceNode); updateDeviceCount(); - delete keyboard; } } @@ -145,22 +139,22 @@ void QEvdevKeyboardManager::loadKeymap(const QString &file) if (arg.startsWith(QLatin1String("keymap="))) keymapFromSpec = arg.mid(7).toString(); } - foreach (QEvdevKeyboardHandler *handler, m_keyboards) { + for (const auto &keyboard : m_keyboards) { if (keymapFromSpec.isEmpty()) - handler->unloadKeymap(); + keyboard.handler->unloadKeymap(); else - handler->loadKeymap(keymapFromSpec); + keyboard.handler->loadKeymap(keymapFromSpec); } } else { - foreach (QEvdevKeyboardHandler *handler, m_keyboards) - handler->loadKeymap(file); + for (const auto &keyboard : m_keyboards) + keyboard.handler->loadKeymap(file); } } void QEvdevKeyboardManager::switchLang() { - foreach (QEvdevKeyboardHandler *handler, m_keyboards) - handler->switchLang(); + for (const auto &keyboard : m_keyboards) + keyboard.handler->switchLang(); } QT_END_NAMESPACE diff --git a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager_p.h b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager_p.h index 9ce0afb522..e495aafc9a 100644 --- a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager_p.h +++ b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager_p.h @@ -53,6 +53,7 @@ #include "qevdevkeyboardhandler_p.h" +#include #include #include @@ -77,7 +78,7 @@ private: void updateDeviceCount(); QString m_spec; - QHash m_keyboards; + QtInputSupport::DeviceHandlerList m_keyboards; QDeviceDiscovery *m_deviceDiscovery; QString m_defaultKeymapFile; }; diff --git a/src/platformsupport/input/evdevmouse/qevdevmousehandler.cpp b/src/platformsupport/input/evdevmouse/qevdevmousehandler.cpp index 86a4cd0076..6a53ad2088 100644 --- a/src/platformsupport/input/evdevmouse/qevdevmousehandler.cpp +++ b/src/platformsupport/input/evdevmouse/qevdevmousehandler.cpp @@ -66,7 +66,7 @@ QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(qLcEvdevMouse, "qt.qpa.input") -QEvdevMouseHandler *QEvdevMouseHandler::create(const QString &device, const QString &specification) +std::unique_ptr QEvdevMouseHandler::create(const QString &device, const QString &specification) { qCDebug(qLcEvdevMouse) << "create mouse handler for" << device << specification; @@ -91,10 +91,10 @@ QEvdevMouseHandler *QEvdevMouseHandler::create(const QString &device, const QStr fd = qt_safe_open(device.toLocal8Bit().constData(), O_RDONLY | O_NDELAY, 0); if (fd >= 0) { ::ioctl(fd, EVIOCGRAB, grab); - return new QEvdevMouseHandler(device, fd, abs, compression, jitterLimit); + return std::unique_ptr(new QEvdevMouseHandler(device, fd, abs, compression, jitterLimit)); } else { qErrnoWarning(errno, "Cannot open mouse input device %s", qPrintable(device)); - return 0; + return nullptr; } } diff --git a/src/platformsupport/input/evdevmouse/qevdevmousehandler_p.h b/src/platformsupport/input/evdevmouse/qevdevmousehandler_p.h index c7f2b04eb2..727f1a02f9 100644 --- a/src/platformsupport/input/evdevmouse/qevdevmousehandler_p.h +++ b/src/platformsupport/input/evdevmouse/qevdevmousehandler_p.h @@ -56,6 +56,8 @@ #include #include +#include + QT_BEGIN_NAMESPACE class QSocketNotifier; @@ -64,7 +66,7 @@ class QEvdevMouseHandler : public QObject { Q_OBJECT public: - static QEvdevMouseHandler *create(const QString &device, const QString &specification); + static std::unique_ptr create(const QString &device, const QString &specification); ~QEvdevMouseHandler(); void readMouseData(); diff --git a/src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp b/src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp index 7abca215da..0873981436 100644 --- a/src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp +++ b/src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp @@ -111,8 +111,6 @@ QEvdevMouseManager::QEvdevMouseManager(const QString &key, const QString &specif QEvdevMouseManager::~QEvdevMouseManager() { - qDeleteAll(m_mice); - m_mice.clear(); } void QEvdevMouseManager::clampPosition() @@ -160,13 +158,13 @@ void QEvdevMouseManager::handleWheelEvent(QPoint delta) void QEvdevMouseManager::addMouse(const QString &deviceNode) { qCDebug(qLcEvdevMouse, "Adding mouse at %ls", qUtf16Printable(deviceNode)); - QEvdevMouseHandler *handler = QEvdevMouseHandler::create(deviceNode, m_spec); + auto handler = QEvdevMouseHandler::create(deviceNode, m_spec); if (handler) { - connect(handler, &QEvdevMouseHandler::handleMouseEvent, + connect(handler.get(), &QEvdevMouseHandler::handleMouseEvent, this, &QEvdevMouseManager::handleMouseEvent); - connect(handler, &QEvdevMouseHandler::handleWheelEvent, + connect(handler.get(), &QEvdevMouseHandler::handleWheelEvent, this, &QEvdevMouseManager::handleWheelEvent); - m_mice.insert(deviceNode, handler); + m_mice.add(deviceNode, std::move(handler)); updateDeviceCount(); } else { qWarning("evdevmouse: Failed to open mouse device %ls", qUtf16Printable(deviceNode)); @@ -175,12 +173,9 @@ void QEvdevMouseManager::addMouse(const QString &deviceNode) void QEvdevMouseManager::removeMouse(const QString &deviceNode) { - if (m_mice.contains(deviceNode)) { + if (m_mice.remove(deviceNode)) { qCDebug(qLcEvdevMouse, "Removing mouse at %ls", qUtf16Printable(deviceNode)); - QEvdevMouseHandler *handler = m_mice.value(deviceNode); - m_mice.remove(deviceNode); updateDeviceCount(); - delete handler; } } diff --git a/src/platformsupport/input/evdevmouse/qevdevmousemanager_p.h b/src/platformsupport/input/evdevmouse/qevdevmousemanager_p.h index 62b3c1beea..1d547da1e8 100644 --- a/src/platformsupport/input/evdevmouse/qevdevmousemanager_p.h +++ b/src/platformsupport/input/evdevmouse/qevdevmousemanager_p.h @@ -53,6 +53,8 @@ #include "qevdevmousehandler_p.h" +#include + #include #include #include @@ -80,7 +82,7 @@ private: void updateDeviceCount(); QString m_spec; - QHash m_mice; + QtInputSupport::DeviceHandlerList m_mice; QDeviceDiscovery *m_deviceDiscovery; int m_x; int m_y; diff --git a/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp b/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp index eedde9a96e..d503476aad 100644 --- a/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp +++ b/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp @@ -46,6 +46,7 @@ #include #include #include +#include QT_BEGIN_NAMESPACE @@ -99,16 +100,14 @@ QEvdevTabletManager::QEvdevTabletManager(const QString &key, const QString &spec QEvdevTabletManager::~QEvdevTabletManager() { - qDeleteAll(m_activeDevices); } void QEvdevTabletManager::addDevice(const QString &deviceNode) { qCDebug(qLcEvdevTablet, "Adding device at %ls", qUtf16Printable(deviceNode)); - QEvdevTabletHandlerThread *handler; - handler = new QEvdevTabletHandlerThread(deviceNode, m_spec); + auto handler = qt_make_unique(deviceNode, m_spec); if (handler) { - m_activeDevices.insert(deviceNode, handler); + m_activeDevices.add(deviceNode, std::move(handler)); updateDeviceCount(); } else { qWarning("evdevtablet: Failed to open tablet device %ls", qUtf16Printable(deviceNode)); @@ -117,12 +116,9 @@ void QEvdevTabletManager::addDevice(const QString &deviceNode) void QEvdevTabletManager::removeDevice(const QString &deviceNode) { - if (m_activeDevices.contains(deviceNode)) { + if (m_activeDevices.remove(deviceNode)) { qCDebug(qLcEvdevTablet, "Removing device at %ls", qUtf16Printable(deviceNode)); - QEvdevTabletHandlerThread *handler = m_activeDevices.value(deviceNode); - m_activeDevices.remove(deviceNode); updateDeviceCount(); - delete handler; } } diff --git a/src/platformsupport/input/evdevtablet/qevdevtabletmanager_p.h b/src/platformsupport/input/evdevtablet/qevdevtabletmanager_p.h index 5178e167b0..0274f9d3ed 100644 --- a/src/platformsupport/input/evdevtablet/qevdevtabletmanager_p.h +++ b/src/platformsupport/input/evdevtablet/qevdevtabletmanager_p.h @@ -51,6 +51,8 @@ // We mean it. // +#include + #include #include #include @@ -74,7 +76,7 @@ private: QString m_spec; QDeviceDiscovery *m_deviceDiscovery; - QHash m_activeDevices; + QtInputSupport::DeviceHandlerList m_activeDevices; }; QT_END_NAMESPACE diff --git a/src/platformsupport/input/evdevtouch/qevdevtouchmanager.cpp b/src/platformsupport/input/evdevtouch/qevdevtouchmanager.cpp index f59d8d5e99..04d4ac777e 100644 --- a/src/platformsupport/input/evdevtouch/qevdevtouchmanager.cpp +++ b/src/platformsupport/input/evdevtouch/qevdevtouchmanager.cpp @@ -46,6 +46,7 @@ #include #include #include +#include QT_BEGIN_NAMESPACE @@ -99,17 +100,15 @@ QEvdevTouchManager::QEvdevTouchManager(const QString &key, const QString &specif QEvdevTouchManager::~QEvdevTouchManager() { - qDeleteAll(m_activeDevices); } void QEvdevTouchManager::addDevice(const QString &deviceNode) { qCDebug(qLcEvdevTouch, "evdevtouch: Adding device at %ls", qUtf16Printable(deviceNode)); - QEvdevTouchScreenHandlerThread *handler; - handler = new QEvdevTouchScreenHandlerThread(deviceNode, m_spec); + auto handler = qt_make_unique(deviceNode, m_spec); if (handler) { - m_activeDevices.insert(deviceNode, handler); - connect(handler, &QEvdevTouchScreenHandlerThread::touchDeviceRegistered, this, &QEvdevTouchManager::updateInputDeviceCount); + m_activeDevices.add(deviceNode, std::move(handler)); + connect(handler.get(), &QEvdevTouchScreenHandlerThread::touchDeviceRegistered, this, &QEvdevTouchManager::updateInputDeviceCount); } else { qWarning("evdevtouch: Failed to open touch device %ls", qUtf16Printable(deviceNode)); } @@ -117,12 +116,8 @@ void QEvdevTouchManager::addDevice(const QString &deviceNode) void QEvdevTouchManager::removeDevice(const QString &deviceNode) { - if (m_activeDevices.contains(deviceNode)) { + if (m_activeDevices.remove(deviceNode)) { qCDebug(qLcEvdevTouch, "evdevtouch: Removing device at %ls", qUtf16Printable(deviceNode)); - QEvdevTouchScreenHandlerThread *handler = m_activeDevices.value(deviceNode); - m_activeDevices.remove(deviceNode); - delete handler; - updateInputDeviceCount(); } } @@ -130,8 +125,8 @@ void QEvdevTouchManager::removeDevice(const QString &deviceNode) void QEvdevTouchManager::updateInputDeviceCount() { int registeredTouchDevices = 0; - Q_FOREACH (QEvdevTouchScreenHandlerThread *handler, m_activeDevices) { - if (handler->isTouchDeviceRegistered()) + for (const auto &device : m_activeDevices) { + if (device.handler->isTouchDeviceRegistered()) ++registeredTouchDevices; } diff --git a/src/platformsupport/input/evdevtouch/qevdevtouchmanager_p.h b/src/platformsupport/input/evdevtouch/qevdevtouchmanager_p.h index b9b772fb3a..97f3137676 100644 --- a/src/platformsupport/input/evdevtouch/qevdevtouchmanager_p.h +++ b/src/platformsupport/input/evdevtouch/qevdevtouchmanager_p.h @@ -51,6 +51,8 @@ // We mean it. // +#include + #include #include #include @@ -74,7 +76,7 @@ public: private: QString m_spec; QDeviceDiscovery *m_deviceDiscovery; - QHash m_activeDevices; + QtInputSupport::DeviceHandlerList m_activeDevices; }; QT_END_NAMESPACE diff --git a/src/platformsupport/input/shared/devicehandlerlist_p.h b/src/platformsupport/input/shared/devicehandlerlist_p.h new file mode 100644 index 0000000000..97794d4d7d --- /dev/null +++ b/src/platformsupport/input/shared/devicehandlerlist_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTINPUTSUPPORT_DEVICEHANDLERLIST_P_H +#define QTINPUTSUPPORT_DEVICEHANDLERLIST_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#include +#include + +namespace QtInputSupport { + +template +class DeviceHandlerList { +public: + struct Device { + QString deviceNode; + std::unique_ptr handler; + }; + + void add(const QString &deviceNode, std::unique_ptr handler) + { + v.push_back({deviceNode, std::move(handler)}); + } + + bool remove(const QString &deviceNode) + { + const auto deviceNodeMatches = [&] (const Device &d) { return d.deviceNode == deviceNode; }; + const auto it = std::find_if(v.cbegin(), v.cend(), deviceNodeMatches); + if (it == v.cend()) + return false; + v.erase(it); + return true; + } + + int count() const noexcept { return static_cast(v.size()); } + + typename std::vector::const_iterator begin() const noexcept { return v.begin(); } + typename std::vector::const_iterator end() const noexcept { return v.end(); } + +private: + std::vector v; +}; + +} // QtInputSupport + +#endif // QTINPUTSUPPORT_DEVICEHANDLERLIST_P_H diff --git a/src/platformsupport/input/shared/shared.pri b/src/platformsupport/input/shared/shared.pri index 1443235244..9608c7de7a 100644 --- a/src/platformsupport/input/shared/shared.pri +++ b/src/platformsupport/input/shared/shared.pri @@ -1,4 +1,5 @@ HEADERS += \ + $$PWD/devicehandlerlist_p.h \ $$PWD/qtouchoutputmapping_p.h SOURCES += \ -- cgit v1.2.3 From e01e848df4519e8ab9755da41188abdede5b0fb8 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Sun, 16 Jun 2019 16:51:18 +0200 Subject: QMimeType: towards re-entrancy: do not cache iconName made from mimetype name To not write into a shared object without mutex protection. If the stored icon name is empty, just calculate a new one on each call. Task-number: QTBUG-45684 Change-Id: I01dfb6697b5275e69451da91fdc7346f40bc424e Reviewed-by: David Faure --- src/corelib/mimetypes/qmimetype.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/corelib/mimetypes/qmimetype.cpp b/src/corelib/mimetypes/qmimetype.cpp index b60ff8e9df..de450c68f4 100644 --- a/src/corelib/mimetypes/qmimetype.cpp +++ b/src/corelib/mimetypes/qmimetype.cpp @@ -311,6 +311,14 @@ QString QMimeType::genericIconName() const return d->genericIconName; } +static QString make_default_icon_name_from_mimetype_name(QString iconName) +{ + const int slashindex = iconName.indexOf(QLatin1Char('/')); + if (slashindex != -1) + iconName[slashindex] = QLatin1Char('-'); + return iconName; +} + /*! \property QMimeType::iconName \brief the file name of an icon image that represents the MIME type @@ -324,12 +332,7 @@ QString QMimeType::iconName() const { QMimeDatabasePrivate::instance()->loadIcon(const_cast(*d)); if (d->iconName.isEmpty()) { - // Make default icon name from the mimetype name - QString iconName = name(); - const int slashindex = iconName.indexOf(QLatin1Char('/')); - if (slashindex != -1) - iconName[slashindex] = QLatin1Char('-'); - const_cast(this)->d->iconName = std::move(iconName); + return make_default_icon_name_from_mimetype_name(name()); } return d->iconName; } -- cgit v1.2.3 From f1404c0ed1f89df931210a843727a1fd4eaef6a8 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Wed, 12 Jun 2019 23:41:36 +0200 Subject: QEvdev: Extract Method parseSpecification() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All four manager classes contained roughly the same code in their ctors that parsed out devices from a colon-separated string. Extract shared code, and port the parsing to QStringRef (later to be ported to QStringView). Saves ~2.4KiB on optimized Linux GCC 9.1 AMD64 builds across all .so's that link to libQtInputSupport.a. Change-Id: I3db826ee2b422cfc02f8d49bd21985a03b6c0935 Reviewed-by: Allan Sandfeld Jensen Reviewed-by: Mårten Nordheim --- .../input/evdevkeyboard/qevdevkeyboardmanager.cpp | 21 ++---- .../input/evdevmouse/qevdevmousemanager.cpp | 21 +++--- .../input/evdevtablet/qevdevtabletmanager.cpp | 20 ++---- .../input/evdevtouch/qevdevtouchmanager.cpp | 20 ++---- src/platformsupport/input/shared/qevdevutil.cpp | 70 ++++++++++++++++++++ src/platformsupport/input/shared/qevdevutil_p.h | 76 ++++++++++++++++++++++ src/platformsupport/input/shared/shared.pri | 2 + 7 files changed, 174 insertions(+), 56 deletions(-) create mode 100644 src/platformsupport/input/shared/qevdevutil.cpp create mode 100644 src/platformsupport/input/shared/qevdevutil_p.h (limited to 'src') diff --git a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp index 5fc080b765..f272728aaf 100644 --- a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp +++ b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp @@ -39,6 +39,8 @@ #include "qevdevkeyboardmanager_p.h" +#include + #include #include #include @@ -61,25 +63,14 @@ QEvdevKeyboardManager::QEvdevKeyboardManager(const QString &key, const QString & if (spec.isEmpty()) spec = specification; - QStringList args = spec.split(QLatin1Char(':')); - QStringList devices; - - foreach (const QString &arg, args) { - if (arg.startsWith(QLatin1String("/dev/"))) { - // if device is specified try to use it - devices.append(arg); - args.removeAll(arg); - } - } - - // build new specification without /dev/ elements - m_spec = args.join(QLatin1Char(':')); + auto parsed = QEvdevUtil::parseSpecification(spec); + m_spec = std::move(parsed.spec); // add all keyboards for devices specified in the argument list - foreach (const QString &device, devices) + for (const QString &device : qAsConst(parsed.devices)) addKeyboard(device); - if (devices.isEmpty()) { + if (parsed.devices.isEmpty()) { qCDebug(qLcEvdevKey, "evdevkeyboard: Using device discovery"); m_deviceDiscovery = QDeviceDiscovery::create(QDeviceDiscovery::Device_Keyboard, this); if (m_deviceDiscovery) { diff --git a/src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp b/src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp index 0873981436..f64ba908e8 100644 --- a/src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp +++ b/src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp @@ -39,6 +39,8 @@ #include "qevdevmousemanager_p.h" +#include + #include #include #include @@ -63,29 +65,22 @@ QEvdevMouseManager::QEvdevMouseManager(const QString &key, const QString &specif if (spec.isEmpty()) spec = specification; - QStringList args = spec.split(QLatin1Char(':')); - QStringList devices; + auto parsed = QEvdevUtil::parseSpecification(spec); + m_spec = std::move(parsed.spec); - foreach (const QString &arg, args) { - if (arg.startsWith(QLatin1String("/dev/"))) { - // if device is specified try to use it - devices.append(arg); - args.removeAll(arg); - } else if (arg.startsWith(QLatin1String("xoffset="))) { + for (const QStringRef &arg : qAsConst(parsed.args)) { + if (arg.startsWith(QLatin1String("xoffset="))) { m_xoffset = arg.mid(8).toInt(); } else if (arg.startsWith(QLatin1String("yoffset="))) { m_yoffset = arg.mid(8).toInt(); } } - // build new specification without /dev/ elements - m_spec = args.join(QLatin1Char(':')); - // add all mice for devices specified in the argument list - foreach (const QString &device, devices) + for (const QString &device : qAsConst(parsed.devices)) addMouse(device); - if (devices.isEmpty()) { + if (parsed.devices.isEmpty()) { qCDebug(qLcEvdevMouse, "evdevmouse: Using device discovery"); m_deviceDiscovery = QDeviceDiscovery::create(QDeviceDiscovery::Device_Mouse | QDeviceDiscovery::Device_Touchpad, this); if (m_deviceDiscovery) { diff --git a/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp b/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp index d503476aad..74d6b19e99 100644 --- a/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp +++ b/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp @@ -40,6 +40,8 @@ #include "qevdevtabletmanager_p.h" #include "qevdevtablethandler_p.h" +#include + #include #include #include @@ -65,24 +67,14 @@ QEvdevTabletManager::QEvdevTabletManager(const QString &key, const QString &spec if (spec.isEmpty()) spec = specification; - QStringList args = spec.split(QLatin1Char(':')); - QStringList devices; - - foreach (const QString &arg, args) { - if (arg.startsWith(QLatin1String("/dev/"))) { - devices.append(arg); - args.removeAll(arg); - } - } - - // build new specification without /dev/ elements - m_spec = args.join(QLatin1Char(':')); + auto parsed = QEvdevUtil::parseSpecification(spec); + m_spec = std::move(parsed.spec); - foreach (const QString &device, devices) + for (const QString &device : qAsConst(parsed.devices)) addDevice(device); // when no devices specified, use device discovery to scan and monitor - if (devices.isEmpty()) { + if (parsed.devices.isEmpty()) { qCDebug(qLcEvdevTablet, "evdevtablet: Using device discovery"); m_deviceDiscovery = QDeviceDiscovery::create(QDeviceDiscovery::Device_Tablet, this); if (m_deviceDiscovery) { diff --git a/src/platformsupport/input/evdevtouch/qevdevtouchmanager.cpp b/src/platformsupport/input/evdevtouch/qevdevtouchmanager.cpp index 04d4ac777e..a945bd6168 100644 --- a/src/platformsupport/input/evdevtouch/qevdevtouchmanager.cpp +++ b/src/platformsupport/input/evdevtouch/qevdevtouchmanager.cpp @@ -40,6 +40,8 @@ #include "qevdevtouchmanager_p.h" #include "qevdevtouchhandler_p.h" +#include + #include #include #include @@ -65,24 +67,14 @@ QEvdevTouchManager::QEvdevTouchManager(const QString &key, const QString &specif if (spec.isEmpty()) spec = specification; - QStringList args = spec.split(QLatin1Char(':')); - QStringList devices; - - foreach (const QString &arg, args) { - if (arg.startsWith(QLatin1String("/dev/"))) { - devices.append(arg); - args.removeAll(arg); - } - } - - // build new specification without /dev/ elements - m_spec = args.join(QLatin1Char(':')); + auto parsed = QEvdevUtil::parseSpecification(spec); + m_spec = std::move(parsed.spec); - foreach (const QString &device, devices) + for (const QString &device : qAsConst(parsed.devices)) addDevice(device); // when no devices specified, use device discovery to scan and monitor - if (devices.isEmpty()) { + if (parsed.devices.isEmpty()) { qCDebug(qLcEvdevTouch, "evdevtouch: Using device discovery"); m_deviceDiscovery = QDeviceDiscovery::create(QDeviceDiscovery::Device_Touchpad | QDeviceDiscovery::Device_Touchscreen, this); if (m_deviceDiscovery) { diff --git a/src/platformsupport/input/shared/qevdevutil.cpp b/src/platformsupport/input/shared/qevdevutil.cpp new file mode 100644 index 0000000000..74f8bcdc2b --- /dev/null +++ b/src/platformsupport/input/shared/qevdevutil.cpp @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qevdevutil_p.h" + +QT_BEGIN_NAMESPACE + +namespace QEvdevUtil { + +ParsedSpecification parseSpecification(const QString &specification) +{ + ParsedSpecification result; + + result.args = specification.splitRef(QLatin1Char(':')); + + for (const QStringRef &arg : qAsConst(result.args)) { + if (arg.startsWith(QLatin1String("/dev/"))) { + // if device is specified try to use it + result.devices.append(arg.toString()); + } else { + // build new specification without /dev/ elements + result.spec += arg + QLatin1Char(':'); + } + } + + if (!result.spec.isEmpty()) + result.spec.chop(1); // remove trailing ':' + + return result; +} + +} // namespace QEvdevUtil + +QT_END_NAMESPACE diff --git a/src/platformsupport/input/shared/qevdevutil_p.h b/src/platformsupport/input/shared/qevdevutil_p.h new file mode 100644 index 0000000000..7d0a5af130 --- /dev/null +++ b/src/platformsupport/input/shared/qevdevutil_p.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QEVDEVUTIL_P_H +#define QEVDEVUTIL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace QEvdevUtil { + +struct ParsedSpecification +{ + QString spec; + QStringList devices; + QVector args; +}; + +ParsedSpecification parseSpecification(const QString &specification); + +} + +QT_END_NAMESPACE + +#endif // QEVDEVUTIL_P_H diff --git a/src/platformsupport/input/shared/shared.pri b/src/platformsupport/input/shared/shared.pri index 9608c7de7a..c29d11e7d6 100644 --- a/src/platformsupport/input/shared/shared.pri +++ b/src/platformsupport/input/shared/shared.pri @@ -1,6 +1,8 @@ HEADERS += \ $$PWD/devicehandlerlist_p.h \ + $$PWD/qevdevutil_p.h \ $$PWD/qtouchoutputmapping_p.h SOURCES += \ + $$PWD/qevdevutil.cpp \ $$PWD/qtouchoutputmapping.cpp -- cgit v1.2.3 From 779f1ff9fad916ce7beb7dfbf3a96b0207c01319 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Wed, 12 Jun 2019 23:58:36 +0200 Subject: QEvdev: remove m_deviceDiscovery members MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit They were never referenced outside the classes' ctor and, worse, remained uninitialized if the specification string contained devices. Change-Id: I977a156acf10190428da00fe128fee70cff8f98d Reviewed-by: Allan Sandfeld Jensen Reviewed-by: Mårten Nordheim --- .../input/evdevkeyboard/qevdevkeyboardmanager.cpp | 9 ++++----- .../input/evdevkeyboard/qevdevkeyboardmanager_p.h | 1 - src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp | 9 ++++----- src/platformsupport/input/evdevmouse/qevdevmousemanager_p.h | 1 - src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp | 9 ++++----- src/platformsupport/input/evdevtablet/qevdevtabletmanager_p.h | 1 - src/platformsupport/input/evdevtouch/qevdevtouchmanager.cpp | 9 ++++----- src/platformsupport/input/evdevtouch/qevdevtouchmanager_p.h | 1 - 8 files changed, 16 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp index f272728aaf..52d9c34b1c 100644 --- a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp +++ b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp @@ -72,16 +72,15 @@ QEvdevKeyboardManager::QEvdevKeyboardManager(const QString &key, const QString & if (parsed.devices.isEmpty()) { qCDebug(qLcEvdevKey, "evdevkeyboard: Using device discovery"); - m_deviceDiscovery = QDeviceDiscovery::create(QDeviceDiscovery::Device_Keyboard, this); - if (m_deviceDiscovery) { + if (auto deviceDiscovery = QDeviceDiscovery::create(QDeviceDiscovery::Device_Keyboard, this)) { // scan and add already connected keyboards - const QStringList devices = m_deviceDiscovery->scanConnectedDevices(); + const QStringList devices = deviceDiscovery->scanConnectedDevices(); for (const QString &device : devices) addKeyboard(device); - connect(m_deviceDiscovery, &QDeviceDiscovery::deviceDetected, + connect(deviceDiscovery, &QDeviceDiscovery::deviceDetected, this, &QEvdevKeyboardManager::addKeyboard); - connect(m_deviceDiscovery, &QDeviceDiscovery::deviceRemoved, + connect(deviceDiscovery, &QDeviceDiscovery::deviceRemoved, this, &QEvdevKeyboardManager::removeKeyboard); } } diff --git a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager_p.h b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager_p.h index e495aafc9a..d91da330c3 100644 --- a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager_p.h +++ b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager_p.h @@ -79,7 +79,6 @@ private: QString m_spec; QtInputSupport::DeviceHandlerList m_keyboards; - QDeviceDiscovery *m_deviceDiscovery; QString m_defaultKeymapFile; }; diff --git a/src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp b/src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp index f64ba908e8..0c19294905 100644 --- a/src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp +++ b/src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp @@ -82,16 +82,15 @@ QEvdevMouseManager::QEvdevMouseManager(const QString &key, const QString &specif if (parsed.devices.isEmpty()) { qCDebug(qLcEvdevMouse, "evdevmouse: Using device discovery"); - m_deviceDiscovery = QDeviceDiscovery::create(QDeviceDiscovery::Device_Mouse | QDeviceDiscovery::Device_Touchpad, this); - if (m_deviceDiscovery) { + if (auto deviceDiscovery = QDeviceDiscovery::create(QDeviceDiscovery::Device_Mouse | QDeviceDiscovery::Device_Touchpad, this)) { // scan and add already connected keyboards - const QStringList devices = m_deviceDiscovery->scanConnectedDevices(); + const QStringList devices = deviceDiscovery->scanConnectedDevices(); for (const QString &device : devices) addMouse(device); - connect(m_deviceDiscovery, &QDeviceDiscovery::deviceDetected, + connect(deviceDiscovery, &QDeviceDiscovery::deviceDetected, this, &QEvdevMouseManager::addMouse); - connect(m_deviceDiscovery, &QDeviceDiscovery::deviceRemoved, + connect(deviceDiscovery, &QDeviceDiscovery::deviceRemoved, this, &QEvdevMouseManager::removeMouse); } } diff --git a/src/platformsupport/input/evdevmouse/qevdevmousemanager_p.h b/src/platformsupport/input/evdevmouse/qevdevmousemanager_p.h index 1d547da1e8..f5c32ed8b5 100644 --- a/src/platformsupport/input/evdevmouse/qevdevmousemanager_p.h +++ b/src/platformsupport/input/evdevmouse/qevdevmousemanager_p.h @@ -83,7 +83,6 @@ private: QString m_spec; QtInputSupport::DeviceHandlerList m_mice; - QDeviceDiscovery *m_deviceDiscovery; int m_x; int m_y; int m_xoffset; diff --git a/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp b/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp index 74d6b19e99..d9888c5b97 100644 --- a/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp +++ b/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp @@ -76,15 +76,14 @@ QEvdevTabletManager::QEvdevTabletManager(const QString &key, const QString &spec // when no devices specified, use device discovery to scan and monitor if (parsed.devices.isEmpty()) { qCDebug(qLcEvdevTablet, "evdevtablet: Using device discovery"); - m_deviceDiscovery = QDeviceDiscovery::create(QDeviceDiscovery::Device_Tablet, this); - if (m_deviceDiscovery) { - const QStringList devices = m_deviceDiscovery->scanConnectedDevices(); + if (auto deviceDiscovery = QDeviceDiscovery::create(QDeviceDiscovery::Device_Tablet, this)) { + const QStringList devices = deviceDiscovery->scanConnectedDevices(); for (const QString &device : devices) addDevice(device); - connect(m_deviceDiscovery, &QDeviceDiscovery::deviceDetected, + connect(deviceDiscovery, &QDeviceDiscovery::deviceDetected, this, &QEvdevTabletManager::addDevice); - connect(m_deviceDiscovery, &QDeviceDiscovery::deviceRemoved, + connect(deviceDiscovery, &QDeviceDiscovery::deviceRemoved, this, &QEvdevTabletManager::removeDevice); } } diff --git a/src/platformsupport/input/evdevtablet/qevdevtabletmanager_p.h b/src/platformsupport/input/evdevtablet/qevdevtabletmanager_p.h index 0274f9d3ed..bb18ffba04 100644 --- a/src/platformsupport/input/evdevtablet/qevdevtabletmanager_p.h +++ b/src/platformsupport/input/evdevtablet/qevdevtabletmanager_p.h @@ -75,7 +75,6 @@ private: void updateDeviceCount(); QString m_spec; - QDeviceDiscovery *m_deviceDiscovery; QtInputSupport::DeviceHandlerList m_activeDevices; }; diff --git a/src/platformsupport/input/evdevtouch/qevdevtouchmanager.cpp b/src/platformsupport/input/evdevtouch/qevdevtouchmanager.cpp index a945bd6168..b280f27fac 100644 --- a/src/platformsupport/input/evdevtouch/qevdevtouchmanager.cpp +++ b/src/platformsupport/input/evdevtouch/qevdevtouchmanager.cpp @@ -76,15 +76,14 @@ QEvdevTouchManager::QEvdevTouchManager(const QString &key, const QString &specif // when no devices specified, use device discovery to scan and monitor if (parsed.devices.isEmpty()) { qCDebug(qLcEvdevTouch, "evdevtouch: Using device discovery"); - m_deviceDiscovery = QDeviceDiscovery::create(QDeviceDiscovery::Device_Touchpad | QDeviceDiscovery::Device_Touchscreen, this); - if (m_deviceDiscovery) { - const QStringList devices = m_deviceDiscovery->scanConnectedDevices(); + if (auto deviceDiscovery = QDeviceDiscovery::create(QDeviceDiscovery::Device_Touchpad | QDeviceDiscovery::Device_Touchscreen, this)) { + const QStringList devices = deviceDiscovery->scanConnectedDevices(); for (const QString &device : devices) addDevice(device); - connect(m_deviceDiscovery, &QDeviceDiscovery::deviceDetected, + connect(deviceDiscovery, &QDeviceDiscovery::deviceDetected, this, &QEvdevTouchManager::addDevice); - connect(m_deviceDiscovery, &QDeviceDiscovery::deviceRemoved, + connect(deviceDiscovery, &QDeviceDiscovery::deviceRemoved, this, &QEvdevTouchManager::removeDevice); } } diff --git a/src/platformsupport/input/evdevtouch/qevdevtouchmanager_p.h b/src/platformsupport/input/evdevtouch/qevdevtouchmanager_p.h index 97f3137676..94ee05d900 100644 --- a/src/platformsupport/input/evdevtouch/qevdevtouchmanager_p.h +++ b/src/platformsupport/input/evdevtouch/qevdevtouchmanager_p.h @@ -75,7 +75,6 @@ public: private: QString m_spec; - QDeviceDiscovery *m_deviceDiscovery; QtInputSupport::DeviceHandlerList m_activeDevices; }; -- cgit v1.2.3 From 784c94c7272684439abfa99273f5688d31ab2959 Mon Sep 17 00:00:00 2001 From: Christian Ehrlicher Date: Thu, 13 Jun 2019 21:21:53 +0200 Subject: Replace QPainter::initFrom() with begin() It's deprecated since e56401818b1aae9856a5334f530c4eda33788429 but still needed for QWidget rendering - therefore move it to QPainterPrivate. Change-Id: I35880ffa22830c2921c6675b1acf7e4ca38601db Reviewed-by: Shawn Rutledge --- src/gui/painting/qpainter.cpp | 28 +++++++++++++++++----------- src/gui/painting/qpainter_p.h | 1 + 2 files changed, 18 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index 95e6bda78b..84b34e390b 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -283,7 +283,7 @@ bool QPainterPrivate::attachPainterPrivate(QPainter *q, QPaintDevice *pdev) Q_ASSERT(q->d_ptr->state); // Now initialize the painter with correct widget properties. - q->initFrom(pdev); + q->d_ptr->initFrom(pdev); QPoint offset; pdev->redirected(&offset); offset += q->d_ptr->engine->coordinateOffset(); @@ -1560,22 +1560,28 @@ void QPainter::initFrom(const QPaintDevice *device) { Q_ASSERT_X(device, "QPainter::initFrom(const QPaintDevice *device)", "QPaintDevice cannot be 0"); Q_D(QPainter); - if (!d->engine) { + d->initFrom(device); +} +#endif + +void QPainterPrivate::initFrom(const QPaintDevice *device) +{ + if (!engine) { qWarning("QPainter::initFrom: Painter not active, aborted"); return; } - device->initPainter(this); + Q_Q(QPainter); + device->initPainter(q); - if (d->extended) { - d->extended->penChanged(); - } else if (d->engine) { - d->engine->setDirty(QPaintEngine::DirtyPen); - d->engine->setDirty(QPaintEngine::DirtyBrush); - d->engine->setDirty(QPaintEngine::DirtyFont); + if (extended) { + extended->penChanged(); + } else if (engine) { + engine->setDirty(QPaintEngine::DirtyPen); + engine->setDirty(QPaintEngine::DirtyBrush); + engine->setDirty(QPaintEngine::DirtyFont); } } -#endif /*! Saves the current painter state (pushes the state onto a stack). A @@ -1843,7 +1849,7 @@ bool QPainter::begin(QPaintDevice *pd) // Copy painter properties from original paint device, // required for QPixmap::grabWidget() if (d->original_device->devType() == QInternal::Widget) { - initFrom(d->original_device); + d->initFrom(d->original_device); } else { d->state->layoutDirection = Qt::LayoutDirectionAuto; // make sure we have a font compatible with the paintdevice diff --git a/src/gui/painting/qpainter_p.h b/src/gui/painting/qpainter_p.h index bd2bc4c9b3..29d4880eb9 100644 --- a/src/gui/painting/qpainter_p.h +++ b/src/gui/painting/qpainter_p.h @@ -256,6 +256,7 @@ public: QTransform hidpiScaleTransform() const; static bool attachPainterPrivate(QPainter *q, QPaintDevice *pdev); void detachPainterPrivate(QPainter *q); + void initFrom(const QPaintDevice *device); QPaintDevice *device; QPaintDevice *original_device; -- cgit v1.2.3 From a5e03f59f4bbfe1cf40ec084d137bf4c91923731 Mon Sep 17 00:00:00 2001 From: BogDan Vatra Date: Fri, 24 May 2019 12:34:28 +0300 Subject: Say hello to androidtestrunner MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit androidtestrunner is a tool needed to run qt tests on Android. Now you can run tests as simple as you run them on Linux, macOS, Windows. "$ make check" it's all you need to run tests on the default android device. ANDROID_DEVICE_SERIAL env variable can be used to use a specific android serial. Use cases: $ make -j1 check -j1 is needed to make sure we don't run multiple tests in parallel. $ ANDROID_DEVICE_SERIAL="emulator-5554" make check Run the test on "emulator-5554" $ make TESTARGS="-- -xml" check Switch to xml output. All params after -- are passed to test application. $ make TESTARGS="-- -o out.xml,xml -o out.txt,txt -o -,tap -vs" check Create two files out.xml and out.txt in the current folder and print "tap" format to stdout and enable logging of every signal emission. [ChangeLog][Android] Make it easy to run Qt tests on Android. "$ make check" is all it's needed to run a test on an Android device. Change-Id: I1a7f64b62608f7367b5a6aabf5d6c6e7e50242e6 Reviewed-by: Jędrzej Nowacki --- src/src.pro | 10 +- src/tools/androidtestrunner/androidtestrunner.pro | 13 + src/tools/androidtestrunner/main.cpp | 464 ++++++++++++++++++++++ 3 files changed, 485 insertions(+), 2 deletions(-) create mode 100644 src/tools/androidtestrunner/androidtestrunner.pro create mode 100644 src/tools/androidtestrunner/main.cpp (limited to 'src') diff --git a/src/src.pro b/src/src.pro index 1c76a2e46f..a39b718e10 100644 --- a/src/src.pro +++ b/src/src.pro @@ -57,6 +57,10 @@ src_tools_androiddeployqt.subdir = tools/androiddeployqt src_tools_androiddeployqt.target = sub-androiddeployqt src_tools_androiddeployqt.depends = src_corelib +src_tools_androidtestrunner.subdir = tools/androidtestrunner +src_tools_androidtestrunner.target = sub-androidtestrunner +src_tools_androidtestrunner.depends = src_corelib + src_tools_qvkgen.subdir = tools/qvkgen src_tools_qvkgen.target = sub-qvkgen force_bootstrap: src_tools_qvkgen.depends = src_tools_bootstrap @@ -189,8 +193,10 @@ qtConfig(dbus) { } android { - SUBDIRS += src_tools_androiddeployqt - TOOLS += src_tools_androiddeployqt + SUBDIRS += src_tools_androiddeployqt \ + src_tools_androidtestrunner + TOOLS += src_tools_androiddeployqt \ + src_tools_androidtestrunner } qtConfig(concurrent): SUBDIRS += src_concurrent diff --git a/src/tools/androidtestrunner/androidtestrunner.pro b/src/tools/androidtestrunner/androidtestrunner.pro new file mode 100644 index 0000000000..641d3e0003 --- /dev/null +++ b/src/tools/androidtestrunner/androidtestrunner.pro @@ -0,0 +1,13 @@ +option(host_build) +CONFIG += console + +SOURCES += \ + main.cpp + +# Required for declarations of popen/pclose on Windows +windows: QMAKE_CXXFLAGS += -U__STRICT_ANSI__ + +DEFINES += QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII +DEFINES += QT_NO_FOREACH + +load(qt_app) diff --git a/src/tools/androidtestrunner/main.cpp b/src/tools/androidtestrunner/main.cpp new file mode 100644 index 0000000000..c7c27c97df --- /dev/null +++ b/src/tools/androidtestrunner/main.cpp @@ -0,0 +1,464 @@ +/**************************************************************************** +** +** Copyright (C) 2019 BogDan Vatra +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef Q_CC_MSVC +#define popen _popen +#define QT_POPEN_READ "rb" +#define pclose _pclose +#else +#define QT_POPEN_READ "r" +#endif + +struct Options +{ + bool helpRequested = false; + bool verbose = false; + std::chrono::seconds timeout{300}; // 5minutes + QString androidDeployQtCommand; + QString buildPath; + QString adbCommand{QStringLiteral("adb")}; + QString makeCommand; + QString package; + QString activity; + QStringList testArgsList; + QHash outFiles; + QString testArgs; + QHash> checkFiles = { + {QStringLiteral("txt"), [](const QByteArray &data) -> bool { + return data.indexOf("\nFAIL! : ") < 0; + }}, + {QStringLiteral("csv"), [](const QByteArray &/*data*/) -> bool { + // It seems csv is broken + return true; + }}, + {QStringLiteral("xml"), [](const QByteArray &data) -> bool { + QXmlStreamReader reader{data}; + while (!reader.atEnd()) { + reader.readNext(); + if (reader.isStartElement() && reader.name() == QStringLiteral("Incident") && + reader.attributes().value(QStringLiteral("type")).toString() == QStringLiteral("fail")) { + return false; + } + } + return true; + }}, + {QStringLiteral("lightxml"), [](const QByteArray &data) -> bool { + return data.indexOf("\n bool { + QXmlStreamReader reader{data}; + while (!reader.atEnd()) { + reader.readNext(); + if (reader.isStartElement() && reader.name() == QStringLiteral("testcase") && + reader.attributes().value(QStringLiteral("result")).toString() == QStringLiteral("fail")) { + return false; + } + } + return true; + }}, + {QStringLiteral("teamcity"), [](const QByteArray &data) -> bool { + return data.indexOf("' message='Failure! |[Loc: ") < 0; + }}, + {QStringLiteral("tap"), [](const QByteArray &data) -> bool { + return data.indexOf("\nnot ok ") < 0; + }}, + }; +}; + +static Options g_options; + +static bool execCommand(const QString &command, QByteArray *output = nullptr, bool verbose = false) +{ +#if defined(Q_OS_WIN32) + QString processedCommand = QLatin1Char('\"') + command + QLatin1Char('\"'); +#else + const QString& processedCommand = command; +#endif + + if (verbose) + fprintf(stdout, "Execute %s\n", processedCommand.toUtf8().constData()); + FILE *process = popen(processedCommand.toUtf8().constData(), QT_POPEN_READ); + + if (!process) { + fprintf(stderr, "Cannot execute command %s", qPrintable(processedCommand)); + return false; + } + char buffer[512]; + while (fgets(buffer, sizeof(buffer), process)) { + if (output) + output->append(buffer); + if (verbose) + fprintf(stdout, "%s", buffer); + } + return pclose(process) == 0; +} + +// Copy-pasted from qmake/library/ioutil.cpp +inline static bool hasSpecialChars(const QString &arg, const uchar (&iqm)[16]) +{ + for (int x = arg.length() - 1; x >= 0; --x) { + ushort c = arg.unicode()[x].unicode(); + if ((c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7)))) + return true; + } + return false; +} + +static QString shellQuoteUnix(const QString &arg) +{ + // Chars that should be quoted (TM). This includes: + static const uchar iqm[] = { + 0xff, 0xff, 0xff, 0xff, 0xdf, 0x07, 0x00, 0xd8, + 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x78 + }; // 0-32 \'"$`<>|;&(){}*?#!~[] + + if (!arg.length()) + return QStringLiteral("\"\""); + + QString ret(arg); + if (hasSpecialChars(ret, iqm)) { + ret.replace(QLatin1Char('\''), QStringLiteral("'\\''")); + ret.prepend(QLatin1Char('\'')); + ret.append(QLatin1Char('\'')); + } + return ret; +} + +static QString shellQuoteWin(const QString &arg) +{ + // Chars that should be quoted (TM). This includes: + // - control chars & space + // - the shell meta chars "&()<>^| + // - the potential separators ,;= + static const uchar iqm[] = { + 0xff, 0xff, 0xff, 0xff, 0x45, 0x13, 0x00, 0x78, + 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x10 + }; + + if (!arg.length()) + return QStringLiteral("\"\""); + + QString ret(arg); + if (hasSpecialChars(ret, iqm)) { + // Quotes are escaped and their preceding backslashes are doubled. + // It's impossible to escape anything inside a quoted string on cmd + // level, so the outer quoting must be "suspended". + ret.replace(QRegExp(QStringLiteral("(\\\\*)\"")), QStringLiteral("\"\\1\\1\\^\"\"")); + // The argument must not end with a \ since this would be interpreted + // as escaping the quote -- rather put the \ behind the quote: e.g. + // rather use "foo"\ than "foo\" + int i = ret.length(); + while (i > 0 && ret.at(i - 1) == QLatin1Char('\\')) + --i; + ret.insert(i, QLatin1Char('"')); + ret.prepend(QLatin1Char('"')); + } + return ret; +} + +static QString shellQuote(const QString &arg) +{ + if (QDir::separator() == QLatin1Char('\\')) + return shellQuoteWin(arg); + else + return shellQuoteUnix(arg); +} + +static bool parseOptions() +{ + QStringList arguments = QCoreApplication::arguments(); + int i = 1; + for (; i < arguments.size(); ++i) { + const QString &argument = arguments.at(i); + if (argument.compare(QStringLiteral("--androiddeployqt"), Qt::CaseInsensitive) == 0) { + if (i + 1 == arguments.size()) + g_options.helpRequested = true; + else + g_options.androidDeployQtCommand = arguments.at(++i).trimmed(); + } else if (argument.compare(QStringLiteral("--adb"), Qt::CaseInsensitive) == 0) { + if (i + 1 == arguments.size()) + g_options.helpRequested = true; + else + g_options.adbCommand = arguments.at(++i); + } else if (argument.compare(QStringLiteral("--path"), Qt::CaseInsensitive) == 0) { + if (i + 1 == arguments.size()) + g_options.helpRequested = true; + else + g_options.buildPath = arguments.at(++i); + } else if (argument.compare(QStringLiteral("--make"), Qt::CaseInsensitive) == 0) { + if (i + 1 == arguments.size()) + g_options.helpRequested = true; + else + g_options.makeCommand = arguments.at(++i); + } else if (argument.compare(QStringLiteral("--activity"), Qt::CaseInsensitive) == 0) { + if (i + 1 == arguments.size()) + g_options.helpRequested = true; + else + g_options.activity = arguments.at(++i); + } else if (argument.compare(QStringLiteral("--timeout"), Qt::CaseInsensitive) == 0) { + if (i + 1 == arguments.size()) + g_options.helpRequested = true; + else + g_options.timeout = std::chrono::seconds{arguments.at(++i).toInt()}; + } else if (argument.compare(QStringLiteral("--help"), Qt::CaseInsensitive) == 0) { + g_options.helpRequested = true; + } else if (argument.compare(QStringLiteral("--verbose"), Qt::CaseInsensitive) == 0) { + g_options.verbose = true; + } else if (argument.compare(QStringLiteral("--"), Qt::CaseInsensitive) == 0) { + ++i; + break; + } else { + g_options.testArgsList << arguments.at(i); + } + } + for (;i < arguments.size(); ++i) + g_options.testArgsList << arguments.at(i); + + if (g_options.helpRequested || g_options.androidDeployQtCommand.isEmpty() || g_options.buildPath.isEmpty()) + return false; + + QString serial = qEnvironmentVariable("ANDROID_DEVICE_SERIAL"); + if (!serial.isEmpty()) + g_options.adbCommand += QStringLiteral(" -s %1").arg(serial); + return true; +} + +static void printHelp() +{// "012345678901234567890123456789012345678901234567890123456789012345678901" + fprintf(stderr, "Syntax: %s -- [TESTARGS] \n" + "\n" + " Creates an Android package in a temp directory and\n" + " runs it on the default emulator/device or on the one specified by\n" + " \"ANDROID_DEVICE_SERIAL\" environment variable.\n\n" + " Mandatory arguments:\n" + " --androiddeployqt : The androiddeployqt:\n" + " path including its additional arguments.\n" + " --path : The path where androiddeployqt will build the .apk.\n" + " Optional arguments:\n" + " --adb : The Android ADB command. If missing the one from\n" + " $PATH will be used.\n" + " --activity : The Activity to run. If missing the first\n" + " activity from AndroidManifest.qml file will be used.\n" + " --timout : Timeout to run the test.\n" + " Default is 5 minutes.\n" + " --make : make command, needed to install the qt library.\n" + " If make is missing make sure the --path is set.\n" + " -- arguments that will be passed to the test application.\n" + " --verbose: Prints out information during processing.\n" + " --help: Displays this information.\n\n", + qPrintable(QCoreApplication::arguments().at(0)) + ); +} + +static QString packageNameFromAndroidManifest(const QString &androidManifestPath) +{ + QFile androidManifestXml(androidManifestPath); + if (androidManifestXml.open(QIODevice::ReadOnly)) { + QXmlStreamReader reader(&androidManifestXml); + while (!reader.atEnd()) { + reader.readNext(); + if (reader.isStartElement() && reader.name() == QStringLiteral("manifest")) + return reader.attributes().value(QStringLiteral("package")).toString(); + } + } + return {}; +} + +static QString activityFromAndroidManifest(const QString &androidManifestPath) +{ + QFile androidManifestXml(androidManifestPath); + if (androidManifestXml.open(QIODevice::ReadOnly)) { + QXmlStreamReader reader(&androidManifestXml); + while (!reader.atEnd()) { + reader.readNext(); + if (reader.isStartElement() && reader.name() == QStringLiteral("activity")) + return reader.attributes().value(QStringLiteral("android:name")).toString(); + } + } + return {}; +} + +static void setOutputFile(QString file, QString format) +{ + if (file.isEmpty()) + file = QStringLiteral("-"); + if (format.isEmpty()) + format = QStringLiteral("txt"); + + g_options.outFiles[format] = file; +} + +static bool parseTestArgs() +{ + QRegExp newLoggingFormat{QStringLiteral("(.*),(txt|csv|xunitxml|xml|lightxml|teamcity|tap)")}; + QRegExp oldFormats{QStringLiteral("-(txt|csv|xunitxml|xml|lightxml|teamcity|tap)")}; + + QString file; + QString logType; + QString unhandledArgs; + for (int i = 0; i < g_options.testArgsList.size(); ++i) { + const QString &arg = g_options.testArgsList[i].trimmed(); + if (arg == QStringLiteral("-o")) { + if (i >= g_options.testArgsList.size() - 1) + return false; // missing file argument + + const auto &filePath = g_options.testArgsList[++i]; + if (!newLoggingFormat.exactMatch(filePath)) { + file = filePath; + } else { + const auto capturedTexts = newLoggingFormat.capturedTexts(); + setOutputFile(capturedTexts.at(1), capturedTexts.at(2)); + } + } else if (oldFormats.exactMatch(arg)) { + logType = oldFormats.capturedTexts().at(1); + } else { + unhandledArgs += QStringLiteral(" %1").arg(arg); + } + } + if (g_options.outFiles.isEmpty() || !file.isEmpty() || !logType.isEmpty()) + setOutputFile(file, logType); + + for (const auto &format : g_options.outFiles.keys()) + g_options.testArgs += QStringLiteral(" -o output.%1,%1").arg(format); + + g_options.testArgs += unhandledArgs; + g_options.testArgs = QStringLiteral("shell am start -e applicationArguments \"%1\" -n %2/%3").arg(shellQuote(g_options.testArgs.trimmed()), + g_options.package, + g_options.activity); + return true; +} + +static bool isRunning() { + QByteArray output; + if (!execCommand(QStringLiteral("%1 shell 'ps | grep \" %2\"'").arg(g_options.adbCommand, + shellQuoteUnix(g_options.package)), &output)) { + + return false; + } + return output.indexOf(" " + g_options.package.toUtf8()) > -1; +} + +static bool waitToFinish() +{ + using clock = std::chrono::system_clock; + auto start = clock::now(); + // wait to start + while (!isRunning()) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + if ((clock::now() - start) > std::chrono::seconds{10}) + return false; + } + + // Wait to finish + while (isRunning()) { + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + if ((clock::now() - start) > g_options.timeout) + return false; + } + return true; +} + + +static bool pullFiles() +{ + bool ret = true; + for (auto it = g_options.outFiles.constBegin(); it != g_options.outFiles.end(); ++it) { + QByteArray output; + if (!execCommand(QStringLiteral("%1 shell run-as %2 cat files/output.%3") + .arg(g_options.adbCommand, g_options.package, it.key()), &output)) { + return false; + } + auto checkerIt = g_options.checkFiles.find(it.key()); + ret &= checkerIt != g_options.checkFiles.end() && checkerIt.value()(output); + if (it.value() == QStringLiteral("-")){ + fprintf(stdout, "%s", output.constData()); + fflush(stdout); + } else { + QFile out{it.value()}; + if (!out.open(QIODevice::WriteOnly)) + return false; + out.write(output); + } + } + return ret; +} + +int main(int argc, char *argv[]) +{ + QCoreApplication a(argc, argv); + if (!parseOptions()) { + printHelp(); + return 1; + } + + if (!g_options.makeCommand.isEmpty()) { + // we need to run make INSTALL_ROOT=path install to install the application file(s) first + if (!execCommand(QStringLiteral("%1 INSTALL_ROOT=%2 install") + .arg(g_options.makeCommand, g_options.buildPath), nullptr, g_options.verbose)) { + return 1; + } + } + // Run androiddeployqt + static auto verbose = g_options.verbose ? QStringLiteral("--verbose") : QStringLiteral(); + if (!execCommand(QStringLiteral("%1 %3 --gradle --reinstall --output %2").arg(g_options.androidDeployQtCommand, + g_options.buildPath, + verbose), nullptr, g_options.verbose)) { + return 1; + } + + QString manifest = g_options.buildPath + QStringLiteral("/AndroidManifest.xml"); + g_options.package = packageNameFromAndroidManifest(manifest); + if (g_options.activity.isEmpty()) + g_options.activity = activityFromAndroidManifest(manifest); + + // parseTestArgs depends on g_options.package + if (!parseTestArgs()) + return 1; + + // start the tests + bool res = execCommand(QStringLiteral("%1 %2").arg(g_options.adbCommand, g_options.testArgs), + nullptr, g_options.verbose) && waitToFinish(); + if (res) + res &= pullFiles(); + res &= execCommand(QStringLiteral("%1 uninstall %2").arg(g_options.adbCommand, g_options.package), + nullptr, g_options.verbose); + fflush(stdout); + return res ? 0 : 1; +} -- cgit v1.2.3 From 0ad4da0c64fc28e26f31eb232aa768af966040ac Mon Sep 17 00:00:00 2001 From: BogDan Vatra Date: Wed, 5 Jun 2019 18:38:02 +0300 Subject: Android: Nuke ant support Ant is history for so many years, yet we forgot to remove it from androiddeployqt. [ChangeLog][Android] Remove ant support from androiddeployqt Change-Id: I093295e18e8710c565e9d101e21c49b3c62f6322 Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/tools/androiddeployqt/main.cpp | 136 +++-------------------------------- src/tools/androidtestrunner/main.cpp | 2 +- 2 files changed, 10 insertions(+), 128 deletions(-) (limited to 'src') diff --git a/src/tools/androiddeployqt/main.cpp b/src/tools/androiddeployqt/main.cpp index 65d95362f7..d3febe7383 100644 --- a/src/tools/androiddeployqt/main.cpp +++ b/src/tools/androiddeployqt/main.cpp @@ -118,7 +118,6 @@ struct Options , timing(false) , generateAssetsFileList(true) , build(true) - , gradle(false) , auxMode(false) , deploymentMechanism(Bundled) , releasePackage(false) @@ -150,7 +149,6 @@ struct Options bool timing; bool generateAssetsFileList; bool build; - bool gradle; bool auxMode; bool stripLibraries = true; ActionTimer timer; @@ -159,7 +157,6 @@ struct Options QString sdkPath; QString sdkBuildToolsVersion; QString ndkPath; - QString antTool; QString jdkPath; // Build paths @@ -369,13 +366,6 @@ Options parseOptions() options.helpRequested = true; } else if (argument.compare(QLatin1String("--verbose"), Qt::CaseInsensitive) == 0) { options.verbose = true; - } else if (argument.compare(QLatin1String("--gradle"), Qt::CaseInsensitive) == 0) { - options.gradle = true; - } else if (argument.compare(QLatin1String("--ant"), Qt::CaseInsensitive) == 0) { - if (i + 1 == arguments.size()) - options.helpRequested = true; - else - options.antTool = arguments.at(++i); } else if (argument.compare(QLatin1String("--deployment"), Qt::CaseInsensitive) == 0) { if (i + 1 == arguments.size()) { options.helpRequested = true; @@ -515,9 +505,6 @@ void printHelp() " --android-platform : Builds against the given android\n" " platform. By default, the highest available version will be\n" " used.\n" - " --gradle. Use gradle instead of ant to create and install the apk.\n" - " --ant : If unspecified, ant from the PATH will be\n" - " used.\n" " --release: Builds a package ready for release. By default, the\n" " package will be signed with a debug key.\n" " --sign : Signs the package with the\n" @@ -1024,16 +1011,13 @@ bool copyAndroidTemplate(const Options &options) if (options.verbose) fprintf(stdout, "Copying Android package template.\n"); - if (options.gradle && !copyGradleTemplate(options)) + if (!copyGradleTemplate(options)) return false; if (!copyAndroidTemplate(options, QLatin1String("/src/android/templates"))) return false; - if (options.gradle) - return true; - - return copyAndroidTemplate(options, QLatin1String("/src/android/java")); + return true; } bool copyAndroidSources(const Options &options) @@ -1282,36 +1266,6 @@ bool updateStringsXml(const Options &options) return true; } - if (!updateFile(fileName, replacements)) - return false; - - if (options.gradle) - return true; - - // ant can't (easily) build multiple res folders, - // so we need to replace the "" placeholder - // from the main res folder - QFile stringsXml(fileName); - if (!stringsXml.open(QIODevice::ReadOnly)) { - fprintf(stderr, "Cannot open %s for reading.\n", qPrintable(fileName)); - return false; - } - - QXmlStreamReader reader(&stringsXml); - while (!reader.atEnd()) { - reader.readNext(); - if (reader.isStartElement() && - reader.name() == QLatin1String("string") && - reader.attributes().hasAttribute(QLatin1String("name")) && - reader.attributes().value(QLatin1String("name")) == QLatin1String("app_name")) { - return true; - } - } - - replacements.clear(); - replacements[QStringLiteral("")] = QString::fromLatin1("%1\n") - .arg(QFileInfo(options.applicationBinary).baseName().mid(sizeof("lib") - 1)); - if (!updateFile(fileName, replacements)) return false; @@ -2220,64 +2174,6 @@ QString findInPath(const QString &fileName) return QString(); } -bool buildAntProject(const Options &options) -{ - if (options.verbose) - fprintf(stdout, "Building Android package using ant.\n"); - - QString antTool = options.antTool; - if (antTool.isEmpty()) { -#if defined(Q_OS_WIN32) - antTool = findInPath(QLatin1String("ant.bat")); -#else - antTool = findInPath(QLatin1String("ant")); -#endif - } - - if (antTool.isEmpty()) { - fprintf(stderr, "Cannot find ant in PATH. Please use --ant option to pass in the correct path.\n"); - return false; - } - - if (options.verbose) - fprintf(stdout, "Using ant: %s\n", qPrintable(antTool)); - - QString oldPath = QDir::currentPath(); - if (!QDir::setCurrent(options.outputDirectory)) { - fprintf(stderr, "Cannot current path to %s\n", qPrintable(options.outputDirectory)); - return false; - } - - QString ant = QString::fromLatin1("%1 %2").arg(shellQuote(antTool)).arg(options.releasePackage ? QLatin1String(" release") : QLatin1String(" debug")); - - FILE *antCommand = openProcess(ant); - if (antCommand == 0) { - fprintf(stderr, "Cannot run ant command: %s\n.", qPrintable(ant)); - return false; - } - - char buffer[512]; - while (fgets(buffer, sizeof(buffer), antCommand) != 0) { - fprintf(stdout, "%s", buffer); - fflush(stdout); - } - - int errorCode = pclose(antCommand); - if (errorCode != 0) { - fprintf(stderr, "Building the android package failed!\n"); - if (!options.verbose) - fprintf(stderr, " -- For more information, run this command with --verbose.\n"); - return false; - } - - if (!QDir::setCurrent(oldPath)) { - fprintf(stderr, "Cannot change back to old path: %s\n", qPrintable(oldPath)); - return false; - } - - return true; -} - typedef QMap GradleProperties; static GradleProperties readGradleProperties(const QString &path) @@ -2335,7 +2231,7 @@ static bool mergeGradleProperties(const QString &path, GradleProperties properti return true; } -bool buildGradleProject(const Options &options) +bool buildAndroidProject(const Options &options) { GradleProperties localProperties; localProperties["sdk.dir"] = options.sdkPath.toLocal8Bit(); @@ -2403,12 +2299,6 @@ bool buildGradleProject(const Options &options) return true; } -bool buildAndroidProject(const Options &options) -{ - return options.gradle ? buildGradleProject(options) - : buildAntProject(options); -} - bool uninstallApk(const Options &options) { if (options.verbose) @@ -2445,15 +2335,11 @@ enum PackageType { QString apkPath(const Options &options, PackageType pt) { QString path(options.outputDirectory); - if (options.gradle) { - path += QLatin1String("/build/outputs/apk/"); - QString buildType(options.releasePackage ? QLatin1String("release/") : QLatin1String("debug/")); - if (QDir(path + buildType).exists()) - path += buildType; - path += QDir(options.outputDirectory).dirName() + QLatin1Char('-'); - } else { - path += QLatin1String("/bin/QtApp-"); - } + path += QLatin1String("/build/outputs/apk/"); + QString buildType(options.releasePackage ? QLatin1String("release/") : QLatin1String("debug/")); + if (QDir(path + buildType).exists()) + path += buildType; + path += QDir(options.outputDirectory).dirName() + QLatin1Char('-'); if (options.releasePackage) { path += QLatin1String("release-"); if (pt == UnsignedAPK) @@ -2930,8 +2816,7 @@ int main(int argc, char *argv[]) } if (options.build) { - if (options.gradle) - cleanAndroidFiles(options); + cleanAndroidFiles(options); if (Q_UNLIKELY(options.timing)) fprintf(stdout, "[TIMING] %d ms: Cleaned Android file\n", options.timer.elapsed()); @@ -3005,9 +2890,6 @@ int main(int argc, char *argv[]) if (Q_UNLIKELY(options.timing)) fprintf(stdout, "[TIMING] %d ms: Updated files\n", options.timer.elapsed()); - if (!options.gradle && !createAndroidProject(options)) - return CannotCreateAndroidProject; - if (Q_UNLIKELY(options.timing)) fprintf(stdout, "[TIMING] %d ms: Created project\n", options.timer.elapsed()); diff --git a/src/tools/androidtestrunner/main.cpp b/src/tools/androidtestrunner/main.cpp index c7c27c97df..229253fe85 100644 --- a/src/tools/androidtestrunner/main.cpp +++ b/src/tools/androidtestrunner/main.cpp @@ -437,7 +437,7 @@ int main(int argc, char *argv[]) } // Run androiddeployqt static auto verbose = g_options.verbose ? QStringLiteral("--verbose") : QStringLiteral(); - if (!execCommand(QStringLiteral("%1 %3 --gradle --reinstall --output %2").arg(g_options.androidDeployQtCommand, + if (!execCommand(QStringLiteral("%1 %3 --reinstall --output %2").arg(g_options.androidDeployQtCommand, g_options.buildPath, verbose), nullptr, g_options.verbose)) { return 1; -- cgit v1.2.3 From d73497cf770c92e38903850267fd8737df3578ca Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Thu, 2 May 2019 14:45:35 +0200 Subject: QObject/QWidget::setParent: add assertions to prevent loops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is perfectly possible to accidentally create a parent/child loop. This can happens by direct means (a->setParent(b); b->setParent(a);), or some more subtle means, e.g. class MyClass : public QObject { MyClass() : QObject(this) {} }; Since this is UB, add a few robustness checks to make sure the code above crashes right away (at least in debug builds). Change-Id: I6583c8514b4c1f8a90677b04c77b8e8f0c15dba3 Reviewed-by: Liang Qi Reviewed-by: Sérgio Martins --- src/corelib/kernel/qobject.cpp | 22 ++++++++++++++++++++++ src/corelib/kernel/qobject.h | 4 ++++ src/widgets/kernel/qwidget.cpp | 19 +++++++++++++++++++ 3 files changed, 45 insertions(+) (limited to 'src') diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 965857d408..b4e7568a23 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -58,6 +58,7 @@ #include #include #include +#include #include #if QT_CONFIG(thread) #include @@ -851,6 +852,8 @@ static bool check_parent_thread(QObject *parent, QObject::QObject(QObject *parent) : d_ptr(new QObjectPrivate) { + Q_ASSERT_X(this != parent, Q_FUNC_INFO, "Cannot parent a QObject to itself"); + Q_D(QObject); d_ptr->q_ptr = this; d->threadData = (parent && !parent->thread()) ? parent->d_func()->threadData : QThreadData::current(); @@ -879,6 +882,8 @@ QObject::QObject(QObject *parent) QObject::QObject(QObjectPrivate &dd, QObject *parent) : d_ptr(&dd) { + Q_ASSERT_X(this != parent, Q_FUNC_INFO, "Cannot parent a QObject to itself"); + Q_D(QObject); d_ptr->q_ptr = this; d->threadData = (parent && !parent->thread()) ? parent->d_func()->threadData : QThreadData::current(); @@ -2069,8 +2074,25 @@ void QObjectPrivate::deleteChildren() void QObjectPrivate::setParent_helper(QObject *o) { Q_Q(QObject); + Q_ASSERT_X(q != o, Q_FUNC_INFO, "Cannot parent a QObject to itself"); +#ifdef QT_DEBUG + const auto checkForParentChildLoops = qScopeGuard([&](){ + int depth = 0; + auto p = parent; + while (p) { + if (++depth == CheckForParentChildLoopsWarnDepth) { + qWarning("QObject %p (class: '%s', object name: '%s') may have a loop in its parent-child chain; " + "this is undefined behavior", + q, q->metaObject()->className(), qPrintable(q->objectName())); + } + p = p->parent(); + } + }); +#endif + if (o == parent) return; + if (parent) { QObjectPrivate *parentD = parent->d_func(); if (parentD->isDeletingChildren && wasDeleted diff --git a/src/corelib/kernel/qobject.h b/src/corelib/kernel/qobject.h index ad5e1163bf..7f72b69c1a 100644 --- a/src/corelib/kernel/qobject.h +++ b/src/corelib/kernel/qobject.h @@ -113,6 +113,10 @@ public: int postedEvents; QDynamicMetaObjectData *metaObject; QMetaObject *dynamicMetaObject() const; + +#ifdef QT_DEBUG + enum { CheckForParentChildLoopsWarnDepth = 4096 }; +#endif }; diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 8f927e8bee..58963d5eec 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -79,6 +79,7 @@ #include "private/qstylesheetstyle_p.h" #include "private/qstyle_p.h" #include "qfileinfo.h" +#include "qscopeguard.h" #include #include #include @@ -1123,6 +1124,8 @@ void QWidgetPrivate::adjustFlags(Qt::WindowFlags &flags, QWidget *w) void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f) { Q_Q(QWidget); + Q_ASSERT_X(q != parentWidget, Q_FUNC_INFO, "Cannot parent a QWidget to itself"); + if (Q_UNLIKELY(!qobject_cast(QCoreApplication::instance()))) qFatal("QWidget: Cannot create a QWidget without QApplication"); @@ -10677,6 +10680,22 @@ static void sendWindowChangeToTextureChildrenRecursively(QWidget *widget) void QWidget::setParent(QWidget *parent, Qt::WindowFlags f) { Q_D(QWidget); + Q_ASSERT_X(this != parent, Q_FUNC_INFO, "Cannot parent a QWidget to itself"); +#ifdef QT_DEBUG + const auto checkForParentChildLoops = qScopeGuard([&](){ + int depth = 0; + auto p = parentWidget(); + while (p) { + if (++depth == QObjectPrivate::CheckForParentChildLoopsWarnDepth) { + qWarning("QWidget %p (class: '%s', object name: '%s') may have a loop in its parent-child chain; " + "this is undefined behavior", + this, metaObject()->className(), qPrintable(objectName())); + } + p = p->parentWidget(); + } + }); +#endif + bool resized = testAttribute(Qt::WA_Resized); bool wasCreated = testAttribute(Qt::WA_WState_Created); QWidget *oldtlw = window(); -- cgit v1.2.3 From 079dafc42fdfddbc6bff7d343463e16151afeb2d Mon Sep 17 00:00:00 2001 From: BogDan Vatra Date: Thu, 6 Jun 2019 15:29:01 +0300 Subject: Run prebuilt android test APKs androidtestrunner now checks is the apk is build and if it is, it will skip the build phase. Now we can build the apks in parallel (which takes most of the time) and run them sequentially. This way running tests on Android is much faster. Change-Id: I82f34723ac08f7728cc0daab3366e03821335eed Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/corelib/kernel/qsharedmemory.cpp | 19 ++++++++++- src/corelib/kernel/qsharedmemory.h | 29 +++++++++++++++-- src/corelib/kernel/qsharedmemory_p.h | 12 +++++-- src/corelib/kernel/qsharedmemory_unix.cpp | 7 ++-- src/corelib/kernel/qsharedmemory_win.cpp | 5 ++- src/tools/androiddeployqt/main.cpp | 21 +++++++++++- src/tools/androidtestrunner/main.cpp | 54 ++++++++++++++++++++++++------- src/tools/bootstrap/bootstrap.pro | 16 ++++++++- 8 files changed, 141 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/corelib/kernel/qsharedmemory.cpp b/src/corelib/kernel/qsharedmemory.cpp index c952655cb8..39f3002394 100644 --- a/src/corelib/kernel/qsharedmemory.cpp +++ b/src/corelib/kernel/qsharedmemory.cpp @@ -150,11 +150,18 @@ QSharedMemoryPrivate::makePlatformSafeKey(const QString &key, \sa setKey() */ + +#ifndef QT_NO_QOBJECT QSharedMemory::QSharedMemory(QObject *parent) : QObject(*new QSharedMemoryPrivate, parent) { } - +#else +QSharedMemory::QSharedMemory() + : d_ptr(new QSharedMemoryPrivate) +{ +} +#endif /*! Constructs a shared memory object with the given \a parent and with its key set to \a key. Because its key is set, its create() and @@ -162,11 +169,19 @@ QSharedMemory::QSharedMemory(QObject *parent) \sa setKey(), create(), attach() */ +#ifndef QT_NO_QOBJECT QSharedMemory::QSharedMemory(const QString &key, QObject *parent) : QObject(*new QSharedMemoryPrivate, parent) { setKey(key); } +#else +QSharedMemory::QSharedMemory(const QString &key) + : d_ptr(new QSharedMemoryPrivate) +{ + setKey(key); +} +#endif /*! The destructor clears the key, which forces the shared memory object @@ -604,4 +619,6 @@ QString QSharedMemory::errorString() const QT_END_NAMESPACE +#ifndef QT_NO_QOBJECT #include "moc_qsharedmemory.cpp" +#endif diff --git a/src/corelib/kernel/qsharedmemory.h b/src/corelib/kernel/qsharedmemory.h index 67cf52ac17..6236c6aa4c 100644 --- a/src/corelib/kernel/qsharedmemory.h +++ b/src/corelib/kernel/qsharedmemory.h @@ -40,8 +40,14 @@ #ifndef QSHAREDMEMORY_H #define QSHAREDMEMORY_H -#include - +#include +#ifndef QT_NO_QOBJECT +# include +#else +# include +# include +# include +#endif QT_BEGIN_NAMESPACE @@ -49,9 +55,14 @@ QT_BEGIN_NAMESPACE class QSharedMemoryPrivate; -class Q_CORE_EXPORT QSharedMemory : public QObject +class Q_CORE_EXPORT QSharedMemory +#ifndef QT_NO_QOBJECT + : public QObject +#endif { +#ifndef QT_NO_QOBJECT Q_OBJECT +#endif Q_DECLARE_PRIVATE(QSharedMemory) public: @@ -74,8 +85,17 @@ public: UnknownError }; +#ifndef QT_NO_QOBJECT QSharedMemory(QObject *parent = nullptr); QSharedMemory(const QString &key, QObject *parent = nullptr); +#else + QSharedMemory(); + QSharedMemory(const QString &key); + static QString tr(const char * str) + { + return QString::fromLatin1(str); + } +#endif ~QSharedMemory(); void setKey(const QString &key); @@ -104,6 +124,9 @@ public: private: Q_DISABLE_COPY(QSharedMemory) +#ifdef QT_NO_QOBJECT + QScopedPointer d_ptr; +#endif }; #endif // QT_NO_SHAREDMEMORY diff --git a/src/corelib/kernel/qsharedmemory_p.h b/src/corelib/kernel/qsharedmemory_p.h index bf7c42dc62..e6e989abda 100644 --- a/src/corelib/kernel/qsharedmemory_p.h +++ b/src/corelib/kernel/qsharedmemory_p.h @@ -67,7 +67,10 @@ namespace QSharedMemoryPrivate #else #include "qsystemsemaphore.h" -#include "private/qobject_p.h" + +#ifndef QT_NO_QOBJECT +# include "private/qobject_p.h" +#endif #if !defined(Q_OS_WIN) && !defined(Q_OS_ANDROID) && !defined(Q_OS_INTEGRITY) && !defined(Q_OS_RTEMS) # include @@ -107,9 +110,14 @@ private: }; #endif // QT_NO_SYSTEMSEMAPHORE -class Q_AUTOTEST_EXPORT QSharedMemoryPrivate : public QObjectPrivate +class Q_AUTOTEST_EXPORT QSharedMemoryPrivate +#ifndef QT_NO_QOBJECT + : public QObjectPrivate +#endif { +#ifndef QT_NO_QOBJECT Q_DECLARE_PUBLIC(QSharedMemory) +#endif public: QSharedMemoryPrivate(); diff --git a/src/corelib/kernel/qsharedmemory_unix.cpp b/src/corelib/kernel/qsharedmemory_unix.cpp index d1d44747e0..f6d7e78441 100644 --- a/src/corelib/kernel/qsharedmemory_unix.cpp +++ b/src/corelib/kernel/qsharedmemory_unix.cpp @@ -64,8 +64,11 @@ #ifndef QT_NO_SHAREDMEMORY QT_BEGIN_NAMESPACE -QSharedMemoryPrivate::QSharedMemoryPrivate() - : QObjectPrivate(), memory(0), size(0), error(QSharedMemory::NoError), +QSharedMemoryPrivate::QSharedMemoryPrivate() : +#ifndef QT_NO_QOBJECT + QObjectPrivate(), +#endif + memory(0), size(0), error(QSharedMemory::NoError), #ifndef QT_NO_SYSTEMSEMAPHORE systemSemaphore(QString()), lockedByMe(false), #endif diff --git a/src/corelib/kernel/qsharedmemory_win.cpp b/src/corelib/kernel/qsharedmemory_win.cpp index c1dba30e2b..02de2339a5 100644 --- a/src/corelib/kernel/qsharedmemory_win.cpp +++ b/src/corelib/kernel/qsharedmemory_win.cpp @@ -47,7 +47,10 @@ QT_BEGIN_NAMESPACE #ifndef QT_NO_SHAREDMEMORY -QSharedMemoryPrivate::QSharedMemoryPrivate() : QObjectPrivate(), +QSharedMemoryPrivate::QSharedMemoryPrivate() : +#ifndef QT_NO_QOBJECT + QObjectPrivate(), +#endif memory(0), size(0), error(QSharedMemory::NoError), systemSemaphore(QString()), lockedByMe(false), hand(0) { diff --git a/src/tools/androiddeployqt/main.cpp b/src/tools/androiddeployqt/main.cpp index d3febe7383..10bbd59bfb 100644 --- a/src/tools/androiddeployqt/main.cpp +++ b/src/tools/androiddeployqt/main.cpp @@ -209,6 +209,7 @@ struct Options bool sectionsOnly; bool protectedAuthenticationPath; bool jarSigner; + QString apkPath; // Gdbserver TriState gdbServer; @@ -396,6 +397,11 @@ Options parseOptions() options.helpRequested = true; else options.jdkPath = arguments.at(++i); + } else if (argument.compare(QLatin1String("--apk"), Qt::CaseInsensitive) == 0) { + if (i + 1 == arguments.size()) + options.helpRequested = true; + else + options.apkPath = arguments.at(++i); } else if (argument.compare(QLatin1String("--sign"), Qt::CaseInsensitive) == 0) { if (i + 2 >= arguments.size()) { options.helpRequested = true; @@ -544,6 +550,7 @@ void printHelp() " dependencies into the build directory and update the XML templates.\n" " The project will not be built or installed.\n" " --no-strip: Do not strip debug symbols from libraries.\n" + " --apk : Path where to copy the built apk.\n" " --help: Displays this information.\n\n", qPrintable(QCoreApplication::arguments().at(0)) ); @@ -2389,6 +2396,14 @@ bool installApk(const Options &options) return true; } +bool copyPackage(const Options &options) +{ + fflush(stdout); + auto from = apkPath(options, options.keyStore.isEmpty() ? UnsignedAPK : SignedAPK); + QFile::remove(options.apkPath); + return QFile::copy(from, options.apkPath); +} + bool copyStdCpp(Options *options) { if (options->verbose) @@ -2759,7 +2774,8 @@ enum ErrorCode CannotSignPackage = 15, CannotInstallApk = 16, CannotGenerateAssetsFileList = 18, - CannotCopyAndroidExtraResources = 19 + CannotCopyAndroidExtraResources = 19, + CannotCopyApk = 20 }; int main(int argc, char *argv[]) @@ -2902,6 +2918,9 @@ int main(int argc, char *argv[]) if (!options.keyStore.isEmpty() && !signPackage(options)) return CannotSignPackage; + if (!options.apkPath.isEmpty() && !copyPackage(options)) + return CannotCopyApk; + if (Q_UNLIKELY(options.timing)) fprintf(stdout, "[TIMING] %d ms: Signed package\n", options.timer.elapsed()); } diff --git a/src/tools/androidtestrunner/main.cpp b/src/tools/androidtestrunner/main.cpp index 229253fe85..043c827403 100644 --- a/src/tools/androidtestrunner/main.cpp +++ b/src/tools/androidtestrunner/main.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -60,6 +61,7 @@ struct Options QStringList testArgsList; QHash outFiles; QString testArgs; + QString apkPath; QHash> checkFiles = { {QStringLiteral("txt"), [](const QByteArray &data) -> bool { return data.indexOf("\nFAIL! : ") < 0; @@ -227,6 +229,11 @@ static bool parseOptions() g_options.helpRequested = true; else g_options.makeCommand = arguments.at(++i); + } else if (argument.compare(QStringLiteral("--apk"), Qt::CaseInsensitive) == 0) { + if (i + 1 == arguments.size()) + g_options.helpRequested = true; + else + g_options.apkPath = arguments.at(++i); } else if (argument.compare(QStringLiteral("--activity"), Qt::CaseInsensitive) == 0) { if (i + 1 == arguments.size()) g_options.helpRequested = true; @@ -280,6 +287,8 @@ static void printHelp() " Default is 5 minutes.\n" " --make : make command, needed to install the qt library.\n" " If make is missing make sure the --path is set.\n" + " --apk : If the apk is specified and if exists, we'll skip\n" + " the package building.\n" " -- arguments that will be passed to the test application.\n" " --verbose: Prints out information during processing.\n" " --help: Displays this information.\n\n", @@ -420,6 +429,19 @@ static bool pullFiles() return ret; } +struct RunnerLocker +{ + RunnerLocker() + { + runner.acquire(); + } + ~RunnerLocker() + { + runner.release(); + } + QSystemSemaphore runner{QStringLiteral("androidtestrunner"), 1, QSystemSemaphore::Open}; +}; + int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); @@ -428,19 +450,29 @@ int main(int argc, char *argv[]) return 1; } - if (!g_options.makeCommand.isEmpty()) { - // we need to run make INSTALL_ROOT=path install to install the application file(s) first - if (!execCommand(QStringLiteral("%1 INSTALL_ROOT=%2 install") - .arg(g_options.makeCommand, g_options.buildPath), nullptr, g_options.verbose)) { + RunnerLocker lock; // do not install or run packages while another test is running + if (!g_options.apkPath.isEmpty() && QFile::exists(g_options.apkPath)) { + if (!execCommand(QStringLiteral("%1 install -r %2") + .arg(g_options.adbCommand, g_options.apkPath), nullptr, g_options.verbose)) { + return 1; + } + } else { + if (!g_options.makeCommand.isEmpty()) { + // we need to run make INSTALL_ROOT=path install to install the application file(s) first + if (!execCommand(QStringLiteral("%1 INSTALL_ROOT=%2 install") + .arg(g_options.makeCommand, g_options.buildPath), nullptr, g_options.verbose)) { + return 1; + } + } + + // Run androiddeployqt + static auto verbose = g_options.verbose ? QStringLiteral("--verbose") : QStringLiteral(); + if (!execCommand(QStringLiteral("%1 %3 --reinstall --output %2 --apk %4").arg(g_options.androidDeployQtCommand, + g_options.buildPath, + verbose, + g_options.apkPath), nullptr, true)) { return 1; } - } - // Run androiddeployqt - static auto verbose = g_options.verbose ? QStringLiteral("--verbose") : QStringLiteral(); - if (!execCommand(QStringLiteral("%1 %3 --reinstall --output %2").arg(g_options.androidDeployQtCommand, - g_options.buildPath, - verbose), nullptr, g_options.verbose)) { - return 1; } QString manifest = g_options.buildPath + QStringLiteral("/AndroidManifest.xml"); diff --git a/src/tools/bootstrap/bootstrap.pro b/src/tools/bootstrap/bootstrap.pro index 40c0702f0a..8598fc2721 100644 --- a/src/tools/bootstrap/bootstrap.pro +++ b/src/tools/bootstrap/bootstrap.pro @@ -60,6 +60,8 @@ SOURCES += \ ../../corelib/kernel/qmetatype.cpp \ ../../corelib/kernel/qvariant.cpp \ ../../corelib/kernel/qsystemerror.cpp \ + ../../corelib/kernel/qsharedmemory.cpp \ + ../../corelib/kernel/qsystemsemaphore.cpp \ ../../corelib/plugin/quuid.cpp \ ../../corelib/serialization/qdatastream.cpp \ ../../corelib/serialization/qjson.cpp \ @@ -103,6 +105,12 @@ SOURCES += \ ../../xml/sax/qxml.cpp unix:SOURCES += ../../corelib/kernel/qcore_unix.cpp \ + ../../corelib/kernel/qsharedmemory_posix.cpp \ + ../../corelib/kernel/qsharedmemory_systemv.cpp \ + ../../corelib/kernel/qsharedmemory_unix.cpp \ + ../../corelib/kernel/qsystemsemaphore_posix.cpp \ + ../../corelib/kernel/qsystemsemaphore_systemv.cpp \ + ../../corelib/kernel/qsystemsemaphore_unix.cpp \ ../../corelib/io/qfilesystemengine_unix.cpp \ ../../corelib/io/qfilesystemiterator_unix.cpp \ ../../corelib/io/qfsfileengine_unix.cpp @@ -112,12 +120,18 @@ win32:SOURCES += ../../corelib/global/qoperatingsystemversion_win.cpp \ ../../corelib/io/qfilesystemiterator_win.cpp \ ../../corelib/io/qfsfileengine_win.cpp \ ../../corelib/kernel/qcoreapplication_win.cpp \ + ../../corelib/kernel/qsharedmemory_win.cpp \ + ../../corelib/kernel/qsystemsemaphore_win.cpp \ ../../corelib/plugin/qsystemlibrary.cpp \ mac { SOURCES += \ ../../corelib/kernel/qcoreapplication_mac.cpp \ - ../../corelib/kernel/qcore_mac.cpp + ../../corelib/kernel/qcore_mac.cpp \ + ../../corelib/io/qfilesystemengine_unix.cpp \ + ../../corelib/io/qfilesystemiterator_unix.cpp \ + ../../corelib/io/qfsfileengine_unix.cpp + OBJECTIVE_SOURCES += \ ../../corelib/global/qoperatingsystemversion_darwin.mm \ ../../corelib/kernel/qcore_mac_objc.mm \ -- cgit v1.2.3 From f2bd46d1df587a3d9eac485c5e98caa98b12909d Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Thu, 13 Jun 2019 23:25:18 +0200 Subject: Fix deep-const-correctness of qGetPtrHelper() for smart pointers The function qGetPtrHelper() is mainly used to implement d_func() within the Q_DECLARE_PRIVATE() macro. The whole purpose of d_func() is to propagate const deeply. But if a smart pointer implements this by itself, then the old version of qGetPtrHelper(), by taking the Ptr as a const-&, would always return a const payload pointer, which would fail in the following reinterpret_cast in d_func() to mutable payloads. This was found while experimenting with making QExplicitlySharedDataPointer deep const-correct, and I have no explanation why it seems to have worked with QSharedDataPointer, which is deep-const-correct already. Change-Id: Iee2e8fcce89c58ba2af7818de6f79ed39c5a4030 Reviewed-by: Thiago Macieira --- src/corelib/global/qglobal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index 87ae704ca4..15f4621cc0 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -1096,7 +1096,7 @@ for (auto _container_ = QtPrivate::qMakeForeachContainer(container); \ #endif template inline T *qGetPtrHelper(T *ptr) { return ptr; } -template inline auto qGetPtrHelper(const Ptr &ptr) -> decltype(ptr.operator->()) { return ptr.operator->(); } +template inline auto qGetPtrHelper(Ptr &ptr) -> decltype(ptr.operator->()) { return ptr.operator->(); } // The body must be a statement: #define Q_CAST_IGNORE_ALIGN(body) QT_WARNING_PUSH QT_WARNING_DISABLE_GCC("-Wcast-align") body QT_WARNING_POP -- cgit v1.2.3 From d0b3aaecd90ba7d899c5ee950c836376e7a87eb9 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Sat, 15 Jun 2019 18:07:37 +0200 Subject: Make QIPAddressUtils::toString() const-correct for IPv6 The IPv4 overload takes a IPv4Address, which is just an quint32, so it doesn't matter whether clients call it with a const or a mutable argument. The IPv6 overload, OTOH, took a IPv6Address, which is a typedef for quint8[16]. This allows users to pass a quint16[16], but not a const quint8[16], because that would lose the const. The function, however, doesn't modify the argument, so it could be const. Make it so, even though, due to the typedef, it looks like a redundant top-level const. Change-Id: I0506f6f9026ad616c4450fceb45fea137ac27692 Reviewed-by: Thiago Macieira --- src/corelib/io/qipaddress.cpp | 2 +- src/corelib/io/qipaddress_p.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/corelib/io/qipaddress.cpp b/src/corelib/io/qipaddress.cpp index 131795330b..b3421fca8f 100644 --- a/src/corelib/io/qipaddress.cpp +++ b/src/corelib/io/qipaddress.cpp @@ -256,7 +256,7 @@ static inline QChar toHex(uchar c) return QChar::fromLatin1(QtMiscUtils::toHexLower(c)); } -void toString(QString &appendTo, IPv6Address address) +void toString(QString &appendTo, const IPv6Address address) { // the longest IPv6 address possible is: // "1111:2222:3333:4444:5555:6666:255.255.255.255" diff --git a/src/corelib/io/qipaddress_p.h b/src/corelib/io/qipaddress_p.h index d95cccb3bd..ea31e5883d 100644 --- a/src/corelib/io/qipaddress_p.h +++ b/src/corelib/io/qipaddress_p.h @@ -64,7 +64,7 @@ typedef quint8 IPv6Address[16]; Q_CORE_EXPORT bool parseIp4(IPv4Address &address, const QChar *begin, const QChar *end); Q_CORE_EXPORT const QChar *parseIp6(IPv6Address &address, const QChar *begin, const QChar *end); Q_CORE_EXPORT void toString(QString &appendTo, IPv4Address address); -Q_CORE_EXPORT void toString(QString &appendTo, IPv6Address address); +Q_CORE_EXPORT void toString(QString &appendTo, const IPv6Address address); } // namespace -- cgit v1.2.3 From 54bf20af5dca8ccd835344fd46a9c4c1ee7b8c61 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Mon, 3 Jun 2019 11:43:24 +0200 Subject: QString: towards QStringView::arg() pt.1: modernize some code Replace explicit iterator loops with ranged for loops. Also remove the pointless detach() of the QString result, caused by calling mutable data() instead of using the old trick of const-casting constData(). Change-Id: Ia7e2cb2926dc30b4dba33200b17697fd33d22446 Reviewed-by: Thiago Macieira --- src/corelib/tools/qstring.cpp | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index 345a786df4..c5a98c9f22 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -8915,9 +8915,9 @@ static ArgIndexToPlaceholderMap makeArgIndexToPlaceholderMap(const ParseResult & { ArgIndexToPlaceholderMap result; - for (ParseResult::const_iterator it = parts.begin(), end = parts.end(); it != end; ++it) { - if (it->number >= 0) - result.push_back(it->number); + for (Part part : parts) { + if (part.number >= 0) + result.push_back(part.number); } std::sort(result.begin(), result.end()); @@ -8930,14 +8930,13 @@ static ArgIndexToPlaceholderMap makeArgIndexToPlaceholderMap(const ParseResult & static int resolveStringRefsAndReturnTotalSize(ParseResult &parts, const ArgIndexToPlaceholderMap &argIndexToPlaceholderMap, const QString *args[]) { int totalSize = 0; - for (ParseResult::iterator pit = parts.begin(), end = parts.end(); pit != end; ++pit) { - if (pit->number != -1) { - const ArgIndexToPlaceholderMap::const_iterator ait - = std::find(argIndexToPlaceholderMap.begin(), argIndexToPlaceholderMap.end(), pit->number); - if (ait != argIndexToPlaceholderMap.end()) - pit->stringRef = QStringRef(args[ait - argIndexToPlaceholderMap.begin()]); + for (Part &part : parts) { + if (part.number != -1) { + const auto it = std::find(argIndexToPlaceholderMap.begin(), argIndexToPlaceholderMap.end(), part.number); + if (it != argIndexToPlaceholderMap.end()) + part.stringRef = QStringRef(args[it - argIndexToPlaceholderMap.begin()]); } - totalSize += pit->stringRef.size(); + totalSize += part.stringRef.size(); } return totalSize; } @@ -8963,11 +8962,11 @@ QString QString::multiArg(int numArgs, const QString **args) const // 6: QString result(totalSize, Qt::Uninitialized); - QChar *out = result.data(); + auto out = const_cast(result.constData()); - for (ParseResult::const_iterator it = parts.begin(), end = parts.end(); it != end; ++it) { - if (const int sz = it->stringRef.size()) { - memcpy(out, it->stringRef.constData(), sz * sizeof(QChar)); + for (Part part : parts) { + if (const int sz = part.stringRef.size()) { + memcpy(out, part.stringRef.data(), sz * sizeof(QChar)); out += sz; } } -- cgit v1.2.3 From a44a3a4999329b054c575e9600a556454cf6111d Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Mon, 3 Jun 2019 11:43:24 +0200 Subject: QString: towards QStringView::arg() pt.2: port internal machinery to QStringView MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This involves replacing the use of QStringRef with QStringView and replacing int indexes and sizes with qsizetype ones. Since we use QStringView now, where mid() is cheap and well-defined, remove the offset and length parameters that used to be Part ctor arguments, and use mid() in the caller. Change-Id: I08f3cd467b7b935f734b73d15eb8564b7deaa87e Reviewed-by: Edward Welbourne Reviewed-by: Mårten Nordheim Reviewed-by: Thiago Macieira --- src/corelib/tools/qstring.cpp | 48 +++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index c5a98c9f22..2d4f10ecb0 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -8797,7 +8797,7 @@ QString QString::arg(double a, int fieldWidth, char fmt, int prec, QChar fillCha return replaceArgEscapes(*this, d, fieldWidth, arg, locale_arg, fillChar); } -static int getEscape(const QChar *uc, int *pos, int len, int maxNumber = 999) +static int getEscape(const QChar *uc, qsizetype *pos, qsizetype len, int maxNumber = 999) { int i = *pos; ++i; @@ -8860,17 +8860,17 @@ static int getEscape(const QChar *uc, int *pos, int len, int maxNumber = 999) namespace { struct Part { - Part() : stringRef(), number(0) {} - Part(const QString &s, int pos, int len, int num = -1) noexcept - : stringRef(&s, pos, len), number(num) {} + Q_DECL_CONSTEXPR Part() : string{}, number{0} {} + Q_DECL_CONSTEXPR Part(QStringView s, int num = -1) + : string{s}, number{num} {} - QStringRef stringRef; + QStringView string; int number; }; } // unnamed namespace template <> -class QTypeInfo : public QTypeInfoMerger {}; // Q_DECLARE_METATYPE +class QTypeInfo : public QTypeInfoMerger {}; // Q_DECLARE_METATYPE namespace { @@ -8880,24 +8880,24 @@ enum { ExpectedParts = 32 }; typedef QVarLengthArray ParseResult; typedef QVarLengthArray ArgIndexToPlaceholderMap; -static ParseResult parseMultiArgFormatString(const QString &s) +static ParseResult parseMultiArgFormatString(QStringView s) { ParseResult result; - const QChar *uc = s.constData(); - const int len = s.size(); - const int end = len - 1; - int i = 0; - int last = 0; + const auto uc = s.data(); + const auto len = s.size(); + const auto end = len - 1; + qsizetype i = 0; + qsizetype last = 0; while (i < end) { if (uc[i] == QLatin1Char('%')) { - int percent = i; + qsizetype percent = i; int number = getEscape(uc, &i, len); if (number != -1) { if (last != percent) - result.push_back(Part(s, last, percent - last)); // literal text (incl. failed placeholders) - result.push_back(Part(s, percent, i - percent, number)); // parsed placeholder + result.push_back(Part{s.mid(last, percent - last)}); // literal text (incl. failed placeholders) + result.push_back(Part{s.mid(percent, i - percent), number}); // parsed placeholder last = i; continue; } @@ -8906,7 +8906,7 @@ static ParseResult parseMultiArgFormatString(const QString &s) } if (last < len) - result.push_back(Part(s, last, len - last)); // trailing literal text + result.push_back(Part{s.mid(last, len - last)}); // trailing literal text return result; } @@ -8927,16 +8927,16 @@ static ArgIndexToPlaceholderMap makeArgIndexToPlaceholderMap(const ParseResult & return result; } -static int resolveStringRefsAndReturnTotalSize(ParseResult &parts, const ArgIndexToPlaceholderMap &argIndexToPlaceholderMap, const QString *args[]) +static qsizetype resolveStringRefsAndReturnTotalSize(ParseResult &parts, const ArgIndexToPlaceholderMap &argIndexToPlaceholderMap, const QString *args[]) { - int totalSize = 0; + qsizetype totalSize = 0; for (Part &part : parts) { if (part.number != -1) { const auto it = std::find(argIndexToPlaceholderMap.begin(), argIndexToPlaceholderMap.end(), part.number); if (it != argIndexToPlaceholderMap.end()) - part.stringRef = QStringRef(args[it - argIndexToPlaceholderMap.begin()]); + part.string = *args[it - argIndexToPlaceholderMap.begin()]; } - totalSize += part.stringRef.size(); + totalSize += part.string.size(); } return totalSize; } @@ -8946,7 +8946,7 @@ static int resolveStringRefsAndReturnTotalSize(ParseResult &parts, const ArgInde QString QString::multiArg(int numArgs, const QString **args) const { // Step 1-2 above - ParseResult parts = parseMultiArgFormatString(*this); + ParseResult parts = parseMultiArgFormatString(qToStringViewIgnoringNull(*this)); // 3-4 ArgIndexToPlaceholderMap argIndexToPlaceholderMap = makeArgIndexToPlaceholderMap(parts); @@ -8958,15 +8958,15 @@ QString QString::multiArg(int numArgs, const QString **args) const numArgs - argIndexToPlaceholderMap.size(), toLocal8Bit().data()); // 5 - const int totalSize = resolveStringRefsAndReturnTotalSize(parts, argIndexToPlaceholderMap, args); + const qsizetype totalSize = resolveStringRefsAndReturnTotalSize(parts, argIndexToPlaceholderMap, args); // 6: QString result(totalSize, Qt::Uninitialized); auto out = const_cast(result.constData()); for (Part part : parts) { - if (const int sz = part.stringRef.size()) { - memcpy(out, part.stringRef.data(), sz * sizeof(QChar)); + if (const qsizetype sz = part.string.size()) { + memcpy(out, part.string.data(), sz * sizeof(QChar)); out += sz; } } -- cgit v1.2.3 From cd563b3497a608a91794a7a086e5fbf248e77998 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Fri, 14 Jun 2019 21:20:00 +0200 Subject: QNetworkConfiguration: remove unused member 'serviceNetworkMembers' It's never set anywhere, neiher in QtBase nor in any other Qt module. So, remove. Change-Id: If616d350a1c1c74f6f3e87f8cd98ccb3bff5cf70 Reviewed-by: Lorn Potter --- src/network/bearer/qnetworkconfiguration.cpp | 29 +--------------------------- src/network/bearer/qnetworkconfiguration_p.h | 7 ------- 2 files changed, 1 insertion(+), 35 deletions(-) (limited to 'src') diff --git a/src/network/bearer/qnetworkconfiguration.cpp b/src/network/bearer/qnetworkconfiguration.cpp index f5ced0693a..19bc44e02a 100644 --- a/src/network/bearer/qnetworkconfiguration.cpp +++ b/src/network/bearer/qnetworkconfiguration.cpp @@ -414,34 +414,7 @@ bool QNetworkConfiguration::isRoamingAvailable() const */ QList QNetworkConfiguration::children() const { - QList results; - - if (!d) - return results; - - QMutexLocker locker(&d->mutex); - - if (d->type != QNetworkConfiguration::ServiceNetwork || !d->isValid) - return results; - - for (auto it = d->serviceNetworkMembers.begin(), end = d->serviceNetworkMembers.end(); it != end;) { - QNetworkConfigurationPrivatePointer p = it.value(); - //if we have an invalid member get rid of it -> was deleted earlier on - { - QMutexLocker childLocker(&p->mutex); - - if (!p->isValid) { - it = d->serviceNetworkMembers.erase(it); - continue; - } - } - QNetworkConfiguration item; - item.d = p; - results << item; - ++it; - } - - return results; + return {}; } /*! diff --git a/src/network/bearer/qnetworkconfiguration_p.h b/src/network/bearer/qnetworkconfiguration_p.h index 1b1ece39b7..2213d2ae14 100644 --- a/src/network/bearer/qnetworkconfiguration_p.h +++ b/src/network/bearer/qnetworkconfiguration_p.h @@ -72,13 +72,6 @@ public: isValid(false), roamingSupported(false), timeout(DefaultTimeout) {} - virtual ~QNetworkConfigurationPrivate() - { - //release pointers to member configurations - serviceNetworkMembers.clear(); - } - - QMap serviceNetworkMembers; mutable QMutex mutex; -- cgit v1.2.3 From eed9a8fbd300dafb2802b6c09c018fbda4e13ef1 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Fri, 5 Apr 2019 14:19:06 +0200 Subject: Add accessors for QWindow and QScreen to QWidgetPrivate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rewrite the existing accessor QWidgetPrivate::windowHandle() to accept a mode enumeration that has an "Any" convenience. Based on that, add QWidgetPrivate::associatedScreen(), which is seful in many places where scaling is performed. Prototypically simplify the code. Task-number: QTBUG-62094 Task-number: QTBUG-73231 Change-Id: I516288363d329bce9bc94e4951106f9357bc6cde Reviewed-by: Tor Arne Vestbø --- src/widgets/accessible/qaccessiblemenu.cpp | 13 ++++-------- src/widgets/dialogs/qmessagebox.cpp | 11 +++------- src/widgets/itemviews/qabstractitemview.cpp | 11 ++-------- src/widgets/kernel/qdesktopwidget.cpp | 12 ++--------- src/widgets/kernel/qopenglwidget.cpp | 7 ++----- src/widgets/kernel/qwidget.cpp | 31 +++++++++++++++++++++++++++-- src/widgets/kernel/qwidget_p.h | 17 ++++++++-------- src/widgets/kernel/qwidgetwindow.cpp | 5 +---- src/widgets/styles/qwindowsstyle.cpp | 17 +++------------- src/widgets/widgets/qcombobox.cpp | 22 +++++++++----------- src/widgets/widgets/qdockwidget.cpp | 8 ++++---- src/widgets/widgets/qlineedit_p.cpp | 4 +--- src/widgets/widgets/qmainwindowlayout.cpp | 6 +++--- 13 files changed, 72 insertions(+), 92 deletions(-) (limited to 'src') diff --git a/src/widgets/accessible/qaccessiblemenu.cpp b/src/widgets/accessible/qaccessiblemenu.cpp index 507584eb02..7f87288520 100644 --- a/src/widgets/accessible/qaccessiblemenu.cpp +++ b/src/widgets/accessible/qaccessiblemenu.cpp @@ -47,6 +47,7 @@ #endif #include #include +#include #ifndef QT_NO_ACCESSIBILITY @@ -241,15 +242,9 @@ QObject *QAccessibleMenuItem::object() const /*! \reimp */ QWindow *QAccessibleMenuItem::window() const { - QWindow *result = nullptr; - if (!m_owner.isNull()) { - result = m_owner->windowHandle(); - if (!result) { - if (const QWidget *nativeParent = m_owner->nativeParentWidget()) - result = nativeParent->windowHandle(); - } - } - return result; + return m_owner.isNull() + ? nullptr + : qt_widget_private(m_owner.data())->windowHandle(QWidgetPrivate::WindowHandleMode::Closest); } QRect QAccessibleMenuItem::rect() const diff --git a/src/widgets/dialogs/qmessagebox.cpp b/src/widgets/dialogs/qmessagebox.cpp index f143e3b527..9bfea06a54 100644 --- a/src/widgets/dialogs/qmessagebox.cpp +++ b/src/widgets/dialogs/qmessagebox.cpp @@ -2662,14 +2662,9 @@ QPixmap QMessageBoxPrivate::standardIcon(QMessageBox::Icon icon, QMessageBox *mb break; } if (!tmpIcon.isNull()) { - QWindow *window = nullptr; - if (mb) { - window = mb->windowHandle(); - if (!window) { - if (const QWidget *nativeParent = mb->nativeParentWidget()) - window = nativeParent->windowHandle(); - } - } + QWindow *window = mb + ? qt_widget_private(mb)->windowHandle(QWidgetPrivate::WindowHandleMode::Closest) + : nullptr; return tmpIcon.pixmap(window, QSize(iconSize, iconSize)); } return QPixmap(); diff --git a/src/widgets/itemviews/qabstractitemview.cpp b/src/widgets/itemviews/qabstractitemview.cpp index 95631ffa5f..1d1c144bb8 100644 --- a/src/widgets/itemviews/qabstractitemview.cpp +++ b/src/widgets/itemviews/qabstractitemview.cpp @@ -4461,15 +4461,8 @@ QPixmap QAbstractItemViewPrivate::renderToPixmap(const QModelIndexList &indexes, if (paintPairs.isEmpty()) return QPixmap(); - qreal scale = 1.0f; - - Q_Q(const QAbstractItemView); - QWidget *window = q->window(); - if (window) { - QWindow *windowHandle = window->windowHandle(); - if (windowHandle) - scale = windowHandle->devicePixelRatio(); - } + QWindow *window = windowHandle(WindowHandleMode::Closest); + const qreal scale = window ? window->devicePixelRatio() : qreal(1); QPixmap pixmap(r->size() * scale); pixmap.setDevicePixelRatio(scale); diff --git a/src/widgets/kernel/qdesktopwidget.cpp b/src/widgets/kernel/qdesktopwidget.cpp index d17c7eb36c..ac0cfcf2f5 100644 --- a/src/widgets/kernel/qdesktopwidget.cpp +++ b/src/widgets/kernel/qdesktopwidget.cpp @@ -321,20 +321,12 @@ int QDesktopWidgetPrivate::screenNumber(const QWidget *w) if (screens.isEmpty()) // This should never happen return primaryScreen(); - const QWindow *winHandle = w->windowHandle(); - if (!winHandle) { - if (const QWidget *nativeParent = w->nativeParentWidget()) - winHandle = nativeParent->windowHandle(); - } - // If there is more than one virtual desktop if (screens.count() != screens.constFirst()->virtualSiblings().count()) { // Find the root widget, get a QScreen from it and use the // virtual siblings for checking the window position. - if (winHandle) { - if (const QScreen *winScreen = winHandle->screen()) - screens = winScreen->virtualSiblings(); - } + if (const QScreen *winScreen = qt_widget_private(const_cast(w))->associatedScreen()) + screens = winScreen->virtualSiblings(); } // Get the screen number from window position using screen geometry diff --git a/src/widgets/kernel/qopenglwidget.cpp b/src/widgets/kernel/qopenglwidget.cpp index 374ea53726..ae7729b49b 100644 --- a/src/widgets/kernel/qopenglwidget.cpp +++ b/src/widgets/kernel/qopenglwidget.cpp @@ -1333,11 +1333,8 @@ int QOpenGLWidget::metric(QPaintDevice::PaintDeviceMetric metric) const if (d->inBackingStorePaint) return QWidget::metric(metric); - QWidget *tlw = window(); - QWindow *window = tlw ? tlw->windowHandle() : 0; - QScreen *screen = tlw && tlw->windowHandle() ? tlw->windowHandle()->screen() : 0; - if (!screen && QGuiApplication::primaryScreen()) - screen = QGuiApplication::primaryScreen(); + auto window = d->windowHandle(QWidgetPrivate::WindowHandleMode::TopLevel); + QScreen *screen = window ? window->screen() : QGuiApplication::primaryScreen(); const float dpmx = qt_defaultDpiX() * 100. / 2.54; const float dpmy = qt_defaultDpiY() * 100. / 2.54; diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 58963d5eec..be2906a743 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -1252,6 +1252,33 @@ void QWidgetPrivate::createRecursively() } } +QWindow *QWidgetPrivate::windowHandle(WindowHandleMode mode) const +{ + if (mode == WindowHandleMode::Direct || mode == WindowHandleMode::Closest) { + if (QTLWExtra *x = maybeTopData()) + return x->window; + } + if (mode == WindowHandleMode::Closest) { + if (auto nativeParent = q_func()->nativeParentWidget()) { + if (auto window = nativeParent->windowHandle()) + return window; + } + } + if (mode == WindowHandleMode::TopLevel || mode == WindowHandleMode::Closest) { + if (auto topLevel = q_func()->topLevelWidget()) { + if (auto window = topLevel ->windowHandle()) + return window; + } + } + return nullptr; +} + +QScreen *QWidgetPrivate::associatedScreen() const +{ + if (auto window = windowHandle(WindowHandleMode::Closest)) + return window->screen(); + return nullptr; +} // ### fixme: Qt 6: Remove parameter window from QWidget::create() @@ -8103,7 +8130,7 @@ void QWidgetPrivate::show_sys() { Q_Q(QWidget); - QWidgetWindow *window = windowHandle(); + auto window = qobject_cast(windowHandle()); if (q->testAttribute(Qt::WA_DontShowOnScreen)) { invalidateBackingStore(q->rect()); @@ -8242,7 +8269,7 @@ void QWidgetPrivate::hide_sys() { Q_Q(QWidget); - QWidgetWindow *window = windowHandle(); + auto window = qobject_cast(windowHandle()); if (q->testAttribute(Qt::WA_DontShowOnScreen)) { q->setAttribute(Qt::WA_Mapped, false); diff --git a/src/widgets/kernel/qwidget_p.h b/src/widgets/kernel/qwidget_p.h index c073b8fb03..1f36c192f5 100644 --- a/src/widgets/kernel/qwidget_p.h +++ b/src/widgets/kernel/qwidget_p.h @@ -342,7 +342,15 @@ public: QPainter *sharedPainter() const; void setSharedPainter(QPainter *painter); QWidgetBackingStore *maybeBackingStore() const; - QWidgetWindow *windowHandle() const; + + enum class WindowHandleMode { + Direct, + Closest, + TopLevel + }; + QWindow *windowHandle(WindowHandleMode mode = WindowHandleMode::Direct) const; + + QScreen *associatedScreen() const; template void repaint(T t); @@ -1014,13 +1022,6 @@ inline QWidgetBackingStore *QWidgetPrivate::maybeBackingStore() const return x ? x->backingStoreTracker.data() : nullptr; } -inline QWidgetWindow *QWidgetPrivate::windowHandle() const -{ - if (QTLWExtra *x = maybeTopData()) - return x->window; - return nullptr; -} - QT_END_NAMESPACE #endif // QWIDGET_P_H diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp index 70b305326c..a5d9eee49d 100644 --- a/src/widgets/kernel/qwidgetwindow.cpp +++ b/src/widgets/kernel/qwidgetwindow.cpp @@ -592,10 +592,7 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event) w->window()->raise(); } - QWindow *win = w->windowHandle(); - if (!win) - win = w->nativeParentWidget()->windowHandle(); - if (win) { + if (auto win = qt_widget_private(w)->windowHandle(QWidgetPrivate::WindowHandleMode::Closest)) { const QRect globalGeometry = win->isTopLevel() ? win->geometry() : QRect(win->mapToGlobal(QPoint(0, 0)), win->size()); diff --git a/src/widgets/styles/qwindowsstyle.cpp b/src/widgets/styles/qwindowsstyle.cpp index 7b5da9bfa8..3c0478b84e 100644 --- a/src/widgets/styles/qwindowsstyle.cpp +++ b/src/widgets/styles/qwindowsstyle.cpp @@ -380,23 +380,12 @@ int QWindowsStylePrivate::fixedPixelMetric(QStyle::PixelMetric pm) return QWindowsStylePrivate::InvalidMetric; } -static QWindow *windowOf(const QWidget *w) +static QScreen *screenOf(const QWidget *w) { - QWindow *result = nullptr; if (w) { - result = w->windowHandle(); - if (!result) { - if (const QWidget *np = w->nativeParentWidget()) - result = np->windowHandle(); - } + if (auto screen = qt_widget_private(const_cast(w))->associatedScreen()) + return screen; } - return result; -} - -static QScreen *screenOf(const QWidget *w) -{ - if (const QWindow *window = windowOf(w)) - return window->screen(); return QGuiApplication::primaryScreen(); } diff --git a/src/widgets/widgets/qcombobox.cpp b/src/widgets/widgets/qcombobox.cpp index a1434203d3..a052f2df79 100644 --- a/src/widgets/widgets/qcombobox.cpp +++ b/src/widgets/widgets/qcombobox.cpp @@ -2838,19 +2838,15 @@ void QComboBox::showPopup() bool startTimer = !container->isVisible(); container->raise(); container->create(); - QWindow *containerWindow = container->window()->windowHandle(); - if (containerWindow) { - QWindow *win = window()->windowHandle(); - if (win) { - QScreen *currentScreen = win->screen(); - if (currentScreen && !currentScreen->virtualSiblings().contains(containerWindow->screen())) { - containerWindow->setScreen(currentScreen); - - // This seems to workaround an issue in xcb+multi GPU+multiscreen - // environment where the window might not always show up when screen - // is changed. - container->hide(); - } + if (QWindow *containerWindow = qt_widget_private(container)->windowHandle(QWidgetPrivate::WindowHandleMode::TopLevel)) { + QScreen *currentScreen = d->associatedScreen(); + if (currentScreen && !currentScreen->virtualSiblings().contains(containerWindow->screen())) { + containerWindow->setScreen(currentScreen); + + // This seems to workaround an issue in xcb+multi GPU+multiscreen + // environment where the window might not always show up when screen + // is changed. + container->hide(); } } container->show(); diff --git a/src/widgets/widgets/qdockwidget.cpp b/src/widgets/widgets/qdockwidget.cpp index f98e0e44db..b8b6c12bf3 100644 --- a/src/widgets/widgets/qdockwidget.cpp +++ b/src/widgets/widgets/qdockwidget.cpp @@ -1535,10 +1535,10 @@ bool QDockWidget::event(QEvent *event) d->toggleViewAction->setChecked(true); QPoint parentTopLeft(0, 0); if (isWindow()) { - if (const QWindow *window = windowHandle()) - parentTopLeft = window->screen()->availableVirtualGeometry().topLeft(); - else - parentTopLeft = QGuiApplication::primaryScreen()->availableVirtualGeometry().topLeft(); + const QScreen *screen = d->associatedScreen(); + parentTopLeft = screen + ? screen->availableVirtualGeometry().topLeft() + : QGuiApplication::primaryScreen()->availableVirtualGeometry().topLeft(); } emit visibilityChanged(geometry().right() >= parentTopLeft.x() && geometry().bottom() >= parentTopLeft.y()); } diff --git a/src/widgets/widgets/qlineedit_p.cpp b/src/widgets/widgets/qlineedit_p.cpp index 21e70db0ac..7d580e50a5 100644 --- a/src/widgets/widgets/qlineedit_p.cpp +++ b/src/widgets/widgets/qlineedit_p.cpp @@ -355,9 +355,7 @@ QLineEditPrivate *QLineEditIconButton::lineEditPrivate() const void QLineEditIconButton::paintEvent(QPaintEvent *) { QPainter painter(this); - QWindow *window = nullptr; - if (const QWidget *nativeParent = nativeParentWidget()) - window = nativeParent->windowHandle(); + QWindow *window = qt_widget_private(this)->windowHandle(QWidgetPrivate::WindowHandleMode::Closest); QIcon::Mode state = QIcon::Disabled; if (isEnabled()) state = isDown() ? QIcon::Active : QIcon::Normal; diff --git a/src/widgets/widgets/qmainwindowlayout.cpp b/src/widgets/widgets/qmainwindowlayout.cpp index f54835f23b..b8f997b782 100644 --- a/src/widgets/widgets/qmainwindowlayout.cpp +++ b/src/widgets/widgets/qmainwindowlayout.cpp @@ -2584,9 +2584,9 @@ void QMainWindowLayout::hover(QLayoutItem *widgetItem, const QPoint &mousePos) } } for (QWidget *w : candidates) { - QWindow *handle1 = widget->windowHandle(); - QWindow *handle2 = w->windowHandle(); - if (handle1 && handle2 && handle1->screen() != handle2->screen()) + const QScreen *screen1 = qt_widget_private(widget)->associatedScreen(); + const QScreen *screen2 = qt_widget_private(w)->associatedScreen(); + if (screen1 && screen2 && screen1 != screen2) continue; if (!w->geometry().contains(mousePos)) continue; -- cgit v1.2.3 From 55240bcdf3cc1d6ac357e5007748378c6bf1d7f1 Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Tue, 2 Apr 2019 16:24:44 +0300 Subject: QStringView, QLatin1String: add lastIndexOf methods While touching the code, factor out internal methods to avoid needlees latin1->utf16 conversion. [ChangeLog][QtCore][QLatin1String] Added lastIndexOf(). [ChangeLog][QtCore][QStringView] Added lastIndexOf(). Change-Id: I1c624f00e4ed10111e0d00b86daff7904eeed176 Reviewed-by: Thiago Macieira --- src/corelib/tools/qstring.cpp | 340 +++++++++++++++++++++------------- src/corelib/tools/qstring.h | 22 ++- src/corelib/tools/qstringalgorithms.h | 5 + src/corelib/tools/qstringview.cpp | 18 ++ src/corelib/tools/qstringview.h | 6 + 5 files changed, 264 insertions(+), 127 deletions(-) (limited to 'src') diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index 2d4f10ecb0..53513d4abb 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -145,7 +145,8 @@ extern "C" void qt_toLatin1_mips_dsp_asm(uchar *dst, const ushort *src, int leng // internal qsizetype qFindStringBoyerMoore(QStringView haystack, qsizetype from, QStringView needle, Qt::CaseSensitivity cs); static inline qsizetype qFindChar(QStringView str, QChar ch, qsizetype from, Qt::CaseSensitivity cs) noexcept; -static inline qsizetype qt_last_index_of(QStringView haystack, QChar needle, qsizetype from, Qt::CaseSensitivity cs); +template +static inline qsizetype qLastIndexOf(Haystack haystack, QChar needle, qsizetype from, Qt::CaseSensitivity cs) noexcept; static inline qsizetype qt_string_count(QStringView haystack, QStringView needle, Qt::CaseSensitivity cs); static inline qsizetype qt_string_count(QStringView haystack, QChar needle, Qt::CaseSensitivity cs); @@ -3779,74 +3780,6 @@ int QString::indexOf(const QStringRef &str, int from, Qt::CaseSensitivity cs) co // ### Qt6: qsize return int(QtPrivate::findString(QStringView(unicode(), length()), from, QStringView(str.unicode(), str.length()), cs)); } -#endif // QT_STRINGVIEW_LEVEL < 2 - -static int lastIndexOfHelper(const ushort *haystack, int from, const ushort *needle, int sl, Qt::CaseSensitivity cs) -{ - /* - See indexOf() for explanations. - */ - - auto sv = [sl](const ushort *v) { return QStringView(v, sl); }; - - const ushort *end = haystack; - haystack += from; - const uint sl_minus_1 = sl - 1; - const ushort *n = needle+sl_minus_1; - const ushort *h = haystack+sl_minus_1; - uint hashNeedle = 0, hashHaystack = 0; - int idx; - - if (cs == Qt::CaseSensitive) { - for (idx = 0; idx < sl; ++idx) { - hashNeedle = ((hashNeedle<<1) + *(n-idx)); - hashHaystack = ((hashHaystack<<1) + *(h-idx)); - } - hashHaystack -= *haystack; - - while (haystack >= end) { - hashHaystack += *haystack; - if (hashHaystack == hashNeedle - && qt_compare_strings(sv(needle), sv(haystack), Qt::CaseSensitive) == 0) - return haystack - end; - --haystack; - REHASH(haystack[sl]); - } - } else { - for (idx = 0; idx < sl; ++idx) { - hashNeedle = ((hashNeedle<<1) + foldCase(n-idx, needle)); - hashHaystack = ((hashHaystack<<1) + foldCase(h-idx, end)); - } - hashHaystack -= foldCase(haystack, end); - - while (haystack >= end) { - hashHaystack += foldCase(haystack, end); - if (hashHaystack == hashNeedle - && qt_compare_strings(sv(haystack), sv(needle), Qt::CaseInsensitive) == 0) - return haystack - end; - --haystack; - REHASH(foldCase(haystack + sl, end)); - } - } - return -1; -} - -static inline int lastIndexOfHelper( - const QStringRef &haystack, int from, const QStringRef &needle, Qt::CaseSensitivity cs) -{ - return lastIndexOfHelper(reinterpret_cast(haystack.unicode()), from, - reinterpret_cast(needle.unicode()), needle.size(), cs); -} - -static inline int lastIndexOfHelper( - const QStringRef &haystack, int from, QLatin1String needle, Qt::CaseSensitivity cs) -{ - const int size = needle.size(); - QVarLengthArray s(size); - qt_from_latin1(s.data(), needle.latin1(), size); - return lastIndexOfHelper(reinterpret_cast(haystack.unicode()), from, - s.data(), size, cs); -} /*! Returns the index position of the last occurrence of the string \a @@ -3866,9 +3799,12 @@ static inline int lastIndexOfHelper( */ int QString::lastIndexOf(const QString &str, int from, Qt::CaseSensitivity cs) const { - return QStringRef(this).lastIndexOf(QStringRef(&str), from, cs); + // ### Qt6: qsize + return int(QtPrivate::lastIndexOf(*this, from, str, cs)); } +#endif // QT_STRINGVIEW_LEVEL < 2 + /*! \since 4.5 \overload lastIndexOf() @@ -3890,7 +3826,8 @@ int QString::lastIndexOf(const QString &str, int from, Qt::CaseSensitivity cs) c */ int QString::lastIndexOf(QLatin1String str, int from, Qt::CaseSensitivity cs) const { - return QStringRef(this).lastIndexOf(str, from, cs); + // ### Qt6: qsize + return int(QtPrivate::lastIndexOf(*this, from, str, cs)); } /*! @@ -3902,9 +3839,10 @@ int QString::lastIndexOf(QLatin1String str, int from, Qt::CaseSensitivity cs) co int QString::lastIndexOf(QChar ch, int from, Qt::CaseSensitivity cs) const { // ### Qt6: qsize - return int(qt_last_index_of(QStringView(unicode(), size()), ch, from, cs)); + return int(qLastIndexOf(*this, ch, from, cs)); } +#if QT_STRINGVIEW_LEVEL < 2 /*! \since 4.8 \overload lastIndexOf() @@ -3922,8 +3860,27 @@ int QString::lastIndexOf(QChar ch, int from, Qt::CaseSensitivity cs) const */ int QString::lastIndexOf(const QStringRef &str, int from, Qt::CaseSensitivity cs) const { - return QStringRef(this).lastIndexOf(str, from, cs); + // ### Qt6: qsize + return int(QtPrivate::lastIndexOf(*this, from, str, cs)); } +#endif // QT_STRINGVIEW_LEVEL < 2 + +/*! + \fn int QString::lastIndexOf(QStringView str, int from, Qt::CaseSensitivity cs) const + \since 5.14 + \overload lastIndexOf() + + Returns the index position of the last occurrence of the string view \a + str in this string, searching backward from index position \a + from. If \a from is -1 (default), the search starts at the last + character; if \a from is -2, at the next to last character and so + on. Returns -1 if \a str is not found. + + If \a cs is Qt::CaseSensitive (default), the search is case + sensitive; otherwise the search is case insensitive. + + \sa indexOf(), contains(), count() +*/ #if !(defined(QT_NO_REGEXP) && !QT_CONFIG(regularexpression)) @@ -9560,6 +9517,24 @@ QString &QString::setRawData(const QChar *unicode, int size) \sa indexOf(), QStringView::contains(), QStringView::indexOf(), QString::indexOf() */ +/*! + \fn int QLatin1String::lastIndexOf(QStringView str, int from, Qt::CaseSensitivity cs) const + \fn int QLatin1String::lastIndexOf(QLatin1String l1, int from, Qt::CaseSensitivity cs) const + \fn int QLatin1String::lastIndexOf(QChar c, int from, Qt::CaseSensitivity cs) const + \since 5.14 + + Returns the index position of the last occurrence of the string-view \a str, + Latin-1 string \a l1, or character \a ch, respectively, in this Latin-1 string, + searching backward from index position \a from. If \a from is -1 (default), + the search starts at the last character; if \a from is -2, at the next to last + character and so on. Returns -1 if \a str is not found. + + If \a cs is Qt::CaseSensitive (default), the search is case + sensitive; otherwise the search is case insensitive. + + \sa indexOf(), QStringView::lastIndexOf(), QStringView::indexOf(), QString::indexOf() +*/ + /*! \fn QLatin1String::const_iterator QLatin1String::begin() const \since 5.10 @@ -11231,7 +11206,8 @@ int QStringRef::indexOf(const QStringRef &str, int from, Qt::CaseSensitivity cs) */ int QStringRef::lastIndexOf(const QString &str, int from, Qt::CaseSensitivity cs) const { - return lastIndexOf(QStringRef(&str), from, cs); + // ### Qt6: qsize + return int(QtPrivate::lastIndexOf(*this, from, str, cs)); } /*! @@ -11246,28 +11222,7 @@ int QStringRef::lastIndexOf(const QString &str, int from, Qt::CaseSensitivity cs int QStringRef::lastIndexOf(QChar ch, int from, Qt::CaseSensitivity cs) const { // ### Qt6: qsize - return int(qt_last_index_of(QStringView(unicode(), size()), ch, from, cs)); -} - -template -static int last_index_of_impl(const QStringRef &haystack, int from, const T &needle, Qt::CaseSensitivity cs) -{ - const int sl = needle.size(); - if (sl == 1) - return haystack.lastIndexOf(needle.at(0), from, cs); - - const int l = haystack.size(); - if (from < 0) - from += l; - int delta = l - sl; - if (from == l && sl == 0) - return from; - if (uint(from) >= uint(l) || delta < 0) - return -1; - if (from > delta) - from = delta; - - return lastIndexOfHelper(haystack, from, needle, cs); + return int(qLastIndexOf(*this, ch, from, cs)); } /*! @@ -11287,7 +11242,8 @@ static int last_index_of_impl(const QStringRef &haystack, int from, const T &nee */ int QStringRef::lastIndexOf(QLatin1String str, int from, Qt::CaseSensitivity cs) const { - return last_index_of_impl(*this, from, str, cs); + // ### Qt6: qsize + return int(QtPrivate::lastIndexOf(*this, from, str, cs)); } /*! @@ -11307,9 +11263,27 @@ int QStringRef::lastIndexOf(QLatin1String str, int from, Qt::CaseSensitivity cs) */ int QStringRef::lastIndexOf(const QStringRef &str, int from, Qt::CaseSensitivity cs) const { - return last_index_of_impl(*this, from, str, cs); + // ### Qt6: qsize + return int(QtPrivate::lastIndexOf(*this, from, str, cs)); } +/*! + \fn int QStringRef::lastIndexOf(QStringView str, int from, Qt::CaseSensitivity cs) const + \since 5.14 + \overload lastIndexOf() + + Returns the index position of the last occurrence of the string view \a + str in this string, searching backward from index position \a + from. If \a from is -1 (default), the search starts at the last + character; if \a from is -2, at the next to last character and so + on. Returns -1 if \a str is not found. + + If \a cs is Qt::CaseSensitive (default), the search is case + sensitive; otherwise the search is case insensitive. + + \sa indexOf(), contains(), count() +*/ + /*! \since 4.8 Returns the number of (potentially overlapping) occurrences of @@ -11612,33 +11586,6 @@ bool QStringRef::endsWith(const QStringRef &str, Qt::CaseSensitivity cs) const \sa indexOf(), count() */ -static inline qsizetype qt_last_index_of(QStringView haystack, QChar needle, - qsizetype from, Qt::CaseSensitivity cs) -{ - if (from < 0) - from += haystack.size(); - if (std::size_t(from) >= std::size_t(haystack.size())) - return -1; - if (from >= 0) { - ushort c = needle.unicode(); - const ushort *b = reinterpret_cast(haystack.data()); - const ushort *n = b + from; - if (cs == Qt::CaseSensitive) { - for (; n >= b; --n) - if (*n == c) - return n - b; - } else { - c = foldCase(c); - for (; n >= b; --n) - if (foldCase(*n) == c) - return n - b; - } - } - return -1; - - -} - static inline qsizetype qt_string_count(QStringView haystack, QStringView needle, Qt::CaseSensitivity cs) { qsizetype num = 0; @@ -11820,6 +11767,38 @@ bool QtPrivate::endsWith(QLatin1String haystack, QLatin1String needle, Qt::CaseS return qt_ends_with_impl(haystack, needle, cs); } +namespace { +template +uint foldCaseHelper(Pointer ch, Pointer start) = delete; + +template <> +uint foldCaseHelper(const QChar* ch, const QChar* start) +{ + return foldCase(reinterpret_cast(ch), reinterpret_cast(start)); +} + +template <> +uint foldCaseHelper(const char* ch, const char*) +{ + return foldCase(ushort(uchar(*ch))); +} + +template +ushort valueTypeToUtf16(T t) = delete; + +template <> +ushort valueTypeToUtf16(QChar t) +{ + return t.unicode(); +} + +template <> +ushort valueTypeToUtf16(char t) +{ + return ushort(uchar(t)); +} +} + /*! \internal @@ -11928,6 +11907,97 @@ qsizetype QtPrivate::findString(QStringView haystack0, qsizetype from, QStringVi return -1; } +template +static inline qsizetype qLastIndexOf(Haystack haystack, QChar needle, + qsizetype from, Qt::CaseSensitivity cs) noexcept +{ + if (from < 0) + from += haystack.size(); + if (std::size_t(from) >= std::size_t(haystack.size())) + return -1; + if (from >= 0) { + ushort c = needle.unicode(); + const auto b = haystack.data(); + auto n = b + from; + if (cs == Qt::CaseSensitive) { + for (; n >= b; --n) + if (valueTypeToUtf16(*n) == c) + return n - b; + } else { + c = foldCase(c); + for (; n >= b; --n) + if (foldCase(valueTypeToUtf16(*n)) == c) + return n - b; + } + } + return -1; +} + +template +static qsizetype qLastIndexOf(Haystack haystack0, qsizetype from, + Needle needle0, Qt::CaseSensitivity cs) noexcept +{ + const qsizetype sl = needle0.size(); + if (sl == 1) + return qLastIndexOf(haystack0, needle0.front(), from, cs); + + const qsizetype l = haystack0.size(); + if (from < 0) + from += l; + if (from == l && sl == 0) + return from; + const qsizetype delta = l - sl; + if (std::size_t(from) >= std::size_t(l) || delta < 0) + return -1; + if (from > delta) + from = delta; + + auto sv = [sl](const typename Haystack::value_type *v) { return Haystack(v, sl); }; + + auto haystack = haystack0.data(); + const auto needle = needle0.data(); + const auto *end = haystack; + haystack += from; + const std::size_t sl_minus_1 = sl - 1; + const auto *n = needle + sl_minus_1; + const auto *h = haystack + sl_minus_1; + std::size_t hashNeedle = 0, hashHaystack = 0; + qsizetype idx; + + if (cs == Qt::CaseSensitive) { + for (idx = 0; idx < sl; ++idx) { + hashNeedle = (hashNeedle << 1) + valueTypeToUtf16(*(n - idx)); + hashHaystack = (hashHaystack << 1) + valueTypeToUtf16(*(h - idx)); + } + hashHaystack -= valueTypeToUtf16(*haystack); + + while (haystack >= end) { + hashHaystack += valueTypeToUtf16(*haystack); + if (hashHaystack == hashNeedle + && qt_compare_strings(needle0, sv(haystack), Qt::CaseSensitive) == 0) + return haystack - end; + --haystack; + REHASH(valueTypeToUtf16(haystack[sl])); + } + } else { + for (idx = 0; idx < sl; ++idx) { + hashNeedle = (hashNeedle << 1) + foldCaseHelper(n - idx, needle); + hashHaystack = (hashHaystack << 1) + foldCaseHelper(h - idx, end); + } + hashHaystack -= foldCaseHelper(haystack, end); + + while (haystack >= end) { + hashHaystack += foldCaseHelper(haystack, end); + if (hashHaystack == hashNeedle + && qt_compare_strings(sv(haystack), needle0, Qt::CaseInsensitive) == 0) + return haystack - end; + --haystack; + REHASH(foldCaseHelper(haystack + sl, end)); + } + } + return -1; +} + qsizetype QtPrivate::findString(QStringView haystack, qsizetype from, QLatin1String needle, Qt::CaseSensitivity cs) noexcept { if (haystack.size() < needle.size()) @@ -11961,6 +12031,26 @@ qsizetype QtPrivate::findString(QLatin1String haystack, qsizetype from, QLatin1S QStringView(reinterpret_cast(n.constData()), n.size()), cs); } +qsizetype QtPrivate::lastIndexOf(QStringView haystack, qsizetype from, QStringView needle, Qt::CaseSensitivity cs) noexcept +{ + return qLastIndexOf(haystack, from, needle, cs); +} + +qsizetype QtPrivate::lastIndexOf(QStringView haystack, qsizetype from, QLatin1String needle, Qt::CaseSensitivity cs) noexcept +{ + return qLastIndexOf(haystack, from, needle, cs); +} + +qsizetype QtPrivate::lastIndexOf(QLatin1String haystack, qsizetype from, QStringView needle, Qt::CaseSensitivity cs) noexcept +{ + return qLastIndexOf(haystack, from, needle, cs); +} + +qsizetype QtPrivate::lastIndexOf(QLatin1String haystack, qsizetype from, QLatin1String needle, Qt::CaseSensitivity cs) noexcept +{ + return qLastIndexOf(haystack, from, needle, cs); +} + /*! \since 4.8 diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index a885ad1412..89b25821ee 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -145,6 +145,13 @@ public: Q_REQUIRED_RESULT inline bool contains(QChar c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept { return indexOf(QStringView(&c, 1), 0, cs) != -1; } + Q_REQUIRED_RESULT int lastIndexOf(QStringView s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept + { return int(QtPrivate::lastIndexOf(*this, from, s, cs)); } // ### Qt6: qsize + Q_REQUIRED_RESULT int lastIndexOf(QLatin1String s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept + { return int(QtPrivate::lastIndexOf(*this, from, s, cs)); } // ### Qt6: qsize + Q_REQUIRED_RESULT inline int lastIndexOf(QChar c, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept + { return int(QtPrivate::lastIndexOf(*this, from, QStringView(&c, 1), cs)); } // ### Qt6: qsize + using value_type = const char; using reference = value_type&; using const_reference = reference; @@ -235,6 +242,8 @@ qsizetype QStringView::indexOf(QLatin1String s, qsizetype from, Qt::CaseSensitiv { return QtPrivate::findString(*this, from, s, cs); } bool QStringView::contains(QLatin1String s, Qt::CaseSensitivity cs) const noexcept { return indexOf(s, 0, cs) != qsizetype(-1); } +qsizetype QStringView::lastIndexOf(QLatin1String s, qsizetype from, Qt::CaseSensitivity cs) const noexcept +{ return QtPrivate::lastIndexOf(*this, from, s, cs); } class Q_CORE_EXPORT QString { @@ -357,9 +366,14 @@ public: Q_REQUIRED_RESULT int indexOf(QStringView s, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept { return int(QtPrivate::findString(*this, from, s, cs)); } // ### Qt6: qsize int lastIndexOf(QChar c, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; - int lastIndexOf(const QString &s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; int lastIndexOf(QLatin1String s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; +#if QT_STRINGVIEW_LEVEL < 2 + int lastIndexOf(const QString &s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; int lastIndexOf(const QStringRef &s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; +#endif + + Q_REQUIRED_RESULT int lastIndexOf(QStringView s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept + { return int(QtPrivate::lastIndexOf(*this, from, s, cs)); } // ### Qt6: qsize inline bool contains(QChar c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; #if QT_STRINGVIEW_LEVEL < 2 @@ -1541,10 +1555,14 @@ public: { return int(QtPrivate::findString(*this, from, s, cs)); } // ### Qt6: qsize int indexOf(QChar ch, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; int indexOf(QLatin1String str, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; +#if QT_STRINGVIEW_LEVEL < 2 + int lastIndexOf(const QStringRef &str, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; int lastIndexOf(const QString &str, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; +#endif int lastIndexOf(QChar ch, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; int lastIndexOf(QLatin1String str, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; - int lastIndexOf(const QStringRef &str, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + Q_REQUIRED_RESULT int lastIndexOf(QStringView s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept + { return int(QtPrivate::lastIndexOf(*this, from, s, cs)); } // ### Qt6: qsize #if QT_STRINGVIEW_LEVEL < 2 inline bool contains(const QString &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; diff --git a/src/corelib/tools/qstringalgorithms.h b/src/corelib/tools/qstringalgorithms.h index 51a86cfeb5..2b480b1e4c 100644 --- a/src/corelib/tools/qstringalgorithms.h +++ b/src/corelib/tools/qstringalgorithms.h @@ -80,6 +80,11 @@ Q_REQUIRED_RESULT Q_CORE_EXPORT Q_DECL_PURE_FUNCTION qsizetype findString(QStrin Q_REQUIRED_RESULT Q_CORE_EXPORT Q_DECL_PURE_FUNCTION qsizetype findString(QLatin1String haystack, qsizetype from, QStringView needle, Qt::CaseSensitivity cs = Qt::CaseSensitive) noexcept; Q_REQUIRED_RESULT Q_CORE_EXPORT Q_DECL_PURE_FUNCTION qsizetype findString(QLatin1String haystack, qsizetype from, QLatin1String needle, Qt::CaseSensitivity cs = Qt::CaseSensitive) noexcept; +Q_REQUIRED_RESULT Q_CORE_EXPORT Q_DECL_PURE_FUNCTION qsizetype lastIndexOf(QStringView haystack, qsizetype from, QStringView needle, Qt::CaseSensitivity cs = Qt::CaseSensitive) noexcept; +Q_REQUIRED_RESULT Q_CORE_EXPORT Q_DECL_PURE_FUNCTION qsizetype lastIndexOf(QStringView haystack, qsizetype from, QLatin1String needle, Qt::CaseSensitivity cs = Qt::CaseSensitive) noexcept; +Q_REQUIRED_RESULT Q_CORE_EXPORT Q_DECL_PURE_FUNCTION qsizetype lastIndexOf(QLatin1String haystack, qsizetype from, QStringView needle, Qt::CaseSensitivity cs = Qt::CaseSensitive) noexcept; +Q_REQUIRED_RESULT Q_CORE_EXPORT Q_DECL_PURE_FUNCTION qsizetype lastIndexOf(QLatin1String haystack, qsizetype from, QLatin1String needle, Qt::CaseSensitivity cs = Qt::CaseSensitive) noexcept; + Q_REQUIRED_RESULT Q_CORE_EXPORT Q_DECL_PURE_FUNCTION QStringView trimmed(QStringView s) noexcept; Q_REQUIRED_RESULT Q_CORE_EXPORT Q_DECL_PURE_FUNCTION QLatin1String trimmed(QLatin1String s) noexcept; diff --git a/src/corelib/tools/qstringview.cpp b/src/corelib/tools/qstringview.cpp index 050097b443..d94ed4110e 100644 --- a/src/corelib/tools/qstringview.cpp +++ b/src/corelib/tools/qstringview.cpp @@ -754,6 +754,24 @@ QT_BEGIN_NAMESPACE \sa indexOf() */ +/*! + \fn qsizetype QStringView::lastIndexOf(QStringView str, qsizetype from, Qt::CaseSensitivity cs) const + \fn qsizetype QStringView::lastIndexOf(QLatin1String l1, qsizetype from, Qt::CaseSensitivity cs) const + \fn qsizetype QStringView::lastIndexOf(QChar c, qsizetype from, Qt::CaseSensitivity cs) const + \since 5.14 + + Returns the index position of the last occurrence of the string-view \a str, + Latin-1 string \a l1, or character \a ch, respectively, in this string-view, + searching backward from index position \a from. If \a from is -1 (default), + the search starts at the last character; if \a from is -2, at the next to last + character and so on. Returns -1 if \a str is not found. + + If \a cs is Qt::CaseSensitive (default), the search is case + sensitive; otherwise the search is case insensitive. + + \sa QString::lastIndexOf() +*/ + /*! \fn QByteArray QStringView::toLatin1() const diff --git a/src/corelib/tools/qstringview.h b/src/corelib/tools/qstringview.h index 2c93b31385..67f5d2203c 100644 --- a/src/corelib/tools/qstringview.h +++ b/src/corelib/tools/qstringview.h @@ -282,6 +282,12 @@ public: { return indexOf(s, 0, cs) != qsizetype(-1); } Q_REQUIRED_RESULT inline bool contains(QLatin1String s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept; + Q_REQUIRED_RESULT qsizetype lastIndexOf(QChar c, qsizetype from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept + { return QtPrivate::lastIndexOf(*this, from, QStringView(&c, 1), cs); } + Q_REQUIRED_RESULT qsizetype lastIndexOf(QStringView s, qsizetype from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept + { return QtPrivate::lastIndexOf(*this, from, s, cs); } + Q_REQUIRED_RESULT inline qsizetype lastIndexOf(QLatin1String s, qsizetype from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept; + Q_REQUIRED_RESULT bool isRightToLeft() const noexcept { return QtPrivate::isRightToLeft(*this); } -- cgit v1.2.3 From b17e05729e3404d2fe65f28a903a7a1b9f6cc115 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 18 Jun 2019 10:41:12 +0200 Subject: vkmemalloc: Disable SRWLOCK for mingw ...until some day someone investigates what is exactly the problem with the sdk that ships with mingw. Task-number: QTBUG-76424 Change-Id: I64869a4fbb07a64cfc1430234d732e143fefe720 Reviewed-by: Friedemann Kleint --- .../patches/0003-Disable-srwlock-for-mingw.patch | 13 +++++++++++++ src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 src/3rdparty/VulkanMemoryAllocator/patches/0003-Disable-srwlock-for-mingw.patch (limited to 'src') diff --git a/src/3rdparty/VulkanMemoryAllocator/patches/0003-Disable-srwlock-for-mingw.patch b/src/3rdparty/VulkanMemoryAllocator/patches/0003-Disable-srwlock-for-mingw.patch new file mode 100644 index 0000000000..ab7acfe40b --- /dev/null +++ b/src/3rdparty/VulkanMemoryAllocator/patches/0003-Disable-srwlock-for-mingw.patch @@ -0,0 +1,13 @@ +diff --git a/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h b/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h +index f043bdc289..2355de091f 100644 +--- a/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h ++++ b/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h +@@ -3298,7 +3298,7 @@ void *aligned_alloc(size_t alignment, size_t size) + std::shared_mutex m_Mutex; + }; + #define VMA_RW_MUTEX VmaRWMutex +- #elif defined(_WIN32) ++ #elif defined(_WIN32) && !defined(__MINGW32__) + // Use SRWLOCK from WinAPI. + class VmaRWMutex + { diff --git a/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h b/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h index f043bdc289..2355de091f 100644 --- a/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h +++ b/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h @@ -3298,7 +3298,7 @@ void *aligned_alloc(size_t alignment, size_t size) std::shared_mutex m_Mutex; }; #define VMA_RW_MUTEX VmaRWMutex - #elif defined(_WIN32) + #elif defined(_WIN32) && !defined(__MINGW32__) // Use SRWLOCK from WinAPI. class VmaRWMutex { -- cgit v1.2.3 From c826e677658e858eaff309e02571e7876702a4ed Mon Sep 17 00:00:00 2001 From: Simone Gaiarin Date: Thu, 14 Mar 2019 12:41:17 +0100 Subject: QAction: add exclusionPolicy property When set to ExclusiveOptional, the new exclusionPolicy property let the user uncheck the active checkable action in an exclusive group. [ChangeLog][QtWidgets][QActionGroup] Added new exclusionPolicy property. Set it to ExclusiveOptional to allow unchecking the active checkable action in an exclusive group. Change-Id: I61a9885cfd076d631cddf8c08313e4b488e5dc38 Fixes: QTBUG-71160 Reviewed-by: Richard Moe Gustavsen --- src/widgets/kernel/qaction.cpp | 5 +- src/widgets/kernel/qactiongroup.cpp | 96 ++++++++++++++++++++++++++++++------- src/widgets/kernel/qactiongroup.h | 11 ++++- 3 files changed, 92 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/widgets/kernel/qaction.cpp b/src/widgets/kernel/qaction.cpp index 1ca5514655..f6631199d6 100644 --- a/src/widgets/kernel/qaction.cpp +++ b/src/widgets/kernel/qaction.cpp @@ -1153,8 +1153,9 @@ void QAction::activate(ActionEvent event) if(event == Trigger) { QPointer guard = this; if(d->checkable) { - // the checked action of an exclusive group cannot be unchecked - if (d->checked && (d->group && d->group->isExclusive() + // the checked action of an exclusive group may not be unchecked + if (d->checked && (d->group + && d->group->exclusionPolicy() == QActionGroup::ExclusionPolicy::Exclusive && d->group->checkedAction() == this)) { if (!guard.isNull()) emit triggered(true); diff --git a/src/widgets/kernel/qactiongroup.cpp b/src/widgets/kernel/qactiongroup.cpp index 4786437d7e..ab42b1c7aa 100644 --- a/src/widgets/kernel/qactiongroup.cpp +++ b/src/widgets/kernel/qactiongroup.cpp @@ -52,12 +52,16 @@ class QActionGroupPrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QActionGroup) public: - QActionGroupPrivate() : exclusive(1), enabled(1), visible(1) { } + QActionGroupPrivate() : enabled(1), + visible(1), + exclusionPolicy(QActionGroup::ExclusionPolicy::Exclusive) + { + } QList actions; QPointer current; - uint exclusive : 1; uint enabled : 1; uint visible : 1; + QActionGroup::ExclusionPolicy exclusionPolicy; private: void _q_actionTriggered(); //private slot @@ -70,7 +74,7 @@ void QActionGroupPrivate::_q_actionChanged() Q_Q(QActionGroup); QAction *action = qobject_cast(q->sender()); Q_ASSERT_X(action != 0, "QWidgetGroup::_q_actionChanged", "internal error"); - if(exclusive) { + if (exclusionPolicy != QActionGroup::ExclusionPolicy::None) { if (action->isChecked()) { if (action != current) { if(current) @@ -127,12 +131,17 @@ void QActionGroupPrivate::_q_actionHovered() actions is chosen. Each action in an action group emits its triggered() signal as usual. - As stated above, an action group is \l exclusive by default; it - ensures that only one checkable action is active at any one time. + As stated above, an action group is exclusive by default; it + ensures that at most only one checkable action is active at any one time. If you want to group checkable actions without making them - exclusive, you can turn of exclusiveness by calling + exclusive, you can turn off exclusiveness by calling setExclusive(false). + By default the active action of an exclusive group cannot be unchecked. + In some cases it may be useful to allow unchecking all the actions, + you can allow this by calling + setExclusionPolicy(QActionGroup::ExclusionPolicy::ExclusiveOptional). + Actions can be added to an action group using addAction(), but it is usually more convenient to specify a group when creating actions; this ensures that actions are automatically created with @@ -145,11 +154,34 @@ void QActionGroupPrivate::_q_actionHovered() \sa QAction */ +/*! + \enum QActionGroup::ExclusionPolicy + + This enum specifies the different policies that can be used to + control how the group performs exclusive checking on checkable actions. + + \value None + The actions in the group can be checked independently of each other. + + \value Exclusive + Exactly one action can be checked at any one time. + This is the default policy. + + \value ExclusiveOptional + At most one action can be checked at any one time. The actions + can also be all unchecked. + + \sa exclusionPolicy + \since 5.14 +*/ + /*! Constructs an action group for the \a parent object. The action group is exclusive by default. Call setExclusive(false) - to make the action group non-exclusive. + to make the action group non-exclusive. To make the group exclusive + but allow unchecking the active action call instead + setExclusionPolicy(QActionGroup::ExclusionPolicy::ExclusiveOptional) */ QActionGroup::QActionGroup(QObject* parent) : QObject(*new QActionGroupPrivate, parent) { @@ -258,26 +290,56 @@ QList QActionGroup::actions() const } /*! - \property QActionGroup::exclusive - \brief whether the action group does exclusive checking + \brief Enable or disable the group exclusion checking - If exclusive is true, only one checkable action in the action group - can ever be active at any time. If the user chooses another - checkable action in the group, the one they chose becomes active and - the one that was active becomes inactive. + This is a convenience method that calls + setExclusionPolicy(ExclusionPolicy::Exclusive). - \sa QAction::checkable + \sa QActionGroup::exclusionPolicy */ void QActionGroup::setExclusive(bool b) { - Q_D(QActionGroup); - d->exclusive = b; + setExclusionPolicy(b ? QActionGroup::ExclusionPolicy::Exclusive + : QActionGroup::ExclusionPolicy::None); } +/*! + \brief Returs true if the group is exclusive + + The group is exclusive if the ExclusionPolicy is either Exclusive + or ExclusionOptional. + +*/ bool QActionGroup::isExclusive() const +{ + return exclusionPolicy() != QActionGroup::ExclusionPolicy::None; +} + +/*! + \property QActionGroup::exclusionPolicy + \brief This property holds the group exclusive checking policy + + If exclusionPolicy is set to Exclusive, only one checkable + action in the action group can ever be active at any time. If the user + chooses another checkable action in the group, the one they chose becomes + active and the one that was active becomes inactive. If exclusionPolicy is + set to ExclusionOptional the group is exclusive but the active checkable + action in the group can be unchecked leaving the group with no actions + checked. + + \sa QAction::checkable + \since 5.14 +*/ +void QActionGroup::setExclusionPolicy(QActionGroup::ExclusionPolicy policy) +{ + Q_D(QActionGroup); + d->exclusionPolicy = policy; +} + +QActionGroup::ExclusionPolicy QActionGroup::exclusionPolicy() const { Q_D(const QActionGroup); - return d->exclusive; + return d->exclusionPolicy; } /*! diff --git a/src/widgets/kernel/qactiongroup.h b/src/widgets/kernel/qactiongroup.h index 61c90b911d..90f488bedb 100644 --- a/src/widgets/kernel/qactiongroup.h +++ b/src/widgets/kernel/qactiongroup.h @@ -55,11 +55,18 @@ class Q_WIDGETS_EXPORT QActionGroup : public QObject Q_OBJECT Q_DECLARE_PRIVATE(QActionGroup) - Q_PROPERTY(bool exclusive READ isExclusive WRITE setExclusive) + Q_PROPERTY(QActionGroup::ExclusionPolicy exclusionPolicy READ exclusionPolicy WRITE setExclusionPolicy) Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled) Q_PROPERTY(bool visible READ isVisible WRITE setVisible) public: + enum class ExclusionPolicy { + None, + Exclusive, + ExclusiveOptional + }; + Q_ENUM(ExclusionPolicy) + explicit QActionGroup(QObject* parent); ~QActionGroup(); @@ -73,6 +80,7 @@ public: bool isExclusive() const; bool isEnabled() const; bool isVisible() const; + ExclusionPolicy exclusionPolicy() const; public Q_SLOTS: @@ -80,6 +88,7 @@ public Q_SLOTS: inline void setDisabled(bool b) { setEnabled(!b); } void setVisible(bool); void setExclusive(bool); + void setExclusionPolicy(ExclusionPolicy policy); Q_SIGNALS: void triggered(QAction *); -- cgit v1.2.3 From 91d2905f438072ac63bbc01000369aa0b5795cc8 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Mon, 3 Jun 2019 08:34:15 +0200 Subject: ANGLE: Decorate symbol names in MinGW .def files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to be able to link against the created ANGLE libraries, MinGW needs decorated symbol names inside the .def files. Task-number: QTBUG-76087 Change-Id: I090dbfb8fd468e2ea18de3785e2b694294f4b63a Reviewed-by: Jörg Bornemann --- src/3rdparty/angle/src/libEGL/libEGL_mingw32.def | 142 ++-- src/3rdparty/angle/src/libEGL/libEGLd_mingw32.def | 142 ++-- .../angle/src/libGLESv2/libGLESv2_mingw32.def | 805 ++++++++++----------- .../angle/src/libGLESv2/libGLESv2d_mingw32.def | 805 ++++++++++----------- 4 files changed, 946 insertions(+), 948 deletions(-) (limited to 'src') diff --git a/src/3rdparty/angle/src/libEGL/libEGL_mingw32.def b/src/3rdparty/angle/src/libEGL/libEGL_mingw32.def index e68d27295e..14eb331b3a 100644 --- a/src/3rdparty/angle/src/libEGL/libEGL_mingw32.def +++ b/src/3rdparty/angle/src/libEGL/libEGL_mingw32.def @@ -1,77 +1,77 @@ LIBRARY libEGL EXPORTS - eglBindAPI @14 - eglBindTexImage @20 - eglChooseConfig @7 - eglCopyBuffers @33 - eglCreateContext @23 - eglCreatePbufferFromClientBuffer @18 - eglCreatePbufferSurface @10 - eglCreatePixmapSurface @11 - eglCreateWindowSurface @9 - eglDestroyContext @24 - eglDestroySurface @12 - eglGetConfigAttrib @8 - eglGetConfigs @6 - eglGetCurrentContext @26 - eglGetCurrentDisplay @28 - eglGetCurrentSurface @27 - eglGetDisplay @2 - eglGetError @1 - eglGetProcAddress @34 - eglInitialize @3 - eglMakeCurrent @25 - eglQueryAPI @15 - eglQueryContext @29 - eglQueryString @5 - eglQuerySurface @13 - eglReleaseTexImage @21 - eglReleaseThread @17 - eglSurfaceAttrib @19 - eglSwapBuffers @32 - eglSwapInterval @22 - eglTerminate @4 - eglWaitClient @16 - eglWaitGL @30 - eglWaitNative @31 + eglBindAPI@4 @14 + eglBindTexImage@12 @20 + eglChooseConfig@20 @7 + eglCopyBuffers@12 @33 + eglCreateContext@16 @23 + eglCreatePbufferFromClientBuffer@20 @18 + eglCreatePbufferSurface@12 @10 + eglCreatePixmapSurface@16 @11 + eglCreateWindowSurface@16 @9 + eglDestroyContext@8 @24 + eglDestroySurface@8 @12 + eglGetConfigAttrib@16 @8 + eglGetConfigs@16 @6 + eglGetCurrentContext@0 @26 + eglGetCurrentDisplay@0 @28 + eglGetCurrentSurface@4 @27 + eglGetDisplay@4 @2 + eglGetError@0 @1 + eglGetProcAddress@4 @34 + eglInitialize@12 @3 + eglMakeCurrent@16 @25 + eglQueryAPI@0 @15 + eglQueryContext@16 @29 + eglQueryString@8 @5 + eglQuerySurface@16 @13 + eglReleaseTexImage@12 @21 + eglReleaseThread@0 @17 + eglSurfaceAttrib@16 @19 + eglSwapBuffers@8 @32 + eglSwapInterval@8 @22 + eglTerminate@4 @4 + eglWaitClient@0 @16 + eglWaitGL@0 @30 + eglWaitNative@4 @31 ; Extensions - eglGetPlatformDisplayEXT @35 - eglQuerySurfacePointerANGLE @36 - eglPostSubBufferNV @37 - eglQueryDisplayAttribEXT @48 - eglQueryDeviceAttribEXT @49 - eglQueryDeviceStringEXT @50 - eglCreateImageKHR @51 - eglDestroyImageKHR @52 - eglCreateDeviceANGLE @53 - eglReleaseDeviceANGLE @54 - eglCreateStreamKHR @55 - eglDestroyStreamKHR @56 - eglStreamAttribKHR @57 - eglQueryStreamKHR @58 - eglQueryStreamu64KHR @59 - eglStreamConsumerGLTextureExternalKHR @60 - eglStreamConsumerAcquireKHR @61 - eglStreamConsumerReleaseKHR @62 - eglStreamConsumerGLTextureExternalAttribsNV @63 - eglCreateStreamProducerD3DTextureNV12ANGLE @64 - eglStreamPostD3DTextureNV12ANGLE @65 - eglGetSyncValuesCHROMIUM @66 - eglSwapBuffersWithDamageEXT @67 - eglProgramCacheGetAttribANGLE @68 - eglProgramCachePopulateANGLE @69 - eglProgramCacheQueryANGLE @70 - eglProgramCacheResizeANGLE @71 + eglGetPlatformDisplayEXT@12 @35 + eglQuerySurfacePointerANGLE@16 @36 + eglPostSubBufferNV@24 @37 + eglQueryDisplayAttribEXT@12 @48 + eglQueryDeviceAttribEXT@12 @49 + eglQueryDeviceStringEXT@8 @50 + eglCreateImageKHR@20 @51 + eglDestroyImageKHR@8 @52 + eglCreateDeviceANGLE@12 @53 + eglReleaseDeviceANGLE@4 @54 + eglCreateStreamKHR@8 @55 + eglDestroyStreamKHR@8 @56 + eglStreamAttribKHR@16 @57 + eglQueryStreamKHR@16 @58 + eglQueryStreamu64KHR@16 @59 + eglStreamConsumerGLTextureExternalKHR@8 @60 + eglStreamConsumerAcquireKHR@8 @61 + eglStreamConsumerReleaseKHR@8 @62 + eglStreamConsumerGLTextureExternalAttribsNV@12 @63 + eglCreateStreamProducerD3DTextureNV12ANGLE@12 @64 + eglStreamPostD3DTextureNV12ANGLE@16 @65 + eglGetSyncValuesCHROMIUM@20 @66 + eglSwapBuffersWithDamageEXT@16 @67 + eglProgramCacheGetAttribANGLE@8 @68 + eglProgramCachePopulateANGLE@20 @69 + eglProgramCacheQueryANGLE@24 @70 + eglProgramCacheResizeANGLE@12 @71 ; 1.5 entry points - eglCreateSync @38 - eglDestroySync @39 - eglClientWaitSync @40 - eglGetSyncAttrib @41 - eglCreateImage @42 - eglDestroyImage @43 - eglGetPlatformDisplay @44 - eglCreatePlatformWindowSurface @45 - eglCreatePlatformPixmapSurface @46 - eglWaitSync @47 + eglCreateSync@12 @38 + eglDestroySync@8 @39 + eglClientWaitSync@20 @40 + eglGetSyncAttrib@16 @41 + eglCreateImage@20 @42 + eglDestroyImage@8 @43 + eglGetPlatformDisplay@12 @44 + eglCreatePlatformWindowSurface@16 @45 + eglCreatePlatformPixmapSurface@16 @46 + eglWaitSync@12 @47 diff --git a/src/3rdparty/angle/src/libEGL/libEGLd_mingw32.def b/src/3rdparty/angle/src/libEGL/libEGLd_mingw32.def index e68d27295e..14eb331b3a 100644 --- a/src/3rdparty/angle/src/libEGL/libEGLd_mingw32.def +++ b/src/3rdparty/angle/src/libEGL/libEGLd_mingw32.def @@ -1,77 +1,77 @@ LIBRARY libEGL EXPORTS - eglBindAPI @14 - eglBindTexImage @20 - eglChooseConfig @7 - eglCopyBuffers @33 - eglCreateContext @23 - eglCreatePbufferFromClientBuffer @18 - eglCreatePbufferSurface @10 - eglCreatePixmapSurface @11 - eglCreateWindowSurface @9 - eglDestroyContext @24 - eglDestroySurface @12 - eglGetConfigAttrib @8 - eglGetConfigs @6 - eglGetCurrentContext @26 - eglGetCurrentDisplay @28 - eglGetCurrentSurface @27 - eglGetDisplay @2 - eglGetError @1 - eglGetProcAddress @34 - eglInitialize @3 - eglMakeCurrent @25 - eglQueryAPI @15 - eglQueryContext @29 - eglQueryString @5 - eglQuerySurface @13 - eglReleaseTexImage @21 - eglReleaseThread @17 - eglSurfaceAttrib @19 - eglSwapBuffers @32 - eglSwapInterval @22 - eglTerminate @4 - eglWaitClient @16 - eglWaitGL @30 - eglWaitNative @31 + eglBindAPI@4 @14 + eglBindTexImage@12 @20 + eglChooseConfig@20 @7 + eglCopyBuffers@12 @33 + eglCreateContext@16 @23 + eglCreatePbufferFromClientBuffer@20 @18 + eglCreatePbufferSurface@12 @10 + eglCreatePixmapSurface@16 @11 + eglCreateWindowSurface@16 @9 + eglDestroyContext@8 @24 + eglDestroySurface@8 @12 + eglGetConfigAttrib@16 @8 + eglGetConfigs@16 @6 + eglGetCurrentContext@0 @26 + eglGetCurrentDisplay@0 @28 + eglGetCurrentSurface@4 @27 + eglGetDisplay@4 @2 + eglGetError@0 @1 + eglGetProcAddress@4 @34 + eglInitialize@12 @3 + eglMakeCurrent@16 @25 + eglQueryAPI@0 @15 + eglQueryContext@16 @29 + eglQueryString@8 @5 + eglQuerySurface@16 @13 + eglReleaseTexImage@12 @21 + eglReleaseThread@0 @17 + eglSurfaceAttrib@16 @19 + eglSwapBuffers@8 @32 + eglSwapInterval@8 @22 + eglTerminate@4 @4 + eglWaitClient@0 @16 + eglWaitGL@0 @30 + eglWaitNative@4 @31 ; Extensions - eglGetPlatformDisplayEXT @35 - eglQuerySurfacePointerANGLE @36 - eglPostSubBufferNV @37 - eglQueryDisplayAttribEXT @48 - eglQueryDeviceAttribEXT @49 - eglQueryDeviceStringEXT @50 - eglCreateImageKHR @51 - eglDestroyImageKHR @52 - eglCreateDeviceANGLE @53 - eglReleaseDeviceANGLE @54 - eglCreateStreamKHR @55 - eglDestroyStreamKHR @56 - eglStreamAttribKHR @57 - eglQueryStreamKHR @58 - eglQueryStreamu64KHR @59 - eglStreamConsumerGLTextureExternalKHR @60 - eglStreamConsumerAcquireKHR @61 - eglStreamConsumerReleaseKHR @62 - eglStreamConsumerGLTextureExternalAttribsNV @63 - eglCreateStreamProducerD3DTextureNV12ANGLE @64 - eglStreamPostD3DTextureNV12ANGLE @65 - eglGetSyncValuesCHROMIUM @66 - eglSwapBuffersWithDamageEXT @67 - eglProgramCacheGetAttribANGLE @68 - eglProgramCachePopulateANGLE @69 - eglProgramCacheQueryANGLE @70 - eglProgramCacheResizeANGLE @71 + eglGetPlatformDisplayEXT@12 @35 + eglQuerySurfacePointerANGLE@16 @36 + eglPostSubBufferNV@24 @37 + eglQueryDisplayAttribEXT@12 @48 + eglQueryDeviceAttribEXT@12 @49 + eglQueryDeviceStringEXT@8 @50 + eglCreateImageKHR@20 @51 + eglDestroyImageKHR@8 @52 + eglCreateDeviceANGLE@12 @53 + eglReleaseDeviceANGLE@4 @54 + eglCreateStreamKHR@8 @55 + eglDestroyStreamKHR@8 @56 + eglStreamAttribKHR@16 @57 + eglQueryStreamKHR@16 @58 + eglQueryStreamu64KHR@16 @59 + eglStreamConsumerGLTextureExternalKHR@8 @60 + eglStreamConsumerAcquireKHR@8 @61 + eglStreamConsumerReleaseKHR@8 @62 + eglStreamConsumerGLTextureExternalAttribsNV@12 @63 + eglCreateStreamProducerD3DTextureNV12ANGLE@12 @64 + eglStreamPostD3DTextureNV12ANGLE@16 @65 + eglGetSyncValuesCHROMIUM@20 @66 + eglSwapBuffersWithDamageEXT@16 @67 + eglProgramCacheGetAttribANGLE@8 @68 + eglProgramCachePopulateANGLE@20 @69 + eglProgramCacheQueryANGLE@24 @70 + eglProgramCacheResizeANGLE@12 @71 ; 1.5 entry points - eglCreateSync @38 - eglDestroySync @39 - eglClientWaitSync @40 - eglGetSyncAttrib @41 - eglCreateImage @42 - eglDestroyImage @43 - eglGetPlatformDisplay @44 - eglCreatePlatformWindowSurface @45 - eglCreatePlatformPixmapSurface @46 - eglWaitSync @47 + eglCreateSync@12 @38 + eglDestroySync@8 @39 + eglClientWaitSync@20 @40 + eglGetSyncAttrib@16 @41 + eglCreateImage@20 @42 + eglDestroyImage@8 @43 + eglGetPlatformDisplay@12 @44 + eglCreatePlatformWindowSurface@16 @45 + eglCreatePlatformPixmapSurface@16 @46 + eglWaitSync@12 @47 diff --git a/src/3rdparty/angle/src/libGLESv2/libGLESv2_mingw32.def b/src/3rdparty/angle/src/libGLESv2/libGLESv2_mingw32.def index 2ff4cc0579..a182c21a05 100644 --- a/src/3rdparty/angle/src/libGLESv2/libGLESv2_mingw32.def +++ b/src/3rdparty/angle/src/libGLESv2/libGLESv2_mingw32.def @@ -1,412 +1,411 @@ LIBRARY libGLESv2 EXPORTS - glActiveTexture @1 - glAttachShader @2 - glBindAttribLocation @3 - glBindBuffer @4 - glBindFramebuffer @5 - glBindRenderbuffer @6 - glBindTexture @7 - glBlendColor @8 - glBlendEquation @9 - glBlendEquationSeparate @10 - glBlendFunc @11 - glBlendFuncSeparate @12 - glBufferData @13 - glBufferSubData @14 - glCheckFramebufferStatus @15 - glClear @16 - glClearColor @17 - glClearDepthf @18 - glClearStencil @19 - glColorMask @20 - glCompileShader @21 - glCompressedTexImage2D @22 - glCompressedTexSubImage2D @23 - glCopyTexImage2D @24 - glCopyTexSubImage2D @25 - glCreateProgram @26 - glCreateShader @27 - glCullFace @28 - glDeleteBuffers @29 - glDeleteFramebuffers @30 - glDeleteProgram @32 - glDeleteRenderbuffers @33 - glDeleteShader @34 - glDeleteTextures @31 - glDepthFunc @36 - glDepthMask @37 - glDepthRangef @38 - glDetachShader @35 - glDisable @39 - glDisableVertexAttribArray @40 - glDrawArrays @41 - glDrawElements @42 - glEnable @43 - glEnableVertexAttribArray @44 - glFinish @45 - glFlush @46 - glFramebufferRenderbuffer @47 - glFramebufferTexture2D @48 - glFrontFace @49 - glGenBuffers @50 - glGenFramebuffers @52 - glGenRenderbuffers @53 - glGenTextures @54 - glGenerateMipmap @51 - glGetActiveAttrib @55 - glGetActiveUniform @56 - glGetAttachedShaders @57 - glGetAttribLocation @58 - glGetBooleanv @59 - glGetBufferParameteriv @60 - glGetError @61 - glGetFloatv @62 - glGetFramebufferAttachmentParameteriv @63 - glGetIntegerv @64 - glGetProgramInfoLog @66 - glGetProgramiv @65 - glGetRenderbufferParameteriv @67 - glGetShaderInfoLog @69 - glGetShaderPrecisionFormat @70 - glGetShaderSource @71 - glGetShaderiv @68 - glGetString @72 - glGetTexParameterfv @73 - glGetTexParameteriv @74 - glGetUniformLocation @77 - glGetUniformfv @75 - glGetUniformiv @76 - glGetVertexAttribPointerv @80 - glGetVertexAttribfv @78 - glGetVertexAttribiv @79 - glHint @81 - glIsBuffer @82 - glIsEnabled @83 - glIsFramebuffer @84 - glIsProgram @85 - glIsRenderbuffer @86 - glIsShader @87 - glIsTexture @88 - glLineWidth @89 - glLinkProgram @90 - glPixelStorei @91 - glPolygonOffset @92 - glReadPixels @93 - glReleaseShaderCompiler @94 - glRenderbufferStorage @95 - glSampleCoverage @96 - glScissor @97 - glShaderBinary @98 - glShaderSource @99 - glStencilFunc @100 - glStencilFuncSeparate @101 - glStencilMask @102 - glStencilMaskSeparate @103 - glStencilOp @104 - glStencilOpSeparate @105 - glTexImage2D @106 - glTexParameterf @107 - glTexParameterfv @108 - glTexParameteri @109 - glTexParameteriv @110 - glTexSubImage2D @111 - glUniform1f @112 - glUniform1fv @113 - glUniform1i @114 - glUniform1iv @115 - glUniform2f @116 - glUniform2fv @117 - glUniform2i @118 - glUniform2iv @119 - glUniform3f @120 - glUniform3fv @121 - glUniform3i @122 - glUniform3iv @123 - glUniform4f @124 - glUniform4fv @125 - glUniform4i @126 - glUniform4iv @127 - glUniformMatrix2fv @128 - glUniformMatrix3fv @129 - glUniformMatrix4fv @130 - glUseProgram @131 - glValidateProgram @132 - glVertexAttrib1f @133 - glVertexAttrib1fv @134 - glVertexAttrib2f @135 - glVertexAttrib2fv @136 - glVertexAttrib3f @137 - glVertexAttrib3fv @138 - glVertexAttrib4f @139 - glVertexAttrib4fv @140 - glVertexAttribPointer @141 - glViewport @142 + glActiveTexture@4 @1 + glAttachShader@8 @2 + glBindAttribLocation@12 @3 + glBindBuffer@8 @4 + glBindFramebuffer@8 @5 + glBindRenderbuffer@8 @6 + glBindTexture@8 @7 + glBlendColor@16 @8 + glBlendEquation@4 @9 + glBlendEquationSeparate@8 @10 + glBlendFunc@8 @11 + glBlendFuncSeparate@16 @12 + glBufferData@16 @13 + glBufferSubData@16 @14 + glCheckFramebufferStatus@4 @15 + glClear@4 @16 + glClearColor@16 @17 + glClearDepthf@4 @18 + glClearStencil@4 @19 + glColorMask@16 @20 + glCompileShader@4 @21 + glCompressedTexImage2D@32 @22 + glCompressedTexSubImage2D@36 @23 + glCopyTexImage2D@32 @24 + glCopyTexSubImage2D@32 @25 + glCreateProgram@0 @26 + glCreateShader@4 @27 + glCullFace@4 @28 + glDeleteBuffers@8 @29 + glDeleteFramebuffers@8 @30 + glDeleteProgram@4 @32 + glDeleteRenderbuffers@8 @33 + glDeleteShader@4 @34 + glDeleteTextures@8 @31 + glDepthFunc@4 @36 + glDepthMask@4 @37 + glDepthRangef@8 @38 + glDetachShader@8 @35 + glDisable@4 @39 + glDisableVertexAttribArray@4 @40 + glDrawArrays@12 @41 + glDrawElements@16 @42 + glEnable@4 @43 + glEnableVertexAttribArray@4 @44 + glFinish@0 @45 + glFlush@0 @46 + glFramebufferRenderbuffer@16 @47 + glFramebufferTexture2D@20 @48 + glFrontFace@4 @49 + glGenBuffers@8 @50 + glGenFramebuffers@8 @52 + glGenRenderbuffers@8 @53 + glGenTextures@8 @54 + glGenerateMipmap@4 @51 + glGetActiveAttrib@28 @55 + glGetActiveUniform@28 @56 + glGetAttachedShaders@16 @57 + glGetAttribLocation@8 @58 + glGetBooleanv@8 @59 + glGetBufferParameteriv@12 @60 + glGetError@0 @61 + glGetFloatv@8 @62 + glGetFramebufferAttachmentParameteriv@16 @63 + glGetIntegerv@8 @64 + glGetProgramInfoLog@16 @66 + glGetProgramiv@12 @65 + glGetRenderbufferParameteriv@12 @67 + glGetShaderInfoLog@16 @69 + glGetShaderPrecisionFormat@16 @70 + glGetShaderSource@16 @71 + glGetShaderiv@12 @68 + glGetString@4 @72 + glGetTexParameterfv@12 @73 + glGetTexParameteriv@12 @74 + glGetUniformLocation@8 @77 + glGetUniformfv@12 @75 + glGetUniformiv@12 @76 + glGetVertexAttribPointerv@12 @80 + glGetVertexAttribfv@12 @78 + glGetVertexAttribiv@12 @79 + glHint@8 @81 + glIsBuffer@4 @82 + glIsEnabled@4 @83 + glIsFramebuffer@4 @84 + glIsProgram@4 @85 + glIsRenderbuffer@4 @86 + glIsShader@4 @87 + glIsTexture@4 @88 + glLineWidth@4 @89 + glLinkProgram@4 @90 + glPixelStorei@8 @91 + glPolygonOffset@8 @92 + glReadPixels@28 @93 + glReleaseShaderCompiler@0 @94 + glRenderbufferStorage@16 @95 + glSampleCoverage@8 @96 + glScissor@16 @97 + glShaderBinary@20 @98 + glShaderSource@16 @99 + glStencilFunc@12 @100 + glStencilFuncSeparate@16 @101 + glStencilMask@4 @102 + glStencilMaskSeparate@8 @103 + glStencilOp@12 @104 + glStencilOpSeparate@16 @105 + glTexImage2D@36 @106 + glTexParameterf@12 @107 + glTexParameterfv@12 @108 + glTexParameteri@12 @109 + glTexParameteriv@12 @110 + glTexSubImage2D@36 @111 + glUniform1f@8 @112 + glUniform1fv@12 @113 + glUniform1i@8 @114 + glUniform1iv@12 @115 + glUniform2f@12 @116 + glUniform2fv@12 @117 + glUniform2i@12 @118 + glUniform2iv@12 @119 + glUniform3f@16 @120 + glUniform3fv@12 @121 + glUniform3i@16 @122 + glUniform3iv@12 @123 + glUniform4f@20 @124 + glUniform4fv@12 @125 + glUniform4i@20 @126 + glUniform4iv@12 @127 + glUniformMatrix2fv@16 @128 + glUniformMatrix3fv@16 @129 + glUniformMatrix4fv@16 @130 + glUseProgram@4 @131 + glValidateProgram@4 @132 + glVertexAttrib1f@8 @133 + glVertexAttrib1fv@8 @134 + glVertexAttrib2f@12 @135 + glVertexAttrib2fv@8 @136 + glVertexAttrib3f@16 @137 + glVertexAttrib3fv@8 @138 + glVertexAttrib4f@20 @139 + glVertexAttrib4fv@8 @140 + glVertexAttribPointer@24 @141 + glViewport@16 @142 ; Extensions - glBlitFramebufferANGLE @149 - glRenderbufferStorageMultisampleANGLE @150 - glDeleteFencesNV @151 - glFinishFenceNV @152 - glGenFencesNV @153 - glGetFenceivNV @154 - glIsFenceNV @155 - glSetFenceNV @156 - glTestFenceNV @157 - glGetTranslatedShaderSourceANGLE @159 - glTexStorage2DEXT @160 - glGetGraphicsResetStatusEXT @161 - glReadnPixelsEXT @162 - glGetnUniformfvEXT @163 - glGetnUniformivEXT @164 - glGenQueriesEXT @165 - glDeleteQueriesEXT @166 - glIsQueryEXT @167 - glBeginQueryEXT @168 - glEndQueryEXT @169 - glGetQueryivEXT @170 - glGetQueryObjectuivEXT @171 - glVertexAttribDivisorANGLE @172 - glDrawArraysInstancedANGLE @173 - glDrawElementsInstancedANGLE @174 - glProgramBinaryOES @175 - glGetProgramBinaryOES @176 - glDrawBuffersEXT @179 - glMapBufferOES @285 - glUnmapBufferOES @286 - glGetBufferPointervOES @287 - glMapBufferRangeEXT @288 - glFlushMappedBufferRangeEXT @289 - glDiscardFramebufferEXT @293 - glInsertEventMarkerEXT @294 - glPushGroupMarkerEXT @295 - glPopGroupMarkerEXT @296 - glEGLImageTargetTexture2DOES @297 - glEGLImageTargetRenderbufferStorageOES @298 - glBindVertexArrayOES @299 - glDeleteVertexArraysOES @300 - glGenVertexArraysOES @301 - glIsVertexArrayOES @302 - glDebugMessageControlKHR @303 - glDebugMessageInsertKHR @304 - glDebugMessageCallbackKHR @305 - glGetDebugMessageLogKHR @306 - glPushDebugGroupKHR @307 - glPopDebugGroupKHR @308 - glObjectLabelKHR @309 - glGetObjectLabelKHR @310 - glObjectPtrLabelKHR @311 - glGetObjectPtrLabelKHR @312 - glGetPointervKHR @313 - glQueryCounterEXT @314 - glGetQueryObjectivEXT @315 - glGetQueryObjecti64vEXT @316 - glGetQueryObjectui64vEXT @317 - glBindUniformLocationCHROMIUM @318 - glCoverageModulationCHROMIUM @319 + glBlitFramebufferANGLE@40 @149 + glRenderbufferStorageMultisampleANGLE@20 @150 + glDeleteFencesNV@8 @151 + glFinishFenceNV@4 @152 + glGenFencesNV@8 @153 + glGetFenceivNV@12 @154 + glIsFenceNV@4 @155 + glSetFenceNV@8 @156 + glTestFenceNV@4 @157 + glGetTranslatedShaderSourceANGLE@16 @159 + glTexStorage2DEXT@20 @160 + glGetGraphicsResetStatusEXT@0 @161 + glReadnPixelsEXT@32 @162 + glGetnUniformfvEXT@16 @163 + glGetnUniformivEXT@16 @164 + glGenQueriesEXT@8 @165 + glDeleteQueriesEXT@8 @166 + glIsQueryEXT@4 @167 + glBeginQueryEXT@8 @168 + glEndQueryEXT@4 @169 + glGetQueryivEXT@12 @170 + glGetQueryObjectuivEXT@12 @171 + glVertexAttribDivisorANGLE@8 @172 + glDrawArraysInstancedANGLE@16 @173 + glDrawElementsInstancedANGLE@20 @174 + glProgramBinaryOES@16 @175 + glGetProgramBinaryOES@20 @176 + glDrawBuffersEXT@8 @179 + glMapBufferOES@8 @285 + glUnmapBufferOES@4 @286 + glGetBufferPointervOES@12 @287 + glMapBufferRangeEXT@16 @288 + glFlushMappedBufferRangeEXT@12 @289 + glDiscardFramebufferEXT@12 @293 + glInsertEventMarkerEXT@8 @294 + glPushGroupMarkerEXT@8 @295 + glPopGroupMarkerEXT@0 @296 + glEGLImageTargetTexture2DOES@8 @297 + glEGLImageTargetRenderbufferStorageOES@8 @298 + glBindVertexArrayOES@4 @299 + glDeleteVertexArraysOES@8 @300 + glGenVertexArraysOES@8 @301 + glIsVertexArrayOES@4 @302 + glDebugMessageControlKHR@24 @303 + glDebugMessageInsertKHR@24 @304 + glDebugMessageCallbackKHR@8 @305 + glGetDebugMessageLogKHR@32 @306 + glPushDebugGroupKHR@16 @307 + glPopDebugGroupKHR@0 @308 + glObjectLabelKHR@16 @309 + glGetObjectLabelKHR@20 @310 + glObjectPtrLabelKHR@12 @311 + glGetObjectPtrLabelKHR@16 @312 + glGetPointervKHR@8 @313 + glQueryCounterEXT@8 @314 + glGetQueryObjectivEXT@12 @315 + glGetQueryObjecti64vEXT@12 @316 + glGetQueryObjectui64vEXT@12 @317 + glBindUniformLocationCHROMIUM@12 @318 + glCoverageModulationCHROMIUM@4 @319 + glMatrixLoadfCHROMIUM@8 @320 + glMatrixLoadIdentityCHROMIUM@4 @321 + glGenPathsCHROMIUM@4 @322 + glDeletePathsCHROMIUM@8 @323 + glIsPathCHROMIUM@4 @324 + glPathCommandsCHROMIUM@24 @325 + glPathParameterfCHROMIUM@12 @326 + glPathParameteriCHROMIUM@12 @327 + glGetPathParameterfvCHROMIUM@12 @328 + glGetPathParameterivCHROMIUM@12 @329 + glPathStencilFuncCHROMIUM@12 @330 + glStencilFillPathCHROMIUM@12 @331 + glStencilStrokePathCHROMIUM@12 @332 + glCoverFillPathCHROMIUM@8 @333 + glCoverStrokePathCHROMIUM@8 @334 + glStencilThenCoverFillPathCHROMIUM@16 @335 + glStencilThenCoverStrokePathCHROMIUM@16 @336 + glCoverFillPathInstancedCHROMIUM@28 @337 + glCoverStrokePathInstancedCHROMIUM@28 @338 + glStencilStrokePathInstancedCHROMIUM@32 @339 + glStencilFillPathInstancedCHROMIUM@32 @340 + glStencilThenCoverFillPathInstancedCHROMIUM@36 @341 + glStencilThenCoverStrokePathInstancedCHROMIUM@36 @342 + glBindFragmentInputLocationCHROMIUM@12 @343 + glProgramPathFragmentInputGenCHROMIUM@20 @344 - glMatrixLoadfCHROMIUM @320 - glMatrixLoadIdentityCHROMIUM @321 - glGenPathsCHROMIUM @322 - glDeletePathsCHROMIUM @323 - glIsPathCHROMIUM @324 - glPathCommandsCHROMIUM @325 - glPathParameterfCHROMIUM @326 - glPathParameteriCHROMIUM @327 - glGetPathParameterfvCHROMIUM @328 - glGetPathParameterivCHROMIUM @329 - glPathStencilFuncCHROMIUM @330 - glStencilFillPathCHROMIUM @331 - glStencilStrokePathCHROMIUM @332 - glCoverFillPathCHROMIUM @333 - glCoverStrokePathCHROMIUM @334 - glStencilThenCoverFillPathCHROMIUM @335 - glStencilThenCoverStrokePathCHROMIUM @336 - glCoverFillPathInstancedCHROMIUM @337 - glCoverStrokePathInstancedCHROMIUM @338 - glStencilStrokePathInstancedCHROMIUM @339 - glStencilFillPathInstancedCHROMIUM @340 - glStencilThenCoverFillPathInstancedCHROMIUM @341 - glStencilThenCoverStrokePathInstancedCHROMIUM @342 - glBindFragmentInputLocationCHROMIUM @343 - glProgramPathFragmentInputGenCHROMIUM @344 - - glFramebufferTextureMultiviewLayeredANGLE @413 - glFramebufferTextureMultiviewSideBySideANGLE @414 - glRequestExtensionANGLE @415 + glFramebufferTextureMultiviewLayeredANGLE@24 @413 + glFramebufferTextureMultiviewSideBySideANGLE@24 @414 + glRequestExtensionANGLE@4 @415 ; GLES 3.0 Functions - glReadBuffer @180 - glDrawRangeElements @181 - glTexImage3D @182 - glTexSubImage3D @183 - glCopyTexSubImage3D @184 - glCompressedTexImage3D @185 - glCompressedTexSubImage3D @186 - glGenQueries @187 - glDeleteQueries @188 - glIsQuery @189 - glBeginQuery @190 - glEndQuery @191 - glGetQueryiv @192 - glGetQueryObjectuiv @193 - glUnmapBuffer @194 - glGetBufferPointerv @195 - glDrawBuffers @196 - glUniformMatrix2x3fv @197 - glUniformMatrix3x2fv @198 - glUniformMatrix2x4fv @199 - glUniformMatrix4x2fv @200 - glUniformMatrix3x4fv @201 - glUniformMatrix4x3fv @202 - glBlitFramebuffer @203 - glRenderbufferStorageMultisample @204 - glFramebufferTextureLayer @205 - glMapBufferRange @206 - glFlushMappedBufferRange @207 - glBindVertexArray @208 - glDeleteVertexArrays @209 - glGenVertexArrays @210 - glIsVertexArray @211 - glGetIntegeri_v @212 - glBeginTransformFeedback @213 - glEndTransformFeedback @214 - glBindBufferRange @215 - glBindBufferBase @216 - glTransformFeedbackVaryings @217 - glGetTransformFeedbackVarying @218 - glVertexAttribIPointer @219 - glGetVertexAttribIiv @220 - glGetVertexAttribIuiv @221 - glVertexAttribI4i @222 - glVertexAttribI4ui @223 - glVertexAttribI4iv @224 - glVertexAttribI4uiv @225 - glGetUniformuiv @226 - glGetFragDataLocation @227 - glUniform1ui @228 - glUniform2ui @229 - glUniform3ui @230 - glUniform4ui @231 - glUniform1uiv @232 - glUniform2uiv @233 - glUniform3uiv @234 - glUniform4uiv @235 - glClearBufferiv @236 - glClearBufferuiv @237 - glClearBufferfv @238 - glClearBufferfi @239 - glGetStringi @240 - glCopyBufferSubData @241 - glGetUniformIndices @242 - glGetActiveUniformsiv @243 - glGetUniformBlockIndex @244 - glGetActiveUniformBlockiv @245 - glGetActiveUniformBlockName @246 - glUniformBlockBinding @247 - glDrawArraysInstanced @248 - glDrawElementsInstanced @249 - glFenceSync @250 - glIsSync @251 - glDeleteSync @252 - glClientWaitSync @253 - glWaitSync @254 - glGetInteger64v @255 - glGetSynciv @256 - glGetInteger64i_v @257 - glGetBufferParameteri64v @258 - glGenSamplers @259 - glDeleteSamplers @260 - glIsSampler @261 - glBindSampler @262 - glSamplerParameteri @263 - glSamplerParameteriv @264 - glSamplerParameterf @265 - glSamplerParameterfv @266 - glGetSamplerParameteriv @267 - glGetSamplerParameterfv @268 - glVertexAttribDivisor @269 - glBindTransformFeedback @270 - glDeleteTransformFeedbacks @271 - glGenTransformFeedbacks @272 - glIsTransformFeedback @273 - glPauseTransformFeedback @274 - glResumeTransformFeedback @275 - glGetProgramBinary @276 - glProgramBinary @277 - glProgramParameteri @278 - glInvalidateFramebuffer @279 - glInvalidateSubFramebuffer @280 - glTexStorage2D @281 - glTexStorage3D @282 - glGetInternalformativ @283 + glReadBuffer@4 @180 + glDrawRangeElements@24 @181 + glTexImage3D@40 @182 + glTexSubImage3D@44 @183 + glCopyTexSubImage3D@36 @184 + glCompressedTexImage3D@36 @185 + glCompressedTexSubImage3D@44 @186 + glGenQueries@8 @187 + glDeleteQueries@8 @188 + glIsQuery@4 @189 + glBeginQuery@8 @190 + glEndQuery@4 @191 + glGetQueryiv@12 @192 + glGetQueryObjectuiv@12 @193 + glUnmapBuffer@4 @194 + glGetBufferPointerv@12 @195 + glDrawBuffers@8 @196 + glUniformMatrix2x3fv@16 @197 + glUniformMatrix3x2fv@16 @198 + glUniformMatrix2x4fv@16 @199 + glUniformMatrix4x2fv@16 @200 + glUniformMatrix3x4fv@16 @201 + glUniformMatrix4x3fv@16 @202 + glBlitFramebuffer@40 @203 + glRenderbufferStorageMultisample@20 @204 + glFramebufferTextureLayer@20 @205 + glMapBufferRange@16 @206 + glFlushMappedBufferRange@12 @207 + glBindVertexArray@4 @208 + glDeleteVertexArrays@8 @209 + glGenVertexArrays@8 @210 + glIsVertexArray@4 @211 + glGetIntegeri_v@12 @212 + glBeginTransformFeedback@4 @213 + glEndTransformFeedback@0 @214 + glBindBufferRange@20 @215 + glBindBufferBase@12 @216 + glTransformFeedbackVaryings@16 @217 + glGetTransformFeedbackVarying@28 @218 + glVertexAttribIPointer@20 @219 + glGetVertexAttribIiv@12 @220 + glGetVertexAttribIuiv@12 @221 + glVertexAttribI4i@20 @222 + glVertexAttribI4ui@20 @223 + glVertexAttribI4iv@8 @224 + glVertexAttribI4uiv@8 @225 + glGetUniformuiv@12 @226 + glGetFragDataLocation@8 @227 + glUniform1ui@8 @228 + glUniform2ui@12 @229 + glUniform3ui@16 @230 + glUniform4ui@20 @231 + glUniform1uiv@12 @232 + glUniform2uiv@12 @233 + glUniform3uiv@12 @234 + glUniform4uiv@12 @235 + glClearBufferiv@12 @236 + glClearBufferuiv@12 @237 + glClearBufferfv@12 @238 + glClearBufferfi@16 @239 + glGetStringi@8 @240 + glCopyBufferSubData@20 @241 + glGetUniformIndices@16 @242 + glGetActiveUniformsiv@20 @243 + glGetUniformBlockIndex@8 @244 + glGetActiveUniformBlockiv@16 @245 + glGetActiveUniformBlockName@20 @246 + glUniformBlockBinding@12 @247 + glDrawArraysInstanced@16 @248 + glDrawElementsInstanced@20 @249 + glFenceSync@8 @250 + glIsSync@4 @251 + glDeleteSync@4 @252 + glClientWaitSync@16 @253 + glWaitSync@16 @254 + glGetInteger64v@8 @255 + glGetSynciv@20 @256 + glGetInteger64i_v@12 @257 + glGetBufferParameteri64v@12 @258 + glGenSamplers@8 @259 + glDeleteSamplers@8 @260 + glIsSampler@4 @261 + glBindSampler@8 @262 + glSamplerParameteri@12 @263 + glSamplerParameteriv@12 @264 + glSamplerParameterf@12 @265 + glSamplerParameterfv@12 @266 + glGetSamplerParameteriv@12 @267 + glGetSamplerParameterfv@12 @268 + glVertexAttribDivisor@8 @269 + glBindTransformFeedback@8 @270 + glDeleteTransformFeedbacks@8 @271 + glGenTransformFeedbacks@8 @272 + glIsTransformFeedback@4 @273 + glPauseTransformFeedback@0 @274 + glResumeTransformFeedback@0 @275 + glGetProgramBinary@20 @276 + glProgramBinary@16 @277 + glProgramParameteri@12 @278 + glInvalidateFramebuffer@12 @279 + glInvalidateSubFramebuffer@28 @280 + glTexStorage2D@20 @281 + glTexStorage3D@24 @282 + glGetInternalformativ@20 @283 ; GLES 3.1 Functions - glDispatchCompute @345 - glDispatchComputeIndirect @346 - glDrawArraysIndirect @347 - glDrawElementsIndirect @348 - glFramebufferParameteri @349 - glGetFramebufferParameteriv @350 - glGetProgramInterfaceiv @351 - glGetProgramResourceIndex @352 - glGetProgramResourceName @353 - glGetProgramResourceiv @354 - glGetProgramResourceLocation @355 - glUseProgramStages @356 - glActiveShaderProgram @357 - glCreateShaderProgramv @358 - glBindProgramPipeline @359 - glDeleteProgramPipelines @360 - glGenProgramPipelines @361 - glIsProgramPipeline @362 - glGetProgramPipelineiv @363 - glProgramUniform1i @364 - glProgramUniform2i @365 - glProgramUniform3i @366 - glProgramUniform4i @367 - glProgramUniform1ui @368 - glProgramUniform2ui @369 - glProgramUniform3ui @370 - glProgramUniform4ui @371 - glProgramUniform1f @372 - glProgramUniform2f @373 - glProgramUniform3f @374 - glProgramUniform4f @375 - glProgramUniform1iv @376 - glProgramUniform2iv @377 - glProgramUniform3iv @378 - glProgramUniform4iv @379 - glProgramUniform1uiv @380 - glProgramUniform2uiv @381 - glProgramUniform3uiv @382 - glProgramUniform4uiv @383 - glProgramUniform1fv @384 - glProgramUniform2fv @385 - glProgramUniform3fv @386 - glProgramUniform4fv @387 - glProgramUniformMatrix2fv @388 - glProgramUniformMatrix3fv @389 - glProgramUniformMatrix4fv @390 - glProgramUniformMatrix2x3fv @391 - glProgramUniformMatrix3x2fv @392 - glProgramUniformMatrix2x4fv @393 - glProgramUniformMatrix4x2fv @394 - glProgramUniformMatrix3x4fv @395 - glProgramUniformMatrix4x3fv @396 - glValidateProgramPipeline @397 - glGetProgramPipelineInfoLog @398 - glBindImageTexture @399 - glGetBooleani_v @400 - glMemoryBarrier @401 - glMemoryBarrierByRegion @402 - glTexStorage2DMultisample @403 - glGetMultisamplefv @404 - glSampleMaski @405 - glGetTexLevelParameteriv @406 - glGetTexLevelParameterfv @407 - glBindVertexBuffer @408 - glVertexAttribFormat @409 - glVertexAttribIFormat @410 - glVertexAttribBinding @411 - glVertexBindingDivisor @412 + glDispatchCompute@12 @345 + glDispatchComputeIndirect@4 @346 + glDrawArraysIndirect@8 @347 + glDrawElementsIndirect@12 @348 + glFramebufferParameteri@12 @349 + glGetFramebufferParameteriv@12 @350 + glGetProgramInterfaceiv@16 @351 + glGetProgramResourceIndex@12 @352 + glGetProgramResourceName@24 @353 + glGetProgramResourceiv@32 @354 + glGetProgramResourceLocation@12 @355 + glUseProgramStages@12 @356 + glActiveShaderProgram@8 @357 + glCreateShaderProgramv@12 @358 + glBindProgramPipeline@4 @359 + glDeleteProgramPipelines@8 @360 + glGenProgramPipelines@8 @361 + glIsProgramPipeline@4 @362 + glGetProgramPipelineiv@12 @363 + glProgramUniform1i@12 @364 + glProgramUniform2i@16 @365 + glProgramUniform3i@20 @366 + glProgramUniform4i@24 @367 + glProgramUniform1ui@12 @368 + glProgramUniform2ui@16 @369 + glProgramUniform3ui@20 @370 + glProgramUniform4ui@24 @371 + glProgramUniform1f@12 @372 + glProgramUniform2f@16 @373 + glProgramUniform3f@20 @374 + glProgramUniform4f@24 @375 + glProgramUniform1iv@16 @376 + glProgramUniform2iv@16 @377 + glProgramUniform3iv@16 @378 + glProgramUniform4iv@16 @379 + glProgramUniform1uiv@16 @380 + glProgramUniform2uiv@16 @381 + glProgramUniform3uiv@16 @382 + glProgramUniform4uiv@16 @383 + glProgramUniform1fv@16 @384 + glProgramUniform2fv@16 @385 + glProgramUniform3fv@16 @386 + glProgramUniform4fv@16 @387 + glProgramUniformMatrix2fv@20 @388 + glProgramUniformMatrix3fv@20 @389 + glProgramUniformMatrix4fv@20 @390 + glProgramUniformMatrix2x3fv@20 @391 + glProgramUniformMatrix3x2fv@20 @392 + glProgramUniformMatrix2x4fv@20 @393 + glProgramUniformMatrix4x2fv@20 @394 + glProgramUniformMatrix3x4fv@20 @395 + glProgramUniformMatrix4x3fv@20 @396 + glValidateProgramPipeline@4 @397 + glGetProgramPipelineInfoLog@16 @398 + glBindImageTexture@28 @399 + glGetBooleani_v@12 @400 + glMemoryBarrier@4 @401 + glMemoryBarrierByRegion@4 @402 + glTexStorage2DMultisample@24 @403 + glGetMultisamplefv@12 @404 + glSampleMaski@8 @405 + glGetTexLevelParameteriv@16 @406 + glGetTexLevelParameterfv@16 @407 + glBindVertexBuffer@16 @408 + glVertexAttribFormat@20 @409 + glVertexAttribIFormat@16 @410 + glVertexAttribBinding@8 @411 + glVertexBindingDivisor@8 @412 diff --git a/src/3rdparty/angle/src/libGLESv2/libGLESv2d_mingw32.def b/src/3rdparty/angle/src/libGLESv2/libGLESv2d_mingw32.def index 2ff4cc0579..a182c21a05 100644 --- a/src/3rdparty/angle/src/libGLESv2/libGLESv2d_mingw32.def +++ b/src/3rdparty/angle/src/libGLESv2/libGLESv2d_mingw32.def @@ -1,412 +1,411 @@ LIBRARY libGLESv2 EXPORTS - glActiveTexture @1 - glAttachShader @2 - glBindAttribLocation @3 - glBindBuffer @4 - glBindFramebuffer @5 - glBindRenderbuffer @6 - glBindTexture @7 - glBlendColor @8 - glBlendEquation @9 - glBlendEquationSeparate @10 - glBlendFunc @11 - glBlendFuncSeparate @12 - glBufferData @13 - glBufferSubData @14 - glCheckFramebufferStatus @15 - glClear @16 - glClearColor @17 - glClearDepthf @18 - glClearStencil @19 - glColorMask @20 - glCompileShader @21 - glCompressedTexImage2D @22 - glCompressedTexSubImage2D @23 - glCopyTexImage2D @24 - glCopyTexSubImage2D @25 - glCreateProgram @26 - glCreateShader @27 - glCullFace @28 - glDeleteBuffers @29 - glDeleteFramebuffers @30 - glDeleteProgram @32 - glDeleteRenderbuffers @33 - glDeleteShader @34 - glDeleteTextures @31 - glDepthFunc @36 - glDepthMask @37 - glDepthRangef @38 - glDetachShader @35 - glDisable @39 - glDisableVertexAttribArray @40 - glDrawArrays @41 - glDrawElements @42 - glEnable @43 - glEnableVertexAttribArray @44 - glFinish @45 - glFlush @46 - glFramebufferRenderbuffer @47 - glFramebufferTexture2D @48 - glFrontFace @49 - glGenBuffers @50 - glGenFramebuffers @52 - glGenRenderbuffers @53 - glGenTextures @54 - glGenerateMipmap @51 - glGetActiveAttrib @55 - glGetActiveUniform @56 - glGetAttachedShaders @57 - glGetAttribLocation @58 - glGetBooleanv @59 - glGetBufferParameteriv @60 - glGetError @61 - glGetFloatv @62 - glGetFramebufferAttachmentParameteriv @63 - glGetIntegerv @64 - glGetProgramInfoLog @66 - glGetProgramiv @65 - glGetRenderbufferParameteriv @67 - glGetShaderInfoLog @69 - glGetShaderPrecisionFormat @70 - glGetShaderSource @71 - glGetShaderiv @68 - glGetString @72 - glGetTexParameterfv @73 - glGetTexParameteriv @74 - glGetUniformLocation @77 - glGetUniformfv @75 - glGetUniformiv @76 - glGetVertexAttribPointerv @80 - glGetVertexAttribfv @78 - glGetVertexAttribiv @79 - glHint @81 - glIsBuffer @82 - glIsEnabled @83 - glIsFramebuffer @84 - glIsProgram @85 - glIsRenderbuffer @86 - glIsShader @87 - glIsTexture @88 - glLineWidth @89 - glLinkProgram @90 - glPixelStorei @91 - glPolygonOffset @92 - glReadPixels @93 - glReleaseShaderCompiler @94 - glRenderbufferStorage @95 - glSampleCoverage @96 - glScissor @97 - glShaderBinary @98 - glShaderSource @99 - glStencilFunc @100 - glStencilFuncSeparate @101 - glStencilMask @102 - glStencilMaskSeparate @103 - glStencilOp @104 - glStencilOpSeparate @105 - glTexImage2D @106 - glTexParameterf @107 - glTexParameterfv @108 - glTexParameteri @109 - glTexParameteriv @110 - glTexSubImage2D @111 - glUniform1f @112 - glUniform1fv @113 - glUniform1i @114 - glUniform1iv @115 - glUniform2f @116 - glUniform2fv @117 - glUniform2i @118 - glUniform2iv @119 - glUniform3f @120 - glUniform3fv @121 - glUniform3i @122 - glUniform3iv @123 - glUniform4f @124 - glUniform4fv @125 - glUniform4i @126 - glUniform4iv @127 - glUniformMatrix2fv @128 - glUniformMatrix3fv @129 - glUniformMatrix4fv @130 - glUseProgram @131 - glValidateProgram @132 - glVertexAttrib1f @133 - glVertexAttrib1fv @134 - glVertexAttrib2f @135 - glVertexAttrib2fv @136 - glVertexAttrib3f @137 - glVertexAttrib3fv @138 - glVertexAttrib4f @139 - glVertexAttrib4fv @140 - glVertexAttribPointer @141 - glViewport @142 + glActiveTexture@4 @1 + glAttachShader@8 @2 + glBindAttribLocation@12 @3 + glBindBuffer@8 @4 + glBindFramebuffer@8 @5 + glBindRenderbuffer@8 @6 + glBindTexture@8 @7 + glBlendColor@16 @8 + glBlendEquation@4 @9 + glBlendEquationSeparate@8 @10 + glBlendFunc@8 @11 + glBlendFuncSeparate@16 @12 + glBufferData@16 @13 + glBufferSubData@16 @14 + glCheckFramebufferStatus@4 @15 + glClear@4 @16 + glClearColor@16 @17 + glClearDepthf@4 @18 + glClearStencil@4 @19 + glColorMask@16 @20 + glCompileShader@4 @21 + glCompressedTexImage2D@32 @22 + glCompressedTexSubImage2D@36 @23 + glCopyTexImage2D@32 @24 + glCopyTexSubImage2D@32 @25 + glCreateProgram@0 @26 + glCreateShader@4 @27 + glCullFace@4 @28 + glDeleteBuffers@8 @29 + glDeleteFramebuffers@8 @30 + glDeleteProgram@4 @32 + glDeleteRenderbuffers@8 @33 + glDeleteShader@4 @34 + glDeleteTextures@8 @31 + glDepthFunc@4 @36 + glDepthMask@4 @37 + glDepthRangef@8 @38 + glDetachShader@8 @35 + glDisable@4 @39 + glDisableVertexAttribArray@4 @40 + glDrawArrays@12 @41 + glDrawElements@16 @42 + glEnable@4 @43 + glEnableVertexAttribArray@4 @44 + glFinish@0 @45 + glFlush@0 @46 + glFramebufferRenderbuffer@16 @47 + glFramebufferTexture2D@20 @48 + glFrontFace@4 @49 + glGenBuffers@8 @50 + glGenFramebuffers@8 @52 + glGenRenderbuffers@8 @53 + glGenTextures@8 @54 + glGenerateMipmap@4 @51 + glGetActiveAttrib@28 @55 + glGetActiveUniform@28 @56 + glGetAttachedShaders@16 @57 + glGetAttribLocation@8 @58 + glGetBooleanv@8 @59 + glGetBufferParameteriv@12 @60 + glGetError@0 @61 + glGetFloatv@8 @62 + glGetFramebufferAttachmentParameteriv@16 @63 + glGetIntegerv@8 @64 + glGetProgramInfoLog@16 @66 + glGetProgramiv@12 @65 + glGetRenderbufferParameteriv@12 @67 + glGetShaderInfoLog@16 @69 + glGetShaderPrecisionFormat@16 @70 + glGetShaderSource@16 @71 + glGetShaderiv@12 @68 + glGetString@4 @72 + glGetTexParameterfv@12 @73 + glGetTexParameteriv@12 @74 + glGetUniformLocation@8 @77 + glGetUniformfv@12 @75 + glGetUniformiv@12 @76 + glGetVertexAttribPointerv@12 @80 + glGetVertexAttribfv@12 @78 + glGetVertexAttribiv@12 @79 + glHint@8 @81 + glIsBuffer@4 @82 + glIsEnabled@4 @83 + glIsFramebuffer@4 @84 + glIsProgram@4 @85 + glIsRenderbuffer@4 @86 + glIsShader@4 @87 + glIsTexture@4 @88 + glLineWidth@4 @89 + glLinkProgram@4 @90 + glPixelStorei@8 @91 + glPolygonOffset@8 @92 + glReadPixels@28 @93 + glReleaseShaderCompiler@0 @94 + glRenderbufferStorage@16 @95 + glSampleCoverage@8 @96 + glScissor@16 @97 + glShaderBinary@20 @98 + glShaderSource@16 @99 + glStencilFunc@12 @100 + glStencilFuncSeparate@16 @101 + glStencilMask@4 @102 + glStencilMaskSeparate@8 @103 + glStencilOp@12 @104 + glStencilOpSeparate@16 @105 + glTexImage2D@36 @106 + glTexParameterf@12 @107 + glTexParameterfv@12 @108 + glTexParameteri@12 @109 + glTexParameteriv@12 @110 + glTexSubImage2D@36 @111 + glUniform1f@8 @112 + glUniform1fv@12 @113 + glUniform1i@8 @114 + glUniform1iv@12 @115 + glUniform2f@12 @116 + glUniform2fv@12 @117 + glUniform2i@12 @118 + glUniform2iv@12 @119 + glUniform3f@16 @120 + glUniform3fv@12 @121 + glUniform3i@16 @122 + glUniform3iv@12 @123 + glUniform4f@20 @124 + glUniform4fv@12 @125 + glUniform4i@20 @126 + glUniform4iv@12 @127 + glUniformMatrix2fv@16 @128 + glUniformMatrix3fv@16 @129 + glUniformMatrix4fv@16 @130 + glUseProgram@4 @131 + glValidateProgram@4 @132 + glVertexAttrib1f@8 @133 + glVertexAttrib1fv@8 @134 + glVertexAttrib2f@12 @135 + glVertexAttrib2fv@8 @136 + glVertexAttrib3f@16 @137 + glVertexAttrib3fv@8 @138 + glVertexAttrib4f@20 @139 + glVertexAttrib4fv@8 @140 + glVertexAttribPointer@24 @141 + glViewport@16 @142 ; Extensions - glBlitFramebufferANGLE @149 - glRenderbufferStorageMultisampleANGLE @150 - glDeleteFencesNV @151 - glFinishFenceNV @152 - glGenFencesNV @153 - glGetFenceivNV @154 - glIsFenceNV @155 - glSetFenceNV @156 - glTestFenceNV @157 - glGetTranslatedShaderSourceANGLE @159 - glTexStorage2DEXT @160 - glGetGraphicsResetStatusEXT @161 - glReadnPixelsEXT @162 - glGetnUniformfvEXT @163 - glGetnUniformivEXT @164 - glGenQueriesEXT @165 - glDeleteQueriesEXT @166 - glIsQueryEXT @167 - glBeginQueryEXT @168 - glEndQueryEXT @169 - glGetQueryivEXT @170 - glGetQueryObjectuivEXT @171 - glVertexAttribDivisorANGLE @172 - glDrawArraysInstancedANGLE @173 - glDrawElementsInstancedANGLE @174 - glProgramBinaryOES @175 - glGetProgramBinaryOES @176 - glDrawBuffersEXT @179 - glMapBufferOES @285 - glUnmapBufferOES @286 - glGetBufferPointervOES @287 - glMapBufferRangeEXT @288 - glFlushMappedBufferRangeEXT @289 - glDiscardFramebufferEXT @293 - glInsertEventMarkerEXT @294 - glPushGroupMarkerEXT @295 - glPopGroupMarkerEXT @296 - glEGLImageTargetTexture2DOES @297 - glEGLImageTargetRenderbufferStorageOES @298 - glBindVertexArrayOES @299 - glDeleteVertexArraysOES @300 - glGenVertexArraysOES @301 - glIsVertexArrayOES @302 - glDebugMessageControlKHR @303 - glDebugMessageInsertKHR @304 - glDebugMessageCallbackKHR @305 - glGetDebugMessageLogKHR @306 - glPushDebugGroupKHR @307 - glPopDebugGroupKHR @308 - glObjectLabelKHR @309 - glGetObjectLabelKHR @310 - glObjectPtrLabelKHR @311 - glGetObjectPtrLabelKHR @312 - glGetPointervKHR @313 - glQueryCounterEXT @314 - glGetQueryObjectivEXT @315 - glGetQueryObjecti64vEXT @316 - glGetQueryObjectui64vEXT @317 - glBindUniformLocationCHROMIUM @318 - glCoverageModulationCHROMIUM @319 + glBlitFramebufferANGLE@40 @149 + glRenderbufferStorageMultisampleANGLE@20 @150 + glDeleteFencesNV@8 @151 + glFinishFenceNV@4 @152 + glGenFencesNV@8 @153 + glGetFenceivNV@12 @154 + glIsFenceNV@4 @155 + glSetFenceNV@8 @156 + glTestFenceNV@4 @157 + glGetTranslatedShaderSourceANGLE@16 @159 + glTexStorage2DEXT@20 @160 + glGetGraphicsResetStatusEXT@0 @161 + glReadnPixelsEXT@32 @162 + glGetnUniformfvEXT@16 @163 + glGetnUniformivEXT@16 @164 + glGenQueriesEXT@8 @165 + glDeleteQueriesEXT@8 @166 + glIsQueryEXT@4 @167 + glBeginQueryEXT@8 @168 + glEndQueryEXT@4 @169 + glGetQueryivEXT@12 @170 + glGetQueryObjectuivEXT@12 @171 + glVertexAttribDivisorANGLE@8 @172 + glDrawArraysInstancedANGLE@16 @173 + glDrawElementsInstancedANGLE@20 @174 + glProgramBinaryOES@16 @175 + glGetProgramBinaryOES@20 @176 + glDrawBuffersEXT@8 @179 + glMapBufferOES@8 @285 + glUnmapBufferOES@4 @286 + glGetBufferPointervOES@12 @287 + glMapBufferRangeEXT@16 @288 + glFlushMappedBufferRangeEXT@12 @289 + glDiscardFramebufferEXT@12 @293 + glInsertEventMarkerEXT@8 @294 + glPushGroupMarkerEXT@8 @295 + glPopGroupMarkerEXT@0 @296 + glEGLImageTargetTexture2DOES@8 @297 + glEGLImageTargetRenderbufferStorageOES@8 @298 + glBindVertexArrayOES@4 @299 + glDeleteVertexArraysOES@8 @300 + glGenVertexArraysOES@8 @301 + glIsVertexArrayOES@4 @302 + glDebugMessageControlKHR@24 @303 + glDebugMessageInsertKHR@24 @304 + glDebugMessageCallbackKHR@8 @305 + glGetDebugMessageLogKHR@32 @306 + glPushDebugGroupKHR@16 @307 + glPopDebugGroupKHR@0 @308 + glObjectLabelKHR@16 @309 + glGetObjectLabelKHR@20 @310 + glObjectPtrLabelKHR@12 @311 + glGetObjectPtrLabelKHR@16 @312 + glGetPointervKHR@8 @313 + glQueryCounterEXT@8 @314 + glGetQueryObjectivEXT@12 @315 + glGetQueryObjecti64vEXT@12 @316 + glGetQueryObjectui64vEXT@12 @317 + glBindUniformLocationCHROMIUM@12 @318 + glCoverageModulationCHROMIUM@4 @319 + glMatrixLoadfCHROMIUM@8 @320 + glMatrixLoadIdentityCHROMIUM@4 @321 + glGenPathsCHROMIUM@4 @322 + glDeletePathsCHROMIUM@8 @323 + glIsPathCHROMIUM@4 @324 + glPathCommandsCHROMIUM@24 @325 + glPathParameterfCHROMIUM@12 @326 + glPathParameteriCHROMIUM@12 @327 + glGetPathParameterfvCHROMIUM@12 @328 + glGetPathParameterivCHROMIUM@12 @329 + glPathStencilFuncCHROMIUM@12 @330 + glStencilFillPathCHROMIUM@12 @331 + glStencilStrokePathCHROMIUM@12 @332 + glCoverFillPathCHROMIUM@8 @333 + glCoverStrokePathCHROMIUM@8 @334 + glStencilThenCoverFillPathCHROMIUM@16 @335 + glStencilThenCoverStrokePathCHROMIUM@16 @336 + glCoverFillPathInstancedCHROMIUM@28 @337 + glCoverStrokePathInstancedCHROMIUM@28 @338 + glStencilStrokePathInstancedCHROMIUM@32 @339 + glStencilFillPathInstancedCHROMIUM@32 @340 + glStencilThenCoverFillPathInstancedCHROMIUM@36 @341 + glStencilThenCoverStrokePathInstancedCHROMIUM@36 @342 + glBindFragmentInputLocationCHROMIUM@12 @343 + glProgramPathFragmentInputGenCHROMIUM@20 @344 - glMatrixLoadfCHROMIUM @320 - glMatrixLoadIdentityCHROMIUM @321 - glGenPathsCHROMIUM @322 - glDeletePathsCHROMIUM @323 - glIsPathCHROMIUM @324 - glPathCommandsCHROMIUM @325 - glPathParameterfCHROMIUM @326 - glPathParameteriCHROMIUM @327 - glGetPathParameterfvCHROMIUM @328 - glGetPathParameterivCHROMIUM @329 - glPathStencilFuncCHROMIUM @330 - glStencilFillPathCHROMIUM @331 - glStencilStrokePathCHROMIUM @332 - glCoverFillPathCHROMIUM @333 - glCoverStrokePathCHROMIUM @334 - glStencilThenCoverFillPathCHROMIUM @335 - glStencilThenCoverStrokePathCHROMIUM @336 - glCoverFillPathInstancedCHROMIUM @337 - glCoverStrokePathInstancedCHROMIUM @338 - glStencilStrokePathInstancedCHROMIUM @339 - glStencilFillPathInstancedCHROMIUM @340 - glStencilThenCoverFillPathInstancedCHROMIUM @341 - glStencilThenCoverStrokePathInstancedCHROMIUM @342 - glBindFragmentInputLocationCHROMIUM @343 - glProgramPathFragmentInputGenCHROMIUM @344 - - glFramebufferTextureMultiviewLayeredANGLE @413 - glFramebufferTextureMultiviewSideBySideANGLE @414 - glRequestExtensionANGLE @415 + glFramebufferTextureMultiviewLayeredANGLE@24 @413 + glFramebufferTextureMultiviewSideBySideANGLE@24 @414 + glRequestExtensionANGLE@4 @415 ; GLES 3.0 Functions - glReadBuffer @180 - glDrawRangeElements @181 - glTexImage3D @182 - glTexSubImage3D @183 - glCopyTexSubImage3D @184 - glCompressedTexImage3D @185 - glCompressedTexSubImage3D @186 - glGenQueries @187 - glDeleteQueries @188 - glIsQuery @189 - glBeginQuery @190 - glEndQuery @191 - glGetQueryiv @192 - glGetQueryObjectuiv @193 - glUnmapBuffer @194 - glGetBufferPointerv @195 - glDrawBuffers @196 - glUniformMatrix2x3fv @197 - glUniformMatrix3x2fv @198 - glUniformMatrix2x4fv @199 - glUniformMatrix4x2fv @200 - glUniformMatrix3x4fv @201 - glUniformMatrix4x3fv @202 - glBlitFramebuffer @203 - glRenderbufferStorageMultisample @204 - glFramebufferTextureLayer @205 - glMapBufferRange @206 - glFlushMappedBufferRange @207 - glBindVertexArray @208 - glDeleteVertexArrays @209 - glGenVertexArrays @210 - glIsVertexArray @211 - glGetIntegeri_v @212 - glBeginTransformFeedback @213 - glEndTransformFeedback @214 - glBindBufferRange @215 - glBindBufferBase @216 - glTransformFeedbackVaryings @217 - glGetTransformFeedbackVarying @218 - glVertexAttribIPointer @219 - glGetVertexAttribIiv @220 - glGetVertexAttribIuiv @221 - glVertexAttribI4i @222 - glVertexAttribI4ui @223 - glVertexAttribI4iv @224 - glVertexAttribI4uiv @225 - glGetUniformuiv @226 - glGetFragDataLocation @227 - glUniform1ui @228 - glUniform2ui @229 - glUniform3ui @230 - glUniform4ui @231 - glUniform1uiv @232 - glUniform2uiv @233 - glUniform3uiv @234 - glUniform4uiv @235 - glClearBufferiv @236 - glClearBufferuiv @237 - glClearBufferfv @238 - glClearBufferfi @239 - glGetStringi @240 - glCopyBufferSubData @241 - glGetUniformIndices @242 - glGetActiveUniformsiv @243 - glGetUniformBlockIndex @244 - glGetActiveUniformBlockiv @245 - glGetActiveUniformBlockName @246 - glUniformBlockBinding @247 - glDrawArraysInstanced @248 - glDrawElementsInstanced @249 - glFenceSync @250 - glIsSync @251 - glDeleteSync @252 - glClientWaitSync @253 - glWaitSync @254 - glGetInteger64v @255 - glGetSynciv @256 - glGetInteger64i_v @257 - glGetBufferParameteri64v @258 - glGenSamplers @259 - glDeleteSamplers @260 - glIsSampler @261 - glBindSampler @262 - glSamplerParameteri @263 - glSamplerParameteriv @264 - glSamplerParameterf @265 - glSamplerParameterfv @266 - glGetSamplerParameteriv @267 - glGetSamplerParameterfv @268 - glVertexAttribDivisor @269 - glBindTransformFeedback @270 - glDeleteTransformFeedbacks @271 - glGenTransformFeedbacks @272 - glIsTransformFeedback @273 - glPauseTransformFeedback @274 - glResumeTransformFeedback @275 - glGetProgramBinary @276 - glProgramBinary @277 - glProgramParameteri @278 - glInvalidateFramebuffer @279 - glInvalidateSubFramebuffer @280 - glTexStorage2D @281 - glTexStorage3D @282 - glGetInternalformativ @283 + glReadBuffer@4 @180 + glDrawRangeElements@24 @181 + glTexImage3D@40 @182 + glTexSubImage3D@44 @183 + glCopyTexSubImage3D@36 @184 + glCompressedTexImage3D@36 @185 + glCompressedTexSubImage3D@44 @186 + glGenQueries@8 @187 + glDeleteQueries@8 @188 + glIsQuery@4 @189 + glBeginQuery@8 @190 + glEndQuery@4 @191 + glGetQueryiv@12 @192 + glGetQueryObjectuiv@12 @193 + glUnmapBuffer@4 @194 + glGetBufferPointerv@12 @195 + glDrawBuffers@8 @196 + glUniformMatrix2x3fv@16 @197 + glUniformMatrix3x2fv@16 @198 + glUniformMatrix2x4fv@16 @199 + glUniformMatrix4x2fv@16 @200 + glUniformMatrix3x4fv@16 @201 + glUniformMatrix4x3fv@16 @202 + glBlitFramebuffer@40 @203 + glRenderbufferStorageMultisample@20 @204 + glFramebufferTextureLayer@20 @205 + glMapBufferRange@16 @206 + glFlushMappedBufferRange@12 @207 + glBindVertexArray@4 @208 + glDeleteVertexArrays@8 @209 + glGenVertexArrays@8 @210 + glIsVertexArray@4 @211 + glGetIntegeri_v@12 @212 + glBeginTransformFeedback@4 @213 + glEndTransformFeedback@0 @214 + glBindBufferRange@20 @215 + glBindBufferBase@12 @216 + glTransformFeedbackVaryings@16 @217 + glGetTransformFeedbackVarying@28 @218 + glVertexAttribIPointer@20 @219 + glGetVertexAttribIiv@12 @220 + glGetVertexAttribIuiv@12 @221 + glVertexAttribI4i@20 @222 + glVertexAttribI4ui@20 @223 + glVertexAttribI4iv@8 @224 + glVertexAttribI4uiv@8 @225 + glGetUniformuiv@12 @226 + glGetFragDataLocation@8 @227 + glUniform1ui@8 @228 + glUniform2ui@12 @229 + glUniform3ui@16 @230 + glUniform4ui@20 @231 + glUniform1uiv@12 @232 + glUniform2uiv@12 @233 + glUniform3uiv@12 @234 + glUniform4uiv@12 @235 + glClearBufferiv@12 @236 + glClearBufferuiv@12 @237 + glClearBufferfv@12 @238 + glClearBufferfi@16 @239 + glGetStringi@8 @240 + glCopyBufferSubData@20 @241 + glGetUniformIndices@16 @242 + glGetActiveUniformsiv@20 @243 + glGetUniformBlockIndex@8 @244 + glGetActiveUniformBlockiv@16 @245 + glGetActiveUniformBlockName@20 @246 + glUniformBlockBinding@12 @247 + glDrawArraysInstanced@16 @248 + glDrawElementsInstanced@20 @249 + glFenceSync@8 @250 + glIsSync@4 @251 + glDeleteSync@4 @252 + glClientWaitSync@16 @253 + glWaitSync@16 @254 + glGetInteger64v@8 @255 + glGetSynciv@20 @256 + glGetInteger64i_v@12 @257 + glGetBufferParameteri64v@12 @258 + glGenSamplers@8 @259 + glDeleteSamplers@8 @260 + glIsSampler@4 @261 + glBindSampler@8 @262 + glSamplerParameteri@12 @263 + glSamplerParameteriv@12 @264 + glSamplerParameterf@12 @265 + glSamplerParameterfv@12 @266 + glGetSamplerParameteriv@12 @267 + glGetSamplerParameterfv@12 @268 + glVertexAttribDivisor@8 @269 + glBindTransformFeedback@8 @270 + glDeleteTransformFeedbacks@8 @271 + glGenTransformFeedbacks@8 @272 + glIsTransformFeedback@4 @273 + glPauseTransformFeedback@0 @274 + glResumeTransformFeedback@0 @275 + glGetProgramBinary@20 @276 + glProgramBinary@16 @277 + glProgramParameteri@12 @278 + glInvalidateFramebuffer@12 @279 + glInvalidateSubFramebuffer@28 @280 + glTexStorage2D@20 @281 + glTexStorage3D@24 @282 + glGetInternalformativ@20 @283 ; GLES 3.1 Functions - glDispatchCompute @345 - glDispatchComputeIndirect @346 - glDrawArraysIndirect @347 - glDrawElementsIndirect @348 - glFramebufferParameteri @349 - glGetFramebufferParameteriv @350 - glGetProgramInterfaceiv @351 - glGetProgramResourceIndex @352 - glGetProgramResourceName @353 - glGetProgramResourceiv @354 - glGetProgramResourceLocation @355 - glUseProgramStages @356 - glActiveShaderProgram @357 - glCreateShaderProgramv @358 - glBindProgramPipeline @359 - glDeleteProgramPipelines @360 - glGenProgramPipelines @361 - glIsProgramPipeline @362 - glGetProgramPipelineiv @363 - glProgramUniform1i @364 - glProgramUniform2i @365 - glProgramUniform3i @366 - glProgramUniform4i @367 - glProgramUniform1ui @368 - glProgramUniform2ui @369 - glProgramUniform3ui @370 - glProgramUniform4ui @371 - glProgramUniform1f @372 - glProgramUniform2f @373 - glProgramUniform3f @374 - glProgramUniform4f @375 - glProgramUniform1iv @376 - glProgramUniform2iv @377 - glProgramUniform3iv @378 - glProgramUniform4iv @379 - glProgramUniform1uiv @380 - glProgramUniform2uiv @381 - glProgramUniform3uiv @382 - glProgramUniform4uiv @383 - glProgramUniform1fv @384 - glProgramUniform2fv @385 - glProgramUniform3fv @386 - glProgramUniform4fv @387 - glProgramUniformMatrix2fv @388 - glProgramUniformMatrix3fv @389 - glProgramUniformMatrix4fv @390 - glProgramUniformMatrix2x3fv @391 - glProgramUniformMatrix3x2fv @392 - glProgramUniformMatrix2x4fv @393 - glProgramUniformMatrix4x2fv @394 - glProgramUniformMatrix3x4fv @395 - glProgramUniformMatrix4x3fv @396 - glValidateProgramPipeline @397 - glGetProgramPipelineInfoLog @398 - glBindImageTexture @399 - glGetBooleani_v @400 - glMemoryBarrier @401 - glMemoryBarrierByRegion @402 - glTexStorage2DMultisample @403 - glGetMultisamplefv @404 - glSampleMaski @405 - glGetTexLevelParameteriv @406 - glGetTexLevelParameterfv @407 - glBindVertexBuffer @408 - glVertexAttribFormat @409 - glVertexAttribIFormat @410 - glVertexAttribBinding @411 - glVertexBindingDivisor @412 + glDispatchCompute@12 @345 + glDispatchComputeIndirect@4 @346 + glDrawArraysIndirect@8 @347 + glDrawElementsIndirect@12 @348 + glFramebufferParameteri@12 @349 + glGetFramebufferParameteriv@12 @350 + glGetProgramInterfaceiv@16 @351 + glGetProgramResourceIndex@12 @352 + glGetProgramResourceName@24 @353 + glGetProgramResourceiv@32 @354 + glGetProgramResourceLocation@12 @355 + glUseProgramStages@12 @356 + glActiveShaderProgram@8 @357 + glCreateShaderProgramv@12 @358 + glBindProgramPipeline@4 @359 + glDeleteProgramPipelines@8 @360 + glGenProgramPipelines@8 @361 + glIsProgramPipeline@4 @362 + glGetProgramPipelineiv@12 @363 + glProgramUniform1i@12 @364 + glProgramUniform2i@16 @365 + glProgramUniform3i@20 @366 + glProgramUniform4i@24 @367 + glProgramUniform1ui@12 @368 + glProgramUniform2ui@16 @369 + glProgramUniform3ui@20 @370 + glProgramUniform4ui@24 @371 + glProgramUniform1f@12 @372 + glProgramUniform2f@16 @373 + glProgramUniform3f@20 @374 + glProgramUniform4f@24 @375 + glProgramUniform1iv@16 @376 + glProgramUniform2iv@16 @377 + glProgramUniform3iv@16 @378 + glProgramUniform4iv@16 @379 + glProgramUniform1uiv@16 @380 + glProgramUniform2uiv@16 @381 + glProgramUniform3uiv@16 @382 + glProgramUniform4uiv@16 @383 + glProgramUniform1fv@16 @384 + glProgramUniform2fv@16 @385 + glProgramUniform3fv@16 @386 + glProgramUniform4fv@16 @387 + glProgramUniformMatrix2fv@20 @388 + glProgramUniformMatrix3fv@20 @389 + glProgramUniformMatrix4fv@20 @390 + glProgramUniformMatrix2x3fv@20 @391 + glProgramUniformMatrix3x2fv@20 @392 + glProgramUniformMatrix2x4fv@20 @393 + glProgramUniformMatrix4x2fv@20 @394 + glProgramUniformMatrix3x4fv@20 @395 + glProgramUniformMatrix4x3fv@20 @396 + glValidateProgramPipeline@4 @397 + glGetProgramPipelineInfoLog@16 @398 + glBindImageTexture@28 @399 + glGetBooleani_v@12 @400 + glMemoryBarrier@4 @401 + glMemoryBarrierByRegion@4 @402 + glTexStorage2DMultisample@24 @403 + glGetMultisamplefv@12 @404 + glSampleMaski@8 @405 + glGetTexLevelParameteriv@16 @406 + glGetTexLevelParameterfv@16 @407 + glBindVertexBuffer@16 @408 + glVertexAttribFormat@20 @409 + glVertexAttribIFormat@16 @410 + glVertexAttribBinding@8 @411 + glVertexBindingDivisor@8 @412 -- cgit v1.2.3 From 6792c42f1ec0dc44617c74e1b1a69c0b200ded07 Mon Sep 17 00:00:00 2001 From: Janne Koskinen Date: Mon, 3 Jun 2019 15:29:23 +0300 Subject: Add names for pthreads in Integrity Set name for pthread instead of "name too long" for easier tracking. Change-Id: Iab22cbeac01277e4dc1325399c7892de2e5bd551 Reviewed-by: Timo Aarnipuro Reviewed-by: Thiago Macieira --- src/corelib/thread/qthread_unix.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src') diff --git a/src/corelib/thread/qthread_unix.cpp b/src/corelib/thread/qthread_unix.cpp index 329caa02ba..695d45d8e7 100644 --- a/src/corelib/thread/qthread_unix.cpp +++ b/src/corelib/thread/qthread_unix.cpp @@ -720,6 +720,12 @@ void QThread::start(Priority priority) } } +#ifdef Q_OS_INTEGRITY + if (Q_LIKELY(objectName().isEmpty())) + pthread_attr_setthreadname(&attr, metaObject()->className()); + else + pthread_attr_setthreadname(&attr, objectName().toLocal8Bit()); +#endif pthread_t threadId; int code = pthread_create(&threadId, &attr, QThreadPrivate::start, this); if (code == EPERM) { -- cgit v1.2.3 From 3797704c4ff0d91f544efa791973eced07c3ef02 Mon Sep 17 00:00:00 2001 From: Eirik Aavitsland Date: Fri, 14 Jun 2019 13:08:56 +0200 Subject: Generalize image file name @2x suffix handling to higher scale factors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @3x is in use on iOS already, so extend the handling in QImageReader to all single-digit factors, like QIcon does. Fixes: QTBUG-76273 Change-Id: Ic9442731c0549dbe8f797e1ddb1a09d8447e8441 Reviewed-by: Morten Johan Sørvig --- src/gui/image/qimagereader.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/gui/image/qimagereader.cpp b/src/gui/image/qimagereader.cpp index 0fb1d808e5..0c75196612 100644 --- a/src/gui/image/qimagereader.cpp +++ b/src/gui/image/qimagereader.cpp @@ -1319,10 +1319,12 @@ bool QImageReader::read(QImage *image) } } - // successful read; check for "@2x" file name suffix and set device pixel ratio. - static bool disable2xImageLoading = !qEnvironmentVariableIsEmpty("QT_HIGHDPI_DISABLE_2X_IMAGE_LOADING"); - if (!disable2xImageLoading && QFileInfo(fileName()).baseName().endsWith(QLatin1String("@2x"))) { - image->setDevicePixelRatio(2.0); + // successful read; check for "@Nx" file name suffix and set device pixel ratio. + static bool disableNxImageLoading = !qEnvironmentVariableIsEmpty("QT_HIGHDPI_DISABLE_2X_IMAGE_LOADING"); + if (!disableNxImageLoading) { + const QByteArray suffix = QFileInfo(fileName()).baseName().right(3).toLatin1(); + if (suffix.length() == 3 && suffix[0] == '@' && suffix[1] >= '2' && suffix[1] <= '9' && suffix[2] == 'x') + image->setDevicePixelRatio(suffix[1] - '0'); } if (autoTransform()) qt_imageTransform(*image, transformation()); -- cgit v1.2.3 From dd5b829468b4818f00a3c3a7df7f104dfa869bd1 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 17 Nov 2017 13:23:30 +0100 Subject: Raster paint engine: optimize rect clipping More aggressively skip processing empty spans. Tested with the qpainter benchmark on armv7; yields small but measurable improvement. Change-Id: Ie0ed0f824a0be7bcc2de3a9aa98ebccb0e8accae Reviewed-by: Lars Knoll Reviewed-by: Eirik Aavitsland --- src/gui/painting/qdrawhelper.cpp | 37 ++++++++++++++++------- src/gui/painting/qpaintengine_raster.cpp | 50 ++++++++++++++++++-------------- 2 files changed, 54 insertions(+), 33 deletions(-) (limited to 'src') diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index ca3590e280..1f7ab5006c 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -4419,7 +4419,9 @@ static void blend_color_rgb16(int count, const QSpan *spans, void *userData) if (mode == QPainter::CompositionMode_Source) { // inline for performance ushort c = data->solid.color.toRgb16(); - while (count--) { + for (; count--; spans++) { + if (!spans->len) + continue; ushort *target = ((ushort *)data->rasterBuffer->scanLine(spans->y)) + spans->x; if (spans->coverage == 255) { QT_MEMFILL_USHORT(target, spans->len, c); @@ -4432,13 +4434,14 @@ static void blend_color_rgb16(int count, const QSpan *spans, void *userData) ++target; } } - ++spans; } return; } if (mode == QPainter::CompositionMode_SourceOver) { - while (count--) { + for (; count--; spans++) { + if (!spans->len) + continue; uint color = BYTE_MUL(data->solid.color.toArgb32(), spans->coverage); int ialpha = qAlpha(~color); ushort c = qConvertRgb32To16(color); @@ -4470,7 +4473,6 @@ static void blend_color_rgb16(int count, const QSpan *spans, void *userData) // one last pixel beyond a full word *target = c + BYTE_MUL_RGB16(*target, ialpha); } - ++spans; } return; } @@ -4487,6 +4489,11 @@ void handleSpans(int count, const QSpan *spans, const QSpanData *data, T &handle int coverage = 0; while (count) { + if (!spans->len) { + ++spans; + --count; + continue; + } int x = spans->x; const int y = spans->y; int right = x + spans->len; @@ -4639,7 +4646,9 @@ static void blend_untransformed_generic(int count, const QSpan *spans, void *use int xoff = -qRound(-data->dx); int yoff = -qRound(-data->dy); - while (count--) { + for (; count--; spans++) { + if (!spans->len) + continue; int x = spans->x; int length = spans->len; int sx = xoff + x; @@ -4667,7 +4676,6 @@ static void blend_untransformed_generic(int count, const QSpan *spans, void *use } } } - ++spans; } } @@ -4688,7 +4696,9 @@ static void blend_untransformed_generic_rgb64(int count, const QSpan *spans, voi int xoff = -qRound(-data->dx); int yoff = -qRound(-data->dy); - while (count--) { + for (; count--; spans++) { + if (!spans->len) + continue; int x = spans->x; int length = spans->len; int sx = xoff + x; @@ -4716,7 +4726,6 @@ static void blend_untransformed_generic_rgb64(int count, const QSpan *spans, voi } } } - ++spans; } } @@ -4736,7 +4745,9 @@ static void blend_untransformed_argb(int count, const QSpan *spans, void *userDa int xoff = -qRound(-data->dx); int yoff = -qRound(-data->dy); - while (count--) { + for (; count--; spans++) { + if (!spans->len) + continue; int x = spans->x; int length = spans->len; int sx = xoff + x; @@ -4756,7 +4767,6 @@ static void blend_untransformed_argb(int count, const QSpan *spans, void *userDa op.func(dest, src, length, coverage); } } - ++spans; } } @@ -4829,7 +4839,12 @@ static void blend_untransformed_rgb565(int count, const QSpan *spans, void *user int xoff = -qRound(-data->dx); int yoff = -qRound(-data->dy); - while (count--) { + const QSpan *end = spans + count; + while (spans < end) { + if (!spans->len) { + ++spans; + continue; + } const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8; if (coverage == 0) { ++spans; diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp index ab22a71134..76c5842f58 100644 --- a/src/gui/painting/qpaintengine_raster.cpp +++ b/src/gui/painting/qpaintengine_raster.cpp @@ -4186,7 +4186,7 @@ static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userDa Clip spans to \a{clip}-rectangle. Returns number of unclipped spans */ -static int qt_intersect_spans(QT_FT_Span *spans, int numSpans, +static int qt_intersect_spans(QT_FT_Span *&spans, int numSpans, const QRect &clip) { const short minx = clip.left(); @@ -4194,29 +4194,32 @@ static int qt_intersect_spans(QT_FT_Span *spans, int numSpans, const short maxx = clip.right(); const short maxy = clip.bottom(); - int n = 0; - for (int i = 0; i < numSpans; ++i) { - if (spans[i].y > maxy) + QT_FT_Span *end = spans + numSpans; + while (spans < end) { + if (spans->y >= miny) break; - if (spans[i].y < miny - || spans[i].x > maxx - || spans[i].x + spans[i].len <= minx) { + ++spans; + } + + QT_FT_Span *s = spans; + while (s < end) { + if (s->y > maxy) + break; + if (s->x > maxx || s->x + s->len <= minx) { + s->len = 0; + ++s; continue; } - if (spans[i].x < minx) { - spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1); - spans[n].x = minx; + if (s->x < minx) { + s->len = qMin(s->len - (minx - s->x), maxx - minx + 1); + s->x = minx; } else { - spans[n].x = spans[i].x; - spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1)); + s->len = qMin(s->len, ushort(maxx - s->x + 1)); } - if (spans[n].len == 0) - continue; - spans[n].y = spans[i].y; - spans[n].coverage = spans[i].coverage; - ++n; + ++s; } - return n; + + return s - spans; } @@ -4229,11 +4232,12 @@ static void qt_span_fill_clipRect(int count, const QSpan *spans, Q_ASSERT(fillData->clip); Q_ASSERT(!fillData->clip->clipRect.isEmpty()); + QSpan *s = const_cast(spans); // hw: check if this const_cast<> is safe!!! - count = qt_intersect_spans(const_cast(spans), count, + count = qt_intersect_spans(s, count, fillData->clip->clipRect); if (count > 0) - fillData->unclipped_blend(count, spans, fillData); + fillData->unclipped_blend(count, s, fillData); } static void qt_span_clip(int count, const QSpan *spans, void *userData) @@ -4844,7 +4848,8 @@ static inline void drawEllipsePoints(int x, int y, int length, if (length == 0) return; - QT_FT_Span outline[4]; + QT_FT_Span _outline[4]; + QT_FT_Span *outline = _outline; const int midx = rect.x() + (rect.width() + 1) / 2; const int midy = rect.y() + (rect.height() + 1) / 2; @@ -4876,7 +4881,8 @@ static inline void drawEllipsePoints(int x, int y, int length, outline[3].coverage = 255; if (brush_func && outline[0].x + outline[0].len < outline[1].x) { - QT_FT_Span fill[2]; + QT_FT_Span _fill[2]; + QT_FT_Span *fill = _fill; // top fill fill[0].x = outline[0].x + outline[0].len - 1; -- cgit v1.2.3 From 1640973a826eccbefb8743bba6c5662a7b4e9fe9 Mon Sep 17 00:00:00 2001 From: Eirik Aavitsland Date: Fri, 14 Jun 2019 10:34:15 +0200 Subject: Fix raster paint error in path joins of tightly bending bezier curves The code for generating round line joins is optimized with a shortcut for the inner, normally invisible joins. For certain joins of a tightly turning bezier, this optimization would lead to visible painting error. Fix by avoiding the optimization if the next control point is so close as to allow such tight turns. As a driveby, make the angle > 90 test cheaper, since absolute precision is not required in the optimization choice. Fixes: QTBUG-75008 Change-Id: I293e0776003310dc36fa7f43fbcd9c25f1f8fa5d Reviewed-by: Lars Knoll Reviewed-by: Allan Sandfeld Jensen --- src/gui/painting/qstroker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/gui/painting/qstroker.cpp b/src/gui/painting/qstroker.cpp index 56d0917c6c..43dd191be8 100644 --- a/src/gui/painting/qstroker.cpp +++ b/src/gui/painting/qstroker.cpp @@ -524,7 +524,7 @@ void QStroker::joinPoints(qfixed focal_x, qfixed focal_y, const QLineF &nextLine QLineF shortCut(prevLine.p2(), nextLine.p1()); qreal angle = shortCut.angleTo(prevLine); - if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) { + if ((type == QLineF::BoundedIntersection || (angle > qreal(90.01))) && nextLine.length() > offset) { emitLineTo(focal_x, focal_y); emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1())); return; -- cgit v1.2.3 From 0c1831178540462da31fd7a4b6d2e446bc84498b Mon Sep 17 00:00:00 2001 From: Erik Kurzinger Date: Thu, 13 Jun 2019 08:15:50 -0700 Subject: Track swap interval in QXcbWindow As per GLX_EXT_swap_control, the GLX swap interval is specified on a per-drawable basis. However, QGLXContext only tracks it per-context using the m_swapInterval member. If a new drawable is made current to a context, it is still necessary to call glXSwapIntervalEXT to change the swap interval, even if it has been previously called for the same context with a different drawable. However, currently, QGLXContext::makeCurrent doesn't do this if its m_swapInterval field matches the new swap interval. This change removes m_swapInterval from QGLXContext, instead tracking it in QXcbWindow. This still avoids unnecessary calls to glXSwapIntervalEXT, while ensuring the swap interval is always set for new window drawables. Change-Id: Idc34101476c6af618059f6f3d8925dee743994a3 Reviewed-by: Giuseppe D'Angelo Reviewed-by: Laszlo Agocs --- .../platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp | 6 +++--- src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.h | 1 - src/plugins/platforms/xcb/qxcbwindow.h | 4 ++++ 3 files changed, 7 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp index 4adf662152..f26f698e76 100644 --- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp +++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp @@ -204,7 +204,6 @@ QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlat , m_shareContext(0) , m_format(format) , m_isPBufferCurrent(false) - , m_swapInterval(-1) , m_ownsContext(nativeHandle.isNull()) , m_getGraphicsResetStatus(0) , m_lost(false) @@ -567,9 +566,9 @@ bool QGLXContext::makeCurrent(QPlatformSurface *surface) if (success && surfaceClass == QSurface::Window) { int interval = surface->format().swapInterval(); + QXcbWindow *window = static_cast(surface); QXcbScreen *screen = screenForPlatformSurface(surface); - if (interval >= 0 && m_swapInterval != interval && screen) { - m_swapInterval = interval; + if (interval >= 0 && interval != window->swapInterval() && screen) { typedef void (*qt_glXSwapIntervalEXT)(Display *, GLXDrawable, int); typedef void (*qt_glXSwapIntervalMESA)(unsigned int); static qt_glXSwapIntervalEXT glXSwapIntervalEXT = 0; @@ -588,6 +587,7 @@ bool QGLXContext::makeCurrent(QPlatformSurface *surface) glXSwapIntervalEXT(m_display, glxDrawable, interval); else if (glXSwapIntervalMESA) glXSwapIntervalMESA(interval); + window->setSwapInterval(interval); } } diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.h b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.h index be9d3f5dcb..2a88fd6e59 100644 --- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.h +++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.h @@ -87,7 +87,6 @@ private: GLXContext m_shareContext; QSurfaceFormat m_format; bool m_isPBufferCurrent; - int m_swapInterval; bool m_ownsContext; GLenum (APIENTRY * m_getGraphicsResetStatus)(); bool m_lost; diff --git a/src/plugins/platforms/xcb/qxcbwindow.h b/src/plugins/platforms/xcb/qxcbwindow.h index f98cd8a74d..8258cc2dfa 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.h +++ b/src/plugins/platforms/xcb/qxcbwindow.h @@ -184,6 +184,9 @@ public: static void setWindowTitle(const QXcbConnection *conn, xcb_window_t window, const QString &title); static QString windowTitle(const QXcbConnection *conn, xcb_window_t window); + int swapInterval() const { return m_swapInterval; } + void setSwapInterval(int swapInterval) { m_swapInterval = swapInterval; } + public Q_SLOTS: void updateSyncRequestCounter(); @@ -276,6 +279,7 @@ protected: SyncState m_syncState = NoSyncNeeded; QXcbSyncWindowRequest *m_pendingSyncRequest = nullptr; + int m_swapInterval = -1; }; class QXcbForeignWindow : public QXcbWindow -- cgit v1.2.3 From 710435ee81c4cf48d66f07eff3cbad3eaef80ab8 Mon Sep 17 00:00:00 2001 From: Vova Mshanetskiy Date: Mon, 6 May 2019 15:00:55 +0300 Subject: QAndroidInputContext: Fix unneeded preedit commits when dragging handles If the cursor handle was dragged by only a few pixels, position of the cursor did not actually change, but finishComposingText() was called anyway. So the keyboard was thinking that nothing changed and a word is still being composed, but the app was thinking that there is no preedit string. This was resulting in invalid handling of following key presses. This commit essentially inlines QPlatformInputContext::setSelectionOnFocusObject() into QAndroidInputContext::handleLocationChanged(). This allows us to call finishComposingText() and to send a QInputMethodEvent only when position of the cursur actually changes. This also allows us to add a QInputMethodEvent::Cursor attribute into the event for consistency with QAndroidInputContext::longPress(). Change-Id: I2fc82f138f717991f34024cdf521236845dc0adf Reviewed-by: BogDan Vatra --- .../platforms/android/qandroidinputcontext.cpp | 63 ++++++++++++++++++---- 1 file changed, 52 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/android/qandroidinputcontext.cpp b/src/plugins/platforms/android/qandroidinputcontext.cpp index db40c30d7d..4c0b3315be 100644 --- a/src/plugins/platforms/android/qandroidinputcontext.cpp +++ b/src/plugins/platforms/android/qandroidinputcontext.cpp @@ -671,8 +671,6 @@ void QAndroidInputContext::handleLocationChanged(int handleId, int x, int y) return; } - finishComposingText(); - auto im = qGuiApp->inputMethod(); auto leftRect = im->cursorRectangle(); // The handle is down of the cursor, but we want the position in the middle. @@ -682,12 +680,9 @@ void QAndroidInputContext::handleLocationChanged(int handleId, int x, int y) : QHighDpiScaling::factor(QtAndroid::androidPlatformIntegration()->screen()); QPointF point(x / pixelDensity, y / pixelDensity); point.setY(point.y() - leftRect.width() / 2); - if (handleId == 1) { - setSelectionOnFocusObject(point, point); - return; - } - QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImAnchorPosition | Qt::ImCurrentSelection); + QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImAnchorPosition + | Qt::ImAbsolutePosition | Qt::ImCurrentSelection); QCoreApplication::sendEvent(m_focusObject, &query); int cpos = query.value(Qt::ImCursorPosition).toInt(); int anchor = query.value(Qt::ImAnchorPosition).toInt(); @@ -729,16 +724,62 @@ void QAndroidInputContext::handleLocationChanged(int handleId, int x, int y) }; if (handleId == 2) { - QPointF rightPoint(rightRect.center()); if ((!rtl && !checkLeftHandle(point)) || (rtl && !checkRtlRightHandle(point))) return; - setSelectionOnFocusObject(point, rightPoint); } else if (handleId == 3) { - QPointF leftPoint(leftRect.center()); if ((!rtl && !checkRightHandle(point)) || (rtl && !checkRtlLeftHandle(point))) return; - setSelectionOnFocusObject(leftPoint, point); } + + const QPointF pointLocal = im->inputItemTransform().inverted().map(point); + bool ok; + const int handlePos = + QInputMethod::queryFocusObject(Qt::ImCursorPosition, pointLocal).toInt(&ok); + if (!ok) + return; + + int newCpos = cpos; + int newAnchor = anchor; + if (newAnchor > newCpos) + std::swap(newAnchor, newCpos); + + if (handleId == 1) { + newCpos = handlePos; + newAnchor = handlePos; + } else if (handleId == 2) { + newAnchor = handlePos; + } else if (handleId == 3) { + newCpos = handlePos; + } + + // Check if handle has been dragged far enough + if (m_composingText.isEmpty() && newCpos == cpos && newAnchor == anchor) + return; + + /* + If there is composing text, we have to compare newCpos with m_composingCursor instead of cpos. + And since there is nothing to compare with newAnchor, we perform the check only when user + drags the cursor handle. + */ + if (!m_composingText.isEmpty() && handleId == 1) { + int absoluteCpos = query.value(Qt::ImAbsolutePosition).toInt(&ok); + if (!ok) + absoluteCpos = cpos; + const int blockPos = absoluteCpos - cpos; + + if (blockPos + newCpos == m_composingCursor) + return; + } + + finishComposingText(); + + QList attributes; + attributes.append({ QInputMethodEvent::Selection, newAnchor, newCpos - newAnchor }); + if (newCpos != newAnchor) + attributes.append({ QInputMethodEvent::Cursor, 0, 0 }); + + QInputMethodEvent event(QString(), attributes); + QGuiApplication::sendEvent(m_focusObject, &event); } void QAndroidInputContext::touchDown(int x, int y) -- cgit v1.2.3 From e5f2be256fb3bb07a98693cb90a4d7e8bc63c508 Mon Sep 17 00:00:00 2001 From: Vova Mshanetskiy Date: Mon, 6 May 2019 19:10:09 +0300 Subject: QAndroidInputContext: Don't allow clearing selection by dragging handles Android's native text editing controls do not allow user to clear selection by dragging selection handles. Qt apps should behave in the same way. Change-Id: I9a7c3a2aafa484eed8ff2bbd46dd48c705195291 Reviewed-by: BogDan Vatra --- .../platforms/android/qandroidinputcontext.cpp | 66 +++++++++------------- 1 file changed, 27 insertions(+), 39 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/android/qandroidinputcontext.cpp b/src/plugins/platforms/android/qandroidinputcontext.cpp index 4c0b3315be..4d9af18053 100644 --- a/src/plugins/platforms/android/qandroidinputcontext.cpp +++ b/src/plugins/platforms/android/qandroidinputcontext.cpp @@ -686,49 +686,15 @@ void QAndroidInputContext::handleLocationChanged(int handleId, int x, int y) QCoreApplication::sendEvent(m_focusObject, &query); int cpos = query.value(Qt::ImCursorPosition).toInt(); int anchor = query.value(Qt::ImAnchorPosition).toInt(); - bool rtl = query.value(Qt::ImCurrentSelection).toString().isRightToLeft(); auto rightRect = im->anchorRectangle(); if (cpos > anchor) std::swap(leftRect, rightRect); - auto checkLeftHandle = [&rightRect](QPointF &handlePos) { - if (handlePos.y() > rightRect.center().y()) - handlePos.setY(rightRect.center().y()); // adjust Y handle pos - if (handlePos.y() >= rightRect.y() && handlePos.y() <= rightRect.bottom() && handlePos.x() >= rightRect.x()) - return false; // same line and wrong X pos ? - return true; - }; - - auto checkRtlRightHandle = [&rightRect](QPointF &handlePos) { - if (handlePos.y() > rightRect.center().y()) - handlePos.setY(rightRect.center().y()); // adjust Y handle pos - if (handlePos.y() >= rightRect.y() && handlePos.y() <= rightRect.bottom() && rightRect.x() >= handlePos.x()) - return false; // same line and wrong X pos ? - return true; - }; - - auto checkRightHandle = [&leftRect](QPointF &handlePos) { - if (handlePos.y() < leftRect.center().y()) - handlePos.setY(leftRect.center().y()); // adjust Y handle pos - if (handlePos.y() >= leftRect.y() && handlePos.y() <= leftRect.bottom() && leftRect.x() >= handlePos.x()) - return false; // same line and wrong X pos ? - return true; - }; - - auto checkRtlLeftHandle = [&leftRect](QPointF &handlePos) { - if (handlePos.y() < leftRect.center().y()) - handlePos.setY(leftRect.center().y()); // adjust Y handle pos - if (handlePos.y() >= leftRect.y() && handlePos.y() <= leftRect.bottom() && handlePos.x() >= leftRect.x()) - return false; // same line and wrong X pos ? - return true; - }; - - if (handleId == 2) { - if ((!rtl && !checkLeftHandle(point)) || (rtl && !checkRtlRightHandle(point))) - return; - } else if (handleId == 3) { - if ((!rtl && !checkRightHandle(point)) || (rtl && !checkRtlLeftHandle(point))) - return; + // Do not allow dragging left handle below right handle, or right handle above left handle + if (handleId == 2 && point.y() > rightRect.center().y()) { + point.setY(rightRect.center().y()); + } else if (handleId == 3 && point.y() < leftRect.center().y()) { + point.setY(leftRect.center().y()); } const QPointF pointLocal = im->inputItemTransform().inverted().map(point); @@ -752,6 +718,28 @@ void QAndroidInputContext::handleLocationChanged(int handleId, int x, int y) newCpos = handlePos; } + /* + Do not allow clearing selection by dragging selection handles and do not allow swapping + selection handles for consistency with Android's native text editing controls. Ensure that at + least one symbol remains selected. + */ + if ((handleId == 2 || handleId == 3) && newCpos <= newAnchor) { + QTextBoundaryFinder finder(QTextBoundaryFinder::Grapheme, + query.value(Qt::ImCurrentSelection).toString()); + + const int oldSelectionStartPos = qMin(cpos, anchor); + + if (handleId == 2) { + finder.toEnd(); + finder.toPreviousBoundary(); + newAnchor = finder.position() + oldSelectionStartPos; + } else { + finder.toStart(); + finder.toNextBoundary(); + newCpos = finder.position() + oldSelectionStartPos; + } + } + // Check if handle has been dragged far enough if (m_composingText.isEmpty() && newCpos == cpos && newAnchor == anchor) return; -- cgit v1.2.3 From 1ade5ea41ab80e49e92fe46e0c44f76ee2e5e7fb Mon Sep 17 00:00:00 2001 From: Vova Mshanetskiy Date: Fri, 31 May 2019 16:25:14 +0300 Subject: QAndroidInputContext: Improve compatibility with virtual keyboards This commit improves QAndroidInputContext's conformance to Android's InputConnection interface and/or consistency of it's behavior with Android's native EditText control. * Composing region is now completely independent from cursor and selection, as required by InputConnection documentation. Also, Qt will now never clear composing region (i.e. call finishComposingText()) without receiving a command to do so from the keyboard. This is important for the following reasons: - Some keyboards misbehave if we change composing region without receiving a command from them. Notably, Samsung Keyboard does (QTBUG-68822). - Due to asynchronous nature of interaction between QAndroidInputContext and the keyboard, when user drags cursor handle quickly, the keyboard may call setComposingRegion() to mark a word, which is no longer under the cursor. This was causing text corruption (QTBUG-43156, QTBUG-59958). Also SwiftKey makes such calls when user presses Enter key (QTBUG-57819). - For similar reasons selecting a word with a double-tap could cause text corruption. The keyboard may call setComposingRegion() in response to the first tap after the second tap has been processed and the word has already been already selected. This is achieved by keeping track of start and end of composing region independently from the editor. Whenever possible (i.e. when there is no selection and the cursor is inside composing region), the composing text is represented as preedit text inside editor. And whenever that is imposible, the editor is told to commit, but QAndroidInputContext keeps information about composing region internally to be able to correctly interract with the keyboard. * deleteSurroundingText() has been re-written to work correctly when there are selection and/or composing region. Some keyboards (e.g Ginger Keyboard) do call deleteSurroundingText() when there is non-empty composing region. * All operations are now performed inside a batch edit (i.e. QAndroidInputContext now calls beginBatchEdit() and endBatchEdit() on itself) to ensure that an intermediate state is never reported to the keyboard, whenever an operation requires more than one QInputMethodEvent. BatchEditLock helper class was added to call begin/endBatchEdit() in RAII style. m_blockUpdateSelection has been removed because m_batchEditNestingLevel is now used instead of it. * Selection start and end positions are now reported to the keyboard so that start <= end. Some keyboards can not handle start > end. * getTextBefore/AfterCursor() now exclude selected text from their return values. While Android docs say "text before/after cursor", what they really mean is "text before/after selection" because "the cursor and the selection are one and the same thing". Some keyboards (e.g. Gboard) were behaving incorrectly when selected text was being returned. * getExtractedText() now tries to obtain and return the whole text from the editor. This is to fix compatibility with some buggy keyboards (e.g. Samsung Keyboard, Minuum) that ignore startOffset field and assume that selectionStart and selectionEnd are absolute values. Then they issue commands with wrong indexes in some cases. Fixes: QTBUG-43156 Fixes: QTBUG-59958 Fixes: QTBUG-57819 Fixes: QTBUG-68822 Change-Id: I7e71f3bcfbb2c32248d653a4197293db03579a79 Reviewed-by: BogDan Vatra --- .../platforms/android/qandroidinputcontext.cpp | 626 ++++++++++++++------- .../platforms/android/qandroidinputcontext.h | 6 +- 2 files changed, 430 insertions(+), 202 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/android/qandroidinputcontext.cpp b/src/plugins/platforms/android/qandroidinputcontext.cpp index 4d9af18053..fa07af8c46 100644 --- a/src/plugins/platforms/android/qandroidinputcontext.cpp +++ b/src/plugins/platforms/android/qandroidinputcontext.cpp @@ -64,29 +64,33 @@ QT_BEGIN_NAMESPACE -template -class ScopedValueChangeBack +namespace { + +class BatchEditLock { public: - ScopedValueChangeBack(T &variable, T newValue) - : m_oldValue(variable), - m_variable(variable) - { - m_variable = newValue; - } - inline void setOldValue() + + explicit BatchEditLock(QAndroidInputContext *context) + : m_context(context) { - m_variable = m_oldValue; + m_context->beginBatchEdit(); } - ~ScopedValueChangeBack() + + ~BatchEditLock() { - setOldValue(); + m_context->endBatchEdit(); } + + BatchEditLock(const BatchEditLock &) = delete; + BatchEditLock &operator=(const BatchEditLock &) = delete; + private: - T m_oldValue; - T &m_variable; + + QAndroidInputContext *m_context; }; +} // namespace anonymous + static QAndroidInputContext *m_androidInputContext = 0; static char const *const QtNativeInputConnectionClassName = "org/qtproject/qt5/android/QtNativeInputConnection"; static char const *const QtExtractedTextClassName = "org/qtproject/qt5/android/QtExtractedText"; @@ -423,8 +427,12 @@ static QRect inputItemRectangle() } QAndroidInputContext::QAndroidInputContext() - : QPlatformInputContext(), m_composingTextStart(-1), m_blockUpdateSelection(false), - m_handleMode(Hidden), m_batchEditNestingLevel(0), m_focusObject(0) + : QPlatformInputContext() + , m_composingTextStart(-1) + , m_composingCursor(-1) + , m_handleMode(Hidden) + , m_batchEditNestingLevel(0) + , m_focusObject(0) { jclass clazz = QJNIEnvironmentPrivate::findClass(QtNativeInputConnectionClassName); if (Q_UNLIKELY(!clazz)) { @@ -565,13 +573,13 @@ void QAndroidInputContext::reset() void QAndroidInputContext::commit() { - finishComposingText(); + focusObjectStopComposing(); } void QAndroidInputContext::updateCursorPosition() { QSharedPointer query = focusObjectInputMethodQuery(); - if (!query.isNull() && !m_blockUpdateSelection && !m_batchEditNestingLevel) { + if (!query.isNull() && m_batchEditNestingLevel == 0) { const int cursorPos = getAbsoluteCursorPosition(query); const int composeLength = m_composingText.length(); @@ -579,24 +587,29 @@ void QAndroidInputContext::updateCursorPosition() if (m_composingText.isEmpty() != (m_composingTextStart == -1)) qWarning() << "Input method out of sync" << m_composingText << m_composingTextStart; - int realCursorPosition = cursorPos; - int realAnchorPosition = cursorPos; + int realSelectionStart = cursorPos; + int realSelectionEnd = cursorPos; int cpos = query->value(Qt::ImCursorPosition).toInt(); int anchor = query->value(Qt::ImAnchorPosition).toInt(); if (cpos != anchor) { if (!m_composingText.isEmpty()) { qWarning("Selecting text while preediting may give unpredictable results."); - finishComposingText(); + focusObjectStopComposing(); } int blockPos = getBlockPosition(query); - realCursorPosition = blockPos + cpos; - realAnchorPosition = blockPos + anchor; + realSelectionStart = blockPos + cpos; + realSelectionEnd = blockPos + anchor; } // Qt's idea of the cursor position is the start of the preedit area, so we maintain our own preedit cursor pos - if (!m_composingText.isEmpty()) - realCursorPosition = realAnchorPosition = m_composingCursor; - QtAndroidInput::updateSelection(realCursorPosition, realAnchorPosition, + if (focusObjectIsComposing()) + realSelectionStart = realSelectionEnd = m_composingCursor; + + // Some keyboards misbahave when selStart > selEnd + if (realSelectionStart > realSelectionEnd) + std::swap(realSelectionStart, realSelectionEnd); + + QtAndroidInput::updateSelection(realSelectionStart, realSelectionEnd, m_composingTextStart, m_composingTextStart + composeLength); // pre-edit text } } @@ -666,7 +679,7 @@ void QAndroidInputContext::updateSelectionHandles() */ void QAndroidInputContext::handleLocationChanged(int handleId, int x, int y) { - if (m_batchEditNestingLevel.load() || m_blockUpdateSelection) { + if (m_batchEditNestingLevel != 0) { qWarning() << "QAndroidInputContext::handleLocationChanged returned"; return; } @@ -741,15 +754,15 @@ void QAndroidInputContext::handleLocationChanged(int handleId, int x, int y) } // Check if handle has been dragged far enough - if (m_composingText.isEmpty() && newCpos == cpos && newAnchor == anchor) + if (!focusObjectIsComposing() && newCpos == cpos && newAnchor == anchor) return; /* - If there is composing text, we have to compare newCpos with m_composingCursor instead of cpos. - And since there is nothing to compare with newAnchor, we perform the check only when user - drags the cursor handle. + If the editor is currently in composing state, we have to compare newCpos with + m_composingCursor instead of cpos. And since there is nothing to compare with newAnchor, we + perform the check only when user drags the cursor handle. */ - if (!m_composingText.isEmpty() && handleId == 1) { + if (focusObjectIsComposing() && handleId == 1) { int absoluteCpos = query.value(Qt::ImAbsolutePosition).toInt(&ok); if (!ok) absoluteCpos = cpos; @@ -759,7 +772,9 @@ void QAndroidInputContext::handleLocationChanged(int handleId, int x, int y) return; } - finishComposingText(); + BatchEditLock batchEditLock(this); + + focusObjectStopComposing(); QList attributes; attributes.append({ QInputMethodEvent::Selection, newAnchor, newCpos - newAnchor }); @@ -777,7 +792,7 @@ void QAndroidInputContext::touchDown(int x, int y) m_handleMode = ShowCursor; // The VK will appear in a moment, stop the timer m_hideCursorHandleTimer.stop(); - finishComposingText(); + focusObjectStopComposing(); updateSelectionHandles(); } } @@ -789,13 +804,19 @@ void QAndroidInputContext::longPress(int x, int y) return; if (m_focusObject && inputItemRectangle().contains(x, y)) { - finishComposingText(); + BatchEditLock batchEditLock(this); + + focusObjectStopComposing(); // Release left button, otherwise the following events will cancel the menu popup QtAndroidInput::releaseMouse(x, y); - handleLocationChanged(1, x, y); - ScopedValueChangeBack svcb(m_blockUpdateSelection, true); + const double pixelDensity = + QGuiApplication::focusWindow() + ? QHighDpiScaling::factor(QGuiApplication::focusWindow()) + : QHighDpiScaling::factor(QtAndroid::androidPlatformIntegration()->screen()); + const QPointF touchPoint(x / pixelDensity, y / pixelDensity); + setSelectionOnFocusObject(touchPoint, touchPoint); QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImAnchorPosition | Qt::ImTextBeforeCursor | Qt::ImTextAfterCursor); QCoreApplication::sendEvent(m_focusObject, &query); @@ -934,6 +955,7 @@ void QAndroidInputContext::clear() { m_composingText.clear(); m_composingTextStart = -1; + m_composingCursor = -1; m_extractedText.clear(); } @@ -941,9 +963,8 @@ void QAndroidInputContext::clear() void QAndroidInputContext::setFocusObject(QObject *object) { if (object != m_focusObject) { + focusObjectStopComposing(); m_focusObject = object; - if (!m_composingText.isEmpty()) - finishComposingText(); reset(); } QPlatformInputContext::setFocusObject(object); @@ -958,78 +979,135 @@ jboolean QAndroidInputContext::beginBatchEdit() jboolean QAndroidInputContext::endBatchEdit() { - if (--m_batchEditNestingLevel == 0 && !m_blockUpdateSelection) //ending batch edit mode + if (--m_batchEditNestingLevel == 0) { //ending batch edit mode + focusObjectStartComposing(); updateCursorPosition(); + } return JNI_TRUE; } /* - Android docs say: If composing, replace compose text with \a text. - Otherwise insert \a text at current cursor position. - - The cursor should then be moved to newCursorPosition. If > 0, this is - relative to the end of the text - 1; if <= 0, this is relative to the start - of the text. updateSelection() needs to be called. + Android docs say: This behaves like calling setComposingText(text, newCursorPosition) then + finishComposingText(). */ jboolean QAndroidInputContext::commitText(const QString &text, jint newCursorPosition) { - ScopedValueChangeBack svcb(m_blockUpdateSelection, true); - QInputMethodEvent event; - event.setCommitString(text); - sendInputMethodEvent(&event); - clear(); - - // Qt has now put the cursor at the end of the text, corresponding to newCursorPosition == 1 - if (newCursorPosition != 1) { - QSharedPointer query = focusObjectInputMethodQuery(); - if (!query.isNull()) { - QList attributes; - const int localPos = query->value(Qt::ImCursorPosition).toInt(); - const int newLocalPos = newCursorPosition > 0 - ? localPos + newCursorPosition - 1 - : localPos - text.length() + newCursorPosition; - //move the cursor - attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection, - newLocalPos, 0)); - } - } - svcb.setOldValue(); - updateCursorPosition(); - return JNI_TRUE; + BatchEditLock batchEditLock(this); + return setComposingText(text, newCursorPosition) && finishComposingText(); } jboolean QAndroidInputContext::deleteSurroundingText(jint leftLength, jint rightLength) { + BatchEditLock batchEditLock(this); + + focusObjectStopComposing(); + QSharedPointer query = focusObjectInputMethodQuery(); if (query.isNull()) return JNI_TRUE; - m_composingText.clear(); - m_composingTextStart = -1; - if (leftLength < 0) { rightLength += -leftLength; leftLength = 0; } + const int initialBlockPos = getBlockPosition(query); + const int initialCursorPos = getAbsoluteCursorPosition(query); + const int initialAnchorPos = initialBlockPos + query->value(Qt::ImAnchorPosition).toInt(); + + /* + According to documentation, we should delete leftLength characters before current selection + and rightLength characters after current selection (without affecting selection). But that is + absolutely not what Android's native EditText does. It deletes leftLength characters before + min(selection start, composing region start) and rightLength characters after max(selection + end, composing region end). There are no known keyboards that depend on this behavior, but + it is better to be consistent with EditText behavior, because there definetly should be no + keyboards that depend on documented behavior. + */ + const int leftEnd = + m_composingText.isEmpty() + ? qMin(initialCursorPos, initialAnchorPos) + : qMin(qMin(initialCursorPos, initialAnchorPos), m_composingTextStart); + + const int rightBegin = + m_composingText.isEmpty() + ? qMax(initialCursorPos, initialAnchorPos) + : qMax(qMax(initialCursorPos, initialAnchorPos), + m_composingTextStart + m_composingText.length()); + + int textBeforeCursorLen; + int textAfterCursorLen; + QVariant textBeforeCursor = query->value(Qt::ImTextBeforeCursor); QVariant textAfterCursor = query->value(Qt::ImTextAfterCursor); if (textBeforeCursor.isValid() && textAfterCursor.isValid()) { - leftLength = qMin(leftLength, textBeforeCursor.toString().length()); - rightLength = qMin(rightLength, textAfterCursor.toString().length()); + textBeforeCursorLen = textBeforeCursor.toString().length(); + textAfterCursorLen = textAfterCursor.toString().length(); } else { - int cursorPos = query->value(Qt::ImCursorPosition).toInt(); - leftLength = qMin(leftLength, cursorPos); - rightLength = qMin(rightLength, query->value(Qt::ImSurroundingText).toString().length() - cursorPos); + textBeforeCursorLen = initialCursorPos - initialBlockPos; + textAfterCursorLen = + query->value(Qt::ImSurroundingText).toString().length() - textBeforeCursorLen; } + leftLength = qMin(qMax(0, textBeforeCursorLen - (initialCursorPos - leftEnd)), leftLength); + rightLength = qMin(qMax(0, textAfterCursorLen - (rightBegin - initialCursorPos)), rightLength); + if (leftLength == 0 && rightLength == 0) return JNI_TRUE; - QInputMethodEvent event; - event.setCommitString(QString(), -leftLength, leftLength+rightLength); - sendInputMethodEvent(&event); - clear(); + if (leftEnd == rightBegin) { + // We have no selection and no composing region; we can do everything using one event + QInputMethodEvent event; + event.setCommitString({}, -leftLength, leftLength + rightLength); + QGuiApplication::sendEvent(m_focusObject, &event); + } else { + if (initialCursorPos != initialAnchorPos) { + QInputMethodEvent event({}, { + { QInputMethodEvent::Selection, initialCursorPos - initialBlockPos, 0 } + }); + + QGuiApplication::sendEvent(m_focusObject, &event); + } + + int currentCursorPos = initialCursorPos; + + if (rightLength > 0) { + QInputMethodEvent event; + event.setCommitString({}, rightBegin - currentCursorPos, rightLength); + QGuiApplication::sendEvent(m_focusObject, &event); + + currentCursorPos = rightBegin; + } + + if (leftLength > 0) { + const int leftBegin = leftEnd - leftLength; + + QInputMethodEvent event; + event.setCommitString({}, leftBegin - currentCursorPos, leftLength); + QGuiApplication::sendEvent(m_focusObject, &event); + + currentCursorPos = leftBegin; + + if (!m_composingText.isEmpty()) + m_composingTextStart -= leftLength; + } + + // Restore cursor position or selection + if (currentCursorPos != initialCursorPos - leftLength + || initialCursorPos != initialAnchorPos) { + // If we have deleted a newline character, we are now in a new block + const int currentBlockPos = getBlockPosition( + focusObjectInputMethodQuery(Qt::ImAbsolutePosition | Qt::ImCursorPosition)); + + QInputMethodEvent event({}, { + { QInputMethodEvent::Selection, initialCursorPos - leftLength - currentBlockPos, + initialAnchorPos - initialCursorPos }, + { QInputMethodEvent::Cursor, 0, 0 } + }); + + QGuiApplication::sendEvent(m_focusObject, &event); + } + } return JNI_TRUE; } @@ -1037,16 +1115,70 @@ jboolean QAndroidInputContext::deleteSurroundingText(jint leftLength, jint right // Android docs say the cursor must not move jboolean QAndroidInputContext::finishComposingText() { - if (m_composingText.isEmpty()) - return JNI_TRUE; // not composing + BatchEditLock batchEditLock(this); + + if (!focusObjectStopComposing()) + return JNI_FALSE; + + clear(); + return JNI_TRUE; +} + +bool QAndroidInputContext::focusObjectIsComposing() const +{ + return m_composingCursor != -1; +} + +void QAndroidInputContext::focusObjectStartComposing() +{ + if (focusObjectIsComposing() || m_composingText.isEmpty()) + return; + + // Composing strings containing newline characters are rare and may cause problems + if (m_composingText.contains(QLatin1Char('\n'))) + return; + + QSharedPointer query = focusObjectInputMethodQuery(); + if (!query) + return; + + if (query->value(Qt::ImCursorPosition).toInt() != query->value(Qt::ImAnchorPosition).toInt()) + return; + + const int absoluteCursorPos = getAbsoluteCursorPosition(query); + if (absoluteCursorPos < m_composingTextStart + || absoluteCursorPos > m_composingTextStart + m_composingText.length()) + return; + + m_composingCursor = absoluteCursorPos; + + QTextCharFormat underlined; + underlined.setFontUnderline(true); + + QInputMethodEvent event(m_composingText, { + { QInputMethodEvent::Cursor, absoluteCursorPos - m_composingTextStart, 1 }, + { QInputMethodEvent::TextFormat, 0, m_composingText.length(), underlined } + }); + + event.setCommitString({}, m_composingTextStart - absoluteCursorPos, m_composingText.length()); + + QGuiApplication::sendEvent(m_focusObject, &event); +} + +bool QAndroidInputContext::focusObjectStopComposing() +{ + if (!focusObjectIsComposing()) + return true; // not composing QSharedPointer query = focusObjectInputMethodQuery(); if (query.isNull()) - return JNI_FALSE; + return false; const int blockPos = getBlockPosition(query); const int localCursorPos = m_composingCursor - blockPos; + m_composingCursor = -1; + // Moving Qt's cursor to where the preedit cursor used to be QList attributes; attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection, localCursorPos, 0)); @@ -1054,9 +1186,8 @@ jboolean QAndroidInputContext::finishComposingText() QInputMethodEvent event(QString(), attributes); event.setCommitString(m_composingText); sendInputMethodEvent(&event); - clear(); - return JNI_TRUE; + return true; } jint QAndroidInputContext::getCursorCapsMode(jint /*reqModes*/) @@ -1096,52 +1227,51 @@ const QAndroidInputContext::ExtractedText &QAndroidInputContext::getExtractedTex // updateExtractedText(View, int, ExtractedText) whenever you call // updateSelection(View, int, int, int, int)." QTBUG-37980 - QSharedPointer query = focusObjectInputMethodQuery(); + QSharedPointer query = focusObjectInputMethodQuery( + Qt::ImCursorPosition | Qt::ImAbsolutePosition | Qt::ImAnchorPosition); if (query.isNull()) return m_extractedText; - int localPos = query->value(Qt::ImCursorPosition).toInt(); //position before pre-edit text relative to the current block - int blockPos = getBlockPosition(query); - QString blockText = query->value(Qt::ImSurroundingText).toString(); - int composeLength = m_composingText.length(); - - if (composeLength > 0) { - //Qt doesn't give us the preedit text, so we have to insert it at the correct position - int localComposePos = m_composingTextStart - blockPos; - blockText = blockText.leftRef(localComposePos) + m_composingText + blockText.midRef(localComposePos); - } - - int cpos = localPos + composeLength; //actual cursor pos relative to the current block - - int localOffset = 0; // start of extracted text relative to the current block - if (blockPos > 0) { - QString prevBlockEnding = query->value(Qt::ImTextBeforeCursor).toString(); - prevBlockEnding.chop(localPos); - if (prevBlockEnding.endsWith(QLatin1Char('\n'))) { - localOffset = -qMin(20, prevBlockEnding.length()); - blockText = prevBlockEnding.right(-localOffset) + blockText; - } - } + const int cursorPos = getAbsoluteCursorPosition(query); + const int blockPos = getBlockPosition(query); // It is documented that we should try to return hintMaxChars - // characters, but that's not what the standard Android controls do, and + // characters, but standard Android controls always return all text, and // there are input methods out there that (surprise) seem to depend on // what happens in reality rather than what's documented. - m_extractedText.text = blockText; - m_extractedText.startOffset = blockPos + localOffset; + QVariant textBeforeCursor = QInputMethod::queryFocusObject(Qt::ImTextBeforeCursor, INT_MAX); + QVariant textAfterCursor = QInputMethod::queryFocusObject(Qt::ImTextAfterCursor, INT_MAX); + if (textBeforeCursor.isValid() && textAfterCursor.isValid()) { + if (focusObjectIsComposing()) { + m_extractedText.text = + textBeforeCursor.toString() + m_composingText + textAfterCursor.toString(); + } else { + m_extractedText.text = textBeforeCursor.toString() + textAfterCursor.toString(); + } - const QString &selection = query->value(Qt::ImCurrentSelection).toString(); - const int selLen = selection.length(); - if (selLen) { - m_extractedText.selectionStart = query->value(Qt::ImAnchorPosition).toInt() - localOffset; - m_extractedText.selectionEnd = m_extractedText.selectionStart + selLen; - } else if (composeLength > 0) { + m_extractedText.startOffset = qMax(0, cursorPos - textBeforeCursor.toString().length()); + } else { + m_extractedText.text = focusObjectInputMethodQuery(Qt::ImSurroundingText) + ->value(Qt::ImSurroundingText).toString(); + + if (focusObjectIsComposing()) + m_extractedText.text.insert(cursorPos - blockPos, m_composingText); + + m_extractedText.startOffset = blockPos; + } + + if (focusObjectIsComposing()) { m_extractedText.selectionStart = m_composingCursor - m_extractedText.startOffset; - m_extractedText.selectionEnd = m_composingCursor - m_extractedText.startOffset; - } else { - m_extractedText.selectionStart = cpos - localOffset; - m_extractedText.selectionEnd = cpos - localOffset; + m_extractedText.selectionEnd = m_extractedText.selectionStart; + } else { + m_extractedText.selectionStart = cursorPos - m_extractedText.startOffset; + m_extractedText.selectionEnd = + blockPos + query->value(Qt::ImAnchorPosition).toInt() - m_extractedText.startOffset; + + // Some keyboards misbehave when selectionStart > selectionEnd + if (m_extractedText.selectionStart > m_extractedText.selectionEnd) + std::swap(m_extractedText.selectionStart, m_extractedText.selectionEnd); } return m_extractedText; @@ -1176,10 +1306,20 @@ QString QAndroidInputContext::getTextAfterCursor(jint length, jint /*flags*/) } } - // Controls do not report preedit text, so we have to add it - if (!m_composingText.isEmpty()) { + if (focusObjectIsComposing()) { + // Controls do not report preedit text, so we have to add it const int cursorPosInsidePreedit = m_composingCursor - m_composingTextStart; text = m_composingText.midRef(cursorPosInsidePreedit) + text; + } else { + // We must not return selected text if there is any + QSharedPointer query = + focusObjectInputMethodQuery(Qt::ImCursorPosition | Qt::ImAnchorPosition); + if (query) { + const int cursorPos = query->value(Qt::ImCursorPosition).toInt(); + const int anchorPos = query->value(Qt::ImAnchorPosition).toInt(); + if (anchorPos > cursorPos) + text.remove(0, anchorPos - cursorPos); + } } text.truncate(length); @@ -1206,10 +1346,20 @@ QString QAndroidInputContext::getTextBeforeCursor(jint length, jint /*flags*/) } } - // Controls do not report preedit text, so we have to add it - if (!m_composingText.isEmpty()) { + if (focusObjectIsComposing()) { + // Controls do not report preedit text, so we have to add it const int cursorPosInsidePreedit = m_composingCursor - m_composingTextStart; text += m_composingText.leftRef(cursorPosInsidePreedit); + } else { + // We must not return selected text if there is any + QSharedPointer query = + focusObjectInputMethodQuery(Qt::ImCursorPosition | Qt::ImAnchorPosition); + if (query) { + const int cursorPos = query->value(Qt::ImCursorPosition).toInt(); + const int anchorPos = query->value(Qt::ImAnchorPosition).toInt(); + if (anchorPos < cursorPos) + text.chop(cursorPos - anchorPos); + } } if (text.length() > length) @@ -1218,11 +1368,13 @@ QString QAndroidInputContext::getTextBeforeCursor(jint length, jint /*flags*/) } /* - Android docs say that this function should remove the current preedit text - if any, and replace it with the given text. Any selected text should be - removed. The cursor is then moved to newCursorPosition. If > 0, this is - relative to the end of the text - 1; if <= 0, this is relative to the start - of the text. + Android docs say that this function should: + - remove the current composing text, if there is any + - otherwise remove currently selected text, if there is any + - insert new text in place of old composing text or, if there was none, at current cursor position + - mark the inserted text as composing + - move cursor as specified by newCursorPosition: if > 0, it is relative to the end of inserted + text - 1; if <= 0, it is relative to the start of inserted text */ jboolean QAndroidInputContext::setComposingText(const QString &text, jint newCursorPosition) @@ -1231,47 +1383,110 @@ jboolean QAndroidInputContext::setComposingText(const QString &text, jint newCur if (query.isNull()) return JNI_FALSE; - const int cursorPos = getAbsoluteCursorPosition(query); - if (newCursorPosition > 0) - newCursorPosition += text.length() - 1; + BatchEditLock batchEditLock(this); + const int absoluteCursorPos = getAbsoluteCursorPosition(query); + int absoluteAnchorPos = getBlockPosition(query) + query->value(Qt::ImAnchorPosition).toInt(); + + // If we have composing region and selection (and therefore focusObjectIsComposing() == false), + // we must clear selection so that we won't delete it when we will be replacing composing text + if (!m_composingText.isEmpty() && absoluteCursorPos != absoluteAnchorPos) { + const int cursorPos = query->value(Qt::ImCursorPosition).toInt(); + QInputMethodEvent event({}, { { QInputMethodEvent::Selection, cursorPos, 0 } }); + QGuiApplication::sendEvent(m_focusObject, &event); + + absoluteAnchorPos = absoluteCursorPos; + } + + // If we had no composing region, pretend that we had a zero-length composing region at current + // cursor position to simplify code. Also account for that we must delete selected text if there + // (still) is any. + const int effectiveAbsoluteCursorPos = qMin(absoluteCursorPos, absoluteAnchorPos); + if (m_composingTextStart == -1) + m_composingTextStart = effectiveAbsoluteCursorPos; + + const int oldComposingTextLen = m_composingText.length(); m_composingText = text; - m_composingTextStart = text.isEmpty() ? -1 : cursorPos; - m_composingCursor = cursorPos + newCursorPosition; - QList attributes; - attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, - newCursorPosition, - 1)); - // Show compose text underlined - QTextCharFormat underlined; - underlined.setFontUnderline(true); - attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,0, text.length(), - QVariant(underlined))); - QInputMethodEvent event(m_composingText, attributes); - sendInputMethodEvent(&event); + const int newAbsoluteCursorPos = + newCursorPosition <= 0 + ? m_composingTextStart + newCursorPosition + : m_composingTextStart + m_composingText.length() + newCursorPosition - 1; - QMetaObject::invokeMethod(this, "keyDown"); + const bool focusObjectWasComposing = focusObjectIsComposing(); - updateCursorPosition(); + // Same checks as in focusObjectStartComposing() + if (!m_composingText.isEmpty() && !m_composingText.contains(QLatin1Char('\n')) + && newAbsoluteCursorPos >= m_composingTextStart + && newAbsoluteCursorPos <= m_composingTextStart + m_composingText.length()) + m_composingCursor = newAbsoluteCursorPos; + else + m_composingCursor = -1; + + QInputMethodEvent event; + if (focusObjectIsComposing()) { + QTextCharFormat underlined; + underlined.setFontUnderline(true); + + event = QInputMethodEvent(m_composingText, { + { QInputMethodEvent::TextFormat, 0, m_composingText.length(), underlined }, + { QInputMethodEvent::Cursor, m_composingCursor - m_composingTextStart, 1 } + }); + + if (oldComposingTextLen > 0 && !focusObjectWasComposing) { + event.setCommitString({}, m_composingTextStart - effectiveAbsoluteCursorPos, + oldComposingTextLen); + } + } else { + event = QInputMethodEvent({}, {}); + + if (focusObjectWasComposing) { + event.setCommitString(m_composingText); + } else { + event.setCommitString(m_composingText, + m_composingTextStart - effectiveAbsoluteCursorPos, + oldComposingTextLen); + } + } + + if (m_composingText.isEmpty()) + clear(); + + QGuiApplication::sendEvent(m_focusObject, &event); + + if (!focusObjectIsComposing() && newCursorPosition != 1) { + // Move cursor using a separate event because if we have inserted or deleted a newline + // character, then we are now inside an another block + + const int newBlockPos = getBlockPosition( + focusObjectInputMethodQuery(Qt::ImCursorPosition | Qt::ImAbsolutePosition)); + + event = QInputMethodEvent({}, { + { QInputMethodEvent::Selection, newAbsoluteCursorPos - newBlockPos, 0 } + }); + + QGuiApplication::sendEvent(m_focusObject, &event); + } + + keyDown(); return JNI_TRUE; } // Android docs say: // * start may be after end, same meaning as if swapped -// * this function should not trigger updateSelection +// * this function should not trigger updateSelection, but Android's native EditText does trigger it // * if start == end then we should stop composing jboolean QAndroidInputContext::setComposingRegion(jint start, jint end) { + BatchEditLock batchEditLock(this); + // Qt will not include the current preedit text in the query results, and interprets all // parameters relative to the text excluding the preedit. The simplest solution is therefore to // tell Qt that we commit the text before we set the new region. This may cause a little flicker, but is // much more robust than trying to keep the two different world views in sync - bool wasComposing = !m_composingText.isEmpty(); - if (wasComposing) - finishComposingText(); + finishComposingText(); QSharedPointer query = focusObjectInputMethodQuery(); if (query.isNull()) @@ -1282,54 +1497,42 @@ jboolean QAndroidInputContext::setComposingRegion(jint start, jint end) if (start > end) qSwap(start, end); - /* - start and end are cursor positions, not character positions, - i.e. selecting the first character is done by start == 0 and end == 1, - and start == end means no character selected - - Therefore, the length of the region is end - start - */ - - int length = end - start; - int localPos = query->value(Qt::ImCursorPosition).toInt(); - int blockPosition = getBlockPosition(query); - int localStart = start - blockPosition; // Qt uses position inside block - int currentCursor = wasComposing ? m_composingCursor : blockPosition + localPos; - - ScopedValueChangeBack svcb(m_blockUpdateSelection, true); - QString text = query->value(Qt::ImSurroundingText).toString(); + int textOffset = getBlockPosition(query); - m_composingText = text.mid(localStart, length); - m_composingTextStart = start; - m_composingCursor = currentCursor; - - //in the Qt text controls, the preedit is defined relative to the cursor position - int relativeStart = localStart - localPos; + if (start < textOffset || end > textOffset + text.length()) { + const int cursorPos = query->value(Qt::ImCursorPosition).toInt(); - QList attributes; + if (end - textOffset > text.length()) { + const QString after = query->value(Qt::ImTextAfterCursor).toString(); + const int additionalSuffixLen = after.length() - (text.length() - cursorPos); - // Show compose text underlined - QTextCharFormat underlined; - underlined.setFontUnderline(true); - attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,0, length, - QVariant(underlined))); + if (additionalSuffixLen > 0) + text += after.rightRef(additionalSuffixLen); + } - // Keep the cursor position unchanged (don't move to end of preedit) - attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, currentCursor - start, 1)); + if (start < textOffset) { + QString before = query->value(Qt::ImTextBeforeCursor).toString(); + before.chop(cursorPos); - QInputMethodEvent event(m_composingText, attributes); - event.setCommitString(QString(), relativeStart, length); - sendInputMethodEvent(&event); + if (!before.isEmpty()) { + text = before + text; + textOffset -= before.length(); + } + } + if (start < textOffset || end - textOffset > text.length()) { #ifdef QT_DEBUG_ANDROID_IM_PROTOCOL - QSharedPointer query2 = focusObjectInputMethodQuery(); - if (!query2.isNull()) { - qDebug() << "Setting. Prev local cpos:" << localPos << "block pos:" < attributes; - if (!m_composingText.isEmpty() && start == end) { + if (focusObjectIsComposing() && start == end && start >= m_composingTextStart + && start <= m_composingTextStart + m_composingText.length()) { // not actually changing the selection; just moving the // preedit cursor int localOldPos = query->value(Qt::ImCursorPosition).toInt(); int pos = localCursorPos - localOldPos; + QList attributes; attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, pos, 1)); //but we have to tell Qt about the compose text all over again @@ -1359,21 +1565,26 @@ jboolean QAndroidInputContext::setSelection(jint start, jint end) QVariant(underlined))); m_composingCursor = start; + QInputMethodEvent event(m_composingText, attributes); + QGuiApplication::sendEvent(m_focusObject, &event); } else { // actually changing the selection + focusObjectStopComposing(); + QList attributes; attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection, localCursorPos, end - start)); + QInputMethodEvent event({}, attributes); + QGuiApplication::sendEvent(m_focusObject, &event); } - QInputMethodEvent event(m_composingText, attributes); - sendInputMethodEvent(&event); - updateCursorPosition(); return JNI_TRUE; } jboolean QAndroidInputContext::selectAll() { - finishComposingText(); + BatchEditLock batchEditLock(this); + + focusObjectStopComposing(); m_handleMode = ShowCursor; sendShortcut(QKeySequence::SelectAll); return JNI_TRUE; @@ -1381,7 +1592,12 @@ jboolean QAndroidInputContext::selectAll() jboolean QAndroidInputContext::cut() { + BatchEditLock batchEditLock(this); + + // This is probably not what native EditText would do, but normally if there is selection, then + // there will be no composing region finishComposingText(); + m_handleMode = ShowCursor; sendShortcut(QKeySequence::Cut); return JNI_TRUE; @@ -1389,7 +1605,9 @@ jboolean QAndroidInputContext::cut() jboolean QAndroidInputContext::copy() { - finishComposingText(); + BatchEditLock batchEditLock(this); + + focusObjectStopComposing(); m_handleMode = ShowCursor; sendShortcut(QKeySequence::Copy); return JNI_TRUE; @@ -1403,7 +1621,11 @@ jboolean QAndroidInputContext::copyURL() jboolean QAndroidInputContext::paste() { + BatchEditLock batchEditLock(this); + + // TODO: This is not what native EditText does finishComposingText(); + m_handleMode = ShowCursor; sendShortcut(QKeySequence::Paste); return JNI_TRUE; @@ -1415,8 +1637,12 @@ void QAndroidInputContext::sendShortcut(const QKeySequence &sequence) const int keys = sequence[i]; Qt::Key key = Qt::Key(keys & ~Qt::KeyboardModifierMask); Qt::KeyboardModifiers mod = Qt::KeyboardModifiers(keys & Qt::KeyboardModifierMask); - QGuiApplication::postEvent(m_focusObject, new QKeyEvent(QEvent::KeyPress, key, mod)); - QGuiApplication::postEvent(m_focusObject, new QKeyEvent(QEvent::KeyRelease, key, mod)); + + QKeyEvent pressEvent(QEvent::KeyPress, key, mod); + QKeyEvent releaseEvent(QEvent::KeyRelease, key, mod); + + QGuiApplication::sendEvent(m_focusObject, &pressEvent); + QGuiApplication::sendEvent(m_focusObject, &releaseEvent); } } diff --git a/src/plugins/platforms/android/qandroidinputcontext.h b/src/plugins/platforms/android/qandroidinputcontext.h index bd3edb30f0..e9bfb98e66 100644 --- a/src/plugins/platforms/android/qandroidinputcontext.h +++ b/src/plugins/platforms/android/qandroidinputcontext.h @@ -151,6 +151,9 @@ private slots: private: void sendInputMethodEvent(QInputMethodEvent *event); QSharedPointer focusObjectInputMethodQuery(Qt::InputMethodQueries queries = Qt::ImQueryAll); + bool focusObjectIsComposing() const; + void focusObjectStartComposing(); + bool focusObjectStopComposing(); private: ExtractedText m_extractedText; @@ -158,9 +161,8 @@ private: int m_composingTextStart; int m_composingCursor; QMetaObject::Connection m_updateCursorPosConnection; - bool m_blockUpdateSelection; HandleModes m_handleMode; - QAtomicInt m_batchEditNestingLevel; + int m_batchEditNestingLevel; QObject *m_focusObject; QTimer m_hideCursorHandleTimer; }; -- cgit v1.2.3 From 7932c99a31493add24f683a974042c8c80d74fc1 Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Wed, 5 Jun 2019 15:27:37 +0200 Subject: Update SQLite to 3.28.0 [ChangeLog][Third-Party Code] Updated bundled SQLite to version 3.28.0 Change-Id: I3561ec3bcf80fe6062ccab27f1008dfda66e1187 Reviewed-by: Jani Heikkinen --- src/3rdparty/sqlite/qt_attribution.json | 4 +- src/3rdparty/sqlite/sqlite3.c | 14275 +++++++++++++++++------------- src/3rdparty/sqlite/sqlite3.h | 134 +- 3 files changed, 8409 insertions(+), 6004 deletions(-) (limited to 'src') diff --git a/src/3rdparty/sqlite/qt_attribution.json b/src/3rdparty/sqlite/qt_attribution.json index 542fa9efb9..e6647b2700 100644 --- a/src/3rdparty/sqlite/qt_attribution.json +++ b/src/3rdparty/sqlite/qt_attribution.json @@ -6,8 +6,8 @@ "Description": "SQLite is a small C library that implements a self-contained, embeddable, zero-configuration SQL database engine.", "Homepage": "https://www.sqlite.org/", - "Version": "3.26.0", - "DownloadLocation": "https://www.sqlite.org/2018/sqlite-amalgamation-3260000.zip", + "Version": "3.28.0", + "DownloadLocation": "https://www.sqlite.org/2019/sqlite-amalgamation-3280000.zip", "License": "Public Domain", "Copyright": "The authors disclaim copyright to the source code. However, a license can be obtained if needed." } diff --git a/src/3rdparty/sqlite/sqlite3.c b/src/3rdparty/sqlite/sqlite3.c index d015df2c31..440429527d 100644 --- a/src/3rdparty/sqlite/sqlite3.c +++ b/src/3rdparty/sqlite/sqlite3.c @@ -1,6 +1,6 @@ /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite -** version 3.26.0. By combining all the individual C code files into this +** version 3.28.0. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a single translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements @@ -1162,9 +1162,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.26.0" -#define SQLITE_VERSION_NUMBER 3026000 -#define SQLITE_SOURCE_ID "2018-12-01 12:34:55 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238b4f9" +#define SQLITE_VERSION "3.28.0" +#define SQLITE_VERSION_NUMBER 3028000 +#define SQLITE_SOURCE_ID "2019-04-16 19:49:53 884b4b7e502b4e991677b53971277adfaf0a04a284f8e483e2553d0f83156b50" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -1228,6 +1228,9 @@ SQLITE_API int sqlite3_libversion_number(void); #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS SQLITE_API int sqlite3_compileoption_used(const char *zOptName); SQLITE_API const char *sqlite3_compileoption_get(int N); +#else +# define sqlite3_compileoption_used(X) 0 +# define sqlite3_compileoption_get(X) ((void*)0) #endif /* @@ -1862,6 +1865,15 @@ struct sqlite3_io_methods { ** file space based on this hint in order to help writes to the database ** file run faster. ** +**
  • [[SQLITE_FCNTL_SIZE_LIMIT]] +** The [SQLITE_FCNTL_SIZE_LIMIT] opcode is used by in-memory VFS that +** implements [sqlite3_deserialize()] to set an upper bound on the size +** of the in-memory database. The argument is a pointer to a [sqlite3_int64]. +** If the integer pointed to is negative, then it is filled in with the +** current limit. Otherwise the limit is set to the larger of the value +** of the integer pointed to and the current database size. The integer +** pointed to is set to the new limit. +** **
  • [[SQLITE_FCNTL_CHUNK_SIZE]] ** The [SQLITE_FCNTL_CHUNK_SIZE] opcode is used to request that the VFS ** extends and truncates the database file in chunks of a size specified @@ -2170,6 +2182,7 @@ struct sqlite3_io_methods { #define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33 #define SQLITE_FCNTL_LOCK_TIMEOUT 34 #define SQLITE_FCNTL_DATA_VERSION 35 +#define SQLITE_FCNTL_SIZE_LIMIT 36 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE @@ -3011,6 +3024,17 @@ struct sqlite3_mem_methods { ** negative value for this option restores the default behaviour. ** This option is only available if SQLite is compiled with the ** [SQLITE_ENABLE_SORTER_REFERENCES] compile-time option. +** +** [[SQLITE_CONFIG_MEMDB_MAXSIZE]] +**
    SQLITE_CONFIG_MEMDB_MAXSIZE +**
    The SQLITE_CONFIG_MEMDB_MAXSIZE option accepts a single parameter +** [sqlite3_int64] parameter which is the default maximum size for an in-memory +** database created using [sqlite3_deserialize()]. This default maximum +** size can be adjusted up or down for individual databases using the +** [SQLITE_FCNTL_SIZE_LIMIT] [sqlite3_file_control|file-control]. If this +** configuration setting is never used, then the default maximum is determined +** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that +** compile-time option is not set, then the default maximum is 1073741824. ** */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ @@ -3041,6 +3065,7 @@ struct sqlite3_mem_methods { #define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */ #define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */ #define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */ +#define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */ /* ** CAPI3REF: Database Connection Configuration Options @@ -3103,8 +3128,8 @@ struct sqlite3_mem_methods { ** ** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]] **
    SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER
    -**
    ^This option is used to enable or disable the two-argument -** version of the [fts3_tokenizer()] function which is part of the +**
    ^This option is used to enable or disable the +** [fts3_tokenizer()] function which is part of the ** [FTS3] full-text search engine extension. ** There should be two additional arguments. ** The first argument is an integer which is 0 to disable fts3_tokenizer() or @@ -3216,6 +3241,17 @@ struct sqlite3_mem_methods { **
  • Direct writes to [shadow tables]. ** **
  • +** +** [[SQLITE_DBCONFIG_WRITABLE_SCHEMA]]
    SQLITE_DBCONFIG_WRITABLE_SCHEMA
    +**
    The SQLITE_DBCONFIG_WRITABLE_SCHEMA option activates or deactivates the +** "writable_schema" flag. This has the same effect and is logically equivalent +** to setting [PRAGMA writable_schema=ON] or [PRAGMA writable_schema=OFF]. +** The first argument to this setting is an integer which is 0 to disable +** the writable_schema, positive to enable writable_schema, or negative to +** leave the setting unchanged. The second parameter is a pointer to an +** integer into which is written 0 or 1 to indicate whether the writable_schema +** is enabled or disabled following this call. +**
    ** */ #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ @@ -3229,7 +3265,8 @@ struct sqlite3_mem_methods { #define SQLITE_DBCONFIG_TRIGGER_EQP 1008 /* int int* */ #define SQLITE_DBCONFIG_RESET_DATABASE 1009 /* int int* */ #define SQLITE_DBCONFIG_DEFENSIVE 1010 /* int int* */ -#define SQLITE_DBCONFIG_MAX 1010 /* Largest DBCONFIG */ +#define SQLITE_DBCONFIG_WRITABLE_SCHEMA 1011 /* int int* */ +#define SQLITE_DBCONFIG_MAX 1011 /* Largest DBCONFIG */ /* ** CAPI3REF: Enable Or Disable Extended Result Codes @@ -3386,7 +3423,7 @@ SQLITE_API int sqlite3_changes(sqlite3*); ** not. ^Changes to a view that are intercepted by INSTEAD OF triggers ** are not counted. ** -** This the [sqlite3_total_changes(D)] interface only reports the number +** The [sqlite3_total_changes(D)] interface only reports the number ** of rows that changed due to SQL statement run against database ** connection D. Any changes by other database connections are ignored. ** To detect changes against a database file from other database @@ -4030,9 +4067,9 @@ SQLITE_API int sqlite3_set_authorizer( ** time is in units of nanoseconds, however the current implementation ** is only capable of millisecond resolution so the six least significant ** digits in the time are meaningless. Future versions of SQLite -** might provide greater resolution on the profiler callback. The -** sqlite3_profile() function is considered experimental and is -** subject to change in future versions of SQLite. +** might provide greater resolution on the profiler callback. Invoking +** either [sqlite3_trace()] or [sqlite3_trace_v2()] will cancel the +** profile callback. */ SQLITE_API SQLITE_DEPRECATED void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*); @@ -4446,6 +4483,8 @@ SQLITE_API int sqlite3_open_v2( ** is not a database file pathname pointer that SQLite passed into the xOpen ** VFS method, then the behavior of this routine is undefined and probably ** undesirable. +** +** See the [URI filename] documentation for additional information. */ SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam); SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault); @@ -4668,18 +4707,23 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); ** deplete the limited store of lookaside memory. Future versions of ** SQLite may act on this hint differently. ** -** [[SQLITE_PREPARE_NORMALIZE]] ^(
    SQLITE_PREPARE_NORMALIZE
    -**
    The SQLITE_PREPARE_NORMALIZE flag indicates that a normalized -** representation of the SQL statement should be calculated and then -** associated with the prepared statement, which can be obtained via -** the [sqlite3_normalized_sql()] interface.)^ The semantics used to -** normalize a SQL statement are unspecified and subject to change. -** At a minimum, literal values will be replaced with suitable -** placeholders. +** [[SQLITE_PREPARE_NORMALIZE]]
    SQLITE_PREPARE_NORMALIZE
    +**
    The SQLITE_PREPARE_NORMALIZE flag is a no-op. This flag used +** to be required for any prepared statement that wanted to use the +** [sqlite3_normalized_sql()] interface. However, the +** [sqlite3_normalized_sql()] interface is now available to all +** prepared statements, regardless of whether or not they use this +** flag. +** +** [[SQLITE_PREPARE_NO_VTAB]]
    SQLITE_PREPARE_NO_VTAB
    +**
    The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler +** to return an error (error code SQLITE_ERROR) if the statement uses +** any virtual tables. ** */ #define SQLITE_PREPARE_PERSISTENT 0x01 #define SQLITE_PREPARE_NORMALIZE 0x02 +#define SQLITE_PREPARE_NO_VTAB 0x04 /* ** CAPI3REF: Compiling An SQL Statement @@ -4904,6 +4948,18 @@ SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt); */ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); +/* +** CAPI3REF: Query The EXPLAIN Setting For A Prepared Statement +** METHOD: sqlite3_stmt +** +** ^The sqlite3_stmt_isexplain(S) interface returns 1 if the +** prepared statement S is an EXPLAIN statement, or 2 if the +** statement S is an EXPLAIN QUERY PLAN. +** ^The sqlite3_stmt_isexplain(S) interface returns 0 if S is +** an ordinary statement or a NULL pointer. +*/ +SQLITE_API int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt); + /* ** CAPI3REF: Determine If A Prepared Statement Has Been Reset ** METHOD: sqlite3_stmt @@ -5043,7 +5099,9 @@ typedef struct sqlite3_context sqlite3_context; ** ^The fifth argument to the BLOB and string binding interfaces ** is a destructor used to dispose of the BLOB or ** string after SQLite has finished with it. ^The destructor is called -** to dispose of the BLOB or string even if the call to bind API fails. +** to dispose of the BLOB or string even if the call to the bind API fails, +** except the destructor is not called if the third parameter is a NULL +** pointer or the fourth parameter is negative. ** ^If the fifth argument is ** the special value [SQLITE_STATIC], then SQLite assumes that the ** information is in static, unmanaged space and does not need to be freed. @@ -5960,6 +6018,8 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6 ** sqlite3_value_nochange   ** →  True if the column is unchanged in an UPDATE ** against a virtual table. +** sqlite3_value_frombind   +** →  True if value originated from a [bound parameter] ** ** ** Details: @@ -6021,6 +6081,11 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6 ** than within an [xUpdate] method call for an UPDATE statement, then ** the return value is arbitrary and meaningless. ** +** ^The sqlite3_value_frombind(X) interface returns non-zero if the +** value X originated from one of the [sqlite3_bind_int|sqlite3_bind()] +** interfaces. ^If X comes from an SQL literal value, or a table column, +** and expression, then sqlite3_value_frombind(X) returns zero. +** ** Please pay particular attention to the fact that the pointer returned ** from [sqlite3_value_blob()], [sqlite3_value_text()], or ** [sqlite3_value_text16()] can be invalidated by a subsequent call to @@ -6066,6 +6131,7 @@ SQLITE_API int sqlite3_value_bytes16(sqlite3_value*); SQLITE_API int sqlite3_value_type(sqlite3_value*); SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); SQLITE_API int sqlite3_value_nochange(sqlite3_value*); +SQLITE_API int sqlite3_value_frombind(sqlite3_value*); /* ** CAPI3REF: Finding The Subtype Of SQL Values @@ -6801,7 +6867,7 @@ SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); ** associated with database N of connection D. ^The main database file ** has the name "main". If there is no attached database N on the database ** connection D, or if database N is a temporary or in-memory database, then -** a NULL pointer is returned. +** this function will return either a NULL pointer or an empty string. ** ** ^The filename returned by this function is the output of the ** xFullPathname method of the [VFS]. ^In other words, the filename @@ -11035,7 +11101,7 @@ SQLITE_API int sqlite3changeset_next(sqlite3_changeset_iter *pIter); ** sqlite3changeset_next() is called on the iterator or until the ** conflict-handler function returns. If pnCol is not NULL, then *pnCol is ** set to the number of columns in the table affected by the change. If -** pbIncorrect is not NULL, then *pbIndirect is set to true (1) if the change +** pbIndirect is not NULL, then *pbIndirect is set to true (1) if the change ** is an indirect change, or false (0) otherwise. See the documentation for ** [sqlite3session_indirect()] for a description of direct and indirect ** changes. Finally, if pOp is not NULL, then *pOp is set to one of @@ -11902,7 +11968,7 @@ SQLITE_API int sqlite3rebaser_configure( ** in size. This function allocates and populates a buffer with a copy ** of the changeset rebased rebased according to the configuration of the ** rebaser object passed as the first argument. If successful, (*ppOut) -** is set to point to the new buffer containing the rebased changset and +** is set to point to the new buffer containing the rebased changeset and ** (*pnOut) to its size in bytes and SQLITE_OK returned. It is the ** responsibility of the caller to eventually free the new buffer using ** sqlite3_free(). Otherwise, if an error occurs, (*ppOut) and (*pnOut) @@ -12269,12 +12335,8 @@ struct Fts5PhraseIter { ** ** Usually, output parameter *piPhrase is set to the phrase number, *piCol ** to the column in which it occurs and *piOff the token offset of the -** first token of the phrase. The exception is if the table was created -** with the offsets=0 option specified. In this case *piOff is always -** set to -1. -** -** Returns SQLITE_OK if successful, or an error code (i.e. SQLITE_NOMEM) -** if an error occurs. +** first token of the phrase. Returns SQLITE_OK if successful, or an error +** code (i.e. SQLITE_NOMEM) if an error occurs. ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. @@ -12315,7 +12377,7 @@ struct Fts5PhraseIter { ** Save the pointer passed as the second argument as the extension functions ** "auxiliary data". The pointer may then be retrieved by the current or any ** future invocation of the same fts5 extension function made as part of -** of the same MATCH query using the xGetAuxdata() API. +** the same MATCH query using the xGetAuxdata() API. ** ** Each extension function is allocated a single auxiliary data slot for ** each FTS query (MATCH expression). If the extension function is invoked @@ -12330,7 +12392,7 @@ struct Fts5PhraseIter { ** The xDelete callback, if one is specified, is also invoked on the ** auxiliary data pointer after the FTS5 query has finished. ** -** If an error (e.g. an OOM condition) occurs within this function, an +** If an error (e.g. an OOM condition) occurs within this function, ** the auxiliary data is set to NULL and an error code returned. If the ** xDelete parameter was not NULL, it is invoked on the auxiliary data ** pointer before returning. @@ -12563,11 +12625,11 @@ struct Fts5ExtensionApi { ** the tokenizer substitutes "first" for "1st" and the query works ** as expected. ** -**
  • By adding multiple synonyms for a single term to the FTS index. -** In this case, when tokenizing query text, the tokenizer may -** provide multiple synonyms for a single term within the document. -** FTS5 then queries the index for each synonym individually. For -** example, faced with the query: +**
  • By querying the index for all synonyms of each query term +** separately. In this case, when tokenizing query text, the +** tokenizer may provide multiple synonyms for a single term +** within the document. FTS5 then queries the index for each +** synonym individually. For example, faced with the query: ** ** ** ... MATCH 'first place' @@ -12591,7 +12653,7 @@ struct Fts5ExtensionApi { ** "place". ** ** This way, even if the tokenizer does not provide synonyms -** when tokenizing query text (it should not - to do would be +** when tokenizing query text (it should not - to do so would be ** inefficient), it doesn't matter if the user queries for ** 'first + place' or '1st + place', as there are entries in the ** FTS index corresponding to both forms of the first token. @@ -13356,7 +13418,7 @@ struct Hash { unsigned int count; /* Number of entries in this table */ HashElem *first; /* The first element of the array */ struct _ht { /* the hash table */ - int count; /* Number of entries with this hash */ + unsigned int count; /* Number of entries with this hash */ HashElem *chain; /* Pointer to first entry with this hash */ } *ht; }; @@ -13497,99 +13559,94 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*); #define TK_PRECEDING 85 #define TK_RANGE 86 #define TK_UNBOUNDED 87 -#define TK_REINDEX 88 -#define TK_RENAME 89 -#define TK_CTIME_KW 90 -#define TK_ANY 91 -#define TK_BITAND 92 -#define TK_BITOR 93 -#define TK_LSHIFT 94 -#define TK_RSHIFT 95 -#define TK_PLUS 96 -#define TK_MINUS 97 -#define TK_STAR 98 -#define TK_SLASH 99 -#define TK_REM 100 -#define TK_CONCAT 101 -#define TK_COLLATE 102 -#define TK_BITNOT 103 -#define TK_ON 104 -#define TK_INDEXED 105 -#define TK_STRING 106 -#define TK_JOIN_KW 107 -#define TK_CONSTRAINT 108 -#define TK_DEFAULT 109 -#define TK_NULL 110 -#define TK_PRIMARY 111 -#define TK_UNIQUE 112 -#define TK_CHECK 113 -#define TK_REFERENCES 114 -#define TK_AUTOINCR 115 -#define TK_INSERT 116 -#define TK_DELETE 117 -#define TK_UPDATE 118 -#define TK_SET 119 -#define TK_DEFERRABLE 120 -#define TK_FOREIGN 121 -#define TK_DROP 122 -#define TK_UNION 123 -#define TK_ALL 124 -#define TK_EXCEPT 125 -#define TK_INTERSECT 126 -#define TK_SELECT 127 -#define TK_VALUES 128 -#define TK_DISTINCT 129 -#define TK_DOT 130 -#define TK_FROM 131 -#define TK_JOIN 132 -#define TK_USING 133 -#define TK_ORDER 134 -#define TK_GROUP 135 -#define TK_HAVING 136 -#define TK_LIMIT 137 -#define TK_WHERE 138 -#define TK_INTO 139 -#define TK_NOTHING 140 -#define TK_FLOAT 141 -#define TK_BLOB 142 -#define TK_INTEGER 143 -#define TK_VARIABLE 144 -#define TK_CASE 145 -#define TK_WHEN 146 -#define TK_THEN 147 -#define TK_ELSE 148 -#define TK_INDEX 149 -#define TK_ALTER 150 -#define TK_ADD 151 -#define TK_WINDOW 152 -#define TK_OVER 153 -#define TK_FILTER 154 -#define TK_TRUEFALSE 155 -#define TK_ISNOT 156 -#define TK_FUNCTION 157 -#define TK_COLUMN 158 -#define TK_AGG_FUNCTION 159 -#define TK_AGG_COLUMN 160 -#define TK_UMINUS 161 -#define TK_UPLUS 162 -#define TK_TRUTH 163 -#define TK_REGISTER 164 -#define TK_VECTOR 165 -#define TK_SELECT_COLUMN 166 -#define TK_IF_NULL_ROW 167 -#define TK_ASTERISK 168 -#define TK_SPAN 169 -#define TK_END_OF_FILE 170 -#define TK_UNCLOSED_STRING 171 -#define TK_SPACE 172 -#define TK_ILLEGAL 173 - -/* The token codes above must all fit in 8 bits */ -#define TKFLG_MASK 0xff - -/* Flags that can be added to a token code when it is not -** being stored in a u8: */ -#define TKFLG_DONTFOLD 0x100 /* Omit constant folding optimizations */ +#define TK_EXCLUDE 88 +#define TK_GROUPS 89 +#define TK_OTHERS 90 +#define TK_TIES 91 +#define TK_REINDEX 92 +#define TK_RENAME 93 +#define TK_CTIME_KW 94 +#define TK_ANY 95 +#define TK_BITAND 96 +#define TK_BITOR 97 +#define TK_LSHIFT 98 +#define TK_RSHIFT 99 +#define TK_PLUS 100 +#define TK_MINUS 101 +#define TK_STAR 102 +#define TK_SLASH 103 +#define TK_REM 104 +#define TK_CONCAT 105 +#define TK_COLLATE 106 +#define TK_BITNOT 107 +#define TK_ON 108 +#define TK_INDEXED 109 +#define TK_STRING 110 +#define TK_JOIN_KW 111 +#define TK_CONSTRAINT 112 +#define TK_DEFAULT 113 +#define TK_NULL 114 +#define TK_PRIMARY 115 +#define TK_UNIQUE 116 +#define TK_CHECK 117 +#define TK_REFERENCES 118 +#define TK_AUTOINCR 119 +#define TK_INSERT 120 +#define TK_DELETE 121 +#define TK_UPDATE 122 +#define TK_SET 123 +#define TK_DEFERRABLE 124 +#define TK_FOREIGN 125 +#define TK_DROP 126 +#define TK_UNION 127 +#define TK_ALL 128 +#define TK_EXCEPT 129 +#define TK_INTERSECT 130 +#define TK_SELECT 131 +#define TK_VALUES 132 +#define TK_DISTINCT 133 +#define TK_DOT 134 +#define TK_FROM 135 +#define TK_JOIN 136 +#define TK_USING 137 +#define TK_ORDER 138 +#define TK_GROUP 139 +#define TK_HAVING 140 +#define TK_LIMIT 141 +#define TK_WHERE 142 +#define TK_INTO 143 +#define TK_NOTHING 144 +#define TK_FLOAT 145 +#define TK_BLOB 146 +#define TK_INTEGER 147 +#define TK_VARIABLE 148 +#define TK_CASE 149 +#define TK_WHEN 150 +#define TK_THEN 151 +#define TK_ELSE 152 +#define TK_INDEX 153 +#define TK_ALTER 154 +#define TK_ADD 155 +#define TK_WINDOW 156 +#define TK_OVER 157 +#define TK_FILTER 158 +#define TK_TRUEFALSE 159 +#define TK_ISNOT 160 +#define TK_FUNCTION 161 +#define TK_COLUMN 162 +#define TK_AGG_FUNCTION 163 +#define TK_AGG_COLUMN 164 +#define TK_UMINUS 165 +#define TK_UPLUS 166 +#define TK_TRUTH 167 +#define TK_REGISTER 168 +#define TK_VECTOR 169 +#define TK_SELECT_COLUMN 170 +#define TK_IF_NULL_ROW 171 +#define TK_ASTERISK 172 +#define TK_SPAN 173 +#define TK_SPACE 174 +#define TK_ILLEGAL 175 /************** End of parse.h ***********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ @@ -14521,9 +14578,6 @@ struct BtreePayload { SQLITE_PRIVATE int sqlite3BtreeInsert(BtCursor*, const BtreePayload *pPayload, int flags, int seekResult); SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor*, int *pRes); -#ifndef SQLITE_OMIT_WINDOWFUNC -SQLITE_PRIVATE void sqlite3BtreeSkipNext(BtCursor*); -#endif SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor*, int *pRes); SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor*, int flags); SQLITE_PRIVATE int sqlite3BtreeEof(BtCursor*); @@ -14535,6 +14589,7 @@ SQLITE_PRIVATE i64 sqlite3BtreeOffset(BtCursor*); SQLITE_PRIVATE int sqlite3BtreePayload(BtCursor*, u32 offset, u32 amt, void*); SQLITE_PRIVATE const void *sqlite3BtreePayloadFetch(BtCursor*, u32 *pAmt); SQLITE_PRIVATE u32 sqlite3BtreePayloadSize(BtCursor*); +SQLITE_PRIVATE sqlite3_int64 sqlite3BtreeMaxRecordSize(BtCursor*); SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*); SQLITE_PRIVATE struct Pager *sqlite3BtreePager(Btree*); @@ -14774,12 +14829,11 @@ typedef struct VdbeOpList VdbeOpList; #endif /* -** The following macro converts a relative address in the p2 field -** of a VdbeOp structure into a negative number so that -** sqlite3VdbeAddOpList() knows that the address is relative. Calling -** the macro again restores the address. +** The following macro converts a label returned by sqlite3VdbeMakeLabel() +** into an index into the Parse.aLabel[] array that contains the resolved +** address of that label. */ -#define ADDR(X) (-1-(X)) +#define ADDR(X) (~(X)) /* ** The makefile scans the vdbe.c source file and creates the "opcodes.h" @@ -14881,25 +14935,25 @@ typedef struct VdbeOpList VdbeOpList; #define OP_Offset 89 /* synopsis: r[P3] = sqlite_offset(P1) */ #define OP_Column 90 /* synopsis: r[P3]=PX */ #define OP_Affinity 91 /* synopsis: affinity(r[P1@P2]) */ -#define OP_BitAnd 92 /* same as TK_BITAND, synopsis: r[P3]=r[P1]&r[P2] */ -#define OP_BitOr 93 /* same as TK_BITOR, synopsis: r[P3]=r[P1]|r[P2] */ -#define OP_ShiftLeft 94 /* same as TK_LSHIFT, synopsis: r[P3]=r[P2]<>r[P1] */ -#define OP_Add 96 /* same as TK_PLUS, synopsis: r[P3]=r[P1]+r[P2] */ -#define OP_Subtract 97 /* same as TK_MINUS, synopsis: r[P3]=r[P2]-r[P1] */ -#define OP_Multiply 98 /* same as TK_STAR, synopsis: r[P3]=r[P1]*r[P2] */ -#define OP_Divide 99 /* same as TK_SLASH, synopsis: r[P3]=r[P2]/r[P1] */ -#define OP_Remainder 100 /* same as TK_REM, synopsis: r[P3]=r[P2]%r[P1] */ -#define OP_Concat 101 /* same as TK_CONCAT, synopsis: r[P3]=r[P2]+r[P1] */ -#define OP_MakeRecord 102 /* synopsis: r[P3]=mkrec(r[P1@P2]) */ -#define OP_BitNot 103 /* same as TK_BITNOT, synopsis: r[P2]= ~r[P1] */ -#define OP_Count 104 /* synopsis: r[P2]=count() */ -#define OP_ReadCookie 105 -#define OP_String8 106 /* same as TK_STRING, synopsis: r[P2]='P4' */ -#define OP_SetCookie 107 -#define OP_ReopenIdx 108 /* synopsis: root=P2 iDb=P3 */ -#define OP_OpenRead 109 /* synopsis: root=P2 iDb=P3 */ -#define OP_OpenWrite 110 /* synopsis: root=P2 iDb=P3 */ +#define OP_MakeRecord 92 /* synopsis: r[P3]=mkrec(r[P1@P2]) */ +#define OP_Count 93 /* synopsis: r[P2]=count() */ +#define OP_ReadCookie 94 +#define OP_SetCookie 95 +#define OP_BitAnd 96 /* same as TK_BITAND, synopsis: r[P3]=r[P1]&r[P2] */ +#define OP_BitOr 97 /* same as TK_BITOR, synopsis: r[P3]=r[P1]|r[P2] */ +#define OP_ShiftLeft 98 /* same as TK_LSHIFT, synopsis: r[P3]=r[P2]<>r[P1] */ +#define OP_Add 100 /* same as TK_PLUS, synopsis: r[P3]=r[P1]+r[P2] */ +#define OP_Subtract 101 /* same as TK_MINUS, synopsis: r[P3]=r[P2]-r[P1] */ +#define OP_Multiply 102 /* same as TK_STAR, synopsis: r[P3]=r[P1]*r[P2] */ +#define OP_Divide 103 /* same as TK_SLASH, synopsis: r[P3]=r[P2]/r[P1] */ +#define OP_Remainder 104 /* same as TK_REM, synopsis: r[P3]=r[P2]%r[P1] */ +#define OP_Concat 105 /* same as TK_CONCAT, synopsis: r[P3]=r[P2]+r[P1] */ +#define OP_ReopenIdx 106 /* synopsis: root=P2 iDb=P3 */ +#define OP_BitNot 107 /* same as TK_BITNOT, synopsis: r[P2]= ~r[P1] */ +#define OP_OpenRead 108 /* synopsis: root=P2 iDb=P3 */ +#define OP_OpenWrite 109 /* synopsis: root=P2 iDb=P3 */ +#define OP_String8 110 /* same as TK_STRING, synopsis: r[P2]='P4' */ #define OP_OpenDup 111 #define OP_OpenAutoindex 112 /* synopsis: nColumn=P2 */ #define OP_OpenEphemeral 113 /* synopsis: nColumn=P2 */ @@ -14912,57 +14966,56 @@ typedef struct VdbeOpList VdbeOpList; #define OP_Sequence 120 /* synopsis: r[P2]=cursor[P1].ctr++ */ #define OP_NewRowid 121 /* synopsis: r[P2]=rowid */ #define OP_Insert 122 /* synopsis: intkey=r[P3] data=r[P2] */ -#define OP_InsertInt 123 /* synopsis: intkey=P3 data=r[P2] */ -#define OP_Delete 124 -#define OP_ResetCount 125 -#define OP_SorterCompare 126 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */ -#define OP_SorterData 127 /* synopsis: r[P2]=data */ -#define OP_RowData 128 /* synopsis: r[P2]=data */ -#define OP_Rowid 129 /* synopsis: r[P2]=rowid */ -#define OP_NullRow 130 -#define OP_SeekEnd 131 -#define OP_SorterInsert 132 /* synopsis: key=r[P2] */ -#define OP_IdxInsert 133 /* synopsis: key=r[P2] */ -#define OP_IdxDelete 134 /* synopsis: key=r[P2@P3] */ -#define OP_DeferredSeek 135 /* synopsis: Move P3 to P1.rowid if needed */ -#define OP_IdxRowid 136 /* synopsis: r[P2]=rowid */ -#define OP_Destroy 137 -#define OP_Clear 138 -#define OP_ResetSorter 139 -#define OP_CreateBtree 140 /* synopsis: r[P2]=root iDb=P1 flags=P3 */ -#define OP_Real 141 /* same as TK_FLOAT, synopsis: r[P2]=P4 */ -#define OP_SqlExec 142 -#define OP_ParseSchema 143 -#define OP_LoadAnalysis 144 -#define OP_DropTable 145 -#define OP_DropIndex 146 -#define OP_DropTrigger 147 -#define OP_IntegrityCk 148 -#define OP_RowSetAdd 149 /* synopsis: rowset(P1)=r[P2] */ -#define OP_Param 150 -#define OP_FkCounter 151 /* synopsis: fkctr[P1]+=P2 */ -#define OP_MemMax 152 /* synopsis: r[P1]=max(r[P1],r[P2]) */ -#define OP_OffsetLimit 153 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */ -#define OP_AggInverse 154 /* synopsis: accum=r[P3] inverse(r[P2@P5]) */ -#define OP_AggStep 155 /* synopsis: accum=r[P3] step(r[P2@P5]) */ -#define OP_AggStep1 156 /* synopsis: accum=r[P3] step(r[P2@P5]) */ -#define OP_AggValue 157 /* synopsis: r[P3]=value N=P2 */ -#define OP_AggFinal 158 /* synopsis: accum=r[P1] N=P2 */ -#define OP_Expire 159 -#define OP_TableLock 160 /* synopsis: iDb=P1 root=P2 write=P3 */ -#define OP_VBegin 161 -#define OP_VCreate 162 -#define OP_VDestroy 163 -#define OP_VOpen 164 -#define OP_VColumn 165 /* synopsis: r[P3]=vcolumn(P2) */ -#define OP_VRename 166 -#define OP_Pagecount 167 -#define OP_MaxPgcnt 168 -#define OP_Trace 169 -#define OP_CursorHint 170 -#define OP_Noop 171 -#define OP_Explain 172 -#define OP_Abortable 173 +#define OP_Delete 123 +#define OP_ResetCount 124 +#define OP_SorterCompare 125 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */ +#define OP_SorterData 126 /* synopsis: r[P2]=data */ +#define OP_RowData 127 /* synopsis: r[P2]=data */ +#define OP_Rowid 128 /* synopsis: r[P2]=rowid */ +#define OP_NullRow 129 +#define OP_SeekEnd 130 +#define OP_SorterInsert 131 /* synopsis: key=r[P2] */ +#define OP_IdxInsert 132 /* synopsis: key=r[P2] */ +#define OP_IdxDelete 133 /* synopsis: key=r[P2@P3] */ +#define OP_DeferredSeek 134 /* synopsis: Move P3 to P1.rowid if needed */ +#define OP_IdxRowid 135 /* synopsis: r[P2]=rowid */ +#define OP_Destroy 136 +#define OP_Clear 137 +#define OP_ResetSorter 138 +#define OP_CreateBtree 139 /* synopsis: r[P2]=root iDb=P1 flags=P3 */ +#define OP_SqlExec 140 +#define OP_ParseSchema 141 +#define OP_LoadAnalysis 142 +#define OP_DropTable 143 +#define OP_DropIndex 144 +#define OP_Real 145 /* same as TK_FLOAT, synopsis: r[P2]=P4 */ +#define OP_DropTrigger 146 +#define OP_IntegrityCk 147 +#define OP_RowSetAdd 148 /* synopsis: rowset(P1)=r[P2] */ +#define OP_Param 149 +#define OP_FkCounter 150 /* synopsis: fkctr[P1]+=P2 */ +#define OP_MemMax 151 /* synopsis: r[P1]=max(r[P1],r[P2]) */ +#define OP_OffsetLimit 152 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */ +#define OP_AggInverse 153 /* synopsis: accum=r[P3] inverse(r[P2@P5]) */ +#define OP_AggStep 154 /* synopsis: accum=r[P3] step(r[P2@P5]) */ +#define OP_AggStep1 155 /* synopsis: accum=r[P3] step(r[P2@P5]) */ +#define OP_AggValue 156 /* synopsis: r[P3]=value N=P2 */ +#define OP_AggFinal 157 /* synopsis: accum=r[P1] N=P2 */ +#define OP_Expire 158 +#define OP_TableLock 159 /* synopsis: iDb=P1 root=P2 write=P3 */ +#define OP_VBegin 160 +#define OP_VCreate 161 +#define OP_VDestroy 162 +#define OP_VOpen 163 +#define OP_VColumn 164 /* synopsis: r[P3]=vcolumn(P2) */ +#define OP_VRename 165 +#define OP_Pagecount 166 +#define OP_MaxPgcnt 167 +#define OP_Trace 168 +#define OP_CursorHint 169 +#define OP_Noop 170 +#define OP_Explain 171 +#define OP_Abortable 172 /* Properties such as "out2" or "jump" that are specified in ** comments following the "case" for each opcode in the vdbe.c @@ -14986,17 +15039,17 @@ typedef struct VdbeOpList VdbeOpList; /* 64 */ 0x00, 0x00, 0x02, 0x02, 0x08, 0x00, 0x10, 0x10,\ /* 72 */ 0x10, 0x10, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10,\ /* 80 */ 0x10, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00,\ -/* 88 */ 0x12, 0x20, 0x00, 0x00, 0x26, 0x26, 0x26, 0x26,\ -/* 96 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x00, 0x12,\ -/* 104 */ 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,\ +/* 88 */ 0x12, 0x20, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00,\ +/* 96 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,\ +/* 104 */ 0x26, 0x26, 0x00, 0x12, 0x00, 0x00, 0x10, 0x00,\ /* 112 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ /* 120 */ 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ -/* 128 */ 0x00, 0x10, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00,\ -/* 136 */ 0x10, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00,\ -/* 144 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x10, 0x00,\ -/* 152 */ 0x04, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ -/* 160 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,\ -/* 168 */ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,} +/* 128 */ 0x10, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x10,\ +/* 136 */ 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,\ +/* 144 */ 0x00, 0x10, 0x00, 0x00, 0x06, 0x10, 0x00, 0x04,\ +/* 152 */ 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ +/* 160 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10,\ +/* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00,} /* The sqlite3P2Values() routine is able to run faster if it knows ** the value of the largest JUMP opcode. The smaller the maximum @@ -15055,6 +15108,12 @@ SQLITE_PRIVATE int sqlite3VdbeExplainParent(Parse*); # define ExplainQueryPlan(P) # define ExplainQueryPlanPop(P) # define ExplainQueryPlanParent(P) 0 +# define sqlite3ExplainBreakpoint(A,B) /*no-op*/ +#endif +#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_EXPLAIN) +SQLITE_PRIVATE void sqlite3ExplainBreakpoint(const char*,const char*); +#else +# define sqlite3ExplainBreakpoint(A,B) /*no-op*/ #endif SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*); SQLITE_PRIVATE void sqlite3VdbeChangeOpcode(Vdbe*, u32 addr, u8); @@ -15070,7 +15129,7 @@ SQLITE_PRIVATE void sqlite3VdbeAppendP4(Vdbe*, void *pP4, int p4type); SQLITE_PRIVATE void sqlite3VdbeSetP4KeyInfo(Parse*, Index*); SQLITE_PRIVATE void sqlite3VdbeUsesBtree(Vdbe*, int); SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe*, int); -SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe*); +SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Parse*); SQLITE_PRIVATE void sqlite3VdbeRunOnlyOnce(Vdbe*); SQLITE_PRIVATE void sqlite3VdbeReusable(Vdbe*); SQLITE_PRIVATE void sqlite3VdbeDelete(Vdbe*); @@ -15091,6 +15150,10 @@ SQLITE_PRIVATE void sqlite3VdbeCountChanges(Vdbe*); SQLITE_PRIVATE sqlite3 *sqlite3VdbeDb(Vdbe*); SQLITE_PRIVATE u8 sqlite3VdbePrepareFlags(Vdbe*); SQLITE_PRIVATE void sqlite3VdbeSetSql(Vdbe*, const char *z, int n, u8); +#ifdef SQLITE_ENABLE_NORMALIZE +SQLITE_PRIVATE void sqlite3VdbeAddDblquoteStr(sqlite3*,Vdbe*,const char*); +SQLITE_PRIVATE int sqlite3VdbeUsesDoubleQuotedString(Vdbe*,const char*); +#endif SQLITE_PRIVATE void sqlite3VdbeSwap(Vdbe*,Vdbe*); SQLITE_PRIVATE VdbeOp *sqlite3VdbeTakeOpArray(Vdbe*, int*, int*); SQLITE_PRIVATE sqlite3_value *sqlite3VdbeGetBoundValue(Vdbe*, int, u8); @@ -16216,10 +16279,13 @@ SQLITE_PRIVATE void sqlite3CryptFunc(sqlite3_context*,int,sqlite3_value**); /* This is an extra SQLITE_TRACE macro that indicates "legacy" tracing ** in the style of sqlite3_trace() */ -#define SQLITE_TRACE_LEGACY 0x80 +#define SQLITE_TRACE_LEGACY 0x40 /* Use the legacy xTrace */ +#define SQLITE_TRACE_XPROFILE 0x80 /* Use the legacy xProfile */ #else -#define SQLITE_TRACE_LEGACY 0 +#define SQLITE_TRACE_LEGACY 0 +#define SQLITE_TRACE_XPROFILE 0 #endif /* SQLITE_OMIT_DEPRECATED */ +#define SQLITE_TRACE_NONLEGACY_MASK 0x0f /* Normal flags */ /* @@ -16278,14 +16344,17 @@ struct sqlite3 { void **aExtension; /* Array of shared library handles */ int (*xTrace)(u32,void*,void*,void*); /* Trace function */ void *pTraceArg; /* Argument to the trace function */ +#ifndef SQLITE_OMIT_DEPRECATED void (*xProfile)(void*,const char*,u64); /* Profiling function */ void *pProfileArg; /* Argument to profile function */ +#endif void *pCommitArg; /* Argument to xCommitCallback() */ int (*xCommitCallback)(void*); /* Invoked at every commit. */ void *pRollbackArg; /* Argument to xRollbackCallback() */ void (*xRollbackCallback)(void*); /* Invoked at every commit. */ void *pUpdateArg; void (*xUpdateCallback)(void*,int, const char*,const char*,sqlite_int64); + Parse *pParse; /* Current parse */ #ifdef SQLITE_ENABLE_PREUPDATE_HOOK void *pPreUpdateArg; /* First argument to xPreUpdateCallback */ void (*xPreUpdateCallback)( /* Registered using sqlite3_preupdate_hook() */ @@ -16410,6 +16479,7 @@ struct sqlite3 { #define SQLITE_VdbeTrace HI(0x0004) /* True to trace VDBE execution */ #define SQLITE_VdbeAddopTrace HI(0x0008) /* Trace sqlite3VdbeAddOp() calls */ #define SQLITE_VdbeEQP HI(0x0010) /* Debug EXPLAIN QUERY PLAN */ +#define SQLITE_ParserTrace HI(0x0020) /* PRAGMA parser_trace=ON */ #endif /* @@ -16418,7 +16488,8 @@ struct sqlite3 { #define DBFLAG_SchemaChange 0x0001 /* Uncommitted Hash table changes */ #define DBFLAG_PreferBuiltin 0x0002 /* Preference to built-in funcs */ #define DBFLAG_Vacuum 0x0004 /* Currently in a VACUUM */ -#define DBFLAG_SchemaKnownOk 0x0008 /* Schema is known to be valid */ +#define DBFLAG_VacuumInto 0x0008 /* Currently running VACUUM INTO */ +#define DBFLAG_SchemaKnownOk 0x0010 /* Schema is known to be valid */ /* ** Bits of the sqlite3.dbOptFlags field that are used by the @@ -16426,7 +16497,7 @@ struct sqlite3 { ** selectively disable various optimizations. */ #define SQLITE_QueryFlattener 0x0001 /* Query flattening */ - /* 0x0002 available for reuse */ +#define SQLITE_WindowFunc 0x0002 /* Use xInverse for window functions */ #define SQLITE_GroupByOrder 0x0004 /* GROUPBY cover of ORDERBY */ #define SQLITE_FactorOutConst 0x0008 /* Constant factoring */ #define SQLITE_DistinctOpt 0x0010 /* DISTINCT using indexes */ @@ -16544,7 +16615,6 @@ struct FuncDestructor { #define SQLITE_FUNC_AFFINITY 0x4000 /* Built-in affinity() function */ #define SQLITE_FUNC_OFFSET 0x8000 /* Built-in sqlite_offset() function */ #define SQLITE_FUNC_WINDOW 0x00010000 /* Built-in window-only function */ -#define SQLITE_FUNC_WINDOW_SIZE 0x20000 /* Requires partition size as arg. */ #define SQLITE_FUNC_INTERNAL 0x00040000 /* For use by NestedParse() only */ /* @@ -16812,9 +16882,6 @@ struct VTable { struct Table { char *zName; /* Name of the table or view */ Column *aCol; /* Information about each column */ -#ifdef SQLITE_ENABLE_NORMALIZE - Hash *pColHash; /* All columns indexed by name */ -#endif Index *pIndex; /* List of SQL indexes on this table. */ Select *pSelect; /* NULL for tables. Points to definition if a view. */ FKey *pFKey; /* Linked list of all foreign keys in this table */ @@ -17101,7 +17168,7 @@ struct Index { u16 nKeyCol; /* Number of columns forming the key */ u16 nColumn; /* Number of columns stored in the index */ u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ - unsigned idxType:2; /* 1==UNIQUE, 2==PRIMARY KEY, 0==CREATE INDEX */ + unsigned idxType:2; /* 0:Normal 1:UNIQUE, 2:PRIMARY KEY, 3:IPK */ unsigned bUnordered:1; /* Use this index for == or IN queries only */ unsigned uniqNotNull:1; /* True if UNIQUE and NOT NULL for all columns */ unsigned isResized:1; /* True if resizeIndexObject() has been called */ @@ -17126,6 +17193,7 @@ struct Index { #define SQLITE_IDXTYPE_APPDEF 0 /* Created using CREATE INDEX */ #define SQLITE_IDXTYPE_UNIQUE 1 /* Implements a UNIQUE constraint */ #define SQLITE_IDXTYPE_PRIMARYKEY 2 /* Is the PRIMARY KEY for the table */ +#define SQLITE_IDXTYPE_IPK 3 /* INTEGER PRIMARY KEY index */ /* Return true if index X is a PRIMARY KEY index */ #define IsPrimaryKeyIndex(X) ((X)->idxType==SQLITE_IDXTYPE_PRIMARYKEY) @@ -17343,17 +17411,25 @@ struct Expr { Table *pTab; /* TK_COLUMN: Table containing column. Can be NULL ** for a column of an index on an expression */ Window *pWin; /* TK_FUNCTION: Window definition for the func */ + struct { /* TK_IN, TK_SELECT, and TK_EXISTS */ + int iAddr; /* Subroutine entry address */ + int regReturn; /* Register used to hold return address */ + } sub; } y; }; /* ** The following are the meanings of bits in the Expr.flags field. +** Value restrictions: +** +** EP_Agg == NC_HasAgg == SF_HasAgg +** EP_Win == NC_HasWin */ #define EP_FromJoin 0x000001 /* Originates in ON/USING clause of outer join */ -#define EP_Agg 0x000002 /* Contains one or more aggregate functions */ +#define EP_Distinct 0x000002 /* Aggregate function with DISTINCT keyword */ #define EP_HasFunc 0x000004 /* Contains one or more functions of any kind */ #define EP_FixedCol 0x000008 /* TK_Column with a known fixed value */ -#define EP_Distinct 0x000010 /* Aggregate function with DISTINCT keyword */ +#define EP_Agg 0x000010 /* Contains one or more aggregate functions */ #define EP_VarSelect 0x000020 /* pSelect is correlated, not constant */ #define EP_DblQuoted 0x000040 /* token.z was originally in "..." */ #define EP_InfixFunc 0x000080 /* True for an infix function: LIKE, GLOB, etc */ @@ -17364,7 +17440,7 @@ struct Expr { #define EP_Skip 0x001000 /* COLLATE, AS, or UNLIKELY */ #define EP_Reduced 0x002000 /* Expr struct EXPR_REDUCEDSIZE bytes only */ #define EP_TokenOnly 0x004000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */ -#define EP_Static 0x008000 /* Held in memory not obtained from malloc() */ +#define EP_Win 0x008000 /* Contains window functions */ #define EP_MemToken 0x010000 /* Need to sqlite3DbFree() Expr.zToken */ #define EP_NoReduce 0x020000 /* Cannot EXPRDUP_REDUCE this Expr */ #define EP_Unlikely 0x040000 /* unlikely() or likelihood() function */ @@ -17374,6 +17450,9 @@ struct Expr { #define EP_Alias 0x400000 /* Is an alias for a result set column */ #define EP_Leaf 0x800000 /* Expr.pLeft, .pRight, .u.pSelect all NULL */ #define EP_WinFunc 0x1000000 /* TK_FUNCTION with Expr.y.pWin set */ +#define EP_Subrtn 0x2000000 /* Uses Expr.y.sub. TK_IN, _SELECT, or _EXISTS */ +#define EP_Quoted 0x4000000 /* TK_ID was originally quoted */ +#define EP_Static 0x8000000 /* Held in memory not obtained from malloc() */ /* ** The EP_Propagate mask is a set of properties that automatically propagate @@ -17613,8 +17692,9 @@ struct NameContext { ** Allowed values for the NameContext, ncFlags field. ** ** Value constraints (all checked via assert()): -** NC_HasAgg == SF_HasAgg +** NC_HasAgg == SF_HasAgg == EP_Agg ** NC_MinMaxAgg == SF_MinMaxAgg == SQLITE_FUNC_MINMAX +** NC_HasWin == EP_Win ** */ #define NC_AllowAgg 0x0001 /* Aggregate functions are allowed here */ @@ -17630,6 +17710,7 @@ struct NameContext { #define NC_MinMaxAgg 0x1000 /* min/max aggregates seen. See note above */ #define NC_Complex 0x2000 /* True if a function or subquery seen */ #define NC_AllowWin 0x4000 /* Window functions are allowed here */ +#define NC_HasWin 0x8000 /* One or more window functions seen */ /* ** An instance of the following object describes a single ON CONFLICT @@ -17917,16 +17998,17 @@ struct Parse { u8 hasCompound; /* Need to invoke convertCompoundSelectToSubquery() */ u8 okConstFactor; /* OK to factor out constants */ u8 disableLookaside; /* Number of times lookaside has been disabled */ + u8 disableVtab; /* Disable all virtual tables for this parse */ int nRangeReg; /* Size of the temporary register block */ int iRangeReg; /* First register in temporary register block */ int nErr; /* Number of errors seen */ int nTab; /* Number of previously allocated VDBE cursors */ int nMem; /* Number of memory cells used so far */ - int nOpAlloc; /* Number of slots allocated for Vdbe.aOp[] */ int szOpAlloc; /* Bytes of memory space allocated for Vdbe.aOp[] */ int iSelfTab; /* Table associated with an index on expr, or negative ** of the base register during check-constraint eval */ - int nLabel; /* Number of labels used */ + int nLabel; /* The *negative* of the number of labels used */ + int nLabelAlloc; /* Number of slots in aLabel */ int *aLabel; /* Space to hold the labels */ ExprList *pConstExpr;/* Constant expressions */ Token constraintName;/* Name of the constraint currently being parsed */ @@ -17943,6 +18025,7 @@ struct Parse { AutoincInfo *pAinc; /* Information about AUTOINCREMENT counters */ Parse *pToplevel; /* Parse structure for main program (or NULL) */ Table *pTriggerTab; /* Table triggers are being coded for */ + Parse *pParentParse; /* Parent parser if this parser is nested */ int addrCrTab; /* Address of OP_CreateBtree opcode on CREATE TABLE */ u32 nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */ u32 oldmask; /* Mask of old.* columns referenced */ @@ -17986,7 +18069,9 @@ struct Parse { Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */ const char *zTail; /* All SQL text past the last semicolon parsed */ Table *pNewTable; /* A table being constructed by CREATE TABLE */ - Index *pNewIndex; /* An index being constructed by CREATE INDEX */ + Index *pNewIndex; /* An index being constructed by CREATE INDEX. + ** Also used to hold redundant UNIQUE constraints + ** during a RENAME COLUMN */ Trigger *pNewTrigger; /* Trigger under construct by a CREATE TRIGGER */ const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */ #ifndef SQLITE_OMIT_VIRTUALTABLE @@ -18214,6 +18299,7 @@ typedef struct { int iDb; /* 0 for main database. 1 for TEMP, 2.. for ATTACHed */ int rc; /* Result code stored here */ u32 mInitFlags; /* Flags controlling error messages */ + u32 nInitRow; /* Number of rows processed */ } InitData; /* @@ -18274,6 +18360,9 @@ struct Sqlite3Config { void (*xVdbeBranch)(void*,unsigned iSrcLine,u8 eThis,u8 eMx); /* Callback */ void *pVdbeBranchArg; /* 1st argument */ #endif +#ifdef SQLITE_ENABLE_DESERIALIZE + sqlite3_int64 mxMemdbSize; /* Default max memdb size */ +#endif #ifndef SQLITE_UNTESTABLE int (*xTestCallback)(int); /* Invoked by sqlite3FaultSim() */ #endif @@ -18377,7 +18466,7 @@ struct TreeView { #endif /* SQLITE_DEBUG */ /* -** This object is used in varioius ways, all related to window functions +** This object is used in various ways, all related to window functions ** ** (1) A single instance of this structure is attached to the ** the Expr.pWin field for each window function in an expression tree. @@ -18392,15 +18481,18 @@ struct TreeView { ** object on a linked list attached to Select.pWinDefn. ** ** The uses (1) and (2) are really the same Window object that just happens -** to be accessible in two different ways. Use (3) is are separate objects. +** to be accessible in two different ways. Use case (3) are separate objects. */ struct Window { char *zName; /* Name of window (may be NULL) */ + char *zBase; /* Name of base window for chaining (may be NULL) */ ExprList *pPartition; /* PARTITION BY clause */ ExprList *pOrderBy; /* ORDER BY clause */ - u8 eType; /* TK_RANGE or TK_ROWS */ + u8 eFrmType; /* TK_RANGE, TK_GROUPS, TK_ROWS, or 0 */ u8 eStart; /* UNBOUNDED, CURRENT, PRECEDING or FOLLOWING */ u8 eEnd; /* UNBOUNDED, CURRENT, PRECEDING or FOLLOWING */ + u8 bImplicitFrame; /* True if frame was implicitly specified */ + u8 eExclude; /* TK_NO, TK_CURRENT, TK_TIES, TK_GROUP, or 0 */ Expr *pStart; /* Expression for " PRECEDING" */ Expr *pEnd; /* Expression for " FOLLOWING" */ Window *pNextWin; /* Next window function belonging to this SELECT */ @@ -18411,17 +18503,19 @@ struct Window { int regResult; int csrApp; /* Function cursor (used by min/max) */ int regApp; /* Function register (also used by min/max) */ - int regPart; /* First in a set of registers holding PARTITION BY - ** and ORDER BY values for the window */ + int regPart; /* Array of registers for PARTITION BY values */ Expr *pOwner; /* Expression object this window is attached to */ int nBufferCol; /* Number of columns in buffer table */ int iArgCol; /* Offset of first argument for this function */ + int regOne; /* Register containing constant value 1 */ + int regStartRowid; + int regEndRowid; }; #ifndef SQLITE_OMIT_WINDOWFUNC SQLITE_PRIVATE void sqlite3WindowDelete(sqlite3*, Window*); SQLITE_PRIVATE void sqlite3WindowListDelete(sqlite3 *db, Window *p); -SQLITE_PRIVATE Window *sqlite3WindowAlloc(Parse*, int, int, Expr*, int , Expr*); +SQLITE_PRIVATE Window *sqlite3WindowAlloc(Parse*, int, int, Expr*, int , Expr*, u8); SQLITE_PRIVATE void sqlite3WindowAttach(Parse*, Expr*, Window*); SQLITE_PRIVATE int sqlite3WindowCompare(Parse*, Window*, Window*); SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse*, Window*); @@ -18432,6 +18526,8 @@ SQLITE_PRIVATE void sqlite3WindowUpdate(Parse*, Window*, Window*, FuncDef*); SQLITE_PRIVATE Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p); SQLITE_PRIVATE Window *sqlite3WindowListDup(sqlite3 *db, Window *p); SQLITE_PRIVATE void sqlite3WindowFunctions(void); +SQLITE_PRIVATE void sqlite3WindowChain(Parse*, Window*, Window*); +SQLITE_PRIVATE Window *sqlite3WindowAssemble(Parse*, Window*, ExprList*, ExprList*, Token*); #else # define sqlite3WindowDelete(a,b) # define sqlite3WindowFunctions() @@ -18661,7 +18757,9 @@ SQLITE_PRIVATE void sqlite3TreeViewWinFunc(TreeView*, const Window*, u8); SQLITE_PRIVATE void sqlite3SetString(char **, sqlite3*, const char*); SQLITE_PRIVATE void sqlite3ErrorMsg(Parse*, const char*, ...); +SQLITE_PRIVATE int sqlite3ErrorToParser(sqlite3*,int); SQLITE_PRIVATE void sqlite3Dequote(char*); +SQLITE_PRIVATE void sqlite3DequoteExpr(Expr*); SQLITE_PRIVATE void sqlite3TokenInit(Token*,char*); SQLITE_PRIVATE int sqlite3KeywordCode(const unsigned char*, int); SQLITE_PRIVATE int sqlite3RunParser(Parse*, const char*, char **); @@ -18690,6 +18788,7 @@ SQLITE_PRIVATE void sqlite3ExprListSetName(Parse*,ExprList*,Token*,int); SQLITE_PRIVATE void sqlite3ExprListSetSpan(Parse*,ExprList*,const char*,const char*); SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3*, ExprList*); SQLITE_PRIVATE u32 sqlite3ExprListFlags(const ExprList*); +SQLITE_PRIVATE int sqlite3IndexHasDuplicateRootPage(Index*); SQLITE_PRIVATE int sqlite3Init(sqlite3*, char**); SQLITE_PRIVATE int sqlite3InitCallback(void*, int, char**, char**); SQLITE_PRIVATE int sqlite3InitOne(sqlite3*, int, char**, u32); @@ -18723,6 +18822,11 @@ SQLITE_PRIVATE void sqlite3AddCollateType(Parse*, Token*); SQLITE_PRIVATE void sqlite3EndTable(Parse*,Token*,Token*,u8,Select*); SQLITE_PRIVATE int sqlite3ParseUri(const char*,const char*,unsigned int*, sqlite3_vfs**,char**,char **); +#ifdef SQLITE_HAS_CODEC +SQLITE_PRIVATE int sqlite3CodecQueryParameters(sqlite3*,const char*,const char*); +#else +# define sqlite3CodecQueryParameters(A,B,C) 0 +#endif SQLITE_PRIVATE Btree *sqlite3DbNameToBtree(sqlite3*,const char*); #ifdef SQLITE_UNTESTABLE @@ -18775,8 +18879,8 @@ SQLITE_PRIVATE void sqlite3Insert(Parse*, SrcList*, Select*, IdList*, int, Upser SQLITE_PRIVATE void *sqlite3ArrayAllocate(sqlite3*,void*,int,int*,int*); SQLITE_PRIVATE IdList *sqlite3IdListAppend(Parse*, IdList*, Token*); SQLITE_PRIVATE int sqlite3IdListIndex(IdList*,const char*); -SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge(sqlite3*, SrcList*, int, int); -SQLITE_PRIVATE SrcList *sqlite3SrcListAppend(sqlite3*, SrcList*, Token*, Token*); +SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge(Parse*, SrcList*, int, int); +SQLITE_PRIVATE SrcList *sqlite3SrcListAppend(Parse*, SrcList*, Token*, Token*); SQLITE_PRIVATE SrcList *sqlite3SrcListAppendFromTerm(Parse*, SrcList*, Token*, Token*, Token*, Select*, Expr*, IdList*); SQLITE_PRIVATE void sqlite3SrcListIndexedBy(Parse *, SrcList *, Token *); @@ -18843,8 +18947,8 @@ SQLITE_PRIVATE Table *sqlite3LocateTableItem(Parse*,u32 flags,struct SrcList_ite SQLITE_PRIVATE Index *sqlite3FindIndex(sqlite3*,const char*, const char*); SQLITE_PRIVATE void sqlite3UnlinkAndDeleteTable(sqlite3*,int,const char*); SQLITE_PRIVATE void sqlite3UnlinkAndDeleteIndex(sqlite3*,int,const char*); -SQLITE_PRIVATE void sqlite3Vacuum(Parse*,Token*); -SQLITE_PRIVATE int sqlite3RunVacuum(char**, sqlite3*, int); +SQLITE_PRIVATE void sqlite3Vacuum(Parse*,Token*,Expr*); +SQLITE_PRIVATE int sqlite3RunVacuum(char**, sqlite3*, int, sqlite3_value*); SQLITE_PRIVATE char *sqlite3NameFromToken(sqlite3*, Token*); SQLITE_PRIVATE int sqlite3ExprCompare(Parse*,Expr*, Expr*, int); SQLITE_PRIVATE int sqlite3ExprCompareSkip(Expr*, Expr*, int); @@ -18882,9 +18986,6 @@ SQLITE_PRIVATE int sqlite3ExprIsInteger(Expr*, int*); SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr*); SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr*, char); SQLITE_PRIVATE int sqlite3IsRowid(const char*); -#ifdef SQLITE_ENABLE_NORMALIZE -SQLITE_PRIVATE int sqlite3IsRowidN(const char*, int); -#endif SQLITE_PRIVATE void sqlite3GenerateRowDelete( Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8,int); SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*, int); @@ -18911,9 +19012,7 @@ SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3*,ExprList*,int); SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3*,SrcList*,int); SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3*,IdList*); SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3*,Select*,int); -#ifdef SQLITE_ENABLE_NORMALIZE -SQLITE_PRIVATE FuncDef *sqlite3FunctionSearchN(int,const char*,int); -#endif +SQLITE_PRIVATE FuncDef *sqlite3FunctionSearch(int,const char*); SQLITE_PRIVATE void sqlite3InsertBuiltinFuncs(FuncDef*,int); SQLITE_PRIVATE FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,u8,u8); SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void); @@ -19118,19 +19217,17 @@ SQLITE_PRIVATE void sqlite3AlterFunctions(void); SQLITE_PRIVATE void sqlite3AlterRenameTable(Parse*, SrcList*, Token*); SQLITE_PRIVATE void sqlite3AlterRenameColumn(Parse*, SrcList*, Token*, Token*); SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *, int *); -#ifdef SQLITE_ENABLE_NORMALIZE -SQLITE_PRIVATE int sqlite3GetTokenNormalized(const unsigned char *, int *, int *); -#endif SQLITE_PRIVATE void sqlite3NestedParse(Parse*, const char*, ...); SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3*, int); -SQLITE_PRIVATE int sqlite3CodeSubselect(Parse*, Expr *, int, int); +SQLITE_PRIVATE void sqlite3CodeRhsOfIN(Parse*, Expr*, int); +SQLITE_PRIVATE int sqlite3CodeSubselect(Parse*, Expr*); SQLITE_PRIVATE void sqlite3SelectPrep(Parse*, Select*, NameContext*); SQLITE_PRIVATE void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p); SQLITE_PRIVATE int sqlite3MatchSpanName(const char*, const char*, const char*, const char*); SQLITE_PRIVATE int sqlite3ResolveExprNames(NameContext*, Expr*); SQLITE_PRIVATE int sqlite3ResolveExprListNames(NameContext*, ExprList*); SQLITE_PRIVATE void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*); -SQLITE_PRIVATE void sqlite3ResolveSelfReference(Parse*,Table*,int,Expr*,ExprList*); +SQLITE_PRIVATE int sqlite3ResolveSelfReference(Parse*,Table*,int,Expr*,ExprList*); SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*); SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *, Table *, int, int); SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *, Token *); @@ -19279,7 +19376,7 @@ SQLITE_PRIVATE int sqlite3VdbeParameterIndex(Vdbe*, const char*, int); SQLITE_PRIVATE int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *); SQLITE_PRIVATE void sqlite3ParserReset(Parse*); #ifdef SQLITE_ENABLE_NORMALIZE -SQLITE_PRIVATE void sqlite3Normalize(Vdbe*, const char*, int, u8); +SQLITE_PRIVATE char *sqlite3Normalize(Vdbe*, const char*); #endif SQLITE_PRIVATE int sqlite3Reprepare(Vdbe*); SQLITE_PRIVATE void sqlite3ExprListCheckLength(Parse*, ExprList*, const char*); @@ -19375,7 +19472,7 @@ SQLITE_PRIVATE void sqlite3EndBenignMalloc(void); #define IN_INDEX_NOOP_OK 0x0001 /* OK to return IN_INDEX_NOOP */ #define IN_INDEX_MEMBERSHIP 0x0002 /* IN operator used for membership test */ #define IN_INDEX_LOOP 0x0004 /* IN operator used as a loop */ -SQLITE_PRIVATE int sqlite3FindInIndex(Parse *, Expr *, u32, int*, int*); +SQLITE_PRIVATE int sqlite3FindInIndex(Parse *, Expr *, u32, int*, int*, int*); SQLITE_PRIVATE int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int); SQLITE_PRIVATE int sqlite3JournalSize(sqlite3_vfs *); @@ -19691,6 +19788,13 @@ SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[256] = { #endif +/* The default maximum size of an in-memory database created using +** sqlite3_deserialize() +*/ +#ifndef SQLITE_MEMDB_DEFAULT_MAXSIZE +# define SQLITE_MEMDB_DEFAULT_MAXSIZE 1073741824 +#endif + /* ** The following singleton contains the global configuration for ** the SQLite library. @@ -19738,13 +19842,16 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = { 0, /* xVdbeBranch */ 0, /* pVbeBranchArg */ #endif +#ifdef SQLITE_ENABLE_DESERIALIZE + SQLITE_MEMDB_DEFAULT_MAXSIZE, /* mxMemdbSize */ +#endif #ifndef SQLITE_UNTESTABLE 0, /* xTestCallback */ #endif 0, /* bLocaltimeFault */ 0, /* bInternalFunctions */ 0x7ffffffe, /* iOnceResetThreshold */ - SQLITE_DEFAULT_SORTERREF_SIZE /* szSorterRef */ + SQLITE_DEFAULT_SORTERREF_SIZE, /* szSorterRef */ }; /* @@ -20074,11 +20181,11 @@ struct sqlite3_value { #define MEM_Real 0x0008 /* Value is a real number */ #define MEM_Blob 0x0010 /* Value is a BLOB */ #define MEM_AffMask 0x001f /* Mask of affinity bits */ -/* Available 0x0020 */ +#define MEM_FromBind 0x0020 /* Value originates from sqlite3_bind() */ /* Available 0x0040 */ #define MEM_Undefined 0x0080 /* Value is undefined */ #define MEM_Cleared 0x0100 /* NULL set by OP_Null, not from data */ -#define MEM_TypeMask 0xc1ff /* Mask of type bits */ +#define MEM_TypeMask 0xc1df /* Mask of type bits */ /* Whenever Mem contains a valid string or blob representation, one of @@ -20110,6 +20217,12 @@ struct sqlite3_value { #define MemSetTypeFlag(p, f) \ ((p)->flags = ((p)->flags&~(MEM_TypeMask|MEM_Zero))|f) +/* +** True if Mem X is a NULL-nochng type. +*/ +#define MemNullNochng(X) \ + ((X)->flags==(MEM_Null|MEM_Zero) && (X)->n==0 && (X)->u.nZero==0) + /* ** Return true if a memory cell is not marked as invalid. This macro ** is for use inside assert() statements only. @@ -20163,6 +20276,9 @@ struct sqlite3_context { */ typedef unsigned bft; /* Bit Field Type */ +/* The ScanStatus object holds a single value for the +** sqlite3_stmt_scanstatus() interface. +*/ typedef struct ScanStatus ScanStatus; struct ScanStatus { int addrExplain; /* OP_Explain for loop */ @@ -20173,6 +20289,19 @@ struct ScanStatus { char *zName; /* Name of table or index */ }; +/* The DblquoteStr object holds the text of a double-quoted +** string for a prepared statement. A linked list of these objects +** is constructed during statement parsing and is held on Vdbe.pDblStr. +** When computing a normalized SQL statement for an SQL statement, that +** list is consulted for each double-quoted identifier to see if the +** identifier should really be a string literal. +*/ +typedef struct DblquoteStr DblquoteStr; +struct DblquoteStr { + DblquoteStr *pNextStr; /* Next string literal in the list */ + char z[8]; /* Dequoted value for the string */ +}; + /* ** An instance of the virtual machine. This structure contains the complete ** state of the virtual machine. @@ -20192,28 +20321,29 @@ struct Vdbe { int pc; /* The program counter */ int rc; /* Value to return */ int nChange; /* Number of db changes made since last reset */ - int iStatement; /* Statement number (or 0 if has not opened stmt) */ + int iStatement; /* Statement number (or 0 if has no opened stmt) */ i64 iCurrentTime; /* Value of julianday('now') for this statement */ i64 nFkConstraint; /* Number of imm. FK constraints this VM */ i64 nStmtDefCons; /* Number of def. constraints when stmt started */ i64 nStmtDefImmCons; /* Number of def. imm constraints when stmt started */ + Mem *aMem; /* The memory locations */ + Mem **apArg; /* Arguments to currently executing user function */ + VdbeCursor **apCsr; /* One element of this array for each open cursor */ + Mem *aVar; /* Values for the OP_Variable opcode. */ /* When allocating a new Vdbe object, all of the fields below should be ** initialized to zero or NULL */ Op *aOp; /* Space to hold the virtual machine's program */ - Mem *aMem; /* The memory locations */ - Mem **apArg; /* Arguments to currently executing user function */ + int nOp; /* Number of instructions in the program */ + int nOpAlloc; /* Slots allocated for aOp[] */ Mem *aColName; /* Column names to return */ Mem *pResultSet; /* Pointer to an array of results */ char *zErrMsg; /* Error message written here */ - VdbeCursor **apCsr; /* One element of this array for each open cursor */ - Mem *aVar; /* Values for the OP_Variable opcode. */ VList *pVList; /* Name of variables */ #ifndef SQLITE_OMIT_TRACE i64 startTime; /* Time when query started - used for profiling */ #endif - int nOp; /* Number of instructions in the program */ #ifdef SQLITE_DEBUG int rcApp; /* errcode set by sqlite3_result_error_code() */ u32 nWrite; /* Number of write operations that have occurred */ @@ -20236,6 +20366,7 @@ struct Vdbe { char *zSql; /* Text of the SQL statement that generated this */ #ifdef SQLITE_ENABLE_NORMALIZE char *zNormSql; /* Normalization of the associated SQL statement */ + DblquoteStr *pDblStr; /* List of double-quoted string literals */ #endif void *pFree; /* Free this when deleting the vdbe */ VdbeFrame *pFrame; /* Parent frame */ @@ -27041,6 +27172,9 @@ SQLITE_PRIVATE void sqlite3OomFault(sqlite3 *db){ db->u1.isInterrupted = 1; } db->lookaside.bDisable++; + if( db->pParse ){ + db->pParse->rc = SQLITE_NOMEM_BKPT; + } } } @@ -27234,7 +27368,8 @@ static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){ static void setStrAccumError(StrAccum *p, u8 eError){ assert( eError==SQLITE_NOMEM || eError==SQLITE_TOOBIG ); p->accError = eError; - p->nAlloc = 0; + if( p->mxAlloc ) sqlite3_str_reset(p); + if( eError==SQLITE_TOOBIG ) sqlite3ErrorToParser(p->db, eError); } /* @@ -27253,6 +27388,28 @@ static char *getTextArg(PrintfArguments *p){ return (char*)sqlite3_value_text(p->apArg[p->nUsed++]); } +/* +** Allocate memory for a temporary buffer needed for printf rendering. +** +** If the requested size of the temp buffer is larger than the size +** of the output buffer in pAccum, then cause an SQLITE_TOOBIG error. +** Do the size check before the memory allocation to prevent rogue +** SQL from requesting large allocations using the precision or width +** field of the printf() function. +*/ +static char *printfTempBuf(sqlite3_str *pAccum, sqlite3_int64 n){ + char *z; + if( pAccum->accError ) return 0; + if( n>pAccum->nAlloc && n>pAccum->mxAlloc ){ + setStrAccumError(pAccum, SQLITE_TOOBIG); + return 0; + } + z = sqlite3DbMallocRaw(pAccum->db, n); + if( z==0 ){ + setStrAccumError(pAccum, SQLITE_NOMEM); + } + return z; +} /* ** On machines with a small stack size, you can redefine the @@ -27335,6 +27492,9 @@ SQLITE_API void sqlite3_str_vappendf( flag_leftjustify = flag_prefix = cThousand = flag_alternateform = flag_altform2 = flag_zeropad = 0; done = 0; + width = 0; + flag_long = 0; + precision = -1; do{ switch( c ){ case '-': flag_leftjustify = 1; break; @@ -27345,80 +27505,93 @@ SQLITE_API void sqlite3_str_vappendf( case '0': flag_zeropad = 1; break; case ',': cThousand = ','; break; default: done = 1; break; - } - }while( !done && (c=(*++fmt))!=0 ); - /* Get the field width */ - if( c=='*' ){ - if( bArgList ){ - width = (int)getIntArg(pArgList); - }else{ - width = va_arg(ap,int); - } - if( width<0 ){ - flag_leftjustify = 1; - width = width >= -2147483647 ? -width : 0; - } - c = *++fmt; - }else{ - unsigned wx = 0; - while( c>='0' && c<='9' ){ - wx = wx*10 + c - '0'; - c = *++fmt; - } - testcase( wx>0x7fffffff ); - width = wx & 0x7fffffff; - } - assert( width>=0 ); + case 'l': { + flag_long = 1; + c = *++fmt; + if( c=='l' ){ + c = *++fmt; + flag_long = 2; + } + done = 1; + break; + } + case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': { + unsigned wx = c - '0'; + while( (c = *++fmt)>='0' && c<='9' ){ + wx = wx*10 + c - '0'; + } + testcase( wx>0x7fffffff ); + width = wx & 0x7fffffff; #ifdef SQLITE_PRINTF_PRECISION_LIMIT - if( width>SQLITE_PRINTF_PRECISION_LIMIT ){ - width = SQLITE_PRINTF_PRECISION_LIMIT; - } + if( width>SQLITE_PRINTF_PRECISION_LIMIT ){ + width = SQLITE_PRINTF_PRECISION_LIMIT; + } #endif - - /* Get the precision */ - if( c=='.' ){ - c = *++fmt; - if( c=='*' ){ - if( bArgList ){ - precision = (int)getIntArg(pArgList); - }else{ - precision = va_arg(ap,int); + if( c!='.' && c!='l' ){ + done = 1; + }else{ + fmt--; + } + break; } - c = *++fmt; - if( precision<0 ){ - precision = precision >= -2147483647 ? -precision : -1; + case '*': { + if( bArgList ){ + width = (int)getIntArg(pArgList); + }else{ + width = va_arg(ap,int); + } + if( width<0 ){ + flag_leftjustify = 1; + width = width >= -2147483647 ? -width : 0; + } +#ifdef SQLITE_PRINTF_PRECISION_LIMIT + if( width>SQLITE_PRINTF_PRECISION_LIMIT ){ + width = SQLITE_PRINTF_PRECISION_LIMIT; + } +#endif + if( (c = fmt[1])!='.' && c!='l' ){ + c = *++fmt; + done = 1; + } + break; } - }else{ - unsigned px = 0; - while( c>='0' && c<='9' ){ - px = px*10 + c - '0'; + case '.': { c = *++fmt; - } - testcase( px>0x7fffffff ); - precision = px & 0x7fffffff; - } - }else{ - precision = -1; - } - assert( precision>=(-1) ); + if( c=='*' ){ + if( bArgList ){ + precision = (int)getIntArg(pArgList); + }else{ + precision = va_arg(ap,int); + } + if( precision<0 ){ + precision = precision >= -2147483647 ? -precision : -1; + } + c = *++fmt; + }else{ + unsigned px = 0; + while( c>='0' && c<='9' ){ + px = px*10 + c - '0'; + c = *++fmt; + } + testcase( px>0x7fffffff ); + precision = px & 0x7fffffff; + } #ifdef SQLITE_PRINTF_PRECISION_LIMIT - if( precision>SQLITE_PRINTF_PRECISION_LIMIT ){ - precision = SQLITE_PRINTF_PRECISION_LIMIT; - } + if( precision>SQLITE_PRINTF_PRECISION_LIMIT ){ + precision = SQLITE_PRINTF_PRECISION_LIMIT; + } #endif - - - /* Get the conversion type modifier */ - if( c=='l' ){ - flag_long = 1; - c = *++fmt; - if( c=='l' ){ - flag_long = 2; - c = *++fmt; + if( c=='l' ){ + --fmt; + }else{ + done = 1; + } + break; + } } - }else{ - flag_long = 0; - } + }while( !done && (c=(*++fmt))!=0 ); + /* Fetch the info entry for the field */ infop = &fmtinfo[0]; xtype = etINVALID; @@ -27503,12 +27676,11 @@ SQLITE_API void sqlite3_str_vappendf( nOut = etBUFSIZE; zOut = buf; }else{ - u64 n = (u64)precision + 10 + precision/3; - zOut = zExtra = sqlite3Malloc( n ); - if( zOut==0 ){ - setStrAccumError(pAccum, SQLITE_NOMEM); - return; - } + u64 n; + n = (u64)precision + 10; + if( cThousand ) n += precision/3; + zOut = zExtra = printfTempBuf(pAccum, n); + if( zOut==0 ) return; nOut = (int)n; } bufpt = &zOut[nOut-1]; @@ -27627,12 +27799,12 @@ SQLITE_API void sqlite3_str_vappendf( }else{ e2 = exp; } - if( MAX(e2,0)+(i64)precision+(i64)width > etBUFSIZE - 15 ){ - bufpt = zExtra - = sqlite3Malloc( MAX(e2,0)+(i64)precision+(i64)width+15 ); - if( bufpt==0 ){ - setStrAccumError(pAccum, SQLITE_NOMEM); - return; + { + i64 szBufNeeded; /* Size of a temporary buffer needed */ + szBufNeeded = MAX(e2,0)+(i64)precision+(i64)width+15; + if( szBufNeeded > etBUFSIZE ){ + bufpt = zExtra = printfTempBuf(pAccum, szBufNeeded); + if( bufpt==0 ) return; } } zOut = bufpt; @@ -27856,11 +28028,8 @@ SQLITE_API void sqlite3_str_vappendf( needQuote = !isnull && xtype==etSQLESCAPE2; n += i + 3; if( n>etBUFSIZE ){ - bufpt = zExtra = sqlite3Malloc( n ); - if( bufpt==0 ){ - setStrAccumError(pAccum, SQLITE_NOMEM); - return; - } + bufpt = zExtra = printfTempBuf(pAccum, n); + if( bufpt==0 ) return; }else{ bufpt = buf; } @@ -27950,9 +28119,8 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){ return 0; } if( p->mxAlloc==0 ){ - N = p->nAlloc - p->nChar - 1; setStrAccumError(p, SQLITE_TOOBIG); - return N; + return p->nAlloc - p->nChar - 1; }else{ char *zOld = isMalloced(p) ? p->zText : 0; i64 szNew = p->nChar; @@ -28024,7 +28192,7 @@ SQLITE_API void sqlite3_str_append(sqlite3_str *p, const char *z, int N){ assert( z!=0 || N==0 ); assert( p->zText!=0 || p->nChar==0 || p->accError ); assert( N>=0 ); - assert( p->accError==0 || p->nAlloc==0 ); + assert( p->accError==0 || p->nAlloc==0 || p->mxAlloc==0 ); if( p->nChar+N >= p->nAlloc ){ enlargeAndAppend(p,z,N); }else if( N ){ @@ -28486,7 +28654,8 @@ SQLITE_PRIVATE void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc) sqlite3_str_appendf(&x, " %s", pItem->zName); } if( pItem->pTab ){ - sqlite3_str_appendf(&x, " tabname=%Q", pItem->pTab->zName); + sqlite3_str_appendf(&x, " tab=%Q nCol=%d ptr=%p", + pItem->pTab->zName, pItem->pTab->nCol, pItem->pTab); } if( pItem->zAlias ){ sqlite3_str_appendf(&x, " (AS %s)", pItem->zAlias); @@ -28656,24 +28825,62 @@ SQLITE_PRIVATE void sqlite3TreeViewBound( ** Generate a human-readable explanation for a Window object */ SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u8 more){ + int nElement = 0; + if( pWin->pFilter ){ + sqlite3TreeViewItem(pView, "FILTER", 1); + sqlite3TreeViewExpr(pView, pWin->pFilter, 0); + sqlite3TreeViewPop(pView); + } pView = sqlite3TreeViewPush(pView, more); if( pWin->zName ){ - sqlite3TreeViewLine(pView, "OVER %s", pWin->zName); + sqlite3TreeViewLine(pView, "OVER %s (%p)", pWin->zName, pWin); }else{ - sqlite3TreeViewLine(pView, "OVER"); + sqlite3TreeViewLine(pView, "OVER (%p)", pWin); + } + if( pWin->zBase ) nElement++; + if( pWin->pOrderBy ) nElement++; + if( pWin->eFrmType ) nElement++; + if( pWin->eExclude ) nElement++; + if( pWin->zBase ){ + sqlite3TreeViewPush(pView, (--nElement)>0); + sqlite3TreeViewLine(pView, "window: %s", pWin->zBase); + sqlite3TreeViewPop(pView); } if( pWin->pPartition ){ - sqlite3TreeViewExprList(pView, pWin->pPartition, 1, "PARTITION-BY"); + sqlite3TreeViewExprList(pView, pWin->pPartition, nElement>0,"PARTITION-BY"); } if( pWin->pOrderBy ){ - sqlite3TreeViewExprList(pView, pWin->pOrderBy, 1, "ORDER-BY"); - } - if( pWin->eType ){ - sqlite3TreeViewItem(pView, pWin->eType==TK_RANGE ? "RANGE" : "ROWS", 0); + sqlite3TreeViewExprList(pView, pWin->pOrderBy, (--nElement)>0, "ORDER-BY"); + } + if( pWin->eFrmType ){ + char zBuf[30]; + const char *zFrmType = "ROWS"; + if( pWin->eFrmType==TK_RANGE ) zFrmType = "RANGE"; + if( pWin->eFrmType==TK_GROUPS ) zFrmType = "GROUPS"; + sqlite3_snprintf(sizeof(zBuf),zBuf,"%s%s",zFrmType, + pWin->bImplicitFrame ? " (implied)" : ""); + sqlite3TreeViewItem(pView, zBuf, (--nElement)>0); sqlite3TreeViewBound(pView, pWin->eStart, pWin->pStart, 1); sqlite3TreeViewBound(pView, pWin->eEnd, pWin->pEnd, 0); sqlite3TreeViewPop(pView); } + if( pWin->eExclude ){ + char zBuf[30]; + const char *zExclude; + switch( pWin->eExclude ){ + case TK_NO: zExclude = "NO OTHERS"; break; + case TK_CURRENT: zExclude = "CURRENT ROW"; break; + case TK_GROUP: zExclude = "GROUP"; break; + case TK_TIES: zExclude = "TIES"; break; + default: + sqlite3_snprintf(sizeof(zBuf),zBuf,"invalid(%d)", pWin->eExclude); + zExclude = zBuf; + break; + } + sqlite3TreeViewPush(pView, 0); + sqlite3TreeViewLine(pView, "EXCLUDE %s", zExclude); + sqlite3TreeViewPop(pView); + } sqlite3TreeViewPop(pView); } #endif /* SQLITE_OMIT_WINDOWFUNC */ @@ -29653,11 +29860,11 @@ SQLITE_PRIVATE u32 sqlite3Utf8Read( ** encoding, or if *pMem does not contain a string value. */ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){ - int len; /* Maximum length of output string in bytes */ - unsigned char *zOut; /* Output buffer */ - unsigned char *zIn; /* Input iterator */ - unsigned char *zTerm; /* End of input */ - unsigned char *z; /* Output iterator */ + sqlite3_int64 len; /* Maximum length of output string in bytes */ + unsigned char *zOut; /* Output buffer */ + unsigned char *zIn; /* Input iterator */ + unsigned char *zTerm; /* End of input */ + unsigned char *z; /* Output iterator */ unsigned int c; assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); @@ -29706,14 +29913,14 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desired ** nul-terminator. */ pMem->n &= ~1; - len = pMem->n * 2 + 1; + len = 2 * (sqlite3_int64)pMem->n + 1; }else{ /* When converting from UTF-8 to UTF-16 the maximum growth is caused ** when a 1-byte UTF-8 character is translated into a 2-byte UTF-16 ** character. Two bytes are required in the output buffer for the ** nul-terminator. */ - len = pMem->n * 2 + 2; + len = 2 * (sqlite3_int64)pMem->n + 2; } /* Set zIn to point at the start of the input buffer and zTerm to point 1 @@ -30020,15 +30227,23 @@ SQLITE_PRIVATE void sqlite3Coverage(int x){ #endif /* -** Give a callback to the test harness that can be used to simulate faults -** in places where it is difficult or expensive to do so purely by means -** of inputs. +** Calls to sqlite3FaultSim() are used to simulate a failure during testing, +** or to bypass normal error detection during testing in order to let +** execute proceed futher downstream. +** +** In deployment, sqlite3FaultSim() *always* return SQLITE_OK (0). The +** sqlite3FaultSim() function only returns non-zero during testing. ** -** The intent of the integer argument is to let the fault simulator know -** which of multiple sqlite3FaultSim() calls has been hit. +** During testing, if the test harness has set a fault-sim callback using +** a call to sqlite3_test_control(SQLITE_TESTCTRL_FAULT_INSTALL), then +** each call to sqlite3FaultSim() is relayed to that application-supplied +** callback and the integer return value form the application-supplied +** callback is returned by sqlite3FaultSim(). ** -** Return whatever integer value the test callback returns, or return -** SQLITE_OK if no test callback is installed. +** The integer argument to sqlite3FaultSim() is a code to identify which +** sqlite3FaultSim() instance is being invoked. Each call to sqlite3FaultSim() +** should have a unique code. To prevent legacy testing applications from +** breaking, the codes should not be changed or reused. */ #ifndef SQLITE_UNTESTABLE SQLITE_PRIVATE int sqlite3FaultSim(int iTest){ @@ -30213,6 +30428,19 @@ SQLITE_PRIVATE void sqlite3ErrorMsg(Parse *pParse, const char *zFormat, ...){ } } +/* +** If database connection db is currently parsing SQL, then transfer +** error code errCode to that parser if the parser has not already +** encountered some other kind of error. +*/ +SQLITE_PRIVATE int sqlite3ErrorToParser(sqlite3 *db, int errCode){ + Parse *pParse; + if( db==0 || (pParse = db->pParse)==0 ) return errCode; + pParse->rc = errCode; + pParse->nErr++; + return errCode; +} + /* ** Convert an SQL-style quoted string into a normal string by removing ** the quote characters. The conversion is done in-place. If the @@ -30226,7 +30454,7 @@ SQLITE_PRIVATE void sqlite3ErrorMsg(Parse *pParse, const char *zFormat, ...){ ** dequoted string, exclusive of the zero terminator, if dequoting does ** occur. ** -** 2002-Feb-14: This routine is extended to remove MS-Access style +** 2002-02-14: This routine is extended to remove MS-Access style ** brackets from around identifiers. For example: "[a-b-c]" becomes ** "a-b-c". */ @@ -30252,6 +30480,11 @@ SQLITE_PRIVATE void sqlite3Dequote(char *z){ } z[j] = 0; } +SQLITE_PRIVATE void sqlite3DequoteExpr(Expr *p){ + assert( sqlite3Isquote(p->u.zToken[0]) ); + p->flags |= p->u.zToken[0]=='"' ? EP_Quoted|EP_DblQuoted : EP_Quoted; + sqlite3Dequote(p->u.zToken); +} /* ** Generate a Token object from a string @@ -31559,7 +31792,7 @@ SQLITE_PRIVATE VList *sqlite3VListAdd( assert( pIn==0 || pIn[0]>=3 ); /* Verify ok to add new elements */ if( pIn==0 || pIn[1]+nInt > pIn[0] ){ /* Enlarge the allocation */ - int nAlloc = (pIn ? pIn[0]*2 : 10) + nInt; + sqlite3_int64 nAlloc = (pIn ? 2*(sqlite3_int64)pIn[0] : 10) + nInt; VList *pOut = sqlite3DbRealloc(db, pIn, nAlloc*sizeof(int)); if( pOut==0 ) return pIn; if( pIn==0 ) pOut[1] = 2; @@ -31679,20 +31912,6 @@ static unsigned int strHash(const char *z){ } return h; } -#ifdef SQLITE_ENABLE_NORMALIZE -static unsigned int strHashN(const char *z, int n){ - unsigned int h = 0; - int i; - for(i=0; iht ){ /*OPTIMIZATION-IF-TRUE*/ - struct _ht *pEntry; - h = strHashN(pKey, nKey) % pH->htsize; - pEntry = &pH->ht[h]; - elem = pEntry->chain; - count = pEntry->count; - }else{ - h = 0; - elem = pH->first; - count = pH->count; - } - if( pHash ) *pHash = h; - while( count-- ){ - assert( elem!=0 ); - if( sqlite3StrNICmp(elem->pKey,pKey,nKey)==0 ){ - return elem; - } - elem = elem->next; - } - return &nullElement; -} -#endif /* SQLITE_ENABLE_NORMALIZE */ /* Remove a single entry from the hash table given a pointer to that ** element and a hash on the element's key. @@ -31861,8 +32046,8 @@ static void removeElementGivenHash( if( pEntry->chain==elem ){ pEntry->chain = elem->next; } + assert( pEntry->count>0 ); pEntry->count--; - assert( pEntry->count>=0 ); } sqlite3_free( elem ); pH->count--; @@ -31882,14 +32067,6 @@ SQLITE_PRIVATE void *sqlite3HashFind(const Hash *pH, const char *pKey){ assert( pKey!=0 ); return findElementWithHash(pH, pKey, 0)->data; } -#ifdef SQLITE_ENABLE_NORMALIZE -SQLITE_PRIVATE void *sqlite3HashFindN(const Hash *pH, const char *pKey, int nKey){ - assert( pH!=0 ); - assert( pKey!=0 ); - assert( nKey>=0 ); - return findElementWithHashN(pH, pKey, nKey, 0)->data; -} -#endif /* SQLITE_ENABLE_NORMALIZE */ /* Insert an element into the hash table pH. The key is pKey ** and the data is "data". @@ -32045,25 +32222,25 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ /* 89 */ "Offset" OpHelp("r[P3] = sqlite_offset(P1)"), /* 90 */ "Column" OpHelp("r[P3]=PX"), /* 91 */ "Affinity" OpHelp("affinity(r[P1@P2])"), - /* 92 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"), - /* 93 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"), - /* 94 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<>r[P1]"), - /* 96 */ "Add" OpHelp("r[P3]=r[P1]+r[P2]"), - /* 97 */ "Subtract" OpHelp("r[P3]=r[P2]-r[P1]"), - /* 98 */ "Multiply" OpHelp("r[P3]=r[P1]*r[P2]"), - /* 99 */ "Divide" OpHelp("r[P3]=r[P2]/r[P1]"), - /* 100 */ "Remainder" OpHelp("r[P3]=r[P2]%r[P1]"), - /* 101 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"), - /* 102 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"), - /* 103 */ "BitNot" OpHelp("r[P2]= ~r[P1]"), - /* 104 */ "Count" OpHelp("r[P2]=count()"), - /* 105 */ "ReadCookie" OpHelp(""), - /* 106 */ "String8" OpHelp("r[P2]='P4'"), - /* 107 */ "SetCookie" OpHelp(""), - /* 108 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"), - /* 109 */ "OpenRead" OpHelp("root=P2 iDb=P3"), - /* 110 */ "OpenWrite" OpHelp("root=P2 iDb=P3"), + /* 92 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"), + /* 93 */ "Count" OpHelp("r[P2]=count()"), + /* 94 */ "ReadCookie" OpHelp(""), + /* 95 */ "SetCookie" OpHelp(""), + /* 96 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"), + /* 97 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"), + /* 98 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<>r[P1]"), + /* 100 */ "Add" OpHelp("r[P3]=r[P1]+r[P2]"), + /* 101 */ "Subtract" OpHelp("r[P3]=r[P2]-r[P1]"), + /* 102 */ "Multiply" OpHelp("r[P3]=r[P1]*r[P2]"), + /* 103 */ "Divide" OpHelp("r[P3]=r[P2]/r[P1]"), + /* 104 */ "Remainder" OpHelp("r[P3]=r[P2]%r[P1]"), + /* 105 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"), + /* 106 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"), + /* 107 */ "BitNot" OpHelp("r[P2]= ~r[P1]"), + /* 108 */ "OpenRead" OpHelp("root=P2 iDb=P3"), + /* 109 */ "OpenWrite" OpHelp("root=P2 iDb=P3"), + /* 110 */ "String8" OpHelp("r[P2]='P4'"), /* 111 */ "OpenDup" OpHelp(""), /* 112 */ "OpenAutoindex" OpHelp("nColumn=P2"), /* 113 */ "OpenEphemeral" OpHelp("nColumn=P2"), @@ -32076,57 +32253,56 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ /* 120 */ "Sequence" OpHelp("r[P2]=cursor[P1].ctr++"), /* 121 */ "NewRowid" OpHelp("r[P2]=rowid"), /* 122 */ "Insert" OpHelp("intkey=r[P3] data=r[P2]"), - /* 123 */ "InsertInt" OpHelp("intkey=P3 data=r[P2]"), - /* 124 */ "Delete" OpHelp(""), - /* 125 */ "ResetCount" OpHelp(""), - /* 126 */ "SorterCompare" OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"), - /* 127 */ "SorterData" OpHelp("r[P2]=data"), - /* 128 */ "RowData" OpHelp("r[P2]=data"), - /* 129 */ "Rowid" OpHelp("r[P2]=rowid"), - /* 130 */ "NullRow" OpHelp(""), - /* 131 */ "SeekEnd" OpHelp(""), - /* 132 */ "SorterInsert" OpHelp("key=r[P2]"), - /* 133 */ "IdxInsert" OpHelp("key=r[P2]"), - /* 134 */ "IdxDelete" OpHelp("key=r[P2@P3]"), - /* 135 */ "DeferredSeek" OpHelp("Move P3 to P1.rowid if needed"), - /* 136 */ "IdxRowid" OpHelp("r[P2]=rowid"), - /* 137 */ "Destroy" OpHelp(""), - /* 138 */ "Clear" OpHelp(""), - /* 139 */ "ResetSorter" OpHelp(""), - /* 140 */ "CreateBtree" OpHelp("r[P2]=root iDb=P1 flags=P3"), - /* 141 */ "Real" OpHelp("r[P2]=P4"), - /* 142 */ "SqlExec" OpHelp(""), - /* 143 */ "ParseSchema" OpHelp(""), - /* 144 */ "LoadAnalysis" OpHelp(""), - /* 145 */ "DropTable" OpHelp(""), - /* 146 */ "DropIndex" OpHelp(""), - /* 147 */ "DropTrigger" OpHelp(""), - /* 148 */ "IntegrityCk" OpHelp(""), - /* 149 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"), - /* 150 */ "Param" OpHelp(""), - /* 151 */ "FkCounter" OpHelp("fkctr[P1]+=P2"), - /* 152 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"), - /* 153 */ "OffsetLimit" OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"), - /* 154 */ "AggInverse" OpHelp("accum=r[P3] inverse(r[P2@P5])"), - /* 155 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"), - /* 156 */ "AggStep1" OpHelp("accum=r[P3] step(r[P2@P5])"), - /* 157 */ "AggValue" OpHelp("r[P3]=value N=P2"), - /* 158 */ "AggFinal" OpHelp("accum=r[P1] N=P2"), - /* 159 */ "Expire" OpHelp(""), - /* 160 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"), - /* 161 */ "VBegin" OpHelp(""), - /* 162 */ "VCreate" OpHelp(""), - /* 163 */ "VDestroy" OpHelp(""), - /* 164 */ "VOpen" OpHelp(""), - /* 165 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), - /* 166 */ "VRename" OpHelp(""), - /* 167 */ "Pagecount" OpHelp(""), - /* 168 */ "MaxPgcnt" OpHelp(""), - /* 169 */ "Trace" OpHelp(""), - /* 170 */ "CursorHint" OpHelp(""), - /* 171 */ "Noop" OpHelp(""), - /* 172 */ "Explain" OpHelp(""), - /* 173 */ "Abortable" OpHelp(""), + /* 123 */ "Delete" OpHelp(""), + /* 124 */ "ResetCount" OpHelp(""), + /* 125 */ "SorterCompare" OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"), + /* 126 */ "SorterData" OpHelp("r[P2]=data"), + /* 127 */ "RowData" OpHelp("r[P2]=data"), + /* 128 */ "Rowid" OpHelp("r[P2]=rowid"), + /* 129 */ "NullRow" OpHelp(""), + /* 130 */ "SeekEnd" OpHelp(""), + /* 131 */ "SorterInsert" OpHelp("key=r[P2]"), + /* 132 */ "IdxInsert" OpHelp("key=r[P2]"), + /* 133 */ "IdxDelete" OpHelp("key=r[P2@P3]"), + /* 134 */ "DeferredSeek" OpHelp("Move P3 to P1.rowid if needed"), + /* 135 */ "IdxRowid" OpHelp("r[P2]=rowid"), + /* 136 */ "Destroy" OpHelp(""), + /* 137 */ "Clear" OpHelp(""), + /* 138 */ "ResetSorter" OpHelp(""), + /* 139 */ "CreateBtree" OpHelp("r[P2]=root iDb=P1 flags=P3"), + /* 140 */ "SqlExec" OpHelp(""), + /* 141 */ "ParseSchema" OpHelp(""), + /* 142 */ "LoadAnalysis" OpHelp(""), + /* 143 */ "DropTable" OpHelp(""), + /* 144 */ "DropIndex" OpHelp(""), + /* 145 */ "Real" OpHelp("r[P2]=P4"), + /* 146 */ "DropTrigger" OpHelp(""), + /* 147 */ "IntegrityCk" OpHelp(""), + /* 148 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"), + /* 149 */ "Param" OpHelp(""), + /* 150 */ "FkCounter" OpHelp("fkctr[P1]+=P2"), + /* 151 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"), + /* 152 */ "OffsetLimit" OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"), + /* 153 */ "AggInverse" OpHelp("accum=r[P3] inverse(r[P2@P5])"), + /* 154 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"), + /* 155 */ "AggStep1" OpHelp("accum=r[P3] step(r[P2@P5])"), + /* 156 */ "AggValue" OpHelp("r[P3]=value N=P2"), + /* 157 */ "AggFinal" OpHelp("accum=r[P1] N=P2"), + /* 158 */ "Expire" OpHelp(""), + /* 159 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"), + /* 160 */ "VBegin" OpHelp(""), + /* 161 */ "VCreate" OpHelp(""), + /* 162 */ "VDestroy" OpHelp(""), + /* 163 */ "VOpen" OpHelp(""), + /* 164 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), + /* 165 */ "VRename" OpHelp(""), + /* 166 */ "Pagecount" OpHelp(""), + /* 167 */ "MaxPgcnt" OpHelp(""), + /* 168 */ "Trace" OpHelp(""), + /* 169 */ "CursorHint" OpHelp(""), + /* 170 */ "Noop" OpHelp(""), + /* 171 */ "Explain" OpHelp(""), + /* 172 */ "Abortable" OpHelp(""), }; return azName[i]; } @@ -46577,7 +46753,8 @@ typedef struct MemFile MemFile; struct MemFile { sqlite3_file base; /* IO methods */ sqlite3_int64 sz; /* Size of the file */ - sqlite3_int64 szMax; /* Space allocated to aData */ + sqlite3_int64 szAlloc; /* Space allocated to aData */ + sqlite3_int64 szMax; /* Maximum allowed size of the file */ unsigned char *aData; /* content of the file */ int nMmap; /* Number of memory mapped pages */ unsigned mFlags; /* Flags */ @@ -46703,10 +46880,15 @@ static int memdbEnlarge(MemFile *p, sqlite3_int64 newSz){ if( (p->mFlags & SQLITE_DESERIALIZE_RESIZEABLE)==0 || p->nMmap>0 ){ return SQLITE_FULL; } + if( newSz>p->szMax ){ + return SQLITE_FULL; + } + newSz *= 2; + if( newSz>p->szMax ) newSz = p->szMax; pNew = sqlite3_realloc64(p->aData, newSz); if( pNew==0 ) return SQLITE_NOMEM; p->aData = pNew; - p->szMax = newSz; + p->szAlloc = newSz; return SQLITE_OK; } @@ -46720,10 +46902,11 @@ static int memdbWrite( sqlite_int64 iOfst ){ MemFile *p = (MemFile *)pFile; + if( NEVER(p->mFlags & SQLITE_DESERIALIZE_READONLY) ) return SQLITE_READONLY; if( iOfst+iAmt>p->sz ){ int rc; - if( iOfst+iAmt>p->szMax - && (rc = memdbEnlarge(p, (iOfst+iAmt)*2))!=SQLITE_OK + if( iOfst+iAmt>p->szAlloc + && (rc = memdbEnlarge(p, iOfst+iAmt))!=SQLITE_OK ){ return rc; } @@ -46769,6 +46952,11 @@ static int memdbFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ */ static int memdbLock(sqlite3_file *pFile, int eLock){ MemFile *p = (MemFile *)pFile; + if( eLock>SQLITE_LOCK_SHARED + && (p->mFlags & SQLITE_DESERIALIZE_READONLY)!=0 + ){ + return SQLITE_READONLY; + } p->eLock = eLock; return SQLITE_OK; } @@ -46793,6 +46981,19 @@ static int memdbFileControl(sqlite3_file *pFile, int op, void *pArg){ *(char**)pArg = sqlite3_mprintf("memdb(%p,%lld)", p->aData, p->sz); rc = SQLITE_OK; } + if( op==SQLITE_FCNTL_SIZE_LIMIT ){ + sqlite3_int64 iLimit = *(sqlite3_int64*)pArg; + if( iLimitsz ){ + if( iLimit<0 ){ + iLimit = p->szMax; + }else{ + iLimit = p->sz; + } + } + p->szMax = iLimit; + *(sqlite3_int64*)pArg = iLimit; + rc = SQLITE_OK; + } return rc; } @@ -46823,8 +47024,12 @@ static int memdbFetch( void **pp ){ MemFile *p = (MemFile *)pFile; - p->nMmap++; - *pp = (void*)(p->aData + iOfst); + if( iOfst+iAmt>p->sz ){ + *pp = 0; + }else{ + p->nMmap++; + *pp = (void*)(p->aData + iOfst); + } return SQLITE_OK; } @@ -46854,6 +47059,7 @@ static int memdbOpen( assert( pOutFlags!=0 ); /* True because flags==SQLITE_OPEN_MAIN_DB */ *pOutFlags = flags | SQLITE_OPEN_MEMORY; p->base.pMethods = &memdb_io_methods; + p->szMax = sqlite3GlobalConfig.mxMemdbSize; return SQLITE_OK; } @@ -47103,7 +47309,11 @@ SQLITE_API int sqlite3_deserialize( }else{ p->aData = pData; p->sz = szDb; + p->szAlloc = szBuf; p->szMax = szBuf; + if( p->szMaxszMax = sqlite3GlobalConfig.mxMemdbSize; + } p->mFlags = mFlags; rc = SQLITE_OK; } @@ -48524,16 +48734,27 @@ typedef struct PGroup PGroup; ** structure. Unless SQLITE_PCACHE_SEPARATE_HEADER is defined, a buffer of ** PgHdr1.pCache->szPage bytes is allocated directly before this structure ** in memory. +** +** Note: Variables isBulkLocal and isAnchor were once type "u8". That works, +** but causes a 2-byte gap in the structure for most architectures (since +** pointers must be either 4 or 8-byte aligned). As this structure is located +** in memory directly after the associated page data, if the database is +** corrupt, code at the b-tree layer may overread the page buffer and +** read part of this structure before the corruption is detected. This +** can cause a valgrind error if the unitialized gap is accessed. Using u16 +** ensures there is no such gap, and therefore no bytes of unitialized memory +** in the structure. */ struct PgHdr1 { sqlite3_pcache_page page; /* Base class. Must be first. pBuf & pExtra */ unsigned int iKey; /* Key value (page number) */ - u8 isBulkLocal; /* This page from bulk local storage */ - u8 isAnchor; /* This is the PGroup.lru element */ + u16 isBulkLocal; /* This page from bulk local storage */ + u16 isAnchor; /* This is the PGroup.lru element */ PgHdr1 *pNext; /* Next in hash table chain */ PCache1 *pCache; /* Cache that currently owns this page */ PgHdr1 *pLruNext; /* Next in LRU list of unpinned pages */ PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */ + /* NB: pLruPrev is only valid if pLruNext!=0 */ }; /* @@ -48599,6 +48820,7 @@ struct PCache1 { unsigned int nMax; /* Configured "cache_size" value */ unsigned int n90pct; /* nMax*9/10 */ unsigned int iMaxKey; /* Largest key seen since xTruncate() */ + unsigned int nPurgeableDummy; /* pnPurgeable points here when not used*/ /* Hash table of all pages. The following variables may only be accessed ** when the accessor is holding the PGroup mutex. @@ -48733,6 +48955,7 @@ static int pcache1InitBulk(PCache1 *pCache){ pX->isBulkLocal = 1; pX->isAnchor = 0; pX->pNext = pCache->pFree; + pX->pLruPrev = 0; /* Initializing this saves a valgrind error */ pCache->pFree = pX; zBulk += pCache->szAlloc; }while( --nBulk ); @@ -48908,6 +49131,7 @@ static void pcache1FreePage(PgHdr1 *p){ ** exists, this function falls back to sqlite3Malloc(). */ SQLITE_PRIVATE void *sqlite3PageMalloc(int sz){ + assert( sz<=65536+8 ); /* These allocations are never very large */ return pcache1Alloc(sz); } @@ -49002,7 +49226,8 @@ static PgHdr1 *pcache1PinPage(PgHdr1 *pPage){ pPage->pLruPrev->pLruNext = pPage->pLruNext; pPage->pLruNext->pLruPrev = pPage->pLruPrev; pPage->pLruNext = 0; - pPage->pLruPrev = 0; + /* pPage->pLruPrev = 0; + ** No need to clear pLruPrev as it is never accessed if pLruNext is 0 */ assert( pPage->isAnchor==0 ); assert( pPage->pCache->pGroup->lru.isAnchor==1 ); pPage->pCache->nRecyclable--; @@ -49212,8 +49437,7 @@ static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){ pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; pCache->pnPurgeable = &pGroup->nPurgeable; }else{ - static unsigned int dummyCurrentPage; - pCache->pnPurgeable = &dummyCurrentPage; + pCache->pnPurgeable = &pCache->nPurgeableDummy; } pcache1LeaveMutex(pGroup); if( pCache->nHash==0 ){ @@ -49340,8 +49564,9 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2( pPage->iKey = iKey; pPage->pNext = pCache->apHash[h]; pPage->pCache = pCache; - pPage->pLruPrev = 0; pPage->pLruNext = 0; + /* pPage->pLruPrev = 0; + ** No need to clear pLruPrev since it is not accessed when pLruNext==0 */ *(void **)pPage->page.pExtra = 0; pCache->apHash[h] = pPage; if( iKey>pCache->iMaxKey ){ @@ -49501,7 +49726,7 @@ static void pcache1Unpin( /* It is an error to call this function if the page is already ** part of the PGroup LRU list. */ - assert( pPage->pLruPrev==0 && pPage->pLruNext==0 ); + assert( pPage->pLruNext==0 ); assert( PAGE_IS_PINNED(pPage) ); if( reuseUnlikely || pGroup->nPurgeable>pGroup->nMaxPage ){ @@ -51190,6 +51415,9 @@ static const unsigned char aJournalMagic[] = { SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){ if( pPager->fd->pMethods==0 ) return 0; if( sqlite3PCacheIsDirty(pPager->pPCache) ) return 0; +#ifdef SQLITE_HAS_CODEC + if( pPager->xCodec!=0 ) return 0; +#endif #ifndef SQLITE_OMIT_WAL if( pPager->pWal ){ u32 iRead = 0; @@ -54139,8 +54367,14 @@ SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager *pPager, u32 *pPageSize, int nR rc = sqlite3OsFileSize(pPager->fd, &nByte); } if( rc==SQLITE_OK ){ - pNew = (char *)sqlite3PageMalloc(pageSize); - if( !pNew ) rc = SQLITE_NOMEM_BKPT; + /* 8 bytes of zeroed overrun space is sufficient so that the b-tree + * cell header parser will never run off the end of the allocation */ + pNew = (char *)sqlite3PageMalloc(pageSize+8); + if( !pNew ){ + rc = SQLITE_NOMEM_BKPT; + }else{ + memset(pNew+pageSize, 0, 8); + } } if( rc==SQLITE_OK ){ @@ -54192,7 +54426,10 @@ SQLITE_PRIVATE int sqlite3PagerMaxPageCount(Pager *pPager, int mxPage){ pPager->mxPgno = mxPage; } assert( pPager->eState!=PAGER_OPEN ); /* Called only by OP_MaxPgcnt */ - assert( pPager->mxPgno>=pPager->dbSize ); /* OP_MaxPgcnt enforces this */ + /* assert( pPager->mxPgno>=pPager->dbSize ); */ + /* OP_MaxPgcnt ensures that the parameter passed to this function is not + ** less than the total number of valid pages in the database. But this + ** may be less than Pager.dbSize, and so the assert() above is not valid */ return pPager->mxPgno; } @@ -57518,8 +57755,12 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i */ pPg->flags &= ~PGHDR_NEED_SYNC; pPgOld = sqlite3PagerLookup(pPager, pgno); - assert( !pPgOld || pPgOld->nRef==1 ); + assert( !pPgOld || pPgOld->nRef==1 || CORRUPT_DB ); if( pPgOld ){ + if( pPgOld->nRef>1 ){ + sqlite3PagerUnrefNotNull(pPgOld); + return SQLITE_CORRUPT_BKPT; + } pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC); if( pPager->tempFile ){ /* Do not discard pages from an in-memory database since we might @@ -58047,7 +58288,7 @@ SQLITE_PRIVATE int sqlite3PagerSnapshotCheck(Pager *pPager, sqlite3_snapshot *pS */ SQLITE_PRIVATE void sqlite3PagerSnapshotUnlock(Pager *pPager){ assert( pPager->pWal ); - return sqlite3WalSnapshotUnlock(pPager->pWal); + sqlite3WalSnapshotUnlock(pPager->pWal); } #endif /* SQLITE_ENABLE_SNAPSHOT */ @@ -58648,7 +58889,7 @@ static SQLITE_NOINLINE int walIndexPageRealloc( /* Enlarge the pWal->apWiData[] array if required */ if( pWal->nWiData<=iPage ){ - int nByte = sizeof(u32*)*(iPage+1); + sqlite3_int64 nByte = sizeof(u32*)*(iPage+1); volatile u32 **apNew; apNew = (volatile u32 **)sqlite3_realloc64((void *)pWal->apWiData, nByte); if( !apNew ){ @@ -58752,6 +58993,7 @@ static void walChecksumBytes( assert( nByte>=8 ); assert( (nByte&0x00000007)==0 ); + assert( nByte<=65536 ); if( nativeCksum ){ do { @@ -59059,6 +59301,7 @@ static void walCleanupHash(Wal *pWal){ int iLimit = 0; /* Zero values greater than this */ int nByte; /* Number of bytes to zero in aPgno[] */ int i; /* Used to iterate through aHash[] */ + int rc; /* Return code form walHashGet() */ assert( pWal->writeLock ); testcase( pWal->hdr.mxFrame==HASHTABLE_NPAGE_ONE-1 ); @@ -59069,11 +59312,12 @@ static void walCleanupHash(Wal *pWal){ /* Obtain pointers to the hash-table and page-number array containing ** the entry that corresponds to frame pWal->hdr.mxFrame. It is guaranteed - ** that the page said hash-table and array reside on is already mapped. + ** that the page said hash-table and array reside on is already mapped.(1) */ assert( pWal->nWiData>walFramePage(pWal->hdr.mxFrame) ); assert( pWal->apWiData[walFramePage(pWal->hdr.mxFrame)] ); - walHashGet(pWal, walFramePage(pWal->hdr.mxFrame), &sLoc); + rc = walHashGet(pWal, walFramePage(pWal->hdr.mxFrame), &sLoc); + if( NEVER(rc) ) return; /* Defense-in-depth, in case (1) above is wrong */ /* Zero all hash-table entries that correspond to frame numbers greater ** than pWal->hdr.mxFrame. @@ -59687,7 +59931,7 @@ static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){ WalIterator *p; /* Return value */ int nSegment; /* Number of segments to merge */ u32 iLast; /* Last frame in log */ - int nByte; /* Number of bytes to allocate */ + sqlite3_int64 nByte; /* Number of bytes to allocate */ int i; /* Iterator variable */ ht_slot *aTmp; /* Temp space used by merge-sort */ int rc = SQLITE_OK; /* Return Code */ @@ -62223,7 +62467,7 @@ struct MemPage { u16 maxLocal; /* Copy of BtShared.maxLocal or BtShared.maxLeaf */ u16 minLocal; /* Copy of BtShared.minLocal or BtShared.minLeaf */ u16 cellOffset; /* Index in aData of first cell pointer */ - u16 nFree; /* Number of free bytes on the page */ + int nFree; /* Number of free bytes on the page. -1 for unknown */ u16 nCell; /* Number of cells on this page, local and ovfl */ u16 maskPage; /* Mask for page offset */ u16 aiOvfl[4]; /* Insert the i-th overflow cell before the aiOvfl-th @@ -62431,9 +62675,16 @@ struct CellInfo { ** found at self->pBt->mutex. ** ** skipNext meaning: -** eState==SKIPNEXT && skipNext>0: Next sqlite3BtreeNext() is no-op. -** eState==SKIPNEXT && skipNext<0: Next sqlite3BtreePrevious() is no-op. -** eState==FAULT: Cursor fault with skipNext as error code. +** The meaning of skipNext depends on the value of eState: +** +** eState Meaning of skipNext +** VALID skipNext is meaningless and is ignored +** INVALID skipNext is meaningless and is ignored +** SKIPNEXT sqlite3BtreeNext() is a no-op if skipNext>0 and +** sqlite3BtreePrevious() is no-op if skipNext<0. +** REQUIRESEEK restoreCursorPosition() restores the cursor to +** eState=SKIPNEXT if skipNext!=0 +** FAULT skipNext holds the cursor fault error code. */ struct BtCursor { u8 eState; /* One of the CURSOR_XXX constants (see below) */ @@ -63597,13 +63848,19 @@ static int saveCursorKey(BtCursor *pCur){ /* Only the rowid is required for a table btree */ pCur->nKey = sqlite3BtreeIntegerKey(pCur); }else{ - /* For an index btree, save the complete key content */ + /* For an index btree, save the complete key content. It is possible + ** that the current key is corrupt. In that case, it is possible that + ** the sqlite3VdbeRecordUnpack() function may overread the buffer by + ** up to the size of 1 varint plus 1 8-byte value when the cursor + ** position is restored. Hence the 17 bytes of padding allocated + ** below. */ void *pKey; pCur->nKey = sqlite3BtreePayloadSize(pCur); - pKey = sqlite3Malloc( pCur->nKey ); + pKey = sqlite3Malloc( pCur->nKey + 9 + 8 ); if( pKey ){ rc = sqlite3BtreePayload(pCur, 0, (int)pCur->nKey, pKey); if( rc==SQLITE_OK ){ + memset(((u8*)pKey)+pCur->nKey, 0, 9+8); pCur->pKey = pKey; }else{ sqlite3_free(pKey); @@ -63735,11 +63992,12 @@ static int btreeMoveto( UnpackedRecord *pIdxKey; /* Unpacked index key */ if( pKey ){ + KeyInfo *pKeyInfo = pCur->pKeyInfo; assert( nKey==(i64)(int)nKey ); - pIdxKey = sqlite3VdbeAllocUnpackedRecord(pCur->pKeyInfo); + pIdxKey = sqlite3VdbeAllocUnpackedRecord(pKeyInfo); if( pIdxKey==0 ) return SQLITE_NOMEM_BKPT; - sqlite3VdbeRecordUnpack(pCur->pKeyInfo, (int)nKey, pKey, pIdxKey); - if( pIdxKey->nField==0 ){ + sqlite3VdbeRecordUnpack(pKeyInfo, (int)nKey, pKey, pIdxKey); + if( pIdxKey->nField==0 || pIdxKey->nField>pKeyInfo->nAllField ){ rc = SQLITE_CORRUPT_BKPT; goto moveto_done; } @@ -63763,19 +64021,23 @@ moveto_done: */ static int btreeRestoreCursorPosition(BtCursor *pCur){ int rc; - int skipNext; + int skipNext = 0; assert( cursorOwnsBtShared(pCur) ); assert( pCur->eState>=CURSOR_REQUIRESEEK ); if( pCur->eState==CURSOR_FAULT ){ return pCur->skipNext; } pCur->eState = CURSOR_INVALID; - rc = btreeMoveto(pCur, pCur->pKey, pCur->nKey, 0, &skipNext); + if( sqlite3FaultSim(410) ){ + rc = SQLITE_IOERR; + }else{ + rc = btreeMoveto(pCur, pCur->pKey, pCur->nKey, 0, &skipNext); + } if( rc==SQLITE_OK ){ sqlite3_free(pCur->pKey); pCur->pKey = 0; assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_INVALID ); - pCur->skipNext |= skipNext; + if( skipNext ) pCur->skipNext = skipNext; if( pCur->skipNext && pCur->eState==CURSOR_VALID ){ pCur->eState = CURSOR_SKIPNEXT; } @@ -63845,7 +64107,6 @@ SQLITE_PRIVATE int sqlite3BtreeCursorRestore(BtCursor *pCur, int *pDifferentRow) if( pCur->eState!=CURSOR_VALID ){ *pDifferentRow = 1; }else{ - assert( pCur->skipNext==0 ); *pDifferentRow = 0; } return SQLITE_OK; @@ -63929,6 +64190,13 @@ static void ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent, int *pRC){ *pRC = rc; return; } + if( ((char*)sqlite3PagerGetExtra(pDbPage))[0]!=0 ){ + /* The first byte of the extra data is the MemPage.isInit byte. + ** If that byte is set, it means this page is also being used + ** as a btree page. */ + *pRC = SQLITE_CORRUPT_BKPT; + goto ptrmap_exit; + } offset = PTRMAP_PTROFFSET(iPtrmap, key); if( offset<0 ){ *pRC = SQLITE_CORRUPT_BKPT; @@ -63991,7 +64259,7 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){ #else /* if defined SQLITE_OMIT_AUTOVACUUM */ #define ptrmapPut(w,x,y,z,rc) #define ptrmapGet(w,x,y,z) SQLITE_OK - #define ptrmapPutOvflPtr(x, y, rc) + #define ptrmapPutOvflPtr(x, y, z, rc) #endif /* @@ -64284,17 +64552,24 @@ static u16 cellSize(MemPage *pPage, int iCell){ #ifndef SQLITE_OMIT_AUTOVACUUM /* -** If the cell pCell, part of page pPage contains a pointer -** to an overflow page, insert an entry into the pointer-map -** for the overflow page. +** The cell pCell is currently part of page pSrc but will ultimately be part +** of pPage. (pSrc and pPager are often the same.) If pCell contains a +** pointer to an overflow page, insert an entry into the pointer-map for +** the overflow page that will be valid after pCell has been moved to pPage. */ -static void ptrmapPutOvflPtr(MemPage *pPage, u8 *pCell, int *pRC){ +static void ptrmapPutOvflPtr(MemPage *pPage, MemPage *pSrc, u8 *pCell,int *pRC){ CellInfo info; if( *pRC ) return; assert( pCell!=0 ); pPage->xParseCell(pPage, pCell, &info); if( info.nLocalaDataEnd, pCell, pCell+info.nLocal) ){ + testcase( pSrc!=pPage ); + *pRC = SQLITE_CORRUPT_BKPT; + return; + } + ovfl = get4byte(&pCell[info.nSize-4]); ptrmapPut(pPage->pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno, pRC); } } @@ -64338,7 +64613,7 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ hdr = pPage->hdrOffset; cellOffset = pPage->cellOffset; nCell = pPage->nCell; - assert( nCell==get2byte(&data[hdr+3]) ); + assert( nCell==get2byte(&data[hdr+3]) || CORRUPT_DB ); iCellFirst = cellOffset + 2*nCell; usableSize = pPage->pBt->usableSize; @@ -64349,19 +64624,10 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ ** reconstruct the entire page. */ if( (int)data[hdr+7]<=nMaxFrag ){ int iFree = get2byte(&data[hdr+1]); + if( iFree>usableSize-4 ) return SQLITE_CORRUPT_PAGE(pPage); if( iFree ){ int iFree2 = get2byte(&data[iFree]); - - /* pageFindSlot() has already verified that free blocks are sorted - ** in order of offset within the page, and that no block extends - ** past the end of the page. Provided the two free slots do not - ** overlap, this guarantees that the memmove() calls below will not - ** overwrite the usableSize byte buffer, even if the database page - ** is corrupt. */ - assert( iFree2==0 || iFree2>iFree ); - assert( iFree+get2byte(&data[iFree+2]) <= usableSize ); - assert( iFree2==0 || iFree2+get2byte(&data[iFree2+2]) <= usableSize ); - + if( iFree2>usableSize-4 ) return SQLITE_CORRUPT_PAGE(pPage); if( 0==iFree2 || (data[iFree2]==0 && data[iFree2+1]==0) ){ u8 *pEnd = &data[cellOffset + nCell*2]; u8 *pAddr; @@ -64372,12 +64638,15 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ return SQLITE_CORRUPT_PAGE(pPage); } if( iFree2 ){ - assert( iFree+sz<=iFree2 ); /* Verified by pageFindSlot() */ + if( iFree+sz>iFree2 ) return SQLITE_CORRUPT_PAGE(pPage); sz2 = get2byte(&data[iFree2+2]); - assert( iFree+sz+sz2+iFree2-(iFree+sz) <= usableSize ); + if( iFree2+sz2 > usableSize ) return SQLITE_CORRUPT_PAGE(pPage); memmove(&data[iFree+sz+sz2], &data[iFree+sz], iFree2-(iFree+sz)); sz += sz2; + }else if( iFree+sz>usableSize ){ + return SQLITE_CORRUPT_PAGE(pPage); } + cbrk = top+sz; assert( cbrk+(iFree-top) <= usableSize ); memmove(&data[cbrk], &data[top], iFree-top); @@ -64428,6 +64697,7 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ data[hdr+7] = 0; defragment_out: + assert( pPage->nFree>=0 ); if( data[hdr+7]+cbrk-iCellFirst!=pPage->nFree ){ return SQLITE_CORRUPT_PAGE(pPage); } @@ -64455,16 +64725,16 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ ** causes the fragmentation count to exceed 60. */ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){ - const int hdr = pPg->hdrOffset; - u8 * const aData = pPg->aData; - int iAddr = hdr + 1; - int pc = get2byte(&aData[iAddr]); - int x; - int usableSize = pPg->pBt->usableSize; - int size; /* Size of the free slot */ + const int hdr = pPg->hdrOffset; /* Offset to page header */ + u8 * const aData = pPg->aData; /* Page data */ + int iAddr = hdr + 1; /* Address of ptr to pc */ + int pc = get2byte(&aData[iAddr]); /* Address of a free slot */ + int x; /* Excess size of the slot */ + int maxPC = pPg->pBt->usableSize - nByte; /* Max address for a usable slot */ + int size; /* Size of the free slot */ assert( pc>0 ); - while( pc<=usableSize-4 ){ + while( pc<=maxPC ){ /* EVIDENCE-OF: R-22710-53328 The third and fourth bytes of each ** freeblock form a big-endian integer which is the size of the freeblock ** in bytes, including the 4-byte header. */ @@ -64472,10 +64742,7 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){ if( (x = size - nByte)>=0 ){ testcase( x==4 ); testcase( x==3 ); - if( size+pc > usableSize ){ - *pRc = SQLITE_CORRUPT_PAGE(pPg); - return 0; - }else if( x<4 ){ + if( x<4 ){ /* EVIDENCE-OF: R-11498-58022 In a well-formed b-tree page, the total ** number of bytes in fragments may not exceed 60. */ if( aData[hdr+7]>57 ) return 0; @@ -64484,21 +64751,31 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){ ** fragmented bytes within the page. */ memcpy(&aData[iAddr], &aData[pc], 2); aData[hdr+7] += (u8)x; + }else if( x+pc > maxPC ){ + /* This slot extends off the end of the usable part of the page */ + *pRc = SQLITE_CORRUPT_PAGE(pPg); + return 0; }else{ /* The slot remains on the free-list. Reduce its size to account - ** for the portion used by the new allocation. */ + ** for the portion used by the new allocation. */ put2byte(&aData[pc+2], x); } return &aData[pc + x]; } iAddr = pc; pc = get2byte(&aData[pc]); - if( pcmaxPC+nByte-4 ){ + /* The free slot chain extends off the end of the page */ *pRc = SQLITE_CORRUPT_PAGE(pPg); } - return 0; } @@ -64548,9 +64825,9 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ } } - /* If there is enough space between gap and top for one more cell pointer - ** array entry offset, and if the freelist is not empty, then search the - ** freelist looking for a free slot big enough to satisfy the request. + /* If there is enough space between gap and top for one more cell pointer, + ** and if the freelist is not empty, then search the + ** freelist looking for a slot big enough to satisfy the request. */ testcase( gap+2==top ); testcase( gap+1==top ); @@ -64572,6 +64849,7 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ testcase( gap+2+nByte==top ); if( gap+2+nByte>top ){ assert( pPage->nCell>0 || CORRUPT_DB ); + assert( pPage->nFree>=0 ); rc = defragmentPage(pPage, MIN(4, pPage->nFree - (2+nByte))); if( rc ) return rc; top = get2byteNotZero(&data[hdr+5]); @@ -64580,7 +64858,7 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ /* Allocate memory from the gap in between the cell pointer array - ** and the cell content area. The btreeInitPage() call has already + ** and the cell content area. The btreeComputeFreeSpace() call has already ** validated the freelist. Given that the freelist is valid, there ** is no way that the allocation can extend off the end of the page. ** The assert() below verifies the previous sentence. @@ -64599,7 +64877,7 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ ** ** Adjacent freeblocks are coalesced. ** -** Note that even though the freeblock list was checked by btreeInitPage(), +** Even though the freeblock list was checked by btreeComputeFreeSpace(), ** that routine will not detect overlap between cells or freeblocks. Nor ** does it detect cells or freeblocks that encrouch into the reserved bytes ** at the end of the page. So do additional corruption checks inside this @@ -64761,21 +65039,14 @@ static int decodeFlags(MemPage *pPage, int flagByte){ } /* -** Initialize the auxiliary information for a disk block. -** -** Return SQLITE_OK on success. If we see that the page does -** not contain a well-formed database page, then return -** SQLITE_CORRUPT. Note that a return of SQLITE_OK does not -** guarantee that the page is well-formed. It only shows that -** we failed to detect any corruption. +** Compute the amount of freespace on the page. In other words, fill +** in the pPage->nFree field. */ -static int btreeInitPage(MemPage *pPage){ +static int btreeComputeFreeSpace(MemPage *pPage){ int pc; /* Address of a freeblock within pPage->aData[] */ u8 hdr; /* Offset to beginning of page header */ u8 *data; /* Equal to pPage->aData */ - BtShared *pBt; /* The main btree structure */ int usableSize; /* Amount of usable space on each page */ - u16 cellOffset; /* Offset from start of page to first cell pointer */ int nFree; /* Number of unused bytes on the page */ int top; /* First byte of the cell content area */ int iCellFirst; /* First allowable cell or freeblock offset */ @@ -64787,71 +65058,18 @@ static int btreeInitPage(MemPage *pPage){ assert( pPage->pgno==sqlite3PagerPagenumber(pPage->pDbPage) ); assert( pPage == sqlite3PagerGetExtra(pPage->pDbPage) ); assert( pPage->aData == sqlite3PagerGetData(pPage->pDbPage) ); - assert( pPage->isInit==0 ); + assert( pPage->isInit==1 ); + assert( pPage->nFree<0 ); - pBt = pPage->pBt; + usableSize = pPage->pBt->usableSize; hdr = pPage->hdrOffset; data = pPage->aData; - /* EVIDENCE-OF: R-28594-02890 The one-byte flag at offset 0 indicating - ** the b-tree page type. */ - if( decodeFlags(pPage, data[hdr]) ){ - return SQLITE_CORRUPT_PAGE(pPage); - } - assert( pBt->pageSize>=512 && pBt->pageSize<=65536 ); - pPage->maskPage = (u16)(pBt->pageSize - 1); - pPage->nOverflow = 0; - usableSize = pBt->usableSize; - pPage->cellOffset = cellOffset = hdr + 8 + pPage->childPtrSize; - pPage->aDataEnd = &data[usableSize]; - pPage->aCellIdx = &data[cellOffset]; - pPage->aDataOfst = &data[pPage->childPtrSize]; /* EVIDENCE-OF: R-58015-48175 The two-byte integer at offset 5 designates ** the start of the cell content area. A zero value for this integer is ** interpreted as 65536. */ top = get2byteNotZero(&data[hdr+5]); - /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the - ** number of cells on the page. */ - pPage->nCell = get2byte(&data[hdr+3]); - if( pPage->nCell>MX_CELL(pBt) ){ - /* To many cells for a single page. The page must be corrupt */ - return SQLITE_CORRUPT_PAGE(pPage); - } - testcase( pPage->nCell==MX_CELL(pBt) ); - /* EVIDENCE-OF: R-24089-57979 If a page contains no cells (which is only - ** possible for a root page of a table that contains no rows) then the - ** offset to the cell content area will equal the page size minus the - ** bytes of reserved space. */ - assert( pPage->nCell>0 || top==usableSize || CORRUPT_DB ); - - /* A malformed database page might cause us to read past the end - ** of page when parsing a cell. - ** - ** The following block of code checks early to see if a cell extends - ** past the end of a page boundary and causes SQLITE_CORRUPT to be - ** returned if it does. - */ - iCellFirst = cellOffset + 2*pPage->nCell; + iCellFirst = hdr + 8 + pPage->childPtrSize + 2*pPage->nCell; iCellLast = usableSize - 4; - if( pBt->db->flags & SQLITE_CellSizeCk ){ - int i; /* Index into the cell pointer array */ - int sz; /* Size of a cell */ - - if( !pPage->leaf ) iCellLast--; - for(i=0; inCell; i++){ - pc = get2byteAligned(&data[cellOffset+i*2]); - testcase( pc==iCellFirst ); - testcase( pc==iCellLast ); - if( pciCellLast ){ - return SQLITE_CORRUPT_PAGE(pPage); - } - sz = pPage->xCellSize(pPage, &data[pc]); - testcase( pc+sz==usableSize ); - if( pc+sz>usableSize ){ - return SQLITE_CORRUPT_PAGE(pPage); - } - } - if( !pPage->leaf ) iCellLast++; - } /* Compute the total free space on the page ** EVIDENCE-OF: R-23588-34450 The two-byte integer at offset 1 gives the @@ -64899,7 +65117,100 @@ static int btreeInitPage(MemPage *pPage){ return SQLITE_CORRUPT_PAGE(pPage); } pPage->nFree = (u16)(nFree - iCellFirst); + return SQLITE_OK; +} + +/* +** Do additional sanity check after btreeInitPage() if +** PRAGMA cell_size_check=ON +*/ +static SQLITE_NOINLINE int btreeCellSizeCheck(MemPage *pPage){ + int iCellFirst; /* First allowable cell or freeblock offset */ + int iCellLast; /* Last possible cell or freeblock offset */ + int i; /* Index into the cell pointer array */ + int sz; /* Size of a cell */ + int pc; /* Address of a freeblock within pPage->aData[] */ + u8 *data; /* Equal to pPage->aData */ + int usableSize; /* Maximum usable space on the page */ + int cellOffset; /* Start of cell content area */ + + iCellFirst = pPage->cellOffset + 2*pPage->nCell; + usableSize = pPage->pBt->usableSize; + iCellLast = usableSize - 4; + data = pPage->aData; + cellOffset = pPage->cellOffset; + if( !pPage->leaf ) iCellLast--; + for(i=0; inCell; i++){ + pc = get2byteAligned(&data[cellOffset+i*2]); + testcase( pc==iCellFirst ); + testcase( pc==iCellLast ); + if( pciCellLast ){ + return SQLITE_CORRUPT_PAGE(pPage); + } + sz = pPage->xCellSize(pPage, &data[pc]); + testcase( pc+sz==usableSize ); + if( pc+sz>usableSize ){ + return SQLITE_CORRUPT_PAGE(pPage); + } + } + return SQLITE_OK; +} + +/* +** Initialize the auxiliary information for a disk block. +** +** Return SQLITE_OK on success. If we see that the page does +** not contain a well-formed database page, then return +** SQLITE_CORRUPT. Note that a return of SQLITE_OK does not +** guarantee that the page is well-formed. It only shows that +** we failed to detect any corruption. +*/ +static int btreeInitPage(MemPage *pPage){ + u8 *data; /* Equal to pPage->aData */ + BtShared *pBt; /* The main btree structure */ + + assert( pPage->pBt!=0 ); + assert( pPage->pBt->db!=0 ); + assert( sqlite3_mutex_held(pPage->pBt->mutex) ); + assert( pPage->pgno==sqlite3PagerPagenumber(pPage->pDbPage) ); + assert( pPage == sqlite3PagerGetExtra(pPage->pDbPage) ); + assert( pPage->aData == sqlite3PagerGetData(pPage->pDbPage) ); + assert( pPage->isInit==0 ); + + pBt = pPage->pBt; + data = pPage->aData + pPage->hdrOffset; + /* EVIDENCE-OF: R-28594-02890 The one-byte flag at offset 0 indicating + ** the b-tree page type. */ + if( decodeFlags(pPage, data[0]) ){ + return SQLITE_CORRUPT_PAGE(pPage); + } + assert( pBt->pageSize>=512 && pBt->pageSize<=65536 ); + pPage->maskPage = (u16)(pBt->pageSize - 1); + pPage->nOverflow = 0; + pPage->cellOffset = pPage->hdrOffset + 8 + pPage->childPtrSize; + pPage->aCellIdx = data + pPage->childPtrSize + 8; + pPage->aDataEnd = pPage->aData + pBt->usableSize; + pPage->aDataOfst = pPage->aData + pPage->childPtrSize; + /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the + ** number of cells on the page. */ + pPage->nCell = get2byte(&data[3]); + if( pPage->nCell>MX_CELL(pBt) ){ + /* To many cells for a single page. The page must be corrupt */ + return SQLITE_CORRUPT_PAGE(pPage); + } + testcase( pPage->nCell==MX_CELL(pBt) ); + /* EVIDENCE-OF: R-24089-57979 If a page contains no cells (which is only + ** possible for a root page of a table that contains no rows) then the + ** offset to the cell content area will equal the page size minus the + ** bytes of reserved space. */ + assert( pPage->nCell>0 + || get2byteNotZero(&data[5])==(int)pBt->usableSize + || CORRUPT_DB ); + pPage->nFree = -1; /* Indicate that this value is yet uncomputed */ pPage->isInit = 1; + if( pBt->db->flags & SQLITE_CellSizeCk ){ + return btreeCellSizeCheck(pPage); + } return SQLITE_OK; } @@ -65042,19 +65353,18 @@ static int getAndInitPage( if( pgno>btreePagecount(pBt) ){ rc = SQLITE_CORRUPT_BKPT; - goto getAndInitPage_error; + goto getAndInitPage_error1; } rc = sqlite3PagerGet(pBt->pPager, pgno, (DbPage**)&pDbPage, bReadOnly); if( rc ){ - goto getAndInitPage_error; + goto getAndInitPage_error1; } *ppPage = (MemPage*)sqlite3PagerGetExtra(pDbPage); if( (*ppPage)->isInit==0 ){ btreePageFromDbPage(pDbPage, pgno, pBt); rc = btreeInitPage(*ppPage); if( rc!=SQLITE_OK ){ - releasePage(*ppPage); - goto getAndInitPage_error; + goto getAndInitPage_error2; } } assert( (*ppPage)->pgno==pgno ); @@ -65064,12 +65374,13 @@ static int getAndInitPage( ** compatible with the root page. */ if( pCur && ((*ppPage)->nCell<1 || (*ppPage)->intKey!=pCur->curIntKey) ){ rc = SQLITE_CORRUPT_PGNO(pgno); - releasePage(*ppPage); - goto getAndInitPage_error; + goto getAndInitPage_error2; } return SQLITE_OK; -getAndInitPage_error: +getAndInitPage_error2: + releasePage(*ppPage); +getAndInitPage_error1: if( pCur ){ pCur->iPage--; pCur->pPage = pCur->apPage[pCur->iPage]; @@ -65929,9 +66240,9 @@ static int newDatabase(BtShared*); static int lockBtree(BtShared *pBt){ int rc; /* Result code from subfunctions */ MemPage *pPage1; /* Page 1 of the database file */ - int nPage; /* Number of pages in the database */ - int nPageFile = 0; /* Number of pages in the database file */ - int nPageHeader; /* Number of pages in the database according to hdr */ + u32 nPage; /* Number of pages in the database */ + u32 nPageFile = 0; /* Number of pages in the database file */ + u32 nPageHeader; /* Number of pages in the database according to hdr */ assert( sqlite3_mutex_held(pBt->mutex) ); assert( pBt->pPage1==0 ); @@ -65944,7 +66255,7 @@ static int lockBtree(BtShared *pBt){ ** a valid database file. */ nPage = nPageHeader = get4byte(28+(u8*)pPage1->aData); - sqlite3PagerPagecount(pBt->pPager, &nPageFile); + sqlite3PagerPagecount(pBt->pPager, (int*)&nPageFile); if( nPage==0 || memcmp(24+(u8*)pPage1->aData, 92+(u8*)pPage1->aData,4)!=0 ){ nPage = nPageFile; } @@ -66025,6 +66336,7 @@ static int lockBtree(BtShared *pBt){ ){ goto page1_init_failed; } + pBt->btsFlags |= BTS_PAGESIZE_FIXED; assert( (pageSize & 7)==0 ); /* EVIDENCE-OF: R-59310-51205 The "reserved space" size in the 1-byte ** integer at offset 20 is the number of bytes of space at the end of @@ -66415,7 +66727,7 @@ static int setChildPtrmaps(MemPage *pPage){ for(i=0; ileaf ){ Pgno childPgno = get4byte(pCell); @@ -67341,6 +67653,7 @@ SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor *pCur){ sqlite3_free(pCur->aOverflow); sqlite3_free(pCur->pKey); sqlite3BtreeLeave(pBtree); + pCur->pBtree = 0; } return SQLITE_OK; } @@ -67439,6 +67752,25 @@ SQLITE_PRIVATE u32 sqlite3BtreePayloadSize(BtCursor *pCur){ return pCur->info.nPayload; } +/* +** Return an upper bound on the size of any record for the table +** that the cursor is pointing into. +** +** This is an optimization. Everything will still work if this +** routine always returns 2147483647 (which is the largest record +** that SQLite can handle) or more. But returning a smaller value might +** prevent large memory allocations when trying to interpret a +** corrupt datrabase. +** +** The current implementation merely returns the size of the underlying +** database file. +*/ +SQLITE_PRIVATE sqlite3_int64 sqlite3BtreeMaxRecordSize(BtCursor *pCur){ + assert( cursorHoldsMutex(pCur) ); + assert( pCur->eState==CURSOR_VALID ); + return pCur->pBt->pageSize * (sqlite3_int64)pCur->pBt->nPage; +} + /* ** Given the page number of an overflow page in the database (parameter ** ovfl), this function finds the page number of the next page in the @@ -68129,23 +68461,6 @@ SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){ return rc; } -/* -** This function is a no-op if cursor pCur does not point to a valid row. -** Otherwise, if pCur is valid, configure it so that the next call to -** sqlite3BtreeNext() is a no-op. -*/ -#ifndef SQLITE_OMIT_WINDOWFUNC -SQLITE_PRIVATE void sqlite3BtreeSkipNext(BtCursor *pCur){ - /* We believe that the cursor must always be in the valid state when - ** this routine is called, but the proof is difficult, so we add an - ** ALWaYS() test just in case we are wrong. */ - if( ALWAYS(pCur->eState==CURSOR_VALID) ){ - pCur->eState = CURSOR_SKIPNEXT; - pCur->skipNext = 1; - } -} -#endif /* SQLITE_OMIT_WINDOWFUNC */ - /* Move the cursor to the last entry in the table. Return SQLITE_OK ** on success. Set *pRes to 0 if the cursor actually points to something ** or set *pRes to 1 if the table is empty. @@ -68253,7 +68568,7 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( ** try to get there using sqlite3BtreeNext() rather than a full ** binary search. This is an optimization only. The correct answer ** is still obtained without this case, only a little more slowely */ - if( pCur->info.nKey+1==intKey && !pCur->skipNext ){ + if( pCur->info.nKey+1==intKey ){ *pRes = 0; rc = sqlite3BtreeNext(pCur, 0); if( rc==SQLITE_OK ){ @@ -68395,7 +68710,7 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( testcase( nCell==0 ); /* Invalid key size: 0x80 0x80 0x00 */ testcase( nCell==1 ); /* Invalid key size: 0x80 0x80 0x01 */ testcase( nCell==2 ); /* Minimum legal index key size */ - if( nCell<2 ){ + if( nCell<2 || nCell/pCur->pBt->usableSize>pCur->pBt->nPage ){ rc = SQLITE_CORRUPT_PAGE(pPage); goto moveto_finish; } @@ -68411,7 +68726,7 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( sqlite3_free(pCellKey); goto moveto_finish; } - c = xRecordCompare(nCell, pCellKey, pIdxKey); + c = sqlite3VdbeRecordCompare(nCell, pCellKey, pIdxKey); sqlite3_free(pCellKey); } assert( @@ -68527,7 +68842,6 @@ static SQLITE_NOINLINE int btreeNext(BtCursor *pCur){ MemPage *pPage; assert( cursorOwnsBtShared(pCur) ); - assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); if( pCur->eState!=CURSOR_VALID ){ assert( (pCur->curFlags & BTCF_ValidOvfl)==0 ); rc = restoreCursorPosition(pCur); @@ -68537,14 +68851,9 @@ static SQLITE_NOINLINE int btreeNext(BtCursor *pCur){ if( CURSOR_INVALID==pCur->eState ){ return SQLITE_DONE; } - if( pCur->skipNext ){ - assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_SKIPNEXT ); + if( pCur->eState==CURSOR_SKIPNEXT ){ pCur->eState = CURSOR_VALID; - if( pCur->skipNext>0 ){ - pCur->skipNext = 0; - return SQLITE_OK; - } - pCur->skipNext = 0; + if( pCur->skipNext>0 ) return SQLITE_OK; } } @@ -68599,7 +68908,6 @@ SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor *pCur, int flags){ UNUSED_PARAMETER( flags ); /* Used in COMDB2 but not native SQLite */ assert( cursorOwnsBtShared(pCur) ); assert( flags==0 || flags==1 ); - assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); pCur->info.nSize = 0; pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl); if( pCur->eState!=CURSOR_VALID ) return btreeNext(pCur); @@ -68640,7 +68948,6 @@ static SQLITE_NOINLINE int btreePrevious(BtCursor *pCur){ MemPage *pPage; assert( cursorOwnsBtShared(pCur) ); - assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); assert( (pCur->curFlags & (BTCF_AtLast|BTCF_ValidOvfl|BTCF_ValidNKey))==0 ); assert( pCur->info.nSize==0 ); if( pCur->eState!=CURSOR_VALID ){ @@ -68651,14 +68958,9 @@ static SQLITE_NOINLINE int btreePrevious(BtCursor *pCur){ if( CURSOR_INVALID==pCur->eState ){ return SQLITE_DONE; } - if( pCur->skipNext ){ - assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_SKIPNEXT ); + if( CURSOR_SKIPNEXT==pCur->eState ){ pCur->eState = CURSOR_VALID; - if( pCur->skipNext<0 ){ - pCur->skipNext = 0; - return SQLITE_OK; - } - pCur->skipNext = 0; + if( pCur->skipNext<0 ) return SQLITE_OK; } } @@ -68693,7 +68995,6 @@ static SQLITE_NOINLINE int btreePrevious(BtCursor *pCur){ SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor *pCur, int flags){ assert( cursorOwnsBtShared(pCur) ); assert( flags==0 || flags==1 ); - assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); UNUSED_PARAMETER( flags ); /* Used in COMDB2 but not native SQLite */ pCur->curFlags &= ~(BTCF_AtLast|BTCF_ValidOvfl|BTCF_ValidNKey); pCur->info.nSize = 0; @@ -69029,7 +69330,7 @@ static int allocateBtreePage( TRACE(("ALLOCATE: %d from end of file\n", *pPgno)); } - assert( *pPgno!=PENDING_BYTE_PAGE(pBt) ); + assert( CORRUPT_DB || *pPgno!=PENDING_BYTE_PAGE(pBt) ); end_allocate_page: releasePage(pTrunk); @@ -69057,13 +69358,15 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){ MemPage *pPage1 = pBt->pPage1; /* Local reference to page 1 */ MemPage *pPage; /* Page being freed. May be NULL. */ int rc; /* Return Code */ - int nFree; /* Initial number of pages on free-list */ + u32 nFree; /* Initial number of pages on free-list */ assert( sqlite3_mutex_held(pBt->mutex) ); assert( CORRUPT_DB || iPage>1 ); assert( !pMemPage || pMemPage->pgno==iPage ); - if( iPage<2 ) return SQLITE_CORRUPT_BKPT; + if( iPage<2 || iPage>pBt->nPage ){ + return SQLITE_CORRUPT_BKPT; + } if( pMemPage ){ pPage = pMemPage; sqlite3PagerRef(pPage->pDbPage); @@ -69474,6 +69777,7 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){ assert( CORRUPT_DB || sz==cellSize(pPage, idx) ); assert( sqlite3PagerIswriteable(pPage->pDbPage) ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); + assert( pPage->nFree>=0 ); data = pPage->aData; ptr = &pPage->aCellIdx[2*idx]; pc = get2byte(ptr); @@ -69544,6 +69848,7 @@ static void insertCell( ** might be less than 8 (leaf-size + pointer) on the interior node. Hence ** the term after the || in the following assert(). */ assert( sz==pPage->xCellSize(pPage, pCell) || (sz==8 && iChild>0) ); + assert( pPage->nFree>=0 ); if( pPage->nOverflow || sz+2>pPage->nFree ){ if( pTemp ){ memcpy(pTemp, pCell, sz); @@ -69584,9 +69889,16 @@ static void insertCell( assert( idx >= pPage->cellOffset+2*pPage->nCell+2 || CORRUPT_DB ); assert( idx+sz <= (int)pPage->pBt->usableSize ); pPage->nFree -= (u16)(2 + sz); - memcpy(&data[idx], pCell, sz); if( iChild ){ + /* In a corrupt database where an entry in the cell index section of + ** a btree page has a value of 3 or less, the pCell value might point + ** as many as 4 bytes in front of the start of the aData buffer for + ** the source page. Make sure this does not cause problems by not + ** reading the first 4 bytes */ + memcpy(&data[idx+4], pCell+4, sz-4); put4byte(&data[idx], iChild); + }else{ + memcpy(&data[idx], pCell, sz); } pIns = pPage->aCellIdx + i*2; memmove(pIns+2, pIns, 2*(pPage->nCell - i)); @@ -69594,21 +69906,100 @@ static void insertCell( pPage->nCell++; /* increment the cell count */ if( (++data[pPage->hdrOffset+4])==0 ) data[pPage->hdrOffset+3]++; - assert( get2byte(&data[pPage->hdrOffset+3])==pPage->nCell ); + assert( get2byte(&data[pPage->hdrOffset+3])==pPage->nCell || CORRUPT_DB ); #ifndef SQLITE_OMIT_AUTOVACUUM if( pPage->pBt->autoVacuum ){ /* The cell may contain a pointer to an overflow page. If so, write ** the entry for the overflow page into the pointer map. */ - ptrmapPutOvflPtr(pPage, pCell, pRC); + ptrmapPutOvflPtr(pPage, pPage, pCell, pRC); } #endif } } +/* +** The following parameters determine how many adjacent pages get involved +** in a balancing operation. NN is the number of neighbors on either side +** of the page that participate in the balancing operation. NB is the +** total number of pages that participate, including the target page and +** NN neighbors on either side. +** +** The minimum value of NN is 1 (of course). Increasing NN above 1 +** (to 2 or 3) gives a modest improvement in SELECT and DELETE performance +** in exchange for a larger degradation in INSERT and UPDATE performance. +** The value of NN appears to give the best results overall. +** +** (Later:) The description above makes it seem as if these values are +** tunable - as if you could change them and recompile and it would all work. +** But that is unlikely. NB has been 3 since the inception of SQLite and +** we have never tested any other value. +*/ +#define NN 1 /* Number of neighbors on either side of pPage */ +#define NB 3 /* (NN*2+1): Total pages involved in the balance */ + /* ** A CellArray object contains a cache of pointers and sizes for a ** consecutive sequence of cells that might be held on multiple pages. +** +** The cells in this array are the divider cell or cells from the pParent +** page plus up to three child pages. There are a total of nCell cells. +** +** pRef is a pointer to one of the pages that contributes cells. This is +** used to access information such as MemPage.intKey and MemPage.pBt->pageSize +** which should be common to all pages that contribute cells to this array. +** +** apCell[] and szCell[] hold, respectively, pointers to the start of each +** cell and the size of each cell. Some of the apCell[] pointers might refer +** to overflow cells. In other words, some apCel[] pointers might not point +** to content area of the pages. +** +** A szCell[] of zero means the size of that cell has not yet been computed. +** +** The cells come from as many as four different pages: +** +** ----------- +** | Parent | +** ----------- +** / | \ +** / | \ +** --------- --------- --------- +** |Child-1| |Child-2| |Child-3| +** --------- --------- --------- +** +** The order of cells is in the array is for an index btree is: +** +** 1. All cells from Child-1 in order +** 2. The first divider cell from Parent +** 3. All cells from Child-2 in order +** 4. The second divider cell from Parent +** 5. All cells from Child-3 in order +** +** For a table-btree (with rowids) the items 2 and 4 are empty because +** content exists only in leaves and there are no divider cells. +** +** For an index btree, the apEnd[] array holds pointer to the end of page +** for Child-1, the Parent, Child-2, the Parent (again), and Child-3, +** respectively. The ixNx[] array holds the number of cells contained in +** each of these 5 stages, and all stages to the left. Hence: +** +** ixNx[0] = Number of cells in Child-1. +** ixNx[1] = Number of cells in Child-1 plus 1 for first divider. +** ixNx[2] = Number of cells in Child-1 and Child-2 + 1 for 1st divider. +** ixNx[3] = Number of cells in Child-1 and Child-2 + both divider cells +** ixNx[4] = Total number of cells. +** +** For a table-btree, the concept is similar, except only apEnd[0]..apEnd[2] +** are used and they point to the leaf pages only, and the ixNx value are: +** +** ixNx[0] = Number of cells in Child-1. +** ixNx[1] = Number of cells in Child-1 and Child-2. +** ixNx[2] = Total number of cells. +** +** Sometimes when deleting, a child page can have zero cells. In those +** cases, ixNx[] entries with higher indexes, and the corresponding apEnd[] +** entries, shift down. The end result is that each ixNx[] entry should +** be larger than the previous */ typedef struct CellArray CellArray; struct CellArray { @@ -69616,6 +70007,8 @@ struct CellArray { MemPage *pRef; /* Reference page */ u8 **apCell; /* All cells begin balanced */ u16 *szCell; /* Local size of all cells in apCell[] */ + u8 *apEnd[NB*2]; /* MemPage.aDataEnd values */ + int ixNx[NB*2]; /* Index of at which we move to the next apEnd[] */ }; /* @@ -69666,36 +70059,59 @@ static u16 cachedCellSize(CellArray *p, int N){ ** responsibility of the caller to set it correctly. */ static int rebuildPage( - MemPage *pPg, /* Edit this page */ + CellArray *pCArray, /* Content to be added to page pPg */ + int iFirst, /* First cell in pCArray to use */ int nCell, /* Final number of cells on page */ - u8 **apCell, /* Array of cells */ - u16 *szCell /* Array of cell sizes */ + MemPage *pPg /* The page to be reconstructed */ ){ const int hdr = pPg->hdrOffset; /* Offset of header on pPg */ u8 * const aData = pPg->aData; /* Pointer to data for pPg */ const int usableSize = pPg->pBt->usableSize; u8 * const pEnd = &aData[usableSize]; - int i; + int i = iFirst; /* Which cell to copy from pCArray*/ + u32 j; /* Start of cell content area */ + int iEnd = i+nCell; /* Loop terminator */ u8 *pCellptr = pPg->aCellIdx; u8 *pTmp = sqlite3PagerTempSpace(pPg->pBt->pPager); u8 *pData; + int k; /* Current slot in pCArray->apEnd[] */ + u8 *pSrcEnd; /* Current pCArray->apEnd[k] value */ + + assert( i(u32)usableSize) ){ j = 0; } + memcpy(&pTmp[j], &aData[j], usableSize - j); - i = get2byte(&aData[hdr+5]); - memcpy(&pTmp[i], &aData[i], usableSize - i); + for(k=0; pCArray->ixNx[k]<=i && ALWAYS(kapEnd[k]; pData = pEnd; - for(i=0; iapCell[i]; + u16 sz = pCArray->szCell[i]; + assert( sz>0 ); if( SQLITE_WITHIN(pCell,aData,pEnd) ){ + if( ((uptr)(pCell+sz))>(uptr)pEnd ) return SQLITE_CORRUPT_BKPT; pCell = &pTmp[pCell - aData]; + }else if( (uptr)(pCell+sz)>(uptr)pSrcEnd + && (uptr)(pCell)<(uptr)pSrcEnd + ){ + return SQLITE_CORRUPT_BKPT; } - pData -= szCell[i]; + + pData -= sz; put2byte(pCellptr, (pData - aData)); pCellptr += 2; if( pData < pCellptr ) return SQLITE_CORRUPT_BKPT; - memcpy(pData, pCell, szCell[i]); - assert( szCell[i]==pPg->xCellSize(pPg, pCell) || CORRUPT_DB ); - testcase( szCell[i]!=pPg->xCellSize(pPg,pCell) ); + memcpy(pData, pCell, sz); + assert( sz==pPg->xCellSize(pPg, pCell) || CORRUPT_DB ); + testcase( sz!=pPg->xCellSize(pPg,pCell) ); + i++; + if( i>=iEnd ) break; + if( pCArray->ixNx[k]<=i ){ + k++; + pSrcEnd = pCArray->apEnd[k]; + } } /* The pPg->nFree field is now set incorrectly. The caller will fix it. */ @@ -69710,12 +70126,11 @@ static int rebuildPage( } /* -** Array apCell[] contains nCell pointers to b-tree cells. Array szCell -** contains the size in bytes of each such cell. This function attempts to -** add the cells stored in the array to page pPg. If it cannot (because -** the page needs to be defragmented before the cells will fit), non-zero -** is returned. Otherwise, if the cells are added successfully, zero is -** returned. +** The pCArray objects contains pointers to b-tree cells and the cell sizes. +** This function attempts to add the cells stored in the array to page pPg. +** If it cannot (because the page needs to be defragmented before the cells +** will fit), non-zero is returned. Otherwise, if the cells are added +** successfully, zero is returned. ** ** Argument pCellptr points to the first entry in the cell-pointer array ** (part of page pPg) to populate. After cell apCell[0] is written to the @@ -69737,18 +70152,23 @@ static int rebuildPage( static int pageInsertArray( MemPage *pPg, /* Page to add cells to */ u8 *pBegin, /* End of cell-pointer array */ - u8 **ppData, /* IN/OUT: Page content -area pointer */ + u8 **ppData, /* IN/OUT: Page content-area pointer */ u8 *pCellptr, /* Pointer to cell-pointer area */ int iFirst, /* Index of first cell to add */ int nCell, /* Number of cells to add to pPg */ CellArray *pCArray /* Array of cells */ ){ - int i; - u8 *aData = pPg->aData; - u8 *pData = *ppData; - int iEnd = iFirst + nCell; + int i = iFirst; /* Loop counter - cell index to insert */ + u8 *aData = pPg->aData; /* Complete page */ + u8 *pData = *ppData; /* Content area. A subset of aData[] */ + int iEnd = iFirst + nCell; /* End of loop. One past last cell to ins */ + int k; /* Current slot in pCArray->apEnd[] */ + u8 *pEnd; /* Maximum extent of cell data */ assert( CORRUPT_DB || pPg->hdrOffset==0 ); /* Never called on page 1 */ - for(i=iFirst; iixNx[k]<=i && ALWAYS(kapEnd[k]; + while( 1 /*Exit by break*/ ){ int sz, rc; u8 *pSlot; sz = cachedCellSize(pCArray, i); @@ -69763,20 +70183,33 @@ static int pageInsertArray( assert( (pSlot+sz)<=pCArray->apCell[i] || pSlot>=(pCArray->apCell[i]+sz) || CORRUPT_DB ); + if( (uptr)(pCArray->apCell[i]+sz)>(uptr)pEnd + && (uptr)(pCArray->apCell[i])<(uptr)pEnd + ){ + assert( CORRUPT_DB ); + (void)SQLITE_CORRUPT_BKPT; + return 1; + } memmove(pSlot, pCArray->apCell[i], sz); put2byte(pCellptr, (pSlot - aData)); pCellptr += 2; + i++; + if( i>=iEnd ) break; + if( pCArray->ixNx[k]<=i ){ + k++; + pEnd = pCArray->apEnd[k]; + } } *ppData = pData; return 0; } /* -** Array apCell[] contains nCell pointers to b-tree cells. Array szCell -** contains the size in bytes of each such cell. This function adds the -** space associated with each cell in the array that is currently stored -** within the body of pPg to the pPg free-list. The cell-pointers and other -** fields of the page are not updated. +** The pCArray object contains pointers to b-tree cells and their sizes. +** +** This function adds the space associated with each cell in the array +** that is currently stored within the body of pPg to the pPg free-list. +** The cell-pointers and other fields of the page are not updated. ** ** This function returns the total number of cells added to the free-list. */ @@ -69826,9 +70259,9 @@ static int pageFreeArray( } /* -** apCell[] and szCell[] contains pointers to and sizes of all cells in the -** pages being balanced. The current page, pPg, has pPg->nCell cells starting -** with apCell[iOld]. After balancing, this page should hold nNew cells +** pCArray contains pointers to and sizes of all cells in the page being +** balanced. The current page, pPg, has pPg->nCell cells starting with +** pCArray->apCell[iOld]. After balancing, this page should hold nNew cells ** starting at apCell[iNew]. ** ** This routine makes the necessary adjustments to pPg so that it contains @@ -69860,13 +70293,17 @@ static int editPage( #endif /* Remove cells from the start and end of the page */ + assert( nCell>=0 ); if( iOldnCell ) return SQLITE_CORRUPT_BKPT; memmove(pPg->aCellIdx, &pPg->aCellIdx[nShift*2], nCell*2); nCell -= nShift; } if( iNewEnd < iOldEnd ){ - nCell -= pageFreeArray(pPg, iNewEnd, iOldEnd - iNewEnd, pCArray); + int nTail = pageFreeArray(pPg, iNewEnd, iOldEnd - iNewEnd, pCArray); + assert( nCell>=nTail ); + nCell -= nTail; } pData = &aData[get2byteNotZero(&aData[hdr+5])]; @@ -69876,6 +70313,7 @@ static int editPage( if( iNew=0 ); pCellptr = pPg->aCellIdx; memmove(&pCellptr[nAdd*2], pCellptr, nCell*2); if( pageInsertArray( @@ -69890,7 +70328,9 @@ static int editPage( int iCell = (iOld + pPg->aiOvfl[i]) - iNew; if( iCell>=0 && iCellaCellIdx[iCell * 2]; - memmove(&pCellptr[2], pCellptr, (nCell - iCell) * 2); + if( nCell>iCell ){ + memmove(&pCellptr[2], pCellptr, (nCell - iCell) * 2); + } nCell++; if( pageInsertArray( pPg, pBegin, &pData, pCellptr, @@ -69900,6 +70340,7 @@ static int editPage( } /* Append cells to the end of the page */ + assert( nCell>=0 ); pCellptr = &pPg->aCellIdx[nCell*2]; if( pageInsertArray( pPg, pBegin, &pData, pCellptr, @@ -69928,24 +70369,9 @@ static int editPage( editpage_fail: /* Unable to edit this page. Rebuild it from scratch instead. */ populateCellCache(pCArray, iNew, nNew); - return rebuildPage(pPg, nNew, &pCArray->apCell[iNew], &pCArray->szCell[iNew]); + return rebuildPage(pCArray, iNew, nNew, pPg); } -/* -** The following parameters determine how many adjacent pages get involved -** in a balancing operation. NN is the number of neighbors on either side -** of the page that participate in the balancing operation. NB is the -** total number of pages that participate, including the target page and -** NN neighbors on either side. -** -** The minimum value of NN is 1 (of course). Increasing NN above 1 -** (to 2 or 3) gives a modest improvement in SELECT and DELETE performance -** in exchange for a larger degradation in INSERT and UPDATE performance. -** The value of NN appears to give the best results overall. -*/ -#define NN 1 /* Number of neighbors on either side of pPage */ -#define NB (NN*2+1) /* Total pages involved in the balance */ - #ifndef SQLITE_OMIT_QUICKBALANCE /* @@ -69980,9 +70406,10 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){ assert( sqlite3_mutex_held(pPage->pBt->mutex) ); assert( sqlite3PagerIswriteable(pParent->pDbPage) ); assert( pPage->nOverflow==1 ); - - /* This error condition is now caught prior to reaching this function */ - if( NEVER(pPage->nCell==0) ) return SQLITE_CORRUPT_BKPT; + + if( pPage->nCell==0 ) return SQLITE_CORRUPT_BKPT; /* dbfuzz001.test */ + assert( pPage->nFree>=0 ); + assert( pParent->nFree>=0 ); /* Allocate a new page. This page will become the right-sibling of ** pPage. Make the parent page writable, so that the new divider cell @@ -69996,12 +70423,22 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){ u8 *pCell = pPage->apOvfl[0]; u16 szCell = pPage->xCellSize(pPage, pCell); u8 *pStop; + CellArray b; assert( sqlite3PagerIswriteable(pNew->pDbPage) ); - assert( pPage->aData[0]==(PTF_INTKEY|PTF_LEAFDATA|PTF_LEAF) ); + assert( CORRUPT_DB || pPage->aData[0]==(PTF_INTKEY|PTF_LEAFDATA|PTF_LEAF) ); zeroPage(pNew, PTF_INTKEY|PTF_LEAFDATA|PTF_LEAF); - rc = rebuildPage(pNew, 1, &pCell, &szCell); - if( NEVER(rc) ) return rc; + b.nCell = 1; + b.pRef = pPage; + b.apCell = &pCell; + b.szCell = &szCell; + b.apEnd[0] = pPage->aDataEnd; + b.ixNx[0] = 2; + rc = rebuildPage(&b, 0, 1, pNew); + if( NEVER(rc) ){ + releasePage(pNew); + return rc; + } pNew->nFree = pBt->usableSize - pNew->cellOffset - 2 - szCell; /* If this is an auto-vacuum database, update the pointer map @@ -70016,7 +70453,7 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){ if( ISAUTOVACUUM ){ ptrmapPut(pBt, pgnoNew, PTRMAP_BTREE, pParent->pgno, &rc); if( szCell>pNew->minLocal ){ - ptrmapPutOvflPtr(pNew, pCell, &rc); + ptrmapPutOvflPtr(pNew, pNew, pCell, &rc); } } @@ -70142,6 +70579,7 @@ static void copyNodeContent(MemPage *pFrom, MemPage *pTo, int *pRC){ */ pTo->isInit = 0; rc = btreeInitPage(pTo); + if( rc==SQLITE_OK ) rc = btreeComputeFreeSpace(pTo); if( rc!=SQLITE_OK ){ *pRC = rc; return; @@ -70239,10 +70677,6 @@ static int balance_nonroot( assert( sqlite3_mutex_held(pBt->mutex) ); assert( sqlite3PagerIswriteable(pParent->pDbPage) ); -#if 0 - TRACE(("BALANCE: begin page %d child of %d\n", pPage->pgno, pParent->pgno)); -#endif - /* At this point pParent may have at most one overflow cell. And if ** this overflow cell is present, it must be the cell with ** index iParentIdx. This scenario comes about when this function @@ -70254,6 +70688,7 @@ static int balance_nonroot( if( !aOvflSpace ){ return SQLITE_NOMEM_BKPT; } + assert( pParent->nFree>=0 ); /* Find the sibling pages to balance. Also locate the cells in pParent ** that divide the siblings. An attempt is made to find NN siblings on @@ -70293,7 +70728,13 @@ static int balance_nonroot( memset(apOld, 0, (i+1)*sizeof(MemPage*)); goto balance_cleanup; } - nMaxCells += 1+apOld[i]->nCell+apOld[i]->nOverflow; + if( apOld[i]->nFree<0 ){ + rc = btreeComputeFreeSpace(apOld[i]); + if( rc ){ + memset(apOld, 0, (i)*sizeof(MemPage*)); + goto balance_cleanup; + } + } if( (i--)==0 ) break; if( pParent->nOverflow && i+nxDiv==pParent->aiOvfl[0] ){ @@ -70337,6 +70778,7 @@ static int balance_nonroot( /* Make nMaxCells a multiple of 4 in order to preserve 8-byte ** alignment */ + nMaxCells = nOld*(MX_CELL(pBt) + ArraySize(pParent->apOvfl)); nMaxCells = (nMaxCells + 3)&~3; /* @@ -70347,7 +70789,7 @@ static int balance_nonroot( + nMaxCells*sizeof(u16) /* b.szCell */ + pBt->pageSize; /* aSpace1 */ - assert( szScratch<=6*(int)pBt->pageSize ); + assert( szScratch<=7*(int)pBt->pageSize ); b.apCell = sqlite3StackAllocRaw(0, szScratch ); if( b.apCell==0 ){ rc = SQLITE_NOMEM_BKPT; @@ -70483,8 +70925,19 @@ static int balance_nonroot( ** */ usableSpace = pBt->usableSize - 12 + leafCorrection; - for(i=0; iaDataEnd; + b.ixNx[k] = cntOld[i]; + if( k && b.ixNx[k]==b.ixNx[k-1] ){ + k--; /* Omit b.ixNx[] entry for child pages with no cells */ + } + if( !leafData ){ + k++; + b.apEnd[k] = pParent->aDataEnd; + b.ixNx[k] = cntOld[i]+1; + } + assert( p->nFree>=0 ); szNew[i] = usableSpace - p->nFree; for(j=0; jnOverflow; j++){ szNew[i] += 2 + p->xCellSize(p, p->apOvfl[j]); @@ -70708,19 +71161,19 @@ static int balance_nonroot( ** populated, not here. */ if( ISAUTOVACUUM ){ - MemPage *pNew = apNew[0]; - u8 *aOld = pNew->aData; + MemPage *pOld; + MemPage *pNew = pOld = apNew[0]; int cntOldNext = pNew->nCell + pNew->nOverflow; - int usableSize = pBt->usableSize; int iNew = 0; int iOld = 0; for(i=0; inCell + pOld->nOverflow + !leafData; - aOld = pOld->aData; } if( i==cntNew[iNew] ){ pNew = apNew[++iNew]; @@ -70735,13 +71188,13 @@ static int balance_nonroot( ** overflow cell), we can skip updating the pointer map entries. */ if( iOld>=nNew || pNew->pgno!=aPgno[iOld] - || !SQLITE_WITHIN(pCell,aOld,&aOld[usableSize]) + || !SQLITE_WITHIN(pCell,pOld->aData,pOld->aDataEnd) ){ if( !leafCorrection ){ ptrmapPut(pBt, get4byte(pCell), PTRMAP_BTREE, pNew->pgno, &rc); } if( cachedCellSize(&b,i)>pNew->minLocal ){ - ptrmapPutOvflPtr(pNew, pCell, &rc); + ptrmapPutOvflPtr(pNew, pOld, pCell, &rc); } if( rc ) goto balance_cleanup; } @@ -70886,7 +71339,8 @@ static int balance_nonroot( rc = defragmentPage(apNew[0], -1); testcase( rc!=SQLITE_OK ); assert( apNew[0]->nFree == - (get2byte(&apNew[0]->aData[5])-apNew[0]->cellOffset-apNew[0]->nCell*2) + (get2byteNotZero(&apNew[0]->aData[5]) - apNew[0]->cellOffset + - apNew[0]->nCell*2) || rc!=SQLITE_OK ); copyNodeContent(apNew[0], pParent, &rc); @@ -70985,7 +71439,7 @@ static int balance_deeper(MemPage *pRoot, MemPage **ppChild){ } assert( sqlite3PagerIswriteable(pChild->pDbPage) ); assert( sqlite3PagerIswriteable(pRoot->pDbPage) ); - assert( pChild->nCell==pRoot->nCell ); + assert( pChild->nCell==pRoot->nCell || CORRUPT_DB ); TRACE(("BALANCE: copy root %d into %d\n", pRoot->pgno, pChild->pgno)); @@ -71027,6 +71481,7 @@ static int balance(BtCursor *pCur){ int iPage = pCur->iPage; MemPage *pPage = pCur->pPage; + if( NEVER(pPage->nFree<0) && btreeComputeFreeSpace(pPage) ) break; if( iPage==0 ){ if( pPage->nOverflow ){ /* The root page of the b-tree is overfull. In this case call the @@ -71055,6 +71510,9 @@ static int balance(BtCursor *pCur){ int const iIdx = pCur->aiIdx[iPage-1]; rc = sqlite3PagerWrite(pParent->pDbPage); + if( rc==SQLITE_OK && pParent->nFree<0 ){ + rc = btreeComputeFreeSpace(pParent); + } if( rc==SQLITE_OK ){ #ifndef SQLITE_OMIT_QUICKBALANCE if( pPage->intKeyLeaf @@ -71165,7 +71623,11 @@ static int btreeOverwriteContent( if( memcmp(pDest, ((u8*)pX->pData) + iOffset, iAmt)!=0 ){ int rc = sqlite3PagerWrite(pPage->pDbPage); if( rc ) return rc; - memcpy(pDest, ((u8*)pX->pData) + iOffset, iAmt); + /* In a corrupt database, it is possible for the source and destination + ** buffers to overlap. This is harmless since the database is already + ** corrupt but it does cause valgrind and ASAN warnings. So use + ** memmove(). */ + memmove(pDest, ((u8*)pX->pData) + iOffset, iAmt); } } return SQLITE_OK; @@ -71397,6 +71859,10 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( pPage = pCur->pPage; assert( pPage->intKey || pX->nKey>=0 ); assert( pPage->leaf || !pPage->intKey ); + if( pPage->nFree<0 ){ + rc = btreeComputeFreeSpace(pPage); + if( rc ) return rc; + } TRACE(("INSERT: table=%d nkey=%lld ndata=%d page=%d %s\n", pCur->pgnoRoot, pX->nKey, pX->nData, pPage->pgno, @@ -71539,14 +72005,18 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ assert( pCur->curFlags & BTCF_WriteFlag ); assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); assert( !hasReadConflicts(p, pCur->pgnoRoot) ); - assert( pCur->ixpPage->nCell ); - assert( pCur->eState==CURSOR_VALID ); assert( (flags & ~(BTREE_SAVEPOSITION | BTREE_AUXDELETE))==0 ); + if( pCur->eState==CURSOR_REQUIRESEEK ){ + rc = btreeRestoreCursorPosition(pCur); + if( rc ) return rc; + } + assert( pCur->eState==CURSOR_VALID ); iCellDepth = pCur->iPage; iCellIdx = pCur->ix; pPage = pCur->pPage; pCell = findCell(pPage, iCellIdx); + if( pPage->nFree<0 && btreeComputeFreeSpace(pPage) ) return SQLITE_CORRUPT; /* If the bPreserve flag is set to true, then the cursor position must ** be preserved following this delete operation. If the current delete @@ -71560,6 +72030,7 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ if( bPreserve ){ if( !pPage->leaf || (pPage->nFree+cellSizePtr(pPage,pCell)+2)>(int)(pBt->usableSize*2/3) + || pPage->nCell==1 /* See dbfuzz001.test for a test case */ ){ /* A b-tree rebalance will be required after deleting this entry. ** Save the cursor key. */ @@ -71616,6 +72087,10 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ Pgno n; unsigned char *pTmp; + if( pLeaf->nFree<0 ){ + rc = btreeComputeFreeSpace(pLeaf); + if( rc ) return rc; + } if( iCellDepthiPage-1 ){ n = pCur->apPage[iCellDepth+1]->pgno; }else{ @@ -71974,6 +72449,9 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){ assert( sqlite3BtreeHoldsMutex(p) ); assert( p->inTrans==TRANS_WRITE ); assert( iTable>=2 ); + if( iTable>btreePagecount(pBt) ){ + return SQLITE_CORRUPT_BKPT; + } rc = btreeGetPage(pBt, (Pgno)iTable, &pPage, 0); if( rc ) return rc; @@ -72322,10 +72800,10 @@ static void checkList( IntegrityCk *pCheck, /* Integrity checking context */ int isFreeList, /* True for a freelist. False for overflow page list */ int iPage, /* Page number for first page in the list */ - int N /* Expected number of pages in the list */ + u32 N /* Expected number of pages in the list */ ){ int i; - int expected = N; + u32 expected = N; int nErrAtStart = pCheck->nErr; while( iPage!=0 && pCheck->mxErr ){ DbPage *pOvflPage; @@ -72338,18 +72816,18 @@ static void checkList( } pOvflData = (unsigned char *)sqlite3PagerGetData(pOvflPage); if( isFreeList ){ - int n = get4byte(&pOvflData[4]); + u32 n = (u32)get4byte(&pOvflData[4]); #ifndef SQLITE_OMIT_AUTOVACUUM if( pCheck->pBt->autoVacuum ){ checkPtrmap(pCheck, iPage, PTRMAP_FREEPAGE, 0); } #endif - if( n>(int)pCheck->pBt->usableSize/4-2 ){ + if( n>pCheck->pBt->usableSize/4-2 ){ checkAppendMsg(pCheck, "freelist leaf count too big on page %d", iPage); N--; }else{ - for(i=0; ipBt->autoVacuum ){ @@ -72507,6 +72985,11 @@ static int checkTreePage( "btreeInitPage() returns error code %d", rc); goto end_of_check; } + if( (rc = btreeComputeFreeSpace(pPage))!=0 ){ + assert( rc==SQLITE_CORRUPT ); + checkAppendMsg(pCheck, "free space corruption", rc); + goto end_of_check; + } data = pPage->aData; hdr = pPage->hdrOffset; @@ -72579,7 +73062,7 @@ static int checkTreePage( /* Check the content overflow list */ if( info.nPayload>info.nLocal ){ - int nPage; /* Number of pages on the overflow chain */ + u32 nPage; /* Number of pages on the overflow chain */ Pgno pgnoOvfl; /* First page of the overflow chain */ assert( pc + info.nSize - 4 <= usableSize ); nPage = (info.nPayload - info.nLocal + usableSize - 5)/(usableSize - 4); @@ -72639,9 +73122,9 @@ static int checkTreePage( i = get2byte(&data[hdr+1]); while( i>0 ){ int size, j; - assert( (u32)i<=usableSize-4 ); /* Enforced by btreeInitPage() */ + assert( (u32)i<=usableSize-4 ); /* Enforced by btreeComputeFreeSpace() */ size = get2byte(&data[i+2]); - assert( (u32)(i+size)<=usableSize ); /* Enforced by btreeInitPage() */ + assert( (u32)(i+size)<=usableSize ); /* due to btreeComputeFreeSpace() */ btreeHeapInsert(heap, (((u32)i)<<16)|(i+size-1)); /* EVIDENCE-OF: R-58208-19414 The first 2 bytes of a freeblock are a ** big-endian integer which is the offset in the b-tree page of the next @@ -72650,8 +73133,8 @@ static int checkTreePage( j = get2byte(&data[i]); /* EVIDENCE-OF: R-06866-39125 Freeblocks are always connected in order of ** increasing offset. */ - assert( j==0 || j>i+size ); /* Enforced by btreeInitPage() */ - assert( (u32)j<=usableSize-4 ); /* Enforced by btreeInitPage() */ + assert( j==0 || j>i+size ); /* Enforced by btreeComputeFreeSpace() */ + assert( (u32)j<=usableSize-4 ); /* Enforced by btreeComputeFreeSpace() */ i = j; } /* Analyze the min-heap looking for overlap between cells and/or @@ -72726,7 +73209,7 @@ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck( Pgno i; IntegrityCk sCheck; BtShared *pBt = p->pBt; - int savedDbFlags = pBt->db->flags; + u64 savedDbFlags = pBt->db->flags; char zErr[100]; VVA_ONLY( int nRef ); @@ -72793,7 +73276,7 @@ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck( } #endif testcase( pBt->db->flags & SQLITE_CellSizeCk ); - pBt->db->flags &= ~SQLITE_CellSizeCk; + pBt->db->flags &= ~(u64)SQLITE_CellSizeCk; for(i=0; (int)iflags&MEM_Static)!=0 ? 1 : 0) <= 1 ); /* No other bits set */ - assert( (p->flags & ~(MEM_Null|MEM_Term|MEM_Subtype + assert( (p->flags & ~(MEM_Null|MEM_Term|MEM_Subtype|MEM_FromBind |MEM_Dyn|MEM_Ephem|MEM_Static))==0 ); }else{ /* A pure NULL might have other flags, such as MEM_Static, MEM_Dyn, @@ -74116,8 +74599,7 @@ SQLITE_PRIVATE int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){ } /* -** Make sure pMem->z points to a writable allocation of at least -** min(n,32) bytes. +** Make sure pMem->z points to a writable allocation of at least n bytes. ** ** If the bPreserve argument is true, then copy of the content of ** pMem->z into the new allocation. pMem must be either a string or @@ -74136,7 +74618,6 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemGrow(Mem *pMem, int n, int bPre assert( pMem->szMalloc==0 || pMem->szMalloc==sqlite3DbMallocSize(pMem->db, pMem->zMalloc) ); - if( n<32 ) n = 32; if( pMem->szMalloc>0 && bPreserve && pMem->z==pMem->zMalloc ){ pMem->z = pMem->zMalloc = sqlite3DbReallocOrFree(pMem->db, pMem->z, n); bPreserve = 0; @@ -74181,7 +74662,7 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemGrow(Mem *pMem, int n, int bPre ** if unable to complete the resizing. */ SQLITE_PRIVATE int sqlite3VdbeMemClearAndResize(Mem *pMem, int szNew){ - assert( szNew>0 ); + assert( CORRUPT_DB || szNew>0 ); assert( (pMem->flags & MEM_Dyn)==0 || pMem->szMalloc==0 ); if( pMem->szMallocflags & MEM_Zero ); - assert( pMem->flags&MEM_Blob ); + assert( (pMem->flags&MEM_Blob)!=0 || MemNullNochng(pMem) ); + testcase( sqlite3_value_nochange(pMem) ); assert( !sqlite3VdbeMemIsRowSet(pMem) ); assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); /* Set nByte to the number of bytes required to store the expanded blob. */ nByte = pMem->n + pMem->u.nZero; if( nByte<=0 ){ + if( (pMem->flags & MEM_Blob)==0 ) return SQLITE_OK; nByte = 1; } if( sqlite3VdbeMemGrow(pMem, nByte, 1) ){ @@ -74985,7 +75468,6 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetStr( assert( enc!=0 ); if( enc==SQLITE_UTF8 ){ nByte = 0x7fffffff & (int)strlen(z); - if( nByte>iLimit ) nByte = iLimit+1; }else{ for(nByte=0; nByte<=iLimit && (z[nByte] | z[nByte+1]); nByte+=2){} } @@ -74997,29 +75479,30 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetStr( ** management (one of MEM_Dyn or MEM_Static). */ if( xDel==SQLITE_TRANSIENT ){ - int nAlloc = nByte; + u32 nAlloc = nByte; if( flags&MEM_Term ){ nAlloc += (enc==SQLITE_UTF8?1:2); } if( nByte>iLimit ){ - return SQLITE_TOOBIG; + return sqlite3ErrorToParser(pMem->db, SQLITE_TOOBIG); } testcase( nAlloc==0 ); testcase( nAlloc==31 ); testcase( nAlloc==32 ); - if( sqlite3VdbeMemClearAndResize(pMem, MAX(nAlloc,32)) ){ + if( sqlite3VdbeMemClearAndResize(pMem, (int)MAX(nAlloc,32)) ){ return SQLITE_NOMEM_BKPT; } memcpy(pMem->z, z, nAlloc); - }else if( xDel==SQLITE_DYNAMIC ){ - sqlite3VdbeMemRelease(pMem); - pMem->zMalloc = pMem->z = (char *)z; - pMem->szMalloc = sqlite3DbMallocSize(pMem->db, pMem->zMalloc); }else{ sqlite3VdbeMemRelease(pMem); pMem->z = (char *)z; - pMem->xDel = xDel; - flags |= ((xDel==SQLITE_STATIC)?MEM_Static:MEM_Dyn); + if( xDel==SQLITE_DYNAMIC ){ + pMem->zMalloc = pMem->z; + pMem->szMalloc = sqlite3DbMallocSize(pMem->db, pMem->zMalloc); + }else{ + pMem->xDel = xDel; + flags |= ((xDel==SQLITE_STATIC)?MEM_Static:MEM_Dyn); + } } pMem->n = nByte; @@ -75062,6 +75545,9 @@ static SQLITE_NOINLINE int vdbeMemFromBtreeResize( ){ int rc; pMem->flags = MEM_Null; + if( sqlite3BtreeMaxRecordSize(pCur)z); if( rc==SQLITE_OK ){ @@ -75468,9 +75954,11 @@ static int valueFromExpr( } #endif else if( op==TK_TRUEFALSE ){ - pVal = valueNew(db, pCtx); - pVal->flags = MEM_Int; - pVal->u.i = pExpr->u.zToken[4]==0; + pVal = valueNew(db, pCtx); + if( pVal ){ + pVal->flags = MEM_Int; + pVal->u.i = pExpr->u.zToken[4]==0; + } } *ppVal = pVal; @@ -75863,7 +76351,7 @@ SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(Parse *pParse){ pParse->pVdbe = p; assert( pParse->aLabel==0 ); assert( pParse->nLabel==0 ); - assert( pParse->nOpAlloc==0 ); + assert( p->nOpAlloc==0 ); assert( pParse->szOpAlloc==0 ); sqlite3VdbeAddOp2(p, OP_Init, 0, 1); return p; @@ -75891,14 +76379,44 @@ SQLITE_PRIVATE void sqlite3VdbeSetSql(Vdbe *p, const char *z, int n, u8 prepFlag } assert( p->zSql==0 ); p->zSql = sqlite3DbStrNDup(p->db, z, n); +} + #ifdef SQLITE_ENABLE_NORMALIZE - assert( p->zNormSql==0 ); - if( p->zSql && (prepFlags & SQLITE_PREPARE_NORMALIZE)!=0 ){ - sqlite3Normalize(p, p->zSql, n, prepFlags); - assert( p->zNormSql!=0 || p->db->mallocFailed ); +/* +** Add a new element to the Vdbe->pDblStr list. +*/ +SQLITE_PRIVATE void sqlite3VdbeAddDblquoteStr(sqlite3 *db, Vdbe *p, const char *z){ + if( p ){ + int n = sqlite3Strlen30(z); + DblquoteStr *pStr = sqlite3DbMallocRawNN(db, + sizeof(*pStr)+n+1-sizeof(pStr->z)); + if( pStr ){ + pStr->pNextStr = p->pDblStr; + p->pDblStr = pStr; + memcpy(pStr->z, z, n+1); + } } +} #endif + +#ifdef SQLITE_ENABLE_NORMALIZE +/* +** zId of length nId is a double-quoted identifier. Check to see if +** that identifier is really used as a string literal. +*/ +SQLITE_PRIVATE int sqlite3VdbeUsesDoubleQuotedString( + Vdbe *pVdbe, /* The prepared statement */ + const char *zId /* The double-quoted identifier, already dequoted */ +){ + DblquoteStr *pStr; + assert( zId!=0 ); + if( pVdbe->pDblStr==0 ) return 0; + for(pStr=pVdbe->pDblStr; pStr; pStr=pStr->pNextStr){ + if( strcmp(zId, pStr->z)==0 ) return 1; + } + return 0; } +#endif /* ** Swap all content between two VDBE structures. @@ -75919,7 +76437,7 @@ SQLITE_PRIVATE void sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){ zTmp = pA->zSql; pA->zSql = pB->zSql; pB->zSql = zTmp; -#ifdef SQLITE_ENABLE_NORMALIZE +#if 0 zTmp = pA->zNormSql; pA->zNormSql = pB->zNormSql; pB->zNormSql = zTmp; @@ -75936,7 +76454,7 @@ SQLITE_PRIVATE void sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){ ** to 1024/sizeof(Op). ** ** If an out-of-memory error occurs while resizing the array, return -** SQLITE_NOMEM. In this case Vdbe.aOp and Parse.nOpAlloc remain +** SQLITE_NOMEM. In this case Vdbe.aOp and Vdbe.nOpAlloc remain ** unchanged (this is so that any opcodes already allocated can be ** correctly deallocated along with the rest of the Vdbe). */ @@ -75952,9 +76470,11 @@ static int growOpArray(Vdbe *v, int nOp){ ** operation (without SQLITE_TEST_REALLOC_STRESS) is to double the current ** size of the op array or add 1KB of space, whichever is smaller. */ #ifdef SQLITE_TEST_REALLOC_STRESS - int nNew = (p->nOpAlloc>=512 ? p->nOpAlloc*2 : p->nOpAlloc+nOp); + sqlite3_int64 nNew = (v->nOpAlloc>=512 ? 2*(sqlite3_int64)v->nOpAlloc + : (sqlite3_int64)v->nOpAlloc+nOp); #else - int nNew = (p->nOpAlloc ? p->nOpAlloc*2 : (int)(1024/sizeof(Op))); + sqlite3_int64 nNew = (v->nOpAlloc ? 2*(sqlite3_int64)v->nOpAlloc + : (sqlite3_int64)(1024/sizeof(Op))); UNUSED_PARAMETER(nOp); #endif @@ -75965,11 +76485,11 @@ static int growOpArray(Vdbe *v, int nOp){ } assert( nOp<=(1024/sizeof(Op)) ); - assert( nNew>=(p->nOpAlloc+nOp) ); + assert( nNew>=(v->nOpAlloc+nOp) ); pNew = sqlite3DbRealloc(p->db, v->aOp, nNew*sizeof(Op)); if( pNew ){ p->szOpAlloc = sqlite3DbMallocSize(p->db, pNew); - p->nOpAlloc = p->szOpAlloc/sizeof(Op); + v->nOpAlloc = p->szOpAlloc/sizeof(Op); v->aOp = pNew; } return (pNew ? SQLITE_OK : SQLITE_NOMEM_BKPT); @@ -76003,9 +76523,9 @@ static void test_addop_breakpoint(void){ ** operand. */ static SQLITE_NOINLINE int growOp3(Vdbe *p, int op, int p1, int p2, int p3){ - assert( p->pParse->nOpAlloc<=p->nOp ); + assert( p->nOpAlloc<=p->nOp ); if( growOpArray(p, 1) ) return 1; - assert( p->pParse->nOpAlloc>p->nOp ); + assert( p->nOpAlloc>p->nOp ); return sqlite3VdbeAddOp3(p, op, p1, p2, p3); } SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){ @@ -76015,7 +76535,7 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){ i = p->nOp; assert( p->magic==VDBE_MAGIC_INIT ); assert( op>=0 && op<0xff ); - if( p->pParse->nOpAlloc<=i ){ + if( p->nOpAlloc<=i ){ return growOp3(p, op, p1, p2, p3); } p->nOp++; @@ -76147,13 +76667,29 @@ SQLITE_PRIVATE int sqlite3VdbeExplainParent(Parse *pParse){ } /* -** Add a new OP_Explain opcode. +** Set a debugger breakpoint on the following routine in order to +** monitor the EXPLAIN QUERY PLAN code generation. +*/ +#if defined(SQLITE_DEBUG) +SQLITE_PRIVATE void sqlite3ExplainBreakpoint(const char *z1, const char *z2){ + (void)z1; + (void)z2; +} +#endif + +/* +** Add a new OP_ opcode. ** ** If the bPush flag is true, then make this opcode the parent for ** subsequent Explains until sqlite3VdbeExplainPop() is called. */ SQLITE_PRIVATE void sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt, ...){ - if( pParse->explain==2 ){ +#ifndef SQLITE_DEBUG + /* Always include the OP_Explain opcodes if SQLITE_DEBUG is defined. + ** But omit them (for performance) during production builds */ + if( pParse->explain==2 ) +#endif + { char *zMsg; Vdbe *v; va_list ap; @@ -76165,7 +76701,10 @@ SQLITE_PRIVATE void sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt iThis = v->nOp; sqlite3VdbeAddOp4(v, OP_Explain, iThis, pParse->addrExplain, 0, zMsg, P4_DYNAMIC); - if( bPush) pParse->addrExplain = iThis; + sqlite3ExplainBreakpoint(bPush?"PUSH":"", sqlite3VdbeGetOp(v,-1)->p4.z); + if( bPush){ + pParse->addrExplain = iThis; + } } } @@ -76173,6 +76712,7 @@ SQLITE_PRIVATE void sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt ** Pop the EXPLAIN QUERY PLAN stack one level. */ SQLITE_PRIVATE void sqlite3VdbeExplainPop(Parse *pParse){ + sqlite3ExplainBreakpoint("POP", 0); pParse->addrExplain = sqlite3VdbeExplainParent(pParse); } #endif /* SQLITE_OMIT_EXPLAIN */ @@ -76237,21 +76777,22 @@ SQLITE_PRIVATE void sqlite3VdbeEndCoroutine(Vdbe *v, int regYield){ ** The VDBE knows that a P2 value is a label because labels are ** always negative and P2 values are suppose to be non-negative. ** Hence, a negative P2 value is a label that has yet to be resolved. +** (Later:) This is only true for opcodes that have the OPFLG_JUMP +** property. ** -** Zero is returned if a malloc() fails. +** Variable usage notes: +** +** Parse.aLabel[x] Stores the address that the x-th label resolves +** into. For testing (SQLITE_DEBUG), unresolved +** labels stores -1, but that is not required. +** Parse.nLabelAlloc Number of slots allocated to Parse.aLabel[] +** Parse.nLabel The *negative* of the number of labels that have +** been issued. The negative is stored because +** that gives a performance improvement over storing +** the equivalent positive value. */ -SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe *v){ - Parse *p = v->pParse; - int i = p->nLabel++; - assert( v->magic==VDBE_MAGIC_INIT ); - if( (i & (i-1))==0 ){ - p->aLabel = sqlite3DbReallocOrFree(p->db, p->aLabel, - (i*2+1)*sizeof(p->aLabel[0])); - } - if( p->aLabel ){ - p->aLabel[i] = -1; - } - return ADDR(i); +SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Parse *pParse){ + return --pParse->nLabel; } /* @@ -76259,18 +76800,35 @@ SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe *v){ ** be inserted. The parameter "x" must have been obtained from ** a prior call to sqlite3VdbeMakeLabel(). */ +static SQLITE_NOINLINE void resizeResolveLabel(Parse *p, Vdbe *v, int j){ + int nNewSize = 10 - p->nLabel; + p->aLabel = sqlite3DbReallocOrFree(p->db, p->aLabel, + nNewSize*sizeof(p->aLabel[0])); + if( p->aLabel==0 ){ + p->nLabelAlloc = 0; + }else{ +#ifdef SQLITE_DEBUG + int i; + for(i=p->nLabelAlloc; iaLabel[i] = -1; +#endif + p->nLabelAlloc = nNewSize; + p->aLabel[j] = v->nOp; + } +} SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe *v, int x){ Parse *p = v->pParse; int j = ADDR(x); assert( v->magic==VDBE_MAGIC_INIT ); - assert( jnLabel ); + assert( j<-p->nLabel ); assert( j>=0 ); - if( p->aLabel ){ #ifdef SQLITE_DEBUG - if( p->db->flags & SQLITE_VdbeAddopTrace ){ - printf("RESOLVE LABEL %d to %d\n", x, v->nOp); - } + if( p->db->flags & SQLITE_VdbeAddopTrace ){ + printf("RESOLVE LABEL %d to %d\n", x, v->nOp); + } #endif + if( p->nLabelAlloc + p->nLabel < 0 ){ + resizeResolveLabel(p,v,j); + }else{ assert( p->aLabel[j]==(-1) ); /* Labels may only be resolved once */ p->aLabel[j] = v->nOp; } @@ -76395,8 +76953,10 @@ SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){ while( (pOp = opIterNext(&sIter))!=0 ){ int opcode = pOp->opcode; if( opcode==OP_Destroy || opcode==OP_VUpdate || opcode==OP_VRename + || opcode==OP_VDestroy + || (opcode==OP_Function0 && pOp->p4.pFunc->funcFlags&SQLITE_FUNC_INTERNAL) || ((opcode==OP_Halt || opcode==OP_HaltIfNull) - && ((pOp->p1&0xff)==SQLITE_CONSTRAINT && pOp->p2==OE_Abort)) + && ((pOp->p1)!=SQLITE_OK && pOp->p2==OE_Abort)) ){ hasAbort = 1; break; @@ -76545,7 +77105,7 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ ** non-jump opcodes less than SQLITE_MX_JUMP_CODE are guaranteed to ** have non-negative values for P2. */ assert( (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)!=0 ); - assert( ADDR(pOp->p2)nLabel ); + assert( ADDR(pOp->p2)<-pParse->nLabel ); pOp->p2 = aLabel[ADDR(pOp->p2)]; } break; @@ -76584,7 +77144,7 @@ SQLITE_PRIVATE int sqlite3VdbeCurrentAddr(Vdbe *p){ */ #if defined(SQLITE_DEBUG) && !defined(SQLITE_TEST_REALLOC_STRESS) SQLITE_PRIVATE void sqlite3VdbeVerifyNoMallocRequired(Vdbe *p, int N){ - assert( p->nOp + N <= p->pParse->nOpAlloc ); + assert( p->nOp + N <= p->nOpAlloc ); } #endif @@ -76656,7 +77216,7 @@ SQLITE_PRIVATE VdbeOp *sqlite3VdbeAddOpList( VdbeOp *pOut, *pFirst; assert( nOp>0 ); assert( p->magic==VDBE_MAGIC_INIT ); - if( p->nOp + nOp > p->pParse->nOpAlloc && growOpArray(p, nOp) ){ + if( p->nOp + nOp > p->nOpAlloc && growOpArray(p, nOp) ){ return 0; } pFirst = pOut = &p->aOp[p->nOp]; @@ -76702,7 +77262,7 @@ SQLITE_PRIVATE void sqlite3VdbeScanStatus( LogEst nEst, /* Estimated number of output rows */ const char *zName /* Name of table or index being scanned */ ){ - int nByte = (p->nScan+1) * sizeof(ScanStatus); + sqlite3_int64 nByte = (p->nScan+1) * sizeof(ScanStatus); ScanStatus *aNew; aNew = (ScanStatus*)sqlite3DbRealloc(p->db, p->aScan, nByte); if( aNew ){ @@ -77823,9 +78383,9 @@ SQLITE_PRIVATE void sqlite3VdbeIOTraceSql(Vdbe *p){ ** of a ReusableSpace object by the allocSpace() routine below. */ struct ReusableSpace { - u8 *pSpace; /* Available memory */ - int nFree; /* Bytes of available memory */ - int nNeeded; /* Total bytes that could not be allocated */ + u8 *pSpace; /* Available memory */ + sqlite3_int64 nFree; /* Bytes of available memory */ + sqlite3_int64 nNeeded; /* Total bytes that could not be allocated */ }; /* Try to allocate nByte bytes of 8-byte aligned bulk memory for pBuf @@ -77845,7 +78405,7 @@ struct ReusableSpace { static void *allocSpace( struct ReusableSpace *p, /* Bulk memory available for allocation */ void *pBuf, /* Pointer to a prior allocation */ - int nByte /* Bytes of memory needed */ + sqlite3_int64 nByte /* Bytes of memory needed */ ){ assert( EIGHT_BYTE_ALIGNMENT(p->pSpace) ); if( pBuf==0 ){ @@ -77978,19 +78538,27 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady( ** the leftover memory at the end of the opcode array. This can significantly ** reduce the amount of memory held by a prepared statement. */ - do { - x.nNeeded = 0; - p->aMem = allocSpace(&x, p->aMem, nMem*sizeof(Mem)); - p->aVar = allocSpace(&x, p->aVar, nVar*sizeof(Mem)); - p->apArg = allocSpace(&x, p->apArg, nArg*sizeof(Mem*)); - p->apCsr = allocSpace(&x, p->apCsr, nCursor*sizeof(VdbeCursor*)); + x.nNeeded = 0; + p->aMem = allocSpace(&x, 0, nMem*sizeof(Mem)); + p->aVar = allocSpace(&x, 0, nVar*sizeof(Mem)); + p->apArg = allocSpace(&x, 0, nArg*sizeof(Mem*)); + p->apCsr = allocSpace(&x, 0, nCursor*sizeof(VdbeCursor*)); #ifdef SQLITE_ENABLE_STMT_SCANSTATUS - p->anExec = allocSpace(&x, p->anExec, p->nOp*sizeof(i64)); + p->anExec = allocSpace(&x, 0, p->nOp*sizeof(i64)); #endif - if( x.nNeeded==0 ) break; + if( x.nNeeded ){ x.pSpace = p->pFree = sqlite3DbMallocRawNN(db, x.nNeeded); x.nFree = x.nNeeded; - }while( !db->mallocFailed ); + if( !db->mallocFailed ){ + p->aMem = allocSpace(&x, p->aMem, nMem*sizeof(Mem)); + p->aVar = allocSpace(&x, p->aVar, nVar*sizeof(Mem)); + p->apArg = allocSpace(&x, p->apArg, nArg*sizeof(Mem*)); + p->apCsr = allocSpace(&x, p->apCsr, nCursor*sizeof(VdbeCursor*)); +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS + p->anExec = allocSpace(&x, p->anExec, p->nOp*sizeof(i64)); +#endif + } + } p->pVList = pParse->pVList; pParse->pVList = 0; @@ -78682,7 +79250,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){ }else{ db->nDeferredCons = 0; db->nDeferredImmCons = 0; - db->flags &= ~SQLITE_DeferFKs; + db->flags &= ~(u64)SQLITE_DeferFKs; sqlite3CommitInternalChanges(db); } }else{ @@ -78997,6 +79565,13 @@ SQLITE_PRIVATE void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){ sqlite3DbFree(db, p->zSql); #ifdef SQLITE_ENABLE_NORMALIZE sqlite3DbFree(db, p->zNormSql); + { + DblquoteStr *pThis, *pNext; + for(pThis=p->pDblStr; pThis; pThis=pNext){ + pNext = pThis->pNextStr; + sqlite3DbFree(db, pThis); + } + } #endif #ifdef SQLITE_ENABLE_STMT_SCANSTATUS { @@ -79537,7 +80112,7 @@ SQLITE_PRIVATE void sqlite3VdbeRecordUnpack( UnpackedRecord *p /* Populate this structure before returning. */ ){ const unsigned char *aKey = (const unsigned char *)pKey; - int d; + u32 d; u32 idx; /* Offset in aKey[] to read from */ u16 u; /* Unsigned loop counter */ u32 szHdr; @@ -79548,7 +80123,7 @@ SQLITE_PRIVATE void sqlite3VdbeRecordUnpack( idx = getVarint32(aKey, szHdr); d = szHdr; u = 0; - while( idx=p->nField ) break; } + if( d>(u32)nKey && u ){ + assert( CORRUPT_DB ); + /* In a corrupt record entry, the last pMem might have been set up using + ** uninitialized memory. Overwrite its value with NULL, to prevent + ** warnings from MSAN. */ + sqlite3VdbeMemSetNull(pMem-1); + } assert( u<=pKeyInfo->nKeyField + 1 ); p->nField = u; } @@ -79626,8 +80208,8 @@ static int vdbeRecordCompareDebug( ** Use that approximation to avoid the more expensive call to ** sqlite3VdbeSerialTypeLen() in the common case. */ - if( d1+serial_type1+2>(u32)nKey1 - && d1+sqlite3VdbeSerialTypeLen(serial_type1)>(u32)nKey1 + if( d1+(u64)serial_type1+2>(u64)nKey1 + && d1+(u64)sqlite3VdbeSerialTypeLen(serial_type1)>(u64)nKey1 ){ break; } @@ -79638,7 +80220,8 @@ static int vdbeRecordCompareDebug( /* Do the comparison */ - rc = sqlite3MemCompare(&mem1, &pPKey2->aMem[i], pKeyInfo->aColl[i]); + rc = sqlite3MemCompare(&mem1, &pPKey2->aMem[i], + pKeyInfo->nAllField>i ? pKeyInfo->aColl[i] : 0); if( rc!=0 ){ assert( mem1.szMalloc==0 ); /* See comment below */ if( pKeyInfo->aSortOrder[i] ){ @@ -79994,12 +80577,12 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip( }else{ idx1 = getVarint32(aKey1, szHdr1); d1 = szHdr1; - if( d1>(unsigned)nKey1 ){ - pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT; - return 0; /* Corruption */ - } i = 0; } + if( d1>(unsigned)nKey1 ){ + pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT; + return 0; /* Corruption */ + } VVA_ONLY( mem1.szMalloc = 0; ) /* Only needed by assert() statements */ assert( pPKey2->pKeyInfo->nAllField>=pPKey2->nField @@ -80069,10 +80652,12 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip( mem1.n = (serial_type - 12) / 2; testcase( (d1+mem1.n)==(unsigned)nKey1 ); testcase( (d1+mem1.n+1)==(unsigned)nKey1 ); - if( (d1+mem1.n) > (unsigned)nKey1 ){ + if( (d1+mem1.n) > (unsigned)nKey1 + || (pKeyInfo = pPKey2->pKeyInfo)->nAllField<=i + ){ pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT; return 0; /* Corruption */ - }else if( (pKeyInfo = pPKey2->pKeyInfo)->aColl[i] ){ + }else if( pKeyInfo->aColl[i] ){ mem1.enc = pKeyInfo->enc; mem1.db = pKeyInfo->db; mem1.flags = MEM_Str; @@ -80772,14 +81357,16 @@ static SQLITE_NOINLINE void invokeProfileCallback(sqlite3 *db, Vdbe *p){ sqlite3_int64 iNow; sqlite3_int64 iElapse; assert( p->startTime>0 ); - assert( db->xProfile!=0 || (db->mTrace & SQLITE_TRACE_PROFILE)!=0 ); + assert( (db->mTrace & (SQLITE_TRACE_PROFILE|SQLITE_TRACE_XPROFILE))!=0 ); assert( db->init.busy==0 ); assert( p->zSql!=0 ); sqlite3OsCurrentTimeInt64(db->pVfs, &iNow); iElapse = (iNow - p->startTime)*1000000; +#ifndef SQLITE_OMIT_DEPRECATED if( db->xProfile ){ db->xProfile(db->pProfileArg, p->zSql, iElapse); } +#endif if( db->mTrace & SQLITE_TRACE_PROFILE ){ db->xTrace(SQLITE_TRACE_PROFILE, db->pTraceArg, p, (void*)&iElapse); } @@ -80983,6 +81570,11 @@ SQLITE_API int sqlite3_value_nochange(sqlite3_value *pVal){ return (pVal->flags&(MEM_Null|MEM_Zero))==(MEM_Null|MEM_Zero); } +/* Return true if a parameter value originated from an sqlite3_bind() */ +SQLITE_API int sqlite3_value_frombind(sqlite3_value *pVal){ + return (pVal->flags&MEM_FromBind)!=0; +} + /* Make a copy of an sqlite3_value object */ SQLITE_API sqlite3_value *sqlite3_value_dup(const sqlite3_value *pOrig){ @@ -81293,7 +81885,7 @@ static int sqlite3Step(Vdbe *p){ return SQLITE_NOMEM_BKPT; } - if( p->pc<=0 && p->expired ){ + if( p->pc<0 && p->expired ){ p->rc = SQLITE_SCHEMA; rc = SQLITE_ERROR; goto end_of_step; @@ -81312,7 +81904,7 @@ static int sqlite3Step(Vdbe *p){ ); #ifndef SQLITE_OMIT_TRACE - if( (db->xProfile || (db->mTrace & SQLITE_TRACE_PROFILE)!=0) + if( (db->mTrace & (SQLITE_TRACE_PROFILE|SQLITE_TRACE_XPROFILE))!=0 && !db->init.busy && p->zSql ){ sqlite3OsCurrentTimeInt64(db->pVfs, &p->startTime); }else{ @@ -81339,16 +81931,18 @@ static int sqlite3Step(Vdbe *p){ db->nVdbeExec--; } + if( rc!=SQLITE_ROW ){ #ifndef SQLITE_OMIT_TRACE - /* If the statement completed successfully, invoke the profile callback */ - if( rc!=SQLITE_ROW ) checkProfileCallback(db, p); + /* If the statement completed successfully, invoke the profile callback */ + checkProfileCallback(db, p); #endif - if( rc==SQLITE_DONE && db->autoCommit ){ - assert( p->rc==SQLITE_OK ); - p->rc = doWalCallbacks(db); - if( p->rc!=SQLITE_OK ){ - rc = SQLITE_ERROR; + if( rc==SQLITE_DONE && db->autoCommit ){ + assert( p->rc==SQLITE_OK ); + p->rc = doWalCallbacks(db); + if( p->rc!=SQLITE_OK ){ + rc = SQLITE_ERROR; + } } } @@ -81368,9 +81962,9 @@ end_of_step: || (rc&0xff)==SQLITE_BUSY || rc==SQLITE_MISUSE ); assert( (p->rc!=SQLITE_ROW && p->rc!=SQLITE_DONE) || p->rc==p->rcApp ); - if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 - && rc!=SQLITE_ROW - && rc!=SQLITE_DONE + if( rc!=SQLITE_ROW + && rc!=SQLITE_DONE + && (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 ){ /* If this statement was prepared using saved SQL and an ** error has occurred, then return the error code in p->rc to the @@ -81826,10 +82420,10 @@ SQLITE_API int sqlite3_column_type(sqlite3_stmt *pStmt, int i){ ** or a constant) then useTypes 2, 3, and 4 return NULL. */ static const void *columnName( - sqlite3_stmt *pStmt, - int N, - const void *(*xFunc)(Mem*), - int useType + sqlite3_stmt *pStmt, /* The statement */ + int N, /* Which column to get the name for */ + int useUtf16, /* True to return the name as UTF16 */ + int useType /* What type of name */ ){ const void *ret; Vdbe *p; @@ -81850,8 +82444,15 @@ static const void *columnName( N += useType*n; sqlite3_mutex_enter(db->mutex); assert( db->mallocFailed==0 ); - ret = xFunc(&p->aColName[N]); - /* A malloc may have failed inside of the xFunc() call. If this +#ifndef SQLITE_OMIT_UTF16 + if( useUtf16 ){ + ret = sqlite3_value_text16((sqlite3_value*)&p->aColName[N]); + }else +#endif + { + ret = sqlite3_value_text((sqlite3_value*)&p->aColName[N]); + } + /* A malloc may have failed inside of the _text() call. If this ** is the case, clear the mallocFailed flag and return NULL. */ if( db->mallocFailed ){ @@ -81868,13 +82469,11 @@ static const void *columnName( ** statement pStmt. */ SQLITE_API const char *sqlite3_column_name(sqlite3_stmt *pStmt, int N){ - return columnName( - pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_NAME); + return columnName(pStmt, N, 0, COLNAME_NAME); } #ifndef SQLITE_OMIT_UTF16 SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt *pStmt, int N){ - return columnName( - pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_NAME); + return columnName(pStmt, N, 1, COLNAME_NAME); } #endif @@ -81893,13 +82492,11 @@ SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt *pStmt, int N){ ** of the result set of SQL statement pStmt. */ SQLITE_API const char *sqlite3_column_decltype(sqlite3_stmt *pStmt, int N){ - return columnName( - pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_DECLTYPE); + return columnName(pStmt, N, 0, COLNAME_DECLTYPE); } #ifndef SQLITE_OMIT_UTF16 SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt *pStmt, int N){ - return columnName( - pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_DECLTYPE); + return columnName(pStmt, N, 1, COLNAME_DECLTYPE); } #endif /* SQLITE_OMIT_UTF16 */ #endif /* SQLITE_OMIT_DECLTYPE */ @@ -81911,13 +82508,11 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt *pStmt, int N){ ** anything else which is not an unambiguous reference to a database column. */ SQLITE_API const char *sqlite3_column_database_name(sqlite3_stmt *pStmt, int N){ - return columnName( - pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_DATABASE); + return columnName(pStmt, N, 0, COLNAME_DATABASE); } #ifndef SQLITE_OMIT_UTF16 SQLITE_API const void *sqlite3_column_database_name16(sqlite3_stmt *pStmt, int N){ - return columnName( - pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_DATABASE); + return columnName(pStmt, N, 1, COLNAME_DATABASE); } #endif /* SQLITE_OMIT_UTF16 */ @@ -81927,13 +82522,11 @@ SQLITE_API const void *sqlite3_column_database_name16(sqlite3_stmt *pStmt, int N ** anything else which is not an unambiguous reference to a database column. */ SQLITE_API const char *sqlite3_column_table_name(sqlite3_stmt *pStmt, int N){ - return columnName( - pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_TABLE); + return columnName(pStmt, N, 0, COLNAME_TABLE); } #ifndef SQLITE_OMIT_UTF16 SQLITE_API const void *sqlite3_column_table_name16(sqlite3_stmt *pStmt, int N){ - return columnName( - pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_TABLE); + return columnName(pStmt, N, 1, COLNAME_TABLE); } #endif /* SQLITE_OMIT_UTF16 */ @@ -81943,13 +82536,11 @@ SQLITE_API const void *sqlite3_column_table_name16(sqlite3_stmt *pStmt, int N){ ** anything else which is not an unambiguous reference to a database column. */ SQLITE_API const char *sqlite3_column_origin_name(sqlite3_stmt *pStmt, int N){ - return columnName( - pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_COLUMN); + return columnName(pStmt, N, 0, COLNAME_COLUMN); } #ifndef SQLITE_OMIT_UTF16 SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt *pStmt, int N){ - return columnName( - pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_COLUMN); + return columnName(pStmt, N, 1, COLNAME_COLUMN); } #endif /* SQLITE_OMIT_UTF16 */ #endif /* SQLITE_ENABLE_COLUMN_METADATA */ @@ -81992,7 +82583,7 @@ static int vdbeUnbind(Vdbe *p, int i){ pVar = &p->aVar[i]; sqlite3VdbeMemRelease(pVar); pVar->flags = MEM_Null; - sqlite3Error(p->db, SQLITE_OK); + p->db->errCode = SQLITE_OK; /* If the bit corresponding to this variable in Vdbe.expmask is set, then ** binding a new value to this variable invalidates the current query plan. @@ -82317,6 +82908,14 @@ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt){ return pStmt ? ((Vdbe*)pStmt)->readOnly : 1; } +/* +** Return 1 if the statement is an EXPLAIN and return 2 if the +** statement is an EXPLAIN QUERY PLAN +*/ +SQLITE_API int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt){ + return pStmt ? ((Vdbe*)pStmt)->explain : 0; +} + /* ** Return true if the prepared statement is in need of being reset. */ @@ -82418,7 +83017,13 @@ SQLITE_API char *sqlite3_expanded_sql(sqlite3_stmt *pStmt){ */ SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt){ Vdbe *p = (Vdbe *)pStmt; - return p ? p->zNormSql : 0; + if( p==0 ) return 0; + if( p->zNormSql==0 && ALWAYS(p->zSql!=0) ){ + sqlite3_mutex_enter(p->db->mutex); + p->zNormSql = sqlite3Normalize(p, p->zSql); + sqlite3_mutex_leave(p->db->mutex); + } + return p->zNormSql; } #endif /* SQLITE_ENABLE_NORMALIZE */ @@ -83000,12 +83605,20 @@ SQLITE_API int sqlite3_found_count = 0; ** feature is used for test suite validation only and does not appear an ** production builds. ** -** M is an integer between 2 and 4. 2 indicates a ordinary two-way -** branch (I=0 means fall through and I=1 means taken). 3 indicates -** a 3-way branch where the third way is when one of the operands is -** NULL. 4 indicates the OP_Jump instruction which has three destinations -** depending on whether the first operand is less than, equal to, or greater -** than the second. +** M is the type of branch. I is the direction taken for this instance of +** the branch. +** +** M: 2 - two-way branch (I=0: fall-thru 1: jump ) +** 3 - two-way + NULL (I=0: fall-thru 1: jump 2: NULL ) +** 4 - OP_Jump (I=0: jump p1 1: jump p2 2: jump p3) +** +** In other words, if M is 2, then I is either 0 (for fall-through) or +** 1 (for when the branch is taken). If M is 3, the I is 0 for an +** ordinary fall-through, I is 1 if the branch was taken, and I is 2 +** if the result of comparison is NULL. For M=3, I=2 the jump may or +** may not be taken, depending on the SQLITE_JUMPIFNULL flags in p5. +** When M is 4, that means that an OP_Jump is being run. I is 0, 1, or 2 +** depending on if the operands are less than, equal, or greater than. ** ** iSrcLine is the source code line (from the __LINE__ macro) that ** generated the VDBE instruction combined with flag bits. The source @@ -83016,9 +83629,9 @@ SQLITE_API int sqlite3_found_count = 0; ** alternate branch are never taken. If a branch is never taken then ** flags should be 0x06 since only the fall-through approach is allowed. ** -** Bit 0x04 of the flags indicates an OP_Jump opcode that is only +** Bit 0x08 of the flags indicates an OP_Jump opcode that is only ** interested in equal or not-equal. In other words, I==0 and I==2 -** should be treated the same. +** should be treated as equivalent ** ** Since only a line number is retained, not the filename, this macro ** only works for amalgamation builds. But that is ok, since these macros @@ -83042,6 +83655,18 @@ SQLITE_API int sqlite3_found_count = 0; mNever = iSrcLine >> 24; assert( (I & mNever)==0 ); if( sqlite3GlobalConfig.xVdbeBranch==0 ) return; /*NO_TEST*/ + /* Invoke the branch coverage callback with three arguments: + ** iSrcLine - the line number of the VdbeCoverage() macro, with + ** flags removed. + ** I - Mask of bits 0x07 indicating which cases are are + ** fulfilled by this instance of the jump. 0x01 means + ** fall-thru, 0x02 means taken, 0x04 means NULL. Any + ** impossible cases (ex: if the comparison is never NULL) + ** are filled in automatically so that the coverage + ** measurement logic does not flag those impossible cases + ** as missed coverage. + ** M - Type of jump. Same as M argument above + */ I |= mNever; if( M==2 ) I |= 0x04; if( M==4 ){ @@ -83118,6 +83743,11 @@ static VdbeCursor *allocateCursor( assert( iCur>=0 && iCurnCursor ); if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/ + /* Before calling sqlite3VdbeFreeCursor(), ensure the isEphemeral flag + ** is clear. Otherwise, if this is an ephemeral cursor created by + ** OP_OpenDup, the cursor will not be closed and will still be part + ** of a BtShared.pCursor list. */ + p->apCsr[iCur]->isEphemeral = 0; sqlite3VdbeFreeCursor(p, p->apCsr[iCur]); p->apCsr[iCur] = 0; } @@ -83258,6 +83888,7 @@ SQLITE_PRIVATE void sqlite3ValueApplyAffinity( static u16 SQLITE_NOINLINE computeNumericType(Mem *pMem){ assert( (pMem->flags & (MEM_Int|MEM_Real))==0 ); assert( (pMem->flags & (MEM_Str|MEM_Blob))!=0 ); + ExpandBlob(pMem); if( sqlite3AtoF(pMem->z, &pMem->u.r, pMem->n, pMem->enc)==0 ){ return 0; } @@ -83583,6 +84214,15 @@ SQLITE_PRIVATE int sqlite3VdbeExec( assert( p->magic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */ sqlite3VdbeEnter(p); +#ifndef SQLITE_OMIT_PROGRESS_CALLBACK + if( db->xProgress ){ + u32 iPrior = p->aCounter[SQLITE_STMTSTATUS_VM_STEP]; + assert( 0 < db->nProgressOps ); + nProgressLimit = db->nProgressOps - (iPrior % db->nProgressOps); + }else{ + nProgressLimit = 0xffffffff; + } +#endif if( p->rc==SQLITE_NOMEM ){ /* This happens if a malloc() inside a call to sqlite3_column_text() or ** sqlite3_column_text16() failed. */ @@ -83596,15 +84236,6 @@ SQLITE_PRIVATE int sqlite3VdbeExec( db->busyHandler.nBusy = 0; if( db->u1.isInterrupted ) goto abort_due_to_interrupt; sqlite3VdbeIOTraceSql(p); -#ifndef SQLITE_OMIT_PROGRESS_CALLBACK - if( db->xProgress ){ - u32 iPrior = p->aCounter[SQLITE_STMTSTATUS_VM_STEP]; - assert( 0 < db->nProgressOps ); - nProgressLimit = db->nProgressOps - (iPrior % db->nProgressOps); - }else{ - nProgressLimit = 0xffffffff; - } -#endif #ifdef SQLITE_DEBUG sqlite3BeginBenignMalloc(); if( p->pc==0 @@ -83780,10 +84411,11 @@ check_for_interrupt: ** If the progress callback returns non-zero, exit the virtual machine with ** a return code SQLITE_ABORT. */ - if( nVmStep>=nProgressLimit && db->xProgress!=0 ){ + while( nVmStep>=nProgressLimit && db->xProgress!=0 ){ assert( db->nProgressOps!=0 ); - nProgressLimit = nVmStep + db->nProgressOps - (nVmStep%db->nProgressOps); + nProgressLimit += db->nProgressOps; if( db->xProgress(db->pProgressArg) ){ + nProgressLimit = 0xffffffff; rc = SQLITE_INTERRUPT; goto abort_due_to_error; } @@ -84062,6 +84694,7 @@ case OP_String8: { /* same as TK_STRING, out2 */ if( encoding!=SQLITE_UTF8 ){ rc = sqlite3VdbeMemSetStr(pOut, pOp->p4.z, -1, SQLITE_UTF8, SQLITE_STATIC); assert( rc==SQLITE_OK || rc==SQLITE_TOOBIG ); + if( rc ) goto too_big; if( SQLITE_OK!=sqlite3VdbeChangeEncoding(pOut, encoding) ) goto no_mem; assert( pOut->szMalloc>0 && pOut->zMalloc==pOut->z ); assert( VdbeMemDynamic(pOut)==0 ); @@ -84074,7 +84707,6 @@ case OP_String8: { /* same as TK_STRING, out2 */ pOp->p4.z = pOut->z; pOp->p1 = pOut->n; } - testcase( rc==SQLITE_TOOBIG ); #endif if( pOp->p1>db->aLimit[SQLITE_LIMIT_LENGTH] ){ goto too_big; @@ -84196,7 +84828,10 @@ case OP_Variable: { /* out2 */ goto too_big; } pOut = &aMem[pOp->p2]; - sqlite3VdbeMemShallowCopy(pOut, pVar, MEM_Static); + if( VdbeMemDynamic(pOut) ) sqlite3VdbeMemSetNull(pOut); + memcpy(pOut, pVar, MEMCELLSIZE); + pOut->flags &= ~(MEM_Dyn|MEM_Ephem); + pOut->flags |= MEM_Static|MEM_FromBind; UPDATE_MAX_BLOBSIZE(pOut); break; } @@ -84329,18 +84964,6 @@ case OP_ResultRow: { assert( pOp->p1>0 ); assert( pOp->p1+pOp->p2<=(p->nMem+1 - p->nCursor)+1 ); -#ifndef SQLITE_OMIT_PROGRESS_CALLBACK - /* Run the progress counter just before returning. - */ - if( db->xProgress!=0 - && nVmStep>=nProgressLimit - && db->xProgress(db->pProgressArg)!=0 - ){ - rc = SQLITE_INTERRUPT; - goto abort_due_to_error; - } -#endif - /* If this statement has violated immediate foreign key constraints, do ** not return the number of rows modified. And do not RELEASE the statement ** transaction. It needs to be rolled back. */ @@ -84545,8 +85168,8 @@ fp_math: break; } default: { - iA = (i64)rA; - iB = (i64)rB; + iA = sqlite3VdbeIntValue(pIn1); + iB = sqlite3VdbeIntValue(pIn2); if( iA==0 ) goto arithmetic_result_is_null; if( iA==-1 ) iA = 1; rB = (double)(iB % iA); @@ -84706,8 +85329,8 @@ case OP_MustBeInt: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; if( (pIn1->flags & MEM_Int)==0 ){ applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding); - VdbeBranchTaken((pIn1->flags&MEM_Int)==0, 2); if( (pIn1->flags & MEM_Int)==0 ){ + VdbeBranchTaken(1, 2); if( pOp->p2==0 ){ rc = SQLITE_MISMATCH; goto abort_due_to_error; @@ -84716,6 +85339,7 @@ case OP_MustBeInt: { /* jump, in1 */ } } } + VdbeBranchTaken(0, 2); MemSetTypeFlag(pIn1, MEM_Int); break; } @@ -84890,15 +85514,15 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ ** OP_Eq or OP_Ne) then take the jump or not depending on whether ** or not both operands are null. */ - assert( pOp->opcode==OP_Eq || pOp->opcode==OP_Ne ); assert( (flags1 & MEM_Cleared)==0 ); - assert( (pOp->p5 & SQLITE_JUMPIFNULL)==0 ); + assert( (pOp->p5 & SQLITE_JUMPIFNULL)==0 || CORRUPT_DB ); + testcase( (pOp->p5 & SQLITE_JUMPIFNULL)!=0 ); if( (flags1&flags3&MEM_Null)!=0 && (flags3&MEM_Cleared)==0 ){ res = 0; /* Operands are equal */ }else{ - res = 1; /* Operands are not equal */ + res = ((flags3 & MEM_Null) ? -1 : +1); /* Operands are not equal */ } }else{ /* SQLITE_NULLEQ is clear and at least one operand is NULL, @@ -85016,7 +85640,7 @@ compare_op: pOut->u.i = res2; REGISTER_TRACE(pOp->p2, pOut); }else{ - VdbeBranchTaken(res!=0, (pOp->p5 & SQLITE_NULLEQ)?2:3); + VdbeBranchTaken(res2!=0, (pOp->p5 & SQLITE_NULLEQ)?2:3); if( res2 ){ goto jump_to_p2; } @@ -85566,15 +86190,15 @@ case OP_Column: { zEndHdr = zData + aOffset[0]; testcase( zHdr>=zEndHdr ); do{ - if( (t = zHdr[0])<0x80 ){ + if( (pC->aType[i] = t = zHdr[0])<0x80 ){ zHdr++; offset64 += sqlite3VdbeOneByteSerialTypeLen(t); }else{ zHdr += sqlite3GetVarint32(zHdr, &t); + pC->aType[i] = t; offset64 += sqlite3VdbeSerialTypeLen(t); } - pC->aType[i++] = t; - aOffset[i] = (u32)(offset64 & 0xffffffff); + aOffset[++i] = (u32)(offset64 & 0xffffffff); }while( i<=p2 && zHdrisEphemeral = 1; pCx->pKeyInfo = pOrig->pKeyInfo; pCx->isTable = pOrig->isTable; - rc = sqlite3BtreeCursor(pOrig->pBtx, MASTER_ROOT, BTREE_WRCSR, + pCx->pgnoRoot = pOrig->pgnoRoot; + pCx->isOrdered = pOrig->isOrdered; + rc = sqlite3BtreeCursor(pOrig->pBtx, pCx->pgnoRoot, BTREE_WRCSR, pCx->pKeyInfo, pCx->uc.pCursor); /* The sqlite3BtreeCursor() routine can only fail for the first cursor ** opened for a database. Since there is already an open cursor when this @@ -86594,6 +87220,9 @@ case OP_OpenDup: { ** the main database is read-only. The ephemeral ** table is deleted automatically when the cursor is closed. ** +** If the cursor P1 is already opened on an ephemeral table, the table +** is cleared (all content is erased). +** ** P2 is the number of columns in the ephemeral table. ** The cursor points to a BTree table if P4==0 and to a BTree index ** if P4 is not 0. If P4 is not NULL, it points to a KeyInfo structure @@ -86625,41 +87254,50 @@ case OP_OpenEphemeral: { SQLITE_OPEN_TRANSIENT_DB; assert( pOp->p1>=0 ); assert( pOp->p2>=0 ); - pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, CURTYPE_BTREE); - if( pCx==0 ) goto no_mem; - pCx->nullRow = 1; - pCx->isEphemeral = 1; - rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->pBtx, - BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, vfsFlags); - if( rc==SQLITE_OK ){ - rc = sqlite3BtreeBeginTrans(pCx->pBtx, 1, 0); - } - if( rc==SQLITE_OK ){ - /* If a transient index is required, create it by calling - ** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before - ** opening it. If a transient table is required, just use the - ** automatically created table with root-page 1 (an BLOB_INTKEY table). - */ - if( (pCx->pKeyInfo = pKeyInfo = pOp->p4.pKeyInfo)!=0 ){ - int pgno; - assert( pOp->p4type==P4_KEYINFO ); - rc = sqlite3BtreeCreateTable(pCx->pBtx, &pgno, BTREE_BLOBKEY | pOp->p5); - if( rc==SQLITE_OK ){ - assert( pgno==MASTER_ROOT+1 ); - assert( pKeyInfo->db==db ); - assert( pKeyInfo->enc==ENC(db) ); - rc = sqlite3BtreeCursor(pCx->pBtx, pgno, BTREE_WRCSR, - pKeyInfo, pCx->uc.pCursor); + pCx = p->apCsr[pOp->p1]; + if( pCx ){ + /* If the ephermeral table is already open, erase all existing content + ** so that the table is empty again, rather than creating a new table. */ + rc = sqlite3BtreeClearTable(pCx->pBtx, pCx->pgnoRoot, 0); + }else{ + pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, CURTYPE_BTREE); + if( pCx==0 ) goto no_mem; + pCx->nullRow = 1; + pCx->isEphemeral = 1; + rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->pBtx, + BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, + vfsFlags); + if( rc==SQLITE_OK ){ + rc = sqlite3BtreeBeginTrans(pCx->pBtx, 1, 0); + } + if( rc==SQLITE_OK ){ + /* If a transient index is required, create it by calling + ** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before + ** opening it. If a transient table is required, just use the + ** automatically created table with root-page 1 (an BLOB_INTKEY table). + */ + if( (pCx->pKeyInfo = pKeyInfo = pOp->p4.pKeyInfo)!=0 ){ + assert( pOp->p4type==P4_KEYINFO ); + rc = sqlite3BtreeCreateTable(pCx->pBtx, (int*)&pCx->pgnoRoot, + BTREE_BLOBKEY | pOp->p5); + if( rc==SQLITE_OK ){ + assert( pCx->pgnoRoot==MASTER_ROOT+1 ); + assert( pKeyInfo->db==db ); + assert( pKeyInfo->enc==ENC(db) ); + rc = sqlite3BtreeCursor(pCx->pBtx, pCx->pgnoRoot, BTREE_WRCSR, + pKeyInfo, pCx->uc.pCursor); + } + pCx->isTable = 0; + }else{ + pCx->pgnoRoot = MASTER_ROOT; + rc = sqlite3BtreeCursor(pCx->pBtx, MASTER_ROOT, BTREE_WRCSR, + 0, pCx->uc.pCursor); + pCx->isTable = 1; } - pCx->isTable = 0; - }else{ - rc = sqlite3BtreeCursor(pCx->pBtx, MASTER_ROOT, BTREE_WRCSR, - 0, pCx->uc.pCursor); - pCx->isTable = 1; } + pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); } if( rc ) goto abort_due_to_error; - pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); break; } @@ -87309,7 +87947,7 @@ case OP_NotExists: /* jump, in3 */ pC = p->apCsr[pOp->p1]; assert( pC!=0 ); #ifdef SQLITE_DEBUG - pC->seekOp = OP_SeekRowid; + if( pOp->opcode==OP_SeekRowid ) pC->seekOp = OP_SeekRowid; #endif assert( pC->isTable ); assert( pC->eCurType==CURTYPE_BTREE ); @@ -87527,14 +88165,7 @@ case OP_NewRowid: { /* out2 */ ** This instruction only works on tables. The equivalent instruction ** for indices is OP_IdxInsert. */ -/* Opcode: InsertInt P1 P2 P3 P4 P5 -** Synopsis: intkey=P3 data=r[P2] -** -** This works exactly like OP_Insert except that the key is the -** integer value P3, not the value of the integer stored in register P3. -*/ -case OP_Insert: -case OP_InsertInt: { +case OP_Insert: { Mem *pData; /* MEM cell holding data for the record to be inserted */ Mem *pKey; /* MEM cell holding key for the record */ VdbeCursor *pC; /* Cursor to table into which insert is written */ @@ -87555,16 +88186,11 @@ case OP_InsertInt: { REGISTER_TRACE(pOp->p2, pData); sqlite3VdbeIncrWriteCounter(p, pC); - if( pOp->opcode==OP_Insert ){ - pKey = &aMem[pOp->p3]; - assert( pKey->flags & MEM_Int ); - assert( memIsValid(pKey) ); - REGISTER_TRACE(pOp->p3, pKey); - x.nKey = pKey->u.i; - }else{ - assert( pOp->opcode==OP_InsertInt ); - x.nKey = pOp->p3; - } + pKey = &aMem[pOp->p3]; + assert( pKey->flags & MEM_Int ); + assert( memIsValid(pKey) ); + REGISTER_TRACE(pOp->p3, pKey); + x.nKey = pKey->u.i; if( pOp->p4type==P4_TABLE && HAS_UPDATE_HOOK(db) ){ assert( pC->iDb>=0 ); @@ -88084,7 +88710,7 @@ case OP_Sort: { /* jump */ p->aCounter[SQLITE_STMTSTATUS_SORT]++; /* Fall through into OP_Rewind */ } -/* Opcode: Rewind P1 P2 * * P5 +/* Opcode: Rewind P1 P2 * * * ** ** The next use of the Rowid or Column or Next instruction for P1 ** will refer to the first entry in the database table or index. @@ -88092,10 +88718,6 @@ case OP_Sort: { /* jump */ ** If the table or index is not empty, fall through to the following ** instruction. ** -** If P5 is non-zero and the table is not empty, then the "skip-next" -** flag is set on the cursor so that the next OP_Next instruction -** executed on it is a no-op. -** ** This opcode leaves the cursor configured to move in forward order, ** from the beginning toward the end. In other words, the cursor is ** configured to use Next, not Prev. @@ -88106,6 +88728,7 @@ case OP_Rewind: { /* jump */ int res; assert( pOp->p1>=0 && pOp->p1nCursor ); + assert( pOp->p5==0 ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( isSorter(pC)==(pOp->opcode==OP_SorterSort) ); @@ -88120,9 +88743,6 @@ case OP_Rewind: { /* jump */ pCrsr = pC->uc.pCursor; assert( pCrsr ); rc = sqlite3BtreeFirst(pCrsr, &res); -#ifndef SQLITE_OMIT_WINDOWFUNC - if( pOp->p5 ) sqlite3BtreeSkipNext(pCrsr); -#endif pC->deferredMoveto = 0; pC->cacheStatus = CACHE_STALE; } @@ -88217,7 +88837,7 @@ case OP_Next: /* jump */ assert( pOp->opcode!=OP_Next || pC->seekOp==OP_SeekGT || pC->seekOp==OP_SeekGE || pC->seekOp==OP_Rewind || pC->seekOp==OP_Found - || pC->seekOp==OP_NullRow); + || pC->seekOp==OP_NullRow|| pC->seekOp==OP_SeekRowid); assert( pOp->opcode!=OP_Prev || pC->seekOp==OP_SeekLT || pC->seekOp==OP_SeekLE || pC->seekOp==OP_Last @@ -88747,9 +89367,16 @@ case OP_ParseSchema: { assert( db->init.busy==0 ); db->init.busy = 1; initData.rc = SQLITE_OK; + initData.nInitRow = 0; assert( !db->mallocFailed ); rc = sqlite3_exec(db, zSql, sqlite3InitCallback, &initData, 0); if( rc==SQLITE_OK ) rc = initData.rc; + if( rc==SQLITE_OK && initData.nInitRow==0 ){ + /* The OP_ParseSchema opcode with a non-NULL P4 argument should parse + ** at least one SQL statement. Any less than that indicates that + ** the sqlite_master table is corrupt. */ + rc = SQLITE_CORRUPT_BKPT; + } sqlite3DbFreeNN(db, zSql); db->init.busy = 0; } @@ -89112,10 +89739,20 @@ case OP_Program: { /* jump */ p->nOp = pProgram->nOp; #ifdef SQLITE_ENABLE_STMT_SCANSTATUS p->anExec = 0; +#endif +#ifdef SQLITE_DEBUG + /* Verify that second and subsequent executions of the same trigger do not + ** try to reuse register values from the first use. */ + { + int i; + for(i=0; inMem; i++){ + aMem[i].pScopyFrom = 0; /* Prevent false-positive AboutToChange() errs */ + aMem[i].flags |= MEM_Undefined; /* Cause a fault if this reg is reused */ + } + } #endif pOp = &aOp[-1]; - - break; + goto check_for_interrupt; } /* Opcode: Param P1 P2 * * * @@ -89487,6 +90124,7 @@ case OP_AggFinal: { assert( (pMem->flags & ~(MEM_Null|MEM_Agg))==0 ); #ifndef SQLITE_OMIT_WINDOWFUNC if( pOp->p3 ){ + memAboutToChange(p, &aMem[pOp->p3]); rc = sqlite3VdbeMemAggValue(pMem, &aMem[pOp->p3], pOp->p4.pFunc); pMem = &aMem[pOp->p3]; }else @@ -89651,14 +90289,19 @@ case OP_JournalMode: { /* out2 */ #endif /* SQLITE_OMIT_PRAGMA */ #if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH) -/* Opcode: Vacuum P1 * * * * +/* Opcode: Vacuum P1 P2 * * * ** ** Vacuum the entire database P1. P1 is 0 for "main", and 2 or more ** for an attached database. The "temp" database may not be vacuumed. +** +** If P2 is not zero, then it is a register holding a string which is +** the file into which the result of vacuum should be written. When +** P2 is zero, the vacuum overwrites the original database. */ case OP_Vacuum: { assert( p->readOnly==0 ); - rc = sqlite3RunVacuum(&p->zErrMsg, db, pOp->p1); + rc = sqlite3RunVacuum(&p->zErrMsg, db, pOp->p1, + pOp->p2 ? &aMem[pOp->p2] : 0); if( rc ) goto abort_due_to_error; break; } @@ -89810,6 +90453,7 @@ case OP_VDestroy: { db->nVDestroy++; rc = sqlite3VtabCallDestroy(db, pOp->p1, pOp->p4.z); db->nVDestroy--; + assert( p->errorAction==OE_Abort && p->usesStmtJournal ); if( rc ) goto abort_due_to_error; break; } @@ -90053,7 +90697,7 @@ case OP_VRename: { rc = sqlite3VdbeChangeEncoding(pName, SQLITE_UTF8); if( rc ) goto abort_due_to_error; rc = pVtab->pModule->xRename(pVtab, pName->z); - if( isLegacy==0 ) db->flags &= ~SQLITE_LegacyAlter; + if( isLegacy==0 ) db->flags &= ~(u64)SQLITE_LegacyAlter; sqlite3VtabImportErrmsg(p, pVtab); p->expired = 0; if( rc ) goto abort_due_to_error; @@ -90518,7 +91162,16 @@ abort_due_to_error: ** release the mutexes on btrees that were acquired at the ** top. */ vdbe_return: - testcase( nVmStep>0 ); +#ifndef SQLITE_OMIT_PROGRESS_CALLBACK + while( nVmStep>=nProgressLimit && db->xProgress!=0 ){ + nProgressLimit += db->nProgressOps; + if( db->xProgress(db->pProgressArg) ){ + nProgressLimit = 0xffffffff; + rc = SQLITE_INTERRUPT; + goto abort_due_to_error; + } + } +#endif p->aCounter[SQLITE_STMTSTATUS_VM_STEP] += (int)nVmStep; sqlite3VdbeLeave(p); assert( rc!=SQLITE_OK || nExtraDelete==0 @@ -91605,7 +92258,7 @@ static int vdbePmaReadBlob( /* Extend the p->aAlloc[] allocation if required. */ if( p->nAllocnAlloc*2); + sqlite3_int64 nNew = MAX(128, 2*(sqlite3_int64)p->nAlloc); while( nByte>nNew ) nNew = nNew*2; aNew = sqlite3Realloc(p->aAlloc, nNew); if( !aNew ) return SQLITE_NOMEM_BKPT; @@ -92896,15 +93549,19 @@ SQLITE_PRIVATE int sqlite3VdbeSorterWrite( if( nMin>pSorter->nMemory ){ u8 *aNew; - int iListOff = (u8*)pSorter->list.pList - pSorter->list.aMemory; - int nNew = pSorter->nMemory * 2; + sqlite3_int64 nNew = 2 * (sqlite3_int64)pSorter->nMemory; + int iListOff = -1; + if( pSorter->list.pList ){ + iListOff = (u8*)pSorter->list.pList - pSorter->list.aMemory; + } while( nNew < nMin ) nNew = nNew*2; if( nNew > pSorter->mxPmaSize ) nNew = pSorter->mxPmaSize; if( nNew < nMin ) nNew = nMin; - aNew = sqlite3Realloc(pSorter->list.aMemory, nNew); if( !aNew ) return SQLITE_NOMEM_BKPT; - pSorter->list.pList = (SorterRecord*)&aNew[iListOff]; + if( iListOff>=0 ){ + pSorter->list.pList = (SorterRecord*)&aNew[iListOff]; + } pSorter->list.aMemory = aNew; pSorter->nMemory = nNew; } @@ -94280,6 +94937,22 @@ SQLITE_PRIVATE int sqlite3JournalSize(sqlite3_vfs *pVfs){ /* #include */ +#if !defined(SQLITE_OMIT_WINDOWFUNC) +/* +** Walk all expressions linked into the list of Window objects passed +** as the second argument. +*/ +static int walkWindowList(Walker *pWalker, Window *pList){ + Window *pWin; + for(pWin=pList; pWin; pWin=pWin->pNextWin){ + if( sqlite3WalkExprList(pWalker, pWin->pOrderBy) ) return WRC_Abort; + if( sqlite3WalkExprList(pWalker, pWin->pPartition) ) return WRC_Abort; + if( sqlite3WalkExpr(pWalker, pWin->pFilter) ) return WRC_Abort; + } + return WRC_Continue; +} +#endif + /* ** Walk an expression tree. Invoke the callback once for each node ** of the expression, while descending. (In other words, the callback @@ -94319,10 +94992,7 @@ static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){ } #ifndef SQLITE_OMIT_WINDOWFUNC if( ExprHasProperty(pExpr, EP_WinFunc) ){ - Window *pWin = pExpr->y.pWin; - if( sqlite3WalkExprList(pWalker, pWin->pPartition) ) return WRC_Abort; - if( sqlite3WalkExprList(pWalker, pWin->pOrderBy) ) return WRC_Abort; - if( sqlite3WalkExpr(pWalker, pWin->pFilter) ) return WRC_Abort; + if( walkWindowList(pWalker, pExpr->y.pWin) ) return WRC_Abort; } #endif } @@ -94362,6 +95032,16 @@ SQLITE_PRIVATE int sqlite3WalkSelectExpr(Walker *pWalker, Select *p){ if( sqlite3WalkExpr(pWalker, p->pHaving) ) return WRC_Abort; if( sqlite3WalkExprList(pWalker, p->pOrderBy) ) return WRC_Abort; if( sqlite3WalkExpr(pWalker, p->pLimit) ) return WRC_Abort; +#if !defined(SQLITE_OMIT_WINDOWFUNC) && !defined(SQLITE_OMIT_ALTERTABLE) + { + Parse *pParse = pWalker->pParse; + if( pParse && IN_RENAME_OBJECT ){ + int rc = walkWindowList(pWalker, p->pWinDefn); + assert( rc==WRC_Continue ); + return rc; + } + } +#endif return WRC_Continue; } @@ -94513,7 +95193,6 @@ static void resolveAlias( if( pExpr->op==TK_COLLATE ){ pDup = sqlite3ExprAddCollateString(pParse, pDup, pExpr->u.zToken); } - ExprSetProperty(pDup, EP_Alias); /* Before calling sqlite3ExprDelete(), set the EP_Static flag. This ** prevents ExprDelete() from deleting the Expr structure itself, @@ -94869,6 +95548,10 @@ static int lookupName( sqlite3ErrorMsg(pParse, "misuse of aliased aggregate %s", zAs); return WRC_Abort; } + if( (pNC->ncFlags&NC_AllowWin)==0 && ExprHasProperty(pOrig, EP_Win) ){ + sqlite3ErrorMsg(pParse, "misuse of aliased window function %s",zAs); + return WRC_Abort; + } if( sqlite3ExprVectorSize(pOrig)!=1 ){ sqlite3ErrorMsg(pParse, "row value misused"); return WRC_Abort; @@ -94907,6 +95590,25 @@ static int lookupName( if( cnt==0 && zTab==0 ){ assert( pExpr->op==TK_ID ); if( ExprHasProperty(pExpr,EP_DblQuoted) ){ + /* If a double-quoted identifier does not match any known column name, + ** then treat it as a string. + ** + ** This hack was added in the early days of SQLite in a misguided attempt + ** to be compatible with MySQL 3.x, which used double-quotes for strings. + ** I now sorely regret putting in this hack. The effect of this hack is + ** that misspelled identifier names are silently converted into strings + ** rather than causing an error, to the frustration of countless + ** programmers. To all those frustrated programmers, my apologies. + ** + ** Someday, I hope to get rid of this hack. Unfortunately there is + ** a huge amount of legacy SQL that uses it. So for now, we just + ** issue a warning. + */ + sqlite3_log(SQLITE_WARNING, + "double-quoted string literal: \"%w\"", zCol); +#ifdef SQLITE_ENABLE_NORMALIZE + sqlite3VdbeAddDblquoteStr(db, pParse->pVdbe, zCol); +#endif pExpr->op = TK_STRING; pExpr->y.pTab = 0; return WRC_Prune; @@ -95140,6 +95842,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ const char *zId; /* The function name. */ FuncDef *pDef; /* Information about the function */ u8 enc = ENC(pParse->db); /* The database encoding */ + int savedAllowFlags = (pNC->ncFlags & (NC_AllowAgg | NC_AllowWin)); assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); zId = pExpr->u.zToken; @@ -95261,8 +95964,11 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ pNC->nErr++; } if( is_agg ){ + /* Window functions may not be arguments of aggregate functions. + ** Or arguments of other window functions. But aggregate functions + ** may be arguments for window functions. */ #ifndef SQLITE_OMIT_WINDOWFUNC - pNC->ncFlags &= ~(pExpr->y.pWin ? NC_AllowWin : NC_AllowAgg); + pNC->ncFlags &= ~(NC_AllowWin | (!pExpr->y.pWin ? NC_AllowAgg : 0)); #else pNC->ncFlags &= ~NC_AllowAgg; #endif @@ -95273,17 +95979,17 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ #ifndef SQLITE_OMIT_WINDOWFUNC if( pExpr->y.pWin ){ Select *pSel = pNC->pWinSelect; + sqlite3WindowUpdate(pParse, pSel->pWinDefn, pExpr->y.pWin, pDef); sqlite3WalkExprList(pWalker, pExpr->y.pWin->pPartition); sqlite3WalkExprList(pWalker, pExpr->y.pWin->pOrderBy); sqlite3WalkExpr(pWalker, pExpr->y.pWin->pFilter); - sqlite3WindowUpdate(pParse, pSel->pWinDefn, pExpr->y.pWin, pDef); if( 0==pSel->pWin || 0==sqlite3WindowCompare(pParse, pSel->pWin, pExpr->y.pWin) ){ pExpr->y.pWin->pNextWin = pSel->pWin; pSel->pWin = pExpr->y.pWin; } - pNC->ncFlags |= NC_AllowWin; + pNC->ncFlags |= NC_HasWin; }else #endif /* SQLITE_OMIT_WINDOWFUNC */ { @@ -95301,8 +96007,8 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ pNC2->ncFlags |= NC_HasAgg | (pDef->funcFlags & SQLITE_FUNC_MINMAX); } - pNC->ncFlags |= NC_AllowAgg; } + pNC->ncFlags |= savedAllowFlags; } /* FIX ME: Compute pExpr->affinity based on the expected return ** type of the function @@ -95553,32 +96259,53 @@ static int resolveCompoundOrderBy( }else{ iCol = resolveAsName(pParse, pEList, pE); if( iCol==0 ){ - pDup = sqlite3ExprDup(db, pE, 0); + /* Now test if expression pE matches one of the values returned + ** by pSelect. In the usual case this is done by duplicating the + ** expression, resolving any symbols in it, and then comparing + ** it against each expression returned by the SELECT statement. + ** Once the comparisons are finished, the duplicate expression + ** is deleted. + ** + ** Or, if this is running as part of an ALTER TABLE operation, + ** resolve the symbols in the actual expression, not a duplicate. + ** And, if one of the comparisons is successful, leave the expression + ** as is instead of transforming it to an integer as in the usual + ** case. This allows the code in alter.c to modify column + ** refererences within the ORDER BY expression as required. */ + if( IN_RENAME_OBJECT ){ + pDup = pE; + }else{ + pDup = sqlite3ExprDup(db, pE, 0); + } if( !db->mallocFailed ){ assert(pDup); iCol = resolveOrderByTermToExprList(pParse, pSelect, pDup); } - sqlite3ExprDelete(db, pDup); + if( !IN_RENAME_OBJECT ){ + sqlite3ExprDelete(db, pDup); + } } } if( iCol>0 ){ /* Convert the ORDER BY term into an integer column number iCol, ** taking care to preserve the COLLATE clause if it exists */ - Expr *pNew = sqlite3Expr(db, TK_INTEGER, 0); - if( pNew==0 ) return 1; - pNew->flags |= EP_IntValue; - pNew->u.iValue = iCol; - if( pItem->pExpr==pE ){ - pItem->pExpr = pNew; - }else{ - Expr *pParent = pItem->pExpr; - assert( pParent->op==TK_COLLATE ); - while( pParent->pLeft->op==TK_COLLATE ) pParent = pParent->pLeft; - assert( pParent->pLeft==pE ); - pParent->pLeft = pNew; + if( !IN_RENAME_OBJECT ){ + Expr *pNew = sqlite3Expr(db, TK_INTEGER, 0); + if( pNew==0 ) return 1; + pNew->flags |= EP_IntValue; + pNew->u.iValue = iCol; + if( pItem->pExpr==pE ){ + pItem->pExpr = pNew; + }else{ + Expr *pParent = pItem->pExpr; + assert( pParent->op==TK_COLLATE ); + while( pParent->pLeft->op==TK_COLLATE ) pParent = pParent->pLeft; + assert( pParent->pLeft==pE ); + pParent->pLeft = pNew; + } + sqlite3ExprDelete(db, pE); + pItem->u.x.iOrderByCol = (u16)iCol; } - sqlite3ExprDelete(db, pE); - pItem->u.x.iOrderByCol = (u16)iCol; pItem->done = 1; }else{ moreToDo = 1; @@ -95637,6 +96364,38 @@ SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy( return 0; } +#ifndef SQLITE_OMIT_WINDOWFUNC +/* +** Walker callback for resolveRemoveWindows(). +*/ +static int resolveRemoveWindowsCb(Walker *pWalker, Expr *pExpr){ + if( ExprHasProperty(pExpr, EP_WinFunc) ){ + Window **pp; + for(pp=&pWalker->u.pSelect->pWin; *pp; pp=&(*pp)->pNextWin){ + if( *pp==pExpr->y.pWin ){ + *pp = (*pp)->pNextWin; + break; + } + } + } + return WRC_Continue; +} + +/* +** Remove any Window objects owned by the expression pExpr from the +** Select.pWin list of Select object pSelect. +*/ +static void resolveRemoveWindows(Select *pSelect, Expr *pExpr){ + Walker sWalker; + memset(&sWalker, 0, sizeof(Walker)); + sWalker.xExprCallback = resolveRemoveWindowsCb; + sWalker.u.pSelect = pSelect; + sqlite3WalkExpr(&sWalker, pExpr); +} +#else +# define resolveRemoveWindows(x,y) +#endif + /* ** pOrderBy is an ORDER BY or GROUP BY clause in SELECT statement pSelect. ** The Name context of the SELECT statement is pNC. zType is either @@ -95703,19 +96462,10 @@ static int resolveOrderGroupBy( } for(j=0; jpEList->nExpr; j++){ if( sqlite3ExprCompare(0, pE, pSelect->pEList->a[j].pExpr, -1)==0 ){ -#ifndef SQLITE_OMIT_WINDOWFUNC - if( ExprHasProperty(pE, EP_WinFunc) ){ - /* Since this window function is being changed into a reference - ** to the same window function the result set, remove the instance - ** of this window function from the Select.pWin list. */ - Window **pp; - for(pp=&pSelect->pWin; *pp; pp=&(*pp)->pNextWin){ - if( *pp==pE->y.pWin ){ - *pp = (*pp)->pNextWin; - } - } - } -#endif + /* Since this expresion is being changed into a reference + ** to an identical expression in the result set, remove all Window + ** objects belonging to the expression from the Select.pWin list. */ + resolveRemoveWindows(pSelect, pE); pItem->u.x.iOrderByCol = j+1; } } @@ -95795,7 +96545,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ */ for(i=0; ipSrc->nSrc; i++){ struct SrcList_item *pItem = &p->pSrc->a[i]; - if( pItem->pSelect ){ + if( pItem->pSelect && (pItem->pSelect->selFlags & SF_Resolved)==0 ){ NameContext *pNC; /* Used to iterate name contexts */ int nRef = 0; /* Refcount for pOuterNC and outer contexts */ const char *zSavedContext = pParse->zAuthContext; @@ -95927,6 +96677,19 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ } } +#ifndef SQLITE_OMIT_WINDOWFUNC + if( IN_RENAME_OBJECT ){ + Window *pWin; + for(pWin=p->pWinDefn; pWin; pWin=pWin->pNextWin){ + if( sqlite3ResolveExprListNames(&sNC, pWin->pOrderBy) + || sqlite3ResolveExprListNames(&sNC, pWin->pPartition) + ){ + return WRC_Abort; + } + } + } +#endif + /* If this is part of a compound SELECT, check that it has the right ** number of expressions in the select list. */ if( p->pNext && p->pEList->nExpr!=p->pNext->pEList->nExpr ){ @@ -96006,8 +96769,8 @@ SQLITE_PRIVATE int sqlite3ResolveExprNames( Walker w; if( pExpr==0 ) return SQLITE_OK; - savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg); - pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg); + savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin); + pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin); w.pParse = pNC->pParse; w.xExprCallback = resolveExprStep; w.xSelectCallback = resolveSelectStep; @@ -96023,9 +96786,11 @@ SQLITE_PRIVATE int sqlite3ResolveExprNames( #if SQLITE_MAX_EXPR_DEPTH>0 w.pParse->nHeight -= pExpr->nHeight; #endif - if( pNC->ncFlags & NC_HasAgg ){ - ExprSetProperty(pExpr, EP_Agg); - } + assert( EP_Agg==NC_HasAgg ); + assert( EP_Win==NC_HasWin ); + testcase( pNC->ncFlags & NC_HasAgg ); + testcase( pNC->ncFlags & NC_HasWin ); + ExprSetProperty(pExpr, pNC->ncFlags & (NC_HasAgg|NC_HasWin) ); pNC->ncFlags |= savedHasAgg; return pNC->nErr>0 || w.pParse->nErr>0; } @@ -96077,38 +96842,47 @@ SQLITE_PRIVATE void sqlite3ResolveSelectNames( } /* -** Resolve names in expressions that can only reference a single table: +** Resolve names in expressions that can only reference a single table +** or which cannot reference any tables at all. Examples: ** -** * CHECK constraints -** * WHERE clauses on partial indices +** (1) CHECK constraints +** (2) WHERE clauses on partial indices +** (3) Expressions in indexes on expressions +** (4) Expression arguments to VACUUM INTO. ** -** The Expr.iTable value for Expr.op==TK_COLUMN nodes of the expression -** is set to -1 and the Expr.iColumn value is set to the column number. +** In all cases except (4), the Expr.iTable value for Expr.op==TK_COLUMN +** nodes of the expression is set to -1 and the Expr.iColumn value is +** set to the column number. In case (4), TK_COLUMN nodes cause an error. ** ** Any errors cause an error message to be set in pParse. */ -SQLITE_PRIVATE void sqlite3ResolveSelfReference( +SQLITE_PRIVATE int sqlite3ResolveSelfReference( Parse *pParse, /* Parsing context */ - Table *pTab, /* The table being referenced */ - int type, /* NC_IsCheck or NC_PartIdx or NC_IdxExpr */ + Table *pTab, /* The table being referenced, or NULL */ + int type, /* NC_IsCheck or NC_PartIdx or NC_IdxExpr, or 0 */ Expr *pExpr, /* Expression to resolve. May be NULL. */ ExprList *pList /* Expression list to resolve. May be NULL. */ ){ SrcList sSrc; /* Fake SrcList for pParse->pNewTable */ NameContext sNC; /* Name context for pParse->pNewTable */ + int rc; - assert( type==NC_IsCheck || type==NC_PartIdx || type==NC_IdxExpr ); + assert( type==0 || pTab!=0 ); + assert( type==NC_IsCheck || type==NC_PartIdx || type==NC_IdxExpr || pTab==0 ); memset(&sNC, 0, sizeof(sNC)); memset(&sSrc, 0, sizeof(sSrc)); - sSrc.nSrc = 1; - sSrc.a[0].zName = pTab->zName; - sSrc.a[0].pTab = pTab; - sSrc.a[0].iCursor = -1; + if( pTab ){ + sSrc.nSrc = 1; + sSrc.a[0].zName = pTab->zName; + sSrc.a[0].pTab = pTab; + sSrc.a[0].iCursor = -1; + } sNC.pParse = pParse; sNC.pSrcList = &sSrc; sNC.ncFlags = type; - if( sqlite3ResolveExprNames(&sNC, pExpr) ) return; - if( pList ) sqlite3ResolveExprListNames(&sNC, pList); + if( (rc = sqlite3ResolveExprNames(&sNC, pExpr))!=SQLITE_OK ) return rc; + if( pList ) rc = sqlite3ResolveExprListNames(&sNC, pList); + return rc; } /************** End of resolve.c *********************************************/ @@ -96256,8 +97030,8 @@ SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){ while( p ){ int op = p->op; if( p->flags & EP_Generic ) break; - if( (op==TK_AGG_COLUMN || op==TK_COLUMN - || op==TK_REGISTER || op==TK_TRIGGER) + if( op==TK_REGISTER ) op = p->op2; + if( (op==TK_AGG_COLUMN || op==TK_COLUMN || op==TK_TRIGGER) && p->y.pTab!=0 ){ /* op==TK_REGISTER && p->y.pTab!=0 happens when pExpr was originally @@ -96273,7 +97047,7 @@ SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){ p = p->pLeft; continue; } - if( op==TK_COLLATE || (op==TK_REGISTER && p->op2==TK_COLLATE) ){ + if( op==TK_COLLATE ){ pColl = sqlite3GetCollSeq(pParse, ENC(db), 0, p->u.zToken); break; } @@ -96580,6 +97354,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprForVectorField( }else{ if( pVector->op==TK_VECTOR ) pVector = pVector->x.pList->a[iField].pExpr; pRet = sqlite3ExprDup(pParse->db, pVector, 0); + sqlite3RenameTokenRemap(pParse, pRet, pVector); } return pRet; } @@ -96596,7 +97371,7 @@ static int exprCodeSubselect(Parse *pParse, Expr *pExpr){ int reg = 0; #ifndef SQLITE_OMIT_SUBQUERY if( pExpr->op==TK_SELECT ){ - reg = sqlite3CodeSubselect(pParse, pExpr, 0, 0); + reg = sqlite3CodeSubselect(pParse, pExpr); } #endif return reg; @@ -96668,7 +97443,7 @@ static void codeVectorCompare( int regLeft = 0; int regRight = 0; u8 opx = op; - int addrDone = sqlite3VdbeMakeLabel(v); + int addrDone = sqlite3VdbeMakeLabel(pParse); if( nLeft!=sqlite3ExprVectorSize(pRight) ){ sqlite3ErrorMsg(pParse, "row value misused"); @@ -96895,8 +97670,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprAlloc( if( pToken->n ) memcpy(pNew->u.zToken, pToken->z, pToken->n); pNew->u.zToken[pToken->n] = 0; if( dequote && sqlite3Isquote(pNew->u.zToken[0]) ){ - if( pNew->u.zToken[0]=='"' ) pNew->flags |= EP_DblQuoted; - sqlite3Dequote(pNew->u.zToken); + sqlite3DequoteExpr(pNew); } } } @@ -96965,14 +97739,14 @@ SQLITE_PRIVATE Expr *sqlite3PExpr( Expr *pRight /* Right operand */ ){ Expr *p; - if( op==TK_AND && pParse->nErr==0 ){ + if( op==TK_AND && pParse->nErr==0 && !IN_RENAME_OBJECT ){ /* Take advantage of short-circuit false optimization for AND */ p = sqlite3ExprAnd(pParse->db, pLeft, pRight); }else{ p = sqlite3DbMallocRawNN(pParse->db, sizeof(Expr)); if( p ){ memset(p, 0, sizeof(Expr)); - p->op = op & TKFLG_MASK; + p->op = op & 0xff; p->iAgg = -1; } sqlite3ExprAttachSubtrees(pParse->db, p, pLeft, pRight); @@ -97214,6 +97988,16 @@ static int exprStructSize(Expr *p){ return EXPR_FULLSIZE; } +/* +** Copy the complete content of an Expr node, taking care not to read +** past the end of the structure for a reduced-size version of the source +** Expr. +*/ +static void exprNodeCopy(Expr *pDest, Expr *pSrc){ + memset(pDest, 0, sizeof(Expr)); + memcpy(pDest, pSrc, exprStructSize(pSrc)); +} + /* ** The dupedExpr*Size() routines each return the number of bytes required ** to store a copy of an expression or expression tree. They differ in @@ -97427,7 +98211,7 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){ static With *withDup(sqlite3 *db, With *p){ With *pRet = 0; if( p ){ - int nByte = sizeof(*p) + sizeof(p->a[0]) * (p->nCte-1); + sqlite3_int64 nByte = sizeof(*p) + sizeof(p->a[0]) * (p->nCte-1); pRet = sqlite3DbMallocZero(db, nByte); if( pRet ){ int i; @@ -97445,6 +98229,36 @@ static With *withDup(sqlite3 *db, With *p){ # define withDup(x,y) 0 #endif +#ifndef SQLITE_OMIT_WINDOWFUNC +/* +** The gatherSelectWindows() procedure and its helper routine +** gatherSelectWindowsCallback() are used to scan all the expressions +** an a newly duplicated SELECT statement and gather all of the Window +** objects found there, assembling them onto the linked list at Select->pWin. +*/ +static int gatherSelectWindowsCallback(Walker *pWalker, Expr *pExpr){ + if( pExpr->op==TK_FUNCTION && pExpr->y.pWin!=0 ){ + assert( ExprHasProperty(pExpr, EP_WinFunc) ); + pExpr->y.pWin->pNextWin = pWalker->u.pSelect->pWin; + pWalker->u.pSelect->pWin = pExpr->y.pWin; + } + return WRC_Continue; +} +static int gatherSelectWindowsSelectCallback(Walker *pWalker, Select *p){ + return p==pWalker->u.pSelect ? WRC_Continue : WRC_Prune; +} +static void gatherSelectWindows(Select *p){ + Walker w; + w.xExprCallback = gatherSelectWindowsCallback; + w.xSelectCallback = gatherSelectWindowsSelectCallback; + w.xSelectCallback2 = 0; + w.pParse = 0; + w.u.pSelect = p; + sqlite3WalkSelect(&w, p); +} +#endif + + /* ** The following group of routines make deep copies of expressions, ** expression lists, ID lists, and select statements. The copies can @@ -97612,6 +98426,7 @@ SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, Select *pDup, int flags){ #ifndef SQLITE_OMIT_WINDOWFUNC pNew->pWin = 0; pNew->pWinDefn = sqlite3WindowListDup(db, p->pWinDefn); + if( p->pWin ) gatherSelectWindows(pNew); #endif pNew->selId = p->selId; *pp = pNew; @@ -97661,7 +98476,7 @@ SQLITE_PRIVATE ExprList *sqlite3ExprListAppend( }else if( (pList->nExpr & (pList->nExpr-1))==0 ){ ExprList *pNew; pNew = sqlite3DbRealloc(db, pList, - sizeof(*pList)+(2*pList->nExpr - 1)*sizeof(pList->a[0])); + sizeof(*pList)+(2*(sqlite3_int64)pList->nExpr-1)*sizeof(pList->a[0])); if( pNew==0 ){ goto no_mem; } @@ -97744,6 +98559,9 @@ SQLITE_PRIVATE ExprList *sqlite3ExprListAppendVector( } vector_append_error: + if( IN_RENAME_OBJECT ){ + sqlite3RenameExprUnmap(pParse, pExpr); + } sqlite3ExprDelete(db, pExpr); sqlite3IdListDelete(db, pColumns); return pList; @@ -97887,8 +98705,9 @@ SQLITE_PRIVATE int sqlite3SelectWalkFail(Walker *pWalker, Select *NotUsed){ */ SQLITE_PRIVATE int sqlite3ExprIdToTrueFalse(Expr *pExpr){ assert( pExpr->op==TK_ID || pExpr->op==TK_STRING ); - if( sqlite3StrICmp(pExpr->u.zToken, "true")==0 - || sqlite3StrICmp(pExpr->u.zToken, "false")==0 + if( !ExprHasProperty(pExpr, EP_Quoted) + && (sqlite3StrICmp(pExpr->u.zToken, "true")==0 + || sqlite3StrICmp(pExpr->u.zToken, "false")==0) ){ pExpr->op = TK_TRUEFALSE; return 1; @@ -98197,7 +99016,9 @@ SQLITE_PRIVATE int sqlite3ExprIsInteger(Expr *p, int *pValue){ */ SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr *p){ u8 op; - while( p->op==TK_UPLUS || p->op==TK_UMINUS ){ p = p->pLeft; } + while( p->op==TK_UPLUS || p->op==TK_UMINUS ){ + p = p->pLeft; + } op = p->op; if( op==TK_REGISTER ) op = p->op2; switch( op ){ @@ -98264,14 +99085,6 @@ SQLITE_PRIVATE int sqlite3IsRowid(const char *z){ if( sqlite3StrICmp(z, "OID")==0 ) return 1; return 0; } -#ifdef SQLITE_ENABLE_NORMALIZE -SQLITE_PRIVATE int sqlite3IsRowidN(const char *z, int n){ - if( sqlite3StrNICmp(z, "_ROWID_", n)==0 ) return 1; - if( sqlite3StrNICmp(z, "ROWID", n)==0 ) return 1; - if( sqlite3StrNICmp(z, "OID", n)==0 ) return 1; - return 0; -} -#endif /* ** pX is the RHS of an IN operator. If pX is a SELECT statement @@ -98441,7 +99254,8 @@ SQLITE_PRIVATE int sqlite3FindInIndex( Expr *pX, /* The right-hand side (RHS) of the IN operator */ u32 inFlags, /* IN_INDEX_LOOP, _MEMBERSHIP, and/or _NOOP_OK */ int *prRhsHasNull, /* Register holding NULL status. See notes */ - int *aiMap /* Mapping from Index fields to RHS fields */ + int *aiMap, /* Mapping from Index fields to RHS fields */ + int *piTab /* OUT: index to use */ ){ Select *p; /* SELECT to the right of IN operator */ int eType = 0; /* Type of RHS table. IN_INDEX_* */ @@ -98536,6 +99350,7 @@ SQLITE_PRIVATE int sqlite3FindInIndex( Bitmask colUsed; /* Columns of the index used */ Bitmask mCol; /* Mask for the current column */ if( pIdx->nColumnpPartIdxWhere!=0 ) continue; /* Maximum nColumn is BMS-2, not BMS-1, so that we can compute ** BITMASK(nExpr) without overflowing */ testcase( pIdx->nColumn==BMS-2 ); @@ -98626,16 +99441,15 @@ SQLITE_PRIVATE int sqlite3FindInIndex( eType = IN_INDEX_EPH; if( inFlags & IN_INDEX_LOOP ){ pParse->nQueryLoop = 0; - if( pX->pLeft->iColumn<0 && !ExprHasProperty(pX, EP_xIsSelect) ){ - eType = IN_INDEX_ROWID; - } }else if( prRhsHasNull ){ *prRhsHasNull = rMayHaveNull = ++pParse->nMem; } - sqlite3CodeSubselect(pParse, pX, rMayHaveNull, eType==IN_INDEX_ROWID); + assert( pX->op==TK_IN ); + sqlite3CodeRhsOfIN(pParse, pX, iTab); + if( rMayHaveNull ){ + sqlite3SetHasNullFlag(v, iTab, rMayHaveNull); + } pParse->nQueryLoop = savedNQueryLoop; - }else{ - pX->iTable = iTab; } if( aiMap && eType!=IN_INDEX_INDEX_ASC && eType!=IN_INDEX_INDEX_DESC ){ @@ -98643,6 +99457,7 @@ SQLITE_PRIVATE int sqlite3FindInIndex( n = sqlite3ExprVectorSize(pX->pLeft); for(i=0; i IN (?, ?, ?)", where is a reference -** to some integer key column of a table B-Tree. In this case, use an -** intkey B-Tree to store the set of IN(...) values instead of the usual -** (slower) variable length keys B-Tree. -** -** If rMayHaveNull is non-zero, that means that the operation is an IN -** (not a SELECT or EXISTS) and that the RHS might contains NULLs. -** All this routine does is initialize the register given by rMayHaveNull -** to NULL. Calling routines will take care of changing this register -** value to non-NULL if the RHS is NULL-free. -** -** For a SELECT or EXISTS operator, return the register that holds the -** result. For a multi-column SELECT, the result is stored in a contiguous -** array of registers and the return value is the register of the left-most -** result column. Return 0 for IN operators or if an error occurs. -*/ -#ifndef SQLITE_OMIT_SUBQUERY -SQLITE_PRIVATE int sqlite3CodeSubselect( +** The pExpr parameter is the IN operator. The cursor number for the +** constructed ephermeral table is returned. The first time the ephemeral +** table is computed, the cursor number is also stored in pExpr->iTable, +** however the cursor number returned might not be the same, as it might +** have been duplicated using OP_OpenDup. +** +** If the LHS expression ("x" in the examples) is a column value, or +** the SELECT statement returns a column value, then the affinity of that +** column is used to build the index keys. If both 'x' and the +** SELECT... statement are columns, then numeric affinity is used +** if either column has NUMERIC or INTEGER affinity. If neither +** 'x' nor the SELECT... statement are columns, then numeric affinity +** is used. +*/ +SQLITE_PRIVATE void sqlite3CodeRhsOfIN( Parse *pParse, /* Parsing context */ - Expr *pExpr, /* The IN, SELECT, or EXISTS operator */ - int rHasNullFlag, /* Register that records whether NULLs exist in RHS */ - int isRowid /* If true, LHS of IN operator is a rowid */ + Expr *pExpr, /* The IN operator */ + int iTab /* Use this cursor number */ ){ - int jmpIfDynamic = -1; /* One-time test address */ - int rReg = 0; /* Register storing resulting */ - Vdbe *v = sqlite3GetVdbe(pParse); - if( NEVER(v==0) ) return 0; + int addrOnce = 0; /* Address of the OP_Once instruction at top */ + int addr; /* Address of OP_OpenEphemeral instruction */ + Expr *pLeft; /* the LHS of the IN operator */ + KeyInfo *pKeyInfo = 0; /* Key information */ + int nVal; /* Size of vector pLeft */ + Vdbe *v; /* The prepared statement under construction */ + + v = pParse->pVdbe; + assert( v!=0 ); - /* The evaluation of the IN/EXISTS/SELECT must be repeated every time it + /* The evaluation of the IN must be repeated every time it ** is encountered if any of the following is true: ** ** * The right-hand side is a correlated subquery ** * The right-hand side is an expression list containing variables ** * We are inside a trigger ** - ** If all of the above are false, then we can run this code just once - ** save the results, and reuse the same result on subsequent invocations. + ** If all of the above are false, then we can compute the RHS just once + ** and reuse it many names. */ - if( !ExprHasProperty(pExpr, EP_VarSelect) ){ - jmpIfDynamic = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); - } + if( !ExprHasProperty(pExpr, EP_VarSelect) && pParse->iSelfTab==0 ){ + /* Reuse of the RHS is allowed */ + /* If this routine has already been coded, but the previous code + ** might not have been invoked yet, so invoke it now as a subroutine. + */ + if( ExprHasProperty(pExpr, EP_Subrtn) ){ + addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); + if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + ExplainQueryPlan((pParse, 0, "REUSE LIST SUBQUERY %d", + pExpr->x.pSelect->selId)); + } + sqlite3VdbeAddOp2(v, OP_Gosub, pExpr->y.sub.regReturn, + pExpr->y.sub.iAddr); + sqlite3VdbeAddOp2(v, OP_OpenDup, iTab, pExpr->iTable); + sqlite3VdbeJumpHere(v, addrOnce); + return; + } - switch( pExpr->op ){ - case TK_IN: { - int addr; /* Address of OP_OpenEphemeral instruction */ - Expr *pLeft = pExpr->pLeft; /* the LHS of the IN operator */ - KeyInfo *pKeyInfo = 0; /* Key information */ - int nVal; /* Size of vector pLeft */ - - nVal = sqlite3ExprVectorSize(pLeft); - assert( !isRowid || nVal==1 ); + /* Begin coding the subroutine */ + ExprSetProperty(pExpr, EP_Subrtn); + pExpr->y.sub.regReturn = ++pParse->nMem; + pExpr->y.sub.iAddr = + sqlite3VdbeAddOp2(v, OP_Integer, 0, pExpr->y.sub.regReturn) + 1; + VdbeComment((v, "return address")); - /* Whether this is an 'x IN(SELECT...)' or an 'x IN()' - ** expression it is handled the same way. An ephemeral table is - ** filled with index keys representing the results from the - ** SELECT or the . - ** - ** If the 'x' expression is a column value, or the SELECT... - ** statement returns a column value, then the affinity of that - ** column is used to build the index keys. If both 'x' and the - ** SELECT... statement are columns, then numeric affinity is used - ** if either column has NUMERIC or INTEGER affinity. If neither - ** 'x' nor the SELECT... statement are columns, then numeric affinity - ** is used. - */ - pExpr->iTable = pParse->nTab++; - addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, - pExpr->iTable, (isRowid?0:nVal)); - pKeyInfo = isRowid ? 0 : sqlite3KeyInfoAlloc(pParse->db, nVal, 1); + addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); + } - if( ExprHasProperty(pExpr, EP_xIsSelect) ){ - /* Case 1: expr IN (SELECT ...) - ** - ** Generate code to write the results of the select into the temporary - ** table allocated and opened above. - */ - Select *pSelect = pExpr->x.pSelect; - ExprList *pEList = pSelect->pEList; - - ExplainQueryPlan((pParse, 1, "%sLIST SUBQUERY", - jmpIfDynamic>=0?"":"CORRELATED " - )); - assert( !isRowid ); - /* If the LHS and RHS of the IN operator do not match, that - ** error will have been caught long before we reach this point. */ - if( ALWAYS(pEList->nExpr==nVal) ){ - SelectDest dest; - int i; - sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable); - dest.zAffSdst = exprINAffinity(pParse, pExpr); - pSelect->iLimit = 0; - testcase( pSelect->selFlags & SF_Distinct ); - testcase( pKeyInfo==0 ); /* Caused by OOM in sqlite3KeyInfoAlloc() */ - if( sqlite3Select(pParse, pSelect, &dest) ){ - sqlite3DbFree(pParse->db, dest.zAffSdst); - sqlite3KeyInfoUnref(pKeyInfo); - return 0; - } - sqlite3DbFree(pParse->db, dest.zAffSdst); - assert( pKeyInfo!=0 ); /* OOM will cause exit after sqlite3Select() */ - assert( pEList!=0 ); - assert( pEList->nExpr>0 ); - assert( sqlite3KeyInfoIsWriteable(pKeyInfo) ); - for(i=0; iaColl[i] = sqlite3BinaryCompareCollSeq( - pParse, p, pEList->a[i].pExpr - ); - } - } - }else if( ALWAYS(pExpr->x.pList!=0) ){ - /* Case 2: expr IN (exprlist) - ** - ** For each expression, build an index key from the evaluation and - ** store it in the temporary table. If is a column, then use - ** that columns affinity when building index keys. If is not - ** a column, use numeric affinity. - */ - char affinity; /* Affinity of the LHS of the IN */ - int i; - ExprList *pList = pExpr->x.pList; - struct ExprList_item *pItem; - int r1, r2, r3; - affinity = sqlite3ExprAffinity(pLeft); - if( !affinity ){ - affinity = SQLITE_AFF_BLOB; - } - if( pKeyInfo ){ - assert( sqlite3KeyInfoIsWriteable(pKeyInfo) ); - pKeyInfo->aColl[0] = sqlite3ExprCollSeq(pParse, pExpr->pLeft); - } + /* Check to see if this is a vector IN operator */ + pLeft = pExpr->pLeft; + nVal = sqlite3ExprVectorSize(pLeft); - /* Loop through each expression in . */ - r1 = sqlite3GetTempReg(pParse); - r2 = sqlite3GetTempReg(pParse); - if( isRowid ) sqlite3VdbeAddOp4(v, OP_Blob, 0, r2, 0, "", P4_STATIC); - for(i=pList->nExpr, pItem=pList->a; i>0; i--, pItem++){ - Expr *pE2 = pItem->pExpr; - int iValToIns; - - /* If the expression is not constant then we will need to - ** disable the test that was generated above that makes sure - ** this code only executes once. Because for a non-constant - ** expression we need to rerun this code each time. - */ - if( jmpIfDynamic>=0 && !sqlite3ExprIsConstant(pE2) ){ - sqlite3VdbeChangeToNoop(v, jmpIfDynamic); - jmpIfDynamic = -1; - } + /* Construct the ephemeral table that will contain the content of + ** RHS of the IN operator. + */ + pExpr->iTable = iTab; + addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, nVal); +#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS + if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + VdbeComment((v, "Result of SELECT %u", pExpr->x.pSelect->selId)); + }else{ + VdbeComment((v, "RHS of IN operator")); + } +#endif + pKeyInfo = sqlite3KeyInfoAlloc(pParse->db, nVal, 1); - /* Evaluate the expression and insert it into the temp table */ - if( isRowid && sqlite3ExprIsInteger(pE2, &iValToIns) ){ - sqlite3VdbeAddOp3(v, OP_InsertInt, pExpr->iTable, r2, iValToIns); - }else{ - r3 = sqlite3ExprCodeTarget(pParse, pE2, r1); - if( isRowid ){ - sqlite3VdbeAddOp2(v, OP_MustBeInt, r3, - sqlite3VdbeCurrentAddr(v)+2); - VdbeCoverage(v); - sqlite3VdbeAddOp3(v, OP_Insert, pExpr->iTable, r2, r3); - }else{ - sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1); - sqlite3VdbeAddOp4Int(v, OP_IdxInsert, pExpr->iTable, r2, r3, 1); - } - } - } - sqlite3ReleaseTempReg(pParse, r1); - sqlite3ReleaseTempReg(pParse, r2); + if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + /* Case 1: expr IN (SELECT ...) + ** + ** Generate code to write the results of the select into the temporary + ** table allocated and opened above. + */ + Select *pSelect = pExpr->x.pSelect; + ExprList *pEList = pSelect->pEList; + + ExplainQueryPlan((pParse, 1, "%sLIST SUBQUERY %d", + addrOnce?"":"CORRELATED ", pSelect->selId + )); + /* If the LHS and RHS of the IN operator do not match, that + ** error will have been caught long before we reach this point. */ + if( ALWAYS(pEList->nExpr==nVal) ){ + SelectDest dest; + int i; + sqlite3SelectDestInit(&dest, SRT_Set, iTab); + dest.zAffSdst = exprINAffinity(pParse, pExpr); + pSelect->iLimit = 0; + testcase( pSelect->selFlags & SF_Distinct ); + testcase( pKeyInfo==0 ); /* Caused by OOM in sqlite3KeyInfoAlloc() */ + if( sqlite3Select(pParse, pSelect, &dest) ){ + sqlite3DbFree(pParse->db, dest.zAffSdst); + sqlite3KeyInfoUnref(pKeyInfo); + return; } - if( pKeyInfo ){ - sqlite3VdbeChangeP4(v, addr, (void *)pKeyInfo, P4_KEYINFO); + sqlite3DbFree(pParse->db, dest.zAffSdst); + assert( pKeyInfo!=0 ); /* OOM will cause exit after sqlite3Select() */ + assert( pEList!=0 ); + assert( pEList->nExpr>0 ); + assert( sqlite3KeyInfoIsWriteable(pKeyInfo) ); + for(i=0; iaColl[i] = sqlite3BinaryCompareCollSeq( + pParse, p, pEList->a[i].pExpr + ); } - break; + } + }else if( ALWAYS(pExpr->x.pList!=0) ){ + /* Case 2: expr IN (exprlist) + ** + ** For each expression, build an index key from the evaluation and + ** store it in the temporary table. If is a column, then use + ** that columns affinity when building index keys. If is not + ** a column, use numeric affinity. + */ + char affinity; /* Affinity of the LHS of the IN */ + int i; + ExprList *pList = pExpr->x.pList; + struct ExprList_item *pItem; + int r1, r2, r3; + affinity = sqlite3ExprAffinity(pLeft); + if( !affinity ){ + affinity = SQLITE_AFF_BLOB; + } + if( pKeyInfo ){ + assert( sqlite3KeyInfoIsWriteable(pKeyInfo) ); + pKeyInfo->aColl[0] = sqlite3ExprCollSeq(pParse, pExpr->pLeft); } - case TK_EXISTS: - case TK_SELECT: - default: { - /* Case 3: (SELECT ... FROM ...) - ** or: EXISTS(SELECT ... FROM ...) - ** - ** For a SELECT, generate code to put the values for all columns of - ** the first row into an array of registers and return the index of - ** the first register. - ** - ** If this is an EXISTS, write an integer 0 (not exists) or 1 (exists) - ** into a register and return that register number. - ** - ** In both cases, the query is augmented with "LIMIT 1". Any - ** preexisting limit is discarded in place of the new LIMIT 1. + /* Loop through each expression in . */ + r1 = sqlite3GetTempReg(pParse); + r2 = sqlite3GetTempReg(pParse); + for(i=pList->nExpr, pItem=pList->a; i>0; i--, pItem++){ + Expr *pE2 = pItem->pExpr; + + /* If the expression is not constant then we will need to + ** disable the test that was generated above that makes sure + ** this code only executes once. Because for a non-constant + ** expression we need to rerun this code each time. */ - Select *pSel; /* SELECT statement to encode */ - SelectDest dest; /* How to deal with SELECT result */ - int nReg; /* Registers to allocate */ - Expr *pLimit; /* New limit expression */ - - testcase( pExpr->op==TK_EXISTS ); - testcase( pExpr->op==TK_SELECT ); - assert( pExpr->op==TK_EXISTS || pExpr->op==TK_SELECT ); - assert( ExprHasProperty(pExpr, EP_xIsSelect) ); - - pSel = pExpr->x.pSelect; - ExplainQueryPlan((pParse, 1, "%sSCALAR SUBQUERY", - jmpIfDynamic>=0?"":"CORRELATED ")); - nReg = pExpr->op==TK_SELECT ? pSel->pEList->nExpr : 1; - sqlite3SelectDestInit(&dest, 0, pParse->nMem+1); - pParse->nMem += nReg; - if( pExpr->op==TK_SELECT ){ - dest.eDest = SRT_Mem; - dest.iSdst = dest.iSDParm; - dest.nSdst = nReg; - sqlite3VdbeAddOp3(v, OP_Null, 0, dest.iSDParm, dest.iSDParm+nReg-1); - VdbeComment((v, "Init subquery result")); - }else{ - dest.eDest = SRT_Exists; - sqlite3VdbeAddOp2(v, OP_Integer, 0, dest.iSDParm); - VdbeComment((v, "Init EXISTS result")); - } - pLimit = sqlite3ExprAlloc(pParse->db, TK_INTEGER,&sqlite3IntTokens[1], 0); - if( pSel->pLimit ){ - sqlite3ExprDelete(pParse->db, pSel->pLimit->pLeft); - pSel->pLimit->pLeft = pLimit; - }else{ - pSel->pLimit = sqlite3PExpr(pParse, TK_LIMIT, pLimit, 0); + if( addrOnce && !sqlite3ExprIsConstant(pE2) ){ + sqlite3VdbeChangeToNoop(v, addrOnce); + addrOnce = 0; } - pSel->iLimit = 0; - if( sqlite3Select(pParse, pSel, &dest) ){ - return 0; - } - rReg = dest.iSDParm; - ExprSetVVAProperty(pExpr, EP_NoReduce); - break; + + /* Evaluate the expression and insert it into the temp table */ + r3 = sqlite3ExprCodeTarget(pParse, pE2, r1); + sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1); + sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iTab, r2, r3, 1); } + sqlite3ReleaseTempReg(pParse, r1); + sqlite3ReleaseTempReg(pParse, r2); } + if( pKeyInfo ){ + sqlite3VdbeChangeP4(v, addr, (void *)pKeyInfo, P4_KEYINFO); + } + if( addrOnce ){ + sqlite3VdbeJumpHere(v, addrOnce); + /* Subroutine return */ + sqlite3VdbeAddOp1(v, OP_Return, pExpr->y.sub.regReturn); + sqlite3VdbeChangeP1(v, pExpr->y.sub.iAddr-1, sqlite3VdbeCurrentAddr(v)-1); + } +} +#endif /* SQLITE_OMIT_SUBQUERY */ + +/* +** Generate code for scalar subqueries used as a subquery expression +** or EXISTS operator: +** +** (SELECT a FROM b) -- subquery +** EXISTS (SELECT a FROM b) -- EXISTS subquery +** +** The pExpr parameter is the SELECT or EXISTS operator to be coded. +** +** The register that holds the result. For a multi-column SELECT, +** the result is stored in a contiguous array of registers and the +** return value is the register of the left-most result column. +** Return 0 if an error occurs. +*/ +#ifndef SQLITE_OMIT_SUBQUERY +SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ + int addrOnce = 0; /* Address of OP_Once at top of subroutine */ + int rReg = 0; /* Register storing resulting */ + Select *pSel; /* SELECT statement to encode */ + SelectDest dest; /* How to deal with SELECT result */ + int nReg; /* Registers to allocate */ + Expr *pLimit; /* New limit expression */ + + Vdbe *v = pParse->pVdbe; + assert( v!=0 ); + testcase( pExpr->op==TK_EXISTS ); + testcase( pExpr->op==TK_SELECT ); + assert( pExpr->op==TK_EXISTS || pExpr->op==TK_SELECT ); + assert( ExprHasProperty(pExpr, EP_xIsSelect) ); + pSel = pExpr->x.pSelect; - if( rHasNullFlag ){ - sqlite3SetHasNullFlag(v, pExpr->iTable, rHasNullFlag); + /* The evaluation of the EXISTS/SELECT must be repeated every time it + ** is encountered if any of the following is true: + ** + ** * The right-hand side is a correlated subquery + ** * The right-hand side is an expression list containing variables + ** * We are inside a trigger + ** + ** If all of the above are false, then we can run this code just once + ** save the results, and reuse the same result on subsequent invocations. + */ + if( !ExprHasProperty(pExpr, EP_VarSelect) ){ + /* If this routine has already been coded, then invoke it as a + ** subroutine. */ + if( ExprHasProperty(pExpr, EP_Subrtn) ){ + ExplainQueryPlan((pParse, 0, "REUSE SUBQUERY %d", pSel->selId)); + sqlite3VdbeAddOp2(v, OP_Gosub, pExpr->y.sub.regReturn, + pExpr->y.sub.iAddr); + return pExpr->iTable; + } + + /* Begin coding the subroutine */ + ExprSetProperty(pExpr, EP_Subrtn); + pExpr->y.sub.regReturn = ++pParse->nMem; + pExpr->y.sub.iAddr = + sqlite3VdbeAddOp2(v, OP_Integer, 0, pExpr->y.sub.regReturn) + 1; + VdbeComment((v, "return address")); + + addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); } + + /* For a SELECT, generate code to put the values for all columns of + ** the first row into an array of registers and return the index of + ** the first register. + ** + ** If this is an EXISTS, write an integer 0 (not exists) or 1 (exists) + ** into a register and return that register number. + ** + ** In both cases, the query is augmented with "LIMIT 1". Any + ** preexisting limit is discarded in place of the new LIMIT 1. + */ + ExplainQueryPlan((pParse, 1, "%sSCALAR SUBQUERY %d", + addrOnce?"":"CORRELATED ", pSel->selId)); + nReg = pExpr->op==TK_SELECT ? pSel->pEList->nExpr : 1; + sqlite3SelectDestInit(&dest, 0, pParse->nMem+1); + pParse->nMem += nReg; + if( pExpr->op==TK_SELECT ){ + dest.eDest = SRT_Mem; + dest.iSdst = dest.iSDParm; + dest.nSdst = nReg; + sqlite3VdbeAddOp3(v, OP_Null, 0, dest.iSDParm, dest.iSDParm+nReg-1); + VdbeComment((v, "Init subquery result")); + }else{ + dest.eDest = SRT_Exists; + sqlite3VdbeAddOp2(v, OP_Integer, 0, dest.iSDParm); + VdbeComment((v, "Init EXISTS result")); + } + pLimit = sqlite3ExprAlloc(pParse->db, TK_INTEGER,&sqlite3IntTokens[1], 0); + if( pSel->pLimit ){ + sqlite3ExprDelete(pParse->db, pSel->pLimit->pLeft); + pSel->pLimit->pLeft = pLimit; + }else{ + pSel->pLimit = sqlite3PExpr(pParse, TK_LIMIT, pLimit, 0); + } + pSel->iLimit = 0; + if( sqlite3Select(pParse, pSel, &dest) ){ + return 0; + } + pExpr->iTable = rReg = dest.iSDParm; + ExprSetVVAProperty(pExpr, EP_NoReduce); + if( addrOnce ){ + sqlite3VdbeJumpHere(v, addrOnce); - if( jmpIfDynamic>=0 ){ - sqlite3VdbeJumpHere(v, jmpIfDynamic); + /* Subroutine return */ + sqlite3VdbeAddOp1(v, OP_Return, pExpr->y.sub.regReturn); + sqlite3VdbeChangeP1(v, pExpr->y.sub.iAddr-1, sqlite3VdbeCurrentAddr(v)-1); } return rReg; @@ -99046,6 +99904,7 @@ static void sqlite3ExprCodeIN( int addrTruthOp; /* Address of opcode that determines the IN is true */ int destNotNull; /* Jump here if a comparison is not true in step 6 */ int addrTop; /* Top of the step-6 loop */ + int iTab = 0; /* Index to use */ pLeft = pExpr->pLeft; if( sqlite3ExprCheckIN(pParse, pExpr) ) return; @@ -99057,7 +99916,7 @@ static void sqlite3ExprCodeIN( if( pParse->db->mallocFailed ) goto sqlite3ExprCodeIN_oom_error; /* Attempt to compute the RHS. After this step, if anything other than - ** IN_INDEX_NOOP is returned, the table opened ith cursor pExpr->iTable + ** IN_INDEX_NOOP is returned, the table opened with cursor iTab ** contains the values that make up the RHS. If IN_INDEX_NOOP is returned, ** the RHS has not yet been coded. */ v = pParse->pVdbe; @@ -99065,7 +99924,8 @@ static void sqlite3ExprCodeIN( VdbeNoopComment((v, "begin IN expr")); eType = sqlite3FindInIndex(pParse, pExpr, IN_INDEX_MEMBERSHIP | IN_INDEX_NOOP_OK, - destIfFalse==destIfNull ? 0 : &rRhsHasNull, aiMap); + destIfFalse==destIfNull ? 0 : &rRhsHasNull, + aiMap, &iTab); assert( pParse->nErr || nVector==1 || eType==IN_INDEX_EPH || eType==IN_INDEX_INDEX_ASC || eType==IN_INDEX_INDEX_DESC @@ -99111,7 +99971,7 @@ static void sqlite3ExprCodeIN( if( eType==IN_INDEX_NOOP ){ ExprList *pList = pExpr->x.pList; CollSeq *pColl = sqlite3ExprCollSeq(pParse, pExpr->pLeft); - int labelOk = sqlite3VdbeMakeLabel(v); + int labelOk = sqlite3VdbeMakeLabel(pParse); int r2, regToFree; int regCkNull = 0; int ii; @@ -99155,7 +100015,7 @@ static void sqlite3ExprCodeIN( if( destIfNull==destIfFalse ){ destStep2 = destIfFalse; }else{ - destStep2 = destStep6 = sqlite3VdbeMakeLabel(v); + destStep2 = destStep6 = sqlite3VdbeMakeLabel(pParse); } for(i=0; ipLeft, i); @@ -99173,19 +100033,19 @@ static void sqlite3ExprCodeIN( /* In this case, the RHS is the ROWID of table b-tree and so we also ** know that the RHS is non-NULL. Hence, we combine steps 3 and 4 ** into a single opcode. */ - sqlite3VdbeAddOp3(v, OP_SeekRowid, pExpr->iTable, destIfFalse, rLhs); + sqlite3VdbeAddOp3(v, OP_SeekRowid, iTab, destIfFalse, rLhs); VdbeCoverage(v); addrTruthOp = sqlite3VdbeAddOp0(v, OP_Goto); /* Return True */ }else{ sqlite3VdbeAddOp4(v, OP_Affinity, rLhs, nVector, 0, zAff, nVector); if( destIfFalse==destIfNull ){ /* Combine Step 3 and Step 5 into a single opcode */ - sqlite3VdbeAddOp4Int(v, OP_NotFound, pExpr->iTable, destIfFalse, + sqlite3VdbeAddOp4Int(v, OP_NotFound, iTab, destIfFalse, rLhs, nVector); VdbeCoverage(v); goto sqlite3ExprCodeIN_finished; } /* Ordinary Step 3, for the case where FALSE and NULL are distinct */ - addrTruthOp = sqlite3VdbeAddOp4Int(v, OP_Found, pExpr->iTable, 0, + addrTruthOp = sqlite3VdbeAddOp4Int(v, OP_Found, iTab, 0, rLhs, nVector); VdbeCoverage(v); } @@ -99210,10 +100070,10 @@ static void sqlite3ExprCodeIN( ** of the RHS. */ if( destStep6 ) sqlite3VdbeResolveLabel(v, destStep6); - addrTop = sqlite3VdbeAddOp2(v, OP_Rewind, pExpr->iTable, destIfFalse); + addrTop = sqlite3VdbeAddOp2(v, OP_Rewind, iTab, destIfFalse); VdbeCoverage(v); if( nVector>1 ){ - destNotNull = sqlite3VdbeMakeLabel(v); + destNotNull = sqlite3VdbeMakeLabel(pParse); }else{ /* For nVector==1, combine steps 6 and 7 by immediately returning ** FALSE if the first comparison is not NULL */ @@ -99225,7 +100085,7 @@ static void sqlite3ExprCodeIN( int r3 = sqlite3GetTempReg(pParse); p = sqlite3VectorFieldSubexpr(pLeft, i); pColl = sqlite3ExprCollSeq(pParse, p); - sqlite3VdbeAddOp3(v, OP_Column, pExpr->iTable, i, r3); + sqlite3VdbeAddOp3(v, OP_Column, iTab, i, r3); sqlite3VdbeAddOp4(v, OP_Ne, rLhs+i, destNotNull, r3, (void*)pColl, P4_COLLSEQ); VdbeCoverage(v); @@ -99234,7 +100094,7 @@ static void sqlite3ExprCodeIN( sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfNull); if( nVector>1 ){ sqlite3VdbeResolveLabel(v, destNotNull); - sqlite3VdbeAddOp2(v, OP_Next, pExpr->iTable, addrTop+1); + sqlite3VdbeAddOp2(v, OP_Next, iTab, addrTop+1); VdbeCoverage(v); /* Step 7: If we reach this point, we know that the result must @@ -99433,7 +100293,7 @@ static int exprCodeVector(Parse *pParse, Expr *p, int *piFreeable){ #if SQLITE_OMIT_SUBQUERY iResult = 0; #else - iResult = sqlite3CodeSubselect(pParse, p, 0, 0); + iResult = sqlite3CodeSubselect(pParse, p); #endif }else{ int i; @@ -99778,7 +100638,7 @@ expr_code_doover: ** arguments past the first non-NULL argument. */ if( pDef->funcFlags & SQLITE_FUNC_COALESCE ){ - int endCoalesce = sqlite3VdbeMakeLabel(v); + int endCoalesce = sqlite3VdbeMakeLabel(pParse); assert( nFarg>=2 ); sqlite3ExprCode(pParse, pFarg->a[0].pExpr, target); for(i=1; ix.pSelect->pEList->nExpr)!=1 ){ sqlite3SubselectError(pParse, nCol, 1); }else{ - return sqlite3CodeSubselect(pParse, pExpr, 0, 0); + return sqlite3CodeSubselect(pParse, pExpr); } break; } case TK_SELECT_COLUMN: { int n; if( pExpr->pLeft->iTable==0 ){ - pExpr->pLeft->iTable = sqlite3CodeSubselect(pParse, pExpr->pLeft, 0, 0); + pExpr->pLeft->iTable = sqlite3CodeSubselect(pParse, pExpr->pLeft); } assert( pExpr->iTable==0 || pExpr->pLeft->op==TK_SELECT ); if( pExpr->iTable @@ -99926,8 +100786,8 @@ expr_code_doover: return pExpr->pLeft->iTable + pExpr->iColumn; } case TK_IN: { - int destIfFalse = sqlite3VdbeMakeLabel(v); - int destIfNull = sqlite3VdbeMakeLabel(v); + int destIfFalse = sqlite3VdbeMakeLabel(pParse); + int destIfNull = sqlite3VdbeMakeLabel(pParse); sqlite3VdbeAddOp2(v, OP_Null, 0, target); sqlite3ExprCodeIN(pParse, pExpr, destIfFalse, destIfNull); sqlite3VdbeAddOp2(v, OP_Integer, 1, target); @@ -100067,9 +100927,9 @@ expr_code_doover: pEList = pExpr->x.pList; aListelem = pEList->a; nExpr = pEList->nExpr; - endLabel = sqlite3VdbeMakeLabel(v); + endLabel = sqlite3VdbeMakeLabel(pParse); if( (pX = pExpr->pLeft)!=0 ){ - tempX = *pX; + exprNodeCopy(&tempX, pX); testcase( pX->op==TK_COLUMN ); exprToRegister(&tempX, exprCodeVector(pParse, &tempX, ®Free1)); testcase( regFree1==0 ); @@ -100090,7 +100950,7 @@ expr_code_doover: }else{ pTest = aListelem[i].pExpr; } - nextCase = sqlite3VdbeMakeLabel(v); + nextCase = sqlite3VdbeMakeLabel(pParse); testcase( pTest->op==TK_COLUMN ); sqlite3ExprIfFalse(pParse, pTest, nextCase, SQLITE_JUMPIFNULL); testcase( aListelem[i+1].pExpr->op==TK_COLUMN ); @@ -100390,13 +101250,12 @@ static void exprCodeBetween( Expr exprX; /* The x subexpression */ int regFree1 = 0; /* Temporary use register */ - memset(&compLeft, 0, sizeof(Expr)); memset(&compRight, 0, sizeof(Expr)); memset(&exprAnd, 0, sizeof(Expr)); assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); - exprX = *pExpr->pLeft; + exprNodeCopy(&exprX, pExpr->pLeft); exprAnd.op = TK_AND; exprAnd.pLeft = &compLeft; exprAnd.pRight = &compRight; @@ -100459,7 +101318,7 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int op = pExpr->op; switch( op ){ case TK_AND: { - int d2 = sqlite3VdbeMakeLabel(v); + int d2 = sqlite3VdbeMakeLabel(pParse); testcase( jumpIfNull==0 ); sqlite3ExprIfFalse(pParse, pExpr->pLeft, d2,jumpIfNull^SQLITE_JUMPIFNULL); sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull); @@ -100545,7 +101404,7 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int } #ifndef SQLITE_OMIT_SUBQUERY case TK_IN: { - int destIfFalse = sqlite3VdbeMakeLabel(v); + int destIfFalse = sqlite3VdbeMakeLabel(pParse); int destIfNull = jumpIfNull ? dest : destIfFalse; sqlite3ExprCodeIN(pParse, pExpr, destIfFalse, destIfNull); sqlite3VdbeGoto(v, dest); @@ -100632,7 +101491,7 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int break; } case TK_OR: { - int d2 = sqlite3VdbeMakeLabel(v); + int d2 = sqlite3VdbeMakeLabel(pParse); testcase( jumpIfNull==0 ); sqlite3ExprIfTrue(pParse, pExpr->pLeft, d2, jumpIfNull^SQLITE_JUMPIFNULL); sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull); @@ -100716,7 +101575,7 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int if( jumpIfNull ){ sqlite3ExprCodeIN(pParse, pExpr, dest, dest); }else{ - int destIfNull = sqlite3VdbeMakeLabel(v); + int destIfNull = sqlite3VdbeMakeLabel(pParse); sqlite3ExprCodeIN(pParse, pExpr, dest, destIfNull); sqlite3VdbeResolveLabel(v, destIfNull); } @@ -100837,7 +101696,7 @@ SQLITE_PRIVATE int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTa } return 2; } - if( pA->op!=pB->op ){ + if( pA->op!=pB->op || pA->op==TK_RAISE ){ if( pA->op==TK_COLLATE && sqlite3ExprCompare(pParse, pA->pLeft,pB,iTab)<2 ){ return 1; } @@ -100863,21 +101722,25 @@ SQLITE_PRIVATE int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTa if( sqlite3WindowCompare(pParse,pA->y.pWin,pB->y.pWin)!=0 ) return 2; } #endif + }else if( pA->op==TK_NULL ){ + return 0; }else if( pA->op==TK_COLLATE ){ if( sqlite3_stricmp(pA->u.zToken,pB->u.zToken)!=0 ) return 2; - }else if( strcmp(pA->u.zToken,pB->u.zToken)!=0 ){ + }else if( ALWAYS(pB->u.zToken!=0) && strcmp(pA->u.zToken,pB->u.zToken)!=0 ){ return 2; } } if( (pA->flags & EP_Distinct)!=(pB->flags & EP_Distinct) ) return 2; - if( ALWAYS((combinedFlags & EP_TokenOnly)==0) ){ + if( (combinedFlags & EP_TokenOnly)==0 ){ if( combinedFlags & EP_xIsSelect ) return 2; if( (combinedFlags & EP_FixedCol)==0 && sqlite3ExprCompare(pParse, pA->pLeft, pB->pLeft, iTab) ) return 2; if( sqlite3ExprCompare(pParse, pA->pRight, pB->pRight, iTab) ) return 2; if( sqlite3ExprListCompare(pA->x.pList, pB->x.pList, iTab) ) return 2; - assert( (combinedFlags & EP_Reduced)==0 ); - if( pA->op!=TK_STRING && pA->op!=TK_TRUEFALSE ){ + if( pA->op!=TK_STRING + && pA->op!=TK_TRUEFALSE + && (combinedFlags & EP_Reduced)==0 + ){ if( pA->iColumn!=pB->iColumn ) return 2; if( pA->iTable!=pB->iTable && (pA->iTable!=iTab || NEVER(pB->iTable>=0)) ) return 2; @@ -100986,6 +101849,7 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ case TK_ISNOT: case TK_NOT: case TK_ISNULL: + case TK_NOTNULL: case TK_IS: case TK_OR: case TK_CASE: @@ -100994,6 +101858,7 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ testcase( pExpr->op==TK_ISNOT ); testcase( pExpr->op==TK_NOT ); testcase( pExpr->op==TK_ISNULL ); + testcase( pExpr->op==TK_NOTNULL ); testcase( pExpr->op==TK_IS ); testcase( pExpr->op==TK_OR ); testcase( pExpr->op==TK_CASE ); @@ -101056,6 +101921,17 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ */ SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr *p, int iTab){ Walker w; + p = sqlite3ExprSkipCollate(p); + while( p ){ + if( p->op==TK_NOTNULL ){ + p = p->pLeft; + }else if( p->op==TK_AND ){ + if( sqlite3ExprImpliesNonNullRow(p->pLeft, iTab) ) return 1; + p = p->pRight; + }else{ + break; + } + } w.xExprCallback = impliesNotNullRow; w.xSelectCallback = 0; w.xSelectCallback2 = 0; @@ -101367,6 +102243,7 @@ SQLITE_PRIVATE void sqlite3ExprAnalyzeAggregates(NameContext *pNC, Expr *pExpr){ w.xSelectCallback2 = analyzeAggregatesInSelectEnd; w.walkerDepth = 0; w.u.pNC = pNC; + w.pParse = 0; assert( pNC->pSrcList!=0 ); sqlite3WalkExpr(&w, pExpr); } @@ -101498,9 +102375,16 @@ SQLITE_PRIVATE int sqlite3NoTempsInRange(Parse *pParse, int iFirst, int iLast){ ** ** Or, if zName is not a system table, zero is returned. */ -static int isSystemTable(Parse *pParse, const char *zName){ - if( 0==sqlite3StrNICmp(zName, "sqlite_", 7) ){ - sqlite3ErrorMsg(pParse, "table %s may not be altered", zName); +static int isAlterableTable(Parse *pParse, Table *pTab){ + if( 0==sqlite3StrNICmp(pTab->zName, "sqlite_", 7) +#ifndef SQLITE_OMIT_VIRTUALTABLE + || ( (pTab->tabFlags & TF_Shadow) + && (pParse->db->flags & SQLITE_Defensive) + && pParse->db->nVdbeExec==0 + ) +#endif + ){ + sqlite3ErrorMsg(pParse, "table %s may not be altered", pTab->zName); return 1; } return 0; @@ -101596,7 +102480,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable( /* Make sure it is not a system table being altered, or a reserved name ** that the table is being renamed to. */ - if( SQLITE_OK!=isSystemTable(pParse, pTab->zName) ){ + if( SQLITE_OK!=isAlterableTable(pParse, pTab) ){ goto exit_rename_table; } if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ goto @@ -101629,15 +102513,15 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable( } #endif - /* Begin a transaction for database iDb. - ** Then modify the schema cookie (since the ALTER TABLE modifies the - ** schema). Open a statement transaction if the table is a virtual - ** table. - */ + /* Begin a transaction for database iDb. Then modify the schema cookie + ** (since the ALTER TABLE modifies the schema). Call sqlite3MayAbort(), + ** as the scalar functions (e.g. sqlite_rename_table()) invoked by the + ** nested SQL may raise an exception. */ v = sqlite3GetVdbe(pParse); if( v==0 ){ goto exit_rename_table; } + sqlite3MayAbort(pParse); /* figure out how many UTF-8 characters are in zName */ zTabName = pTab->zName; @@ -101706,7 +102590,6 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable( int i = ++pParse->nMem; sqlite3VdbeLoadString(v, i, zName); sqlite3VdbeAddOp4(v, OP_VRename, i, 0, 0,(const char*)pVTab, P4_VTAB); - sqlite3MayAbort(pParse); } #endif @@ -101894,7 +102777,7 @@ SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){ sqlite3ErrorMsg(pParse, "Cannot add a column to a view"); goto exit_begin_add_column; } - if( SQLITE_OK!=isSystemTable(pParse, pTab->zName) ){ + if( SQLITE_OK!=isAlterableTable(pParse, pTab) ){ goto exit_begin_add_column; } @@ -101996,7 +102879,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameColumn( if( !pTab ) goto exit_rename_column; /* Cannot alter a system table */ - if( SQLITE_OK!=isSystemTable(pParse, pTab->zName) ) goto exit_rename_column; + if( SQLITE_OK!=isAlterableTable(pParse, pTab) ) goto exit_rename_column; if( SQLITE_OK!=isRealTable(pParse, pTab) ) goto exit_rename_column; /* Which schema holds the table to be altered */ @@ -102027,6 +102910,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameColumn( ** uses the sqlite_rename_column() SQL function to compute the new ** CREATE statement text for the sqlite_master table. */ + sqlite3MayAbort(pParse); zNew = sqlite3NameFromToken(db, pNew); if( !zNew ) goto exit_rename_column; assert( pNew->n>0 ); @@ -102250,14 +103134,31 @@ static void renameTokenFind(Parse *pParse, struct RenameCtx *pCtx, void *pPtr){ } } +/* +** Iterate through the Select objects that are part of WITH clauses attached +** to select statement pSelect. +*/ +static void renameWalkWith(Walker *pWalker, Select *pSelect){ + if( pSelect->pWith ){ + int i; + for(i=0; ipWith->nCte; i++){ + Select *p = pSelect->pWith->a[i].pSelect; + NameContext sNC; + memset(&sNC, 0, sizeof(sNC)); + sNC.pParse = pWalker->pParse; + sqlite3SelectPrep(sNC.pParse, p, &sNC); + sqlite3WalkSelect(pWalker, p); + } + } +} + /* ** This is a Walker select callback. It does nothing. It is only required ** because without a dummy callback, sqlite3WalkExpr() and similar do not ** descend into sub-select statements. */ static int renameColumnSelectCb(Walker *pWalker, Select *p){ - UNUSED_PARAMETER(pWalker); - UNUSED_PARAMETER(p); + renameWalkWith(pWalker, p); return WRC_Continue; } @@ -102407,7 +103308,6 @@ static int renameParseSql( rc = sqlite3RunParser(p, zSql, &zErr); assert( p->zErrMsg==0 ); assert( rc!=SQLITE_OK || zErr==0 ); - assert( (0!=p->pNewTable) + (0!=p->pNewIndex) + (0!=p->pNewTrigger)<2 ); p->zErrMsg = zErr; if( db->mallocFailed ) rc = SQLITE_NOMEM; if( rc==SQLITE_OK @@ -102590,6 +103490,7 @@ static int renameResolveTrigger(Parse *pParse, const char *zDb){ } sNC.ncFlags = 0; } + sNC.pSrcList = 0; } } } @@ -102627,11 +103528,15 @@ static void renameWalkTrigger(Walker *pWalker, Trigger *pTrigger){ */ static void renameParseCleanup(Parse *pParse){ sqlite3 *db = pParse->db; + Index *pIdx; if( pParse->pVdbe ){ sqlite3VdbeFinalize(pParse->pVdbe); } sqlite3DeleteTable(db, pParse->pNewTable); - if( pParse->pNewIndex ) sqlite3FreeIndex(db, pParse->pNewIndex); + while( (pIdx = pParse->pNewIndex)!=0 ){ + pParse->pNewIndex = pIdx->pNext; + sqlite3FreeIndex(db, pIdx); + } sqlite3DeleteTrigger(db, pParse->pNewTrigger); sqlite3DbFree(db, pParse->zErrMsg); renameTokenFree(db, pParse->pRename); @@ -102742,6 +103647,9 @@ static void renameColumnFunc( for(pIdx=sParse.pNewTable->pIndex; pIdx; pIdx=pIdx->pNext){ sqlite3WalkExprList(&sWalker, pIdx->aColExpr); } + for(pIdx=sParse.pNewIndex; pIdx; pIdx=pIdx->pNext){ + sqlite3WalkExprList(&sWalker, pIdx->aColExpr); + } } for(pFKey=sParse.pNewTable->pFKey; pFKey; pFKey=pFKey->pNextFrom){ @@ -102828,12 +103736,17 @@ static int renameTableSelectCb(Walker *pWalker, Select *pSelect){ int i; RenameCtx *p = pWalker->u.pRename; SrcList *pSrc = pSelect->pSrc; + if( pSrc==0 ){ + assert( pWalker->pParse->db->mallocFailed ); + return WRC_Abort; + } for(i=0; inSrc; i++){ struct SrcList_item *pItem = &pSrc->a[i]; if( pItem->pTab==p->pTab ){ renameTokenFind(pWalker->pParse, p, pItem->zName); } } + renameWalkWith(pWalker, pSelect); return WRC_Continue; } @@ -104235,7 +105148,7 @@ static void analyzeOneTable( addrNextRow = sqlite3VdbeCurrentAddr(v); if( nColTest>0 ){ - int endDistinctTest = sqlite3VdbeMakeLabel(v); + int endDistinctTest = sqlite3VdbeMakeLabel(pParse); int *aGotoChng; /* Array of jump instruction addresses */ aGotoChng = sqlite3DbMallocRawNN(db, sizeof(int)*nColTest); if( aGotoChng==0 ) continue; @@ -105173,8 +106086,8 @@ static void attachFunc( assert( pVfs ); flags |= SQLITE_OPEN_MAIN_DB; rc = sqlite3BtreeOpen(pVfs, zPath, db, &pNew->pBt, 0, flags); - sqlite3_free( zPath ); db->nDb++; + pNew->zDbSName = sqlite3DbStrDup(db, zName); } db->noSharedCache = 0; if( rc==SQLITE_CONSTRAINT ){ @@ -105202,7 +106115,6 @@ static void attachFunc( sqlite3BtreeLeave(pNew->pBt); } pNew->safety_level = SQLITE_DEFAULT_SYNCHRONOUS+1; - if( !REOPEN_AS_MEMDB(db) ) pNew->zDbSName = sqlite3DbStrDup(db, zName); if( rc==SQLITE_OK && pNew->zDbSName==0 ){ rc = SQLITE_NOMEM_BKPT; } @@ -105230,15 +106142,19 @@ static void attachFunc( break; case SQLITE_NULL: - /* No key specified. Use the key from the main database */ - sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey); - if( nKey || sqlite3BtreeGetOptimalReserve(db->aDb[0].pBt)>0 ){ - rc = sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); + /* No key specified. Use the key from URI filename, or if none, + ** use the key from the main database. */ + if( sqlite3CodecQueryParameters(db, zName, zPath)==0 ){ + sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey); + if( nKey || sqlite3BtreeGetOptimalReserve(db->aDb[0].pBt)>0 ){ + rc = sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); + } } break; } } #endif + sqlite3_free( zPath ); /* If the file was opened successfully, read the schema for the new database. ** If this fails, or if opening the file failed, then close the file and @@ -105249,12 +106165,14 @@ static void attachFunc( sqlite3BtreeEnterAll(db); db->init.iDb = 0; db->mDbFlags &= ~(DBFLAG_SchemaKnownOk); - rc = sqlite3Init(db, &zErrDyn); + if( !REOPEN_AS_MEMDB(db) ){ + rc = sqlite3Init(db, &zErrDyn); + } sqlite3BtreeLeaveAll(db); assert( zErrDyn==0 || rc!=SQLITE_OK ); } #ifdef SQLITE_USER_AUTHENTICATION - if( rc==SQLITE_OK ){ + if( rc==SQLITE_OK && !REOPEN_AS_MEMDB(db) ){ u8 newAuth = 0; rc = sqlite3UserAuthCheckLogin(db, zName, &newAuth); if( newAuthauth.authLevel ){ @@ -106150,7 +107068,7 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){ if( v && pParse->nErr==0 && !db->mallocFailed ){ /* A minimum of one cursor is required if autoincrement is used * See ticket [a696379c1f08866] */ - if( pParse->pAinc!=0 && pParse->nTab==0 ) pParse->nTab = 1; + assert( pParse->pAinc==0 || pParse->nTab>0 ); sqlite3VdbeMakeReady(v, pParse); pParse->rc = SQLITE_DONE; }else{ @@ -106183,7 +107101,12 @@ SQLITE_PRIVATE void sqlite3NestedParse(Parse *pParse, const char *zFormat, ...){ zSql = sqlite3VMPrintf(db, zFormat, ap); va_end(ap); if( zSql==0 ){ - return; /* A malloc must have failed */ + /* This can result either from an OOM or because the formatted string + ** exceeds SQLITE_LIMIT_LENGTH. In the latter case, we need to set + ** an error */ + if( !db->mallocFailed ) pParse->rc = SQLITE_TOOBIG; + pParse->nErr++; + return; } pParse->nested++; memcpy(saveBuf, PARSE_TAIL(pParse), PARSE_TAIL_SZ); @@ -106277,26 +107200,32 @@ SQLITE_PRIVATE Table *sqlite3LocateTable( p = sqlite3FindTable(db, zName, zDbase); if( p==0 ){ - const char *zMsg = flags & LOCATE_VIEW ? "no such view" : "no such table"; #ifndef SQLITE_OMIT_VIRTUALTABLE /* If zName is the not the name of a table in the schema created using ** CREATE, then check to see if it is the name of an virtual table that ** can be an eponymous virtual table. */ - Module *pMod = (Module*)sqlite3HashFind(&db->aModule, zName); - if( pMod==0 && sqlite3_strnicmp(zName, "pragma_", 7)==0 ){ - pMod = sqlite3PragmaVtabRegister(db, zName); - } - if( pMod && sqlite3VtabEponymousTableInit(pParse, pMod) ){ - return pMod->pEpoTab; + if( pParse->disableVtab==0 ){ + Module *pMod = (Module*)sqlite3HashFind(&db->aModule, zName); + if( pMod==0 && sqlite3_strnicmp(zName, "pragma_", 7)==0 ){ + pMod = sqlite3PragmaVtabRegister(db, zName); + } + if( pMod && sqlite3VtabEponymousTableInit(pParse, pMod) ){ + return pMod->pEpoTab; + } } #endif - if( (flags & LOCATE_NOERR)==0 ){ - if( zDbase ){ - sqlite3ErrorMsg(pParse, "%s: %s.%s", zMsg, zDbase, zName); - }else{ - sqlite3ErrorMsg(pParse, "%s: %s", zMsg, zName); - } - pParse->checkSchema = 1; + if( flags & LOCATE_NOERR ) return 0; + pParse->checkSchema = 1; + }else if( IsVirtual(p) && pParse->disableVtab ){ + p = 0; + } + + if( p==0 ){ + const char *zMsg = flags & LOCATE_VIEW ? "no such view" : "no such table"; + if( zDbase ){ + sqlite3ErrorMsg(pParse, "%s: %s.%s", zMsg, zDbase, zName); + }else{ + sqlite3ErrorMsg(pParse, "%s: %s", zMsg, zName); } } @@ -106559,12 +107488,6 @@ static void SQLITE_NOINLINE deleteTable(sqlite3 *db, Table *pTable){ /* Delete the Table structure itself. */ -#ifdef SQLITE_ENABLE_NORMALIZE - if( pTable->pColHash ){ - sqlite3HashClear(pTable->pColHash); - sqlite3_free(pTable->pColHash); - } -#endif sqlite3DeleteColumnNames(db, pTable); sqlite3DbFree(db, pTable->zName); sqlite3DbFree(db, pTable->zColAff); @@ -107323,7 +108246,8 @@ SQLITE_PRIVATE void sqlite3AddPrimaryKey( && sortOrder!=SQLITE_SO_DESC ){ if( IN_RENAME_OBJECT && pList ){ - sqlite3RenameTokenRemap(pParse, &pTab->iPKey, pList->a[0].pExpr); + Expr *pCExpr = sqlite3ExprSkipCollate(pList->a[0].pExpr); + sqlite3RenameTokenRemap(pParse, &pTab->iPKey, pCExpr); } pTab->iPKey = iCol; pTab->keyConf = (u8)onError; @@ -107744,6 +108668,7 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ pTab->iPKey = -1; }else{ pPk = sqlite3PrimaryKeyIndex(pTab); + assert( pPk!=0 ); /* ** Remove all redundant columns from the PRIMARY KEY. For example, change @@ -107913,6 +108838,11 @@ SQLITE_PRIVATE void sqlite3EndTable( if( p->tnum==1 ) p->tabFlags |= TF_Readonly; } + assert( (p->tabFlags & TF_HasPrimaryKey)==0 + || p->iPKey>=0 || sqlite3PrimaryKeyIndex(p)!=0 ); + assert( (p->tabFlags & TF_HasPrimaryKey)!=0 + || (p->iPKey<0 && sqlite3PrimaryKeyIndex(p)==0) ); + /* Special processing for WITHOUT ROWID Tables */ if( tabOpts & TF_WithoutRowid ){ if( (p->tabFlags & TF_Autoincrement) ){ @@ -108561,6 +109491,7 @@ SQLITE_PRIVATE void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, in */ if( IsVirtual(pTab) ){ sqlite3VdbeAddOp4(v, OP_VDestroy, iDb, 0, 0, pTab->zName, 0); + sqlite3MayAbort(pParse); } sqlite3VdbeAddOp4(v, OP_DropTable, iDb, 0, 0, pTab->zName, 0); sqlite3ChangeCookie(pParse, iDb); @@ -109065,13 +109996,13 @@ SQLITE_PRIVATE void sqlite3CreateIndex( assert( pParse->nErr==0 ); if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 && db->init.busy==0 + && pTblName!=0 #if SQLITE_USER_AUTHENTICATION && sqlite3UserAuthTable(pTab->zName)==0 #endif #ifdef SQLITE_ALLOW_SQLITE_MASTER_INDEX && sqlite3StrICmp(&pTab->zName[7],"master")!=0 #endif - && sqlite3StrNICmp(&pTab->zName[7],"altertab_",9)!=0 ){ sqlite3ErrorMsg(pParse, "table %s may not be indexed", pTab->zName); goto exit_create_index; @@ -109175,6 +110106,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex( sqlite3ExprListSetSortOrder(pList, sortOrder); }else{ sqlite3ExprListCheckLength(pParse, pList, "index"); + if( pParse->nErr ) goto exit_create_index; } /* Figure out how many bytes of space are required to store explicitly @@ -109193,6 +110125,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex( */ nName = sqlite3Strlen30(zName); nExtraCol = pPk ? pPk->nKeyCol : 1; + assert( pList->nExpr + nExtraCol <= 32767 /* Fits in i16 */ ); pIndex = sqlite3AllocateIndexObject(db, pList->nExpr + nExtraCol, nName + nExtra + 1, &zExtra); if( db->mallocFailed ){ @@ -109389,6 +110322,11 @@ SQLITE_PRIVATE void sqlite3CreateIndex( } } if( idxType==SQLITE_IDXTYPE_PRIMARYKEY ) pIdx->idxType = idxType; + if( IN_RENAME_OBJECT ){ + pIndex->pNext = pParse->pNewIndex; + pParse->pNewIndex = pIndex; + pIndex = 0; + } goto exit_create_index; } } @@ -109404,6 +110342,14 @@ SQLITE_PRIVATE void sqlite3CreateIndex( Index *p; assert( !IN_SPECIAL_PARSE ); assert( sqlite3SchemaMutexHeld(db, 0, pIndex->pSchema) ); + if( pTblName!=0 ){ + pIndex->tnum = db->init.newTnum; + if( sqlite3IndexHasDuplicateRootPage(pIndex) ){ + sqlite3ErrorMsg(pParse, "invalid rootpage"); + pParse->rc = SQLITE_CORRUPT_BKPT; + goto exit_create_index; + } + } p = sqlite3HashInsert(&pIndex->pSchema->idxHash, pIndex->zName, pIndex); if( p ){ @@ -109412,9 +110358,6 @@ SQLITE_PRIVATE void sqlite3CreateIndex( goto exit_create_index; } db->mDbFlags |= DBFLAG_SchemaChange; - if( pTblName!=0 ){ - pIndex->tnum = db->init.newTnum; - } } /* If this is the initial CREATE INDEX statement (or CREATE TABLE if the @@ -109666,9 +110609,9 @@ SQLITE_PRIVATE void *sqlite3ArrayAllocate( int *pIdx /* Write the index of a new slot here */ ){ char *z; - int n = *pnEntry; + sqlite3_int64 n = *pIdx = *pnEntry; if( (n & (n-1))==0 ){ - int sz = (n==0) ? 1 : 2*n; + sqlite3_int64 sz = (n==0) ? 1 : 2*n; void *pNew = sqlite3DbRealloc(db, pArray, sz*szEntry); if( pNew==0 ){ *pIdx = -1; @@ -109678,7 +110621,6 @@ SQLITE_PRIVATE void *sqlite3ArrayAllocate( } z = (char*)pArray; memset(&z[n * szEntry], 0, szEntry); - *pIdx = n; ++*pnEntry; return pArray; } @@ -109740,6 +110682,18 @@ SQLITE_PRIVATE int sqlite3IdListIndex(IdList *pList, const char *zName){ return -1; } +/* +** Maximum size of a SrcList object. +** The SrcList object is used to represent the FROM clause of a +** SELECT statement, and the query planner cannot deal with more +** than 64 tables in a join. So any value larger than 64 here +** is sufficient for most uses. Smaller values, like say 10, are +** appropriate for small and memory-limited applications. +*/ +#ifndef SQLITE_MAX_SRCLIST +# define SQLITE_MAX_SRCLIST 200 +#endif + /* ** Expand the space allocated for the given SrcList object by ** creating nExtra new slots beginning at iStart. iStart is zero based. @@ -109756,11 +110710,12 @@ SQLITE_PRIVATE int sqlite3IdListIndex(IdList *pList, const char *zName){ ** the iStart value would be 0. The result then would ** be: nil, nil, nil, A, B. ** -** If a memory allocation fails the SrcList is unchanged. The -** db->mallocFailed flag will be set to true. +** If a memory allocation fails or the SrcList becomes too large, leave +** the original SrcList unchanged, return NULL, and leave an error message +** in pParse. */ SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge( - sqlite3 *db, /* Database connection to notify of OOM errors */ + Parse *pParse, /* Parsing context into which errors are reported */ SrcList *pSrc, /* The SrcList to be enlarged */ int nExtra, /* Number of new slots to add to pSrc->a[] */ int iStart /* Index in pSrc->a[] of first new slot */ @@ -109776,17 +110731,23 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge( /* Allocate additional space if needed */ if( (u32)pSrc->nSrc+nExtra>pSrc->nAlloc ){ SrcList *pNew; - int nAlloc = pSrc->nSrc*2+nExtra; - int nGot; + sqlite3_int64 nAlloc = 2*(sqlite3_int64)pSrc->nSrc+nExtra; + sqlite3 *db = pParse->db; + + if( pSrc->nSrc+nExtra>=SQLITE_MAX_SRCLIST ){ + sqlite3ErrorMsg(pParse, "too many FROM clause terms, max: %d", + SQLITE_MAX_SRCLIST); + return 0; + } + if( nAlloc>SQLITE_MAX_SRCLIST ) nAlloc = SQLITE_MAX_SRCLIST; pNew = sqlite3DbRealloc(db, pSrc, sizeof(*pSrc) + (nAlloc-1)*sizeof(pSrc->a[0]) ); if( pNew==0 ){ assert( db->mallocFailed ); - return pSrc; + return 0; } pSrc = pNew; - nGot = (sqlite3DbMallocSize(db, pNew) - sizeof(*pSrc))/sizeof(pSrc->a[0])+1; - pSrc->nAlloc = nGot; + pSrc->nAlloc = nAlloc; } /* Move existing slots that come after the newly inserted slots @@ -109811,7 +110772,8 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge( ** Append a new table name to the given SrcList. Create a new SrcList if ** need be. A new entry is created in the SrcList even if pTable is NULL. ** -** A SrcList is returned, or NULL if there is an OOM error. The returned +** A SrcList is returned, or NULL if there is an OOM error or if the +** SrcList grows to large. The returned ** SrcList might be the same as the SrcList that was input or it might be ** a new one. If an OOM error does occurs, then the prior value of pList ** that is input to this routine is automatically freed. @@ -109842,27 +110804,32 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge( ** before being added to the SrcList. */ SQLITE_PRIVATE SrcList *sqlite3SrcListAppend( - sqlite3 *db, /* Connection to notify of malloc failures */ + Parse *pParse, /* Parsing context, in which errors are reported */ SrcList *pList, /* Append to this SrcList. NULL creates a new SrcList */ Token *pTable, /* Table to append */ Token *pDatabase /* Database of the table */ ){ struct SrcList_item *pItem; + sqlite3 *db; assert( pDatabase==0 || pTable!=0 ); /* Cannot have C without B */ - assert( db!=0 ); + assert( pParse!=0 ); + assert( pParse->db!=0 ); + db = pParse->db; if( pList==0 ){ - pList = sqlite3DbMallocRawNN(db, sizeof(SrcList) ); + pList = sqlite3DbMallocRawNN(pParse->db, sizeof(SrcList) ); if( pList==0 ) return 0; pList->nAlloc = 1; pList->nSrc = 1; memset(&pList->a[0], 0, sizeof(pList->a[0])); pList->a[0].iCursor = -1; }else{ - pList = sqlite3SrcListEnlarge(db, pList, 1, pList->nSrc); - } - if( db->mallocFailed ){ - sqlite3SrcListDelete(db, pList); - return 0; + SrcList *pNew = sqlite3SrcListEnlarge(pParse, pList, 1, pList->nSrc); + if( pNew==0 ){ + sqlite3SrcListDelete(db, pList); + return 0; + }else{ + pList = pNew; + } } pItem = &pList->a[pList->nSrc-1]; if( pDatabase && pDatabase->z==0 ){ @@ -109951,7 +110918,7 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListAppendFromTerm( ); goto append_from_error; } - p = sqlite3SrcListAppend(db, p, pTable, pDatabase); + p = sqlite3SrcListAppend(pParse, p, pTable, pDatabase); if( p==0 ){ goto append_from_error; } @@ -110271,7 +111238,8 @@ SQLITE_PRIVATE void sqlite3UniqueConstraint( StrAccum errMsg; Table *pTab = pIdx->pTable; - sqlite3StrAccumInit(&errMsg, pParse->db, 0, 0, 200); + sqlite3StrAccumInit(&errMsg, pParse->db, 0, 0, + pParse->db->aLimit[SQLITE_LIMIT_LENGTH]); if( pIdx->aColExpr ){ sqlite3_str_appendf(&errMsg, "index '%q'", pIdx->zName); }else{ @@ -110340,13 +111308,15 @@ static int collationMatch(const char *zColl, Index *pIndex){ */ #ifndef SQLITE_OMIT_REINDEX static void reindexTable(Parse *pParse, Table *pTab, char const *zColl){ - Index *pIndex; /* An index associated with pTab */ + if( !IsVirtual(pTab) ){ + Index *pIndex; /* An index associated with pTab */ - for(pIndex=pTab->pIndex; pIndex; pIndex=pIndex->pNext){ - if( zColl==0 || collationMatch(zColl, pIndex) ){ - int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); - sqlite3BeginWriteOperation(pParse, 0, iDb); - sqlite3RefillIndex(pParse, pIndex, -1); + for(pIndex=pTab->pIndex; pIndex; pIndex=pIndex->pNext){ + if( zColl==0 || collationMatch(zColl, pIndex) ){ + int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); + sqlite3BeginWriteOperation(pParse, 0, iDb); + sqlite3RefillIndex(pParse, pIndex, -1); + } } } } @@ -110518,7 +111488,7 @@ SQLITE_PRIVATE With *sqlite3WithAdd( } if( pWith ){ - int nByte = sizeof(*pWith) + (sizeof(pWith->a[1]) * pWith->nCte); + sqlite3_int64 nByte = sizeof(*pWith) + (sizeof(pWith->a[1]) * pWith->nCte); pNew = sqlite3DbRealloc(db, pWith, nByte); }else{ pNew = sqlite3DbMallocZero(db, sizeof(*pWith)); @@ -110845,7 +111815,7 @@ static int matchQuality( ** Search a FuncDefHash for a function with the given name. Return ** a pointer to the matching FuncDef if found, or 0 if there is no match. */ -static FuncDef *functionSearch( +SQLITE_PRIVATE FuncDef *sqlite3FunctionSearch( int h, /* Hash of the name */ const char *zFunc /* Name of function */ ){ @@ -110857,21 +111827,6 @@ static FuncDef *functionSearch( } return 0; } -#ifdef SQLITE_ENABLE_NORMALIZE -SQLITE_PRIVATE FuncDef *sqlite3FunctionSearchN( - int h, /* Hash of the name */ - const char *zFunc, /* Name of function */ - int nFunc /* Length of the name */ -){ - FuncDef *p; - for(p=sqlite3BuiltinFunctions.a[h]; p; p=p->u.pHash){ - if( sqlite3StrNICmp(p->zName, zFunc, nFunc)==0 ){ - return p; - } - } - return 0; -} -#endif /* SQLITE_ENABLE_NORMALIZE */ /* ** Insert a new FuncDef into a FuncDefHash hash table. @@ -110887,7 +111842,7 @@ SQLITE_PRIVATE void sqlite3InsertBuiltinFuncs( int nName = sqlite3Strlen30(zName); int h = SQLITE_FUNC_HASH(zName[0], nName); assert( zName[0]>='a' && zName[0]<='z' ); - pOther = functionSearch(h, zName); + pOther = sqlite3FunctionSearch(h, zName); if( pOther ){ assert( pOther!=&aDef[i] && pOther->pNext!=&aDef[i] ); aDef[i].pNext = pOther->pNext; @@ -110965,7 +111920,7 @@ SQLITE_PRIVATE FuncDef *sqlite3FindFunction( if( !createFlag && (pBest==0 || (db->mDbFlags & DBFLAG_PreferBuiltin)!=0) ){ bestScore = 0; h = SQLITE_FUNC_HASH(sqlite3UpperToLower[(u8)zName[0]], nName); - p = functionSearch(h, zName); + p = sqlite3FunctionSearch(h, zName); while( p ){ int score = matchQuality(p, nArg, enc); if( score>bestScore ){ @@ -111185,7 +112140,7 @@ SQLITE_PRIVATE void sqlite3MaterializeView( sqlite3 *db = pParse->db; int iDb = sqlite3SchemaToIndex(db, pView->pSchema); pWhere = sqlite3ExprDup(db, pWhere, 0); - pFrom = sqlite3SrcListAppend(db, 0, 0, 0); + pFrom = sqlite3SrcListAppend(pParse, 0, 0, 0); if( pFrom ){ assert( pFrom->nSrc==1 ); pFrom->a[0].zName = sqlite3DbStrDup(db, pView->zName); @@ -111585,7 +112540,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( /* If this DELETE cannot use the ONEPASS strategy, this is the ** end of the WHERE loop */ if( eOnePass!=ONEPASS_OFF ){ - addrBypass = sqlite3VdbeMakeLabel(v); + addrBypass = sqlite3VdbeMakeLabel(pParse); }else{ sqlite3WhereEnd(pWInfo); } @@ -111774,7 +112729,7 @@ SQLITE_PRIVATE void sqlite3GenerateRowDelete( /* Seek cursor iCur to the row to delete. If this row no longer exists ** (this can happen if a trigger program has already deleted it), do ** not attempt to delete it or fire any DELETE triggers. */ - iLabel = sqlite3VdbeMakeLabel(v); + iLabel = sqlite3VdbeMakeLabel(pParse); opSeek = HasRowid(pTab) ? OP_NotExists : OP_NotFound; if( eMode==ONEPASS_OFF ){ sqlite3VdbeAddOp4Int(v, opSeek, iDataCur, iLabel, iPk, nPk); @@ -111980,7 +112935,7 @@ SQLITE_PRIVATE int sqlite3GenerateIndexKey( if( piPartIdxLabel ){ if( pIdx->pPartIdxWhere ){ - *piPartIdxLabel = sqlite3VdbeMakeLabel(v); + *piPartIdxLabel = sqlite3VdbeMakeLabel(pParse); pParse->iSelfTab = iDataCur + 1; sqlite3ExprIfFalseDup(pParse, pIdx->pPartIdxWhere, *piPartIdxLabel, SQLITE_JUMPIFNULL); @@ -112236,6 +113191,7 @@ static void instrFunc( int typeHaystack, typeNeedle; int N = 1; int isText; + unsigned char firstChar; UNUSED_PARAMETER(argc); typeHaystack = sqlite3_value_type(argv[0]); @@ -112254,7 +113210,10 @@ static void instrFunc( isText = 1; } if( zNeedle==0 || (nHaystack && zHaystack==0) ) return; - while( nNeedle<=nHaystack && memcmp(zHaystack, zNeedle, nNeedle)!=0 ){ + firstChar = zNeedle[0]; + while( nNeedle<=nHaystack + && (zHaystack[0]!=firstChar || memcmp(zHaystack, zNeedle, nNeedle)!=0) + ){ N++; do{ nHaystack--; @@ -112545,11 +113504,11 @@ static void randomBlob( int argc, sqlite3_value **argv ){ - int n; + sqlite3_int64 n; unsigned char *p; assert( argc==1 ); UNUSED_PARAMETER(argc); - n = sqlite3_value_int(argv[0]); + n = sqlite3_value_int64(argv[0]); if( n<1 ){ n = 1; } @@ -113837,6 +114796,10 @@ static void setLikeOptFlag(sqlite3 *db, const char *zName, u8 flagVal){ if( ALWAYS(pDef) ){ pDef->funcFlags |= flagVal; } + pDef = sqlite3FindFunction(db, zName, 3, SQLITE_UTF8, 0); + if( pDef ){ + pDef->funcFlags |= flagVal; + } } /* @@ -114385,7 +115348,7 @@ static void fkLookupParent( int i; /* Iterator variable */ Vdbe *v = sqlite3GetVdbe(pParse); /* Vdbe to add code to */ int iCur = pParse->nTab - 1; /* Cursor number to use */ - int iOk = sqlite3VdbeMakeLabel(v); /* jump here if parent key found */ + int iOk = sqlite3VdbeMakeLabel(pParse); /* jump here if parent key found */ sqlite3VdbeVerifyAbortable(v, (!pFKey->isDeferred @@ -114658,8 +115621,11 @@ static void fkScanChildren( ** NOT( $current_a==a AND $current_b==b AND ... ) ** ** The first form is used for rowid tables. The second form is used - ** for WITHOUT ROWID tables. In the second form, the primary key is - ** (a,b,...) + ** for WITHOUT ROWID tables. In the second form, the *parent* key is + ** (a,b,...). Either the parent or primary key could be used to + ** uniquely identify the current row, but the parent key is more convenient + ** as the required values have already been loaded into registers + ** by the caller. */ if( pTab==pFKey->pFrom && nIncr>0 ){ Expr *pNe; /* Expression (pLeft != pRight) */ @@ -114671,14 +115637,13 @@ static void fkScanChildren( pNe = sqlite3PExpr(pParse, TK_NE, pLeft, pRight); }else{ Expr *pEq, *pAll = 0; - Index *pPk = sqlite3PrimaryKeyIndex(pTab); assert( pIdx!=0 ); - for(i=0; inKeyCol; i++){ + for(i=0; inKeyCol; i++){ i16 iCol = pIdx->aiColumn[i]; assert( iCol>=0 ); pLeft = exprTableRegister(pParse, pTab, regData, iCol); - pRight = exprTableColumn(db, pTab, pSrc->a[0].iCursor, iCol); - pEq = sqlite3PExpr(pParse, TK_EQ, pLeft, pRight); + pRight = sqlite3Expr(db, TK_ID, pTab->aCol[iCol].zName); + pEq = sqlite3PExpr(pParse, TK_IS, pLeft, pRight); pAll = sqlite3ExprAnd(db, pAll, pEq); } pNe = sqlite3PExpr(pParse, TK_NOT, pAll, 0); @@ -114783,7 +115748,7 @@ SQLITE_PRIVATE void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTa if( p->isDeferred || (db->flags & SQLITE_DeferFKs) ) break; } if( !p ) return; - iSkip = sqlite3VdbeMakeLabel(v); + iSkip = sqlite3VdbeMakeLabel(pParse); sqlite3VdbeAddOp2(v, OP_FkIfZero, 1, iSkip); VdbeCoverage(v); } @@ -115068,7 +116033,7 @@ SQLITE_PRIVATE void sqlite3FkCheck( /* Create a SrcList structure containing the child table. We need the ** child table as a SrcList for sqlite3WhereBegin() */ - pSrc = sqlite3SrcListAppend(db, 0, 0, 0); + pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0); if( pSrc ){ struct SrcList_item *pItem = pSrc->a; pItem->pTab = pFKey->pFrom; @@ -115345,7 +116310,7 @@ static Trigger *fkActionTrigger( } pSelect = sqlite3SelectNew(pParse, sqlite3ExprListAppend(pParse, 0, pRaise), - sqlite3SrcListAppend(db, 0, &tFrom, 0), + sqlite3SrcListAppend(pParse, 0, &tFrom, 0), pWhere, 0, 0, 0, 0, 0 ); @@ -115807,6 +116772,7 @@ SQLITE_PRIVATE void sqlite3AutoincrementBegin(Parse *pParse){ aOp[7].p2 = memId+2; aOp[7].p1 = memId; aOp[10].p2 = memId; + if( pParse->nTab==0 ) pParse->nTab = 1; } } @@ -116313,6 +117279,11 @@ SQLITE_PRIVATE void sqlite3Insert( } #ifndef SQLITE_OMIT_UPSERT if( pUpsert ){ + if( IsVirtual(pTab) ){ + sqlite3ErrorMsg(pParse, "UPSERT not implemented for virtual table \"%s\"", + pTab->zName); + goto insert_cleanup; + } pTabList->a[0].iCursor = iDataCur; pUpsert->pUpsertSrc = pTabList; pUpsert->regData = regData; @@ -116353,7 +117324,7 @@ SQLITE_PRIVATE void sqlite3Insert( /* Run the BEFORE and INSTEAD OF triggers, if there are any */ - endOfLoop = sqlite3VdbeMakeLabel(v); + endOfLoop = sqlite3VdbeMakeLabel(pParse); if( tmask & TRIGGER_BEFORE ){ int regCols = sqlite3GetTempRange(pParse, pTab->nCol+1); @@ -116435,16 +117406,12 @@ SQLITE_PRIVATE void sqlite3Insert( }else if( pSelect ){ sqlite3VdbeAddOp2(v, OP_Copy, regFromSelect+ipkColumn, regRowid); }else{ - VdbeOp *pOp; - sqlite3ExprCode(pParse, pList->a[ipkColumn].pExpr, regRowid); - pOp = sqlite3VdbeGetOp(v, -1); - assert( pOp!=0 ); - if( pOp->opcode==OP_Null && !IsVirtual(pTab) ){ + Expr *pIpk = pList->a[ipkColumn].pExpr; + if( pIpk->op==TK_NULL && !IsVirtual(pTab) ){ + sqlite3VdbeAddOp3(v, OP_NewRowid, iDataCur, regRowid, regAutoinc); appendFlag = 1; - pOp->opcode = OP_NewRowid; - pOp->p1 = iDataCur; - pOp->p2 = regRowid; - pOp->p3 = regAutoinc; + }else{ + sqlite3ExprCode(pParse, pList->a[ipkColumn].pExpr, regRowid); } } /* If the PRIMARY KEY expression is NULL, then use OP_NewRowid @@ -116839,7 +117806,20 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( } assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail || onError==OE_Ignore || onError==OE_Replace ); + addr1 = 0; switch( onError ){ + case OE_Replace: { + assert( onError==OE_Replace ); + addr1 = sqlite3VdbeMakeLabel(pParse); + sqlite3VdbeAddOp2(v, OP_NotNull, regNewData+1+i, addr1); + VdbeCoverage(v); + sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regNewData+1+i); + sqlite3VdbeAddOp2(v, OP_NotNull, regNewData+1+i, addr1); + VdbeCoverage(v); + onError = OE_Abort; + /* Fall through into the OE_Abort case to generate code that runs + ** if both the input and the default value are NULL */ + } case OE_Abort: sqlite3MayAbort(pParse); /* Fall through */ @@ -116852,21 +117832,15 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( sqlite3VdbeAppendP4(v, zMsg, P4_DYNAMIC); sqlite3VdbeChangeP5(v, P5_ConstraintNotNull); VdbeCoverage(v); + if( addr1 ) sqlite3VdbeResolveLabel(v, addr1); break; } - case OE_Ignore: { + default: { + assert( onError==OE_Ignore ); sqlite3VdbeAddOp2(v, OP_IsNull, regNewData+1+i, ignoreDest); VdbeCoverage(v); break; } - default: { - assert( onError==OE_Replace ); - addr1 = sqlite3VdbeAddOp1(v, OP_NotNull, regNewData+1+i); - VdbeCoverage(v); - sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regNewData+1+i); - sqlite3VdbeJumpHere(v, addr1); - break; - } } } @@ -116887,7 +117861,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( ** updated so there is no point it verifying the check constraint */ continue; } - allOk = sqlite3VdbeMakeLabel(v); + allOk = sqlite3VdbeMakeLabel(pParse); sqlite3VdbeVerifyAbortable(v, onError); sqlite3ExprIfTrue(pParse, pExpr, allOk, SQLITE_JUMPIFNULL); if( onError==OE_Ignore ){ @@ -116954,7 +117928,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( ** exist in the table. */ if( pkChng && pPk==0 ){ - int addrRowidOk = sqlite3VdbeMakeLabel(v); + int addrRowidOk = sqlite3VdbeMakeLabel(pParse); /* Figure out what action to take in case of a rowid collision */ onError = pTab->keyConf; @@ -117104,7 +118078,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( VdbeComment((v, "Skip upsert subroutine")); sqlite3VdbeJumpHere(v, upsertJump); }else{ - addrUniqueOk = sqlite3VdbeMakeLabel(v); + addrUniqueOk = sqlite3VdbeMakeLabel(pParse); } if( bAffinityDone==0 && (pUpIdx==0 || pUpIdx==pIdx) ){ sqlite3TableAffinity(v, pTab, regNewData+1); @@ -117148,7 +118122,9 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn, aRegIdx[ix]); VdbeComment((v, "for %s", pIdx->zName)); #ifdef SQLITE_ENABLE_NULL_TRIM - if( pIdx->idxType==2 ) sqlite3SetMakeRecordP5(v, pIdx->pTable); + if( pIdx->idxType==SQLITE_IDXTYPE_PRIMARYKEY ){ + sqlite3SetMakeRecordP5(v, pIdx->pTable); + } #endif /* In an UPDATE operation, if this index is the PRIMARY KEY index @@ -117187,7 +118163,11 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( ** (3) There are no secondary indexes on the table ** (4) No delete triggers need to be fired if there is a conflict ** (5) No FK constraint counters need to be updated if a conflict occurs. - */ + ** + ** This is not possible for ENABLE_PREUPDATE_HOOK builds, as the row + ** must be explicitly deleted in order to ensure any pre-update hook + ** is invoked. */ +#ifndef SQLITE_ENABLE_PREUPDATE_HOOK if( (ix==0 && pIdx->pNext==0) /* Condition 3 */ && pPk==pIdx /* Condition 2 */ && onError==OE_Replace /* Condition 1 */ @@ -117199,6 +118179,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( sqlite3VdbeResolveLabel(v, addrUniqueOk); continue; } +#endif /* ifndef SQLITE_ENABLE_PREUPDATE_HOOK */ /* Check to see if the new index entry will be unique */ sqlite3VdbeVerifyAbortable(v, onError); @@ -117312,7 +118293,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( /* If the IPK constraint is a REPLACE, run it last */ if( ipkTop ){ - sqlite3VdbeGoto(v, ipkTop+1); + sqlite3VdbeGoto(v, ipkTop); VdbeComment((v, "Do IPK REPLACE")); sqlite3VdbeJumpHere(v, ipkBottom); } @@ -117393,10 +118374,13 @@ SQLITE_PRIVATE void sqlite3CompleteInsertion( pik_flags |= (update_flags & OPFLAG_SAVEPOSITION); #ifdef SQLITE_ENABLE_PREUPDATE_HOOK if( update_flags==0 ){ - sqlite3VdbeAddOp4(v, OP_InsertInt, - iIdxCur+i, aRegIdx[i], 0, (char*)pTab, P4_TABLE + int r = sqlite3GetTempReg(pParse); + sqlite3VdbeAddOp2(v, OP_Integer, 0, r); + sqlite3VdbeAddOp4(v, OP_Insert, + iIdxCur+i, aRegIdx[i], r, (char*)pTab, P4_TABLE ); sqlite3VdbeChangeP5(v, OPFLAG_ISNOOP); + sqlite3ReleaseTempReg(pParse, r); } #endif } @@ -117682,7 +118666,8 @@ static int xferOptimization( if( pSrc==0 ){ return 0; /* FROM clause does not contain a real table */ } - if( pSrc==pDest ){ + if( pSrc->tnum==pDest->tnum && pSrc->pSchema==pDest->pSchema ){ + testcase( pSrc!=pDest ); /* Possible due to bad sqlite_master.rootpage */ return 0; /* tab1 and tab2 may not be the same table */ } if( HasRowid(pDest)!=HasRowid(pSrc) ){ @@ -117743,6 +118728,13 @@ static int xferOptimization( if( pSrcIdx==0 ){ return 0; /* pDestIdx has no corresponding index in pSrc */ } + if( pSrcIdx->tnum==pDestIdx->tnum && pSrc->pSchema==pDest->pSchema + && sqlite3FaultSim(411)==SQLITE_OK ){ + /* The sqlite3FaultSim() call allows this corruption test to be + ** bypassed during testing, in order to exercise other corruption tests + ** further downstream. */ + return 0; /* Corrupt schema - two indexes on the same btree */ + } } #ifndef SQLITE_OMIT_CHECK if( pDest->pCheck && sqlite3ExprListCompare(pSrc->pCheck,pDest->pCheck,-1) ){ @@ -117820,7 +118812,7 @@ static int xferOptimization( sqlite3RowidConstraint(pParse, onError, pDest); sqlite3VdbeJumpHere(v, addr2); autoIncStep(pParse, regAutoinc, regRowid); - }else if( pDest->pIndex==0 ){ + }else if( pDest->pIndex==0 && !(db->mDbFlags & DBFLAG_VacuumInto) ){ addr1 = sqlite3VdbeAddOp2(v, OP_NewRowid, iDest, regRowid); }else{ addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid); @@ -117883,7 +118875,7 @@ static int xferOptimization( sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest); } } - if( !HasRowid(pSrc) && pDestIdx->idxType==2 ){ + if( !HasRowid(pSrc) && pDestIdx->idxType==SQLITE_IDXTYPE_PRIMARYKEY ){ idxInsFlags |= OPFLAG_NCHANGE; } sqlite3VdbeAddOp2(v, OP_IdxInsert, iDest, regData); @@ -117958,7 +118950,7 @@ SQLITE_API int sqlite3_exec( sqlite3_mutex_enter(db->mutex); sqlite3Error(db, SQLITE_OK); while( rc==SQLITE_OK && zSql[0] ){ - int nCol; + int nCol = 0; char **azVals = 0; pStmt = 0; @@ -117972,9 +118964,7 @@ SQLITE_API int sqlite3_exec( zSql = zLeftover; continue; } - callbackIsInit = 0; - nCol = sqlite3_column_count(pStmt); while( 1 ){ int i; @@ -117985,6 +118975,7 @@ SQLITE_API int sqlite3_exec( (SQLITE_DONE==rc && !callbackIsInit && db->flags&SQLITE_NullCallback)) ){ if( !callbackIsInit ){ + nCol = sqlite3_column_count(pStmt); azCols = sqlite3DbMallocRaw(db, (2*nCol+1)*sizeof(const char*)); if( azCols==0 ){ goto exec_out; @@ -118396,6 +119387,9 @@ struct sqlite3_api_routines { void(*xDestroy)(void*)); /* Version 3.26.0 and later */ const char *(*normalized_sql)(sqlite3_stmt*); + /* Version 3.28.0 and later */ + int (*stmt_isexplain)(sqlite3_stmt*); + int (*value_frombind)(sqlite3_value*); }; /* @@ -118685,6 +119679,9 @@ typedef int (*sqlite3_loadext_entry)( #define sqlite3_create_window_function sqlite3_api->create_window_function /* Version 3.26.0 and later */ #define sqlite3_normalized_sql sqlite3_api->normalized_sql +/* Version 3.28.0 and later */ +#define sqlite3_stmt_isexplain sqlite3_api->isexplain +#define sqlite3_value_frombind sqlite3_api->frombind #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) @@ -119144,10 +120141,13 @@ static const sqlite3_api_routines sqlite3Apis = { sqlite3_create_window_function, /* Version 3.26.0 and later */ #ifdef SQLITE_ENABLE_NORMALIZE - sqlite3_normalized_sql + sqlite3_normalized_sql, #else - 0 + 0, #endif + /* Version 3.28.0 and later */ + sqlite3_stmt_isexplain, + sqlite3_value_frombind }; /* @@ -119339,7 +120339,7 @@ SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff){ if( onoff ){ db->flags |= SQLITE_LoadExtension|SQLITE_LoadExtFunc; }else{ - db->flags &= ~(SQLITE_LoadExtension|SQLITE_LoadExtFunc); + db->flags &= ~(u64)(SQLITE_LoadExtension|SQLITE_LoadExtFunc); } sqlite3_mutex_leave(db->mutex); return SQLITE_OK; @@ -119598,8 +120598,7 @@ SQLITE_PRIVATE void sqlite3AutoLoadExtensions(sqlite3 *db){ #define PragTyp_HEXKEY 41 #define PragTyp_KEY 42 #define PragTyp_LOCK_STATUS 43 -#define PragTyp_PARSER_TRACE 44 -#define PragTyp_STATS 45 +#define PragTyp_STATS 44 /* Property flags associated with various pragma. */ #define PragFlg_NeedSchema 0x01 /* Force schema load before running */ @@ -120010,12 +121009,14 @@ static const PragmaName aPragmaName[] = { /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif -#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_PARSER_TRACE) +#if !defined(SQLITE_OMIT_FLAG_PRAGMAS) +#if defined(SQLITE_DEBUG) {/* zName: */ "parser_trace", - /* ePragTyp: */ PragTyp_PARSER_TRACE, - /* ePragFlg: */ 0, + /* ePragTyp: */ PragTyp_FLAG, + /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, - /* iArg: */ 0 }, + /* iArg: */ SQLITE_ParserTrace }, +#endif #endif #if defined(SQLITE_INTROSPECTION_PRAGMAS) {/* zName: */ "pragma_list", @@ -121006,7 +122007,7 @@ SQLITE_PRIVATE void sqlite3Pragma( if( sqlite3GetBoolean(zRight, size!=0) ){ db->flags |= SQLITE_CacheSpill; }else{ - db->flags &= ~SQLITE_CacheSpill; + db->flags &= ~(u64)SQLITE_CacheSpill; } setAllPagerFlags(db); } @@ -121566,7 +122567,7 @@ SQLITE_PRIVATE void sqlite3Pragma( x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, &aiCols); assert( x==0 ); } - addrOk = sqlite3VdbeMakeLabel(v); + addrOk = sqlite3VdbeMakeLabel(pParse); /* Generate code to read the child key values into registers ** regRow..regRow+n. If any of the child key values are NULL, this @@ -121611,19 +122612,6 @@ SQLITE_PRIVATE void sqlite3Pragma( #endif /* !defined(SQLITE_OMIT_TRIGGER) */ #endif /* !defined(SQLITE_OMIT_FOREIGN_KEY) */ -#ifndef NDEBUG - case PragTyp_PARSER_TRACE: { - if( zRight ){ - if( sqlite3GetBoolean(zRight, 0) ){ - sqlite3ParserTrace(stdout, "parser: "); - }else{ - sqlite3ParserTrace(0, 0); - } - } - } - break; -#endif - /* Reinstall the LIKE and GLOB functions. The variant of LIKE ** used will be case sensitive or not depending on the RHS. */ @@ -121786,8 +122774,8 @@ SQLITE_PRIVATE void sqlite3Pragma( if( pTab->pCheck && (db->flags & SQLITE_IgnoreChecks)==0 ){ ExprList *pCheck = sqlite3ExprListDup(db, pTab->pCheck, 0); if( db->mallocFailed==0 ){ - int addrCkFault = sqlite3VdbeMakeLabel(v); - int addrCkOk = sqlite3VdbeMakeLabel(v); + int addrCkFault = sqlite3VdbeMakeLabel(pParse); + int addrCkOk = sqlite3VdbeMakeLabel(pParse); char *zErr; int k; pParse->iSelfTab = iDataCur + 1; @@ -121810,7 +122798,7 @@ SQLITE_PRIVATE void sqlite3Pragma( /* Validate index entries for the current row */ for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ int jmp2, jmp3, jmp4, jmp5; - int ckUniq = sqlite3VdbeMakeLabel(v); + int ckUniq = sqlite3VdbeMakeLabel(pParse); if( pPk==pIdx ) continue; r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 0, &jmp3, pPrior, r1); @@ -121831,7 +122819,7 @@ SQLITE_PRIVATE void sqlite3Pragma( ** current key. The entry is unique if (1) any column is NULL ** or (2) the next entry has a different key */ if( IsUniqueIndex(pIdx) ){ - int uniqOk = sqlite3VdbeMakeLabel(v); + int uniqOk = sqlite3VdbeMakeLabel(pParse); int jmp6; int kk; for(kk=0; kknKeyCol; kk++){ @@ -122745,6 +123733,19 @@ static void corruptSchema( } } +/* +** Check to see if any sibling index (another index on the same table) +** of pIndex has the same root page number, and if it does, return true. +** This would indicate a corrupt schema. +*/ +SQLITE_PRIVATE int sqlite3IndexHasDuplicateRootPage(Index *pIndex){ + Index *p; + for(p=pIndex->pTable->pIndex; p; p=p->pNext){ + if( p->tnum==pIndex->tnum && p!=pIndex ) return 1; + } + return 0; +} + /* ** This is the callback routine for the code that initializes the ** database. See sqlite3Init() below for additional information. @@ -122766,6 +123767,7 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char UNUSED_PARAMETER2(NotUsed, argc); assert( sqlite3_mutex_held(db->mutex) ); DbClearProperty(db, iDb, DB_Empty); + pData->nInitRow++; if( db->mallocFailed ){ corruptSchema(pData, argv[0], 0); return 1; @@ -122819,15 +123821,12 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char */ Index *pIndex; pIndex = sqlite3FindIndex(db, argv[0], db->aDb[iDb].zDbSName); - if( pIndex==0 ){ - /* This can occur if there exists an index on a TEMP table which - ** has the same name as another index on a permanent index. Since - ** the permanent table is hidden by the TEMP table, we can also - ** safely ignore the index on the permanent table. - */ - /* Do Nothing */; - }else if( sqlite3GetInt32(argv[1], &pIndex->tnum)==0 ){ - corruptSchema(pData, argv[0], "invalid rootpage"); + if( pIndex==0 + || sqlite3GetInt32(argv[1],&pIndex->tnum)==0 + || pIndex->tnum<2 + || sqlite3IndexHasDuplicateRootPage(pIndex) + ){ + corruptSchema(pData, argv[0], pIndex?"invalid rootpage":"orphan index"); } } return 0; @@ -122877,6 +123876,7 @@ SQLITE_PRIVATE int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFl initData.rc = SQLITE_OK; initData.pzErrMsg = pzErrMsg; initData.mInitFlags = mFlags; + initData.nInitRow = 0; sqlite3InitCallback(&initData, 3, (char **)azArg, 0); if( initData.rc ){ rc = initData.rc; @@ -122994,7 +123994,7 @@ SQLITE_PRIVATE int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFl ** indices that the user might have created. */ if( iDb==0 && meta[BTREE_FILE_FORMAT-1]>=4 ){ - db->flags &= ~SQLITE_LegacyFileFmt; + db->flags &= ~(u64)SQLITE_LegacyFileFmt; } /* Read the schema information out of the schema tables @@ -123246,6 +124246,7 @@ static int sqlite3Prepare( sParse.disableLookaside++; db->lookaside.bDisable++; } + sParse.disableVtab = (prepFlags & SQLITE_PREPARE_NO_VTAB)!=0; /* Check to verify that it is possible to get a read lock on all ** database schemas. The inability to get a read lock indicates that @@ -123410,293 +124411,6 @@ static int sqlite3LockAndPrepare( return rc; } -#ifdef SQLITE_ENABLE_NORMALIZE -/* -** Checks if the specified token is a table, column, or function name, -** based on the databases associated with the statement being prepared. -** If the function fails, zero is returned and pRc is filled with the -** error code. -*/ -static int shouldTreatAsIdentifier( - sqlite3 *db, /* Database handle. */ - const char *zToken, /* Pointer to start of token to be checked */ - int nToken, /* Length of token to be checked */ - int *pRc /* Pointer to error code upon failure */ -){ - int bFound = 0; /* Non-zero if token is an identifier name. */ - int i, j; /* Database and column loop indexes. */ - Schema *pSchema; /* Schema for current database. */ - Hash *pHash; /* Hash table of tables for current database. */ - HashElem *e; /* Hash element for hash table iteration. */ - Table *pTab; /* Database table for columns being checked. */ - - if( sqlite3IsRowidN(zToken, nToken) ){ - return 1; - } - if( nToken>0 ){ - int hash = SQLITE_FUNC_HASH(sqlite3UpperToLower[(u8)zToken[0]], nToken); - if( sqlite3FunctionSearchN(hash, zToken, nToken) ) return 1; - } - assert( db!=0 ); - sqlite3_mutex_enter(db->mutex); - sqlite3BtreeEnterAll(db); - for(i=0; inDb; i++){ - pHash = &db->aFunc; - if( sqlite3HashFindN(pHash, zToken, nToken) ){ - bFound = 1; - break; - } - pSchema = db->aDb[i].pSchema; - if( pSchema==0 ) continue; - pHash = &pSchema->tblHash; - if( sqlite3HashFindN(pHash, zToken, nToken) ){ - bFound = 1; - break; - } - for(e=sqliteHashFirst(pHash); e; e=sqliteHashNext(e)){ - pTab = sqliteHashData(e); - if( pTab==0 ) continue; - pHash = pTab->pColHash; - if( pHash==0 ){ - pTab->pColHash = pHash = sqlite3_malloc(sizeof(Hash)); - if( pHash ){ - sqlite3HashInit(pHash); - for(j=0; jnCol; j++){ - Column *pCol = &pTab->aCol[j]; - sqlite3HashInsert(pHash, pCol->zName, pCol); - } - }else{ - *pRc = SQLITE_NOMEM_BKPT; - bFound = 0; - goto done; - } - } - if( pHash && sqlite3HashFindN(pHash, zToken, nToken) ){ - bFound = 1; - goto done; - } - } - } -done: - sqlite3BtreeLeaveAll(db); - sqlite3_mutex_leave(db->mutex); - return bFound; -} - -/* -** Attempt to estimate the final output buffer size needed for the fully -** normalized version of the specified SQL string. This should take into -** account any potential expansion that could occur (e.g. via IN clauses -** being expanded, etc). This size returned is the total number of bytes -** including the NUL terminator. -*/ -static int estimateNormalizedSize( - const char *zSql, /* The original SQL string */ - int nSql, /* Length of original SQL string */ - u8 prepFlags /* The flags passed to sqlite3_prepare_v3() */ -){ - int nOut = nSql + 4; - const char *z = zSql; - while( nOut0 ){ - zOut[j++] = '"'; - continue; - }else if( k==nToken-1 ){ - zOut[j++] = '"'; - continue; - } - } - if( bKeyword ){ - zOut[j++] = sqlite3Toupper(zSql[iIn+k]); - }else{ - zOut[j++] = sqlite3Tolower(zSql[iIn+k]); - } - } - *piOut = j; -} - -/* -** Perform normalization of the SQL contained in the prepared statement and -** store the result in the zNormSql field. The schema for the associated -** databases are consulted while performing the normalization in order to -** determine if a token appears to be an identifier. All identifiers are -** left intact in the normalized SQL and all literals are replaced with a -** single '?'. -*/ -SQLITE_PRIVATE void sqlite3Normalize( - Vdbe *pVdbe, /* VM being reprepared */ - const char *zSql, /* The original SQL string */ - int nSql, /* Size of the input string in bytes */ - u8 prepFlags /* The flags passed to sqlite3_prepare_v3() */ -){ - sqlite3 *db; /* Database handle. */ - char *z; /* The output string */ - int nZ; /* Size of the output string in bytes */ - int i; /* Next character to read from zSql[] */ - int j; /* Next character to fill in on z[] */ - int tokenType = 0; /* Type of the next token */ - int prevTokenType = 0; /* Type of the previous token, except spaces */ - int n; /* Size of the next token */ - int nParen = 0; /* Nesting level of parenthesis */ - Hash inHash; /* Table of parenthesis levels to output index. */ - - db = sqlite3VdbeDb(pVdbe); - assert( db!=0 ); - assert( pVdbe->zNormSql==0 ); - if( zSql==0 ) return; - nZ = estimateNormalizedSize(zSql, nSql, prepFlags); - z = sqlite3DbMallocRawNN(db, nZ); - if( z==0 ) return; - sqlite3HashInit(&inHash); - for(i=j=0; i0 ){ - sqlite3HashInsert(&inHash, zSql+nParen, 0); - assert( jj+6=0 ); - assert( nZ-1-j=0 ); - /* Fall through */ - } - case TK_MINUS: - case TK_SEMI: - case TK_PLUS: - case TK_STAR: - case TK_SLASH: - case TK_REM: - case TK_EQ: - case TK_LE: - case TK_NE: - case TK_LSHIFT: - case TK_LT: - case TK_RSHIFT: - case TK_GT: - case TK_GE: - case TK_BITOR: - case TK_CONCAT: - case TK_COMMA: - case TK_BITAND: - case TK_BITNOT: - case TK_DOT: - case TK_IN: - case TK_IS: - case TK_NOT: - case TK_NULL: - case TK_ID: { - if( tokenType==TK_NULL ){ - if( prevTokenType==TK_IS || prevTokenType==TK_NOT ){ - /* NULL is a keyword in this case, not a literal value */ - }else{ - /* Here the NULL is a literal value */ - z[j++] = '?'; - break; - } - } - if( j>0 && sqlite3IsIdChar(z[j-1]) && sqlite3IsIdChar(zSql[i]) ){ - z[j++] = ' '; - } - if( tokenType==TK_ID ){ - int i2 = i, n2 = n, rc = SQLITE_OK; - if( nParen>0 ){ - assert( nParen0 && z[j-1]==' ' ){ j--; } - if( j>0 && z[j-1]!=';' ){ z[j++] = ';'; } - z[j] = 0; - assert( jzNormSql = z; - sqlite3HashClear(&inHash); -} -#endif /* SQLITE_ENABLE_NORMALIZE */ /* ** Rerun the compilation of a statement after a schema change. @@ -124538,7 +125252,7 @@ static void pushOntoSorter( } assert( pSelect->iOffset==0 || pSelect->iLimit!=0 ); iLimit = pSelect->iOffset ? pSelect->iOffset+1 : pSelect->iLimit; - pSort->labelDone = sqlite3VdbeMakeLabel(v); + pSort->labelDone = sqlite3VdbeMakeLabel(pParse); sqlite3ExprCodeExprList(pParse, pSort->pOrderBy, regBase, regOrigData, SQLITE_ECEL_DUP | (regOrigData? SQLITE_ECEL_REF : 0)); if( bSeq ){ @@ -124577,7 +125291,7 @@ static void pushOntoSorter( pKI->nAllField-pKI->nKeyField-1); addrJmp = sqlite3VdbeCurrentAddr(v); sqlite3VdbeAddOp3(v, OP_Jump, addrJmp+1, 0, addrJmp+1); VdbeCoverage(v); - pSort->labelBkOut = sqlite3VdbeMakeLabel(v); + pSort->labelBkOut = sqlite3VdbeMakeLabel(pParse); pSort->regReturn = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Gosub, pSort->regReturn, pSort->labelBkOut); sqlite3VdbeAddOp1(v, OP_ResetSorter, pSort->iECursor); @@ -125324,7 +126038,7 @@ static void generateSortTail( ){ Vdbe *v = pParse->pVdbe; /* The prepared statement */ int addrBreak = pSort->labelDone; /* Jump here to exit loop */ - int addrContinue = sqlite3VdbeMakeLabel(v); /* Jump here for next cycle */ + int addrContinue = sqlite3VdbeMakeLabel(pParse);/* Jump here for next cycle */ int addr; /* Top of output loop. Jump for Next. */ int addrOnce = 0; int iTab; @@ -125364,7 +126078,12 @@ static void generateSortTail( regRow = pDest->iSdst; }else{ regRowid = sqlite3GetTempReg(pParse); - regRow = sqlite3GetTempRange(pParse, nColumn); + if( eDest==SRT_EphemTab || eDest==SRT_Table ){ + regRow = sqlite3GetTempReg(pParse); + nColumn = 0; + }else{ + regRow = sqlite3GetTempRange(pParse, nColumn); + } } nKey = pOrderBy->nExpr - pSort->nOBSat; if( pSort->sortFlags & SORTFLAG_UseSorter ){ @@ -125444,6 +126163,7 @@ static void generateSortTail( switch( eDest ){ case SRT_Table: case SRT_EphemTab: { + sqlite3VdbeAddOp3(v, OP_Column, iSortTab, nKey+bSeq, regRow); sqlite3VdbeAddOp2(v, OP_NewRowid, iParm, regRowid); sqlite3VdbeAddOp3(v, OP_Insert, iParm, regRow, regRowid); sqlite3VdbeChangeP5(v, OPFLAG_APPEND); @@ -125984,15 +126704,15 @@ SQLITE_PRIVATE void sqlite3SelectAddColumnTypeAndCollation( SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){ Table *pTab; sqlite3 *db = pParse->db; - int savedFlags; + u64 savedFlags; savedFlags = db->flags; - db->flags &= ~SQLITE_FullColNames; + db->flags &= ~(u64)SQLITE_FullColNames; db->flags |= SQLITE_ShortColNames; sqlite3SelectPrep(pParse, pSelect, 0); + db->flags = savedFlags; if( pParse->nErr ) return 0; while( pSelect->pPrior ) pSelect = pSelect->pPrior; - db->flags = savedFlags; pTab = sqlite3DbMallocZero(db, sizeof(Table) ); if( pTab==0 ){ return 0; @@ -126236,7 +126956,7 @@ static void generateWithRecursiveQuery( if( sqlite3AuthCheck(pParse, SQLITE_RECURSIVE, 0, 0, 0) ) return; /* Process the LIMIT and OFFSET clauses, if they exist */ - addrBreak = sqlite3VdbeMakeLabel(v); + addrBreak = sqlite3VdbeMakeLabel(pParse); p->nSelectRow = 320; /* 4 billion rows */ computeLimitRegisters(pParse, p, addrBreak); pLimit = p->pLimit; @@ -126306,7 +127026,7 @@ static void generateWithRecursiveQuery( sqlite3VdbeAddOp1(v, OP_Delete, iQueue); /* Output the single row in Current */ - addrCont = sqlite3VdbeMakeLabel(v); + addrCont = sqlite3VdbeMakeLabel(pParse); codeOffset(v, regOffset, addrCont); selectInnerLoop(pParse, p, iCurrent, 0, 0, pDest, addrCont, addrBreak); @@ -126614,8 +127334,8 @@ static int multiSelect( if( dest.eDest!=priorOp ){ int iCont, iBreak, iStart; assert( p->pEList ); - iBreak = sqlite3VdbeMakeLabel(v); - iCont = sqlite3VdbeMakeLabel(v); + iBreak = sqlite3VdbeMakeLabel(pParse); + iCont = sqlite3VdbeMakeLabel(pParse); computeLimitRegisters(pParse, p, iBreak); sqlite3VdbeAddOp2(v, OP_Rewind, unionTab, iBreak); VdbeCoverage(v); iStart = sqlite3VdbeCurrentAddr(v); @@ -126683,8 +127403,8 @@ static int multiSelect( ** tables. */ assert( p->pEList ); - iBreak = sqlite3VdbeMakeLabel(v); - iCont = sqlite3VdbeMakeLabel(v); + iBreak = sqlite3VdbeMakeLabel(pParse); + iCont = sqlite3VdbeMakeLabel(pParse); computeLimitRegisters(pParse, p, iBreak); sqlite3VdbeAddOp2(v, OP_Rewind, tab1, iBreak); VdbeCoverage(v); r1 = sqlite3GetTempReg(pParse); @@ -126814,7 +127534,7 @@ static int generateOutputSubroutine( int addr; addr = sqlite3VdbeCurrentAddr(v); - iContinue = sqlite3VdbeMakeLabel(v); + iContinue = sqlite3VdbeMakeLabel(pParse); /* Suppress duplicates for UNION, EXCEPT, and INTERSECT */ @@ -127051,8 +127771,8 @@ static int multiSelectOrderBy( db = pParse->db; v = pParse->pVdbe; assert( v!=0 ); /* Already thrown the error if VDBE alloc failed */ - labelEnd = sqlite3VdbeMakeLabel(v); - labelCmpr = sqlite3VdbeMakeLabel(v); + labelEnd = sqlite3VdbeMakeLabel(pParse); + labelCmpr = sqlite3VdbeMakeLabel(pParse); /* Patch up the ORDER BY clause @@ -127368,6 +128088,7 @@ static Expr *substExpr( ifNullRow.iTable = pSubst->iNewTable; pCopy = &ifNullRow; } + testcase( ExprHasProperty(pCopy, EP_Subquery) ); pNew = sqlite3ExprDup(db, pCopy, 0); if( pNew && pSubst->isLeftJoin ){ ExprSetProperty(pNew, EP_CanBeNull); @@ -127860,11 +128581,9 @@ static int flattenSubquery( jointype = pSubitem->fg.jointype; }else{ assert( pParent!=p ); /* 2nd and subsequent times through the loop */ - pSrc = pParent->pSrc = sqlite3SrcListAppend(db, 0, 0, 0); - if( pSrc==0 ){ - assert( db->mallocFailed ); - break; - } + pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0); + if( pSrc==0 ) break; + pParent->pSrc = pSrc; } /* The subquery uses a single slot of the FROM clause of the outer @@ -127883,10 +128602,9 @@ static int flattenSubquery( ** for the two elements in the FROM clause of the subquery. */ if( nSubSrc>1 ){ - pParent->pSrc = pSrc = sqlite3SrcListEnlarge(db, pSrc, nSubSrc-1,iFrom+1); - if( db->mallocFailed ){ - break; - } + pSrc = sqlite3SrcListEnlarge(pParse, pSrc, nSubSrc-1,iFrom+1); + if( pSrc==0 ) break; + pParent->pSrc = pSrc; } /* Transfer the FROM clause terms from the subquery into the @@ -127932,7 +128650,8 @@ static int flattenSubquery( pParent->pOrderBy = pOrderBy; pSub->pOrderBy = 0; } - pWhere = sqlite3ExprDup(db, pSub->pWhere, 0); + pWhere = pSub->pWhere; + pSub->pWhere = 0; if( isLeftJoin>0 ){ setJoinExpr(pWhere, iNewParent); } @@ -129208,7 +129927,7 @@ static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){ ** ** If regAcc is non-zero and there are no min() or max() aggregates ** in pAggInfo, then only populate the pAggInfo->nAccumulator accumulator -** registers i register regAcc contains 0. The caller will take care +** registers if register regAcc contains 0. The caller will take care ** of setting and clearing regAcc. */ static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){ @@ -129235,7 +129954,7 @@ static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){ regAgg = 0; } if( pF->iDistinct>=0 ){ - addrNext = sqlite3VdbeMakeLabel(v); + addrNext = sqlite3VdbeMakeLabel(pParse); testcase( nArg==0 ); /* Error condition */ testcase( nArg>1 ); /* Also an error */ codeDistinct(pParse, pF->iDistinct, addrNext, 1, regAgg); @@ -129371,14 +130090,19 @@ static struct SrcList_item *isSelfJoinView( ){ struct SrcList_item *pItem; for(pItem = pTabList->a; pItempSelect==0 ) continue; if( pItem->fg.viaCoroutine ) continue; if( pItem->zName==0 ) continue; if( sqlite3_stricmp(pItem->zDatabase, pThis->zDatabase)!=0 ) continue; if( sqlite3_stricmp(pItem->zName, pThis->zName)!=0 ) continue; - if( sqlite3ExprCompare(0, - pThis->pSelect->pWhere, pItem->pSelect->pWhere, -1) - ){ + pS1 = pItem->pSelect; + if( pThis->pSelect->selId!=pS1->selId ){ + /* The query flattener left two different CTE tables with identical + ** names in the same FROM clause. */ + continue; + } + if( sqlite3ExprCompare(0, pThis->pSelect->pWhere, pS1->pWhere, -1) ){ /* The view was modified by some other optimization such as ** pushDownWhereTerms() */ continue; @@ -129640,6 +130364,7 @@ SQLITE_PRIVATE int sqlite3Select( } if( flattenSubquery(pParse, p, i, isAgg) ){ + if( pParse->nErr ) goto select_end; /* This subquery can be absorbed into its parent. */ i = -1; } @@ -129735,22 +130460,12 @@ SQLITE_PRIVATE int sqlite3Select( pSub = pItem->pSelect; if( pSub==0 ) continue; - /* Sometimes the code for a subquery will be generated more than - ** once, if the subquery is part of the WHERE clause in a LEFT JOIN, - ** for example. In that case, do not regenerate the code to manifest - ** a view or the co-routine to implement a view. The first instance - ** is sufficient, though the subroutine to manifest the view does need - ** to be invoked again. */ - if( pItem->addrFillSub ){ - if( pItem->fg.viaCoroutine==0 ){ - /* The subroutine that manifests the view might be a one-time routine, - ** or it might need to be rerun on each iteration because it - ** encodes a correlated subquery. */ - testcase( sqlite3VdbeGetOp(v, pItem->addrFillSub)->opcode==OP_Once ); - sqlite3VdbeAddOp2(v, OP_Gosub, pItem->regReturn, pItem->addrFillSub); - } - continue; - } + /* The code for a subquery should only be generated once, though it is + ** technically harmless for it to be generated multiple times. The + ** following assert() will detect if something changes to cause + ** the same subquery to be coded multiple times, as a signal to the + ** developers to try to optimize the situation. */ + assert( pItem->addrFillSub==0 ); /* Increment Parse.nHeight by the height of the largest expression ** tree referred to by this, the parent select. The child select @@ -129938,7 +130653,7 @@ SQLITE_PRIVATE int sqlite3Select( /* Set the limiter. */ - iEnd = sqlite3VdbeMakeLabel(v); + iEnd = sqlite3VdbeMakeLabel(pParse); if( (p->selFlags & SF_FixedLimit)==0 ){ p->nSelectRow = 320; /* 4 billion rows */ } @@ -130005,9 +130720,9 @@ SQLITE_PRIVATE int sqlite3Select( assert( p->pEList==pEList ); #ifndef SQLITE_OMIT_WINDOWFUNC if( pWin ){ - int addrGosub = sqlite3VdbeMakeLabel(v); - int iCont = sqlite3VdbeMakeLabel(v); - int iBreak = sqlite3VdbeMakeLabel(v); + int addrGosub = sqlite3VdbeMakeLabel(pParse); + int iCont = sqlite3VdbeMakeLabel(pParse); + int iBreak = sqlite3VdbeMakeLabel(pParse); int regGosub = ++pParse->nMem; sqlite3WindowCodeStep(pParse, p, pWInfo, regGosub, addrGosub); @@ -130082,7 +130797,7 @@ SQLITE_PRIVATE int sqlite3Select( } /* Create a label to jump to when we want to abort the query */ - addrEnd = sqlite3VdbeMakeLabel(v); + addrEnd = sqlite3VdbeMakeLabel(pParse); /* Convert TK_COLUMN nodes into TK_AGG_COLUMN and make entries in ** sAggInfo for all TK_AGG_FUNCTION nodes in expressions of the @@ -130171,9 +130886,9 @@ SQLITE_PRIVATE int sqlite3Select( iUseFlag = ++pParse->nMem; iAbortFlag = ++pParse->nMem; regOutputRow = ++pParse->nMem; - addrOutputRow = sqlite3VdbeMakeLabel(v); + addrOutputRow = sqlite3VdbeMakeLabel(pParse); regReset = ++pParse->nMem; - addrReset = sqlite3VdbeMakeLabel(v); + addrReset = sqlite3VdbeMakeLabel(pParse); iAMem = pParse->nMem + 1; pParse->nMem += pGroupBy->nExpr; iBMem = pParse->nMem + 1; @@ -131460,7 +132175,7 @@ static SrcList *targetSrcList( int iDb; /* Index of the database to use */ SrcList *pSrc; /* SrcList to be returned */ - pSrc = sqlite3SrcListAppend(db, 0, 0, 0); + pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0); if( pSrc ){ assert( pSrc->nSrc>0 ); pSrc->a[pSrc->nSrc-1].zName = sqlite3DbStrDup(db, pStep->zTarget); @@ -131645,6 +132360,7 @@ static TriggerPrg *codeRowTrigger( pSubParse->zAuthContext = pTrigger->zName; pSubParse->eTriggerOp = pTrigger->op; pSubParse->nQueryLoop = pParse->nQueryLoop; + pSubParse->disableVtab = pParse->disableVtab; v = sqlite3GetVdbe(pSubParse); if( v ){ @@ -131672,7 +132388,7 @@ static TriggerPrg *codeRowTrigger( if( SQLITE_OK==sqlite3ResolveExprNames(&sNC, pWhen) && db->mallocFailed==0 ){ - iEndTrigger = sqlite3VdbeMakeLabel(v); + iEndTrigger = sqlite3VdbeMakeLabel(pSubParse); sqlite3ExprIfFalse(pSubParse, pWhen, iEndTrigger, SQLITE_JUMPIFNULL); } sqlite3ExprDelete(db, pWhen); @@ -132271,6 +132987,7 @@ SQLITE_PRIVATE void sqlite3Update( ** being updated. Fill in aRegIdx[] with a register number that will hold ** the key for accessing each index. */ + if( onError==OE_Replace ) bReplace = 1; for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ int reg; if( chngKey || hasFK>1 || pIdx==pPk @@ -132284,9 +133001,7 @@ SQLITE_PRIVATE void sqlite3Update( if( indexColumnIsBeingUpdated(pIdx, i, aXRef, chngRowid) ){ reg = ++pParse->nMem; pParse->nMem += pIdx->nColumn; - if( (onError==OE_Replace) - || (onError==OE_Default && pIdx->onError==OE_Replace) - ){ + if( onError==OE_Default && pIdx->onError==OE_Replace ){ bReplace = 1; } break; @@ -132358,7 +133073,7 @@ SQLITE_PRIVATE void sqlite3Update( #endif /* Jump to labelBreak to abandon further processing of this UPDATE */ - labelContinue = labelBreak = sqlite3VdbeMakeLabel(v); + labelContinue = labelBreak = sqlite3VdbeMakeLabel(pParse); /* Not an UPSERT. Normal processing. Begin by ** initialize the count of updated rows */ @@ -132493,13 +133208,13 @@ SQLITE_PRIVATE void sqlite3Update( VdbeCoverage(v); } if( eOnePass!=ONEPASS_SINGLE ){ - labelContinue = sqlite3VdbeMakeLabel(v); + labelContinue = sqlite3VdbeMakeLabel(pParse); } sqlite3VdbeAddOp2(v, OP_IsNull, pPk ? regKey : regOldRowid, labelBreak); VdbeCoverageIf(v, pPk==0); VdbeCoverageIf(v, pPk!=0); }else if( pPk ){ - labelContinue = sqlite3VdbeMakeLabel(v); + labelContinue = sqlite3VdbeMakeLabel(pParse); sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak); VdbeCoverage(v); addrTop = sqlite3VdbeAddOp2(v, OP_RowData, iEph, regKey); sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey, 0); @@ -133267,16 +133982,16 @@ static int execSqlF(sqlite3 *db, char **pzErrMsg, const char *zSql, ...){ ** transient would cause the database file to appear to be deleted ** following reboot. */ -SQLITE_PRIVATE void sqlite3Vacuum(Parse *pParse, Token *pNm){ +SQLITE_PRIVATE void sqlite3Vacuum(Parse *pParse, Token *pNm, Expr *pInto){ Vdbe *v = sqlite3GetVdbe(pParse); int iDb = 0; - if( v==0 ) return; + if( v==0 ) goto build_vacuum_end; if( pNm ){ #ifndef SQLITE_BUG_COMPATIBLE_20160819 /* Default behavior: Report an error if the argument to VACUUM is ** not recognized */ iDb = sqlite3TwoPartName(pParse, pNm, pNm, &pNm); - if( iDb<0 ) return; + if( iDb<0 ) goto build_vacuum_end; #else /* When SQLITE_BUG_COMPATIBLE_20160819 is defined, unrecognized arguments ** to VACUUM are silently ignored. This is a back-out of a bug fix that @@ -133288,37 +134003,63 @@ SQLITE_PRIVATE void sqlite3Vacuum(Parse *pParse, Token *pNm){ #endif } if( iDb!=1 ){ - sqlite3VdbeAddOp1(v, OP_Vacuum, iDb); + int iIntoReg = 0; + if( pInto && sqlite3ResolveSelfReference(pParse,0,0,pInto,0)==0 ){ + iIntoReg = ++pParse->nMem; + sqlite3ExprCode(pParse, pInto, iIntoReg); + } + sqlite3VdbeAddOp2(v, OP_Vacuum, iDb, iIntoReg); sqlite3VdbeUsesBtree(v, iDb); } +build_vacuum_end: + sqlite3ExprDelete(pParse->db, pInto); return; } /* ** This routine implements the OP_Vacuum opcode of the VDBE. */ -SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){ +SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum( + char **pzErrMsg, /* Write error message here */ + sqlite3 *db, /* Database connection */ + int iDb, /* Which attached DB to vacuum */ + sqlite3_value *pOut /* Write results here, if not NULL. VACUUM INTO */ +){ int rc = SQLITE_OK; /* Return code from service routines */ Btree *pMain; /* The database being vacuumed */ Btree *pTemp; /* The temporary database we vacuum into */ - u16 saved_mDbFlags; /* Saved value of db->mDbFlags */ - u32 saved_flags; /* Saved value of db->flags */ + u32 saved_mDbFlags; /* Saved value of db->mDbFlags */ + u64 saved_flags; /* Saved value of db->flags */ int saved_nChange; /* Saved value of db->nChange */ int saved_nTotalChange; /* Saved value of db->nTotalChange */ + u32 saved_openFlags; /* Saved value of db->openFlags */ u8 saved_mTrace; /* Saved trace settings */ Db *pDb = 0; /* Database to detach at end of vacuum */ int isMemDb; /* True if vacuuming a :memory: database */ int nRes; /* Bytes of reserved space at the end of each page */ int nDb; /* Number of attached databases */ const char *zDbMain; /* Schema name of database to vacuum */ + const char *zOut; /* Name of output file */ if( !db->autoCommit ){ sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction"); - return SQLITE_ERROR; + return SQLITE_ERROR; /* IMP: R-12218-18073 */ } if( db->nVdbeActive>1 ){ sqlite3SetString(pzErrMsg, db,"cannot VACUUM - SQL statements in progress"); - return SQLITE_ERROR; + return SQLITE_ERROR; /* IMP: R-15610-35227 */ + } + saved_openFlags = db->openFlags; + if( pOut ){ + if( sqlite3_value_type(pOut)!=SQLITE_TEXT ){ + sqlite3SetString(pzErrMsg, db, "non-text filename"); + return SQLITE_ERROR; + } + zOut = (const char*)sqlite3_value_text(pOut); + db->openFlags &= ~SQLITE_OPEN_READONLY; + db->openFlags |= SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE; + }else{ + zOut = ""; } /* Save the current value of the database flags so that it can be @@ -133331,7 +134072,7 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){ saved_mTrace = db->mTrace; db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks; db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum; - db->flags &= ~(SQLITE_ForeignKeys | SQLITE_ReverseOrder + db->flags &= ~(u64)(SQLITE_ForeignKeys | SQLITE_ReverseOrder | SQLITE_Defensive | SQLITE_CountRows); db->mTrace = 0; @@ -133354,19 +134095,23 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){ ** to write the journal header file. */ nDb = db->nDb; - rc = execSql(db, pzErrMsg, "ATTACH''AS vacuum_db"); + rc = execSqlF(db, pzErrMsg, "ATTACH %Q AS vacuum_db", zOut); + db->openFlags = saved_openFlags; if( rc!=SQLITE_OK ) goto end_of_vacuum; assert( (db->nDb-1)==nDb ); pDb = &db->aDb[nDb]; assert( strcmp(pDb->zDbSName,"vacuum_db")==0 ); pTemp = pDb->pBt; - - /* The call to execSql() to attach the temp database has left the file - ** locked (as there was more than one active statement when the transaction - ** to read the schema was concluded. Unlock it here so that this doesn't - ** cause problems for the call to BtreeSetPageSize() below. */ - sqlite3BtreeCommit(pTemp); - + if( pOut ){ + sqlite3_file *id = sqlite3PagerFile(sqlite3BtreePager(pTemp)); + i64 sz = 0; + if( id->pMethods!=0 && (sqlite3OsFileSize(id, &sz)!=SQLITE_OK || sz>0) ){ + rc = SQLITE_ERROR; + sqlite3SetString(pzErrMsg, db, "output file already exists"); + goto end_of_vacuum; + } + db->mDbFlags |= DBFLAG_VacuumInto; + } nRes = sqlite3BtreeGetOptimalReserve(pMain); /* A VACUUM cannot change the pagesize of an encrypted database. */ @@ -133390,7 +134135,7 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){ */ rc = execSql(db, pzErrMsg, "BEGIN"); if( rc!=SQLITE_OK ) goto end_of_vacuum; - rc = sqlite3BtreeBeginTrans(pMain, 2, 0); + rc = sqlite3BtreeBeginTrans(pMain, pOut==0 ? 2 : 0, 0); if( rc!=SQLITE_OK ) goto end_of_vacuum; /* Do not attempt to change the page size for a WAL database */ @@ -133485,7 +134230,7 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){ }; assert( 1==sqlite3BtreeIsInTrans(pTemp) ); - assert( 1==sqlite3BtreeIsInTrans(pMain) ); + assert( pOut!=0 || 1==sqlite3BtreeIsInTrans(pMain) ); /* Copy Btree meta values */ for(i=0; iflags */ @@ -133848,9 +134599,13 @@ SQLITE_PRIVATE void sqlite3VtabClear(sqlite3 *db, Table *p){ ** string will be freed automatically when the table is ** deleted. */ -static void addModuleArgument(sqlite3 *db, Table *pTable, char *zArg){ - int nBytes = sizeof(char *)*(2+pTable->nModuleArg); +static void addModuleArgument(Parse *pParse, Table *pTable, char *zArg){ + sqlite3_int64 nBytes = sizeof(char *)*(2+pTable->nModuleArg); char **azModuleArg; + sqlite3 *db = pParse->db; + if( pTable->nModuleArg+3>=db->aLimit[SQLITE_LIMIT_COLUMN] ){ + sqlite3ErrorMsg(pParse, "too many columns on %s", pTable->zName); + } azModuleArg = sqlite3DbRealloc(db, pTable->azModuleArg, nBytes); if( azModuleArg==0 ){ sqlite3DbFree(db, zArg); @@ -133885,9 +134640,9 @@ SQLITE_PRIVATE void sqlite3VtabBeginParse( db = pParse->db; assert( pTable->nModuleArg==0 ); - addModuleArgument(db, pTable, sqlite3NameFromToken(db, pModuleName)); - addModuleArgument(db, pTable, 0); - addModuleArgument(db, pTable, sqlite3DbStrDup(db, pTable->zName)); + addModuleArgument(pParse, pTable, sqlite3NameFromToken(db, pModuleName)); + addModuleArgument(pParse, pTable, 0); + addModuleArgument(pParse, pTable, sqlite3DbStrDup(db, pTable->zName)); assert( (pParse->sNameToken.z==pName2->z && pName2->z!=0) || (pParse->sNameToken.z==pName1->z && pName2->z==0) ); @@ -133920,7 +134675,7 @@ static void addArgumentToVtab(Parse *pParse){ const char *z = (const char*)pParse->sArg.z; int n = pParse->sArg.n; sqlite3 *db = pParse->db; - addModuleArgument(db, pParse->pNewTable, sqlite3DbStrNDup(db, z, n)); + addModuleArgument(pParse, pParse->pNewTable, sqlite3DbStrNDup(db, z, n)); } } @@ -134209,7 +134964,8 @@ static int growVTrans(sqlite3 *db){ /* Grow the sqlite3.aVTrans array if required */ if( (db->nVTrans%ARRAY_INCR)==0 ){ VTable **aVTrans; - int nBytes = sizeof(sqlite3_vtab *) * (db->nVTrans + ARRAY_INCR); + sqlite3_int64 nBytes = sizeof(sqlite3_vtab*)* + ((sqlite3_int64)db->nVTrans + ARRAY_INCR); aVTrans = sqlite3DbRealloc(db, (void *)db->aVTrans, nBytes); if( !aVTrans ){ return SQLITE_NOMEM_BKPT; @@ -134547,6 +135303,7 @@ SQLITE_PRIVATE int sqlite3VtabSavepoint(sqlite3 *db, int op, int iSavepoint){ const sqlite3_module *pMod = pVTab->pMod->pModule; if( pVTab->pVtab && pMod->iVersion>=2 ){ int (*xMethod)(sqlite3_vtab *, int); + sqlite3VtabLock(pVTab); switch( op ){ case SAVEPOINT_BEGIN: xMethod = pMod->xSavepoint; @@ -134562,6 +135319,7 @@ SQLITE_PRIVATE int sqlite3VtabSavepoint(sqlite3 *db, int op, int iSavepoint){ if( xMethod && pVTab->iSavepoint>iSavepoint ){ rc = xMethod(pVTab->pVtab, iSavepoint); } + sqlite3VtabUnlock(pVTab); } } } @@ -134703,9 +135461,9 @@ SQLITE_PRIVATE int sqlite3VtabEponymousTableInit(Parse *pParse, Module *pMod){ pTab->pSchema = db->aDb[0].pSchema; assert( pTab->nModuleArg==0 ); pTab->iPKey = -1; - addModuleArgument(db, pTab, sqlite3DbStrDup(db, pTab->zName)); - addModuleArgument(db, pTab, 0); - addModuleArgument(db, pTab, sqlite3DbStrDup(db, pTab->zName)); + addModuleArgument(pParse, pTab, sqlite3DbStrDup(db, pTab->zName)); + addModuleArgument(pParse, pTab, 0); + addModuleArgument(pParse, pTab, sqlite3DbStrDup(db, pTab->zName)); rc = vtabCallConstructor(db, pTab, pMod, pModule->xConnect, &zErr); if( rc ){ sqlite3ErrorMsg(pParse, "%s", zErr); @@ -135323,8 +136081,11 @@ SQLITE_PRIVATE void sqlite3WhereAddScanStatus( # define sqlite3WhereAddScanStatus(a, b, c, d) ((void)d) #endif SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + Parse *pParse, /* Parsing context */ + Vdbe *v, /* Prepared statement under construction */ WhereInfo *pWInfo, /* Complete information about the WHERE clause */ int iLevel, /* Which level of pWInfo->a[] should be coded */ + WhereLevel *pLevel, /* The current level pointer */ Bitmask notReady /* Which tables are currently available */ ); @@ -135594,6 +136355,7 @@ SQLITE_PRIVATE int sqlite3WhereExplainOneScan( } #endif zMsg = sqlite3StrAccumFinish(&str); + sqlite3ExplainBreakpoint("",zMsg); ret = sqlite3VdbeAddOp4(v, OP_Explain, sqlite3VdbeCurrentAddr(v), pParse->addrExplain, 0, zMsg,P4_DYNAMIC); } @@ -135919,16 +136681,17 @@ static int codeEqualityTerm( if( pLoop->aLTerm[i]->pExpr==pX ) nEq++; } + iTab = 0; if( (pX->flags & EP_xIsSelect)==0 || pX->x.pSelect->pEList->nExpr==1 ){ - eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, 0); + eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, 0, &iTab); }else{ sqlite3 *db = pParse->db; pX = removeUnindexableInClauseTerms(pParse, iEq, pLoop, pX); if( !db->mallocFailed ){ aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*nEq); - eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap); - pTerm->pExpr->iTable = pX->iTable; + eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap, &iTab); + pTerm->pExpr->iTable = iTab; } sqlite3ExprDelete(db, pX); pX = pTerm->pExpr; @@ -135938,7 +136701,6 @@ static int codeEqualityTerm( testcase( bRev ); bRev = !bRev; } - iTab = pX->iTable; sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iTab, 0); VdbeCoverageIf(v, bRev); VdbeCoverageIf(v, !bRev); @@ -135946,7 +136708,7 @@ static int codeEqualityTerm( pLoop->wsFlags |= WHERE_IN_ABLE; if( pLevel->u.in.nIn==0 ){ - pLevel->addrNxt = sqlite3VdbeMakeLabel(v); + pLevel->addrNxt = sqlite3VdbeMakeLabel(pParse); } i = pLevel->u.in.nIn; @@ -135962,7 +136724,6 @@ static int codeEqualityTerm( if( pLoop->aLTerm[i]->pExpr==pX ){ int iOut = iReg + i - iEq; if( eType==IN_INDEX_ROWID ){ - testcase( nEq>1 ); /* Happens with a UNIQUE index on ROWID */ pIn->addrInTop = sqlite3VdbeAddOp2(v, OP_Rowid, iTab, iOut); }else{ int iCol = aiMap ? aiMap[iMap++] : 0; @@ -136457,7 +137218,9 @@ static void codeExprOrVector(Parse *pParse, Expr *p, int iReg, int nReg){ #ifndef SQLITE_OMIT_SUBQUERY if( (p->flags & EP_xIsSelect) ){ Vdbe *v = pParse->pVdbe; - int iSelect = sqlite3CodeSubselect(pParse, p, 0, 0); + int iSelect; + assert( p->op==TK_SELECT ); + iSelect = sqlite3CodeSubselect(pParse, p); sqlite3VdbeAddOp3(v, OP_Copy, iSelect, iReg, nReg-1); }else #endif @@ -136538,27 +137301,54 @@ static void whereIndexExprTrans( } } +/* +** The pTruth expression is always true because it is the WHERE clause +** a partial index that is driving a query loop. Look through all of the +** WHERE clause terms on the query, and if any of those terms must be +** true because pTruth is true, then mark those WHERE clause terms as +** coded. +*/ +static void whereApplyPartialIndexConstraints( + Expr *pTruth, + int iTabCur, + WhereClause *pWC +){ + int i; + WhereTerm *pTerm; + while( pTruth->op==TK_AND ){ + whereApplyPartialIndexConstraints(pTruth->pLeft, iTabCur, pWC); + pTruth = pTruth->pRight; + } + for(i=0, pTerm=pWC->a; inTerm; i++, pTerm++){ + Expr *pExpr; + if( pTerm->wtFlags & TERM_CODED ) continue; + pExpr = pTerm->pExpr; + if( sqlite3ExprCompare(0, pExpr, pTruth, iTabCur)==0 ){ + pTerm->wtFlags |= TERM_CODED; + } + } +} + /* ** Generate code for the start of the iLevel-th loop in the WHERE clause ** implementation described by pWInfo. */ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + Parse *pParse, /* Parsing context */ + Vdbe *v, /* Prepared statement under construction */ WhereInfo *pWInfo, /* Complete information about the WHERE clause */ int iLevel, /* Which level of pWInfo->a[] should be coded */ + WhereLevel *pLevel, /* The current level pointer */ Bitmask notReady /* Which tables are currently available */ ){ int j, k; /* Loop counters */ int iCur; /* The VDBE cursor for the table */ int addrNxt; /* Where to jump to continue with the next IN case */ - int omitTable; /* True if we use the index only */ int bRev; /* True if we need to scan in reverse order */ - WhereLevel *pLevel; /* The where level to be coded */ WhereLoop *pLoop; /* The WhereLoop object being coded */ WhereClause *pWC; /* Decomposition of the entire WHERE clause */ WhereTerm *pTerm; /* A WHERE clause term */ - Parse *pParse; /* Parsing context */ sqlite3 *db; /* Database connection */ - Vdbe *v; /* The prepared stmt under constructions */ struct SrcList_item *pTabItem; /* FROM clause term being coded */ int addrBrk; /* Jump here to break out of the loop */ int addrHalt; /* addrBrk for the outermost loop */ @@ -136568,18 +137358,13 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( Index *pIdx = 0; /* Index used by loop (if any) */ int iLoop; /* Iteration of constraint generator loop */ - pParse = pWInfo->pParse; - v = pParse->pVdbe; pWC = &pWInfo->sWC; db = pParse->db; - pLevel = &pWInfo->a[iLevel]; pLoop = pLevel->pWLoop; pTabItem = &pWInfo->pTabList->a[pLevel->iFrom]; iCur = pTabItem->iCursor; pLevel->notReady = notReady & ~sqlite3WhereGetMask(&pWInfo->sMaskSet, iCur); bRev = (pWInfo->revMask>>iLevel)&1; - omitTable = (pLoop->wsFlags & WHERE_IDX_ONLY)!=0 - && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0; VdbeModuleComment((v, "Begin WHERE-loop%d: %s",iLevel,pTabItem->pTab->zName)); /* Create labels for the "break" and "continue" instructions @@ -136592,8 +137377,8 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( ** there are no IN operators in the constraints, the "addrNxt" label ** is the same as "addrBrk". */ - addrBrk = pLevel->addrBrk = pLevel->addrNxt = sqlite3VdbeMakeLabel(v); - addrCont = pLevel->addrCont = sqlite3VdbeMakeLabel(v); + addrBrk = pLevel->addrBrk = pLevel->addrNxt = sqlite3VdbeMakeLabel(pParse); + addrCont = pLevel->addrCont = sqlite3VdbeMakeLabel(pParse); /* If this is the right table of a LEFT OUTER JOIN, allocate and ** initialize a memory cell that records if this table matches any @@ -136720,7 +137505,6 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( pTerm = pLoop->aLTerm[0]; assert( pTerm!=0 ); assert( pTerm->pExpr!=0 ); - assert( omitTable==0 ); testcase( pTerm->wtFlags & TERM_VIRTUAL ); iReleaseReg = ++pParse->nMem; iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, 0, bRev, iReleaseReg); @@ -136729,6 +137513,9 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( sqlite3VdbeAddOp3(v, OP_SeekRowid, iCur, addrNxt, iRowidReg); VdbeCoverage(v); pLevel->op = OP_Noop; + if( (pTerm->prereqAll & pLevel->notReady)==0 ){ + pTerm->wtFlags |= TERM_CODED; + } }else if( (pLoop->wsFlags & WHERE_IPK)!=0 && (pLoop->wsFlags & WHERE_COLUMN_RANGE)!=0 ){ @@ -136739,7 +137526,6 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( int memEndValue = 0; WhereTerm *pStart, *pEnd; - assert( omitTable==0 ); j = 0; pStart = pEnd = 0; if( pLoop->wsFlags & WHERE_BTM_LIMIT ) pStart = pLoop->aLTerm[j++]; @@ -136903,6 +137689,8 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( char *zEndAff = 0; /* Affinity for end of range constraint */ u8 bSeekPastNull = 0; /* True to seek past initial nulls */ u8 bStopAtNull = 0; /* Add condition to terminate at NULLs */ + int omitTable; /* True if we use the index only */ + pIdx = pLoop->u.btree.pIndex; iIdxCur = pLevel->iIdxCur; @@ -137104,6 +137892,8 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( } /* Seek the table cursor, if required */ + omitTable = (pLoop->wsFlags & WHERE_IDX_ONLY)!=0 + && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0; if( omitTable ){ /* pIdx is a covering index. No need to access the main table. */ }else if( HasRowid(pIdx->pTable) ){ @@ -137138,11 +137928,24 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( ** the cursor. In this case it is important to do the full evaluation, ** as the result of the expression may not be NULL, even if all table ** column values are. https://www.sqlite.org/src/info/7fa8049685b50b5a + ** + ** Also, do not do this when processing one index an a multi-index + ** OR clause, since the transformation will become invalid once we + ** move forward to the next index. + ** https://sqlite.org/src/info/4e8e4857d32d401f */ - if( pLevel->iLeftJoin==0 ){ + if( pLevel->iLeftJoin==0 && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0 ){ whereIndexExprTrans(pIdx, iCur, iIdxCur, pWInfo); } + /* If a partial index is driving the loop, try to eliminate WHERE clause + ** terms from the query that must be true due to the WHERE clause of + ** the partial index + */ + if( pIdx->pPartIdxWhere ){ + whereApplyPartialIndexConstraints(pIdx->pPartIdxWhere, iCur, pWC); + } + /* Record the instruction used to terminate the loop. */ if( pLoop->wsFlags & WHERE_ONEROW ){ pLevel->op = OP_Noop; @@ -137214,7 +138017,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( int regReturn = ++pParse->nMem; /* Register used with OP_Gosub */ int regRowset = 0; /* Register for RowSet object */ int regRowid = 0; /* Register holding rowid */ - int iLoopBody = sqlite3VdbeMakeLabel(v); /* Start of loop body */ + int iLoopBody = sqlite3VdbeMakeLabel(pParse);/* Start of loop body */ int iRetInit; /* Address of regReturn init */ int untestedTerms = 0; /* Some terms not completely tested */ int ii; /* Loop counter */ @@ -137306,7 +138109,12 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( pAndExpr = sqlite3ExprAnd(db, pAndExpr, pExpr); } if( pAndExpr ){ - pAndExpr = sqlite3PExpr(pParse, TK_AND|TKFLG_DONTFOLD, 0, pAndExpr); + /* The extra 0x10000 bit on the opcode is masked off and does not + ** become part of the new Expr.op. However, it does make the + ** op==TK_AND comparison inside of sqlite3PExpr() false, and this + ** prevents sqlite3PExpr() from implementing AND short-circuit + ** optimization, which we do not want here. */ + pAndExpr = sqlite3PExpr(pParse, TK_AND|0x10000, 0, pAndExpr); } } @@ -137330,6 +138138,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( pOrExpr = pAndExpr; } /* Loop through table entries that match term pOrTerm. */ + ExplainQueryPlan((pParse, 1, "INDEX %d", ii+1)); WHERETRACE(0xffff, ("Subplan for OR-clause:\n")); pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0, wctrlFlags, iCovCur); @@ -137433,6 +138242,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( /* Finish the loop through table entries that match term pOrTerm. */ sqlite3WhereEnd(pSubWInfo); + ExplainQueryPlanPop(pParse); } } } @@ -137534,8 +138344,9 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( u32 x = pLevel->iLikeRepCntr; if( x>0 ){ skipLikeAddr = sqlite3VdbeAddOp1(v, (x&1)?OP_IfNot:OP_If,(int)(x>>1)); + VdbeCoverageIf(v, (x&1)==1); + VdbeCoverageIf(v, (x&1)==0); } - VdbeCoverage(v); #endif } #ifdef WHERETRACE_ENABLED /* 0xffff */ @@ -138394,6 +139205,7 @@ static void exprAnalyzeOrTerm( ** and column is found but leave okToChngToIN false if not found. */ for(j=0; j<2 && !okToChngToIN; j++){ + Expr *pLeft = 0; pOrTerm = pOrWc->a; for(i=pOrWc->nTerm-1; i>=0; i--, pOrTerm++){ assert( pOrTerm->eOperator & WO_EQ ); @@ -138417,6 +139229,7 @@ static void exprAnalyzeOrTerm( } iColumn = pOrTerm->u.leftColumn; iCursor = pOrTerm->leftCursor; + pLeft = pOrTerm->pExpr->pLeft; break; } if( i<0 ){ @@ -138436,7 +139249,9 @@ static void exprAnalyzeOrTerm( assert( pOrTerm->eOperator & WO_EQ ); if( pOrTerm->leftCursor!=iCursor ){ pOrTerm->wtFlags &= ~TERM_OR_OK; - }else if( pOrTerm->u.leftColumn!=iColumn ){ + }else if( pOrTerm->u.leftColumn!=iColumn || (iColumn==XN_EXPR + && sqlite3ExprCompare(pParse, pOrTerm->pExpr->pLeft, pLeft, -1) + )){ okToChngToIN = 0; }else{ int affLeft, affRight; @@ -139133,6 +139948,12 @@ SQLITE_PRIVATE Bitmask sqlite3WhereExprUsageNN(WhereMaskSet *pMaskSet, Expr *p){ }else if( p->x.pList ){ mask |= sqlite3WhereExprListUsage(pMaskSet, p->x.pList); } +#ifndef SQLITE_OMIT_WINDOWFUNC + if( p->op==TK_FUNCTION && p->y.pWin ){ + mask |= sqlite3WhereExprListUsage(pMaskSet, p->y.pWin->pPartition); + mask |= sqlite3WhereExprListUsage(pMaskSet, p->y.pWin->pOrderBy); + } +#endif return mask; } SQLITE_PRIVATE Bitmask sqlite3WhereExprUsage(WhereMaskSet *pMaskSet, Expr *p){ @@ -139524,6 +140345,17 @@ static WhereTerm *whereScanNext(WhereScan *pScan){ return 0; } +/* +** This is whereScanInit() for the case of an index on an expression. +** It is factored out into a separate tail-recursion subroutine so that +** the normal whereScanInit() routine, which is a high-runner, does not +** need to push registers onto the stack as part of its prologue. +*/ +static SQLITE_NOINLINE WhereTerm *whereScanInitIndexExpr(WhereScan *pScan){ + pScan->idxaff = sqlite3ExprAffinity(pScan->pIdxExpr); + return whereScanNext(pScan); +} + /* ** Initialize a WHERE clause scanner object. Return a pointer to the ** first match. Return NULL if there are no matches. @@ -139556,12 +140388,19 @@ static WhereTerm *whereScanInit( pScan->pIdxExpr = 0; pScan->idxaff = 0; pScan->zCollName = 0; + pScan->opMask = opMask; + pScan->k = 0; + pScan->aiCur[0] = iCur; + pScan->nEquiv = 1; + pScan->iEquiv = 1; if( pIdx ){ int j = iColumn; iColumn = pIdx->aiColumn[j]; if( iColumn==XN_EXPR ){ pScan->pIdxExpr = pIdx->aColExpr->a[j].pExpr; pScan->zCollName = pIdx->azColl[j]; + pScan->aiColumn[0] = XN_EXPR; + return whereScanInitIndexExpr(pScan); }else if( iColumn==pIdx->pTable->iPKey ){ iColumn = XN_ROWID; }else if( iColumn>=0 ){ @@ -139571,12 +140410,7 @@ static WhereTerm *whereScanInit( }else if( iColumn==XN_EXPR ){ return 0; } - pScan->opMask = opMask; - pScan->k = 0; - pScan->aiCur[0] = iCur; pScan->aiColumn[0] = iColumn; - pScan->nEquiv = 1; - pScan->iEquiv = 1; return whereScanNext(pScan); } @@ -140051,7 +140885,7 @@ static void constructAutomaticIndex( addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur); VdbeCoverage(v); } if( pPartial ){ - iContinue = sqlite3VdbeMakeLabel(v); + iContinue = sqlite3VdbeMakeLabel(pParse); sqlite3ExprIfFalse(pParse, pPartial, iContinue, SQLITE_JUMPIFNULL); pLoop->wsFlags |= WHERE_PARTIALIDX; } @@ -140068,6 +140902,7 @@ static void constructAutomaticIndex( translateColumnToCopy(pParse, addrTop, pLevel->iTabCur, pTabItem->regResult, 1); sqlite3VdbeGoto(v, addrTop); + pTabItem->fg.viaCoroutine = 0; }else{ sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); VdbeCoverage(v); } @@ -141423,7 +142258,7 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){ rc = whereLoopXfer(db, p, pTemplate); if( (p->wsFlags & WHERE_VIRTUALTABLE)==0 ){ Index *pIndex = p->u.btree.pIndex; - if( pIndex && pIndex->tnum==0 ){ + if( pIndex && pIndex->idxType==SQLITE_IDXTYPE_IPK ){ p->u.btree.pIndex = 0; } } @@ -141590,8 +142425,8 @@ static int whereRangeVectorLen( ** terms only. If it is modified, this value is restored before this ** function returns. ** -** If pProbe->tnum==0, that means pIndex is a fake index used for the -** INTEGER PRIMARY KEY. +** If pProbe->idxType==SQLITE_IDXTYPE_IPK, that means pIndex is +** a fake index used for the INTEGER PRIMARY KEY. */ static int whereLoopAddBtreeIndex( WhereLoopBuilder *pBuilder, /* The WhereLoop factory */ @@ -142091,6 +142926,7 @@ static int whereLoopAddBtree( sPk.onError = OE_Replace; sPk.pTable = pTab; sPk.szIdxRow = pTab->szTabRow; + sPk.idxType = SQLITE_IDXTYPE_IPK; aiRowEstPk[0] = pTab->nRowLogEst; aiRowEstPk[1] = 0; pFirst = pSrc->pTab->pIndex; @@ -142181,7 +143017,7 @@ static int whereLoopAddBtree( b = indexMightHelpWithOrderBy(pBuilder, pProbe, pSrc->iCursor); /* The ONEPASS_DESIRED flags never occurs together with ORDER BY */ assert( (pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 || b==0 ); - if( pProbe->tnum<=0 ){ + if( pProbe->idxType==SQLITE_IDXTYPE_IPK ){ /* Integer primary key index */ pNew->wsFlags = WHERE_IPK; @@ -142534,11 +143370,11 @@ static int whereLoopAddVirtual( rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn); /* If the call to xBestIndex() with all terms enabled produced a plan - ** that does not require any source tables (IOW: a plan with mBest==0), - ** then there is no point in making any further calls to xBestIndex() - ** since they will all return the same result (if the xBestIndex() - ** implementation is sane). */ - if( rc==SQLITE_OK && (mBest = (pNew->prereq & ~mPrereq))!=0 ){ + ** that does not require any source tables (IOW: a plan with mBest==0) + ** and does not use an IN(...) operator, then there is no point in making + ** any further calls to xBestIndex() since they will all return the same + ** result (if the xBestIndex() implementation is sane). */ + if( rc==SQLITE_OK && ((mBest = (pNew->prereq & ~mPrereq))!=0 || bIn) ){ int seenZero = 0; /* True if a plan with no prereqs seen */ int seenZeroNoIN = 0; /* Plan with no prereqs and no IN(...) seen */ Bitmask mPrev = 0; @@ -143857,7 +144693,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( pWInfo->pResultSet = pResultSet; pWInfo->aiCurOnePass[0] = pWInfo->aiCurOnePass[1] = -1; pWInfo->nLevel = nTabList; - pWInfo->iBreak = pWInfo->iContinue = sqlite3VdbeMakeLabel(v); + pWInfo->iBreak = pWInfo->iContinue = sqlite3VdbeMakeLabel(pParse); pWInfo->wctrlFlags = wctrlFlags; pWInfo->iLimit = iAuxArg; pWInfo->savedNQueryLoop = pParse->nQueryLoop; @@ -144131,9 +144967,10 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( if( (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0 ){ int wsFlags = pWInfo->a[0].pWLoop->wsFlags; int bOnerow = (wsFlags & WHERE_ONEROW)!=0; + assert( !(wsFlags & WHERE_VIRTUALTABLE) || IsVirtual(pTabList->a[0].pTab) ); if( bOnerow || ( 0!=(wctrlFlags & WHERE_ONEPASS_MULTIROW) - && 0==(wsFlags & WHERE_VIRTUALTABLE) + && !IsVirtual(pTabList->a[0].pTab) && (0==(wsFlags & WHERE_MULTI_OR) || (wctrlFlags & WHERE_DUPLICATES_OK)) )){ pWInfo->eOnePass = bOnerow ? ONEPASS_SINGLE : ONEPASS_MULTI; @@ -144288,7 +145125,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( pParse, pTabList, pLevel, wctrlFlags ); pLevel->addrBody = sqlite3VdbeCurrentAddr(v); - notReady = sqlite3WhereCodeOneLoopStart(pWInfo, ii, notReady); + notReady = sqlite3WhereCodeOneLoopStart(pParse,v,pWInfo,ii,pLevel,notReady); pWInfo->iContinue = pLevel->addrCont; if( (wsFlags&WHERE_MULTI_OR)==0 && (wctrlFlags&WHERE_OR_SUBCLAUSE)==0 ){ sqlite3WhereAddScanStatus(v, pTabList, pLevel, addrExplain); @@ -144473,6 +145310,29 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ continue; } +#ifdef SQLITE_ENABLE_EARLY_CURSOR_CLOSE + /* Close all of the cursors that were opened by sqlite3WhereBegin. + ** Except, do not close cursors that will be reused by the OR optimization + ** (WHERE_OR_SUBCLAUSE). And do not close the OP_OpenWrite cursors + ** created for the ONEPASS optimization. + */ + if( (pTab->tabFlags & TF_Ephemeral)==0 + && pTab->pSelect==0 + && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0 + ){ + int ws = pLoop->wsFlags; + if( pWInfo->eOnePass==ONEPASS_OFF && (ws & WHERE_IDX_ONLY)==0 ){ + sqlite3VdbeAddOp1(v, OP_Close, pTabItem->iCursor); + } + if( (ws & WHERE_INDEXED)!=0 + && (ws & (WHERE_IPK|WHERE_AUTO_INDEX))==0 + && pLevel->iIdxCur!=pWInfo->aiCurOnePass[1] + ){ + sqlite3VdbeAddOp1(v, OP_Close, pLevel->iIdxCur); + } + } +#endif + /* If this scan uses an index, make VDBE code substitutions to read data ** from the index instead of from the table where possible. In some cases ** this optimization prevents the table from ever being read, which can @@ -144747,6 +145607,96 @@ static void dense_rankValueFunc(sqlite3_context *pCtx){ } } +/* +** Implementation of built-in window function nth_value(). This +** implementation is used in "slow mode" only - when the EXCLUDE clause +** is not set to the default value "NO OTHERS". +*/ +struct NthValueCtx { + i64 nStep; + sqlite3_value *pValue; +}; +static void nth_valueStepFunc( + sqlite3_context *pCtx, + int nArg, + sqlite3_value **apArg +){ + struct NthValueCtx *p; + p = (struct NthValueCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p)); + if( p ){ + i64 iVal; + switch( sqlite3_value_numeric_type(apArg[1]) ){ + case SQLITE_INTEGER: + iVal = sqlite3_value_int64(apArg[1]); + break; + case SQLITE_FLOAT: { + double fVal = sqlite3_value_double(apArg[1]); + if( ((i64)fVal)!=fVal ) goto error_out; + iVal = (i64)fVal; + break; + } + default: + goto error_out; + } + if( iVal<=0 ) goto error_out; + + p->nStep++; + if( iVal==p->nStep ){ + p->pValue = sqlite3_value_dup(apArg[0]); + if( !p->pValue ){ + sqlite3_result_error_nomem(pCtx); + } + } + } + UNUSED_PARAMETER(nArg); + UNUSED_PARAMETER(apArg); + return; + + error_out: + sqlite3_result_error( + pCtx, "second argument to nth_value must be a positive integer", -1 + ); +} +static void nth_valueFinalizeFunc(sqlite3_context *pCtx){ + struct NthValueCtx *p; + p = (struct NthValueCtx*)sqlite3_aggregate_context(pCtx, 0); + if( p && p->pValue ){ + sqlite3_result_value(pCtx, p->pValue); + sqlite3_value_free(p->pValue); + p->pValue = 0; + } +} +#define nth_valueInvFunc noopStepFunc +#define nth_valueValueFunc noopValueFunc + +static void first_valueStepFunc( + sqlite3_context *pCtx, + int nArg, + sqlite3_value **apArg +){ + struct NthValueCtx *p; + p = (struct NthValueCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p)); + if( p && p->pValue==0 ){ + p->pValue = sqlite3_value_dup(apArg[0]); + if( !p->pValue ){ + sqlite3_result_error_nomem(pCtx); + } + } + UNUSED_PARAMETER(nArg); + UNUSED_PARAMETER(apArg); +} +static void first_valueFinalizeFunc(sqlite3_context *pCtx){ + struct NthValueCtx *p; + p = (struct NthValueCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p)); + if( p && p->pValue ){ + sqlite3_result_value(pCtx, p->pValue); + sqlite3_value_free(p->pValue); + p->pValue = 0; + } +} +#define first_valueInvFunc noopStepFunc +#define first_valueValueFunc noopValueFunc + /* ** Implementation of built-in window function rank(). Assumes that ** the window frame has been set to: @@ -144782,7 +145732,7 @@ static void rankValueFunc(sqlite3_context *pCtx){ ** Implementation of built-in window function percent_rank(). Assumes that ** the window frame has been set to: ** -** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW +** GROUPS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING */ static void percent_rankStepFunc( sqlite3_context *pCtx, @@ -144790,38 +145740,44 @@ static void percent_rankStepFunc( sqlite3_value **apArg ){ struct CallCount *p; - UNUSED_PARAMETER(nArg); assert( nArg==1 ); - + UNUSED_PARAMETER(nArg); assert( nArg==0 ); + UNUSED_PARAMETER(apArg); p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p)); if( p ){ - if( p->nTotal==0 ){ - p->nTotal = sqlite3_value_int64(apArg[0]); - } - p->nStep++; - if( p->nValue==0 ){ - p->nValue = p->nStep; - } + p->nTotal++; } } +static void percent_rankInvFunc( + sqlite3_context *pCtx, + int nArg, + sqlite3_value **apArg +){ + struct CallCount *p; + UNUSED_PARAMETER(nArg); assert( nArg==0 ); + UNUSED_PARAMETER(apArg); + p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p)); + p->nStep++; +} static void percent_rankValueFunc(sqlite3_context *pCtx){ struct CallCount *p; p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p)); if( p ){ + p->nValue = p->nStep; if( p->nTotal>1 ){ - double r = (double)(p->nValue-1) / (double)(p->nTotal-1); + double r = (double)p->nValue / (double)(p->nTotal-1); sqlite3_result_double(pCtx, r); }else{ sqlite3_result_double(pCtx, 0.0); } - p->nValue = 0; } } +#define percent_rankFinalizeFunc percent_rankValueFunc /* ** Implementation of built-in window function cume_dist(). Assumes that ** the window frame has been set to: ** -** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW +** GROUPS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING */ static void cume_distStepFunc( sqlite3_context *pCtx, @@ -144829,24 +145785,33 @@ static void cume_distStepFunc( sqlite3_value **apArg ){ struct CallCount *p; - assert( nArg==1 ); UNUSED_PARAMETER(nArg); - + UNUSED_PARAMETER(nArg); assert( nArg==0 ); + UNUSED_PARAMETER(apArg); p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p)); if( p ){ - if( p->nTotal==0 ){ - p->nTotal = sqlite3_value_int64(apArg[0]); - } - p->nStep++; + p->nTotal++; } } -static void cume_distValueFunc(sqlite3_context *pCtx){ +static void cume_distInvFunc( + sqlite3_context *pCtx, + int nArg, + sqlite3_value **apArg +){ struct CallCount *p; + UNUSED_PARAMETER(nArg); assert( nArg==0 ); + UNUSED_PARAMETER(apArg); p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p)); - if( p && p->nTotal ){ + p->nStep++; +} +static void cume_distValueFunc(sqlite3_context *pCtx){ + struct CallCount *p; + p = (struct CallCount*)sqlite3_aggregate_context(pCtx, 0); + if( p ){ double r = (double)(p->nStep) / (double)(p->nTotal); sqlite3_result_double(pCtx, r); } } +#define cume_distFinalizeFunc cume_distValueFunc /* ** Context object for ntile() window function. @@ -144861,7 +145826,7 @@ struct NtileCtx { ** Implementation of ntile(). This assumes that the window frame has ** been coerced to: ** -** ROWS UNBOUNDED PRECEDING AND CURRENT ROW +** ROWS CURRENT ROW AND UNBOUNDED FOLLOWING */ static void ntileStepFunc( sqlite3_context *pCtx, @@ -144869,32 +145834,42 @@ static void ntileStepFunc( sqlite3_value **apArg ){ struct NtileCtx *p; - assert( nArg==2 ); UNUSED_PARAMETER(nArg); + assert( nArg==1 ); UNUSED_PARAMETER(nArg); p = (struct NtileCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p)); if( p ){ if( p->nTotal==0 ){ p->nParam = sqlite3_value_int64(apArg[0]); - p->nTotal = sqlite3_value_int64(apArg[1]); if( p->nParam<=0 ){ sqlite3_result_error( pCtx, "argument of ntile must be a positive integer", -1 ); } } - p->iRow++; + p->nTotal++; } } +static void ntileInvFunc( + sqlite3_context *pCtx, + int nArg, + sqlite3_value **apArg +){ + struct NtileCtx *p; + assert( nArg==1 ); UNUSED_PARAMETER(nArg); + UNUSED_PARAMETER(apArg); + p = (struct NtileCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p)); + p->iRow++; +} static void ntileValueFunc(sqlite3_context *pCtx){ struct NtileCtx *p; p = (struct NtileCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p)); if( p && p->nParam>0 ){ int nSize = (p->nTotal / p->nParam); if( nSize==0 ){ - sqlite3_result_int64(pCtx, p->iRow); + sqlite3_result_int64(pCtx, p->iRow+1); }else{ i64 nLarge = p->nTotal - p->nParam*nSize; i64 iSmall = nLarge*(nSize+1); - i64 iRow = p->iRow-1; + i64 iRow = p->iRow; assert( (nLarge*(nSize+1) + (p->nParam-nLarge)*nSize)==p->nTotal ); @@ -144906,6 +145881,7 @@ static void ntileValueFunc(sqlite3_context *pCtx){ } } } +#define ntileFinalizeFunc ntileValueFunc /* ** Context object for last_value() window function. @@ -144955,7 +145931,7 @@ static void last_valueInvFunc( } static void last_valueValueFunc(sqlite3_context *pCtx){ struct LastValueCtx *p; - p = (struct LastValueCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p)); + p = (struct LastValueCtx*)sqlite3_aggregate_context(pCtx, 0); if( p && p->pVal ){ sqlite3_result_value(pCtx, p->pVal); } @@ -145045,12 +146021,12 @@ SQLITE_PRIVATE void sqlite3WindowFunctions(void){ WINDOWFUNCX(row_number, 0, 0), WINDOWFUNCX(dense_rank, 0, 0), WINDOWFUNCX(rank, 0, 0), - WINDOWFUNCX(percent_rank, 0, SQLITE_FUNC_WINDOW_SIZE), - WINDOWFUNCX(cume_dist, 0, SQLITE_FUNC_WINDOW_SIZE), - WINDOWFUNCX(ntile, 1, SQLITE_FUNC_WINDOW_SIZE), + WINDOWFUNCALL(percent_rank, 0, 0), + WINDOWFUNCALL(cume_dist, 0, 0), + WINDOWFUNCALL(ntile, 1, 0), WINDOWFUNCALL(last_value, 1, 0), - WINDOWFUNCNOOP(nth_value, 2, 0), - WINDOWFUNCNOOP(first_value, 1, 0), + WINDOWFUNCALL(nth_value, 2, 0), + WINDOWFUNCALL(first_value, 1, 0), WINDOWFUNCNOOP(lead, 1, 0), WINDOWFUNCNOOP(lead, 2, 0), WINDOWFUNCNOOP(lead, 3, 0), @@ -145061,6 +146037,17 @@ SQLITE_PRIVATE void sqlite3WindowFunctions(void){ sqlite3InsertBuiltinFuncs(aWindowFuncs, ArraySize(aWindowFuncs)); } +static Window *windowFind(Parse *pParse, Window *pList, const char *zName){ + Window *p; + for(p=pList; p; p=p->pNextWin){ + if( sqlite3StrICmp(p->zName, zName)==0 ) break; + } + if( p==0 ){ + sqlite3ErrorMsg(pParse, "no such window: %s", zName); + } + return p; +} + /* ** This function is called immediately after resolving the function name ** for a window function within a SELECT statement. Argument pList is a @@ -145084,48 +146071,66 @@ SQLITE_PRIVATE void sqlite3WindowUpdate( Window *pWin, /* Window frame to update */ FuncDef *pFunc /* Window function definition */ ){ - if( pWin->zName && pWin->eType==0 ){ - Window *p; - for(p=pList; p; p=p->pNextWin){ - if( sqlite3StrICmp(p->zName, pWin->zName)==0 ) break; - } - if( p==0 ){ - sqlite3ErrorMsg(pParse, "no such window: %s", pWin->zName); - return; - } + if( pWin->zName && pWin->eFrmType==0 ){ + Window *p = windowFind(pParse, pList, pWin->zName); + if( p==0 ) return; pWin->pPartition = sqlite3ExprListDup(pParse->db, p->pPartition, 0); pWin->pOrderBy = sqlite3ExprListDup(pParse->db, p->pOrderBy, 0); pWin->pStart = sqlite3ExprDup(pParse->db, p->pStart, 0); pWin->pEnd = sqlite3ExprDup(pParse->db, p->pEnd, 0); pWin->eStart = p->eStart; pWin->eEnd = p->eEnd; - pWin->eType = p->eType; + pWin->eFrmType = p->eFrmType; + pWin->eExclude = p->eExclude; + }else{ + sqlite3WindowChain(pParse, pWin, pList); } + if( (pWin->eFrmType==TK_RANGE) + && (pWin->pStart || pWin->pEnd) + && (pWin->pOrderBy==0 || pWin->pOrderBy->nExpr!=1) + ){ + sqlite3ErrorMsg(pParse, + "RANGE with offset PRECEDING/FOLLOWING requires one ORDER BY expression" + ); + }else if( pFunc->funcFlags & SQLITE_FUNC_WINDOW ){ sqlite3 *db = pParse->db; if( pWin->pFilter ){ sqlite3ErrorMsg(pParse, "FILTER clause may only be used with aggregate window functions" ); - }else - if( pFunc->zName==row_numberName || pFunc->zName==ntileName ){ - sqlite3ExprDelete(db, pWin->pStart); - sqlite3ExprDelete(db, pWin->pEnd); - pWin->pStart = pWin->pEnd = 0; - pWin->eType = TK_ROWS; - pWin->eStart = TK_UNBOUNDED; - pWin->eEnd = TK_CURRENT; - }else - - if( pFunc->zName==dense_rankName || pFunc->zName==rankName - || pFunc->zName==percent_rankName || pFunc->zName==cume_distName - ){ - sqlite3ExprDelete(db, pWin->pStart); - sqlite3ExprDelete(db, pWin->pEnd); - pWin->pStart = pWin->pEnd = 0; - pWin->eType = TK_RANGE; - pWin->eStart = TK_UNBOUNDED; - pWin->eEnd = TK_CURRENT; + }else{ + struct WindowUpdate { + const char *zFunc; + int eFrmType; + int eStart; + int eEnd; + } aUp[] = { + { row_numberName, TK_ROWS, TK_UNBOUNDED, TK_CURRENT }, + { dense_rankName, TK_RANGE, TK_UNBOUNDED, TK_CURRENT }, + { rankName, TK_RANGE, TK_UNBOUNDED, TK_CURRENT }, + { percent_rankName, TK_GROUPS, TK_CURRENT, TK_UNBOUNDED }, + { cume_distName, TK_GROUPS, TK_FOLLOWING, TK_UNBOUNDED }, + { ntileName, TK_ROWS, TK_CURRENT, TK_UNBOUNDED }, + { leadName, TK_ROWS, TK_UNBOUNDED, TK_UNBOUNDED }, + { lagName, TK_ROWS, TK_UNBOUNDED, TK_CURRENT }, + }; + int i; + for(i=0; izName==aUp[i].zFunc ){ + sqlite3ExprDelete(db, pWin->pStart); + sqlite3ExprDelete(db, pWin->pEnd); + pWin->pEnd = pWin->pStart = 0; + pWin->eFrmType = aUp[i].eFrmType; + pWin->eStart = aUp[i].eStart; + pWin->eEnd = aUp[i].eEnd; + pWin->eExclude = 0; + if( pWin->eStart==TK_FOLLOWING ){ + pWin->pStart = sqlite3Expr(db, TK_INTEGER, "1"); + } + break; + } + } } } pWin->pFunc = pFunc; @@ -145330,6 +146335,7 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){ ** The OpenEphemeral instruction is coded later, after it is known how ** many columns the table will have. */ pMWin->iEphCsr = pParse->nTab++; + pParse->nTab += 3; selectWindowRewriteEList(pParse, pMWin, pSrc, p->pEList, &pSublist); selectWindowRewriteEList(pParse, pMWin, pSrc, p->pOrderBy, &pSublist); @@ -145372,8 +146378,7 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){ pSub = sqlite3SelectNew( pParse, pSublist, pSrc, pWhere, pGroupBy, pHaving, pSort, 0, 0 ); - p->pSrc = sqlite3SrcListAppend(db, 0, 0, 0); - assert( p->pSrc || db->mallocFailed ); + p->pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0); if( p->pSrc ){ p->pSrc->a[0].pSelect = pSub; sqlite3SrcListAssignCursors(pParse, p->pSrc); @@ -145386,6 +146391,9 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){ } sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pMWin->iEphCsr, pSublist->nExpr); + sqlite3VdbeAddOp2(v, OP_OpenDup, pMWin->iEphCsr+1, pMWin->iEphCsr); + sqlite3VdbeAddOp2(v, OP_OpenDup, pMWin->iEphCsr+2, pMWin->iEphCsr); + sqlite3VdbeAddOp2(v, OP_OpenDup, pMWin->iEphCsr+3, pMWin->iEphCsr); }else{ sqlite3SelectDelete(db, pSub); } @@ -145406,6 +146414,7 @@ SQLITE_PRIVATE void sqlite3WindowDelete(sqlite3 *db, Window *p){ sqlite3ExprDelete(db, p->pEnd); sqlite3ExprDelete(db, p->pStart); sqlite3DbFree(db, p->zName); + sqlite3DbFree(db, p->zBase); sqlite3DbFree(db, p); } } @@ -145430,6 +146439,7 @@ SQLITE_PRIVATE void sqlite3WindowListDelete(sqlite3 *db, Window *p){ */ static Expr *sqlite3WindowOffsetExpr(Parse *pParse, Expr *pExpr){ if( 0==sqlite3ExprIsConstant(pExpr) ){ + if( IN_RENAME_OBJECT ) sqlite3RenameExprUnmap(pParse, pExpr); sqlite3ExprDelete(pParse->db, pExpr); pExpr = sqlite3ExprAlloc(pParse->db, TK_NULL, 0, 0); } @@ -145441,16 +146451,18 @@ static Expr *sqlite3WindowOffsetExpr(Parse *pParse, Expr *pExpr){ */ SQLITE_PRIVATE Window *sqlite3WindowAlloc( Parse *pParse, /* Parsing context */ - int eType, /* Frame type. TK_RANGE or TK_ROWS */ + int eType, /* Frame type. TK_RANGE, TK_ROWS, TK_GROUPS, or 0 */ int eStart, /* Start type: CURRENT, PRECEDING, FOLLOWING, UNBOUNDED */ Expr *pStart, /* Start window size if TK_PRECEDING or FOLLOWING */ int eEnd, /* End type: CURRENT, FOLLOWING, TK_UNBOUNDED, PRECEDING */ - Expr *pEnd /* End window size if TK_FOLLOWING or PRECEDING */ + Expr *pEnd, /* End window size if TK_FOLLOWING or PRECEDING */ + u8 eExclude /* EXCLUDE clause */ ){ Window *pWin = 0; + int bImplicitFrame = 0; /* Parser assures the following: */ - assert( eType==TK_RANGE || eType==TK_ROWS ); + assert( eType==0 || eType==TK_RANGE || eType==TK_ROWS || eType==TK_GROUPS ); assert( eStart==TK_CURRENT || eStart==TK_PRECEDING || eStart==TK_UNBOUNDED || eStart==TK_FOLLOWING ); assert( eEnd==TK_CURRENT || eEnd==TK_FOLLOWING @@ -145458,13 +146470,9 @@ SQLITE_PRIVATE Window *sqlite3WindowAlloc( assert( (eStart==TK_PRECEDING || eStart==TK_FOLLOWING)==(pStart!=0) ); assert( (eEnd==TK_FOLLOWING || eEnd==TK_PRECEDING)==(pEnd!=0) ); - - /* If a frame is declared "RANGE" (not "ROWS"), then it may not use - ** either " PRECEDING" or " FOLLOWING". - */ - if( eType==TK_RANGE && (pStart!=0 || pEnd!=0) ){ - sqlite3ErrorMsg(pParse, "RANGE must use only UNBOUNDED or CURRENT ROW"); - goto windowAllocErr; + if( eType==0 ){ + bImplicitFrame = 1; + eType = TK_RANGE; } /* Additionally, the @@ -145484,15 +146492,20 @@ SQLITE_PRIVATE Window *sqlite3WindowAlloc( if( (eStart==TK_CURRENT && eEnd==TK_PRECEDING) || (eStart==TK_FOLLOWING && (eEnd==TK_PRECEDING || eEnd==TK_CURRENT)) ){ - sqlite3ErrorMsg(pParse, "unsupported frame delimiter for ROWS"); + sqlite3ErrorMsg(pParse, "unsupported frame specification"); goto windowAllocErr; } pWin = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); if( pWin==0 ) goto windowAllocErr; - pWin->eType = eType; + pWin->eFrmType = eType; pWin->eStart = eStart; pWin->eEnd = eEnd; + if( eExclude==0 && OptimizationDisabled(pParse->db, SQLITE_WindowFunc) ){ + eExclude = TK_NO; + } + pWin->eExclude = eExclude; + pWin->bImplicitFrame = bImplicitFrame; pWin->pEnd = sqlite3WindowOffsetExpr(pParse, pEnd); pWin->pStart = sqlite3WindowOffsetExpr(pParse, pStart); return pWin; @@ -145503,6 +146516,69 @@ windowAllocErr: return 0; } +/* +** Attach PARTITION and ORDER BY clauses pPartition and pOrderBy to window +** pWin. Also, if parameter pBase is not NULL, set pWin->zBase to the +** equivalent nul-terminated string. +*/ +SQLITE_PRIVATE Window *sqlite3WindowAssemble( + Parse *pParse, + Window *pWin, + ExprList *pPartition, + ExprList *pOrderBy, + Token *pBase +){ + if( pWin ){ + pWin->pPartition = pPartition; + pWin->pOrderBy = pOrderBy; + if( pBase ){ + pWin->zBase = sqlite3DbStrNDup(pParse->db, pBase->z, pBase->n); + } + }else{ + sqlite3ExprListDelete(pParse->db, pPartition); + sqlite3ExprListDelete(pParse->db, pOrderBy); + } + return pWin; +} + +/* +** Window *pWin has just been created from a WINDOW clause. Tokne pBase +** is the base window. Earlier windows from the same WINDOW clause are +** stored in the linked list starting at pWin->pNextWin. This function +** either updates *pWin according to the base specification, or else +** leaves an error in pParse. +*/ +SQLITE_PRIVATE void sqlite3WindowChain(Parse *pParse, Window *pWin, Window *pList){ + if( pWin->zBase ){ + sqlite3 *db = pParse->db; + Window *pExist = windowFind(pParse, pList, pWin->zBase); + if( pExist ){ + const char *zErr = 0; + /* Check for errors */ + if( pWin->pPartition ){ + zErr = "PARTITION clause"; + }else if( pExist->pOrderBy && pWin->pOrderBy ){ + zErr = "ORDER BY clause"; + }else if( pExist->bImplicitFrame==0 ){ + zErr = "frame specification"; + } + if( zErr ){ + sqlite3ErrorMsg(pParse, + "cannot override %s of window: %s", zErr, pWin->zBase + ); + }else{ + pWin->pPartition = sqlite3ExprListDup(db, pExist->pPartition, 0); + if( pExist->pOrderBy ){ + assert( pWin->pOrderBy==0 ); + pWin->pOrderBy = sqlite3ExprListDup(db, pExist->pOrderBy, 0); + } + sqlite3DbFree(db, pWin->zBase); + pWin->zBase = 0; + } + } + } +} + /* ** Attach window object pWin to expression p. */ @@ -145531,9 +146607,10 @@ SQLITE_PRIVATE void sqlite3WindowAttach(Parse *pParse, Expr *p, Window *pWin){ ** Identical window objects can be processed in a single scan. */ SQLITE_PRIVATE int sqlite3WindowCompare(Parse *pParse, Window *p1, Window *p2){ - if( p1->eType!=p2->eType ) return 1; + if( p1->eFrmType!=p2->eFrmType ) return 1; if( p1->eStart!=p2->eStart ) return 1; if( p1->eEnd!=p2->eEnd ) return 1; + if( p1->eExclude!=p2->eExclude ) return 1; if( sqlite3ExprCompare(pParse, p1->pStart, p2->pStart, -1) ) return 1; if( sqlite3ExprCompare(pParse, p1->pEnd, p2->pEnd, -1) ) return 1; if( sqlite3ExprListCompare(p1->pPartition, p2->pPartition, -1) ) return 1; @@ -145550,12 +146627,27 @@ SQLITE_PRIVATE int sqlite3WindowCompare(Parse *pParse, Window *p1, Window *p2){ SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse *pParse, Window *pMWin){ Window *pWin; Vdbe *v = sqlite3GetVdbe(pParse); - int nPart = (pMWin->pPartition ? pMWin->pPartition->nExpr : 0); - nPart += (pMWin->pOrderBy ? pMWin->pOrderBy->nExpr : 0); - if( nPart ){ + + /* Allocate registers to use for PARTITION BY values, if any. Initialize + ** said registers to NULL. */ + if( pMWin->pPartition ){ + int nExpr = pMWin->pPartition->nExpr; pMWin->regPart = pParse->nMem+1; - pParse->nMem += nPart; - sqlite3VdbeAddOp3(v, OP_Null, 0, pMWin->regPart, pMWin->regPart+nPart-1); + pParse->nMem += nExpr; + sqlite3VdbeAddOp3(v, OP_Null, 0, pMWin->regPart, pMWin->regPart+nExpr-1); + } + + pMWin->regOne = ++pParse->nMem; + sqlite3VdbeAddOp2(v, OP_Integer, 1, pMWin->regOne); + + if( pMWin->eExclude ){ + pMWin->regStartRowid = ++pParse->nMem; + pMWin->regEndRowid = ++pParse->nMem; + pMWin->csrApp = pParse->nTab++; + sqlite3VdbeAddOp2(v, OP_Integer, 1, pMWin->regStartRowid); + sqlite3VdbeAddOp2(v, OP_Integer, 0, pMWin->regEndRowid); + sqlite3VdbeAddOp2(v, OP_OpenDup, pMWin->csrApp, pMWin->iEphCsr); + return; } for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ @@ -145584,20 +146676,24 @@ SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse *pParse, Window *pMWin){ else if( p->zName==nth_valueName || p->zName==first_valueName ){ /* Allocate two registers at pWin->regApp. These will be used to ** store the start and end index of the current frame. */ - assert( pMWin->iEphCsr ); pWin->regApp = pParse->nMem+1; pWin->csrApp = pParse->nTab++; pParse->nMem += 2; sqlite3VdbeAddOp2(v, OP_OpenDup, pWin->csrApp, pMWin->iEphCsr); } else if( p->zName==leadName || p->zName==lagName ){ - assert( pMWin->iEphCsr ); pWin->csrApp = pParse->nTab++; sqlite3VdbeAddOp2(v, OP_OpenDup, pWin->csrApp, pMWin->iEphCsr); } } } +#define WINDOW_STARTING_INT 0 +#define WINDOW_ENDING_INT 1 +#define WINDOW_NTH_VALUE_INT 2 +#define WINDOW_STARTING_NUM 3 +#define WINDOW_ENDING_NUM 4 + /* ** A "PRECEDING " (eCond==0) or "FOLLOWING " (eCond==1) or the ** value of the second argument to nth_value() (eCond==2) has just been @@ -145605,25 +146701,43 @@ SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse *pParse, Window *pMWin){ ** code to check that the value is a non-negative integer and throws an ** exception if it is not. */ -static void windowCheckIntValue(Parse *pParse, int reg, int eCond){ +static void windowCheckValue(Parse *pParse, int reg, int eCond){ static const char *azErr[] = { "frame starting offset must be a non-negative integer", "frame ending offset must be a non-negative integer", - "second argument to nth_value must be a positive integer" + "second argument to nth_value must be a positive integer", + "frame starting offset must be a non-negative number", + "frame ending offset must be a non-negative number", }; - static int aOp[] = { OP_Ge, OP_Ge, OP_Gt }; + static int aOp[] = { OP_Ge, OP_Ge, OP_Gt, OP_Ge, OP_Ge }; Vdbe *v = sqlite3GetVdbe(pParse); int regZero = sqlite3GetTempReg(pParse); - assert( eCond==0 || eCond==1 || eCond==2 ); + assert( eCond>=0 && eCond=WINDOW_STARTING_NUM ){ + int regString = sqlite3GetTempReg(pParse); + sqlite3VdbeAddOp4(v, OP_String8, 0, regString, 0, "", P4_STATIC); + sqlite3VdbeAddOp3(v, OP_Ge, regString, sqlite3VdbeCurrentAddr(v)+2, reg); + sqlite3VdbeChangeP5(v, SQLITE_AFF_NUMERIC|SQLITE_JUMPIFNULL); + VdbeCoverage(v); + assert( eCond==3 || eCond==4 ); + VdbeCoverageIf(v, eCond==3); + VdbeCoverageIf(v, eCond==4); + }else{ + sqlite3VdbeAddOp2(v, OP_MustBeInt, reg, sqlite3VdbeCurrentAddr(v)+2); + VdbeCoverage(v); + assert( eCond==0 || eCond==1 || eCond==2 ); + VdbeCoverageIf(v, eCond==0); + VdbeCoverageIf(v, eCond==1); + VdbeCoverageIf(v, eCond==2); + } sqlite3VdbeAddOp3(v, aOp[eCond], regZero, sqlite3VdbeCurrentAddr(v)+2, reg); - VdbeCoverageNeverNullIf(v, eCond==0); - VdbeCoverageNeverNullIf(v, eCond==1); + VdbeCoverageNeverNullIf(v, eCond==0); /* NULL case captured by */ + VdbeCoverageNeverNullIf(v, eCond==1); /* the OP_MustBeInt */ VdbeCoverageNeverNullIf(v, eCond==2); + VdbeCoverageNeverNullIf(v, eCond==3); /* NULL case caught by */ + VdbeCoverageNeverNullIf(v, eCond==4); /* the OP_Ge */ + sqlite3MayAbort(pParse); sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_ERROR, OE_Abort); sqlite3VdbeAppendP4(v, (void*)azErr[eCond], P4_STATIC); sqlite3ReleaseTempReg(pParse, regZero); @@ -145662,37 +146776,28 @@ static void windowAggStep( Window *pMWin, /* Linked list of window functions */ int csr, /* Read arguments from this cursor */ int bInverse, /* True to invoke xInverse instead of xStep */ - int reg, /* Array of registers */ - int regPartSize /* Register containing size of partition */ + int reg /* Array of registers */ ){ Vdbe *v = sqlite3GetVdbe(pParse); Window *pWin; for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ - int flags = pWin->pFunc->funcFlags; + FuncDef *pFunc = pWin->pFunc; int regArg; int nArg = windowArgCount(pWin); + int i; - if( csr>=0 ){ - int i; - for(i=0; izName!=nth_valueName ){ sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+i, reg+i); + }else{ + sqlite3VdbeAddOp3(v, OP_Column, pMWin->iEphCsr, pWin->iArgCol+i, reg+i); } - regArg = reg; - if( flags & SQLITE_FUNC_WINDOW_SIZE ){ - if( nArg==0 ){ - regArg = regPartSize; - }else{ - sqlite3VdbeAddOp2(v, OP_SCopy, regPartSize, reg+nArg); - } - nArg++; - } - }else{ - assert( !(flags & SQLITE_FUNC_WINDOW_SIZE) ); - regArg = reg + pWin->iArgCol; } + regArg = reg; - if( (pWin->pFunc->funcFlags & SQLITE_FUNC_MINMAX) - && pWin->eStart!=TK_UNBOUNDED + if( pMWin->regStartRowid==0 + && (pFunc->funcFlags & SQLITE_FUNC_MINMAX) + && (pWin->eStart!=TK_UNBOUNDED) ){ int addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, regArg); VdbeCoverage(v); @@ -145709,34 +146814,24 @@ static void windowAggStep( } sqlite3VdbeJumpHere(v, addrIsNull); }else if( pWin->regApp ){ - assert( pWin->pFunc->zName==nth_valueName - || pWin->pFunc->zName==first_valueName + assert( pFunc->zName==nth_valueName + || pFunc->zName==first_valueName ); assert( bInverse==0 || bInverse==1 ); sqlite3VdbeAddOp2(v, OP_AddImm, pWin->regApp+1-bInverse, 1); - }else if( pWin->pFunc->zName==leadName - || pWin->pFunc->zName==lagName - ){ - /* no-op */ - }else{ + }else if( pFunc->xSFunc!=noopStepFunc ){ int addrIf = 0; if( pWin->pFilter ){ int regTmp; assert( nArg==0 || nArg==pWin->pOwner->x.pList->nExpr ); assert( nArg || pWin->pOwner->x.pList==0 ); - if( csr>0 ){ - regTmp = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+nArg,regTmp); - }else{ - regTmp = regArg + nArg; - } + regTmp = sqlite3GetTempReg(pParse); + sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+nArg,regTmp); addrIf = sqlite3VdbeAddOp3(v, OP_IfNot, regTmp, 0, 1); VdbeCoverage(v); - if( csr>0 ){ - sqlite3ReleaseTempReg(pParse, regTmp); - } + sqlite3ReleaseTempReg(pParse, regTmp); } - if( pWin->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){ + if( pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){ CollSeq *pColl; assert( nArg>0 ); pColl = sqlite3ExprNNCollSeq(pParse, pWin->pOwner->x.pList->a[0].pExpr); @@ -145744,45 +146839,96 @@ static void windowAggStep( } sqlite3VdbeAddOp3(v, bInverse? OP_AggInverse : OP_AggStep, bInverse, regArg, pWin->regAccum); - sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF); + sqlite3VdbeAppendP4(v, pFunc, P4_FUNCDEF); sqlite3VdbeChangeP5(v, (u8)nArg); if( addrIf ) sqlite3VdbeJumpHere(v, addrIf); } } } +typedef struct WindowCodeArg WindowCodeArg; +typedef struct WindowCsrAndReg WindowCsrAndReg; +struct WindowCsrAndReg { + int csr; + int reg; +}; + +struct WindowCodeArg { + Parse *pParse; + Window *pMWin; + Vdbe *pVdbe; + int regGosub; + int addrGosub; + int regArg; + int eDelete; + + WindowCsrAndReg start; + WindowCsrAndReg current; + WindowCsrAndReg end; +}; + +/* +** Values that may be passed as the second argument to windowCodeOp(). +*/ +#define WINDOW_RETURN_ROW 1 +#define WINDOW_AGGINVERSE 2 +#define WINDOW_AGGSTEP 3 + +/* +** Generate VM code to read the window frames peer values from cursor csr into +** an array of registers starting at reg. +*/ +static void windowReadPeerValues( + WindowCodeArg *p, + int csr, + int reg +){ + Window *pMWin = p->pMWin; + ExprList *pOrderBy = pMWin->pOrderBy; + if( pOrderBy ){ + Vdbe *v = sqlite3GetVdbe(p->pParse); + ExprList *pPart = pMWin->pPartition; + int iColOff = pMWin->nBufferCol + (pPart ? pPart->nExpr : 0); + int i; + for(i=0; inExpr; i++){ + sqlite3VdbeAddOp3(v, OP_Column, csr, iColOff+i, reg+i); + } + } +} + /* -** Generate VM code to invoke either xValue() (bFinal==0) or xFinalize() -** (bFinal==1) for each window function in the linked list starting at +** Generate VM code to invoke either xValue() (bFin==0) or xFinalize() +** (bFin==1) for each window function in the linked list starting at ** pMWin. Or, for built-in window-functions that do not use the standard ** API, generate the equivalent VM code. */ -static void windowAggFinal(Parse *pParse, Window *pMWin, int bFinal){ +static void windowAggFinal(WindowCodeArg *p, int bFin){ + Parse *pParse = p->pParse; + Window *pMWin = p->pMWin; Vdbe *v = sqlite3GetVdbe(pParse); Window *pWin; for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ - if( (pWin->pFunc->funcFlags & SQLITE_FUNC_MINMAX) - && pWin->eStart!=TK_UNBOUNDED + if( pMWin->regStartRowid==0 + && (pWin->pFunc->funcFlags & SQLITE_FUNC_MINMAX) + && (pWin->eStart!=TK_UNBOUNDED) ){ sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult); sqlite3VdbeAddOp1(v, OP_Last, pWin->csrApp); VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_Column, pWin->csrApp, 0, pWin->regResult); sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2); - if( bFinal ){ - sqlite3VdbeAddOp1(v, OP_ResetSorter, pWin->csrApp); - } }else if( pWin->regApp ){ + assert( pMWin->regStartRowid==0 ); }else{ - if( bFinal ){ - sqlite3VdbeAddOp2(v, OP_AggFinal, pWin->regAccum, windowArgCount(pWin)); + int nArg = windowArgCount(pWin); + if( bFin ){ + sqlite3VdbeAddOp2(v, OP_AggFinal, pWin->regAccum, nArg); sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF); sqlite3VdbeAddOp2(v, OP_Copy, pWin->regAccum, pWin->regResult); sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum); }else{ - sqlite3VdbeAddOp3(v, OP_AggValue, pWin->regAccum, windowArgCount(pWin), - pWin->regResult); + sqlite3VdbeAddOp3(v, OP_AggValue,pWin->regAccum,nArg,pWin->regResult); sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF); } } @@ -145790,66 +146936,97 @@ static void windowAggFinal(Parse *pParse, Window *pMWin, int bFinal){ } /* -** This function generates VM code to invoke the sub-routine at address -** lblFlushPart once for each partition with the entire partition cached in -** the Window.iEphCsr temp table. +** Generate code to calculate the current values of all window functions in the +** p->pMWin list by doing a full scan of the current window frame. Store the +** results in the Window.regResult registers, ready to return the upper +** layer. */ -static void windowPartitionCache( - Parse *pParse, - Select *p, /* The rewritten SELECT statement */ - WhereInfo *pWInfo, /* WhereInfo to call WhereEnd() on */ - int regFlushPart, /* Register to use with Gosub lblFlushPart */ - int lblFlushPart, /* Subroutine to Gosub to */ - int *pRegSize /* OUT: Register containing partition size */ -){ - Window *pMWin = p->pWin; - Vdbe *v = sqlite3GetVdbe(pParse); - int iSubCsr = p->pSrc->a[0].iCursor; - int nSub = p->pSrc->a[0].pTab->nCol; - int k; +static void windowFullScan(WindowCodeArg *p){ + Window *pWin; + Parse *pParse = p->pParse; + Window *pMWin = p->pMWin; + Vdbe *v = p->pVdbe; - int reg = pParse->nMem+1; - int regRecord = reg+nSub; - int regRowid = regRecord+1; + int regCRowid = 0; /* Current rowid value */ + int regCPeer = 0; /* Current peer values */ + int regRowid = 0; /* AggStep rowid value */ + int regPeer = 0; /* AggStep peer values */ - *pRegSize = regRowid; - pParse->nMem += nSub + 2; + int nPeer; + int lblNext; + int lblBrk; + int addrNext; + int csr = pMWin->csrApp; - /* Load the column values for the row returned by the sub-select - ** into an array of registers starting at reg. */ - for(k=0; kpOrderBy ? pMWin->pOrderBy->nExpr : 0); + + lblNext = sqlite3VdbeMakeLabel(pParse); + lblBrk = sqlite3VdbeMakeLabel(pParse); + + regCRowid = sqlite3GetTempReg(pParse); + regRowid = sqlite3GetTempReg(pParse); + if( nPeer ){ + regCPeer = sqlite3GetTempRange(pParse, nPeer); + regPeer = sqlite3GetTempRange(pParse, nPeer); } - sqlite3VdbeAddOp3(v, OP_MakeRecord, reg, nSub, regRecord); - /* Check if this is the start of a new partition. If so, call the - ** flush_partition sub-routine. */ - if( pMWin->pPartition ){ + sqlite3VdbeAddOp2(v, OP_Rowid, pMWin->iEphCsr, regCRowid); + windowReadPeerValues(p, pMWin->iEphCsr, regCPeer); + + for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ + sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum); + } + + sqlite3VdbeAddOp3(v, OP_SeekGE, csr, lblBrk, pMWin->regStartRowid); + VdbeCoverage(v); + addrNext = sqlite3VdbeCurrentAddr(v); + sqlite3VdbeAddOp2(v, OP_Rowid, csr, regRowid); + sqlite3VdbeAddOp3(v, OP_Gt, pMWin->regEndRowid, lblBrk, regRowid); + VdbeCoverageNeverNull(v); + + if( pMWin->eExclude==TK_CURRENT ){ + sqlite3VdbeAddOp3(v, OP_Eq, regCRowid, lblNext, regRowid); + VdbeCoverageNeverNull(v); + }else if( pMWin->eExclude!=TK_NO ){ int addr; - ExprList *pPart = pMWin->pPartition; - int nPart = pPart->nExpr; - int regNewPart = reg + pMWin->nBufferCol; - KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pPart, 0, 0); + int addrEq = 0; + KeyInfo *pKeyInfo = 0; - addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPart, pMWin->regPart,nPart); - sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO); - sqlite3VdbeAddOp3(v, OP_Jump, addr+2, addr+4, addr+2); - VdbeCoverageEqNe(v); - sqlite3VdbeAddOp3(v, OP_Copy, regNewPart, pMWin->regPart, nPart-1); - sqlite3VdbeAddOp2(v, OP_Gosub, regFlushPart, lblFlushPart); - VdbeComment((v, "call flush_partition")); + if( pMWin->pOrderBy ){ + pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pMWin->pOrderBy, 0, 0); + } + if( pMWin->eExclude==TK_TIES ){ + addrEq = sqlite3VdbeAddOp3(v, OP_Eq, regCRowid, 0, regRowid); + VdbeCoverageNeverNull(v); + } + if( pKeyInfo ){ + windowReadPeerValues(p, csr, regPeer); + sqlite3VdbeAddOp3(v, OP_Compare, regPeer, regCPeer, nPeer); + sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO); + addr = sqlite3VdbeCurrentAddr(v)+1; + sqlite3VdbeAddOp3(v, OP_Jump, addr, lblNext, addr); + VdbeCoverageEqNe(v); + }else{ + sqlite3VdbeAddOp2(v, OP_Goto, 0, lblNext); + } + if( addrEq ) sqlite3VdbeJumpHere(v, addrEq); } - /* Buffer the current row in the ephemeral table. */ - sqlite3VdbeAddOp2(v, OP_NewRowid, pMWin->iEphCsr, regRowid); - sqlite3VdbeAddOp3(v, OP_Insert, pMWin->iEphCsr, regRecord, regRowid); + windowAggStep(pParse, pMWin, csr, 0, p->regArg); - /* End of the input loop */ - sqlite3WhereEnd(pWInfo); + sqlite3VdbeResolveLabel(v, lblNext); + sqlite3VdbeAddOp2(v, OP_Next, csr, addrNext); + VdbeCoverage(v); + sqlite3VdbeJumpHere(v, addrNext-1); + sqlite3VdbeJumpHere(v, addrNext+1); + sqlite3ReleaseTempReg(pParse, regRowid); + sqlite3ReleaseTempReg(pParse, regCRowid); + if( nPeer ){ + sqlite3ReleaseTempRange(pParse, regPeer, nPeer); + sqlite3ReleaseTempRange(pParse, regCPeer, nPeer); + } - /* Invoke "flush_partition" to deal with the final (or only) partition */ - sqlite3VdbeAddOp2(v, OP_Gosub, regFlushPart, lblFlushPart); - VdbeComment((v, "call flush_partition")); + windowAggFinal(p, 1); } /* @@ -145865,110 +147042,74 @@ static void windowPartitionCache( ** lag() ** lead() */ -static void windowReturnOneRow( - Parse *pParse, - Window *pMWin, - int regGosub, - int addrGosub -){ - Vdbe *v = sqlite3GetVdbe(pParse); - Window *pWin; - for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ - FuncDef *pFunc = pWin->pFunc; - if( pFunc->zName==nth_valueName - || pFunc->zName==first_valueName - ){ - int csr = pWin->csrApp; - int lbl = sqlite3VdbeMakeLabel(v); - int tmpReg = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult); +static void windowReturnOneRow(WindowCodeArg *p){ + Window *pMWin = p->pMWin; + Vdbe *v = p->pVdbe; - if( pFunc->zName==nth_valueName ){ - sqlite3VdbeAddOp3(v, OP_Column, pMWin->iEphCsr, pWin->iArgCol+1,tmpReg); - windowCheckIntValue(pParse, tmpReg, 2); - }else{ - sqlite3VdbeAddOp2(v, OP_Integer, 1, tmpReg); - } - sqlite3VdbeAddOp3(v, OP_Add, tmpReg, pWin->regApp, tmpReg); - sqlite3VdbeAddOp3(v, OP_Gt, pWin->regApp+1, lbl, tmpReg); - VdbeCoverageNeverNull(v); - sqlite3VdbeAddOp3(v, OP_SeekRowid, csr, 0, tmpReg); - VdbeCoverageNeverTaken(v); - sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol, pWin->regResult); - sqlite3VdbeResolveLabel(v, lbl); - sqlite3ReleaseTempReg(pParse, tmpReg); - } - else if( pFunc->zName==leadName || pFunc->zName==lagName ){ - int nArg = pWin->pOwner->x.pList->nExpr; - int iEph = pMWin->iEphCsr; - int csr = pWin->csrApp; - int lbl = sqlite3VdbeMakeLabel(v); - int tmpReg = sqlite3GetTempReg(pParse); - - if( nArg<3 ){ + if( pMWin->regStartRowid ){ + windowFullScan(p); + }else{ + Parse *pParse = p->pParse; + Window *pWin; + + for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ + FuncDef *pFunc = pWin->pFunc; + if( pFunc->zName==nth_valueName + || pFunc->zName==first_valueName + ){ + int csr = pWin->csrApp; + int lbl = sqlite3VdbeMakeLabel(pParse); + int tmpReg = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult); - }else{ - sqlite3VdbeAddOp3(v, OP_Column, iEph, pWin->iArgCol+2, pWin->regResult); - } - sqlite3VdbeAddOp2(v, OP_Rowid, iEph, tmpReg); - if( nArg<2 ){ - int val = (pFunc->zName==leadName ? 1 : -1); - sqlite3VdbeAddOp2(v, OP_AddImm, tmpReg, val); - }else{ - int op = (pFunc->zName==leadName ? OP_Add : OP_Subtract); - int tmpReg2 = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp3(v, OP_Column, iEph, pWin->iArgCol+1, tmpReg2); - sqlite3VdbeAddOp3(v, op, tmpReg2, tmpReg, tmpReg); - sqlite3ReleaseTempReg(pParse, tmpReg2); + + if( pFunc->zName==nth_valueName ){ + sqlite3VdbeAddOp3(v, OP_Column,pMWin->iEphCsr,pWin->iArgCol+1,tmpReg); + windowCheckValue(pParse, tmpReg, 2); + }else{ + sqlite3VdbeAddOp2(v, OP_Integer, 1, tmpReg); + } + sqlite3VdbeAddOp3(v, OP_Add, tmpReg, pWin->regApp, tmpReg); + sqlite3VdbeAddOp3(v, OP_Gt, pWin->regApp+1, lbl, tmpReg); + VdbeCoverageNeverNull(v); + sqlite3VdbeAddOp3(v, OP_SeekRowid, csr, 0, tmpReg); + VdbeCoverageNeverTaken(v); + sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol, pWin->regResult); + sqlite3VdbeResolveLabel(v, lbl); + sqlite3ReleaseTempReg(pParse, tmpReg); + } + else if( pFunc->zName==leadName || pFunc->zName==lagName ){ + int nArg = pWin->pOwner->x.pList->nExpr; + int csr = pWin->csrApp; + int lbl = sqlite3VdbeMakeLabel(pParse); + int tmpReg = sqlite3GetTempReg(pParse); + int iEph = pMWin->iEphCsr; + + if( nArg<3 ){ + sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult); + }else{ + sqlite3VdbeAddOp3(v, OP_Column, iEph,pWin->iArgCol+2,pWin->regResult); + } + sqlite3VdbeAddOp2(v, OP_Rowid, iEph, tmpReg); + if( nArg<2 ){ + int val = (pFunc->zName==leadName ? 1 : -1); + sqlite3VdbeAddOp2(v, OP_AddImm, tmpReg, val); + }else{ + int op = (pFunc->zName==leadName ? OP_Add : OP_Subtract); + int tmpReg2 = sqlite3GetTempReg(pParse); + sqlite3VdbeAddOp3(v, OP_Column, iEph, pWin->iArgCol+1, tmpReg2); + sqlite3VdbeAddOp3(v, op, tmpReg2, tmpReg, tmpReg); + sqlite3ReleaseTempReg(pParse, tmpReg2); + } + + sqlite3VdbeAddOp3(v, OP_SeekRowid, csr, lbl, tmpReg); + VdbeCoverage(v); + sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol, pWin->regResult); + sqlite3VdbeResolveLabel(v, lbl); + sqlite3ReleaseTempReg(pParse, tmpReg); } - - sqlite3VdbeAddOp3(v, OP_SeekRowid, csr, lbl, tmpReg); - VdbeCoverage(v); - sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol, pWin->regResult); - sqlite3VdbeResolveLabel(v, lbl); - sqlite3ReleaseTempReg(pParse, tmpReg); } } - sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub); -} - -/* -** Invoke the code generated by windowReturnOneRow() and, optionally, the -** xInverse() function for each window function, for one or more rows -** from the Window.iEphCsr temp table. This routine generates VM code -** similar to: -** -** while( regCtr>0 ){ -** regCtr--; -** windowReturnOneRow() -** if( bInverse ){ -** AggInverse -** } -** Next (Window.iEphCsr) -** } -*/ -static void windowReturnRows( - Parse *pParse, - Window *pMWin, /* List of window functions */ - int regCtr, /* Register containing number of rows */ - int regGosub, /* Register for Gosub addrGosub */ - int addrGosub, /* Address of sub-routine for ReturnOneRow */ - int regInvArg, /* Array of registers for xInverse args */ - int regInvSize /* Register containing size of partition */ -){ - int addr; - Vdbe *v = sqlite3GetVdbe(pParse); - windowAggFinal(pParse, pMWin, 0); - addr = sqlite3VdbeAddOp3(v, OP_IfPos, regCtr, sqlite3VdbeCurrentAddr(v)+2 ,1); - VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_Goto, 0, 0); - windowReturnOneRow(pParse, pMWin, regGosub, addrGosub); - if( regInvArg ){ - windowAggStep(pParse, pMWin, pMWin->iEphCsr, 1, regInvArg, regInvSize); - } - sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, addr); - VdbeCoverage(v); - sqlite3VdbeJumpHere(v, addr+1); /* The OP_Goto */ + sqlite3VdbeAddOp2(v, OP_Gosub, p->regGosub, p->addrGosub); } /* @@ -145986,17 +147127,17 @@ static int windowInitAccum(Parse *pParse, Window *pMWin){ FuncDef *pFunc = pWin->pFunc; sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum); nArg = MAX(nArg, windowArgCount(pWin)); - if( pFunc->zName==nth_valueName - || pFunc->zName==first_valueName - ){ - sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp); - sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp+1); - } + if( pMWin->regStartRowid==0 ){ + if( pFunc->zName==nth_valueName || pFunc->zName==first_valueName ){ + sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp); + sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp+1); + } - if( (pFunc->funcFlags & SQLITE_FUNC_MINMAX) && pWin->csrApp ){ - assert( pWin->eStart!=TK_UNBOUNDED ); - sqlite3VdbeAddOp1(v, OP_ResetSorter, pWin->csrApp); - sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp+1); + if( (pFunc->funcFlags & SQLITE_FUNC_MINMAX) && pWin->csrApp ){ + assert( pWin->eStart!=TK_UNBOUNDED ); + sqlite3VdbeAddOp1(v, OP_ResetSorter, pWin->csrApp); + sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp+1); + } } } regArg = pParse->nMem+1; @@ -146004,672 +147145,248 @@ static int windowInitAccum(Parse *pParse, Window *pMWin){ return regArg; } +/* +** Return true if the current frame should be cached in the ephemeral table, +** even if there are no xInverse() calls required. +*/ +static int windowCacheFrame(Window *pMWin){ + Window *pWin; + if( pMWin->regStartRowid ) return 1; + for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ + FuncDef *pFunc = pWin->pFunc; + if( (pFunc->zName==nth_valueName) + || (pFunc->zName==first_valueName) + || (pFunc->zName==leadName) + || (pFunc->zName==lagName) + ){ + return 1; + } + } + return 0; +} /* -** This function does the work of sqlite3WindowCodeStep() for all "ROWS" -** window frame types except for "BETWEEN UNBOUNDED PRECEDING AND CURRENT -** ROW". Pseudo-code for each follows. -** -** ROWS BETWEEN PRECEDING AND FOLLOWING -** -** ... -** if( new partition ){ -** Gosub flush_partition -** } -** Insert (record in eph-table) -** sqlite3WhereEnd() -** Gosub flush_partition -** -** flush_partition: -** Once { -** OpenDup (iEphCsr -> csrStart) -** OpenDup (iEphCsr -> csrEnd) -** } -** regStart = // PRECEDING expression -** regEnd = // FOLLOWING expression -** if( regStart<0 || regEnd<0 ){ error! } -** Rewind (csr,csrStart,csrEnd) // if EOF goto flush_partition_done -** Next(csrEnd) // if EOF skip Aggstep -** Aggstep (csrEnd) -** if( (regEnd--)<=0 ){ -** AggFinal (xValue) -** Gosub addrGosub -** Next(csr) // if EOF goto flush_partition_done -** if( (regStart--)<=0 ){ -** AggInverse (csrStart) -** Next(csrStart) -** } -** } -** flush_partition_done: -** ResetSorter (csr) -** Return -** -** ROWS BETWEEN PRECEDING AND CURRENT ROW -** ROWS BETWEEN CURRENT ROW AND FOLLOWING -** ROWS BETWEEN UNBOUNDED PRECEDING AND FOLLOWING -** -** These are similar to the above. For "CURRENT ROW", intialize the -** register to 0. For "UNBOUNDED PRECEDING" to infinity. -** -** ROWS BETWEEN PRECEDING AND UNBOUNDED FOLLOWING -** ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING -** -** Rewind (csr,csrStart,csrEnd) // if EOF goto flush_partition_done -** while( 1 ){ -** Next(csrEnd) // Exit while(1) at EOF -** Aggstep (csrEnd) -** } -** while( 1 ){ -** AggFinal (xValue) -** Gosub addrGosub -** Next(csr) // if EOF goto flush_partition_done -** if( (regStart--)<=0 ){ -** AggInverse (csrStart) -** Next(csrStart) -** } -** } -** -** For the "CURRENT ROW AND UNBOUNDED FOLLOWING" case, the final if() -** condition is always true (as if regStart were initialized to 0). -** -** RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING -** -** This is the only RANGE case handled by this routine. It modifies the -** second while( 1 ) loop in "ROWS BETWEEN CURRENT ... UNBOUNDED..." to -** be: -** -** while( 1 ){ -** AggFinal (xValue) -** while( 1 ){ -** regPeer++ -** Gosub addrGosub -** Next(csr) // if EOF goto flush_partition_done -** if( new peer ) break; -** } -** while( (regPeer--)>0 ){ -** AggInverse (csrStart) -** Next(csrStart) -** } -** } -** -** ROWS BETWEEN FOLLOWING AND FOLLOWING -** -** regEnd = regEnd - regStart -** Rewind (csr,csrStart,csrEnd) // if EOF goto flush_partition_done -** Aggstep (csrEnd) -** Next(csrEnd) // if EOF fall-through -** if( (regEnd--)<=0 ){ -** if( (regStart--)<=0 ){ -** AggFinal (xValue) -** Gosub addrGosub -** Next(csr) // if EOF goto flush_partition_done -** } -** AggInverse (csrStart) -** Next (csrStart) -** } -** -** ROWS BETWEEN PRECEDING AND PRECEDING -** -** Replace the bit after "Rewind" in the above with: -** -** if( (regEnd--)<=0 ){ -** AggStep (csrEnd) -** Next (csrEnd) -** } -** AggFinal (xValue) -** Gosub addrGosub -** Next(csr) // if EOF goto flush_partition_done -** if( (regStart--)<=0 ){ -** AggInverse (csr2) -** Next (csr2) -** } +** regOld and regNew are each the first register in an array of size +** pOrderBy->nExpr. This function generates code to compare the two +** arrays of registers using the collation sequences and other comparison +** parameters specified by pOrderBy. ** +** If the two arrays are not equal, the contents of regNew is copied to +** regOld and control falls through. Otherwise, if the contents of the arrays +** are equal, an OP_Goto is executed. The address of the OP_Goto is returned. */ -static void windowCodeRowExprStep( - Parse *pParse, - Select *p, - WhereInfo *pWInfo, - int regGosub, - int addrGosub +static void windowIfNewPeer( + Parse *pParse, + ExprList *pOrderBy, + int regNew, /* First in array of new values */ + int regOld, /* First in array of old values */ + int addr /* Jump here */ ){ - Window *pMWin = p->pWin; Vdbe *v = sqlite3GetVdbe(pParse); - int regFlushPart; /* Register for "Gosub flush_partition" */ - int lblFlushPart; /* Label for "Gosub flush_partition" */ - int lblFlushDone; /* Label for "Gosub flush_partition_done" */ - - int regArg; - int addr; - int csrStart = pParse->nTab++; - int csrEnd = pParse->nTab++; - int regStart; /* Value of PRECEDING */ - int regEnd; /* Value of FOLLOWING */ - int addrGoto; - int addrTop; - int addrIfPos1 = 0; - int addrIfPos2 = 0; - int regSize = 0; - - assert( pMWin->eStart==TK_PRECEDING - || pMWin->eStart==TK_CURRENT - || pMWin->eStart==TK_FOLLOWING - || pMWin->eStart==TK_UNBOUNDED - ); - assert( pMWin->eEnd==TK_FOLLOWING - || pMWin->eEnd==TK_CURRENT - || pMWin->eEnd==TK_UNBOUNDED - || pMWin->eEnd==TK_PRECEDING - ); - - /* Allocate register and label for the "flush_partition" sub-routine. */ - regFlushPart = ++pParse->nMem; - lblFlushPart = sqlite3VdbeMakeLabel(v); - lblFlushDone = sqlite3VdbeMakeLabel(v); - - regStart = ++pParse->nMem; - regEnd = ++pParse->nMem; - - windowPartitionCache(pParse, p, pWInfo, regFlushPart, lblFlushPart, ®Size); - - addrGoto = sqlite3VdbeAddOp0(v, OP_Goto); - - /* Start of "flush_partition" */ - sqlite3VdbeResolveLabel(v, lblFlushPart); - sqlite3VdbeAddOp2(v, OP_Once, 0, sqlite3VdbeCurrentAddr(v)+3); - VdbeCoverage(v); - VdbeComment((v, "Flush_partition subroutine")); - sqlite3VdbeAddOp2(v, OP_OpenDup, csrStart, pMWin->iEphCsr); - sqlite3VdbeAddOp2(v, OP_OpenDup, csrEnd, pMWin->iEphCsr); - - /* If either regStart or regEnd are not non-negative integers, throw - ** an exception. */ - if( pMWin->pStart ){ - sqlite3ExprCode(pParse, pMWin->pStart, regStart); - windowCheckIntValue(pParse, regStart, 0); - } - if( pMWin->pEnd ){ - sqlite3ExprCode(pParse, pMWin->pEnd, regEnd); - windowCheckIntValue(pParse, regEnd, 1); - } - - /* If this is "ROWS FOLLOWING AND ROWS FOLLOWING", do: - ** - ** if( regEndpEnd && pMWin->eStart==TK_FOLLOWING ){ - assert( pMWin->pStart!=0 ); - assert( pMWin->eEnd==TK_FOLLOWING ); - sqlite3VdbeAddOp3(v, OP_Ge, regStart, sqlite3VdbeCurrentAddr(v)+2, regEnd); - VdbeCoverageNeverNull(v); - sqlite3VdbeAddOp2(v, OP_Copy, regSize, regStart); - sqlite3VdbeAddOp3(v, OP_Subtract, regStart, regEnd, regEnd); - } - - if( pMWin->pStart && pMWin->eEnd==TK_PRECEDING ){ - assert( pMWin->pEnd!=0 ); - assert( pMWin->eStart==TK_PRECEDING ); - sqlite3VdbeAddOp3(v, OP_Le, regStart, sqlite3VdbeCurrentAddr(v)+3, regEnd); - VdbeCoverageNeverNull(v); - sqlite3VdbeAddOp2(v, OP_Copy, regSize, regStart); - sqlite3VdbeAddOp2(v, OP_Copy, regSize, regEnd); - } - - /* Initialize the accumulator register for each window function to NULL */ - regArg = windowInitAccum(pParse, pMWin); - - sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr, lblFlushDone); - VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_Rewind, csrStart, lblFlushDone); - VdbeCoverageNeverTaken(v); - sqlite3VdbeChangeP5(v, 1); - sqlite3VdbeAddOp2(v, OP_Rewind, csrEnd, lblFlushDone); - VdbeCoverageNeverTaken(v); - sqlite3VdbeChangeP5(v, 1); - - /* Invoke AggStep function for each window function using the row that - ** csrEnd currently points to. Or, if csrEnd is already at EOF, - ** do nothing. */ - addrTop = sqlite3VdbeCurrentAddr(v); - if( pMWin->eEnd==TK_PRECEDING ){ - addrIfPos1 = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0 , 1); - VdbeCoverage(v); - } - sqlite3VdbeAddOp2(v, OP_Next, csrEnd, sqlite3VdbeCurrentAddr(v)+2); - VdbeCoverage(v); - addr = sqlite3VdbeAddOp0(v, OP_Goto); - windowAggStep(pParse, pMWin, csrEnd, 0, regArg, regSize); - if( pMWin->eEnd==TK_UNBOUNDED ){ - sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop); - sqlite3VdbeJumpHere(v, addr); - addrTop = sqlite3VdbeCurrentAddr(v); + if( pOrderBy ){ + int nVal = pOrderBy->nExpr; + KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOrderBy, 0, 0); + sqlite3VdbeAddOp3(v, OP_Compare, regOld, regNew, nVal); + sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO); + sqlite3VdbeAddOp3(v, OP_Jump, + sqlite3VdbeCurrentAddr(v)+1, addr, sqlite3VdbeCurrentAddr(v)+1 + ); + VdbeCoverageEqNe(v); + sqlite3VdbeAddOp3(v, OP_Copy, regNew, regOld, nVal-1); }else{ - sqlite3VdbeJumpHere(v, addr); - if( pMWin->eEnd==TK_PRECEDING ){ - sqlite3VdbeJumpHere(v, addrIfPos1); - } - } - - if( pMWin->eEnd==TK_FOLLOWING ){ - addrIfPos1 = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0 , 1); - VdbeCoverage(v); + sqlite3VdbeAddOp2(v, OP_Goto, 0, addr); } - if( pMWin->eStart==TK_FOLLOWING ){ - addrIfPos2 = sqlite3VdbeAddOp3(v, OP_IfPos, regStart, 0 , 1); - VdbeCoverage(v); - } - windowAggFinal(pParse, pMWin, 0); - windowReturnOneRow(pParse, pMWin, regGosub, addrGosub); - sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)+2); - VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_Goto, 0, lblFlushDone); - if( pMWin->eStart==TK_FOLLOWING ){ - sqlite3VdbeJumpHere(v, addrIfPos2); - } - - if( pMWin->eStart==TK_CURRENT - || pMWin->eStart==TK_PRECEDING - || pMWin->eStart==TK_FOLLOWING - ){ - int lblSkipInverse = sqlite3VdbeMakeLabel(v);; - if( pMWin->eStart==TK_PRECEDING ){ - sqlite3VdbeAddOp3(v, OP_IfPos, regStart, lblSkipInverse, 1); - VdbeCoverage(v); - } - if( pMWin->eStart==TK_FOLLOWING ){ - sqlite3VdbeAddOp2(v, OP_Next, csrStart, sqlite3VdbeCurrentAddr(v)+2); - VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_Goto, 0, lblSkipInverse); - }else{ - sqlite3VdbeAddOp2(v, OP_Next, csrStart, sqlite3VdbeCurrentAddr(v)+1); - VdbeCoverageAlwaysTaken(v); - } - windowAggStep(pParse, pMWin, csrStart, 1, regArg, regSize); - sqlite3VdbeResolveLabel(v, lblSkipInverse); - } - if( pMWin->eEnd==TK_FOLLOWING ){ - sqlite3VdbeJumpHere(v, addrIfPos1); - } - sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop); - - /* flush_partition_done: */ - sqlite3VdbeResolveLabel(v, lblFlushDone); - sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr); - sqlite3VdbeAddOp1(v, OP_Return, regFlushPart); - VdbeComment((v, "end flush_partition subroutine")); - - /* Jump to here to skip over flush_partition */ - sqlite3VdbeJumpHere(v, addrGoto); } /* -** This function does the work of sqlite3WindowCodeStep() for cases that -** would normally be handled by windowCodeDefaultStep() when there are -** one or more built-in window-functions that require the entire partition -** to be cached in a temp table before any rows can be returned. Additionally. -** "RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING" is always handled by -** this function. -** -** Pseudo-code corresponding to the VM code generated by this function -** for each type of window follows. -** -** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW -** -** flush_partition: -** Once { -** OpenDup (iEphCsr -> csrLead) -** } -** Integer ctr 0 -** foreach row (csrLead){ -** if( new peer ){ -** AggFinal (xValue) -** for(i=0; i csrLead) -** } -** foreach row (csrLead) { -** AggStep (csrLead) -** } -** foreach row (iEphCsr) { -** Gosub addrGosub -** } -** -** RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING -** -** flush_partition: -** Once { -** OpenDup (iEphCsr -> csrLead) -** } -** foreach row (csrLead){ -** AggStep (csrLead) -** } -** Rewind (csrLead) -** Integer ctr 0 -** foreach row (csrLead){ -** if( new peer ){ -** AggFinal (xValue) -** for(i=0; i= csr2.peerVal ) goto lbl; ** -** ResetSorter (csr) -** Return +** A special type of arithmetic is used such that if csr.peerVal is not +** a numeric type (real or integer), then the result of the addition is +** a copy of csr1.peerVal. */ -static void windowCodeCacheStep( - Parse *pParse, - Select *p, - WhereInfo *pWInfo, - int regGosub, - int addrGosub +static void windowCodeRangeTest( + WindowCodeArg *p, + int op, /* OP_Ge or OP_Gt */ + int csr1, + int regVal, + int csr2, + int lbl ){ - Window *pMWin = p->pWin; + Parse *pParse = p->pParse; Vdbe *v = sqlite3GetVdbe(pParse); - int k; - int addr; - ExprList *pPart = pMWin->pPartition; - ExprList *pOrderBy = pMWin->pOrderBy; - int nPeer = pOrderBy ? pOrderBy->nExpr : 0; - int regNewPeer; - - int addrGoto; /* Address of Goto used to jump flush_par.. */ - int addrNext; /* Jump here for next iteration of loop */ - int regFlushPart; - int lblFlushPart; - int csrLead; - int regCtr; - int regArg; /* Register array to martial function args */ - int regSize; - int lblEmpty; - int bReverse = pMWin->pOrderBy && pMWin->eStart==TK_CURRENT - && pMWin->eEnd==TK_UNBOUNDED; - - assert( (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_CURRENT) - || (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_UNBOUNDED) - || (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_CURRENT) - || (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_UNBOUNDED) - ); - - lblEmpty = sqlite3VdbeMakeLabel(v); - regNewPeer = pParse->nMem+1; - pParse->nMem += nPeer; + int reg1 = sqlite3GetTempReg(pParse); + int reg2 = sqlite3GetTempReg(pParse); + int arith = OP_Add; + int addrGe; - /* Allocate register and label for the "flush_partition" sub-routine. */ - regFlushPart = ++pParse->nMem; - lblFlushPart = sqlite3VdbeMakeLabel(v); + int regString = ++pParse->nMem; - csrLead = pParse->nTab++; - regCtr = ++pParse->nMem; - - windowPartitionCache(pParse, p, pWInfo, regFlushPart, lblFlushPart, ®Size); - addrGoto = sqlite3VdbeAddOp0(v, OP_Goto); - - /* Start of "flush_partition" */ - sqlite3VdbeResolveLabel(v, lblFlushPart); - sqlite3VdbeAddOp2(v, OP_Once, 0, sqlite3VdbeCurrentAddr(v)+2); - VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_OpenDup, csrLead, pMWin->iEphCsr); - - /* Initialize the accumulator register for each window function to NULL */ - regArg = windowInitAccum(pParse, pMWin); - - sqlite3VdbeAddOp2(v, OP_Integer, 0, regCtr); - sqlite3VdbeAddOp2(v, OP_Rewind, csrLead, lblEmpty); - VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr, lblEmpty); - VdbeCoverageNeverTaken(v); - - if( bReverse ){ - int addr2 = sqlite3VdbeCurrentAddr(v); - windowAggStep(pParse, pMWin, csrLead, 0, regArg, regSize); - sqlite3VdbeAddOp2(v, OP_Next, csrLead, addr2); - VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_Rewind, csrLead, lblEmpty); - VdbeCoverageNeverTaken(v); - } - addrNext = sqlite3VdbeCurrentAddr(v); - - if( pOrderBy && (pMWin->eEnd==TK_CURRENT || pMWin->eStart==TK_CURRENT) ){ - int bCurrent = (pMWin->eStart==TK_CURRENT); - int addrJump = 0; /* Address of OP_Jump below */ - if( pMWin->eType==TK_RANGE ){ - int iOff = pMWin->nBufferCol + (pPart ? pPart->nExpr : 0); - int regPeer = pMWin->regPart + (pPart ? pPart->nExpr : 0); - KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOrderBy, 0, 0); - for(k=0; kpMWin->pOrderBy && p->pMWin->pOrderBy->nExpr==1 ); + if( p->pMWin->pOrderBy->a[0].sortOrder ){ + switch( op ){ + case OP_Ge: op = OP_Le; break; + case OP_Gt: op = OP_Lt; break; + default: assert( op==OP_Le ); op = OP_Ge; break; } - - windowReturnRows(pParse, pMWin, regCtr, regGosub, addrGosub, - (bCurrent ? regArg : 0), (bCurrent ? regSize : 0) - ); - if( addrJump ) sqlite3VdbeJumpHere(v, addrJump); + arith = OP_Subtract; } - if( bReverse==0 ){ - windowAggStep(pParse, pMWin, csrLead, 0, regArg, regSize); - } - sqlite3VdbeAddOp2(v, OP_AddImm, regCtr, 1); - sqlite3VdbeAddOp2(v, OP_Next, csrLead, addrNext); - VdbeCoverage(v); + windowReadPeerValues(p, csr1, reg1); + windowReadPeerValues(p, csr2, reg2); - windowReturnRows(pParse, pMWin, regCtr, regGosub, addrGosub, 0, 0); - - sqlite3VdbeResolveLabel(v, lblEmpty); - sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr); - sqlite3VdbeAddOp1(v, OP_Return, regFlushPart); + /* Check if the peer value for csr1 value is a text or blob by comparing + ** it to the smallest possible string - ''. If it is, jump over the + ** OP_Add or OP_Subtract operation and proceed directly to the comparison. */ + sqlite3VdbeAddOp4(v, OP_String8, 0, regString, 0, "", P4_STATIC); + addrGe = sqlite3VdbeAddOp3(v, OP_Ge, regString, 0, reg1); + VdbeCoverage(v); + sqlite3VdbeAddOp3(v, arith, regVal, reg1, reg1); + sqlite3VdbeJumpHere(v, addrGe); + sqlite3VdbeAddOp3(v, op, reg2, lbl, reg1); VdbeCoverage(v); + sqlite3VdbeChangeP5(v, SQLITE_NULLEQ); + assert( op==OP_Ge || op==OP_Gt || op==OP_Lt || op==OP_Le ); + testcase(op==OP_Ge); VdbeCoverageIf(v, op==OP_Ge); + testcase(op==OP_Lt); VdbeCoverageIf(v, op==OP_Lt); + testcase(op==OP_Le); VdbeCoverageIf(v, op==OP_Le); + testcase(op==OP_Gt); VdbeCoverageIf(v, op==OP_Gt); - /* Jump to here to skip over flush_partition */ - sqlite3VdbeJumpHere(v, addrGoto); + sqlite3ReleaseTempReg(pParse, reg1); + sqlite3ReleaseTempReg(pParse, reg2); } - /* -** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW -** -** ... -** if( new partition ){ -** AggFinal (xFinalize) -** Gosub addrGosub -** ResetSorter eph-table -** } -** else if( new peer ){ -** AggFinal (xValue) -** Gosub addrGosub -** ResetSorter eph-table -** } -** AggStep -** Insert (record into eph-table) -** sqlite3WhereEnd() -** AggFinal (xFinalize) -** Gosub addrGosub -** -** RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING -** -** As above, except take no action for a "new peer". Invoke -** the sub-routine once only for each partition. -** -** RANGE BETWEEN CURRENT ROW AND CURRENT ROW -** -** As above, except that the "new peer" condition is handled in the -** same way as "new partition" (so there is no "else if" block). -** -** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW -** -** As above, except assume every row is a "new peer". +** Helper function for sqlite3WindowCodeStep(). Each call to this function +** generates VM code for a single RETURN_ROW, AGGSTEP or AGGINVERSE +** operation. Refer to the header comment for sqlite3WindowCodeStep() for +** details. */ -static void windowCodeDefaultStep( - Parse *pParse, - Select *p, - WhereInfo *pWInfo, - int regGosub, - int addrGosub +static int windowCodeOp( + WindowCodeArg *p, /* Context object */ + int op, /* WINDOW_RETURN_ROW, AGGSTEP or AGGINVERSE */ + int regCountdown, /* Register for OP_IfPos countdown */ + int jumpOnEof /* Jump here if stepped cursor reaches EOF */ ){ - Window *pMWin = p->pWin; - Vdbe *v = sqlite3GetVdbe(pParse); - int k; - int iSubCsr = p->pSrc->a[0].iCursor; - int nSub = p->pSrc->a[0].pTab->nCol; - int reg = pParse->nMem+1; - int regRecord = reg+nSub; - int regRowid = regRecord+1; - int addr; - ExprList *pPart = pMWin->pPartition; - ExprList *pOrderBy = pMWin->pOrderBy; - - assert( pMWin->eType==TK_RANGE - || (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_CURRENT) - ); - - assert( (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_CURRENT) - || (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_UNBOUNDED) - || (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_CURRENT) - || (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_UNBOUNDED && !pOrderBy) - ); - - if( pMWin->eEnd==TK_UNBOUNDED ){ - pOrderBy = 0; - } - - pParse->nMem += nSub + 2; - - /* Load the individual column values of the row returned by - ** the sub-select into an array of registers. */ - for(k=0; kpParse; + Window *pMWin = p->pMWin; + int ret = 0; + Vdbe *v = p->pVdbe; + int addrIf = 0; + int addrContinue = 0; + int addrGoto = 0; + int bPeer = (pMWin->eFrmType!=TK_ROWS); + + int lblDone = sqlite3VdbeMakeLabel(pParse); + int addrNextRange = 0; + + /* Special case - WINDOW_AGGINVERSE is always a no-op if the frame + ** starts with UNBOUNDED PRECEDING. */ + if( op==WINDOW_AGGINVERSE && pMWin->eStart==TK_UNBOUNDED ){ + assert( regCountdown==0 && jumpOnEof==0 ); + return 0; } - /* Check if this is the start of a new partition or peer group. */ - if( pPart || pOrderBy ){ - int nPart = (pPart ? pPart->nExpr : 0); - int addrGoto = 0; - int addrJump = 0; - int nPeer = (pOrderBy ? pOrderBy->nExpr : 0); - - if( pPart ){ - int regNewPart = reg + pMWin->nBufferCol; - KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pPart, 0, 0); - addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPart, pMWin->regPart,nPart); - sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO); - addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2); - VdbeCoverageEqNe(v); - windowAggFinal(pParse, pMWin, 1); - if( pOrderBy ){ - addrGoto = sqlite3VdbeAddOp0(v, OP_Goto); + if( regCountdown>0 ){ + if( pMWin->eFrmType==TK_RANGE ){ + addrNextRange = sqlite3VdbeCurrentAddr(v); + assert( op==WINDOW_AGGINVERSE || op==WINDOW_AGGSTEP ); + if( op==WINDOW_AGGINVERSE ){ + if( pMWin->eStart==TK_FOLLOWING ){ + windowCodeRangeTest( + p, OP_Le, p->current.csr, regCountdown, p->start.csr, lblDone + ); + }else{ + windowCodeRangeTest( + p, OP_Ge, p->start.csr, regCountdown, p->current.csr, lblDone + ); + } + }else{ + windowCodeRangeTest( + p, OP_Gt, p->end.csr, regCountdown, p->current.csr, lblDone + ); } + }else{ + addrIf = sqlite3VdbeAddOp3(v, OP_IfPos, regCountdown, 0, 1); + VdbeCoverage(v); } + } - if( pOrderBy ){ - int regNewPeer = reg + pMWin->nBufferCol + nPart; - int regPeer = pMWin->regPart + nPart; + if( op==WINDOW_RETURN_ROW && pMWin->regStartRowid==0 ){ + windowAggFinal(p, 0); + } + addrContinue = sqlite3VdbeCurrentAddr(v); + switch( op ){ + case WINDOW_RETURN_ROW: + csr = p->current.csr; + reg = p->current.reg; + windowReturnOneRow(p); + break; - if( addrJump ) sqlite3VdbeJumpHere(v, addrJump); - if( pMWin->eType==TK_RANGE ){ - KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOrderBy, 0, 0); - addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPeer, regPeer, nPeer); - sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO); - addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2); - VdbeCoverage(v); + case WINDOW_AGGINVERSE: + csr = p->start.csr; + reg = p->start.reg; + if( pMWin->regStartRowid ){ + assert( pMWin->regEndRowid ); + sqlite3VdbeAddOp2(v, OP_AddImm, pMWin->regStartRowid, 1); }else{ - addrJump = 0; + windowAggStep(pParse, pMWin, csr, 1, p->regArg); } - windowAggFinal(pParse, pMWin, pMWin->eStart==TK_CURRENT); - if( addrGoto ) sqlite3VdbeJumpHere(v, addrGoto); - } - - sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr,sqlite3VdbeCurrentAddr(v)+3); - VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub); - sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)-1); - VdbeCoverage(v); - - sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr); - sqlite3VdbeAddOp3( - v, OP_Copy, reg+pMWin->nBufferCol, pMWin->regPart, nPart+nPeer-1 - ); + break; - if( addrJump ) sqlite3VdbeJumpHere(v, addrJump); + default: + assert( op==WINDOW_AGGSTEP ); + csr = p->end.csr; + reg = p->end.reg; + if( pMWin->regStartRowid ){ + assert( pMWin->regEndRowid ); + sqlite3VdbeAddOp2(v, OP_AddImm, pMWin->regEndRowid, 1); + }else{ + windowAggStep(pParse, pMWin, csr, 0, p->regArg); + } + break; } - /* Invoke step function for window functions */ - windowAggStep(pParse, pMWin, -1, 0, reg, 0); + if( op==p->eDelete ){ + sqlite3VdbeAddOp1(v, OP_Delete, csr); + sqlite3VdbeChangeP5(v, OPFLAG_SAVEPOSITION); + } - /* Buffer the current row in the ephemeral table. */ - if( pMWin->nBufferCol>0 ){ - sqlite3VdbeAddOp3(v, OP_MakeRecord, reg, pMWin->nBufferCol, regRecord); + if( jumpOnEof ){ + sqlite3VdbeAddOp2(v, OP_Next, csr, sqlite3VdbeCurrentAddr(v)+2); + VdbeCoverage(v); + ret = sqlite3VdbeAddOp0(v, OP_Goto); }else{ - sqlite3VdbeAddOp2(v, OP_Blob, 0, regRecord); - sqlite3VdbeAppendP4(v, (void*)"", 0); + sqlite3VdbeAddOp2(v, OP_Next, csr, sqlite3VdbeCurrentAddr(v)+1+bPeer); + VdbeCoverage(v); + if( bPeer ){ + addrGoto = sqlite3VdbeAddOp0(v, OP_Goto); + } } - sqlite3VdbeAddOp2(v, OP_NewRowid, pMWin->iEphCsr, regRowid); - sqlite3VdbeAddOp3(v, OP_Insert, pMWin->iEphCsr, regRecord, regRowid); - /* End the database scan loop. */ - sqlite3WhereEnd(pWInfo); + if( bPeer ){ + int nReg = (pMWin->pOrderBy ? pMWin->pOrderBy->nExpr : 0); + int regTmp = (nReg ? sqlite3GetTempRange(pParse, nReg) : 0); + windowReadPeerValues(p, csr, regTmp); + windowIfNewPeer(pParse, pMWin->pOrderBy, regTmp, reg, addrContinue); + sqlite3ReleaseTempRange(pParse, regTmp, nReg); + } - windowAggFinal(pParse, pMWin, 1); - sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr,sqlite3VdbeCurrentAddr(v)+3); - VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub); - sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)-1); - VdbeCoverage(v); + if( addrNextRange ){ + sqlite3VdbeAddOp2(v, OP_Goto, 0, addrNextRange); + } + sqlite3VdbeResolveLabel(v, lblDone); + if( addrGoto ) sqlite3VdbeJumpHere(v, addrGoto); + if( addrIf ) sqlite3VdbeJumpHere(v, addrIf); + return ret; } + /* ** Allocate and return a duplicate of the Window object indicated by the ** third argument. Set the Window.pOwner field of the new object to @@ -146682,11 +147399,13 @@ SQLITE_PRIVATE Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p){ if( pNew ){ pNew->zName = sqlite3DbStrDup(db, p->zName); pNew->pFilter = sqlite3ExprDup(db, p->pFilter, 0); + pNew->pFunc = p->pFunc; pNew->pPartition = sqlite3ExprListDup(db, p->pPartition, 0); pNew->pOrderBy = sqlite3ExprListDup(db, p->pOrderBy, 0); - pNew->eType = p->eType; + pNew->eFrmType = p->eFrmType; pNew->eEnd = p->eEnd; pNew->eStart = p->eStart; + pNew->eExclude = p->eExclude; pNew->pStart = sqlite3ExprDup(db, p->pStart, 0); pNew->pEnd = sqlite3ExprDup(db, p->pEnd, 0); pNew->pOwner = pOwner; @@ -146713,12 +147432,360 @@ SQLITE_PRIVATE Window *sqlite3WindowListDup(sqlite3 *db, Window *p){ return pRet; } +/* +** Return true if it can be determined at compile time that expression +** pExpr evaluates to a value that, when cast to an integer, is greater +** than zero. False otherwise. +** +** If an OOM error occurs, this function sets the Parse.db.mallocFailed +** flag and returns zero. +*/ +static int windowExprGtZero(Parse *pParse, Expr *pExpr){ + int ret = 0; + sqlite3 *db = pParse->db; + sqlite3_value *pVal = 0; + sqlite3ValueFromExpr(db, pExpr, db->enc, SQLITE_AFF_NUMERIC, &pVal); + if( pVal && sqlite3_value_int(pVal)>0 ){ + ret = 1; + } + sqlite3ValueFree(pVal); + return ret; +} + /* ** sqlite3WhereBegin() has already been called for the SELECT statement ** passed as the second argument when this function is invoked. It generates -** code to populate the Window.regResult register for each window function and -** invoke the sub-routine at instruction addrGosub once for each row. -** This function calls sqlite3WhereEnd() before returning. +** code to populate the Window.regResult register for each window function +** and invoke the sub-routine at instruction addrGosub once for each row. +** sqlite3WhereEnd() is always called before returning. +** +** This function handles several different types of window frames, which +** require slightly different processing. The following pseudo code is +** used to implement window frames of the form: +** +** ROWS BETWEEN PRECEDING AND FOLLOWING +** +** Other window frame types use variants of the following: +** +** ... loop started by sqlite3WhereBegin() ... +** if( new partition ){ +** Gosub flush +** } +** Insert new row into eph table. +** +** if( first row of partition ){ +** // Rewind three cursors, all open on the eph table. +** Rewind(csrEnd); +** Rewind(csrStart); +** Rewind(csrCurrent); +** +** regEnd = // FOLLOWING expression +** regStart = // PRECEDING expression +** }else{ +** // First time this branch is taken, the eph table contains two +** // rows. The first row in the partition, which all three cursors +** // currently point to, and the following row. +** AGGSTEP +** if( (regEnd--)<=0 ){ +** RETURN_ROW +** if( (regStart--)<=0 ){ +** AGGINVERSE +** } +** } +** } +** } +** flush: +** AGGSTEP +** while( 1 ){ +** RETURN ROW +** if( csrCurrent is EOF ) break; +** if( (regStart--)<=0 ){ +** AggInverse(csrStart) +** Next(csrStart) +** } +** } +** +** The pseudo-code above uses the following shorthand: +** +** AGGSTEP: invoke the aggregate xStep() function for each window function +** with arguments read from the current row of cursor csrEnd, then +** step cursor csrEnd forward one row (i.e. sqlite3BtreeNext()). +** +** RETURN_ROW: return a row to the caller based on the contents of the +** current row of csrCurrent and the current state of all +** aggregates. Then step cursor csrCurrent forward one row. +** +** AGGINVERSE: invoke the aggregate xInverse() function for each window +** functions with arguments read from the current row of cursor +** csrStart. Then step csrStart forward one row. +** +** There are two other ROWS window frames that are handled significantly +** differently from the above - "BETWEEN PRECEDING AND PRECEDING" +** and "BETWEEN FOLLOWING AND FOLLOWING". These are special +** cases because they change the order in which the three cursors (csrStart, +** csrCurrent and csrEnd) iterate through the ephemeral table. Cases that +** use UNBOUNDED or CURRENT ROW are much simpler variations on one of these +** three. +** +** ROWS BETWEEN PRECEDING AND PRECEDING +** +** ... loop started by sqlite3WhereBegin() ... +** if( new partition ){ +** Gosub flush +** } +** Insert new row into eph table. +** if( first row of partition ){ +** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent) +** regEnd = +** regStart = +** }else{ +** if( (regEnd--)<=0 ){ +** AGGSTEP +** } +** RETURN_ROW +** if( (regStart--)<=0 ){ +** AGGINVERSE +** } +** } +** } +** flush: +** if( (regEnd--)<=0 ){ +** AGGSTEP +** } +** RETURN_ROW +** +** +** ROWS BETWEEN FOLLOWING AND FOLLOWING +** +** ... loop started by sqlite3WhereBegin() ... +** if( new partition ){ +** Gosub flush +** } +** Insert new row into eph table. +** if( first row of partition ){ +** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent) +** regEnd = +** regStart = regEnd - +** }else{ +** AGGSTEP +** if( (regEnd--)<=0 ){ +** RETURN_ROW +** } +** if( (regStart--)<=0 ){ +** AGGINVERSE +** } +** } +** } +** flush: +** AGGSTEP +** while( 1 ){ +** if( (regEnd--)<=0 ){ +** RETURN_ROW +** if( eof ) break; +** } +** if( (regStart--)<=0 ){ +** AGGINVERSE +** if( eof ) break +** } +** } +** while( !eof csrCurrent ){ +** RETURN_ROW +** } +** +** For the most part, the patterns above are adapted to support UNBOUNDED by +** assuming that it is equivalent to "infinity PRECEDING/FOLLOWING" and +** CURRENT ROW by assuming that it is equivilent to "0 PRECEDING/FOLLOWING". +** This is optimized of course - branches that will never be taken and +** conditions that are always true are omitted from the VM code. The only +** exceptional case is: +** +** ROWS BETWEEN FOLLOWING AND UNBOUNDED FOLLOWING +** +** ... loop started by sqlite3WhereBegin() ... +** if( new partition ){ +** Gosub flush +** } +** Insert new row into eph table. +** if( first row of partition ){ +** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent) +** regStart = +** }else{ +** AGGSTEP +** } +** } +** flush: +** AGGSTEP +** while( 1 ){ +** if( (regStart--)<=0 ){ +** AGGINVERSE +** if( eof ) break +** } +** RETURN_ROW +** } +** while( !eof csrCurrent ){ +** RETURN_ROW +** } +** +** Also requiring special handling are the cases: +** +** ROWS BETWEEN PRECEDING AND PRECEDING +** ROWS BETWEEN FOLLOWING AND FOLLOWING +** +** when (expr1 < expr2). This is detected at runtime, not by this function. +** To handle this case, the pseudo-code programs depicted above are modified +** slightly to be: +** +** ... loop started by sqlite3WhereBegin() ... +** if( new partition ){ +** Gosub flush +** } +** Insert new row into eph table. +** if( first row of partition ){ +** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent) +** regEnd = +** regStart = +** if( regEnd < regStart ){ +** RETURN_ROW +** delete eph table contents +** continue +** } +** ... +** +** The new "continue" statement in the above jumps to the next iteration +** of the outer loop - the one started by sqlite3WhereBegin(). +** +** The various GROUPS cases are implemented using the same patterns as +** ROWS. The VM code is modified slightly so that: +** +** 1. The else branch in the main loop is only taken if the row just +** added to the ephemeral table is the start of a new group. In +** other words, it becomes: +** +** ... loop started by sqlite3WhereBegin() ... +** if( new partition ){ +** Gosub flush +** } +** Insert new row into eph table. +** if( first row of partition ){ +** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent) +** regEnd = +** regStart = +** }else if( new group ){ +** ... +** } +** } +** +** 2. Instead of processing a single row, each RETURN_ROW, AGGSTEP or +** AGGINVERSE step processes the current row of the relevant cursor and +** all subsequent rows belonging to the same group. +** +** RANGE window frames are a little different again. As for GROUPS, the +** main loop runs once per group only. And RETURN_ROW, AGGSTEP and AGGINVERSE +** deal in groups instead of rows. As for ROWS and GROUPS, there are three +** basic cases: +** +** RANGE BETWEEN PRECEDING AND FOLLOWING +** +** ... loop started by sqlite3WhereBegin() ... +** if( new partition ){ +** Gosub flush +** } +** Insert new row into eph table. +** if( first row of partition ){ +** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent) +** regEnd = +** regStart = +** }else{ +** AGGSTEP +** while( (csrCurrent.key + regEnd) < csrEnd.key ){ +** RETURN_ROW +** while( csrStart.key + regStart) < csrCurrent.key ){ +** AGGINVERSE +** } +** } +** } +** } +** flush: +** AGGSTEP +** while( 1 ){ +** RETURN ROW +** if( csrCurrent is EOF ) break; +** while( csrStart.key + regStart) < csrCurrent.key ){ +** AGGINVERSE +** } +** } +** } +** +** In the above notation, "csr.key" means the current value of the ORDER BY +** expression (there is only ever 1 for a RANGE that uses an FOLLOWING +** or PRECEDING AND PRECEDING +** +** ... loop started by sqlite3WhereBegin() ... +** if( new partition ){ +** Gosub flush +** } +** Insert new row into eph table. +** if( first row of partition ){ +** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent) +** regEnd = +** regStart = +** }else{ +** if( (csrEnd.key + regEnd) <= csrCurrent.key ){ +** AGGSTEP +** } +** while( (csrStart.key + regStart) < csrCurrent.key ){ +** AGGINVERSE +** } +** RETURN_ROW +** } +** } +** flush: +** while( (csrEnd.key + regEnd) <= csrCurrent.key ){ +** AGGSTEP +** } +** while( (csrStart.key + regStart) < csrCurrent.key ){ +** AGGINVERSE +** } +** RETURN_ROW +** +** RANGE BETWEEN FOLLOWING AND FOLLOWING +** +** ... loop started by sqlite3WhereBegin() ... +** if( new partition ){ +** Gosub flush +** } +** Insert new row into eph table. +** if( first row of partition ){ +** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent) +** regEnd = +** regStart = +** }else{ +** AGGSTEP +** while( (csrCurrent.key + regEnd) < csrEnd.key ){ +** while( (csrCurrent.key + regStart) > csrStart.key ){ +** AGGINVERSE +** } +** RETURN_ROW +** } +** } +** } +** flush: +** AGGSTEP +** while( 1 ){ +** while( (csrCurrent.key + regStart) > csrStart.key ){ +** AGGINVERSE +** if( eof ) break "while( 1 )" loop. +** } +** RETURN_ROW +** } +** while( !eof csrCurrent ){ +** RETURN_ROW +** } +** +** The text above leaves out many details. Refer to the code and comments +** below for a more complete picture. */ SQLITE_PRIVATE void sqlite3WindowCodeStep( Parse *pParse, /* Parse context */ @@ -146728,75 +147795,321 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep( int addrGosub /* OP_Gosub here to return each row */ ){ Window *pMWin = p->pWin; + ExprList *pOrderBy = pMWin->pOrderBy; + Vdbe *v = sqlite3GetVdbe(pParse); + int csrWrite; /* Cursor used to write to eph. table */ + int csrInput = p->pSrc->a[0].iCursor; /* Cursor of sub-select */ + int nInput = p->pSrc->a[0].pTab->nCol; /* Number of cols returned by sub */ + int iInput; /* To iterate through sub cols */ + int addrNe; /* Address of OP_Ne */ + int addrGosubFlush = 0; /* Address of OP_Gosub to flush: */ + int addrInteger = 0; /* Address of OP_Integer */ + int addrEmpty; /* Address of OP_Rewind in flush: */ + int regStart = 0; /* Value of PRECEDING */ + int regEnd = 0; /* Value of FOLLOWING */ + int regNew; /* Array of registers holding new input row */ + int regRecord; /* regNew array in record form */ + int regRowid; /* Rowid for regRecord in eph table */ + int regNewPeer = 0; /* Peer values for new row (part of regNew) */ + int regPeer = 0; /* Peer values for current row */ + int regFlushPart = 0; /* Register for "Gosub flush_partition" */ + WindowCodeArg s; /* Context object for sub-routines */ + int lblWhereEnd; /* Label just before sqlite3WhereEnd() code */ + + assert( pMWin->eStart==TK_PRECEDING || pMWin->eStart==TK_CURRENT + || pMWin->eStart==TK_FOLLOWING || pMWin->eStart==TK_UNBOUNDED + ); + assert( pMWin->eEnd==TK_FOLLOWING || pMWin->eEnd==TK_CURRENT + || pMWin->eEnd==TK_UNBOUNDED || pMWin->eEnd==TK_PRECEDING + ); + assert( pMWin->eExclude==0 || pMWin->eExclude==TK_CURRENT + || pMWin->eExclude==TK_GROUP || pMWin->eExclude==TK_TIES + || pMWin->eExclude==TK_NO + ); - /* There are three different functions that may be used to do the work - ** of this one, depending on the window frame and the specific built-in - ** window functions used (if any). - ** - ** windowCodeRowExprStep() handles all "ROWS" window frames, except for: - ** - ** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW - ** - ** The exception is because windowCodeRowExprStep() implements all window - ** frame types by caching the entire partition in a temp table, and - ** "ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW" is easy enough to - ** implement without such a cache. - ** - ** windowCodeCacheStep() is used for: - ** - ** RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING - ** - ** It is also used for anything not handled by windowCodeRowExprStep() - ** that invokes a built-in window function that requires the entire - ** partition to be cached in a temp table before any rows are returned - ** (e.g. nth_value() or percent_rank()). - ** - ** Finally, assuming there is no built-in window function that requires - ** the partition to be cached, windowCodeDefaultStep() is used for: - ** - ** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW - ** RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING - ** RANGE BETWEEN CURRENT ROW AND CURRENT ROW - ** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW - ** - ** windowCodeDefaultStep() is the only one of the three functions that - ** does not cache each partition in a temp table before beginning to - ** return rows. - */ - if( pMWin->eType==TK_ROWS - && (pMWin->eStart!=TK_UNBOUNDED||pMWin->eEnd!=TK_CURRENT||!pMWin->pOrderBy) - ){ - VdbeModuleComment((pParse->pVdbe, "Begin RowExprStep()")); - windowCodeRowExprStep(pParse, p, pWInfo, regGosub, addrGosub); - }else{ - Window *pWin; - int bCache = 0; /* True to use CacheStep() */ - - if( pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_UNBOUNDED ){ - bCache = 1; - }else{ - for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ - FuncDef *pFunc = pWin->pFunc; - if( (pFunc->funcFlags & SQLITE_FUNC_WINDOW_SIZE) - || (pFunc->zName==nth_valueName) - || (pFunc->zName==first_valueName) - || (pFunc->zName==leadName) - || (pFunc->zName==lagName) - ){ - bCache = 1; - break; + lblWhereEnd = sqlite3VdbeMakeLabel(pParse); + + /* Fill in the context object */ + memset(&s, 0, sizeof(WindowCodeArg)); + s.pParse = pParse; + s.pMWin = pMWin; + s.pVdbe = v; + s.regGosub = regGosub; + s.addrGosub = addrGosub; + s.current.csr = pMWin->iEphCsr; + csrWrite = s.current.csr+1; + s.start.csr = s.current.csr+2; + s.end.csr = s.current.csr+3; + + /* Figure out when rows may be deleted from the ephemeral table. There + ** are four options - they may never be deleted (eDelete==0), they may + ** be deleted as soon as they are no longer part of the window frame + ** (eDelete==WINDOW_AGGINVERSE), they may be deleted as after the row + ** has been returned to the caller (WINDOW_RETURN_ROW), or they may + ** be deleted after they enter the frame (WINDOW_AGGSTEP). */ + switch( pMWin->eStart ){ + case TK_FOLLOWING: + if( pMWin->eFrmType!=TK_RANGE + && windowExprGtZero(pParse, pMWin->pStart) + ){ + s.eDelete = WINDOW_RETURN_ROW; + } + break; + case TK_UNBOUNDED: + if( windowCacheFrame(pMWin)==0 ){ + if( pMWin->eEnd==TK_PRECEDING ){ + if( pMWin->eFrmType!=TK_RANGE + && windowExprGtZero(pParse, pMWin->pEnd) + ){ + s.eDelete = WINDOW_AGGSTEP; + } + }else{ + s.eDelete = WINDOW_RETURN_ROW; + } + } + break; + default: + s.eDelete = WINDOW_AGGINVERSE; + break; + } + + /* Allocate registers for the array of values from the sub-query, the + ** samve values in record form, and the rowid used to insert said record + ** into the ephemeral table. */ + regNew = pParse->nMem+1; + pParse->nMem += nInput; + regRecord = ++pParse->nMem; + regRowid = ++pParse->nMem; + + /* If the window frame contains an " PRECEDING" or " FOLLOWING" + ** clause, allocate registers to store the results of evaluating each + ** . */ + if( pMWin->eStart==TK_PRECEDING || pMWin->eStart==TK_FOLLOWING ){ + regStart = ++pParse->nMem; + } + if( pMWin->eEnd==TK_PRECEDING || pMWin->eEnd==TK_FOLLOWING ){ + regEnd = ++pParse->nMem; + } + + /* If this is not a "ROWS BETWEEN ..." frame, then allocate arrays of + ** registers to store copies of the ORDER BY expressions (peer values) + ** for the main loop, and for each cursor (start, current and end). */ + if( pMWin->eFrmType!=TK_ROWS ){ + int nPeer = (pOrderBy ? pOrderBy->nExpr : 0); + regNewPeer = regNew + pMWin->nBufferCol; + if( pMWin->pPartition ) regNewPeer += pMWin->pPartition->nExpr; + regPeer = pParse->nMem+1; pParse->nMem += nPeer; + s.start.reg = pParse->nMem+1; pParse->nMem += nPeer; + s.current.reg = pParse->nMem+1; pParse->nMem += nPeer; + s.end.reg = pParse->nMem+1; pParse->nMem += nPeer; + } + + /* Load the column values for the row returned by the sub-select + ** into an array of registers starting at regNew. Assemble them into + ** a record in register regRecord. */ + for(iInput=0; iInputpPartition ){ + int addr; + ExprList *pPart = pMWin->pPartition; + int nPart = pPart->nExpr; + int regNewPart = regNew + pMWin->nBufferCol; + KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pPart, 0, 0); + + regFlushPart = ++pParse->nMem; + addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPart, pMWin->regPart, nPart); + sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO); + sqlite3VdbeAddOp3(v, OP_Jump, addr+2, addr+4, addr+2); + VdbeCoverageEqNe(v); + addrGosubFlush = sqlite3VdbeAddOp1(v, OP_Gosub, regFlushPart); + VdbeComment((v, "call flush_partition")); + sqlite3VdbeAddOp3(v, OP_Copy, regNewPart, pMWin->regPart, nPart-1); + } + + /* Insert the new row into the ephemeral table */ + sqlite3VdbeAddOp2(v, OP_NewRowid, csrWrite, regRowid); + sqlite3VdbeAddOp3(v, OP_Insert, csrWrite, regRecord, regRowid); + addrNe = sqlite3VdbeAddOp3(v, OP_Ne, pMWin->regOne, 0, regRowid); + VdbeCoverageNeverNull(v); + + /* This block is run for the first row of each partition */ + s.regArg = windowInitAccum(pParse, pMWin); + + if( regStart ){ + sqlite3ExprCode(pParse, pMWin->pStart, regStart); + windowCheckValue(pParse, regStart, 0 + (pMWin->eFrmType==TK_RANGE ? 3 : 0)); + } + if( regEnd ){ + sqlite3ExprCode(pParse, pMWin->pEnd, regEnd); + windowCheckValue(pParse, regEnd, 1 + (pMWin->eFrmType==TK_RANGE ? 3 : 0)); + } + + if( pMWin->eStart==pMWin->eEnd && regStart ){ + int op = ((pMWin->eStart==TK_FOLLOWING) ? OP_Ge : OP_Le); + int addrGe = sqlite3VdbeAddOp3(v, op, regStart, 0, regEnd); + VdbeCoverageNeverNullIf(v, op==OP_Ge); /* NeverNull because bound */ + VdbeCoverageNeverNullIf(v, op==OP_Le); /* values previously checked */ + windowAggFinal(&s, 0); + sqlite3VdbeAddOp2(v, OP_Rewind, s.current.csr, 1); + VdbeCoverageNeverTaken(v); + windowReturnOneRow(&s); + sqlite3VdbeAddOp1(v, OP_ResetSorter, s.current.csr); + sqlite3VdbeAddOp2(v, OP_Goto, 0, lblWhereEnd); + sqlite3VdbeJumpHere(v, addrGe); + } + if( pMWin->eStart==TK_FOLLOWING && pMWin->eFrmType!=TK_RANGE && regEnd ){ + assert( pMWin->eEnd==TK_FOLLOWING ); + sqlite3VdbeAddOp3(v, OP_Subtract, regStart, regEnd, regStart); + } + + if( pMWin->eStart!=TK_UNBOUNDED ){ + sqlite3VdbeAddOp2(v, OP_Rewind, s.start.csr, 1); + VdbeCoverageNeverTaken(v); + } + sqlite3VdbeAddOp2(v, OP_Rewind, s.current.csr, 1); + VdbeCoverageNeverTaken(v); + sqlite3VdbeAddOp2(v, OP_Rewind, s.end.csr, 1); + VdbeCoverageNeverTaken(v); + if( regPeer && pOrderBy ){ + sqlite3VdbeAddOp3(v, OP_Copy, regNewPeer, regPeer, pOrderBy->nExpr-1); + sqlite3VdbeAddOp3(v, OP_Copy, regPeer, s.start.reg, pOrderBy->nExpr-1); + sqlite3VdbeAddOp3(v, OP_Copy, regPeer, s.current.reg, pOrderBy->nExpr-1); + sqlite3VdbeAddOp3(v, OP_Copy, regPeer, s.end.reg, pOrderBy->nExpr-1); + } + + sqlite3VdbeAddOp2(v, OP_Goto, 0, lblWhereEnd); + + sqlite3VdbeJumpHere(v, addrNe); + + /* Beginning of the block executed for the second and subsequent rows. */ + if( regPeer ){ + windowIfNewPeer(pParse, pOrderBy, regNewPeer, regPeer, lblWhereEnd); + } + if( pMWin->eStart==TK_FOLLOWING ){ + windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0); + if( pMWin->eEnd!=TK_UNBOUNDED ){ + if( pMWin->eFrmType==TK_RANGE ){ + int lbl = sqlite3VdbeMakeLabel(pParse); + int addrNext = sqlite3VdbeCurrentAddr(v); + windowCodeRangeTest(&s, OP_Ge, s.current.csr, regEnd, s.end.csr, lbl); + windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0); + windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0); + sqlite3VdbeAddOp2(v, OP_Goto, 0, addrNext); + sqlite3VdbeResolveLabel(v, lbl); + }else{ + windowCodeOp(&s, WINDOW_RETURN_ROW, regEnd, 0); + windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0); + } + } + }else + if( pMWin->eEnd==TK_PRECEDING ){ + int bRPS = (pMWin->eStart==TK_PRECEDING && pMWin->eFrmType==TK_RANGE); + windowCodeOp(&s, WINDOW_AGGSTEP, regEnd, 0); + if( bRPS ) windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0); + windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0); + if( !bRPS ) windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0); + }else{ + int addr = 0; + windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0); + if( pMWin->eEnd!=TK_UNBOUNDED ){ + if( pMWin->eFrmType==TK_RANGE ){ + int lbl = 0; + addr = sqlite3VdbeCurrentAddr(v); + if( regEnd ){ + lbl = sqlite3VdbeMakeLabel(pParse); + windowCodeRangeTest(&s, OP_Ge, s.current.csr, regEnd, s.end.csr, lbl); + } + windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0); + windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0); + if( regEnd ){ + sqlite3VdbeAddOp2(v, OP_Goto, 0, addr); + sqlite3VdbeResolveLabel(v, lbl); } + }else{ + if( regEnd ){ + addr = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0, 1); + VdbeCoverage(v); + } + windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0); + windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0); + if( regEnd ) sqlite3VdbeJumpHere(v, addr); } } + } - /* Otherwise, call windowCodeDefaultStep(). */ - if( bCache ){ - VdbeModuleComment((pParse->pVdbe, "Begin CacheStep()")); - windowCodeCacheStep(pParse, p, pWInfo, regGosub, addrGosub); - }else{ - VdbeModuleComment((pParse->pVdbe, "Begin DefaultStep()")); - windowCodeDefaultStep(pParse, p, pWInfo, regGosub, addrGosub); + /* End of the main input loop */ + sqlite3VdbeResolveLabel(v, lblWhereEnd); + sqlite3WhereEnd(pWInfo); + + /* Fall through */ + if( pMWin->pPartition ){ + addrInteger = sqlite3VdbeAddOp2(v, OP_Integer, 0, regFlushPart); + sqlite3VdbeJumpHere(v, addrGosubFlush); + } + + addrEmpty = sqlite3VdbeAddOp1(v, OP_Rewind, csrWrite); + VdbeCoverage(v); + if( pMWin->eEnd==TK_PRECEDING ){ + int bRPS = (pMWin->eStart==TK_PRECEDING && pMWin->eFrmType==TK_RANGE); + windowCodeOp(&s, WINDOW_AGGSTEP, regEnd, 0); + if( bRPS ) windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0); + windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0); + }else if( pMWin->eStart==TK_FOLLOWING ){ + int addrStart; + int addrBreak1; + int addrBreak2; + int addrBreak3; + windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0); + if( pMWin->eFrmType==TK_RANGE ){ + addrStart = sqlite3VdbeCurrentAddr(v); + addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 1); + addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 1); + }else + if( pMWin->eEnd==TK_UNBOUNDED ){ + addrStart = sqlite3VdbeCurrentAddr(v); + addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, regStart, 1); + addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, 0, 1); + }else{ + assert( pMWin->eEnd==TK_FOLLOWING ); + addrStart = sqlite3VdbeCurrentAddr(v); + addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, regEnd, 1); + addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 1); + } + sqlite3VdbeAddOp2(v, OP_Goto, 0, addrStart); + sqlite3VdbeJumpHere(v, addrBreak2); + addrStart = sqlite3VdbeCurrentAddr(v); + addrBreak3 = windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 1); + sqlite3VdbeAddOp2(v, OP_Goto, 0, addrStart); + sqlite3VdbeJumpHere(v, addrBreak1); + sqlite3VdbeJumpHere(v, addrBreak3); + }else{ + int addrBreak; + int addrStart; + windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0); + addrStart = sqlite3VdbeCurrentAddr(v); + addrBreak = windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 1); + windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0); + sqlite3VdbeAddOp2(v, OP_Goto, 0, addrStart); + sqlite3VdbeJumpHere(v, addrBreak); + } + sqlite3VdbeJumpHere(v, addrEmpty); + + sqlite3VdbeAddOp1(v, OP_ResetSorter, s.current.csr); + if( pMWin->pPartition ){ + if( pMWin->regStartRowid ){ + sqlite3VdbeAddOp2(v, OP_Integer, 1, pMWin->regStartRowid); + sqlite3VdbeAddOp2(v, OP_Integer, 0, pMWin->regEndRowid); } + sqlite3VdbeChangeP1(v, addrInteger, sqlite3VdbeCurrentAddr(v)); + sqlite3VdbeAddOp1(v, OP_Return, regFlushPart); } } @@ -146939,8 +148252,7 @@ static void disableLookaside(Parse *pParse){ memcpy(p->u.zToken, t.z, t.n); p->u.zToken[t.n] = 0; if( sqlite3Isquote(p->u.zToken[0]) ){ - if( p->u.zToken[0]=='"' ) p->flags |= EP_DblQuoted; - sqlite3Dequote(p->u.zToken); + sqlite3DequoteExpr(p); } #if SQLITE_MAX_EXPR_DEPTH>0 p->nHeight = 1; @@ -146986,6 +148298,10 @@ static void disableLookaside(Parse *pParse){ sqlite3ExprListSetName(pParse, p, pIdToken, 1); return p; } + +#if TK_SPAN>255 +# error too many tokens in the grammar +#endif /**************** End of %include directives **********************************/ /* These constants specify the various numeric values for terminal symbols ** in a format understandable to "makeheaders". This section is blank unless @@ -147049,27 +148365,28 @@ static void disableLookaside(Parse *pParse){ #endif /************* Begin control #defines *****************************************/ #define YYCODETYPE unsigned short int -#define YYNOCODE 277 +#define YYNOCODE 301 #define YYACTIONTYPE unsigned short int -#define YYWILDCARD 91 +#define YYWILDCARD 95 #define sqlite3ParserTOKENTYPE Token typedef union { int yyinit; sqlite3ParserTOKENTYPE yy0; - Expr* yy18; - struct TrigEvent yy34; - IdList* yy48; - int yy70; - struct {int value; int mask;} yy111; - struct FrameBound yy119; - SrcList* yy135; - TriggerStep* yy207; - Window* yy327; - Upsert* yy340; - const char* yy392; - ExprList* yy420; - With* yy449; - Select* yy489; + With* yy59; + IdList* yy62; + struct TrigEvent yy90; + Upsert* yy136; + struct FrameBound yy201; + u8 yy238; + const char* yy294; + Window* yy295; + struct {int value; int mask;} yy355; + ExprList* yy434; + TriggerStep* yy455; + Select* yy457; + SrcList* yy483; + int yy494; + Expr* yy524; } YYMINORTYPE; #ifndef YYSTACKDEPTH #define YYSTACKDEPTH 100 @@ -147085,17 +148402,17 @@ typedef union { #define sqlite3ParserCTX_FETCH Parse *pParse=yypParser->pParse; #define sqlite3ParserCTX_STORE yypParser->pParse=pParse; #define YYFALLBACK 1 -#define YYNSTATE 521 -#define YYNRULE 367 -#define YYNTOKEN 155 -#define YY_MAX_SHIFT 520 -#define YY_MIN_SHIFTREDUCE 756 -#define YY_MAX_SHIFTREDUCE 1122 -#define YY_ERROR_ACTION 1123 -#define YY_ACCEPT_ACTION 1124 -#define YY_NO_ACTION 1125 -#define YY_MIN_REDUCE 1126 -#define YY_MAX_REDUCE 1492 +#define YYNSTATE 541 +#define YYNRULE 375 +#define YYNTOKEN 176 +#define YY_MAX_SHIFT 540 +#define YY_MIN_SHIFTREDUCE 784 +#define YY_MAX_SHIFTREDUCE 1158 +#define YY_ERROR_ACTION 1159 +#define YY_ACCEPT_ACTION 1160 +#define YY_NO_ACTION 1161 +#define YY_MIN_REDUCE 1162 +#define YY_MAX_REDUCE 1536 /************* End control #defines *******************************************/ #define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0]))) @@ -147162,568 +148479,603 @@ typedef union { ** yy_default[] Default action for each state. ** *********** Begin parsing tables **********************************************/ -#define YY_ACTTAB_COUNT (2009) +#define YY_ACTTAB_COUNT (2142) static const YYACTIONTYPE yy_action[] = { - /* 0 */ 368, 105, 102, 197, 105, 102, 197, 515, 1124, 1, - /* 10 */ 1, 520, 2, 1128, 515, 1192, 1171, 1456, 275, 370, - /* 20 */ 127, 1389, 1197, 1197, 1192, 1166, 178, 1205, 64, 64, - /* 30 */ 477, 887, 322, 428, 348, 37, 37, 808, 362, 888, - /* 40 */ 509, 509, 509, 112, 113, 103, 1100, 1100, 953, 956, - /* 50 */ 946, 946, 110, 110, 111, 111, 111, 111, 365, 252, - /* 60 */ 252, 515, 252, 252, 497, 515, 309, 515, 459, 515, - /* 70 */ 1079, 491, 512, 478, 6, 512, 809, 134, 498, 228, - /* 80 */ 194, 428, 37, 37, 515, 208, 64, 64, 64, 64, - /* 90 */ 13, 13, 109, 109, 109, 109, 108, 108, 107, 107, - /* 100 */ 107, 106, 401, 258, 381, 13, 13, 398, 397, 428, - /* 110 */ 252, 252, 370, 476, 405, 1104, 1079, 1080, 1081, 386, - /* 120 */ 1106, 390, 497, 512, 497, 1423, 1419, 304, 1105, 307, - /* 130 */ 1256, 496, 370, 499, 16, 16, 112, 113, 103, 1100, - /* 140 */ 1100, 953, 956, 946, 946, 110, 110, 111, 111, 111, - /* 150 */ 111, 262, 1107, 495, 1107, 401, 112, 113, 103, 1100, - /* 160 */ 1100, 953, 956, 946, 946, 110, 110, 111, 111, 111, - /* 170 */ 111, 129, 1425, 343, 1420, 339, 1059, 492, 1057, 263, - /* 180 */ 73, 105, 102, 197, 994, 109, 109, 109, 109, 108, - /* 190 */ 108, 107, 107, 107, 106, 401, 370, 111, 111, 111, - /* 200 */ 111, 104, 492, 89, 1432, 109, 109, 109, 109, 108, - /* 210 */ 108, 107, 107, 107, 106, 401, 111, 111, 111, 111, - /* 220 */ 112, 113, 103, 1100, 1100, 953, 956, 946, 946, 110, - /* 230 */ 110, 111, 111, 111, 111, 109, 109, 109, 109, 108, - /* 240 */ 108, 107, 107, 107, 106, 401, 114, 108, 108, 107, - /* 250 */ 107, 107, 106, 401, 109, 109, 109, 109, 108, 108, - /* 260 */ 107, 107, 107, 106, 401, 152, 399, 399, 399, 109, - /* 270 */ 109, 109, 109, 108, 108, 107, 107, 107, 106, 401, - /* 280 */ 178, 493, 1412, 434, 1037, 1486, 1079, 515, 1486, 370, - /* 290 */ 421, 297, 357, 412, 74, 1079, 109, 109, 109, 109, - /* 300 */ 108, 108, 107, 107, 107, 106, 401, 1413, 37, 37, - /* 310 */ 1431, 274, 506, 112, 113, 103, 1100, 1100, 953, 956, - /* 320 */ 946, 946, 110, 110, 111, 111, 111, 111, 1436, 520, - /* 330 */ 2, 1128, 1079, 1080, 1081, 430, 275, 1079, 127, 366, - /* 340 */ 933, 1079, 1080, 1081, 220, 1205, 913, 458, 455, 454, - /* 350 */ 392, 167, 515, 1035, 152, 445, 924, 453, 152, 874, - /* 360 */ 923, 289, 109, 109, 109, 109, 108, 108, 107, 107, - /* 370 */ 107, 106, 401, 13, 13, 261, 853, 252, 252, 227, - /* 380 */ 106, 401, 370, 1079, 1080, 1081, 311, 388, 1079, 296, - /* 390 */ 512, 923, 923, 925, 231, 323, 1255, 1388, 1423, 490, - /* 400 */ 274, 506, 12, 208, 274, 506, 112, 113, 103, 1100, - /* 410 */ 1100, 953, 956, 946, 946, 110, 110, 111, 111, 111, - /* 420 */ 111, 1440, 286, 1128, 288, 1079, 1097, 247, 275, 1098, - /* 430 */ 127, 387, 405, 389, 1079, 1080, 1081, 1205, 159, 238, - /* 440 */ 255, 321, 461, 316, 460, 225, 790, 105, 102, 197, - /* 450 */ 513, 314, 842, 842, 445, 109, 109, 109, 109, 108, - /* 460 */ 108, 107, 107, 107, 106, 401, 515, 514, 515, 252, - /* 470 */ 252, 1079, 1080, 1081, 435, 370, 1098, 933, 1460, 794, - /* 480 */ 274, 506, 512, 105, 102, 197, 336, 63, 63, 64, - /* 490 */ 64, 27, 790, 924, 287, 208, 1354, 923, 515, 112, - /* 500 */ 113, 103, 1100, 1100, 953, 956, 946, 946, 110, 110, - /* 510 */ 111, 111, 111, 111, 107, 107, 107, 106, 401, 49, - /* 520 */ 49, 515, 28, 1079, 405, 497, 421, 297, 923, 923, - /* 530 */ 925, 186, 468, 1079, 467, 999, 999, 442, 515, 1079, - /* 540 */ 334, 515, 45, 45, 1083, 342, 173, 168, 109, 109, - /* 550 */ 109, 109, 108, 108, 107, 107, 107, 106, 401, 13, - /* 560 */ 13, 205, 13, 13, 252, 252, 1195, 1195, 370, 1079, - /* 570 */ 1080, 1081, 787, 265, 5, 359, 494, 512, 469, 1079, - /* 580 */ 1080, 1081, 398, 397, 1079, 1079, 1080, 1081, 3, 282, - /* 590 */ 1079, 1083, 112, 113, 103, 1100, 1100, 953, 956, 946, - /* 600 */ 946, 110, 110, 111, 111, 111, 111, 252, 252, 1015, - /* 610 */ 220, 1079, 873, 458, 455, 454, 943, 943, 954, 957, - /* 620 */ 512, 252, 252, 453, 1016, 1079, 445, 1107, 1209, 1107, - /* 630 */ 1079, 1080, 1081, 515, 512, 426, 1079, 1080, 1081, 1017, - /* 640 */ 512, 109, 109, 109, 109, 108, 108, 107, 107, 107, - /* 650 */ 106, 401, 1052, 515, 50, 50, 515, 1079, 1080, 1081, - /* 660 */ 828, 370, 1051, 379, 411, 1064, 1358, 207, 408, 773, - /* 670 */ 829, 1079, 1080, 1081, 64, 64, 322, 64, 64, 1302, - /* 680 */ 947, 411, 410, 1358, 1360, 112, 113, 103, 1100, 1100, - /* 690 */ 953, 956, 946, 946, 110, 110, 111, 111, 111, 111, - /* 700 */ 294, 482, 515, 1037, 1487, 515, 434, 1487, 354, 1120, - /* 710 */ 483, 996, 913, 485, 466, 996, 132, 178, 33, 450, - /* 720 */ 1203, 136, 406, 64, 64, 479, 64, 64, 419, 369, - /* 730 */ 283, 1146, 252, 252, 109, 109, 109, 109, 108, 108, - /* 740 */ 107, 107, 107, 106, 401, 512, 224, 440, 411, 266, - /* 750 */ 1358, 266, 252, 252, 370, 296, 416, 284, 934, 396, - /* 760 */ 976, 470, 400, 252, 252, 512, 9, 473, 231, 500, - /* 770 */ 354, 1036, 1035, 1488, 355, 374, 512, 1121, 112, 113, - /* 780 */ 103, 1100, 1100, 953, 956, 946, 946, 110, 110, 111, - /* 790 */ 111, 111, 111, 252, 252, 1015, 515, 1347, 295, 252, - /* 800 */ 252, 252, 252, 1098, 375, 249, 512, 445, 872, 322, - /* 810 */ 1016, 480, 512, 195, 512, 434, 273, 15, 15, 515, - /* 820 */ 314, 515, 95, 515, 93, 1017, 367, 109, 109, 109, - /* 830 */ 109, 108, 108, 107, 107, 107, 106, 401, 515, 1121, - /* 840 */ 39, 39, 51, 51, 52, 52, 503, 370, 515, 1204, - /* 850 */ 1098, 918, 439, 341, 133, 436, 223, 222, 221, 53, - /* 860 */ 53, 322, 1400, 761, 762, 763, 515, 370, 88, 54, - /* 870 */ 54, 112, 113, 103, 1100, 1100, 953, 956, 946, 946, - /* 880 */ 110, 110, 111, 111, 111, 111, 407, 55, 55, 196, - /* 890 */ 515, 112, 113, 103, 1100, 1100, 953, 956, 946, 946, - /* 900 */ 110, 110, 111, 111, 111, 111, 135, 264, 1149, 376, - /* 910 */ 515, 40, 40, 515, 872, 515, 993, 515, 993, 116, - /* 920 */ 109, 109, 109, 109, 108, 108, 107, 107, 107, 106, - /* 930 */ 401, 41, 41, 515, 43, 43, 44, 44, 56, 56, - /* 940 */ 109, 109, 109, 109, 108, 108, 107, 107, 107, 106, - /* 950 */ 401, 515, 379, 515, 57, 57, 515, 799, 515, 379, - /* 960 */ 515, 445, 200, 515, 323, 515, 1397, 515, 1459, 515, - /* 970 */ 1287, 817, 58, 58, 14, 14, 515, 59, 59, 118, - /* 980 */ 118, 60, 60, 515, 46, 46, 61, 61, 62, 62, - /* 990 */ 47, 47, 515, 190, 189, 91, 515, 140, 140, 515, - /* 1000 */ 394, 515, 277, 1200, 141, 141, 515, 1115, 515, 992, - /* 1010 */ 515, 992, 515, 69, 69, 370, 278, 48, 48, 259, - /* 1020 */ 65, 65, 119, 119, 246, 246, 260, 66, 66, 120, - /* 1030 */ 120, 121, 121, 117, 117, 370, 515, 512, 383, 112, - /* 1040 */ 113, 103, 1100, 1100, 953, 956, 946, 946, 110, 110, - /* 1050 */ 111, 111, 111, 111, 515, 872, 515, 139, 139, 112, - /* 1060 */ 113, 103, 1100, 1100, 953, 956, 946, 946, 110, 110, - /* 1070 */ 111, 111, 111, 111, 1287, 138, 138, 125, 125, 515, - /* 1080 */ 12, 515, 281, 1287, 515, 445, 131, 1287, 109, 109, - /* 1090 */ 109, 109, 108, 108, 107, 107, 107, 106, 401, 515, - /* 1100 */ 124, 124, 122, 122, 515, 123, 123, 515, 109, 109, - /* 1110 */ 109, 109, 108, 108, 107, 107, 107, 106, 401, 515, - /* 1120 */ 68, 68, 463, 783, 515, 70, 70, 302, 67, 67, - /* 1130 */ 1032, 253, 253, 356, 1287, 191, 196, 1433, 465, 1301, - /* 1140 */ 38, 38, 384, 94, 512, 42, 42, 177, 848, 274, - /* 1150 */ 506, 385, 420, 847, 1356, 441, 508, 376, 377, 153, - /* 1160 */ 423, 872, 432, 370, 224, 251, 194, 887, 182, 293, - /* 1170 */ 783, 848, 88, 254, 466, 888, 847, 915, 807, 806, - /* 1180 */ 230, 1241, 910, 370, 17, 413, 797, 112, 113, 103, - /* 1190 */ 1100, 1100, 953, 956, 946, 946, 110, 110, 111, 111, - /* 1200 */ 111, 111, 395, 814, 815, 1175, 983, 112, 101, 103, - /* 1210 */ 1100, 1100, 953, 956, 946, 946, 110, 110, 111, 111, - /* 1220 */ 111, 111, 375, 422, 427, 429, 298, 230, 230, 88, - /* 1230 */ 1240, 451, 312, 797, 226, 88, 109, 109, 109, 109, - /* 1240 */ 108, 108, 107, 107, 107, 106, 401, 86, 433, 979, - /* 1250 */ 927, 881, 226, 983, 230, 415, 109, 109, 109, 109, - /* 1260 */ 108, 108, 107, 107, 107, 106, 401, 320, 845, 781, - /* 1270 */ 846, 100, 130, 100, 1403, 290, 370, 319, 1377, 1376, - /* 1280 */ 437, 1449, 299, 1237, 303, 306, 308, 310, 1188, 1174, - /* 1290 */ 1173, 1172, 315, 324, 325, 1228, 370, 927, 1249, 271, - /* 1300 */ 1286, 113, 103, 1100, 1100, 953, 956, 946, 946, 110, - /* 1310 */ 110, 111, 111, 111, 111, 1224, 1235, 502, 501, 1292, - /* 1320 */ 1221, 1155, 103, 1100, 1100, 953, 956, 946, 946, 110, - /* 1330 */ 110, 111, 111, 111, 111, 1148, 1137, 1136, 1138, 1443, - /* 1340 */ 446, 244, 184, 98, 507, 188, 4, 353, 327, 109, - /* 1350 */ 109, 109, 109, 108, 108, 107, 107, 107, 106, 401, - /* 1360 */ 510, 329, 331, 199, 414, 456, 292, 285, 318, 109, - /* 1370 */ 109, 109, 109, 108, 108, 107, 107, 107, 106, 401, - /* 1380 */ 11, 1271, 1279, 402, 361, 192, 1171, 1351, 431, 505, - /* 1390 */ 346, 1350, 333, 98, 507, 504, 4, 187, 1446, 1115, - /* 1400 */ 233, 1396, 155, 1394, 1112, 152, 72, 75, 378, 425, - /* 1410 */ 510, 165, 149, 157, 933, 1276, 86, 30, 1268, 417, - /* 1420 */ 96, 96, 8, 160, 161, 162, 163, 97, 418, 402, - /* 1430 */ 517, 516, 449, 402, 923, 210, 358, 424, 1282, 438, - /* 1440 */ 169, 214, 360, 1345, 80, 504, 31, 444, 1365, 301, - /* 1450 */ 245, 274, 506, 216, 174, 305, 488, 447, 217, 462, - /* 1460 */ 1139, 487, 218, 363, 933, 923, 923, 925, 926, 24, - /* 1470 */ 96, 96, 1191, 1190, 1189, 391, 1182, 97, 1163, 402, - /* 1480 */ 517, 516, 799, 364, 923, 1162, 317, 1161, 98, 507, - /* 1490 */ 1181, 4, 1458, 472, 393, 269, 270, 475, 481, 1232, - /* 1500 */ 85, 1233, 326, 328, 232, 510, 495, 1231, 330, 98, - /* 1510 */ 507, 1230, 4, 486, 335, 923, 923, 925, 926, 24, - /* 1520 */ 1435, 1068, 404, 181, 336, 256, 510, 115, 402, 332, - /* 1530 */ 352, 352, 351, 241, 349, 1214, 1414, 770, 338, 10, - /* 1540 */ 504, 340, 272, 92, 1331, 1213, 87, 183, 484, 402, - /* 1550 */ 201, 488, 280, 239, 344, 345, 489, 1145, 29, 933, - /* 1560 */ 279, 504, 1074, 518, 240, 96, 96, 242, 243, 519, - /* 1570 */ 1134, 1129, 97, 154, 402, 517, 516, 372, 373, 923, - /* 1580 */ 933, 142, 143, 128, 1381, 267, 96, 96, 852, 757, - /* 1590 */ 203, 144, 403, 97, 1382, 402, 517, 516, 204, 1380, - /* 1600 */ 923, 146, 1379, 1159, 1158, 71, 1156, 276, 202, 185, - /* 1610 */ 923, 923, 925, 926, 24, 198, 257, 126, 991, 989, - /* 1620 */ 907, 98, 507, 156, 4, 145, 158, 206, 831, 209, - /* 1630 */ 291, 923, 923, 925, 926, 24, 1005, 911, 510, 164, - /* 1640 */ 147, 380, 371, 382, 166, 76, 77, 274, 506, 148, - /* 1650 */ 78, 79, 1008, 211, 212, 1004, 137, 213, 18, 300, - /* 1660 */ 230, 402, 997, 1109, 443, 215, 32, 170, 171, 772, - /* 1670 */ 409, 448, 319, 504, 219, 172, 452, 81, 19, 457, - /* 1680 */ 313, 20, 82, 268, 488, 150, 810, 179, 83, 487, - /* 1690 */ 464, 151, 933, 180, 959, 84, 1040, 34, 96, 96, - /* 1700 */ 471, 1041, 35, 474, 193, 97, 248, 402, 517, 516, - /* 1710 */ 1068, 404, 923, 250, 256, 880, 229, 175, 875, 352, - /* 1720 */ 352, 351, 241, 349, 100, 21, 770, 22, 1054, 1056, - /* 1730 */ 7, 98, 507, 1045, 4, 337, 1058, 23, 974, 201, - /* 1740 */ 176, 280, 88, 923, 923, 925, 926, 24, 510, 279, - /* 1750 */ 960, 958, 962, 1014, 963, 1013, 235, 234, 25, 36, - /* 1760 */ 99, 90, 507, 928, 4, 511, 350, 782, 26, 841, - /* 1770 */ 236, 402, 347, 1069, 237, 1125, 1125, 1451, 510, 203, - /* 1780 */ 1450, 1125, 1125, 504, 1125, 1125, 1125, 204, 1125, 1125, - /* 1790 */ 146, 1125, 1125, 1125, 1125, 1125, 1125, 202, 1125, 1125, - /* 1800 */ 1125, 402, 933, 1125, 1125, 1125, 1125, 1125, 96, 96, - /* 1810 */ 1125, 1125, 1125, 504, 1125, 97, 1125, 402, 517, 516, - /* 1820 */ 1125, 1125, 923, 1125, 1125, 1125, 1125, 1125, 1125, 1125, - /* 1830 */ 1125, 371, 933, 1125, 1125, 1125, 274, 506, 96, 96, - /* 1840 */ 1125, 1125, 1125, 1125, 1125, 97, 1125, 402, 517, 516, - /* 1850 */ 1125, 1125, 923, 923, 923, 925, 926, 24, 1125, 409, - /* 1860 */ 1125, 1125, 1125, 256, 1125, 1125, 1125, 1125, 352, 352, - /* 1870 */ 351, 241, 349, 1125, 1125, 770, 1125, 1125, 1125, 1125, - /* 1880 */ 1125, 1125, 1125, 923, 923, 925, 926, 24, 201, 1125, - /* 1890 */ 280, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 279, 1125, - /* 1900 */ 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, - /* 1910 */ 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, - /* 1920 */ 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 203, 1125, - /* 1930 */ 1125, 1125, 1125, 1125, 1125, 1125, 204, 1125, 1125, 146, - /* 1940 */ 1125, 1125, 1125, 1125, 1125, 1125, 202, 1125, 1125, 1125, - /* 1950 */ 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, - /* 1960 */ 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, - /* 1970 */ 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, - /* 1980 */ 371, 1125, 1125, 1125, 1125, 274, 506, 1125, 1125, 1125, - /* 1990 */ 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, - /* 2000 */ 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 409, + /* 0 */ 535, 1323, 112, 109, 209, 112, 109, 209, 1160, 1, + /* 10 */ 1, 540, 2, 1164, 535, 1292, 1228, 1207, 289, 384, + /* 20 */ 134, 42, 42, 1427, 382, 1228, 9, 1241, 242, 492, + /* 30 */ 1291, 915, 373, 379, 1026, 70, 70, 427, 1026, 916, + /* 40 */ 529, 529, 529, 119, 120, 110, 1136, 1136, 981, 984, + /* 50 */ 974, 974, 117, 117, 118, 118, 118, 118, 380, 264, + /* 60 */ 264, 264, 264, 1134, 264, 264, 112, 109, 209, 397, + /* 70 */ 454, 517, 532, 491, 532, 1233, 1233, 532, 239, 206, + /* 80 */ 493, 112, 109, 209, 464, 219, 118, 118, 118, 118, + /* 90 */ 111, 393, 440, 444, 16, 16, 116, 116, 116, 116, + /* 100 */ 115, 115, 114, 114, 114, 113, 415, 971, 971, 982, + /* 110 */ 985, 235, 1463, 351, 1134, 419, 384, 116, 116, 116, + /* 120 */ 116, 115, 115, 114, 114, 114, 113, 415, 116, 116, + /* 130 */ 116, 116, 115, 115, 114, 114, 114, 113, 415, 961, + /* 140 */ 119, 120, 110, 1136, 1136, 981, 984, 974, 974, 117, + /* 150 */ 117, 118, 118, 118, 118, 952, 415, 941, 298, 951, + /* 160 */ 941, 1480, 540, 2, 1164, 1115, 535, 1458, 160, 289, + /* 170 */ 6, 134, 1504, 389, 406, 975, 338, 1024, 1241, 337, + /* 180 */ 1089, 1476, 1089, 118, 118, 118, 118, 42, 42, 329, + /* 190 */ 951, 951, 953, 116, 116, 116, 116, 115, 115, 114, + /* 200 */ 114, 114, 113, 415, 311, 430, 299, 311, 881, 160, + /* 210 */ 264, 264, 401, 384, 324, 1115, 1116, 1117, 288, 526, + /* 220 */ 96, 159, 1441, 532, 141, 116, 116, 116, 116, 115, + /* 230 */ 115, 114, 114, 114, 113, 415, 219, 119, 120, 110, + /* 240 */ 1136, 1136, 981, 984, 974, 974, 117, 117, 118, 118, + /* 250 */ 118, 118, 115, 115, 114, 114, 114, 113, 415, 288, + /* 260 */ 526, 403, 533, 121, 870, 870, 419, 250, 267, 336, + /* 270 */ 475, 331, 474, 236, 160, 319, 1084, 322, 1465, 329, + /* 280 */ 350, 12, 535, 384, 502, 1115, 1084, 435, 312, 1084, + /* 290 */ 116, 116, 116, 116, 115, 115, 114, 114, 114, 113, + /* 300 */ 415, 535, 836, 42, 42, 138, 426, 119, 120, 110, + /* 310 */ 1136, 1136, 981, 984, 974, 974, 117, 117, 118, 118, + /* 320 */ 118, 118, 70, 70, 288, 526, 412, 411, 480, 1457, + /* 330 */ 335, 79, 6, 473, 1140, 1115, 1116, 1117, 501, 1142, + /* 340 */ 334, 837, 811, 1484, 512, 1164, 534, 1141, 123, 187, + /* 350 */ 289, 384, 134, 448, 434, 1115, 80, 349, 498, 1241, + /* 360 */ 116, 116, 116, 116, 115, 115, 114, 114, 114, 113, + /* 370 */ 415, 1143, 1115, 1143, 459, 119, 120, 110, 1136, 1136, + /* 380 */ 981, 984, 974, 974, 117, 117, 118, 118, 118, 118, + /* 390 */ 404, 264, 264, 811, 1463, 506, 368, 1156, 535, 114, + /* 400 */ 114, 114, 113, 415, 532, 1115, 1116, 1117, 231, 518, + /* 410 */ 1500, 472, 469, 468, 175, 497, 422, 219, 1202, 70, + /* 420 */ 70, 467, 1115, 1116, 1117, 176, 201, 200, 116, 116, + /* 430 */ 116, 116, 115, 115, 114, 114, 114, 113, 415, 535, + /* 440 */ 1115, 264, 264, 435, 312, 1115, 273, 419, 384, 513, + /* 450 */ 1450, 1115, 326, 1084, 532, 517, 82, 1084, 167, 388, + /* 460 */ 69, 69, 1115, 1084, 519, 509, 1084, 1084, 12, 1157, + /* 470 */ 1084, 420, 119, 120, 110, 1136, 1136, 981, 984, 974, + /* 480 */ 974, 117, 117, 118, 118, 118, 118, 258, 258, 535, + /* 490 */ 1115, 1116, 1117, 1045, 535, 1115, 1116, 1117, 1323, 535, + /* 500 */ 532, 1115, 1116, 1117, 296, 483, 1211, 818, 1046, 448, + /* 510 */ 70, 70, 1115, 1116, 1117, 50, 50, 448, 356, 500, + /* 520 */ 70, 70, 207, 1047, 32, 116, 116, 116, 116, 115, + /* 530 */ 115, 114, 114, 114, 113, 415, 453, 264, 264, 1115, + /* 540 */ 450, 449, 961, 508, 856, 384, 517, 5, 900, 822, + /* 550 */ 532, 484, 181, 1115, 857, 516, 517, 818, 952, 507, + /* 560 */ 3, 1115, 951, 1231, 1231, 482, 398, 1115, 1095, 119, + /* 570 */ 120, 110, 1136, 1136, 981, 984, 974, 974, 117, 117, + /* 580 */ 118, 118, 118, 118, 1115, 535, 238, 1115, 1391, 1115, + /* 590 */ 1116, 1117, 159, 951, 951, 953, 231, 1115, 259, 472, + /* 600 */ 469, 468, 310, 1115, 1116, 1117, 13, 13, 297, 467, + /* 610 */ 276, 1115, 1116, 1117, 412, 411, 1095, 1115, 1116, 1117, + /* 620 */ 395, 355, 116, 116, 116, 116, 115, 115, 114, 114, + /* 630 */ 114, 113, 415, 208, 1115, 1116, 1117, 1115, 1116, 1117, + /* 640 */ 264, 264, 384, 337, 902, 393, 815, 1115, 1116, 1117, + /* 650 */ 413, 413, 413, 532, 112, 109, 209, 309, 900, 1143, + /* 660 */ 535, 1143, 535, 393, 901, 1210, 119, 120, 110, 1136, + /* 670 */ 1136, 981, 984, 974, 974, 117, 117, 118, 118, 118, + /* 680 */ 118, 13, 13, 13, 13, 265, 265, 535, 143, 264, + /* 690 */ 264, 288, 526, 535, 1119, 400, 535, 402, 532, 510, + /* 700 */ 1457, 512, 532, 6, 113, 415, 1067, 1530, 70, 70, + /* 710 */ 1530, 535, 271, 535, 70, 70, 535, 13, 13, 116, + /* 720 */ 116, 116, 116, 115, 115, 114, 114, 114, 113, 415, + /* 730 */ 272, 277, 13, 13, 13, 13, 535, 13, 13, 384, + /* 740 */ 535, 304, 425, 1100, 284, 1119, 184, 801, 185, 338, + /* 750 */ 285, 514, 1532, 369, 1239, 1438, 1182, 70, 70, 425, + /* 760 */ 424, 70, 70, 119, 120, 110, 1136, 1136, 981, 984, + /* 770 */ 974, 974, 117, 117, 118, 118, 118, 118, 190, 1065, + /* 780 */ 1067, 1531, 442, 107, 1531, 408, 264, 264, 264, 264, + /* 790 */ 383, 1396, 261, 410, 95, 900, 485, 414, 421, 532, + /* 800 */ 1045, 532, 301, 1133, 303, 488, 433, 1451, 1396, 1398, + /* 810 */ 278, 535, 278, 520, 1435, 1046, 116, 116, 116, 116, + /* 820 */ 115, 115, 114, 114, 114, 113, 415, 425, 264, 264, + /* 830 */ 1047, 190, 54, 54, 535, 291, 384, 264, 264, 362, + /* 840 */ 962, 532, 1004, 376, 1084, 264, 264, 1029, 1029, 456, + /* 850 */ 532, 523, 270, 1065, 1084, 55, 55, 1084, 532, 442, + /* 860 */ 119, 120, 110, 1136, 1136, 981, 984, 974, 974, 117, + /* 870 */ 117, 118, 118, 118, 118, 535, 1396, 190, 302, 1383, + /* 880 */ 208, 535, 789, 790, 791, 535, 515, 535, 1323, 371, + /* 890 */ 337, 234, 233, 232, 459, 515, 15, 15, 459, 477, + /* 900 */ 459, 459, 44, 44, 136, 900, 56, 56, 57, 57, + /* 910 */ 1185, 390, 197, 116, 116, 116, 116, 115, 115, 114, + /* 920 */ 114, 114, 113, 415, 535, 876, 535, 442, 535, 274, + /* 930 */ 875, 1323, 357, 384, 353, 140, 1426, 946, 1455, 1323, + /* 940 */ 1390, 6, 1240, 1236, 292, 58, 58, 59, 59, 60, + /* 950 */ 60, 535, 1456, 384, 535, 6, 399, 119, 120, 110, + /* 960 */ 1136, 1136, 981, 984, 974, 974, 117, 117, 118, 118, + /* 970 */ 118, 118, 61, 61, 535, 45, 45, 119, 120, 110, + /* 980 */ 1136, 1136, 981, 984, 974, 974, 117, 117, 118, 118, + /* 990 */ 118, 118, 1477, 479, 202, 46, 46, 275, 95, 455, + /* 1000 */ 535, 212, 535, 337, 535, 1454, 535, 409, 6, 242, + /* 1010 */ 116, 116, 116, 116, 115, 115, 114, 114, 114, 113, + /* 1020 */ 415, 48, 48, 49, 49, 62, 62, 63, 63, 535, + /* 1030 */ 116, 116, 116, 116, 115, 115, 114, 114, 114, 113, + /* 1040 */ 415, 535, 459, 535, 1134, 535, 1151, 535, 142, 535, + /* 1050 */ 64, 64, 535, 1338, 535, 494, 535, 446, 535, 1264, + /* 1060 */ 535, 1337, 14, 14, 65, 65, 125, 125, 66, 66, + /* 1070 */ 51, 51, 535, 67, 67, 68, 68, 52, 52, 147, + /* 1080 */ 147, 148, 148, 1453, 317, 98, 6, 535, 1245, 481, + /* 1090 */ 535, 827, 535, 75, 75, 1134, 102, 481, 100, 535, + /* 1100 */ 532, 535, 368, 1066, 1503, 384, 535, 845, 53, 53, + /* 1110 */ 93, 71, 71, 126, 126, 295, 528, 390, 288, 526, + /* 1120 */ 72, 72, 127, 127, 139, 384, 38, 128, 128, 119, + /* 1130 */ 120, 110, 1136, 1136, 981, 984, 974, 974, 117, 117, + /* 1140 */ 118, 118, 118, 118, 535, 495, 535, 447, 535, 119, + /* 1150 */ 120, 110, 1136, 1136, 981, 984, 974, 974, 117, 117, + /* 1160 */ 118, 118, 118, 118, 235, 124, 124, 146, 146, 145, + /* 1170 */ 145, 287, 535, 1277, 535, 1157, 535, 391, 161, 263, + /* 1180 */ 206, 381, 116, 116, 116, 116, 115, 115, 114, 114, + /* 1190 */ 114, 113, 415, 132, 132, 131, 131, 129, 129, 535, + /* 1200 */ 30, 535, 116, 116, 116, 116, 115, 115, 114, 114, + /* 1210 */ 114, 113, 415, 535, 216, 1062, 1276, 535, 370, 535, + /* 1220 */ 130, 130, 74, 74, 535, 915, 389, 876, 17, 437, + /* 1230 */ 429, 31, 875, 916, 76, 76, 266, 101, 73, 73, + /* 1240 */ 43, 43, 835, 834, 308, 47, 47, 95, 825, 943, + /* 1250 */ 441, 938, 241, 241, 305, 443, 313, 384, 241, 95, + /* 1260 */ 842, 843, 193, 465, 1209, 327, 237, 436, 95, 1011, + /* 1270 */ 1007, 909, 873, 237, 241, 107, 1023, 384, 1023, 955, + /* 1280 */ 1415, 119, 120, 110, 1136, 1136, 981, 984, 974, 974, + /* 1290 */ 117, 117, 118, 118, 118, 118, 1022, 809, 1022, 825, + /* 1300 */ 137, 119, 108, 110, 1136, 1136, 981, 984, 974, 974, + /* 1310 */ 117, 117, 118, 118, 118, 118, 874, 1414, 451, 107, + /* 1320 */ 1011, 314, 1273, 318, 218, 321, 323, 325, 1224, 1208, + /* 1330 */ 955, 330, 339, 340, 116, 116, 116, 116, 115, 115, + /* 1340 */ 114, 114, 114, 113, 415, 1285, 1322, 1260, 1493, 1470, + /* 1350 */ 1271, 283, 521, 1328, 116, 116, 116, 116, 115, 115, + /* 1360 */ 114, 114, 114, 113, 415, 1191, 1184, 1173, 1172, 1174, + /* 1370 */ 522, 1487, 211, 460, 384, 256, 199, 367, 1257, 342, + /* 1380 */ 195, 470, 307, 344, 11, 333, 525, 445, 1307, 1315, + /* 1390 */ 375, 203, 1207, 1151, 384, 346, 1387, 188, 360, 120, + /* 1400 */ 110, 1136, 1136, 981, 984, 974, 974, 117, 117, 118, + /* 1410 */ 118, 118, 118, 1386, 428, 1490, 245, 300, 348, 1148, + /* 1420 */ 110, 1136, 1136, 981, 984, 974, 974, 117, 117, 118, + /* 1430 */ 118, 118, 118, 189, 198, 1434, 1432, 78, 81, 163, + /* 1440 */ 82, 392, 439, 1392, 173, 105, 527, 35, 4, 157, + /* 1450 */ 1312, 116, 116, 116, 116, 115, 115, 114, 114, 114, + /* 1460 */ 113, 415, 530, 165, 93, 1304, 431, 432, 168, 463, + /* 1470 */ 221, 116, 116, 116, 116, 115, 115, 114, 114, 114, + /* 1480 */ 113, 415, 169, 452, 170, 416, 171, 374, 372, 438, + /* 1490 */ 36, 1318, 177, 225, 1381, 87, 458, 524, 1403, 316, + /* 1500 */ 257, 105, 527, 227, 4, 182, 461, 160, 320, 228, + /* 1510 */ 377, 1175, 476, 229, 1227, 1226, 405, 1225, 530, 1218, + /* 1520 */ 961, 378, 1199, 1198, 827, 332, 103, 103, 1197, 407, + /* 1530 */ 8, 1217, 1502, 104, 487, 416, 537, 536, 281, 282, + /* 1540 */ 951, 416, 490, 1268, 496, 92, 341, 243, 1269, 343, + /* 1550 */ 244, 1267, 122, 524, 345, 1461, 515, 288, 526, 10, + /* 1560 */ 354, 1266, 1460, 352, 504, 1250, 99, 1367, 94, 503, + /* 1570 */ 499, 951, 951, 953, 954, 27, 961, 347, 1249, 194, + /* 1580 */ 251, 358, 103, 103, 359, 1181, 34, 538, 1110, 104, + /* 1590 */ 255, 416, 537, 536, 286, 252, 951, 254, 539, 149, + /* 1600 */ 1170, 1419, 1165, 1420, 1418, 150, 1417, 135, 279, 785, + /* 1610 */ 151, 417, 1195, 196, 290, 210, 386, 1194, 269, 387, + /* 1620 */ 162, 1021, 133, 77, 1192, 1019, 935, 951, 951, 953, + /* 1630 */ 954, 27, 1479, 1104, 418, 164, 153, 268, 217, 166, + /* 1640 */ 859, 306, 366, 366, 365, 253, 363, 220, 1035, 798, + /* 1650 */ 172, 939, 105, 527, 155, 4, 394, 174, 396, 156, + /* 1660 */ 83, 1038, 213, 84, 294, 85, 86, 223, 222, 530, + /* 1670 */ 1034, 144, 293, 18, 224, 315, 241, 1027, 1145, 178, + /* 1680 */ 457, 226, 179, 37, 800, 334, 462, 230, 328, 466, + /* 1690 */ 180, 471, 416, 88, 19, 20, 89, 280, 838, 158, + /* 1700 */ 191, 90, 215, 478, 524, 1097, 204, 192, 987, 91, + /* 1710 */ 152, 1070, 39, 154, 1071, 504, 486, 40, 489, 205, + /* 1720 */ 505, 260, 105, 527, 214, 4, 908, 961, 262, 183, + /* 1730 */ 240, 21, 903, 103, 103, 107, 22, 1086, 23, 530, + /* 1740 */ 104, 1088, 416, 537, 536, 24, 1093, 951, 25, 1074, + /* 1750 */ 1090, 1094, 7, 33, 511, 186, 26, 1002, 385, 95, + /* 1760 */ 988, 986, 416, 288, 526, 990, 1044, 246, 1043, 247, + /* 1770 */ 991, 28, 41, 106, 524, 956, 810, 29, 951, 951, + /* 1780 */ 953, 954, 27, 531, 361, 504, 423, 248, 869, 249, + /* 1790 */ 503, 1495, 364, 1105, 1161, 1494, 1161, 961, 1161, 1161, + /* 1800 */ 1161, 1161, 1161, 103, 103, 1161, 1161, 1161, 1161, 1161, + /* 1810 */ 104, 1161, 416, 537, 536, 1104, 418, 951, 1161, 268, + /* 1820 */ 1161, 1161, 1161, 1161, 366, 366, 365, 253, 363, 1161, + /* 1830 */ 1161, 798, 1161, 1161, 1161, 1161, 105, 527, 1161, 4, + /* 1840 */ 1161, 1161, 1161, 1161, 213, 1161, 294, 1161, 951, 951, + /* 1850 */ 953, 954, 27, 530, 293, 1161, 1161, 1161, 1161, 1161, + /* 1860 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, + /* 1870 */ 1161, 1161, 1161, 1161, 1161, 1161, 416, 1161, 1161, 1161, + /* 1880 */ 1161, 1161, 1161, 1161, 215, 1161, 1161, 1161, 524, 1161, + /* 1890 */ 1161, 1161, 152, 1161, 1161, 154, 105, 527, 1161, 4, + /* 1900 */ 1161, 1161, 1161, 1161, 1161, 1161, 214, 1161, 1161, 1161, + /* 1910 */ 1161, 961, 1161, 530, 1161, 1161, 1161, 103, 103, 880, + /* 1920 */ 1161, 1161, 1161, 1161, 104, 1161, 416, 537, 536, 1161, + /* 1930 */ 1161, 951, 1161, 1161, 1161, 1161, 416, 1161, 1161, 1161, + /* 1940 */ 385, 1161, 1161, 1161, 1161, 288, 526, 1161, 524, 1161, + /* 1950 */ 1161, 1161, 1161, 1161, 1161, 1161, 97, 527, 1161, 4, + /* 1960 */ 1161, 1161, 951, 951, 953, 954, 27, 1161, 423, 1161, + /* 1970 */ 1161, 961, 1161, 530, 1161, 1161, 1161, 103, 103, 1161, + /* 1980 */ 1161, 1161, 1161, 1161, 104, 1161, 416, 537, 536, 1161, + /* 1990 */ 1161, 951, 268, 1161, 1161, 1161, 416, 366, 366, 365, + /* 2000 */ 253, 363, 1161, 1161, 798, 1161, 1161, 1161, 524, 1161, + /* 2010 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 213, 1161, 294, + /* 2020 */ 1161, 1161, 951, 951, 953, 954, 27, 293, 1161, 1161, + /* 2030 */ 1161, 961, 1161, 1161, 1161, 1161, 1161, 103, 103, 1161, + /* 2040 */ 1161, 1161, 1161, 1161, 104, 1161, 416, 537, 536, 1161, + /* 2050 */ 1161, 951, 1161, 1161, 1161, 1161, 1161, 215, 1161, 1161, + /* 2060 */ 1161, 1161, 1161, 1161, 1161, 152, 1161, 1161, 154, 1161, + /* 2070 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 214, + /* 2080 */ 1161, 1161, 951, 951, 953, 954, 27, 1161, 1161, 1161, + /* 2090 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, + /* 2100 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, + /* 2110 */ 1161, 1161, 1161, 385, 1161, 1161, 1161, 1161, 288, 526, + /* 2120 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, + /* 2130 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, + /* 2140 */ 1161, 423, }; static const YYCODETYPE yy_lookahead[] = { - /* 0 */ 184, 238, 239, 240, 238, 239, 240, 163, 155, 156, - /* 10 */ 157, 158, 159, 160, 163, 191, 192, 183, 165, 19, - /* 20 */ 167, 258, 202, 203, 200, 191, 163, 174, 184, 185, - /* 30 */ 174, 31, 163, 163, 171, 184, 185, 35, 175, 39, - /* 40 */ 179, 180, 181, 43, 44, 45, 46, 47, 48, 49, - /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 184, 206, - /* 60 */ 207, 163, 206, 207, 220, 163, 16, 163, 66, 163, - /* 70 */ 59, 270, 219, 229, 273, 219, 74, 208, 174, 223, - /* 80 */ 224, 163, 184, 185, 163, 232, 184, 185, 184, 185, - /* 90 */ 184, 185, 92, 93, 94, 95, 96, 97, 98, 99, - /* 100 */ 100, 101, 102, 233, 198, 184, 185, 96, 97, 163, - /* 110 */ 206, 207, 19, 163, 261, 104, 105, 106, 107, 198, - /* 120 */ 109, 119, 220, 219, 220, 274, 275, 77, 117, 79, - /* 130 */ 187, 229, 19, 229, 184, 185, 43, 44, 45, 46, - /* 140 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 150 */ 57, 233, 141, 134, 143, 102, 43, 44, 45, 46, - /* 160 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 170 */ 57, 152, 274, 216, 276, 218, 83, 163, 85, 233, - /* 180 */ 67, 238, 239, 240, 11, 92, 93, 94, 95, 96, - /* 190 */ 97, 98, 99, 100, 101, 102, 19, 54, 55, 56, - /* 200 */ 57, 58, 163, 26, 163, 92, 93, 94, 95, 96, - /* 210 */ 97, 98, 99, 100, 101, 102, 54, 55, 56, 57, - /* 220 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, - /* 230 */ 53, 54, 55, 56, 57, 92, 93, 94, 95, 96, - /* 240 */ 97, 98, 99, 100, 101, 102, 69, 96, 97, 98, - /* 250 */ 99, 100, 101, 102, 92, 93, 94, 95, 96, 97, - /* 260 */ 98, 99, 100, 101, 102, 81, 179, 180, 181, 92, - /* 270 */ 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, - /* 280 */ 163, 267, 268, 163, 22, 23, 59, 163, 26, 19, - /* 290 */ 117, 118, 175, 109, 24, 59, 92, 93, 94, 95, - /* 300 */ 96, 97, 98, 99, 100, 101, 102, 268, 184, 185, - /* 310 */ 269, 127, 128, 43, 44, 45, 46, 47, 48, 49, - /* 320 */ 50, 51, 52, 53, 54, 55, 56, 57, 157, 158, - /* 330 */ 159, 160, 105, 106, 107, 163, 165, 59, 167, 184, - /* 340 */ 90, 105, 106, 107, 108, 174, 73, 111, 112, 113, - /* 350 */ 19, 22, 163, 91, 81, 163, 106, 121, 81, 132, - /* 360 */ 110, 16, 92, 93, 94, 95, 96, 97, 98, 99, - /* 370 */ 100, 101, 102, 184, 185, 255, 98, 206, 207, 26, - /* 380 */ 101, 102, 19, 105, 106, 107, 23, 198, 59, 116, - /* 390 */ 219, 141, 142, 143, 24, 163, 187, 205, 274, 275, - /* 400 */ 127, 128, 182, 232, 127, 128, 43, 44, 45, 46, - /* 410 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 420 */ 57, 158, 77, 160, 79, 59, 26, 182, 165, 59, - /* 430 */ 167, 199, 261, 102, 105, 106, 107, 174, 72, 108, - /* 440 */ 109, 110, 111, 112, 113, 114, 59, 238, 239, 240, - /* 450 */ 123, 120, 125, 126, 163, 92, 93, 94, 95, 96, - /* 460 */ 97, 98, 99, 100, 101, 102, 163, 163, 163, 206, - /* 470 */ 207, 105, 106, 107, 254, 19, 106, 90, 197, 23, - /* 480 */ 127, 128, 219, 238, 239, 240, 22, 184, 185, 184, - /* 490 */ 185, 22, 105, 106, 149, 232, 205, 110, 163, 43, - /* 500 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, - /* 510 */ 54, 55, 56, 57, 98, 99, 100, 101, 102, 184, - /* 520 */ 185, 163, 53, 59, 261, 220, 117, 118, 141, 142, - /* 530 */ 143, 131, 174, 59, 229, 116, 117, 118, 163, 59, - /* 540 */ 163, 163, 184, 185, 59, 242, 72, 22, 92, 93, - /* 550 */ 94, 95, 96, 97, 98, 99, 100, 101, 102, 184, - /* 560 */ 185, 24, 184, 185, 206, 207, 202, 203, 19, 105, - /* 570 */ 106, 107, 23, 198, 22, 174, 198, 219, 220, 105, - /* 580 */ 106, 107, 96, 97, 59, 105, 106, 107, 22, 174, - /* 590 */ 59, 106, 43, 44, 45, 46, 47, 48, 49, 50, - /* 600 */ 51, 52, 53, 54, 55, 56, 57, 206, 207, 12, - /* 610 */ 108, 59, 132, 111, 112, 113, 46, 47, 48, 49, - /* 620 */ 219, 206, 207, 121, 27, 59, 163, 141, 207, 143, - /* 630 */ 105, 106, 107, 163, 219, 234, 105, 106, 107, 42, - /* 640 */ 219, 92, 93, 94, 95, 96, 97, 98, 99, 100, - /* 650 */ 101, 102, 76, 163, 184, 185, 163, 105, 106, 107, - /* 660 */ 63, 19, 86, 163, 163, 23, 163, 130, 205, 21, - /* 670 */ 73, 105, 106, 107, 184, 185, 163, 184, 185, 237, - /* 680 */ 110, 180, 181, 180, 181, 43, 44, 45, 46, 47, - /* 690 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, - /* 700 */ 174, 163, 163, 22, 23, 163, 163, 26, 22, 23, - /* 710 */ 220, 29, 73, 220, 272, 33, 22, 163, 24, 19, - /* 720 */ 174, 208, 259, 184, 185, 19, 184, 185, 80, 175, - /* 730 */ 230, 174, 206, 207, 92, 93, 94, 95, 96, 97, - /* 740 */ 98, 99, 100, 101, 102, 219, 46, 65, 247, 195, - /* 750 */ 247, 197, 206, 207, 19, 116, 117, 118, 23, 220, - /* 760 */ 112, 174, 220, 206, 207, 219, 22, 174, 24, 174, - /* 770 */ 22, 23, 91, 264, 265, 168, 219, 91, 43, 44, - /* 780 */ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, - /* 790 */ 55, 56, 57, 206, 207, 12, 163, 149, 255, 206, - /* 800 */ 207, 206, 207, 59, 104, 23, 219, 163, 26, 163, - /* 810 */ 27, 105, 219, 163, 219, 163, 211, 184, 185, 163, - /* 820 */ 120, 163, 146, 163, 148, 42, 221, 92, 93, 94, - /* 830 */ 95, 96, 97, 98, 99, 100, 101, 102, 163, 91, - /* 840 */ 184, 185, 184, 185, 184, 185, 63, 19, 163, 205, - /* 850 */ 106, 23, 245, 163, 208, 248, 116, 117, 118, 184, - /* 860 */ 185, 163, 163, 7, 8, 9, 163, 19, 26, 184, - /* 870 */ 185, 43, 44, 45, 46, 47, 48, 49, 50, 51, - /* 880 */ 52, 53, 54, 55, 56, 57, 163, 184, 185, 107, - /* 890 */ 163, 43, 44, 45, 46, 47, 48, 49, 50, 51, - /* 900 */ 52, 53, 54, 55, 56, 57, 208, 255, 177, 178, - /* 910 */ 163, 184, 185, 163, 132, 163, 141, 163, 143, 22, - /* 920 */ 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, - /* 930 */ 102, 184, 185, 163, 184, 185, 184, 185, 184, 185, - /* 940 */ 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, - /* 950 */ 102, 163, 163, 163, 184, 185, 163, 115, 163, 163, - /* 960 */ 163, 163, 15, 163, 163, 163, 163, 163, 23, 163, - /* 970 */ 163, 26, 184, 185, 184, 185, 163, 184, 185, 184, - /* 980 */ 185, 184, 185, 163, 184, 185, 184, 185, 184, 185, - /* 990 */ 184, 185, 163, 96, 97, 147, 163, 184, 185, 163, - /* 1000 */ 199, 163, 163, 205, 184, 185, 163, 60, 163, 141, - /* 1010 */ 163, 143, 163, 184, 185, 19, 163, 184, 185, 230, - /* 1020 */ 184, 185, 184, 185, 206, 207, 230, 184, 185, 184, - /* 1030 */ 185, 184, 185, 184, 185, 19, 163, 219, 231, 43, - /* 1040 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, - /* 1050 */ 54, 55, 56, 57, 163, 26, 163, 184, 185, 43, - /* 1060 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, - /* 1070 */ 54, 55, 56, 57, 163, 184, 185, 184, 185, 163, - /* 1080 */ 182, 163, 163, 163, 163, 163, 22, 163, 92, 93, - /* 1090 */ 94, 95, 96, 97, 98, 99, 100, 101, 102, 163, - /* 1100 */ 184, 185, 184, 185, 163, 184, 185, 163, 92, 93, - /* 1110 */ 94, 95, 96, 97, 98, 99, 100, 101, 102, 163, - /* 1120 */ 184, 185, 98, 59, 163, 184, 185, 205, 184, 185, - /* 1130 */ 23, 206, 207, 26, 163, 26, 107, 153, 154, 237, - /* 1140 */ 184, 185, 231, 147, 219, 184, 185, 249, 124, 127, - /* 1150 */ 128, 231, 254, 129, 163, 231, 177, 178, 262, 263, - /* 1160 */ 118, 132, 19, 19, 46, 223, 224, 31, 24, 23, - /* 1170 */ 106, 124, 26, 22, 272, 39, 129, 23, 109, 110, - /* 1180 */ 26, 163, 140, 19, 22, 234, 59, 43, 44, 45, - /* 1190 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, - /* 1200 */ 56, 57, 231, 7, 8, 193, 59, 43, 44, 45, - /* 1210 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, - /* 1220 */ 56, 57, 104, 61, 23, 23, 23, 26, 26, 26, - /* 1230 */ 163, 23, 23, 106, 26, 26, 92, 93, 94, 95, - /* 1240 */ 96, 97, 98, 99, 100, 101, 102, 138, 105, 23, - /* 1250 */ 59, 23, 26, 106, 26, 163, 92, 93, 94, 95, - /* 1260 */ 96, 97, 98, 99, 100, 101, 102, 110, 23, 23, - /* 1270 */ 23, 26, 26, 26, 163, 163, 19, 120, 163, 163, - /* 1280 */ 163, 130, 163, 163, 163, 163, 163, 163, 163, 193, - /* 1290 */ 193, 163, 163, 163, 163, 225, 19, 106, 163, 222, - /* 1300 */ 163, 44, 45, 46, 47, 48, 49, 50, 51, 52, - /* 1310 */ 53, 54, 55, 56, 57, 163, 163, 203, 163, 163, - /* 1320 */ 222, 163, 45, 46, 47, 48, 49, 50, 51, 52, - /* 1330 */ 53, 54, 55, 56, 57, 163, 163, 163, 163, 163, - /* 1340 */ 251, 250, 209, 19, 20, 182, 22, 161, 222, 92, - /* 1350 */ 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, - /* 1360 */ 36, 222, 222, 260, 226, 188, 256, 226, 187, 92, - /* 1370 */ 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, - /* 1380 */ 210, 213, 213, 59, 213, 196, 192, 187, 256, 244, - /* 1390 */ 212, 187, 226, 19, 20, 71, 22, 210, 166, 60, - /* 1400 */ 130, 170, 260, 170, 38, 81, 257, 257, 170, 104, - /* 1410 */ 36, 22, 43, 201, 90, 236, 138, 235, 213, 18, - /* 1420 */ 96, 97, 48, 204, 204, 204, 204, 103, 170, 105, - /* 1430 */ 106, 107, 18, 59, 110, 169, 213, 213, 201, 170, - /* 1440 */ 201, 169, 236, 213, 146, 71, 235, 62, 253, 252, - /* 1450 */ 170, 127, 128, 169, 22, 170, 82, 189, 169, 104, - /* 1460 */ 170, 87, 169, 189, 90, 141, 142, 143, 144, 145, - /* 1470 */ 96, 97, 186, 186, 186, 64, 194, 103, 186, 105, - /* 1480 */ 106, 107, 115, 189, 110, 188, 186, 186, 19, 20, - /* 1490 */ 194, 22, 186, 189, 102, 246, 246, 189, 133, 228, - /* 1500 */ 104, 228, 227, 227, 170, 36, 134, 228, 227, 19, - /* 1510 */ 20, 228, 22, 84, 271, 141, 142, 143, 144, 145, - /* 1520 */ 0, 1, 2, 216, 22, 5, 36, 137, 59, 227, - /* 1530 */ 10, 11, 12, 13, 14, 217, 269, 17, 216, 22, - /* 1540 */ 71, 170, 243, 146, 241, 217, 136, 215, 135, 59, - /* 1550 */ 30, 82, 32, 25, 214, 213, 87, 173, 26, 90, - /* 1560 */ 40, 71, 13, 172, 164, 96, 97, 164, 6, 162, - /* 1570 */ 162, 162, 103, 263, 105, 106, 107, 266, 266, 110, - /* 1580 */ 90, 176, 176, 190, 182, 190, 96, 97, 98, 4, - /* 1590 */ 70, 176, 3, 103, 182, 105, 106, 107, 78, 182, - /* 1600 */ 110, 81, 182, 182, 182, 182, 182, 151, 88, 22, - /* 1610 */ 141, 142, 143, 144, 145, 15, 89, 16, 23, 23, - /* 1620 */ 128, 19, 20, 139, 22, 119, 131, 24, 20, 133, - /* 1630 */ 16, 141, 142, 143, 144, 145, 1, 140, 36, 131, - /* 1640 */ 119, 61, 122, 37, 139, 53, 53, 127, 128, 119, - /* 1650 */ 53, 53, 105, 34, 130, 1, 5, 104, 22, 149, - /* 1660 */ 26, 59, 68, 75, 41, 130, 24, 68, 104, 20, - /* 1670 */ 150, 19, 120, 71, 114, 22, 67, 22, 22, 67, - /* 1680 */ 23, 22, 22, 67, 82, 37, 28, 23, 138, 87, - /* 1690 */ 22, 153, 90, 23, 23, 26, 23, 22, 96, 97, - /* 1700 */ 24, 23, 22, 24, 130, 103, 23, 105, 106, 107, - /* 1710 */ 1, 2, 110, 23, 5, 105, 34, 22, 132, 10, - /* 1720 */ 11, 12, 13, 14, 26, 34, 17, 34, 85, 83, - /* 1730 */ 44, 19, 20, 23, 22, 24, 75, 34, 23, 30, - /* 1740 */ 26, 32, 26, 141, 142, 143, 144, 145, 36, 40, - /* 1750 */ 23, 23, 23, 23, 11, 23, 22, 26, 22, 22, - /* 1760 */ 22, 19, 20, 23, 22, 26, 15, 23, 22, 124, - /* 1770 */ 130, 59, 23, 1, 130, 277, 277, 130, 36, 70, - /* 1780 */ 130, 277, 277, 71, 277, 277, 277, 78, 277, 277, - /* 1790 */ 81, 277, 277, 277, 277, 277, 277, 88, 277, 277, - /* 1800 */ 277, 59, 90, 277, 277, 277, 277, 277, 96, 97, - /* 1810 */ 277, 277, 277, 71, 277, 103, 277, 105, 106, 107, - /* 1820 */ 277, 277, 110, 277, 277, 277, 277, 277, 277, 277, - /* 1830 */ 277, 122, 90, 277, 277, 277, 127, 128, 96, 97, - /* 1840 */ 277, 277, 277, 277, 277, 103, 277, 105, 106, 107, - /* 1850 */ 277, 277, 110, 141, 142, 143, 144, 145, 277, 150, - /* 1860 */ 277, 277, 277, 5, 277, 277, 277, 277, 10, 11, - /* 1870 */ 12, 13, 14, 277, 277, 17, 277, 277, 277, 277, - /* 1880 */ 277, 277, 277, 141, 142, 143, 144, 145, 30, 277, - /* 1890 */ 32, 277, 277, 277, 277, 277, 277, 277, 40, 277, - /* 1900 */ 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, - /* 1910 */ 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, - /* 1920 */ 277, 277, 277, 277, 277, 277, 277, 277, 70, 277, - /* 1930 */ 277, 277, 277, 277, 277, 277, 78, 277, 277, 81, - /* 1940 */ 277, 277, 277, 277, 277, 277, 88, 277, 277, 277, - /* 1950 */ 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, - /* 1960 */ 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, - /* 1970 */ 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, - /* 1980 */ 122, 277, 277, 277, 277, 127, 128, 277, 277, 277, - /* 1990 */ 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, - /* 2000 */ 277, 277, 277, 277, 277, 277, 277, 277, 150, 277, - /* 2010 */ 277, 277, 277, 277, 277, 277, 277, 277, 277, + /* 0 */ 184, 184, 259, 260, 261, 259, 260, 261, 176, 177, + /* 10 */ 178, 179, 180, 181, 184, 208, 212, 213, 186, 19, + /* 20 */ 188, 205, 206, 280, 205, 221, 22, 195, 24, 195, + /* 30 */ 208, 31, 195, 205, 29, 205, 206, 255, 33, 39, + /* 40 */ 200, 201, 202, 43, 44, 45, 46, 47, 48, 49, + /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 205, 227, + /* 60 */ 228, 227, 228, 59, 227, 228, 259, 260, 261, 252, + /* 70 */ 65, 241, 240, 184, 240, 223, 224, 240, 244, 245, + /* 80 */ 250, 259, 260, 261, 19, 253, 54, 55, 56, 57, + /* 90 */ 58, 184, 255, 184, 205, 206, 96, 97, 98, 99, + /* 100 */ 100, 101, 102, 103, 104, 105, 106, 46, 47, 48, + /* 110 */ 49, 46, 296, 297, 110, 283, 19, 96, 97, 98, + /* 120 */ 99, 100, 101, 102, 103, 104, 105, 106, 96, 97, + /* 130 */ 98, 99, 100, 101, 102, 103, 104, 105, 106, 94, + /* 140 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, + /* 150 */ 53, 54, 55, 56, 57, 110, 106, 73, 251, 114, + /* 160 */ 73, 178, 179, 180, 181, 59, 184, 292, 81, 186, + /* 170 */ 295, 188, 218, 108, 19, 114, 184, 11, 195, 184, + /* 180 */ 83, 184, 85, 54, 55, 56, 57, 205, 206, 124, + /* 190 */ 145, 146, 147, 96, 97, 98, 99, 100, 101, 102, + /* 200 */ 103, 104, 105, 106, 120, 121, 122, 120, 102, 81, + /* 210 */ 227, 228, 220, 19, 16, 109, 110, 111, 131, 132, + /* 220 */ 26, 184, 184, 240, 229, 96, 97, 98, 99, 100, + /* 230 */ 101, 102, 103, 104, 105, 106, 253, 43, 44, 45, + /* 240 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + /* 250 */ 56, 57, 100, 101, 102, 103, 104, 105, 106, 131, + /* 260 */ 132, 106, 127, 69, 129, 130, 283, 112, 113, 114, + /* 270 */ 115, 116, 117, 118, 81, 77, 76, 79, 296, 124, + /* 280 */ 298, 203, 184, 19, 84, 59, 86, 121, 122, 89, + /* 290 */ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + /* 300 */ 106, 184, 35, 205, 206, 22, 113, 43, 44, 45, + /* 310 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + /* 320 */ 56, 57, 205, 206, 131, 132, 100, 101, 291, 292, + /* 330 */ 114, 67, 295, 66, 108, 109, 110, 111, 138, 113, + /* 340 */ 124, 74, 59, 179, 184, 181, 184, 121, 22, 271, + /* 350 */ 186, 19, 188, 184, 276, 59, 24, 184, 241, 195, + /* 360 */ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + /* 370 */ 106, 145, 59, 147, 184, 43, 44, 45, 46, 47, + /* 380 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + /* 390 */ 123, 227, 228, 110, 296, 297, 22, 23, 184, 102, + /* 400 */ 103, 104, 105, 106, 240, 109, 110, 111, 112, 195, + /* 410 */ 204, 115, 116, 117, 22, 184, 226, 253, 212, 205, + /* 420 */ 206, 125, 109, 110, 111, 22, 100, 101, 96, 97, + /* 430 */ 98, 99, 100, 101, 102, 103, 104, 105, 106, 184, + /* 440 */ 59, 227, 228, 121, 122, 59, 277, 283, 19, 289, + /* 450 */ 290, 59, 23, 76, 240, 241, 143, 76, 72, 189, + /* 460 */ 205, 206, 59, 86, 250, 84, 89, 86, 203, 95, + /* 470 */ 89, 281, 43, 44, 45, 46, 47, 48, 49, 50, + /* 480 */ 51, 52, 53, 54, 55, 56, 57, 227, 228, 184, + /* 490 */ 109, 110, 111, 12, 184, 109, 110, 111, 184, 184, + /* 500 */ 240, 109, 110, 111, 184, 195, 214, 59, 27, 184, + /* 510 */ 205, 206, 109, 110, 111, 205, 206, 184, 263, 138, + /* 520 */ 205, 206, 184, 42, 22, 96, 97, 98, 99, 100, + /* 530 */ 101, 102, 103, 104, 105, 106, 266, 227, 228, 59, + /* 540 */ 270, 276, 94, 66, 63, 19, 241, 22, 26, 23, + /* 550 */ 240, 241, 72, 59, 73, 250, 241, 109, 110, 82, + /* 560 */ 22, 59, 114, 223, 224, 250, 252, 59, 91, 43, + /* 570 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + /* 580 */ 54, 55, 56, 57, 59, 184, 26, 59, 268, 109, + /* 590 */ 110, 111, 184, 145, 146, 147, 112, 59, 203, 115, + /* 600 */ 116, 117, 277, 109, 110, 111, 205, 206, 195, 125, + /* 610 */ 277, 109, 110, 111, 100, 101, 139, 109, 110, 111, + /* 620 */ 219, 184, 96, 97, 98, 99, 100, 101, 102, 103, + /* 630 */ 104, 105, 106, 111, 109, 110, 111, 109, 110, 111, + /* 640 */ 227, 228, 19, 184, 136, 184, 23, 109, 110, 111, + /* 650 */ 200, 201, 202, 240, 259, 260, 261, 195, 136, 145, + /* 660 */ 184, 147, 184, 184, 136, 214, 43, 44, 45, 46, + /* 670 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + /* 680 */ 57, 205, 206, 205, 206, 227, 228, 184, 229, 227, + /* 690 */ 228, 131, 132, 184, 59, 219, 184, 219, 240, 291, + /* 700 */ 292, 184, 240, 295, 105, 106, 22, 23, 205, 206, + /* 710 */ 26, 184, 251, 184, 205, 206, 184, 205, 206, 96, + /* 720 */ 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + /* 730 */ 251, 219, 205, 206, 205, 206, 184, 205, 206, 19, + /* 740 */ 184, 16, 184, 23, 241, 110, 219, 21, 219, 184, + /* 750 */ 241, 219, 286, 287, 195, 184, 195, 205, 206, 201, + /* 760 */ 202, 205, 206, 43, 44, 45, 46, 47, 48, 49, + /* 770 */ 50, 51, 52, 53, 54, 55, 56, 57, 184, 95, + /* 780 */ 22, 23, 184, 26, 26, 220, 227, 228, 227, 228, + /* 790 */ 196, 184, 23, 241, 26, 26, 195, 241, 184, 240, + /* 800 */ 12, 240, 77, 26, 79, 195, 80, 290, 201, 202, + /* 810 */ 216, 184, 218, 195, 184, 27, 96, 97, 98, 99, + /* 820 */ 100, 101, 102, 103, 104, 105, 106, 269, 227, 228, + /* 830 */ 42, 184, 205, 206, 184, 184, 19, 227, 228, 192, + /* 840 */ 23, 240, 116, 196, 76, 227, 228, 120, 121, 122, + /* 850 */ 240, 63, 254, 95, 86, 205, 206, 89, 240, 184, + /* 860 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, + /* 870 */ 53, 54, 55, 56, 57, 184, 269, 184, 153, 153, + /* 880 */ 111, 184, 7, 8, 9, 184, 138, 184, 184, 196, + /* 890 */ 184, 120, 121, 122, 184, 138, 205, 206, 184, 102, + /* 900 */ 184, 184, 205, 206, 156, 136, 205, 206, 205, 206, + /* 910 */ 198, 199, 135, 96, 97, 98, 99, 100, 101, 102, + /* 920 */ 103, 104, 105, 106, 184, 128, 184, 184, 184, 254, + /* 930 */ 133, 184, 237, 19, 239, 229, 226, 23, 292, 184, + /* 940 */ 226, 295, 226, 226, 184, 205, 206, 205, 206, 205, + /* 950 */ 206, 184, 292, 19, 184, 295, 252, 43, 44, 45, + /* 960 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + /* 970 */ 56, 57, 205, 206, 184, 205, 206, 43, 44, 45, + /* 980 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + /* 990 */ 56, 57, 157, 158, 26, 205, 206, 254, 26, 252, + /* 1000 */ 184, 15, 184, 184, 184, 292, 184, 252, 295, 24, + /* 1010 */ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + /* 1020 */ 106, 205, 206, 205, 206, 205, 206, 205, 206, 184, + /* 1030 */ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + /* 1040 */ 106, 184, 184, 184, 59, 184, 60, 184, 229, 184, + /* 1050 */ 205, 206, 184, 258, 184, 19, 184, 19, 184, 246, + /* 1060 */ 184, 258, 205, 206, 205, 206, 205, 206, 205, 206, + /* 1070 */ 205, 206, 184, 205, 206, 205, 206, 205, 206, 205, + /* 1080 */ 206, 205, 206, 292, 226, 151, 295, 184, 228, 294, + /* 1090 */ 184, 119, 184, 205, 206, 110, 150, 294, 152, 184, + /* 1100 */ 240, 184, 22, 23, 23, 19, 184, 26, 205, 206, + /* 1110 */ 142, 205, 206, 205, 206, 184, 198, 199, 131, 132, + /* 1120 */ 205, 206, 205, 206, 22, 19, 24, 205, 206, 43, + /* 1130 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + /* 1140 */ 54, 55, 56, 57, 184, 109, 184, 109, 184, 43, + /* 1150 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + /* 1160 */ 54, 55, 56, 57, 46, 205, 206, 205, 206, 205, + /* 1170 */ 206, 232, 184, 184, 184, 95, 184, 284, 285, 244, + /* 1180 */ 245, 242, 96, 97, 98, 99, 100, 101, 102, 103, + /* 1190 */ 104, 105, 106, 205, 206, 205, 206, 205, 206, 184, + /* 1200 */ 22, 184, 96, 97, 98, 99, 100, 101, 102, 103, + /* 1210 */ 104, 105, 106, 184, 24, 23, 184, 184, 26, 184, + /* 1220 */ 205, 206, 205, 206, 184, 31, 108, 128, 22, 122, + /* 1230 */ 184, 53, 133, 39, 205, 206, 22, 151, 205, 206, + /* 1240 */ 205, 206, 113, 114, 23, 205, 206, 26, 59, 23, + /* 1250 */ 23, 144, 26, 26, 184, 23, 23, 19, 26, 26, + /* 1260 */ 7, 8, 24, 23, 214, 23, 26, 61, 26, 59, + /* 1270 */ 23, 23, 23, 26, 26, 26, 145, 19, 147, 59, + /* 1280 */ 184, 43, 44, 45, 46, 47, 48, 49, 50, 51, + /* 1290 */ 52, 53, 54, 55, 56, 57, 145, 23, 147, 110, + /* 1300 */ 26, 43, 44, 45, 46, 47, 48, 49, 50, 51, + /* 1310 */ 52, 53, 54, 55, 56, 57, 23, 184, 184, 26, + /* 1320 */ 110, 184, 184, 184, 134, 184, 184, 184, 184, 184, + /* 1330 */ 110, 184, 184, 184, 96, 97, 98, 99, 100, 101, + /* 1340 */ 102, 103, 104, 105, 106, 184, 184, 184, 134, 300, + /* 1350 */ 184, 243, 184, 184, 96, 97, 98, 99, 100, 101, + /* 1360 */ 102, 103, 104, 105, 106, 184, 184, 184, 184, 184, + /* 1370 */ 224, 184, 282, 273, 19, 272, 203, 182, 243, 243, + /* 1380 */ 230, 209, 278, 243, 231, 208, 265, 278, 234, 234, + /* 1390 */ 234, 217, 213, 60, 19, 243, 208, 237, 233, 44, + /* 1400 */ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + /* 1410 */ 55, 56, 57, 208, 247, 187, 134, 247, 247, 38, + /* 1420 */ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + /* 1430 */ 55, 56, 57, 237, 231, 191, 191, 279, 279, 282, + /* 1440 */ 143, 191, 108, 268, 22, 19, 20, 256, 22, 43, + /* 1450 */ 257, 96, 97, 98, 99, 100, 101, 102, 103, 104, + /* 1460 */ 105, 106, 36, 222, 142, 234, 18, 191, 225, 18, + /* 1470 */ 190, 96, 97, 98, 99, 100, 101, 102, 103, 104, + /* 1480 */ 105, 106, 225, 191, 225, 59, 225, 257, 234, 234, + /* 1490 */ 256, 222, 222, 190, 234, 150, 62, 71, 275, 274, + /* 1500 */ 191, 19, 20, 190, 22, 22, 210, 81, 191, 190, + /* 1510 */ 210, 191, 108, 190, 207, 207, 64, 207, 36, 215, + /* 1520 */ 94, 210, 207, 209, 119, 207, 100, 101, 207, 106, + /* 1530 */ 48, 215, 207, 107, 210, 109, 110, 111, 267, 267, + /* 1540 */ 114, 59, 210, 249, 137, 108, 248, 191, 249, 248, + /* 1550 */ 88, 249, 141, 71, 248, 299, 138, 131, 132, 22, + /* 1560 */ 191, 249, 299, 237, 82, 238, 150, 262, 140, 87, + /* 1570 */ 139, 145, 146, 147, 148, 149, 94, 248, 238, 236, + /* 1580 */ 25, 235, 100, 101, 234, 194, 26, 193, 13, 107, + /* 1590 */ 6, 109, 110, 111, 264, 185, 114, 185, 183, 197, + /* 1600 */ 183, 203, 183, 203, 203, 197, 203, 211, 211, 4, + /* 1610 */ 197, 3, 203, 22, 155, 15, 288, 203, 93, 288, + /* 1620 */ 285, 23, 16, 203, 203, 23, 132, 145, 146, 147, + /* 1630 */ 148, 149, 0, 1, 2, 143, 123, 5, 24, 135, + /* 1640 */ 20, 16, 10, 11, 12, 13, 14, 137, 1, 17, + /* 1650 */ 135, 144, 19, 20, 123, 22, 61, 143, 37, 123, + /* 1660 */ 53, 109, 30, 53, 32, 53, 53, 134, 34, 36, + /* 1670 */ 1, 5, 40, 22, 108, 153, 26, 68, 75, 68, + /* 1680 */ 41, 134, 108, 24, 20, 124, 19, 118, 23, 67, + /* 1690 */ 22, 67, 59, 22, 22, 22, 22, 67, 28, 37, + /* 1700 */ 23, 142, 70, 22, 71, 23, 157, 23, 23, 26, + /* 1710 */ 78, 23, 22, 81, 23, 82, 24, 22, 24, 134, + /* 1720 */ 87, 23, 19, 20, 92, 22, 109, 94, 23, 22, + /* 1730 */ 34, 34, 136, 100, 101, 26, 34, 85, 34, 36, + /* 1740 */ 107, 83, 109, 110, 111, 34, 90, 114, 34, 23, + /* 1750 */ 75, 75, 44, 22, 24, 26, 34, 23, 126, 26, + /* 1760 */ 23, 23, 59, 131, 132, 23, 23, 26, 23, 22, + /* 1770 */ 11, 22, 22, 22, 71, 23, 23, 22, 145, 146, + /* 1780 */ 147, 148, 149, 26, 23, 82, 154, 134, 128, 134, + /* 1790 */ 87, 134, 15, 1, 301, 134, 301, 94, 301, 301, + /* 1800 */ 301, 301, 301, 100, 101, 301, 301, 301, 301, 301, + /* 1810 */ 107, 301, 109, 110, 111, 1, 2, 114, 301, 5, + /* 1820 */ 301, 301, 301, 301, 10, 11, 12, 13, 14, 301, + /* 1830 */ 301, 17, 301, 301, 301, 301, 19, 20, 301, 22, + /* 1840 */ 301, 301, 301, 301, 30, 301, 32, 301, 145, 146, + /* 1850 */ 147, 148, 149, 36, 40, 301, 301, 301, 301, 301, + /* 1860 */ 301, 301, 301, 301, 301, 301, 301, 301, 301, 301, + /* 1870 */ 301, 301, 301, 301, 301, 301, 59, 301, 301, 301, + /* 1880 */ 301, 301, 301, 301, 70, 301, 301, 301, 71, 301, + /* 1890 */ 301, 301, 78, 301, 301, 81, 19, 20, 301, 22, + /* 1900 */ 301, 301, 301, 301, 301, 301, 92, 301, 301, 301, + /* 1910 */ 301, 94, 301, 36, 301, 301, 301, 100, 101, 102, + /* 1920 */ 301, 301, 301, 301, 107, 301, 109, 110, 111, 301, + /* 1930 */ 301, 114, 301, 301, 301, 301, 59, 301, 301, 301, + /* 1940 */ 126, 301, 301, 301, 301, 131, 132, 301, 71, 301, + /* 1950 */ 301, 301, 301, 301, 301, 301, 19, 20, 301, 22, + /* 1960 */ 301, 301, 145, 146, 147, 148, 149, 301, 154, 301, + /* 1970 */ 301, 94, 301, 36, 301, 301, 301, 100, 101, 301, + /* 1980 */ 301, 301, 301, 301, 107, 301, 109, 110, 111, 301, + /* 1990 */ 301, 114, 5, 301, 301, 301, 59, 10, 11, 12, + /* 2000 */ 13, 14, 301, 301, 17, 301, 301, 301, 71, 301, + /* 2010 */ 301, 301, 301, 301, 301, 301, 301, 30, 301, 32, + /* 2020 */ 301, 301, 145, 146, 147, 148, 149, 40, 301, 301, + /* 2030 */ 301, 94, 301, 301, 301, 301, 301, 100, 101, 301, + /* 2040 */ 301, 301, 301, 301, 107, 301, 109, 110, 111, 301, + /* 2050 */ 301, 114, 301, 301, 301, 301, 301, 70, 301, 301, + /* 2060 */ 301, 301, 301, 301, 301, 78, 301, 301, 81, 301, + /* 2070 */ 301, 301, 301, 301, 301, 301, 301, 301, 301, 92, + /* 2080 */ 301, 301, 145, 146, 147, 148, 149, 301, 301, 301, + /* 2090 */ 301, 301, 301, 301, 301, 301, 301, 301, 301, 301, + /* 2100 */ 301, 301, 301, 301, 301, 301, 301, 301, 301, 301, + /* 2110 */ 301, 301, 301, 126, 301, 301, 301, 301, 131, 132, + /* 2120 */ 301, 301, 301, 301, 301, 301, 301, 301, 301, 301, + /* 2130 */ 301, 301, 301, 301, 301, 301, 301, 301, 301, 301, + /* 2140 */ 301, 154, 301, 301, 301, 301, 301, 301, 301, 301, + /* 2150 */ 301, 301, 301, 301, 301, 301, 301, 301, 301, 301, + /* 2160 */ 301, 301, 301, 301, 301, 301, 301, 301, 301, }; -#define YY_SHIFT_COUNT (520) +#define YY_SHIFT_COUNT (540) #define YY_SHIFT_MIN (0) -#define YY_SHIFT_MAX (1858) +#define YY_SHIFT_MAX (1987) static const unsigned short int yy_shift_ofst[] = { - /* 0 */ 1709, 1520, 1858, 1324, 1324, 277, 1374, 1469, 1602, 1712, - /* 10 */ 1712, 1712, 273, 0, 0, 113, 1016, 1712, 1712, 1712, - /* 20 */ 1712, 1712, 1712, 1712, 1712, 1712, 1712, 11, 11, 236, - /* 30 */ 184, 277, 277, 277, 277, 277, 277, 93, 177, 270, - /* 40 */ 363, 456, 549, 642, 735, 828, 848, 996, 1144, 1016, - /* 50 */ 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, - /* 60 */ 1016, 1016, 1016, 1016, 1016, 1016, 1164, 1016, 1257, 1277, - /* 70 */ 1277, 1490, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, - /* 80 */ 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, - /* 90 */ 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, - /* 100 */ 1712, 1712, 1712, 1742, 1712, 1712, 1712, 1712, 1712, 1712, - /* 110 */ 1712, 1712, 1712, 1712, 1712, 1712, 1712, 143, 162, 162, - /* 120 */ 162, 162, 162, 204, 151, 416, 531, 648, 700, 531, - /* 130 */ 486, 486, 531, 353, 353, 353, 353, 409, 279, 53, - /* 140 */ 2009, 2009, 331, 331, 331, 329, 366, 329, 329, 597, - /* 150 */ 597, 464, 474, 262, 681, 531, 531, 531, 531, 531, - /* 160 */ 531, 531, 531, 531, 531, 531, 531, 531, 531, 531, - /* 170 */ 531, 531, 531, 531, 531, 531, 531, 173, 485, 984, - /* 180 */ 984, 576, 485, 19, 1022, 2009, 2009, 2009, 387, 250, - /* 190 */ 250, 525, 502, 278, 552, 227, 480, 566, 531, 531, - /* 200 */ 531, 531, 531, 531, 531, 531, 531, 531, 639, 531, - /* 210 */ 531, 531, 531, 531, 531, 531, 531, 531, 531, 531, - /* 220 */ 531, 2, 2, 2, 531, 531, 531, 531, 782, 531, - /* 230 */ 531, 531, 744, 531, 531, 783, 531, 531, 531, 531, - /* 240 */ 531, 531, 531, 531, 419, 682, 327, 370, 370, 370, - /* 250 */ 370, 1029, 327, 327, 1024, 897, 856, 947, 1109, 706, - /* 260 */ 706, 1143, 1109, 1109, 1143, 842, 945, 1118, 1136, 1136, - /* 270 */ 1136, 706, 676, 400, 1047, 694, 1339, 1270, 1270, 1366, - /* 280 */ 1366, 1270, 1305, 1389, 1369, 1278, 1401, 1401, 1401, 1401, - /* 290 */ 1270, 1414, 1278, 1278, 1305, 1389, 1369, 1369, 1278, 1270, - /* 300 */ 1414, 1298, 1385, 1270, 1414, 1432, 1270, 1414, 1270, 1414, - /* 310 */ 1432, 1355, 1355, 1355, 1411, 1432, 1355, 1367, 1355, 1411, - /* 320 */ 1355, 1355, 1432, 1392, 1392, 1432, 1365, 1396, 1365, 1396, - /* 330 */ 1365, 1396, 1365, 1396, 1270, 1372, 1429, 1502, 1390, 1372, - /* 340 */ 1517, 1270, 1397, 1390, 1410, 1413, 1278, 1528, 1532, 1549, - /* 350 */ 1549, 1562, 1562, 1562, 2009, 2009, 2009, 2009, 2009, 2009, - /* 360 */ 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, - /* 370 */ 570, 345, 686, 748, 50, 740, 1064, 1107, 469, 537, - /* 380 */ 1042, 1146, 1162, 1154, 1201, 1202, 1203, 1208, 1209, 1127, - /* 390 */ 1069, 1196, 1157, 1147, 1226, 1228, 1245, 775, 868, 1246, - /* 400 */ 1247, 1191, 1151, 1585, 1589, 1587, 1456, 1600, 1527, 1601, - /* 410 */ 1595, 1596, 1492, 1484, 1506, 1603, 1495, 1608, 1496, 1614, - /* 420 */ 1635, 1508, 1497, 1521, 1580, 1606, 1505, 1592, 1593, 1597, - /* 430 */ 1598, 1530, 1547, 1619, 1524, 1654, 1651, 1636, 1553, 1510, - /* 440 */ 1594, 1634, 1599, 1588, 1623, 1535, 1564, 1642, 1649, 1652, - /* 450 */ 1552, 1560, 1653, 1609, 1655, 1656, 1657, 1659, 1612, 1658, - /* 460 */ 1660, 1616, 1648, 1664, 1550, 1668, 1538, 1670, 1671, 1669, - /* 470 */ 1673, 1675, 1676, 1678, 1680, 1679, 1574, 1683, 1690, 1610, - /* 480 */ 1682, 1695, 1586, 1698, 1691, 1698, 1693, 1643, 1661, 1646, - /* 490 */ 1686, 1710, 1711, 1714, 1716, 1703, 1715, 1698, 1727, 1728, - /* 500 */ 1729, 1730, 1731, 1732, 1734, 1743, 1736, 1737, 1740, 1744, - /* 510 */ 1738, 1746, 1739, 1645, 1640, 1644, 1647, 1650, 1749, 1751, - /* 520 */ 1772, + /* 0 */ 1814, 1632, 1987, 1426, 1426, 128, 1482, 1633, 1703, 1877, + /* 10 */ 1877, 1877, 87, 0, 0, 264, 1106, 1877, 1877, 1877, + /* 20 */ 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, + /* 30 */ 226, 226, 381, 381, 296, 193, 128, 128, 128, 128, + /* 40 */ 128, 128, 97, 194, 332, 429, 526, 623, 720, 817, + /* 50 */ 914, 934, 1086, 1238, 1106, 1106, 1106, 1106, 1106, 1106, + /* 60 */ 1106, 1106, 1106, 1106, 1106, 1106, 1106, 1106, 1106, 1106, + /* 70 */ 1106, 1106, 1258, 1106, 1355, 1375, 1375, 1817, 1877, 1877, + /* 80 */ 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, + /* 90 */ 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, + /* 100 */ 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, + /* 110 */ 1937, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, + /* 120 */ 1877, 1877, 1877, 1877, 32, 129, 129, 129, 129, 129, + /* 130 */ 21, 152, 297, 494, 726, 65, 494, 514, 514, 494, + /* 140 */ 560, 560, 560, 560, 322, 599, 50, 2142, 2142, 155, + /* 150 */ 155, 155, 313, 392, 386, 392, 392, 481, 481, 200, + /* 160 */ 480, 684, 758, 494, 494, 494, 494, 494, 494, 494, + /* 170 */ 494, 494, 494, 494, 494, 494, 494, 494, 494, 494, + /* 180 */ 494, 494, 494, 494, 768, 768, 494, 166, 377, 377, + /* 190 */ 635, 835, 835, 635, 748, 987, 2142, 2142, 2142, 448, + /* 200 */ 45, 45, 403, 484, 502, 106, 525, 508, 528, 538, + /* 210 */ 494, 494, 494, 494, 494, 494, 494, 494, 494, 84, + /* 220 */ 494, 494, 494, 494, 494, 494, 494, 494, 494, 494, + /* 230 */ 494, 494, 267, 267, 267, 494, 494, 494, 494, 769, + /* 240 */ 494, 494, 494, 4, 477, 494, 494, 788, 494, 494, + /* 250 */ 494, 494, 494, 494, 494, 494, 727, 5, 135, 985, + /* 260 */ 985, 985, 985, 522, 135, 135, 797, 326, 875, 986, + /* 270 */ 968, 1036, 1036, 1038, 968, 968, 1038, 972, 1081, 1118, + /* 280 */ 1194, 1194, 1194, 1036, 757, 757, 946, 777, 1099, 1102, + /* 290 */ 1333, 1282, 1282, 1381, 1381, 1282, 1297, 1334, 1422, 1406, + /* 300 */ 1322, 1448, 1448, 1448, 1448, 1282, 1451, 1322, 1322, 1334, + /* 310 */ 1422, 1406, 1406, 1322, 1282, 1451, 1345, 1434, 1282, 1451, + /* 320 */ 1483, 1282, 1451, 1282, 1451, 1483, 1404, 1404, 1404, 1452, + /* 330 */ 1483, 1404, 1405, 1404, 1452, 1404, 1404, 1483, 1423, 1423, + /* 340 */ 1483, 1407, 1437, 1407, 1437, 1407, 1437, 1407, 1437, 1282, + /* 350 */ 1462, 1462, 1411, 1418, 1537, 1282, 1416, 1411, 1428, 1431, + /* 360 */ 1322, 1555, 1560, 1575, 1575, 1584, 1584, 1584, 2142, 2142, + /* 370 */ 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, + /* 380 */ 2142, 2142, 2142, 2142, 61, 725, 374, 1080, 198, 771, + /* 390 */ 283, 1192, 1178, 1190, 1107, 1221, 1206, 1226, 1227, 1232, + /* 400 */ 1233, 1240, 1242, 1189, 1129, 1253, 216, 1210, 1247, 1248, + /* 410 */ 1249, 1131, 1151, 1274, 1293, 1220, 1214, 1605, 1608, 1591, + /* 420 */ 1459, 1600, 1525, 1606, 1598, 1602, 1494, 1492, 1513, 1614, + /* 430 */ 1504, 1620, 1510, 1625, 1647, 1515, 1507, 1531, 1595, 1621, + /* 440 */ 1514, 1607, 1610, 1612, 1613, 1536, 1552, 1634, 1533, 1669, + /* 450 */ 1666, 1651, 1566, 1522, 1609, 1650, 1611, 1603, 1639, 1547, + /* 460 */ 1574, 1659, 1664, 1667, 1561, 1569, 1668, 1622, 1671, 1672, + /* 470 */ 1665, 1673, 1624, 1670, 1674, 1630, 1662, 1677, 1559, 1681, + /* 480 */ 1682, 1549, 1684, 1685, 1683, 1688, 1690, 1692, 1691, 1695, + /* 490 */ 1694, 1585, 1698, 1705, 1617, 1696, 1707, 1596, 1709, 1697, + /* 500 */ 1702, 1704, 1711, 1652, 1675, 1658, 1708, 1676, 1656, 1714, + /* 510 */ 1726, 1731, 1730, 1729, 1733, 1722, 1734, 1709, 1737, 1738, + /* 520 */ 1742, 1743, 1741, 1745, 1747, 1759, 1749, 1750, 1752, 1753, + /* 530 */ 1751, 1755, 1757, 1660, 1653, 1655, 1657, 1661, 1761, 1777, + /* 540 */ 1792, }; -#define YY_REDUCE_COUNT (369) -#define YY_REDUCE_MIN (-237) -#define YY_REDUCE_MAX (1424) +#define YY_REDUCE_COUNT (383) +#define YY_REDUCE_MIN (-257) +#define YY_REDUCE_MAX (1421) static const short yy_reduce_ofst[] = { - /* 0 */ -147, 171, 263, -96, 358, -144, -149, -102, 124, -156, - /* 10 */ -98, 305, 401, -57, 209, -237, 245, -94, -79, 189, - /* 20 */ 375, 490, 493, 378, 303, 539, 542, 501, 503, 554, - /* 30 */ 415, 526, 546, 557, 587, 593, 595, -234, -234, -234, - /* 40 */ -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, - /* 50 */ -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, - /* 60 */ -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, - /* 70 */ -234, -50, 335, 470, 633, 656, 658, 660, 675, 685, - /* 80 */ 703, 727, 747, 750, 752, 754, 770, 788, 790, 793, - /* 90 */ 795, 797, 800, 802, 804, 806, 813, 820, 829, 833, - /* 100 */ 836, 838, 843, 845, 847, 849, 873, 891, 893, 916, - /* 110 */ 918, 921, 936, 941, 944, 956, 961, -234, -234, -234, - /* 120 */ -234, -234, -234, -234, -234, -234, 463, 607, -176, 14, - /* 130 */ -139, 87, -137, 818, 925, 818, 925, 898, -234, -234, - /* 140 */ -234, -234, -166, -166, -166, -130, -131, -82, -54, -180, - /* 150 */ 364, 41, 513, 509, 509, 117, 500, 789, 796, 646, - /* 160 */ 192, 291, 644, 798, 120, 807, 543, 911, 920, 652, - /* 170 */ 924, 922, 232, 698, 801, 971, 39, 220, 731, 442, - /* 180 */ 902, -199, 979, -43, 421, 896, 942, 605, -184, -126, - /* 190 */ 155, 172, 281, 304, 377, 538, 650, 690, 699, 723, - /* 200 */ 803, 839, 853, 919, 991, 1018, 1067, 1092, 951, 1111, - /* 210 */ 1112, 1115, 1116, 1117, 1119, 1120, 1121, 1122, 1123, 1124, - /* 220 */ 1125, 1012, 1096, 1097, 1128, 1129, 1130, 1131, 1070, 1135, - /* 230 */ 1137, 1152, 1077, 1153, 1155, 1114, 1156, 304, 1158, 1172, - /* 240 */ 1173, 1174, 1175, 1176, 1089, 1091, 1133, 1098, 1126, 1139, - /* 250 */ 1140, 1070, 1133, 1133, 1170, 1163, 1186, 1103, 1168, 1138, - /* 260 */ 1141, 1110, 1169, 1171, 1132, 1177, 1189, 1194, 1181, 1200, - /* 270 */ 1204, 1166, 1145, 1178, 1187, 1232, 1142, 1231, 1233, 1149, - /* 280 */ 1150, 1238, 1179, 1182, 1212, 1205, 1219, 1220, 1221, 1222, - /* 290 */ 1258, 1266, 1223, 1224, 1206, 1211, 1237, 1239, 1230, 1269, - /* 300 */ 1272, 1195, 1197, 1280, 1284, 1268, 1285, 1289, 1290, 1293, - /* 310 */ 1274, 1286, 1287, 1288, 1282, 1294, 1292, 1297, 1300, 1296, - /* 320 */ 1301, 1306, 1304, 1249, 1250, 1308, 1271, 1275, 1273, 1276, - /* 330 */ 1279, 1281, 1283, 1302, 1334, 1307, 1243, 1267, 1318, 1322, - /* 340 */ 1303, 1371, 1299, 1328, 1332, 1340, 1342, 1384, 1391, 1400, - /* 350 */ 1403, 1407, 1408, 1409, 1311, 1312, 1310, 1405, 1402, 1412, - /* 360 */ 1417, 1420, 1406, 1393, 1395, 1421, 1422, 1423, 1424, 1415, + /* 0 */ -168, -17, 164, 214, 310, -166, -184, -18, 98, -170, + /* 10 */ 305, 315, -163, -193, -178, -257, 395, 401, 476, 478, + /* 20 */ 512, 117, 527, 529, 503, 509, 532, 255, 552, 556, + /* 30 */ 558, 607, 37, 408, 594, 413, 462, 559, 561, 601, + /* 40 */ 610, 618, -254, -254, -254, -254, -254, -254, -254, -254, + /* 50 */ -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, + /* 60 */ -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, + /* 70 */ -254, -254, -254, -254, -254, -254, -254, -111, 627, 650, + /* 80 */ 691, 697, 701, 703, 740, 742, 744, 767, 770, 790, + /* 90 */ 816, 818, 820, 822, 845, 857, 859, 861, 863, 865, + /* 100 */ 868, 870, 872, 874, 876, 888, 903, 906, 908, 915, + /* 110 */ 917, 922, 960, 962, 964, 988, 990, 992, 1015, 1017, + /* 120 */ 1029, 1033, 1035, 1040, -254, -254, -254, -254, -254, -254, + /* 130 */ -254, -254, -254, 190, 270, -196, 160, -160, 450, 647, + /* 140 */ 260, 458, 260, 458, 78, -254, -254, -254, -254, 206, + /* 150 */ 206, 206, 320, 598, -5, 675, 743, -148, 340, -125, + /* 160 */ 459, 466, 466, 693, -93, 461, 479, 706, 710, 714, + /* 170 */ 716, 717, 169, -183, 325, 314, 704, 333, 747, 858, + /* 180 */ -8, 819, 565, 755, 646, 660, 517, 265, 713, 791, + /* 190 */ 712, 795, 803, 918, 695, 860, 893, 935, 939, -181, + /* 200 */ -172, -147, -91, -46, -3, 162, 173, 231, 338, 437, + /* 210 */ 571, 614, 630, 651, 760, 931, 989, 1032, 1046, -218, + /* 220 */ 38, 1070, 1096, 1133, 1134, 1137, 1138, 1139, 1141, 1142, + /* 230 */ 1143, 1144, 292, 451, 1050, 1145, 1147, 1148, 1149, 813, + /* 240 */ 1161, 1162, 1163, 1108, 1049, 1166, 1168, 1146, 1169, 162, + /* 250 */ 1181, 1182, 1183, 1184, 1185, 1187, 1100, 1103, 1150, 1135, + /* 260 */ 1136, 1140, 1152, 813, 1150, 1150, 1153, 1173, 1195, 1090, + /* 270 */ 1154, 1167, 1170, 1104, 1155, 1156, 1109, 1172, 1174, 1179, + /* 280 */ 1177, 1188, 1205, 1171, 1160, 1196, 1121, 1165, 1203, 1228, + /* 290 */ 1157, 1244, 1245, 1158, 1159, 1250, 1175, 1193, 1191, 1241, + /* 300 */ 1231, 1243, 1257, 1259, 1261, 1276, 1280, 1254, 1255, 1230, + /* 310 */ 1234, 1269, 1270, 1260, 1292, 1303, 1223, 1225, 1309, 1313, + /* 320 */ 1296, 1317, 1319, 1320, 1323, 1300, 1307, 1308, 1310, 1304, + /* 330 */ 1311, 1315, 1314, 1318, 1316, 1321, 1325, 1324, 1271, 1272, + /* 340 */ 1332, 1294, 1298, 1299, 1301, 1302, 1306, 1312, 1329, 1356, + /* 350 */ 1256, 1263, 1327, 1326, 1305, 1369, 1330, 1340, 1343, 1346, + /* 360 */ 1350, 1391, 1394, 1410, 1412, 1415, 1417, 1419, 1328, 1331, + /* 370 */ 1335, 1402, 1398, 1400, 1401, 1403, 1408, 1396, 1397, 1409, + /* 380 */ 1414, 1420, 1421, 1413, }; static const YYACTIONTYPE yy_default[] = { - /* 0 */ 1492, 1492, 1492, 1340, 1123, 1229, 1123, 1123, 1123, 1340, - /* 10 */ 1340, 1340, 1123, 1259, 1259, 1391, 1154, 1123, 1123, 1123, - /* 20 */ 1123, 1123, 1123, 1123, 1339, 1123, 1123, 1123, 1123, 1123, - /* 30 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1265, 1123, - /* 40 */ 1123, 1123, 1123, 1123, 1341, 1342, 1123, 1123, 1123, 1390, - /* 50 */ 1392, 1275, 1274, 1273, 1272, 1373, 1246, 1270, 1263, 1267, - /* 60 */ 1335, 1336, 1334, 1338, 1342, 1341, 1123, 1266, 1306, 1320, - /* 70 */ 1305, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 80 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 90 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 100 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 110 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1314, 1319, 1325, - /* 120 */ 1318, 1315, 1308, 1307, 1309, 1310, 1123, 1144, 1193, 1123, - /* 130 */ 1123, 1123, 1123, 1409, 1408, 1123, 1123, 1154, 1311, 1312, - /* 140 */ 1322, 1321, 1398, 1448, 1447, 1123, 1123, 1123, 1123, 1123, - /* 150 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 160 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 170 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1154, 1150, 1300, - /* 180 */ 1299, 1418, 1150, 1253, 1123, 1404, 1229, 1220, 1123, 1123, - /* 190 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 200 */ 1123, 1395, 1393, 1123, 1355, 1123, 1123, 1123, 1123, 1123, - /* 210 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 220 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 230 */ 1123, 1123, 1225, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 240 */ 1123, 1123, 1123, 1442, 1123, 1368, 1207, 1225, 1225, 1225, - /* 250 */ 1225, 1227, 1208, 1206, 1219, 1154, 1130, 1484, 1269, 1248, - /* 260 */ 1248, 1481, 1269, 1269, 1481, 1168, 1462, 1165, 1259, 1259, - /* 270 */ 1259, 1248, 1337, 1226, 1219, 1123, 1484, 1234, 1234, 1483, - /* 280 */ 1483, 1234, 1278, 1284, 1196, 1269, 1202, 1202, 1202, 1202, - /* 290 */ 1234, 1141, 1269, 1269, 1278, 1284, 1196, 1196, 1269, 1234, - /* 300 */ 1141, 1372, 1478, 1234, 1141, 1348, 1234, 1141, 1234, 1141, - /* 310 */ 1348, 1194, 1194, 1194, 1183, 1348, 1194, 1168, 1194, 1183, - /* 320 */ 1194, 1194, 1348, 1352, 1352, 1348, 1252, 1247, 1252, 1247, - /* 330 */ 1252, 1247, 1252, 1247, 1234, 1253, 1417, 1123, 1264, 1253, - /* 340 */ 1343, 1234, 1123, 1264, 1262, 1260, 1269, 1147, 1186, 1445, - /* 350 */ 1445, 1441, 1441, 1441, 1489, 1489, 1404, 1457, 1154, 1154, - /* 360 */ 1154, 1154, 1457, 1170, 1170, 1154, 1154, 1154, 1154, 1457, - /* 370 */ 1123, 1123, 1123, 1123, 1123, 1123, 1452, 1123, 1357, 1238, - /* 380 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 390 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 400 */ 1123, 1123, 1289, 1123, 1126, 1401, 1123, 1123, 1399, 1123, - /* 410 */ 1123, 1123, 1123, 1123, 1123, 1239, 1123, 1123, 1123, 1123, - /* 420 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 430 */ 1123, 1123, 1123, 1123, 1480, 1123, 1123, 1123, 1123, 1123, - /* 440 */ 1123, 1371, 1370, 1123, 1123, 1236, 1123, 1123, 1123, 1123, - /* 450 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 460 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 470 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 480 */ 1123, 1123, 1123, 1261, 1123, 1416, 1123, 1123, 1123, 1123, - /* 490 */ 1123, 1123, 1123, 1430, 1254, 1123, 1123, 1471, 1123, 1123, - /* 500 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 510 */ 1123, 1123, 1466, 1210, 1291, 1123, 1290, 1294, 1123, 1135, - /* 520 */ 1123, + /* 0 */ 1536, 1536, 1536, 1376, 1159, 1265, 1159, 1159, 1159, 1376, + /* 10 */ 1376, 1376, 1159, 1295, 1295, 1429, 1190, 1159, 1159, 1159, + /* 20 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1375, 1159, 1159, + /* 30 */ 1159, 1159, 1459, 1459, 1159, 1159, 1159, 1159, 1159, 1159, + /* 40 */ 1159, 1159, 1159, 1301, 1159, 1159, 1159, 1159, 1159, 1377, + /* 50 */ 1378, 1159, 1159, 1159, 1428, 1430, 1393, 1311, 1310, 1309, + /* 60 */ 1308, 1411, 1282, 1306, 1299, 1303, 1371, 1372, 1370, 1374, + /* 70 */ 1378, 1377, 1159, 1302, 1342, 1356, 1341, 1159, 1159, 1159, + /* 80 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, + /* 90 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, + /* 100 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, + /* 110 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, + /* 120 */ 1159, 1159, 1159, 1159, 1350, 1355, 1361, 1354, 1351, 1344, + /* 130 */ 1343, 1345, 1346, 1159, 1180, 1229, 1159, 1159, 1159, 1159, + /* 140 */ 1447, 1446, 1159, 1159, 1190, 1347, 1348, 1358, 1357, 1436, + /* 150 */ 1492, 1491, 1394, 1159, 1159, 1159, 1159, 1159, 1159, 1459, + /* 160 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, + /* 170 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, + /* 180 */ 1159, 1159, 1159, 1159, 1459, 1459, 1159, 1190, 1459, 1459, + /* 190 */ 1186, 1336, 1335, 1186, 1289, 1159, 1442, 1265, 1256, 1159, + /* 200 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, + /* 210 */ 1159, 1159, 1159, 1433, 1431, 1159, 1159, 1159, 1159, 1159, + /* 220 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, + /* 230 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, + /* 240 */ 1159, 1159, 1159, 1261, 1159, 1159, 1159, 1159, 1159, 1159, + /* 250 */ 1159, 1159, 1159, 1159, 1159, 1486, 1159, 1406, 1243, 1261, + /* 260 */ 1261, 1261, 1261, 1263, 1244, 1242, 1255, 1190, 1166, 1528, + /* 270 */ 1305, 1284, 1284, 1525, 1305, 1305, 1525, 1204, 1506, 1201, + /* 280 */ 1295, 1295, 1295, 1284, 1289, 1289, 1373, 1262, 1255, 1159, + /* 290 */ 1528, 1270, 1270, 1527, 1527, 1270, 1394, 1314, 1320, 1232, + /* 300 */ 1305, 1238, 1238, 1238, 1238, 1270, 1177, 1305, 1305, 1314, + /* 310 */ 1320, 1232, 1232, 1305, 1270, 1177, 1410, 1522, 1270, 1177, + /* 320 */ 1384, 1270, 1177, 1270, 1177, 1384, 1230, 1230, 1230, 1219, + /* 330 */ 1384, 1230, 1204, 1230, 1219, 1230, 1230, 1384, 1388, 1388, + /* 340 */ 1384, 1288, 1283, 1288, 1283, 1288, 1283, 1288, 1283, 1270, + /* 350 */ 1469, 1469, 1300, 1289, 1379, 1270, 1159, 1300, 1298, 1296, + /* 360 */ 1305, 1183, 1222, 1489, 1489, 1485, 1485, 1485, 1533, 1533, + /* 370 */ 1442, 1501, 1190, 1190, 1190, 1190, 1501, 1206, 1206, 1190, + /* 380 */ 1190, 1190, 1190, 1501, 1159, 1159, 1159, 1159, 1159, 1159, + /* 390 */ 1496, 1159, 1395, 1274, 1159, 1159, 1159, 1159, 1159, 1159, + /* 400 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, + /* 410 */ 1159, 1159, 1159, 1159, 1159, 1159, 1325, 1159, 1162, 1439, + /* 420 */ 1159, 1159, 1437, 1159, 1159, 1159, 1159, 1159, 1159, 1275, + /* 430 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, + /* 440 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1524, 1159, + /* 450 */ 1159, 1159, 1159, 1159, 1159, 1409, 1408, 1159, 1159, 1272, + /* 460 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, + /* 470 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, + /* 480 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, + /* 490 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1297, 1159, + /* 500 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, + /* 510 */ 1159, 1159, 1159, 1474, 1290, 1159, 1159, 1515, 1159, 1159, + /* 520 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, + /* 530 */ 1159, 1159, 1510, 1246, 1327, 1159, 1326, 1330, 1159, 1171, + /* 540 */ 1159, }; /********** End of lemon-generated parsing tables *****************************/ @@ -147831,6 +149183,10 @@ static const YYCODETYPE yyFallback[] = { 59, /* PRECEDING => ID */ 59, /* RANGE => ID */ 59, /* UNBOUNDED => ID */ + 59, /* EXCLUDE => ID */ + 59, /* GROUPS => ID */ + 59, /* OTHERS => ID */ + 59, /* TIES => ID */ 59, /* REINDEX => ID */ 59, /* RENAME => ID */ 59, /* CTIME_KW => ID */ @@ -148009,195 +149365,219 @@ static const char *const yyTokenName[] = { /* 85 */ "PRECEDING", /* 86 */ "RANGE", /* 87 */ "UNBOUNDED", - /* 88 */ "REINDEX", - /* 89 */ "RENAME", - /* 90 */ "CTIME_KW", - /* 91 */ "ANY", - /* 92 */ "BITAND", - /* 93 */ "BITOR", - /* 94 */ "LSHIFT", - /* 95 */ "RSHIFT", - /* 96 */ "PLUS", - /* 97 */ "MINUS", - /* 98 */ "STAR", - /* 99 */ "SLASH", - /* 100 */ "REM", - /* 101 */ "CONCAT", - /* 102 */ "COLLATE", - /* 103 */ "BITNOT", - /* 104 */ "ON", - /* 105 */ "INDEXED", - /* 106 */ "STRING", - /* 107 */ "JOIN_KW", - /* 108 */ "CONSTRAINT", - /* 109 */ "DEFAULT", - /* 110 */ "NULL", - /* 111 */ "PRIMARY", - /* 112 */ "UNIQUE", - /* 113 */ "CHECK", - /* 114 */ "REFERENCES", - /* 115 */ "AUTOINCR", - /* 116 */ "INSERT", - /* 117 */ "DELETE", - /* 118 */ "UPDATE", - /* 119 */ "SET", - /* 120 */ "DEFERRABLE", - /* 121 */ "FOREIGN", - /* 122 */ "DROP", - /* 123 */ "UNION", - /* 124 */ "ALL", - /* 125 */ "EXCEPT", - /* 126 */ "INTERSECT", - /* 127 */ "SELECT", - /* 128 */ "VALUES", - /* 129 */ "DISTINCT", - /* 130 */ "DOT", - /* 131 */ "FROM", - /* 132 */ "JOIN", - /* 133 */ "USING", - /* 134 */ "ORDER", - /* 135 */ "GROUP", - /* 136 */ "HAVING", - /* 137 */ "LIMIT", - /* 138 */ "WHERE", - /* 139 */ "INTO", - /* 140 */ "NOTHING", - /* 141 */ "FLOAT", - /* 142 */ "BLOB", - /* 143 */ "INTEGER", - /* 144 */ "VARIABLE", - /* 145 */ "CASE", - /* 146 */ "WHEN", - /* 147 */ "THEN", - /* 148 */ "ELSE", - /* 149 */ "INDEX", - /* 150 */ "ALTER", - /* 151 */ "ADD", - /* 152 */ "WINDOW", - /* 153 */ "OVER", - /* 154 */ "FILTER", - /* 155 */ "input", - /* 156 */ "cmdlist", - /* 157 */ "ecmd", - /* 158 */ "cmdx", - /* 159 */ "explain", - /* 160 */ "cmd", - /* 161 */ "transtype", - /* 162 */ "trans_opt", - /* 163 */ "nm", - /* 164 */ "savepoint_opt", - /* 165 */ "create_table", - /* 166 */ "create_table_args", - /* 167 */ "createkw", - /* 168 */ "temp", - /* 169 */ "ifnotexists", - /* 170 */ "dbnm", - /* 171 */ "columnlist", - /* 172 */ "conslist_opt", - /* 173 */ "table_options", - /* 174 */ "select", - /* 175 */ "columnname", - /* 176 */ "carglist", - /* 177 */ "typetoken", - /* 178 */ "typename", - /* 179 */ "signed", - /* 180 */ "plus_num", - /* 181 */ "minus_num", - /* 182 */ "scanpt", - /* 183 */ "ccons", - /* 184 */ "term", - /* 185 */ "expr", - /* 186 */ "onconf", - /* 187 */ "sortorder", - /* 188 */ "autoinc", - /* 189 */ "eidlist_opt", - /* 190 */ "refargs", - /* 191 */ "defer_subclause", - /* 192 */ "refarg", - /* 193 */ "refact", - /* 194 */ "init_deferred_pred_opt", - /* 195 */ "conslist", - /* 196 */ "tconscomma", - /* 197 */ "tcons", - /* 198 */ "sortlist", - /* 199 */ "eidlist", - /* 200 */ "defer_subclause_opt", - /* 201 */ "orconf", - /* 202 */ "resolvetype", - /* 203 */ "raisetype", - /* 204 */ "ifexists", - /* 205 */ "fullname", - /* 206 */ "selectnowith", - /* 207 */ "oneselect", - /* 208 */ "wqlist", - /* 209 */ "multiselect_op", - /* 210 */ "distinct", - /* 211 */ "selcollist", - /* 212 */ "from", - /* 213 */ "where_opt", - /* 214 */ "groupby_opt", - /* 215 */ "having_opt", - /* 216 */ "orderby_opt", - /* 217 */ "limit_opt", - /* 218 */ "window_clause", - /* 219 */ "values", - /* 220 */ "nexprlist", - /* 221 */ "sclp", - /* 222 */ "as", - /* 223 */ "seltablist", - /* 224 */ "stl_prefix", - /* 225 */ "joinop", - /* 226 */ "indexed_opt", - /* 227 */ "on_opt", - /* 228 */ "using_opt", - /* 229 */ "exprlist", - /* 230 */ "xfullname", - /* 231 */ "idlist", - /* 232 */ "with", - /* 233 */ "setlist", - /* 234 */ "insert_cmd", - /* 235 */ "idlist_opt", - /* 236 */ "upsert", - /* 237 */ "over_clause", - /* 238 */ "likeop", - /* 239 */ "between_op", - /* 240 */ "in_op", - /* 241 */ "paren_exprlist", - /* 242 */ "case_operand", - /* 243 */ "case_exprlist", - /* 244 */ "case_else", - /* 245 */ "uniqueflag", - /* 246 */ "collate", - /* 247 */ "nmnum", - /* 248 */ "trigger_decl", - /* 249 */ "trigger_cmd_list", - /* 250 */ "trigger_time", - /* 251 */ "trigger_event", - /* 252 */ "foreach_clause", - /* 253 */ "when_clause", - /* 254 */ "trigger_cmd", - /* 255 */ "trnm", - /* 256 */ "tridxby", - /* 257 */ "database_kw_opt", - /* 258 */ "key_opt", - /* 259 */ "add_column_fullname", - /* 260 */ "kwcolumn_opt", - /* 261 */ "create_vtab", - /* 262 */ "vtabarglist", - /* 263 */ "vtabarg", - /* 264 */ "vtabargtoken", - /* 265 */ "lp", - /* 266 */ "anylist", - /* 267 */ "windowdefn_list", - /* 268 */ "windowdefn", - /* 269 */ "window", - /* 270 */ "frame_opt", - /* 271 */ "part_opt", - /* 272 */ "filter_opt", - /* 273 */ "range_or_rows", - /* 274 */ "frame_bound", - /* 275 */ "frame_bound_s", - /* 276 */ "frame_bound_e", + /* 88 */ "EXCLUDE", + /* 89 */ "GROUPS", + /* 90 */ "OTHERS", + /* 91 */ "TIES", + /* 92 */ "REINDEX", + /* 93 */ "RENAME", + /* 94 */ "CTIME_KW", + /* 95 */ "ANY", + /* 96 */ "BITAND", + /* 97 */ "BITOR", + /* 98 */ "LSHIFT", + /* 99 */ "RSHIFT", + /* 100 */ "PLUS", + /* 101 */ "MINUS", + /* 102 */ "STAR", + /* 103 */ "SLASH", + /* 104 */ "REM", + /* 105 */ "CONCAT", + /* 106 */ "COLLATE", + /* 107 */ "BITNOT", + /* 108 */ "ON", + /* 109 */ "INDEXED", + /* 110 */ "STRING", + /* 111 */ "JOIN_KW", + /* 112 */ "CONSTRAINT", + /* 113 */ "DEFAULT", + /* 114 */ "NULL", + /* 115 */ "PRIMARY", + /* 116 */ "UNIQUE", + /* 117 */ "CHECK", + /* 118 */ "REFERENCES", + /* 119 */ "AUTOINCR", + /* 120 */ "INSERT", + /* 121 */ "DELETE", + /* 122 */ "UPDATE", + /* 123 */ "SET", + /* 124 */ "DEFERRABLE", + /* 125 */ "FOREIGN", + /* 126 */ "DROP", + /* 127 */ "UNION", + /* 128 */ "ALL", + /* 129 */ "EXCEPT", + /* 130 */ "INTERSECT", + /* 131 */ "SELECT", + /* 132 */ "VALUES", + /* 133 */ "DISTINCT", + /* 134 */ "DOT", + /* 135 */ "FROM", + /* 136 */ "JOIN", + /* 137 */ "USING", + /* 138 */ "ORDER", + /* 139 */ "GROUP", + /* 140 */ "HAVING", + /* 141 */ "LIMIT", + /* 142 */ "WHERE", + /* 143 */ "INTO", + /* 144 */ "NOTHING", + /* 145 */ "FLOAT", + /* 146 */ "BLOB", + /* 147 */ "INTEGER", + /* 148 */ "VARIABLE", + /* 149 */ "CASE", + /* 150 */ "WHEN", + /* 151 */ "THEN", + /* 152 */ "ELSE", + /* 153 */ "INDEX", + /* 154 */ "ALTER", + /* 155 */ "ADD", + /* 156 */ "WINDOW", + /* 157 */ "OVER", + /* 158 */ "FILTER", + /* 159 */ "TRUEFALSE", + /* 160 */ "ISNOT", + /* 161 */ "FUNCTION", + /* 162 */ "COLUMN", + /* 163 */ "AGG_FUNCTION", + /* 164 */ "AGG_COLUMN", + /* 165 */ "UMINUS", + /* 166 */ "UPLUS", + /* 167 */ "TRUTH", + /* 168 */ "REGISTER", + /* 169 */ "VECTOR", + /* 170 */ "SELECT_COLUMN", + /* 171 */ "IF_NULL_ROW", + /* 172 */ "ASTERISK", + /* 173 */ "SPAN", + /* 174 */ "SPACE", + /* 175 */ "ILLEGAL", + /* 176 */ "input", + /* 177 */ "cmdlist", + /* 178 */ "ecmd", + /* 179 */ "cmdx", + /* 180 */ "explain", + /* 181 */ "cmd", + /* 182 */ "transtype", + /* 183 */ "trans_opt", + /* 184 */ "nm", + /* 185 */ "savepoint_opt", + /* 186 */ "create_table", + /* 187 */ "create_table_args", + /* 188 */ "createkw", + /* 189 */ "temp", + /* 190 */ "ifnotexists", + /* 191 */ "dbnm", + /* 192 */ "columnlist", + /* 193 */ "conslist_opt", + /* 194 */ "table_options", + /* 195 */ "select", + /* 196 */ "columnname", + /* 197 */ "carglist", + /* 198 */ "typetoken", + /* 199 */ "typename", + /* 200 */ "signed", + /* 201 */ "plus_num", + /* 202 */ "minus_num", + /* 203 */ "scanpt", + /* 204 */ "ccons", + /* 205 */ "term", + /* 206 */ "expr", + /* 207 */ "onconf", + /* 208 */ "sortorder", + /* 209 */ "autoinc", + /* 210 */ "eidlist_opt", + /* 211 */ "refargs", + /* 212 */ "defer_subclause", + /* 213 */ "refarg", + /* 214 */ "refact", + /* 215 */ "init_deferred_pred_opt", + /* 216 */ "conslist", + /* 217 */ "tconscomma", + /* 218 */ "tcons", + /* 219 */ "sortlist", + /* 220 */ "eidlist", + /* 221 */ "defer_subclause_opt", + /* 222 */ "orconf", + /* 223 */ "resolvetype", + /* 224 */ "raisetype", + /* 225 */ "ifexists", + /* 226 */ "fullname", + /* 227 */ "selectnowith", + /* 228 */ "oneselect", + /* 229 */ "wqlist", + /* 230 */ "multiselect_op", + /* 231 */ "distinct", + /* 232 */ "selcollist", + /* 233 */ "from", + /* 234 */ "where_opt", + /* 235 */ "groupby_opt", + /* 236 */ "having_opt", + /* 237 */ "orderby_opt", + /* 238 */ "limit_opt", + /* 239 */ "window_clause", + /* 240 */ "values", + /* 241 */ "nexprlist", + /* 242 */ "sclp", + /* 243 */ "as", + /* 244 */ "seltablist", + /* 245 */ "stl_prefix", + /* 246 */ "joinop", + /* 247 */ "indexed_opt", + /* 248 */ "on_opt", + /* 249 */ "using_opt", + /* 250 */ "exprlist", + /* 251 */ "xfullname", + /* 252 */ "idlist", + /* 253 */ "with", + /* 254 */ "setlist", + /* 255 */ "insert_cmd", + /* 256 */ "idlist_opt", + /* 257 */ "upsert", + /* 258 */ "over_clause", + /* 259 */ "likeop", + /* 260 */ "between_op", + /* 261 */ "in_op", + /* 262 */ "paren_exprlist", + /* 263 */ "case_operand", + /* 264 */ "case_exprlist", + /* 265 */ "case_else", + /* 266 */ "uniqueflag", + /* 267 */ "collate", + /* 268 */ "vinto", + /* 269 */ "nmnum", + /* 270 */ "trigger_decl", + /* 271 */ "trigger_cmd_list", + /* 272 */ "trigger_time", + /* 273 */ "trigger_event", + /* 274 */ "foreach_clause", + /* 275 */ "when_clause", + /* 276 */ "trigger_cmd", + /* 277 */ "trnm", + /* 278 */ "tridxby", + /* 279 */ "database_kw_opt", + /* 280 */ "key_opt", + /* 281 */ "add_column_fullname", + /* 282 */ "kwcolumn_opt", + /* 283 */ "create_vtab", + /* 284 */ "vtabarglist", + /* 285 */ "vtabarg", + /* 286 */ "vtabargtoken", + /* 287 */ "lp", + /* 288 */ "anylist", + /* 289 */ "windowdefn_list", + /* 290 */ "windowdefn", + /* 291 */ "window", + /* 292 */ "frame_opt", + /* 293 */ "part_opt", + /* 294 */ "filter_opt", + /* 295 */ "range_or_rows", + /* 296 */ "frame_bound", + /* 297 */ "frame_bound_s", + /* 298 */ "frame_bound_e", + /* 299 */ "frame_exclude_opt", + /* 300 */ "frame_exclude", }; #endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */ @@ -148434,144 +149814,152 @@ static const char *const yyRuleName[] = { /* 226 */ "collate ::=", /* 227 */ "collate ::= COLLATE ID|STRING", /* 228 */ "cmd ::= DROP INDEX ifexists fullname", - /* 229 */ "cmd ::= VACUUM", - /* 230 */ "cmd ::= VACUUM nm", - /* 231 */ "cmd ::= PRAGMA nm dbnm", - /* 232 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", - /* 233 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", - /* 234 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", - /* 235 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", - /* 236 */ "plus_num ::= PLUS INTEGER|FLOAT", - /* 237 */ "minus_num ::= MINUS INTEGER|FLOAT", - /* 238 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END", - /* 239 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause", - /* 240 */ "trigger_time ::= BEFORE|AFTER", - /* 241 */ "trigger_time ::= INSTEAD OF", - /* 242 */ "trigger_time ::=", - /* 243 */ "trigger_event ::= DELETE|INSERT", - /* 244 */ "trigger_event ::= UPDATE", - /* 245 */ "trigger_event ::= UPDATE OF idlist", - /* 246 */ "when_clause ::=", - /* 247 */ "when_clause ::= WHEN expr", - /* 248 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", - /* 249 */ "trigger_cmd_list ::= trigger_cmd SEMI", - /* 250 */ "trnm ::= nm DOT nm", - /* 251 */ "tridxby ::= INDEXED BY nm", - /* 252 */ "tridxby ::= NOT INDEXED", - /* 253 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt", - /* 254 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt", - /* 255 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt", - /* 256 */ "trigger_cmd ::= scanpt select scanpt", - /* 257 */ "expr ::= RAISE LP IGNORE RP", - /* 258 */ "expr ::= RAISE LP raisetype COMMA nm RP", - /* 259 */ "raisetype ::= ROLLBACK", - /* 260 */ "raisetype ::= ABORT", - /* 261 */ "raisetype ::= FAIL", - /* 262 */ "cmd ::= DROP TRIGGER ifexists fullname", - /* 263 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", - /* 264 */ "cmd ::= DETACH database_kw_opt expr", - /* 265 */ "key_opt ::=", - /* 266 */ "key_opt ::= KEY expr", - /* 267 */ "cmd ::= REINDEX", - /* 268 */ "cmd ::= REINDEX nm dbnm", - /* 269 */ "cmd ::= ANALYZE", - /* 270 */ "cmd ::= ANALYZE nm dbnm", - /* 271 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", - /* 272 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist", - /* 273 */ "add_column_fullname ::= fullname", - /* 274 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm", - /* 275 */ "cmd ::= create_vtab", - /* 276 */ "cmd ::= create_vtab LP vtabarglist RP", - /* 277 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm", - /* 278 */ "vtabarg ::=", - /* 279 */ "vtabargtoken ::= ANY", - /* 280 */ "vtabargtoken ::= lp anylist RP", - /* 281 */ "lp ::= LP", - /* 282 */ "with ::= WITH wqlist", - /* 283 */ "with ::= WITH RECURSIVE wqlist", - /* 284 */ "wqlist ::= nm eidlist_opt AS LP select RP", - /* 285 */ "wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP", - /* 286 */ "windowdefn_list ::= windowdefn", - /* 287 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn", - /* 288 */ "windowdefn ::= nm AS window", - /* 289 */ "window ::= LP part_opt orderby_opt frame_opt RP", - /* 290 */ "part_opt ::= PARTITION BY nexprlist", - /* 291 */ "part_opt ::=", - /* 292 */ "frame_opt ::=", - /* 293 */ "frame_opt ::= range_or_rows frame_bound_s", - /* 294 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e", - /* 295 */ "range_or_rows ::= RANGE", - /* 296 */ "range_or_rows ::= ROWS", - /* 297 */ "frame_bound_s ::= frame_bound", - /* 298 */ "frame_bound_s ::= UNBOUNDED PRECEDING", - /* 299 */ "frame_bound_e ::= frame_bound", - /* 300 */ "frame_bound_e ::= UNBOUNDED FOLLOWING", - /* 301 */ "frame_bound ::= expr PRECEDING", - /* 302 */ "frame_bound ::= CURRENT ROW", - /* 303 */ "frame_bound ::= expr FOLLOWING", - /* 304 */ "window_clause ::= WINDOW windowdefn_list", - /* 305 */ "over_clause ::= filter_opt OVER window", - /* 306 */ "over_clause ::= filter_opt OVER nm", - /* 307 */ "filter_opt ::=", - /* 308 */ "filter_opt ::= FILTER LP WHERE expr RP", - /* 309 */ "input ::= cmdlist", - /* 310 */ "cmdlist ::= cmdlist ecmd", - /* 311 */ "cmdlist ::= ecmd", - /* 312 */ "ecmd ::= SEMI", - /* 313 */ "ecmd ::= cmdx SEMI", - /* 314 */ "ecmd ::= explain cmdx", - /* 315 */ "trans_opt ::=", - /* 316 */ "trans_opt ::= TRANSACTION", - /* 317 */ "trans_opt ::= TRANSACTION nm", - /* 318 */ "savepoint_opt ::= SAVEPOINT", - /* 319 */ "savepoint_opt ::=", - /* 320 */ "cmd ::= create_table create_table_args", - /* 321 */ "columnlist ::= columnlist COMMA columnname carglist", - /* 322 */ "columnlist ::= columnname carglist", - /* 323 */ "nm ::= ID|INDEXED", - /* 324 */ "nm ::= STRING", - /* 325 */ "nm ::= JOIN_KW", - /* 326 */ "typetoken ::= typename", - /* 327 */ "typename ::= ID|STRING", - /* 328 */ "signed ::= plus_num", - /* 329 */ "signed ::= minus_num", - /* 330 */ "carglist ::= carglist ccons", - /* 331 */ "carglist ::=", - /* 332 */ "ccons ::= NULL onconf", - /* 333 */ "conslist_opt ::= COMMA conslist", - /* 334 */ "conslist ::= conslist tconscomma tcons", - /* 335 */ "conslist ::= tcons", - /* 336 */ "tconscomma ::=", - /* 337 */ "defer_subclause_opt ::= defer_subclause", - /* 338 */ "resolvetype ::= raisetype", - /* 339 */ "selectnowith ::= oneselect", - /* 340 */ "oneselect ::= values", - /* 341 */ "sclp ::= selcollist COMMA", - /* 342 */ "as ::= ID|STRING", - /* 343 */ "expr ::= term", - /* 344 */ "likeop ::= LIKE_KW|MATCH", - /* 345 */ "exprlist ::= nexprlist", - /* 346 */ "nmnum ::= plus_num", - /* 347 */ "nmnum ::= nm", - /* 348 */ "nmnum ::= ON", - /* 349 */ "nmnum ::= DELETE", - /* 350 */ "nmnum ::= DEFAULT", - /* 351 */ "plus_num ::= INTEGER|FLOAT", - /* 352 */ "foreach_clause ::=", - /* 353 */ "foreach_clause ::= FOR EACH ROW", - /* 354 */ "trnm ::= nm", - /* 355 */ "tridxby ::=", - /* 356 */ "database_kw_opt ::= DATABASE", - /* 357 */ "database_kw_opt ::=", - /* 358 */ "kwcolumn_opt ::=", - /* 359 */ "kwcolumn_opt ::= COLUMNKW", - /* 360 */ "vtabarglist ::= vtabarg", - /* 361 */ "vtabarglist ::= vtabarglist COMMA vtabarg", - /* 362 */ "vtabarg ::= vtabarg vtabargtoken", - /* 363 */ "anylist ::=", - /* 364 */ "anylist ::= anylist LP anylist RP", - /* 365 */ "anylist ::= anylist ANY", - /* 366 */ "with ::=", + /* 229 */ "cmd ::= VACUUM vinto", + /* 230 */ "cmd ::= VACUUM nm vinto", + /* 231 */ "vinto ::= INTO expr", + /* 232 */ "vinto ::=", + /* 233 */ "cmd ::= PRAGMA nm dbnm", + /* 234 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", + /* 235 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", + /* 236 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", + /* 237 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", + /* 238 */ "plus_num ::= PLUS INTEGER|FLOAT", + /* 239 */ "minus_num ::= MINUS INTEGER|FLOAT", + /* 240 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END", + /* 241 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause", + /* 242 */ "trigger_time ::= BEFORE|AFTER", + /* 243 */ "trigger_time ::= INSTEAD OF", + /* 244 */ "trigger_time ::=", + /* 245 */ "trigger_event ::= DELETE|INSERT", + /* 246 */ "trigger_event ::= UPDATE", + /* 247 */ "trigger_event ::= UPDATE OF idlist", + /* 248 */ "when_clause ::=", + /* 249 */ "when_clause ::= WHEN expr", + /* 250 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", + /* 251 */ "trigger_cmd_list ::= trigger_cmd SEMI", + /* 252 */ "trnm ::= nm DOT nm", + /* 253 */ "tridxby ::= INDEXED BY nm", + /* 254 */ "tridxby ::= NOT INDEXED", + /* 255 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt", + /* 256 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt", + /* 257 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt", + /* 258 */ "trigger_cmd ::= scanpt select scanpt", + /* 259 */ "expr ::= RAISE LP IGNORE RP", + /* 260 */ "expr ::= RAISE LP raisetype COMMA nm RP", + /* 261 */ "raisetype ::= ROLLBACK", + /* 262 */ "raisetype ::= ABORT", + /* 263 */ "raisetype ::= FAIL", + /* 264 */ "cmd ::= DROP TRIGGER ifexists fullname", + /* 265 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", + /* 266 */ "cmd ::= DETACH database_kw_opt expr", + /* 267 */ "key_opt ::=", + /* 268 */ "key_opt ::= KEY expr", + /* 269 */ "cmd ::= REINDEX", + /* 270 */ "cmd ::= REINDEX nm dbnm", + /* 271 */ "cmd ::= ANALYZE", + /* 272 */ "cmd ::= ANALYZE nm dbnm", + /* 273 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", + /* 274 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist", + /* 275 */ "add_column_fullname ::= fullname", + /* 276 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm", + /* 277 */ "cmd ::= create_vtab", + /* 278 */ "cmd ::= create_vtab LP vtabarglist RP", + /* 279 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm", + /* 280 */ "vtabarg ::=", + /* 281 */ "vtabargtoken ::= ANY", + /* 282 */ "vtabargtoken ::= lp anylist RP", + /* 283 */ "lp ::= LP", + /* 284 */ "with ::= WITH wqlist", + /* 285 */ "with ::= WITH RECURSIVE wqlist", + /* 286 */ "wqlist ::= nm eidlist_opt AS LP select RP", + /* 287 */ "wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP", + /* 288 */ "windowdefn_list ::= windowdefn", + /* 289 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn", + /* 290 */ "windowdefn ::= nm AS LP window RP", + /* 291 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt", + /* 292 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt", + /* 293 */ "window ::= ORDER BY sortlist frame_opt", + /* 294 */ "window ::= nm ORDER BY sortlist frame_opt", + /* 295 */ "window ::= frame_opt", + /* 296 */ "window ::= nm frame_opt", + /* 297 */ "frame_opt ::=", + /* 298 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt", + /* 299 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt", + /* 300 */ "range_or_rows ::= RANGE|ROWS|GROUPS", + /* 301 */ "frame_bound_s ::= frame_bound", + /* 302 */ "frame_bound_s ::= UNBOUNDED PRECEDING", + /* 303 */ "frame_bound_e ::= frame_bound", + /* 304 */ "frame_bound_e ::= UNBOUNDED FOLLOWING", + /* 305 */ "frame_bound ::= expr PRECEDING|FOLLOWING", + /* 306 */ "frame_bound ::= CURRENT ROW", + /* 307 */ "frame_exclude_opt ::=", + /* 308 */ "frame_exclude_opt ::= EXCLUDE frame_exclude", + /* 309 */ "frame_exclude ::= NO OTHERS", + /* 310 */ "frame_exclude ::= CURRENT ROW", + /* 311 */ "frame_exclude ::= GROUP|TIES", + /* 312 */ "window_clause ::= WINDOW windowdefn_list", + /* 313 */ "over_clause ::= filter_opt OVER LP window RP", + /* 314 */ "over_clause ::= filter_opt OVER nm", + /* 315 */ "filter_opt ::=", + /* 316 */ "filter_opt ::= FILTER LP WHERE expr RP", + /* 317 */ "input ::= cmdlist", + /* 318 */ "cmdlist ::= cmdlist ecmd", + /* 319 */ "cmdlist ::= ecmd", + /* 320 */ "ecmd ::= SEMI", + /* 321 */ "ecmd ::= cmdx SEMI", + /* 322 */ "ecmd ::= explain cmdx", + /* 323 */ "trans_opt ::=", + /* 324 */ "trans_opt ::= TRANSACTION", + /* 325 */ "trans_opt ::= TRANSACTION nm", + /* 326 */ "savepoint_opt ::= SAVEPOINT", + /* 327 */ "savepoint_opt ::=", + /* 328 */ "cmd ::= create_table create_table_args", + /* 329 */ "columnlist ::= columnlist COMMA columnname carglist", + /* 330 */ "columnlist ::= columnname carglist", + /* 331 */ "nm ::= ID|INDEXED", + /* 332 */ "nm ::= STRING", + /* 333 */ "nm ::= JOIN_KW", + /* 334 */ "typetoken ::= typename", + /* 335 */ "typename ::= ID|STRING", + /* 336 */ "signed ::= plus_num", + /* 337 */ "signed ::= minus_num", + /* 338 */ "carglist ::= carglist ccons", + /* 339 */ "carglist ::=", + /* 340 */ "ccons ::= NULL onconf", + /* 341 */ "conslist_opt ::= COMMA conslist", + /* 342 */ "conslist ::= conslist tconscomma tcons", + /* 343 */ "conslist ::= tcons", + /* 344 */ "tconscomma ::=", + /* 345 */ "defer_subclause_opt ::= defer_subclause", + /* 346 */ "resolvetype ::= raisetype", + /* 347 */ "selectnowith ::= oneselect", + /* 348 */ "oneselect ::= values", + /* 349 */ "sclp ::= selcollist COMMA", + /* 350 */ "as ::= ID|STRING", + /* 351 */ "expr ::= term", + /* 352 */ "likeop ::= LIKE_KW|MATCH", + /* 353 */ "exprlist ::= nexprlist", + /* 354 */ "nmnum ::= plus_num", + /* 355 */ "nmnum ::= nm", + /* 356 */ "nmnum ::= ON", + /* 357 */ "nmnum ::= DELETE", + /* 358 */ "nmnum ::= DEFAULT", + /* 359 */ "plus_num ::= INTEGER|FLOAT", + /* 360 */ "foreach_clause ::=", + /* 361 */ "foreach_clause ::= FOR EACH ROW", + /* 362 */ "trnm ::= nm", + /* 363 */ "tridxby ::=", + /* 364 */ "database_kw_opt ::= DATABASE", + /* 365 */ "database_kw_opt ::=", + /* 366 */ "kwcolumn_opt ::=", + /* 367 */ "kwcolumn_opt ::= COLUMNKW", + /* 368 */ "vtabarglist ::= vtabarg", + /* 369 */ "vtabarglist ::= vtabarglist COMMA vtabarg", + /* 370 */ "vtabarg ::= vtabarg vtabargtoken", + /* 371 */ "anylist ::=", + /* 372 */ "anylist ::= anylist LP anylist RP", + /* 373 */ "anylist ::= anylist ANY", + /* 374 */ "with ::=", }; #endif /* NDEBUG */ @@ -148697,96 +150085,97 @@ static void yy_destructor( ** inside the C code. */ /********* Begin destructor definitions ***************************************/ - case 174: /* select */ - case 206: /* selectnowith */ - case 207: /* oneselect */ - case 219: /* values */ + case 195: /* select */ + case 227: /* selectnowith */ + case 228: /* oneselect */ + case 240: /* values */ { -sqlite3SelectDelete(pParse->db, (yypminor->yy489)); +sqlite3SelectDelete(pParse->db, (yypminor->yy457)); } break; - case 184: /* term */ - case 185: /* expr */ - case 213: /* where_opt */ - case 215: /* having_opt */ - case 227: /* on_opt */ - case 242: /* case_operand */ - case 244: /* case_else */ - case 253: /* when_clause */ - case 258: /* key_opt */ - case 272: /* filter_opt */ + case 205: /* term */ + case 206: /* expr */ + case 234: /* where_opt */ + case 236: /* having_opt */ + case 248: /* on_opt */ + case 263: /* case_operand */ + case 265: /* case_else */ + case 268: /* vinto */ + case 275: /* when_clause */ + case 280: /* key_opt */ + case 294: /* filter_opt */ { -sqlite3ExprDelete(pParse->db, (yypminor->yy18)); +sqlite3ExprDelete(pParse->db, (yypminor->yy524)); } break; - case 189: /* eidlist_opt */ - case 198: /* sortlist */ - case 199: /* eidlist */ - case 211: /* selcollist */ - case 214: /* groupby_opt */ - case 216: /* orderby_opt */ - case 220: /* nexprlist */ - case 221: /* sclp */ - case 229: /* exprlist */ - case 233: /* setlist */ - case 241: /* paren_exprlist */ - case 243: /* case_exprlist */ - case 271: /* part_opt */ + case 210: /* eidlist_opt */ + case 219: /* sortlist */ + case 220: /* eidlist */ + case 232: /* selcollist */ + case 235: /* groupby_opt */ + case 237: /* orderby_opt */ + case 241: /* nexprlist */ + case 242: /* sclp */ + case 250: /* exprlist */ + case 254: /* setlist */ + case 262: /* paren_exprlist */ + case 264: /* case_exprlist */ + case 293: /* part_opt */ { -sqlite3ExprListDelete(pParse->db, (yypminor->yy420)); +sqlite3ExprListDelete(pParse->db, (yypminor->yy434)); } break; - case 205: /* fullname */ - case 212: /* from */ - case 223: /* seltablist */ - case 224: /* stl_prefix */ - case 230: /* xfullname */ + case 226: /* fullname */ + case 233: /* from */ + case 244: /* seltablist */ + case 245: /* stl_prefix */ + case 251: /* xfullname */ { -sqlite3SrcListDelete(pParse->db, (yypminor->yy135)); +sqlite3SrcListDelete(pParse->db, (yypminor->yy483)); } break; - case 208: /* wqlist */ + case 229: /* wqlist */ { -sqlite3WithDelete(pParse->db, (yypminor->yy449)); +sqlite3WithDelete(pParse->db, (yypminor->yy59)); } break; - case 218: /* window_clause */ - case 267: /* windowdefn_list */ + case 239: /* window_clause */ + case 289: /* windowdefn_list */ { -sqlite3WindowListDelete(pParse->db, (yypminor->yy327)); +sqlite3WindowListDelete(pParse->db, (yypminor->yy295)); } break; - case 228: /* using_opt */ - case 231: /* idlist */ - case 235: /* idlist_opt */ + case 249: /* using_opt */ + case 252: /* idlist */ + case 256: /* idlist_opt */ { -sqlite3IdListDelete(pParse->db, (yypminor->yy48)); +sqlite3IdListDelete(pParse->db, (yypminor->yy62)); } break; - case 237: /* over_clause */ - case 268: /* windowdefn */ - case 269: /* window */ - case 270: /* frame_opt */ + case 258: /* over_clause */ + case 290: /* windowdefn */ + case 291: /* window */ + case 292: /* frame_opt */ { -sqlite3WindowDelete(pParse->db, (yypminor->yy327)); +sqlite3WindowDelete(pParse->db, (yypminor->yy295)); } break; - case 249: /* trigger_cmd_list */ - case 254: /* trigger_cmd */ + case 271: /* trigger_cmd_list */ + case 276: /* trigger_cmd */ { -sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy207)); +sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy455)); } break; - case 251: /* trigger_event */ + case 273: /* trigger_event */ { -sqlite3IdListDelete(pParse->db, (yypminor->yy34).b); +sqlite3IdListDelete(pParse->db, (yypminor->yy90).b); } break; - case 274: /* frame_bound */ - case 275: /* frame_bound_s */ - case 276: /* frame_bound_e */ + case 296: /* frame_bound */ + case 297: /* frame_bound_s */ + case 298: /* frame_bound_e */ { -sqlite3ExprDelete(pParse->db, (yypminor->yy119).pExpr); +sqlite3ExprDelete(pParse->db, (yypminor->yy201).pExpr); } break; /********* End destructor definitions *****************************************/ @@ -149078,380 +150467,764 @@ static void yy_shift( yyTraceShift(yypParser, yyNewState, "Shift"); } -/* The following table contains information about every rule that -** is used during the reduce. -*/ -static const struct { - YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */ - signed char nrhs; /* Negative of the number of RHS symbols in the rule */ -} yyRuleInfo[] = { - { 159, -1 }, /* (0) explain ::= EXPLAIN */ - { 159, -3 }, /* (1) explain ::= EXPLAIN QUERY PLAN */ - { 158, -1 }, /* (2) cmdx ::= cmd */ - { 160, -3 }, /* (3) cmd ::= BEGIN transtype trans_opt */ - { 161, 0 }, /* (4) transtype ::= */ - { 161, -1 }, /* (5) transtype ::= DEFERRED */ - { 161, -1 }, /* (6) transtype ::= IMMEDIATE */ - { 161, -1 }, /* (7) transtype ::= EXCLUSIVE */ - { 160, -2 }, /* (8) cmd ::= COMMIT|END trans_opt */ - { 160, -2 }, /* (9) cmd ::= ROLLBACK trans_opt */ - { 160, -2 }, /* (10) cmd ::= SAVEPOINT nm */ - { 160, -3 }, /* (11) cmd ::= RELEASE savepoint_opt nm */ - { 160, -5 }, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */ - { 165, -6 }, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */ - { 167, -1 }, /* (14) createkw ::= CREATE */ - { 169, 0 }, /* (15) ifnotexists ::= */ - { 169, -3 }, /* (16) ifnotexists ::= IF NOT EXISTS */ - { 168, -1 }, /* (17) temp ::= TEMP */ - { 168, 0 }, /* (18) temp ::= */ - { 166, -5 }, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_options */ - { 166, -2 }, /* (20) create_table_args ::= AS select */ - { 173, 0 }, /* (21) table_options ::= */ - { 173, -2 }, /* (22) table_options ::= WITHOUT nm */ - { 175, -2 }, /* (23) columnname ::= nm typetoken */ - { 177, 0 }, /* (24) typetoken ::= */ - { 177, -4 }, /* (25) typetoken ::= typename LP signed RP */ - { 177, -6 }, /* (26) typetoken ::= typename LP signed COMMA signed RP */ - { 178, -2 }, /* (27) typename ::= typename ID|STRING */ - { 182, 0 }, /* (28) scanpt ::= */ - { 183, -2 }, /* (29) ccons ::= CONSTRAINT nm */ - { 183, -4 }, /* (30) ccons ::= DEFAULT scanpt term scanpt */ - { 183, -4 }, /* (31) ccons ::= DEFAULT LP expr RP */ - { 183, -4 }, /* (32) ccons ::= DEFAULT PLUS term scanpt */ - { 183, -4 }, /* (33) ccons ::= DEFAULT MINUS term scanpt */ - { 183, -3 }, /* (34) ccons ::= DEFAULT scanpt ID|INDEXED */ - { 183, -3 }, /* (35) ccons ::= NOT NULL onconf */ - { 183, -5 }, /* (36) ccons ::= PRIMARY KEY sortorder onconf autoinc */ - { 183, -2 }, /* (37) ccons ::= UNIQUE onconf */ - { 183, -4 }, /* (38) ccons ::= CHECK LP expr RP */ - { 183, -4 }, /* (39) ccons ::= REFERENCES nm eidlist_opt refargs */ - { 183, -1 }, /* (40) ccons ::= defer_subclause */ - { 183, -2 }, /* (41) ccons ::= COLLATE ID|STRING */ - { 188, 0 }, /* (42) autoinc ::= */ - { 188, -1 }, /* (43) autoinc ::= AUTOINCR */ - { 190, 0 }, /* (44) refargs ::= */ - { 190, -2 }, /* (45) refargs ::= refargs refarg */ - { 192, -2 }, /* (46) refarg ::= MATCH nm */ - { 192, -3 }, /* (47) refarg ::= ON INSERT refact */ - { 192, -3 }, /* (48) refarg ::= ON DELETE refact */ - { 192, -3 }, /* (49) refarg ::= ON UPDATE refact */ - { 193, -2 }, /* (50) refact ::= SET NULL */ - { 193, -2 }, /* (51) refact ::= SET DEFAULT */ - { 193, -1 }, /* (52) refact ::= CASCADE */ - { 193, -1 }, /* (53) refact ::= RESTRICT */ - { 193, -2 }, /* (54) refact ::= NO ACTION */ - { 191, -3 }, /* (55) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ - { 191, -2 }, /* (56) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ - { 194, 0 }, /* (57) init_deferred_pred_opt ::= */ - { 194, -2 }, /* (58) init_deferred_pred_opt ::= INITIALLY DEFERRED */ - { 194, -2 }, /* (59) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ - { 172, 0 }, /* (60) conslist_opt ::= */ - { 196, -1 }, /* (61) tconscomma ::= COMMA */ - { 197, -2 }, /* (62) tcons ::= CONSTRAINT nm */ - { 197, -7 }, /* (63) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ - { 197, -5 }, /* (64) tcons ::= UNIQUE LP sortlist RP onconf */ - { 197, -5 }, /* (65) tcons ::= CHECK LP expr RP onconf */ - { 197, -10 }, /* (66) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ - { 200, 0 }, /* (67) defer_subclause_opt ::= */ - { 186, 0 }, /* (68) onconf ::= */ - { 186, -3 }, /* (69) onconf ::= ON CONFLICT resolvetype */ - { 201, 0 }, /* (70) orconf ::= */ - { 201, -2 }, /* (71) orconf ::= OR resolvetype */ - { 202, -1 }, /* (72) resolvetype ::= IGNORE */ - { 202, -1 }, /* (73) resolvetype ::= REPLACE */ - { 160, -4 }, /* (74) cmd ::= DROP TABLE ifexists fullname */ - { 204, -2 }, /* (75) ifexists ::= IF EXISTS */ - { 204, 0 }, /* (76) ifexists ::= */ - { 160, -9 }, /* (77) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ - { 160, -4 }, /* (78) cmd ::= DROP VIEW ifexists fullname */ - { 160, -1 }, /* (79) cmd ::= select */ - { 174, -3 }, /* (80) select ::= WITH wqlist selectnowith */ - { 174, -4 }, /* (81) select ::= WITH RECURSIVE wqlist selectnowith */ - { 174, -1 }, /* (82) select ::= selectnowith */ - { 206, -3 }, /* (83) selectnowith ::= selectnowith multiselect_op oneselect */ - { 209, -1 }, /* (84) multiselect_op ::= UNION */ - { 209, -2 }, /* (85) multiselect_op ::= UNION ALL */ - { 209, -1 }, /* (86) multiselect_op ::= EXCEPT|INTERSECT */ - { 207, -9 }, /* (87) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ - { 207, -10 }, /* (88) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ - { 219, -4 }, /* (89) values ::= VALUES LP nexprlist RP */ - { 219, -5 }, /* (90) values ::= values COMMA LP nexprlist RP */ - { 210, -1 }, /* (91) distinct ::= DISTINCT */ - { 210, -1 }, /* (92) distinct ::= ALL */ - { 210, 0 }, /* (93) distinct ::= */ - { 221, 0 }, /* (94) sclp ::= */ - { 211, -5 }, /* (95) selcollist ::= sclp scanpt expr scanpt as */ - { 211, -3 }, /* (96) selcollist ::= sclp scanpt STAR */ - { 211, -5 }, /* (97) selcollist ::= sclp scanpt nm DOT STAR */ - { 222, -2 }, /* (98) as ::= AS nm */ - { 222, 0 }, /* (99) as ::= */ - { 212, 0 }, /* (100) from ::= */ - { 212, -2 }, /* (101) from ::= FROM seltablist */ - { 224, -2 }, /* (102) stl_prefix ::= seltablist joinop */ - { 224, 0 }, /* (103) stl_prefix ::= */ - { 223, -7 }, /* (104) seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ - { 223, -9 }, /* (105) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */ - { 223, -7 }, /* (106) seltablist ::= stl_prefix LP select RP as on_opt using_opt */ - { 223, -7 }, /* (107) seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ - { 170, 0 }, /* (108) dbnm ::= */ - { 170, -2 }, /* (109) dbnm ::= DOT nm */ - { 205, -1 }, /* (110) fullname ::= nm */ - { 205, -3 }, /* (111) fullname ::= nm DOT nm */ - { 230, -1 }, /* (112) xfullname ::= nm */ - { 230, -3 }, /* (113) xfullname ::= nm DOT nm */ - { 230, -5 }, /* (114) xfullname ::= nm DOT nm AS nm */ - { 230, -3 }, /* (115) xfullname ::= nm AS nm */ - { 225, -1 }, /* (116) joinop ::= COMMA|JOIN */ - { 225, -2 }, /* (117) joinop ::= JOIN_KW JOIN */ - { 225, -3 }, /* (118) joinop ::= JOIN_KW nm JOIN */ - { 225, -4 }, /* (119) joinop ::= JOIN_KW nm nm JOIN */ - { 227, -2 }, /* (120) on_opt ::= ON expr */ - { 227, 0 }, /* (121) on_opt ::= */ - { 226, 0 }, /* (122) indexed_opt ::= */ - { 226, -3 }, /* (123) indexed_opt ::= INDEXED BY nm */ - { 226, -2 }, /* (124) indexed_opt ::= NOT INDEXED */ - { 228, -4 }, /* (125) using_opt ::= USING LP idlist RP */ - { 228, 0 }, /* (126) using_opt ::= */ - { 216, 0 }, /* (127) orderby_opt ::= */ - { 216, -3 }, /* (128) orderby_opt ::= ORDER BY sortlist */ - { 198, -4 }, /* (129) sortlist ::= sortlist COMMA expr sortorder */ - { 198, -2 }, /* (130) sortlist ::= expr sortorder */ - { 187, -1 }, /* (131) sortorder ::= ASC */ - { 187, -1 }, /* (132) sortorder ::= DESC */ - { 187, 0 }, /* (133) sortorder ::= */ - { 214, 0 }, /* (134) groupby_opt ::= */ - { 214, -3 }, /* (135) groupby_opt ::= GROUP BY nexprlist */ - { 215, 0 }, /* (136) having_opt ::= */ - { 215, -2 }, /* (137) having_opt ::= HAVING expr */ - { 217, 0 }, /* (138) limit_opt ::= */ - { 217, -2 }, /* (139) limit_opt ::= LIMIT expr */ - { 217, -4 }, /* (140) limit_opt ::= LIMIT expr OFFSET expr */ - { 217, -4 }, /* (141) limit_opt ::= LIMIT expr COMMA expr */ - { 160, -6 }, /* (142) cmd ::= with DELETE FROM xfullname indexed_opt where_opt */ - { 213, 0 }, /* (143) where_opt ::= */ - { 213, -2 }, /* (144) where_opt ::= WHERE expr */ - { 160, -8 }, /* (145) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt */ - { 233, -5 }, /* (146) setlist ::= setlist COMMA nm EQ expr */ - { 233, -7 }, /* (147) setlist ::= setlist COMMA LP idlist RP EQ expr */ - { 233, -3 }, /* (148) setlist ::= nm EQ expr */ - { 233, -5 }, /* (149) setlist ::= LP idlist RP EQ expr */ - { 160, -7 }, /* (150) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ - { 160, -7 }, /* (151) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */ - { 236, 0 }, /* (152) upsert ::= */ - { 236, -11 }, /* (153) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */ - { 236, -8 }, /* (154) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */ - { 236, -4 }, /* (155) upsert ::= ON CONFLICT DO NOTHING */ - { 234, -2 }, /* (156) insert_cmd ::= INSERT orconf */ - { 234, -1 }, /* (157) insert_cmd ::= REPLACE */ - { 235, 0 }, /* (158) idlist_opt ::= */ - { 235, -3 }, /* (159) idlist_opt ::= LP idlist RP */ - { 231, -3 }, /* (160) idlist ::= idlist COMMA nm */ - { 231, -1 }, /* (161) idlist ::= nm */ - { 185, -3 }, /* (162) expr ::= LP expr RP */ - { 185, -1 }, /* (163) expr ::= ID|INDEXED */ - { 185, -1 }, /* (164) expr ::= JOIN_KW */ - { 185, -3 }, /* (165) expr ::= nm DOT nm */ - { 185, -5 }, /* (166) expr ::= nm DOT nm DOT nm */ - { 184, -1 }, /* (167) term ::= NULL|FLOAT|BLOB */ - { 184, -1 }, /* (168) term ::= STRING */ - { 184, -1 }, /* (169) term ::= INTEGER */ - { 185, -1 }, /* (170) expr ::= VARIABLE */ - { 185, -3 }, /* (171) expr ::= expr COLLATE ID|STRING */ - { 185, -6 }, /* (172) expr ::= CAST LP expr AS typetoken RP */ - { 185, -5 }, /* (173) expr ::= ID|INDEXED LP distinct exprlist RP */ - { 185, -4 }, /* (174) expr ::= ID|INDEXED LP STAR RP */ - { 185, -6 }, /* (175) expr ::= ID|INDEXED LP distinct exprlist RP over_clause */ - { 185, -5 }, /* (176) expr ::= ID|INDEXED LP STAR RP over_clause */ - { 184, -1 }, /* (177) term ::= CTIME_KW */ - { 185, -5 }, /* (178) expr ::= LP nexprlist COMMA expr RP */ - { 185, -3 }, /* (179) expr ::= expr AND expr */ - { 185, -3 }, /* (180) expr ::= expr OR expr */ - { 185, -3 }, /* (181) expr ::= expr LT|GT|GE|LE expr */ - { 185, -3 }, /* (182) expr ::= expr EQ|NE expr */ - { 185, -3 }, /* (183) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ - { 185, -3 }, /* (184) expr ::= expr PLUS|MINUS expr */ - { 185, -3 }, /* (185) expr ::= expr STAR|SLASH|REM expr */ - { 185, -3 }, /* (186) expr ::= expr CONCAT expr */ - { 238, -2 }, /* (187) likeop ::= NOT LIKE_KW|MATCH */ - { 185, -3 }, /* (188) expr ::= expr likeop expr */ - { 185, -5 }, /* (189) expr ::= expr likeop expr ESCAPE expr */ - { 185, -2 }, /* (190) expr ::= expr ISNULL|NOTNULL */ - { 185, -3 }, /* (191) expr ::= expr NOT NULL */ - { 185, -3 }, /* (192) expr ::= expr IS expr */ - { 185, -4 }, /* (193) expr ::= expr IS NOT expr */ - { 185, -2 }, /* (194) expr ::= NOT expr */ - { 185, -2 }, /* (195) expr ::= BITNOT expr */ - { 185, -2 }, /* (196) expr ::= PLUS|MINUS expr */ - { 239, -1 }, /* (197) between_op ::= BETWEEN */ - { 239, -2 }, /* (198) between_op ::= NOT BETWEEN */ - { 185, -5 }, /* (199) expr ::= expr between_op expr AND expr */ - { 240, -1 }, /* (200) in_op ::= IN */ - { 240, -2 }, /* (201) in_op ::= NOT IN */ - { 185, -5 }, /* (202) expr ::= expr in_op LP exprlist RP */ - { 185, -3 }, /* (203) expr ::= LP select RP */ - { 185, -5 }, /* (204) expr ::= expr in_op LP select RP */ - { 185, -5 }, /* (205) expr ::= expr in_op nm dbnm paren_exprlist */ - { 185, -4 }, /* (206) expr ::= EXISTS LP select RP */ - { 185, -5 }, /* (207) expr ::= CASE case_operand case_exprlist case_else END */ - { 243, -5 }, /* (208) case_exprlist ::= case_exprlist WHEN expr THEN expr */ - { 243, -4 }, /* (209) case_exprlist ::= WHEN expr THEN expr */ - { 244, -2 }, /* (210) case_else ::= ELSE expr */ - { 244, 0 }, /* (211) case_else ::= */ - { 242, -1 }, /* (212) case_operand ::= expr */ - { 242, 0 }, /* (213) case_operand ::= */ - { 229, 0 }, /* (214) exprlist ::= */ - { 220, -3 }, /* (215) nexprlist ::= nexprlist COMMA expr */ - { 220, -1 }, /* (216) nexprlist ::= expr */ - { 241, 0 }, /* (217) paren_exprlist ::= */ - { 241, -3 }, /* (218) paren_exprlist ::= LP exprlist RP */ - { 160, -12 }, /* (219) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ - { 245, -1 }, /* (220) uniqueflag ::= UNIQUE */ - { 245, 0 }, /* (221) uniqueflag ::= */ - { 189, 0 }, /* (222) eidlist_opt ::= */ - { 189, -3 }, /* (223) eidlist_opt ::= LP eidlist RP */ - { 199, -5 }, /* (224) eidlist ::= eidlist COMMA nm collate sortorder */ - { 199, -3 }, /* (225) eidlist ::= nm collate sortorder */ - { 246, 0 }, /* (226) collate ::= */ - { 246, -2 }, /* (227) collate ::= COLLATE ID|STRING */ - { 160, -4 }, /* (228) cmd ::= DROP INDEX ifexists fullname */ - { 160, -1 }, /* (229) cmd ::= VACUUM */ - { 160, -2 }, /* (230) cmd ::= VACUUM nm */ - { 160, -3 }, /* (231) cmd ::= PRAGMA nm dbnm */ - { 160, -5 }, /* (232) cmd ::= PRAGMA nm dbnm EQ nmnum */ - { 160, -6 }, /* (233) cmd ::= PRAGMA nm dbnm LP nmnum RP */ - { 160, -5 }, /* (234) cmd ::= PRAGMA nm dbnm EQ minus_num */ - { 160, -6 }, /* (235) cmd ::= PRAGMA nm dbnm LP minus_num RP */ - { 180, -2 }, /* (236) plus_num ::= PLUS INTEGER|FLOAT */ - { 181, -2 }, /* (237) minus_num ::= MINUS INTEGER|FLOAT */ - { 160, -5 }, /* (238) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ - { 248, -11 }, /* (239) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ - { 250, -1 }, /* (240) trigger_time ::= BEFORE|AFTER */ - { 250, -2 }, /* (241) trigger_time ::= INSTEAD OF */ - { 250, 0 }, /* (242) trigger_time ::= */ - { 251, -1 }, /* (243) trigger_event ::= DELETE|INSERT */ - { 251, -1 }, /* (244) trigger_event ::= UPDATE */ - { 251, -3 }, /* (245) trigger_event ::= UPDATE OF idlist */ - { 253, 0 }, /* (246) when_clause ::= */ - { 253, -2 }, /* (247) when_clause ::= WHEN expr */ - { 249, -3 }, /* (248) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ - { 249, -2 }, /* (249) trigger_cmd_list ::= trigger_cmd SEMI */ - { 255, -3 }, /* (250) trnm ::= nm DOT nm */ - { 256, -3 }, /* (251) tridxby ::= INDEXED BY nm */ - { 256, -2 }, /* (252) tridxby ::= NOT INDEXED */ - { 254, -8 }, /* (253) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */ - { 254, -8 }, /* (254) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ - { 254, -6 }, /* (255) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ - { 254, -3 }, /* (256) trigger_cmd ::= scanpt select scanpt */ - { 185, -4 }, /* (257) expr ::= RAISE LP IGNORE RP */ - { 185, -6 }, /* (258) expr ::= RAISE LP raisetype COMMA nm RP */ - { 203, -1 }, /* (259) raisetype ::= ROLLBACK */ - { 203, -1 }, /* (260) raisetype ::= ABORT */ - { 203, -1 }, /* (261) raisetype ::= FAIL */ - { 160, -4 }, /* (262) cmd ::= DROP TRIGGER ifexists fullname */ - { 160, -6 }, /* (263) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ - { 160, -3 }, /* (264) cmd ::= DETACH database_kw_opt expr */ - { 258, 0 }, /* (265) key_opt ::= */ - { 258, -2 }, /* (266) key_opt ::= KEY expr */ - { 160, -1 }, /* (267) cmd ::= REINDEX */ - { 160, -3 }, /* (268) cmd ::= REINDEX nm dbnm */ - { 160, -1 }, /* (269) cmd ::= ANALYZE */ - { 160, -3 }, /* (270) cmd ::= ANALYZE nm dbnm */ - { 160, -6 }, /* (271) cmd ::= ALTER TABLE fullname RENAME TO nm */ - { 160, -7 }, /* (272) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ - { 259, -1 }, /* (273) add_column_fullname ::= fullname */ - { 160, -8 }, /* (274) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ - { 160, -1 }, /* (275) cmd ::= create_vtab */ - { 160, -4 }, /* (276) cmd ::= create_vtab LP vtabarglist RP */ - { 261, -8 }, /* (277) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ - { 263, 0 }, /* (278) vtabarg ::= */ - { 264, -1 }, /* (279) vtabargtoken ::= ANY */ - { 264, -3 }, /* (280) vtabargtoken ::= lp anylist RP */ - { 265, -1 }, /* (281) lp ::= LP */ - { 232, -2 }, /* (282) with ::= WITH wqlist */ - { 232, -3 }, /* (283) with ::= WITH RECURSIVE wqlist */ - { 208, -6 }, /* (284) wqlist ::= nm eidlist_opt AS LP select RP */ - { 208, -8 }, /* (285) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */ - { 267, -1 }, /* (286) windowdefn_list ::= windowdefn */ - { 267, -3 }, /* (287) windowdefn_list ::= windowdefn_list COMMA windowdefn */ - { 268, -3 }, /* (288) windowdefn ::= nm AS window */ - { 269, -5 }, /* (289) window ::= LP part_opt orderby_opt frame_opt RP */ - { 271, -3 }, /* (290) part_opt ::= PARTITION BY nexprlist */ - { 271, 0 }, /* (291) part_opt ::= */ - { 270, 0 }, /* (292) frame_opt ::= */ - { 270, -2 }, /* (293) frame_opt ::= range_or_rows frame_bound_s */ - { 270, -5 }, /* (294) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e */ - { 273, -1 }, /* (295) range_or_rows ::= RANGE */ - { 273, -1 }, /* (296) range_or_rows ::= ROWS */ - { 275, -1 }, /* (297) frame_bound_s ::= frame_bound */ - { 275, -2 }, /* (298) frame_bound_s ::= UNBOUNDED PRECEDING */ - { 276, -1 }, /* (299) frame_bound_e ::= frame_bound */ - { 276, -2 }, /* (300) frame_bound_e ::= UNBOUNDED FOLLOWING */ - { 274, -2 }, /* (301) frame_bound ::= expr PRECEDING */ - { 274, -2 }, /* (302) frame_bound ::= CURRENT ROW */ - { 274, -2 }, /* (303) frame_bound ::= expr FOLLOWING */ - { 218, -2 }, /* (304) window_clause ::= WINDOW windowdefn_list */ - { 237, -3 }, /* (305) over_clause ::= filter_opt OVER window */ - { 237, -3 }, /* (306) over_clause ::= filter_opt OVER nm */ - { 272, 0 }, /* (307) filter_opt ::= */ - { 272, -5 }, /* (308) filter_opt ::= FILTER LP WHERE expr RP */ - { 155, -1 }, /* (309) input ::= cmdlist */ - { 156, -2 }, /* (310) cmdlist ::= cmdlist ecmd */ - { 156, -1 }, /* (311) cmdlist ::= ecmd */ - { 157, -1 }, /* (312) ecmd ::= SEMI */ - { 157, -2 }, /* (313) ecmd ::= cmdx SEMI */ - { 157, -2 }, /* (314) ecmd ::= explain cmdx */ - { 162, 0 }, /* (315) trans_opt ::= */ - { 162, -1 }, /* (316) trans_opt ::= TRANSACTION */ - { 162, -2 }, /* (317) trans_opt ::= TRANSACTION nm */ - { 164, -1 }, /* (318) savepoint_opt ::= SAVEPOINT */ - { 164, 0 }, /* (319) savepoint_opt ::= */ - { 160, -2 }, /* (320) cmd ::= create_table create_table_args */ - { 171, -4 }, /* (321) columnlist ::= columnlist COMMA columnname carglist */ - { 171, -2 }, /* (322) columnlist ::= columnname carglist */ - { 163, -1 }, /* (323) nm ::= ID|INDEXED */ - { 163, -1 }, /* (324) nm ::= STRING */ - { 163, -1 }, /* (325) nm ::= JOIN_KW */ - { 177, -1 }, /* (326) typetoken ::= typename */ - { 178, -1 }, /* (327) typename ::= ID|STRING */ - { 179, -1 }, /* (328) signed ::= plus_num */ - { 179, -1 }, /* (329) signed ::= minus_num */ - { 176, -2 }, /* (330) carglist ::= carglist ccons */ - { 176, 0 }, /* (331) carglist ::= */ - { 183, -2 }, /* (332) ccons ::= NULL onconf */ - { 172, -2 }, /* (333) conslist_opt ::= COMMA conslist */ - { 195, -3 }, /* (334) conslist ::= conslist tconscomma tcons */ - { 195, -1 }, /* (335) conslist ::= tcons */ - { 196, 0 }, /* (336) tconscomma ::= */ - { 200, -1 }, /* (337) defer_subclause_opt ::= defer_subclause */ - { 202, -1 }, /* (338) resolvetype ::= raisetype */ - { 206, -1 }, /* (339) selectnowith ::= oneselect */ - { 207, -1 }, /* (340) oneselect ::= values */ - { 221, -2 }, /* (341) sclp ::= selcollist COMMA */ - { 222, -1 }, /* (342) as ::= ID|STRING */ - { 185, -1 }, /* (343) expr ::= term */ - { 238, -1 }, /* (344) likeop ::= LIKE_KW|MATCH */ - { 229, -1 }, /* (345) exprlist ::= nexprlist */ - { 247, -1 }, /* (346) nmnum ::= plus_num */ - { 247, -1 }, /* (347) nmnum ::= nm */ - { 247, -1 }, /* (348) nmnum ::= ON */ - { 247, -1 }, /* (349) nmnum ::= DELETE */ - { 247, -1 }, /* (350) nmnum ::= DEFAULT */ - { 180, -1 }, /* (351) plus_num ::= INTEGER|FLOAT */ - { 252, 0 }, /* (352) foreach_clause ::= */ - { 252, -3 }, /* (353) foreach_clause ::= FOR EACH ROW */ - { 255, -1 }, /* (354) trnm ::= nm */ - { 256, 0 }, /* (355) tridxby ::= */ - { 257, -1 }, /* (356) database_kw_opt ::= DATABASE */ - { 257, 0 }, /* (357) database_kw_opt ::= */ - { 260, 0 }, /* (358) kwcolumn_opt ::= */ - { 260, -1 }, /* (359) kwcolumn_opt ::= COLUMNKW */ - { 262, -1 }, /* (360) vtabarglist ::= vtabarg */ - { 262, -3 }, /* (361) vtabarglist ::= vtabarglist COMMA vtabarg */ - { 263, -2 }, /* (362) vtabarg ::= vtabarg vtabargtoken */ - { 266, 0 }, /* (363) anylist ::= */ - { 266, -4 }, /* (364) anylist ::= anylist LP anylist RP */ - { 266, -2 }, /* (365) anylist ::= anylist ANY */ - { 232, 0 }, /* (366) with ::= */ +/* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side +** of that rule */ +static const YYCODETYPE yyRuleInfoLhs[] = { + 180, /* (0) explain ::= EXPLAIN */ + 180, /* (1) explain ::= EXPLAIN QUERY PLAN */ + 179, /* (2) cmdx ::= cmd */ + 181, /* (3) cmd ::= BEGIN transtype trans_opt */ + 182, /* (4) transtype ::= */ + 182, /* (5) transtype ::= DEFERRED */ + 182, /* (6) transtype ::= IMMEDIATE */ + 182, /* (7) transtype ::= EXCLUSIVE */ + 181, /* (8) cmd ::= COMMIT|END trans_opt */ + 181, /* (9) cmd ::= ROLLBACK trans_opt */ + 181, /* (10) cmd ::= SAVEPOINT nm */ + 181, /* (11) cmd ::= RELEASE savepoint_opt nm */ + 181, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */ + 186, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */ + 188, /* (14) createkw ::= CREATE */ + 190, /* (15) ifnotexists ::= */ + 190, /* (16) ifnotexists ::= IF NOT EXISTS */ + 189, /* (17) temp ::= TEMP */ + 189, /* (18) temp ::= */ + 187, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_options */ + 187, /* (20) create_table_args ::= AS select */ + 194, /* (21) table_options ::= */ + 194, /* (22) table_options ::= WITHOUT nm */ + 196, /* (23) columnname ::= nm typetoken */ + 198, /* (24) typetoken ::= */ + 198, /* (25) typetoken ::= typename LP signed RP */ + 198, /* (26) typetoken ::= typename LP signed COMMA signed RP */ + 199, /* (27) typename ::= typename ID|STRING */ + 203, /* (28) scanpt ::= */ + 204, /* (29) ccons ::= CONSTRAINT nm */ + 204, /* (30) ccons ::= DEFAULT scanpt term scanpt */ + 204, /* (31) ccons ::= DEFAULT LP expr RP */ + 204, /* (32) ccons ::= DEFAULT PLUS term scanpt */ + 204, /* (33) ccons ::= DEFAULT MINUS term scanpt */ + 204, /* (34) ccons ::= DEFAULT scanpt ID|INDEXED */ + 204, /* (35) ccons ::= NOT NULL onconf */ + 204, /* (36) ccons ::= PRIMARY KEY sortorder onconf autoinc */ + 204, /* (37) ccons ::= UNIQUE onconf */ + 204, /* (38) ccons ::= CHECK LP expr RP */ + 204, /* (39) ccons ::= REFERENCES nm eidlist_opt refargs */ + 204, /* (40) ccons ::= defer_subclause */ + 204, /* (41) ccons ::= COLLATE ID|STRING */ + 209, /* (42) autoinc ::= */ + 209, /* (43) autoinc ::= AUTOINCR */ + 211, /* (44) refargs ::= */ + 211, /* (45) refargs ::= refargs refarg */ + 213, /* (46) refarg ::= MATCH nm */ + 213, /* (47) refarg ::= ON INSERT refact */ + 213, /* (48) refarg ::= ON DELETE refact */ + 213, /* (49) refarg ::= ON UPDATE refact */ + 214, /* (50) refact ::= SET NULL */ + 214, /* (51) refact ::= SET DEFAULT */ + 214, /* (52) refact ::= CASCADE */ + 214, /* (53) refact ::= RESTRICT */ + 214, /* (54) refact ::= NO ACTION */ + 212, /* (55) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ + 212, /* (56) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ + 215, /* (57) init_deferred_pred_opt ::= */ + 215, /* (58) init_deferred_pred_opt ::= INITIALLY DEFERRED */ + 215, /* (59) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ + 193, /* (60) conslist_opt ::= */ + 217, /* (61) tconscomma ::= COMMA */ + 218, /* (62) tcons ::= CONSTRAINT nm */ + 218, /* (63) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ + 218, /* (64) tcons ::= UNIQUE LP sortlist RP onconf */ + 218, /* (65) tcons ::= CHECK LP expr RP onconf */ + 218, /* (66) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ + 221, /* (67) defer_subclause_opt ::= */ + 207, /* (68) onconf ::= */ + 207, /* (69) onconf ::= ON CONFLICT resolvetype */ + 222, /* (70) orconf ::= */ + 222, /* (71) orconf ::= OR resolvetype */ + 223, /* (72) resolvetype ::= IGNORE */ + 223, /* (73) resolvetype ::= REPLACE */ + 181, /* (74) cmd ::= DROP TABLE ifexists fullname */ + 225, /* (75) ifexists ::= IF EXISTS */ + 225, /* (76) ifexists ::= */ + 181, /* (77) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ + 181, /* (78) cmd ::= DROP VIEW ifexists fullname */ + 181, /* (79) cmd ::= select */ + 195, /* (80) select ::= WITH wqlist selectnowith */ + 195, /* (81) select ::= WITH RECURSIVE wqlist selectnowith */ + 195, /* (82) select ::= selectnowith */ + 227, /* (83) selectnowith ::= selectnowith multiselect_op oneselect */ + 230, /* (84) multiselect_op ::= UNION */ + 230, /* (85) multiselect_op ::= UNION ALL */ + 230, /* (86) multiselect_op ::= EXCEPT|INTERSECT */ + 228, /* (87) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ + 228, /* (88) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ + 240, /* (89) values ::= VALUES LP nexprlist RP */ + 240, /* (90) values ::= values COMMA LP nexprlist RP */ + 231, /* (91) distinct ::= DISTINCT */ + 231, /* (92) distinct ::= ALL */ + 231, /* (93) distinct ::= */ + 242, /* (94) sclp ::= */ + 232, /* (95) selcollist ::= sclp scanpt expr scanpt as */ + 232, /* (96) selcollist ::= sclp scanpt STAR */ + 232, /* (97) selcollist ::= sclp scanpt nm DOT STAR */ + 243, /* (98) as ::= AS nm */ + 243, /* (99) as ::= */ + 233, /* (100) from ::= */ + 233, /* (101) from ::= FROM seltablist */ + 245, /* (102) stl_prefix ::= seltablist joinop */ + 245, /* (103) stl_prefix ::= */ + 244, /* (104) seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ + 244, /* (105) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */ + 244, /* (106) seltablist ::= stl_prefix LP select RP as on_opt using_opt */ + 244, /* (107) seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ + 191, /* (108) dbnm ::= */ + 191, /* (109) dbnm ::= DOT nm */ + 226, /* (110) fullname ::= nm */ + 226, /* (111) fullname ::= nm DOT nm */ + 251, /* (112) xfullname ::= nm */ + 251, /* (113) xfullname ::= nm DOT nm */ + 251, /* (114) xfullname ::= nm DOT nm AS nm */ + 251, /* (115) xfullname ::= nm AS nm */ + 246, /* (116) joinop ::= COMMA|JOIN */ + 246, /* (117) joinop ::= JOIN_KW JOIN */ + 246, /* (118) joinop ::= JOIN_KW nm JOIN */ + 246, /* (119) joinop ::= JOIN_KW nm nm JOIN */ + 248, /* (120) on_opt ::= ON expr */ + 248, /* (121) on_opt ::= */ + 247, /* (122) indexed_opt ::= */ + 247, /* (123) indexed_opt ::= INDEXED BY nm */ + 247, /* (124) indexed_opt ::= NOT INDEXED */ + 249, /* (125) using_opt ::= USING LP idlist RP */ + 249, /* (126) using_opt ::= */ + 237, /* (127) orderby_opt ::= */ + 237, /* (128) orderby_opt ::= ORDER BY sortlist */ + 219, /* (129) sortlist ::= sortlist COMMA expr sortorder */ + 219, /* (130) sortlist ::= expr sortorder */ + 208, /* (131) sortorder ::= ASC */ + 208, /* (132) sortorder ::= DESC */ + 208, /* (133) sortorder ::= */ + 235, /* (134) groupby_opt ::= */ + 235, /* (135) groupby_opt ::= GROUP BY nexprlist */ + 236, /* (136) having_opt ::= */ + 236, /* (137) having_opt ::= HAVING expr */ + 238, /* (138) limit_opt ::= */ + 238, /* (139) limit_opt ::= LIMIT expr */ + 238, /* (140) limit_opt ::= LIMIT expr OFFSET expr */ + 238, /* (141) limit_opt ::= LIMIT expr COMMA expr */ + 181, /* (142) cmd ::= with DELETE FROM xfullname indexed_opt where_opt */ + 234, /* (143) where_opt ::= */ + 234, /* (144) where_opt ::= WHERE expr */ + 181, /* (145) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt */ + 254, /* (146) setlist ::= setlist COMMA nm EQ expr */ + 254, /* (147) setlist ::= setlist COMMA LP idlist RP EQ expr */ + 254, /* (148) setlist ::= nm EQ expr */ + 254, /* (149) setlist ::= LP idlist RP EQ expr */ + 181, /* (150) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ + 181, /* (151) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */ + 257, /* (152) upsert ::= */ + 257, /* (153) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */ + 257, /* (154) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */ + 257, /* (155) upsert ::= ON CONFLICT DO NOTHING */ + 255, /* (156) insert_cmd ::= INSERT orconf */ + 255, /* (157) insert_cmd ::= REPLACE */ + 256, /* (158) idlist_opt ::= */ + 256, /* (159) idlist_opt ::= LP idlist RP */ + 252, /* (160) idlist ::= idlist COMMA nm */ + 252, /* (161) idlist ::= nm */ + 206, /* (162) expr ::= LP expr RP */ + 206, /* (163) expr ::= ID|INDEXED */ + 206, /* (164) expr ::= JOIN_KW */ + 206, /* (165) expr ::= nm DOT nm */ + 206, /* (166) expr ::= nm DOT nm DOT nm */ + 205, /* (167) term ::= NULL|FLOAT|BLOB */ + 205, /* (168) term ::= STRING */ + 205, /* (169) term ::= INTEGER */ + 206, /* (170) expr ::= VARIABLE */ + 206, /* (171) expr ::= expr COLLATE ID|STRING */ + 206, /* (172) expr ::= CAST LP expr AS typetoken RP */ + 206, /* (173) expr ::= ID|INDEXED LP distinct exprlist RP */ + 206, /* (174) expr ::= ID|INDEXED LP STAR RP */ + 206, /* (175) expr ::= ID|INDEXED LP distinct exprlist RP over_clause */ + 206, /* (176) expr ::= ID|INDEXED LP STAR RP over_clause */ + 205, /* (177) term ::= CTIME_KW */ + 206, /* (178) expr ::= LP nexprlist COMMA expr RP */ + 206, /* (179) expr ::= expr AND expr */ + 206, /* (180) expr ::= expr OR expr */ + 206, /* (181) expr ::= expr LT|GT|GE|LE expr */ + 206, /* (182) expr ::= expr EQ|NE expr */ + 206, /* (183) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ + 206, /* (184) expr ::= expr PLUS|MINUS expr */ + 206, /* (185) expr ::= expr STAR|SLASH|REM expr */ + 206, /* (186) expr ::= expr CONCAT expr */ + 259, /* (187) likeop ::= NOT LIKE_KW|MATCH */ + 206, /* (188) expr ::= expr likeop expr */ + 206, /* (189) expr ::= expr likeop expr ESCAPE expr */ + 206, /* (190) expr ::= expr ISNULL|NOTNULL */ + 206, /* (191) expr ::= expr NOT NULL */ + 206, /* (192) expr ::= expr IS expr */ + 206, /* (193) expr ::= expr IS NOT expr */ + 206, /* (194) expr ::= NOT expr */ + 206, /* (195) expr ::= BITNOT expr */ + 206, /* (196) expr ::= PLUS|MINUS expr */ + 260, /* (197) between_op ::= BETWEEN */ + 260, /* (198) between_op ::= NOT BETWEEN */ + 206, /* (199) expr ::= expr between_op expr AND expr */ + 261, /* (200) in_op ::= IN */ + 261, /* (201) in_op ::= NOT IN */ + 206, /* (202) expr ::= expr in_op LP exprlist RP */ + 206, /* (203) expr ::= LP select RP */ + 206, /* (204) expr ::= expr in_op LP select RP */ + 206, /* (205) expr ::= expr in_op nm dbnm paren_exprlist */ + 206, /* (206) expr ::= EXISTS LP select RP */ + 206, /* (207) expr ::= CASE case_operand case_exprlist case_else END */ + 264, /* (208) case_exprlist ::= case_exprlist WHEN expr THEN expr */ + 264, /* (209) case_exprlist ::= WHEN expr THEN expr */ + 265, /* (210) case_else ::= ELSE expr */ + 265, /* (211) case_else ::= */ + 263, /* (212) case_operand ::= expr */ + 263, /* (213) case_operand ::= */ + 250, /* (214) exprlist ::= */ + 241, /* (215) nexprlist ::= nexprlist COMMA expr */ + 241, /* (216) nexprlist ::= expr */ + 262, /* (217) paren_exprlist ::= */ + 262, /* (218) paren_exprlist ::= LP exprlist RP */ + 181, /* (219) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ + 266, /* (220) uniqueflag ::= UNIQUE */ + 266, /* (221) uniqueflag ::= */ + 210, /* (222) eidlist_opt ::= */ + 210, /* (223) eidlist_opt ::= LP eidlist RP */ + 220, /* (224) eidlist ::= eidlist COMMA nm collate sortorder */ + 220, /* (225) eidlist ::= nm collate sortorder */ + 267, /* (226) collate ::= */ + 267, /* (227) collate ::= COLLATE ID|STRING */ + 181, /* (228) cmd ::= DROP INDEX ifexists fullname */ + 181, /* (229) cmd ::= VACUUM vinto */ + 181, /* (230) cmd ::= VACUUM nm vinto */ + 268, /* (231) vinto ::= INTO expr */ + 268, /* (232) vinto ::= */ + 181, /* (233) cmd ::= PRAGMA nm dbnm */ + 181, /* (234) cmd ::= PRAGMA nm dbnm EQ nmnum */ + 181, /* (235) cmd ::= PRAGMA nm dbnm LP nmnum RP */ + 181, /* (236) cmd ::= PRAGMA nm dbnm EQ minus_num */ + 181, /* (237) cmd ::= PRAGMA nm dbnm LP minus_num RP */ + 201, /* (238) plus_num ::= PLUS INTEGER|FLOAT */ + 202, /* (239) minus_num ::= MINUS INTEGER|FLOAT */ + 181, /* (240) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ + 270, /* (241) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + 272, /* (242) trigger_time ::= BEFORE|AFTER */ + 272, /* (243) trigger_time ::= INSTEAD OF */ + 272, /* (244) trigger_time ::= */ + 273, /* (245) trigger_event ::= DELETE|INSERT */ + 273, /* (246) trigger_event ::= UPDATE */ + 273, /* (247) trigger_event ::= UPDATE OF idlist */ + 275, /* (248) when_clause ::= */ + 275, /* (249) when_clause ::= WHEN expr */ + 271, /* (250) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + 271, /* (251) trigger_cmd_list ::= trigger_cmd SEMI */ + 277, /* (252) trnm ::= nm DOT nm */ + 278, /* (253) tridxby ::= INDEXED BY nm */ + 278, /* (254) tridxby ::= NOT INDEXED */ + 276, /* (255) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */ + 276, /* (256) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ + 276, /* (257) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ + 276, /* (258) trigger_cmd ::= scanpt select scanpt */ + 206, /* (259) expr ::= RAISE LP IGNORE RP */ + 206, /* (260) expr ::= RAISE LP raisetype COMMA nm RP */ + 224, /* (261) raisetype ::= ROLLBACK */ + 224, /* (262) raisetype ::= ABORT */ + 224, /* (263) raisetype ::= FAIL */ + 181, /* (264) cmd ::= DROP TRIGGER ifexists fullname */ + 181, /* (265) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + 181, /* (266) cmd ::= DETACH database_kw_opt expr */ + 280, /* (267) key_opt ::= */ + 280, /* (268) key_opt ::= KEY expr */ + 181, /* (269) cmd ::= REINDEX */ + 181, /* (270) cmd ::= REINDEX nm dbnm */ + 181, /* (271) cmd ::= ANALYZE */ + 181, /* (272) cmd ::= ANALYZE nm dbnm */ + 181, /* (273) cmd ::= ALTER TABLE fullname RENAME TO nm */ + 181, /* (274) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + 281, /* (275) add_column_fullname ::= fullname */ + 181, /* (276) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ + 181, /* (277) cmd ::= create_vtab */ + 181, /* (278) cmd ::= create_vtab LP vtabarglist RP */ + 283, /* (279) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + 285, /* (280) vtabarg ::= */ + 286, /* (281) vtabargtoken ::= ANY */ + 286, /* (282) vtabargtoken ::= lp anylist RP */ + 287, /* (283) lp ::= LP */ + 253, /* (284) with ::= WITH wqlist */ + 253, /* (285) with ::= WITH RECURSIVE wqlist */ + 229, /* (286) wqlist ::= nm eidlist_opt AS LP select RP */ + 229, /* (287) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */ + 289, /* (288) windowdefn_list ::= windowdefn */ + 289, /* (289) windowdefn_list ::= windowdefn_list COMMA windowdefn */ + 290, /* (290) windowdefn ::= nm AS LP window RP */ + 291, /* (291) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + 291, /* (292) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + 291, /* (293) window ::= ORDER BY sortlist frame_opt */ + 291, /* (294) window ::= nm ORDER BY sortlist frame_opt */ + 291, /* (295) window ::= frame_opt */ + 291, /* (296) window ::= nm frame_opt */ + 292, /* (297) frame_opt ::= */ + 292, /* (298) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ + 292, /* (299) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ + 295, /* (300) range_or_rows ::= RANGE|ROWS|GROUPS */ + 297, /* (301) frame_bound_s ::= frame_bound */ + 297, /* (302) frame_bound_s ::= UNBOUNDED PRECEDING */ + 298, /* (303) frame_bound_e ::= frame_bound */ + 298, /* (304) frame_bound_e ::= UNBOUNDED FOLLOWING */ + 296, /* (305) frame_bound ::= expr PRECEDING|FOLLOWING */ + 296, /* (306) frame_bound ::= CURRENT ROW */ + 299, /* (307) frame_exclude_opt ::= */ + 299, /* (308) frame_exclude_opt ::= EXCLUDE frame_exclude */ + 300, /* (309) frame_exclude ::= NO OTHERS */ + 300, /* (310) frame_exclude ::= CURRENT ROW */ + 300, /* (311) frame_exclude ::= GROUP|TIES */ + 239, /* (312) window_clause ::= WINDOW windowdefn_list */ + 258, /* (313) over_clause ::= filter_opt OVER LP window RP */ + 258, /* (314) over_clause ::= filter_opt OVER nm */ + 294, /* (315) filter_opt ::= */ + 294, /* (316) filter_opt ::= FILTER LP WHERE expr RP */ + 176, /* (317) input ::= cmdlist */ + 177, /* (318) cmdlist ::= cmdlist ecmd */ + 177, /* (319) cmdlist ::= ecmd */ + 178, /* (320) ecmd ::= SEMI */ + 178, /* (321) ecmd ::= cmdx SEMI */ + 178, /* (322) ecmd ::= explain cmdx */ + 183, /* (323) trans_opt ::= */ + 183, /* (324) trans_opt ::= TRANSACTION */ + 183, /* (325) trans_opt ::= TRANSACTION nm */ + 185, /* (326) savepoint_opt ::= SAVEPOINT */ + 185, /* (327) savepoint_opt ::= */ + 181, /* (328) cmd ::= create_table create_table_args */ + 192, /* (329) columnlist ::= columnlist COMMA columnname carglist */ + 192, /* (330) columnlist ::= columnname carglist */ + 184, /* (331) nm ::= ID|INDEXED */ + 184, /* (332) nm ::= STRING */ + 184, /* (333) nm ::= JOIN_KW */ + 198, /* (334) typetoken ::= typename */ + 199, /* (335) typename ::= ID|STRING */ + 200, /* (336) signed ::= plus_num */ + 200, /* (337) signed ::= minus_num */ + 197, /* (338) carglist ::= carglist ccons */ + 197, /* (339) carglist ::= */ + 204, /* (340) ccons ::= NULL onconf */ + 193, /* (341) conslist_opt ::= COMMA conslist */ + 216, /* (342) conslist ::= conslist tconscomma tcons */ + 216, /* (343) conslist ::= tcons */ + 217, /* (344) tconscomma ::= */ + 221, /* (345) defer_subclause_opt ::= defer_subclause */ + 223, /* (346) resolvetype ::= raisetype */ + 227, /* (347) selectnowith ::= oneselect */ + 228, /* (348) oneselect ::= values */ + 242, /* (349) sclp ::= selcollist COMMA */ + 243, /* (350) as ::= ID|STRING */ + 206, /* (351) expr ::= term */ + 259, /* (352) likeop ::= LIKE_KW|MATCH */ + 250, /* (353) exprlist ::= nexprlist */ + 269, /* (354) nmnum ::= plus_num */ + 269, /* (355) nmnum ::= nm */ + 269, /* (356) nmnum ::= ON */ + 269, /* (357) nmnum ::= DELETE */ + 269, /* (358) nmnum ::= DEFAULT */ + 201, /* (359) plus_num ::= INTEGER|FLOAT */ + 274, /* (360) foreach_clause ::= */ + 274, /* (361) foreach_clause ::= FOR EACH ROW */ + 277, /* (362) trnm ::= nm */ + 278, /* (363) tridxby ::= */ + 279, /* (364) database_kw_opt ::= DATABASE */ + 279, /* (365) database_kw_opt ::= */ + 282, /* (366) kwcolumn_opt ::= */ + 282, /* (367) kwcolumn_opt ::= COLUMNKW */ + 284, /* (368) vtabarglist ::= vtabarg */ + 284, /* (369) vtabarglist ::= vtabarglist COMMA vtabarg */ + 285, /* (370) vtabarg ::= vtabarg vtabargtoken */ + 288, /* (371) anylist ::= */ + 288, /* (372) anylist ::= anylist LP anylist RP */ + 288, /* (373) anylist ::= anylist ANY */ + 253, /* (374) with ::= */ +}; + +/* For rule J, yyRuleInfoNRhs[J] contains the negative of the number +** of symbols on the right-hand side of that rule. */ +static const signed char yyRuleInfoNRhs[] = { + -1, /* (0) explain ::= EXPLAIN */ + -3, /* (1) explain ::= EXPLAIN QUERY PLAN */ + -1, /* (2) cmdx ::= cmd */ + -3, /* (3) cmd ::= BEGIN transtype trans_opt */ + 0, /* (4) transtype ::= */ + -1, /* (5) transtype ::= DEFERRED */ + -1, /* (6) transtype ::= IMMEDIATE */ + -1, /* (7) transtype ::= EXCLUSIVE */ + -2, /* (8) cmd ::= COMMIT|END trans_opt */ + -2, /* (9) cmd ::= ROLLBACK trans_opt */ + -2, /* (10) cmd ::= SAVEPOINT nm */ + -3, /* (11) cmd ::= RELEASE savepoint_opt nm */ + -5, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */ + -6, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */ + -1, /* (14) createkw ::= CREATE */ + 0, /* (15) ifnotexists ::= */ + -3, /* (16) ifnotexists ::= IF NOT EXISTS */ + -1, /* (17) temp ::= TEMP */ + 0, /* (18) temp ::= */ + -5, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_options */ + -2, /* (20) create_table_args ::= AS select */ + 0, /* (21) table_options ::= */ + -2, /* (22) table_options ::= WITHOUT nm */ + -2, /* (23) columnname ::= nm typetoken */ + 0, /* (24) typetoken ::= */ + -4, /* (25) typetoken ::= typename LP signed RP */ + -6, /* (26) typetoken ::= typename LP signed COMMA signed RP */ + -2, /* (27) typename ::= typename ID|STRING */ + 0, /* (28) scanpt ::= */ + -2, /* (29) ccons ::= CONSTRAINT nm */ + -4, /* (30) ccons ::= DEFAULT scanpt term scanpt */ + -4, /* (31) ccons ::= DEFAULT LP expr RP */ + -4, /* (32) ccons ::= DEFAULT PLUS term scanpt */ + -4, /* (33) ccons ::= DEFAULT MINUS term scanpt */ + -3, /* (34) ccons ::= DEFAULT scanpt ID|INDEXED */ + -3, /* (35) ccons ::= NOT NULL onconf */ + -5, /* (36) ccons ::= PRIMARY KEY sortorder onconf autoinc */ + -2, /* (37) ccons ::= UNIQUE onconf */ + -4, /* (38) ccons ::= CHECK LP expr RP */ + -4, /* (39) ccons ::= REFERENCES nm eidlist_opt refargs */ + -1, /* (40) ccons ::= defer_subclause */ + -2, /* (41) ccons ::= COLLATE ID|STRING */ + 0, /* (42) autoinc ::= */ + -1, /* (43) autoinc ::= AUTOINCR */ + 0, /* (44) refargs ::= */ + -2, /* (45) refargs ::= refargs refarg */ + -2, /* (46) refarg ::= MATCH nm */ + -3, /* (47) refarg ::= ON INSERT refact */ + -3, /* (48) refarg ::= ON DELETE refact */ + -3, /* (49) refarg ::= ON UPDATE refact */ + -2, /* (50) refact ::= SET NULL */ + -2, /* (51) refact ::= SET DEFAULT */ + -1, /* (52) refact ::= CASCADE */ + -1, /* (53) refact ::= RESTRICT */ + -2, /* (54) refact ::= NO ACTION */ + -3, /* (55) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ + -2, /* (56) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ + 0, /* (57) init_deferred_pred_opt ::= */ + -2, /* (58) init_deferred_pred_opt ::= INITIALLY DEFERRED */ + -2, /* (59) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ + 0, /* (60) conslist_opt ::= */ + -1, /* (61) tconscomma ::= COMMA */ + -2, /* (62) tcons ::= CONSTRAINT nm */ + -7, /* (63) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ + -5, /* (64) tcons ::= UNIQUE LP sortlist RP onconf */ + -5, /* (65) tcons ::= CHECK LP expr RP onconf */ + -10, /* (66) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ + 0, /* (67) defer_subclause_opt ::= */ + 0, /* (68) onconf ::= */ + -3, /* (69) onconf ::= ON CONFLICT resolvetype */ + 0, /* (70) orconf ::= */ + -2, /* (71) orconf ::= OR resolvetype */ + -1, /* (72) resolvetype ::= IGNORE */ + -1, /* (73) resolvetype ::= REPLACE */ + -4, /* (74) cmd ::= DROP TABLE ifexists fullname */ + -2, /* (75) ifexists ::= IF EXISTS */ + 0, /* (76) ifexists ::= */ + -9, /* (77) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ + -4, /* (78) cmd ::= DROP VIEW ifexists fullname */ + -1, /* (79) cmd ::= select */ + -3, /* (80) select ::= WITH wqlist selectnowith */ + -4, /* (81) select ::= WITH RECURSIVE wqlist selectnowith */ + -1, /* (82) select ::= selectnowith */ + -3, /* (83) selectnowith ::= selectnowith multiselect_op oneselect */ + -1, /* (84) multiselect_op ::= UNION */ + -2, /* (85) multiselect_op ::= UNION ALL */ + -1, /* (86) multiselect_op ::= EXCEPT|INTERSECT */ + -9, /* (87) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ + -10, /* (88) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ + -4, /* (89) values ::= VALUES LP nexprlist RP */ + -5, /* (90) values ::= values COMMA LP nexprlist RP */ + -1, /* (91) distinct ::= DISTINCT */ + -1, /* (92) distinct ::= ALL */ + 0, /* (93) distinct ::= */ + 0, /* (94) sclp ::= */ + -5, /* (95) selcollist ::= sclp scanpt expr scanpt as */ + -3, /* (96) selcollist ::= sclp scanpt STAR */ + -5, /* (97) selcollist ::= sclp scanpt nm DOT STAR */ + -2, /* (98) as ::= AS nm */ + 0, /* (99) as ::= */ + 0, /* (100) from ::= */ + -2, /* (101) from ::= FROM seltablist */ + -2, /* (102) stl_prefix ::= seltablist joinop */ + 0, /* (103) stl_prefix ::= */ + -7, /* (104) seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ + -9, /* (105) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */ + -7, /* (106) seltablist ::= stl_prefix LP select RP as on_opt using_opt */ + -7, /* (107) seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ + 0, /* (108) dbnm ::= */ + -2, /* (109) dbnm ::= DOT nm */ + -1, /* (110) fullname ::= nm */ + -3, /* (111) fullname ::= nm DOT nm */ + -1, /* (112) xfullname ::= nm */ + -3, /* (113) xfullname ::= nm DOT nm */ + -5, /* (114) xfullname ::= nm DOT nm AS nm */ + -3, /* (115) xfullname ::= nm AS nm */ + -1, /* (116) joinop ::= COMMA|JOIN */ + -2, /* (117) joinop ::= JOIN_KW JOIN */ + -3, /* (118) joinop ::= JOIN_KW nm JOIN */ + -4, /* (119) joinop ::= JOIN_KW nm nm JOIN */ + -2, /* (120) on_opt ::= ON expr */ + 0, /* (121) on_opt ::= */ + 0, /* (122) indexed_opt ::= */ + -3, /* (123) indexed_opt ::= INDEXED BY nm */ + -2, /* (124) indexed_opt ::= NOT INDEXED */ + -4, /* (125) using_opt ::= USING LP idlist RP */ + 0, /* (126) using_opt ::= */ + 0, /* (127) orderby_opt ::= */ + -3, /* (128) orderby_opt ::= ORDER BY sortlist */ + -4, /* (129) sortlist ::= sortlist COMMA expr sortorder */ + -2, /* (130) sortlist ::= expr sortorder */ + -1, /* (131) sortorder ::= ASC */ + -1, /* (132) sortorder ::= DESC */ + 0, /* (133) sortorder ::= */ + 0, /* (134) groupby_opt ::= */ + -3, /* (135) groupby_opt ::= GROUP BY nexprlist */ + 0, /* (136) having_opt ::= */ + -2, /* (137) having_opt ::= HAVING expr */ + 0, /* (138) limit_opt ::= */ + -2, /* (139) limit_opt ::= LIMIT expr */ + -4, /* (140) limit_opt ::= LIMIT expr OFFSET expr */ + -4, /* (141) limit_opt ::= LIMIT expr COMMA expr */ + -6, /* (142) cmd ::= with DELETE FROM xfullname indexed_opt where_opt */ + 0, /* (143) where_opt ::= */ + -2, /* (144) where_opt ::= WHERE expr */ + -8, /* (145) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt */ + -5, /* (146) setlist ::= setlist COMMA nm EQ expr */ + -7, /* (147) setlist ::= setlist COMMA LP idlist RP EQ expr */ + -3, /* (148) setlist ::= nm EQ expr */ + -5, /* (149) setlist ::= LP idlist RP EQ expr */ + -7, /* (150) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ + -7, /* (151) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */ + 0, /* (152) upsert ::= */ + -11, /* (153) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */ + -8, /* (154) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */ + -4, /* (155) upsert ::= ON CONFLICT DO NOTHING */ + -2, /* (156) insert_cmd ::= INSERT orconf */ + -1, /* (157) insert_cmd ::= REPLACE */ + 0, /* (158) idlist_opt ::= */ + -3, /* (159) idlist_opt ::= LP idlist RP */ + -3, /* (160) idlist ::= idlist COMMA nm */ + -1, /* (161) idlist ::= nm */ + -3, /* (162) expr ::= LP expr RP */ + -1, /* (163) expr ::= ID|INDEXED */ + -1, /* (164) expr ::= JOIN_KW */ + -3, /* (165) expr ::= nm DOT nm */ + -5, /* (166) expr ::= nm DOT nm DOT nm */ + -1, /* (167) term ::= NULL|FLOAT|BLOB */ + -1, /* (168) term ::= STRING */ + -1, /* (169) term ::= INTEGER */ + -1, /* (170) expr ::= VARIABLE */ + -3, /* (171) expr ::= expr COLLATE ID|STRING */ + -6, /* (172) expr ::= CAST LP expr AS typetoken RP */ + -5, /* (173) expr ::= ID|INDEXED LP distinct exprlist RP */ + -4, /* (174) expr ::= ID|INDEXED LP STAR RP */ + -6, /* (175) expr ::= ID|INDEXED LP distinct exprlist RP over_clause */ + -5, /* (176) expr ::= ID|INDEXED LP STAR RP over_clause */ + -1, /* (177) term ::= CTIME_KW */ + -5, /* (178) expr ::= LP nexprlist COMMA expr RP */ + -3, /* (179) expr ::= expr AND expr */ + -3, /* (180) expr ::= expr OR expr */ + -3, /* (181) expr ::= expr LT|GT|GE|LE expr */ + -3, /* (182) expr ::= expr EQ|NE expr */ + -3, /* (183) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ + -3, /* (184) expr ::= expr PLUS|MINUS expr */ + -3, /* (185) expr ::= expr STAR|SLASH|REM expr */ + -3, /* (186) expr ::= expr CONCAT expr */ + -2, /* (187) likeop ::= NOT LIKE_KW|MATCH */ + -3, /* (188) expr ::= expr likeop expr */ + -5, /* (189) expr ::= expr likeop expr ESCAPE expr */ + -2, /* (190) expr ::= expr ISNULL|NOTNULL */ + -3, /* (191) expr ::= expr NOT NULL */ + -3, /* (192) expr ::= expr IS expr */ + -4, /* (193) expr ::= expr IS NOT expr */ + -2, /* (194) expr ::= NOT expr */ + -2, /* (195) expr ::= BITNOT expr */ + -2, /* (196) expr ::= PLUS|MINUS expr */ + -1, /* (197) between_op ::= BETWEEN */ + -2, /* (198) between_op ::= NOT BETWEEN */ + -5, /* (199) expr ::= expr between_op expr AND expr */ + -1, /* (200) in_op ::= IN */ + -2, /* (201) in_op ::= NOT IN */ + -5, /* (202) expr ::= expr in_op LP exprlist RP */ + -3, /* (203) expr ::= LP select RP */ + -5, /* (204) expr ::= expr in_op LP select RP */ + -5, /* (205) expr ::= expr in_op nm dbnm paren_exprlist */ + -4, /* (206) expr ::= EXISTS LP select RP */ + -5, /* (207) expr ::= CASE case_operand case_exprlist case_else END */ + -5, /* (208) case_exprlist ::= case_exprlist WHEN expr THEN expr */ + -4, /* (209) case_exprlist ::= WHEN expr THEN expr */ + -2, /* (210) case_else ::= ELSE expr */ + 0, /* (211) case_else ::= */ + -1, /* (212) case_operand ::= expr */ + 0, /* (213) case_operand ::= */ + 0, /* (214) exprlist ::= */ + -3, /* (215) nexprlist ::= nexprlist COMMA expr */ + -1, /* (216) nexprlist ::= expr */ + 0, /* (217) paren_exprlist ::= */ + -3, /* (218) paren_exprlist ::= LP exprlist RP */ + -12, /* (219) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ + -1, /* (220) uniqueflag ::= UNIQUE */ + 0, /* (221) uniqueflag ::= */ + 0, /* (222) eidlist_opt ::= */ + -3, /* (223) eidlist_opt ::= LP eidlist RP */ + -5, /* (224) eidlist ::= eidlist COMMA nm collate sortorder */ + -3, /* (225) eidlist ::= nm collate sortorder */ + 0, /* (226) collate ::= */ + -2, /* (227) collate ::= COLLATE ID|STRING */ + -4, /* (228) cmd ::= DROP INDEX ifexists fullname */ + -2, /* (229) cmd ::= VACUUM vinto */ + -3, /* (230) cmd ::= VACUUM nm vinto */ + -2, /* (231) vinto ::= INTO expr */ + 0, /* (232) vinto ::= */ + -3, /* (233) cmd ::= PRAGMA nm dbnm */ + -5, /* (234) cmd ::= PRAGMA nm dbnm EQ nmnum */ + -6, /* (235) cmd ::= PRAGMA nm dbnm LP nmnum RP */ + -5, /* (236) cmd ::= PRAGMA nm dbnm EQ minus_num */ + -6, /* (237) cmd ::= PRAGMA nm dbnm LP minus_num RP */ + -2, /* (238) plus_num ::= PLUS INTEGER|FLOAT */ + -2, /* (239) minus_num ::= MINUS INTEGER|FLOAT */ + -5, /* (240) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ + -11, /* (241) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + -1, /* (242) trigger_time ::= BEFORE|AFTER */ + -2, /* (243) trigger_time ::= INSTEAD OF */ + 0, /* (244) trigger_time ::= */ + -1, /* (245) trigger_event ::= DELETE|INSERT */ + -1, /* (246) trigger_event ::= UPDATE */ + -3, /* (247) trigger_event ::= UPDATE OF idlist */ + 0, /* (248) when_clause ::= */ + -2, /* (249) when_clause ::= WHEN expr */ + -3, /* (250) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + -2, /* (251) trigger_cmd_list ::= trigger_cmd SEMI */ + -3, /* (252) trnm ::= nm DOT nm */ + -3, /* (253) tridxby ::= INDEXED BY nm */ + -2, /* (254) tridxby ::= NOT INDEXED */ + -8, /* (255) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */ + -8, /* (256) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ + -6, /* (257) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ + -3, /* (258) trigger_cmd ::= scanpt select scanpt */ + -4, /* (259) expr ::= RAISE LP IGNORE RP */ + -6, /* (260) expr ::= RAISE LP raisetype COMMA nm RP */ + -1, /* (261) raisetype ::= ROLLBACK */ + -1, /* (262) raisetype ::= ABORT */ + -1, /* (263) raisetype ::= FAIL */ + -4, /* (264) cmd ::= DROP TRIGGER ifexists fullname */ + -6, /* (265) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + -3, /* (266) cmd ::= DETACH database_kw_opt expr */ + 0, /* (267) key_opt ::= */ + -2, /* (268) key_opt ::= KEY expr */ + -1, /* (269) cmd ::= REINDEX */ + -3, /* (270) cmd ::= REINDEX nm dbnm */ + -1, /* (271) cmd ::= ANALYZE */ + -3, /* (272) cmd ::= ANALYZE nm dbnm */ + -6, /* (273) cmd ::= ALTER TABLE fullname RENAME TO nm */ + -7, /* (274) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + -1, /* (275) add_column_fullname ::= fullname */ + -8, /* (276) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ + -1, /* (277) cmd ::= create_vtab */ + -4, /* (278) cmd ::= create_vtab LP vtabarglist RP */ + -8, /* (279) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + 0, /* (280) vtabarg ::= */ + -1, /* (281) vtabargtoken ::= ANY */ + -3, /* (282) vtabargtoken ::= lp anylist RP */ + -1, /* (283) lp ::= LP */ + -2, /* (284) with ::= WITH wqlist */ + -3, /* (285) with ::= WITH RECURSIVE wqlist */ + -6, /* (286) wqlist ::= nm eidlist_opt AS LP select RP */ + -8, /* (287) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */ + -1, /* (288) windowdefn_list ::= windowdefn */ + -3, /* (289) windowdefn_list ::= windowdefn_list COMMA windowdefn */ + -5, /* (290) windowdefn ::= nm AS LP window RP */ + -5, /* (291) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + -6, /* (292) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + -4, /* (293) window ::= ORDER BY sortlist frame_opt */ + -5, /* (294) window ::= nm ORDER BY sortlist frame_opt */ + -1, /* (295) window ::= frame_opt */ + -2, /* (296) window ::= nm frame_opt */ + 0, /* (297) frame_opt ::= */ + -3, /* (298) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ + -6, /* (299) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ + -1, /* (300) range_or_rows ::= RANGE|ROWS|GROUPS */ + -1, /* (301) frame_bound_s ::= frame_bound */ + -2, /* (302) frame_bound_s ::= UNBOUNDED PRECEDING */ + -1, /* (303) frame_bound_e ::= frame_bound */ + -2, /* (304) frame_bound_e ::= UNBOUNDED FOLLOWING */ + -2, /* (305) frame_bound ::= expr PRECEDING|FOLLOWING */ + -2, /* (306) frame_bound ::= CURRENT ROW */ + 0, /* (307) frame_exclude_opt ::= */ + -2, /* (308) frame_exclude_opt ::= EXCLUDE frame_exclude */ + -2, /* (309) frame_exclude ::= NO OTHERS */ + -2, /* (310) frame_exclude ::= CURRENT ROW */ + -1, /* (311) frame_exclude ::= GROUP|TIES */ + -2, /* (312) window_clause ::= WINDOW windowdefn_list */ + -5, /* (313) over_clause ::= filter_opt OVER LP window RP */ + -3, /* (314) over_clause ::= filter_opt OVER nm */ + 0, /* (315) filter_opt ::= */ + -5, /* (316) filter_opt ::= FILTER LP WHERE expr RP */ + -1, /* (317) input ::= cmdlist */ + -2, /* (318) cmdlist ::= cmdlist ecmd */ + -1, /* (319) cmdlist ::= ecmd */ + -1, /* (320) ecmd ::= SEMI */ + -2, /* (321) ecmd ::= cmdx SEMI */ + -2, /* (322) ecmd ::= explain cmdx */ + 0, /* (323) trans_opt ::= */ + -1, /* (324) trans_opt ::= TRANSACTION */ + -2, /* (325) trans_opt ::= TRANSACTION nm */ + -1, /* (326) savepoint_opt ::= SAVEPOINT */ + 0, /* (327) savepoint_opt ::= */ + -2, /* (328) cmd ::= create_table create_table_args */ + -4, /* (329) columnlist ::= columnlist COMMA columnname carglist */ + -2, /* (330) columnlist ::= columnname carglist */ + -1, /* (331) nm ::= ID|INDEXED */ + -1, /* (332) nm ::= STRING */ + -1, /* (333) nm ::= JOIN_KW */ + -1, /* (334) typetoken ::= typename */ + -1, /* (335) typename ::= ID|STRING */ + -1, /* (336) signed ::= plus_num */ + -1, /* (337) signed ::= minus_num */ + -2, /* (338) carglist ::= carglist ccons */ + 0, /* (339) carglist ::= */ + -2, /* (340) ccons ::= NULL onconf */ + -2, /* (341) conslist_opt ::= COMMA conslist */ + -3, /* (342) conslist ::= conslist tconscomma tcons */ + -1, /* (343) conslist ::= tcons */ + 0, /* (344) tconscomma ::= */ + -1, /* (345) defer_subclause_opt ::= defer_subclause */ + -1, /* (346) resolvetype ::= raisetype */ + -1, /* (347) selectnowith ::= oneselect */ + -1, /* (348) oneselect ::= values */ + -2, /* (349) sclp ::= selcollist COMMA */ + -1, /* (350) as ::= ID|STRING */ + -1, /* (351) expr ::= term */ + -1, /* (352) likeop ::= LIKE_KW|MATCH */ + -1, /* (353) exprlist ::= nexprlist */ + -1, /* (354) nmnum ::= plus_num */ + -1, /* (355) nmnum ::= nm */ + -1, /* (356) nmnum ::= ON */ + -1, /* (357) nmnum ::= DELETE */ + -1, /* (358) nmnum ::= DEFAULT */ + -1, /* (359) plus_num ::= INTEGER|FLOAT */ + 0, /* (360) foreach_clause ::= */ + -3, /* (361) foreach_clause ::= FOR EACH ROW */ + -1, /* (362) trnm ::= nm */ + 0, /* (363) tridxby ::= */ + -1, /* (364) database_kw_opt ::= DATABASE */ + 0, /* (365) database_kw_opt ::= */ + 0, /* (366) kwcolumn_opt ::= */ + -1, /* (367) kwcolumn_opt ::= COLUMNKW */ + -1, /* (368) vtabarglist ::= vtabarg */ + -3, /* (369) vtabarglist ::= vtabarglist COMMA vtabarg */ + -2, /* (370) vtabarg ::= vtabarg vtabargtoken */ + 0, /* (371) anylist ::= */ + -4, /* (372) anylist ::= anylist LP anylist RP */ + -2, /* (373) anylist ::= anylist ANY */ + 0, /* (374) with ::= */ }; static void yy_accept(yyParser*); /* Forward Declaration */ @@ -149483,7 +151256,7 @@ static YYACTIONTYPE yy_reduce( yymsp = yypParser->yytos; #ifndef NDEBUG if( yyTraceFILE && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){ - yysize = yyRuleInfo[yyruleno].nrhs; + yysize = yyRuleInfoNRhs[yyruleno]; if( yysize ){ fprintf(yyTraceFILE, "%sReduce %d [%s], go to state %d.\n", yyTracePrompt, @@ -149498,7 +151271,7 @@ static YYACTIONTYPE yy_reduce( /* Check that the stack is large enough to grow by a single entry ** if the RHS of the rule is empty. This ensures that there is room ** enough on the stack to push the LHS value */ - if( yyRuleInfo[yyruleno].nrhs==0 ){ + if( yyRuleInfoNRhs[yyruleno]==0 ){ #ifdef YYTRACKMAXSTACKDEPTH if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){ yypParser->yyhwm++; @@ -149548,15 +151321,16 @@ static YYACTIONTYPE yy_reduce( { sqlite3FinishCoding(pParse); } break; case 3: /* cmd ::= BEGIN transtype trans_opt */ -{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy70);} +{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy494);} break; case 4: /* transtype ::= */ -{yymsp[1].minor.yy70 = TK_DEFERRED;} +{yymsp[1].minor.yy494 = TK_DEFERRED;} break; case 5: /* transtype ::= DEFERRED */ case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6); case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7); -{yymsp[0].minor.yy70 = yymsp[0].major; /*A-overwrites-X*/} + case 300: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==300); +{yymsp[0].minor.yy494 = yymsp[0].major; /*A-overwrites-X*/} break; case 8: /* cmd ::= COMMIT|END trans_opt */ case 9: /* cmd ::= ROLLBACK trans_opt */ yytestcase(yyruleno==9); @@ -149579,7 +151353,7 @@ static YYACTIONTYPE yy_reduce( break; case 13: /* create_table ::= createkw temp TABLE ifnotexists nm dbnm */ { - sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy70,0,0,yymsp[-2].minor.yy70); + sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy494,0,0,yymsp[-2].minor.yy494); } break; case 14: /* createkw ::= CREATE */ @@ -149594,32 +151368,32 @@ static YYACTIONTYPE yy_reduce( case 76: /* ifexists ::= */ yytestcase(yyruleno==76); case 93: /* distinct ::= */ yytestcase(yyruleno==93); case 226: /* collate ::= */ yytestcase(yyruleno==226); -{yymsp[1].minor.yy70 = 0;} +{yymsp[1].minor.yy494 = 0;} break; case 16: /* ifnotexists ::= IF NOT EXISTS */ -{yymsp[-2].minor.yy70 = 1;} +{yymsp[-2].minor.yy494 = 1;} break; case 17: /* temp ::= TEMP */ case 43: /* autoinc ::= AUTOINCR */ yytestcase(yyruleno==43); -{yymsp[0].minor.yy70 = 1;} +{yymsp[0].minor.yy494 = 1;} break; case 19: /* create_table_args ::= LP columnlist conslist_opt RP table_options */ { - sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy70,0); + sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy494,0); } break; case 20: /* create_table_args ::= AS select */ { - sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy489); - sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy489); + sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy457); + sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy457); } break; case 22: /* table_options ::= WITHOUT nm */ { if( yymsp[0].minor.yy0.n==5 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"rowid",5)==0 ){ - yymsp[-1].minor.yy70 = TF_WithoutRowid | TF_NoVisibleRowid; + yymsp[-1].minor.yy494 = TF_WithoutRowid | TF_NoVisibleRowid; }else{ - yymsp[-1].minor.yy70 = 0; + yymsp[-1].minor.yy494 = 0; sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z); } } @@ -149648,7 +151422,7 @@ static YYACTIONTYPE yy_reduce( case 28: /* scanpt ::= */ { assert( yyLookahead!=YYNOCODE ); - yymsp[1].minor.yy392 = yyLookaheadToken.z; + yymsp[1].minor.yy294 = yyLookaheadToken.z; } break; case 29: /* ccons ::= CONSTRAINT nm */ @@ -149656,18 +151430,18 @@ static YYACTIONTYPE yy_reduce( {pParse->constraintName = yymsp[0].minor.yy0;} break; case 30: /* ccons ::= DEFAULT scanpt term scanpt */ -{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy18,yymsp[-2].minor.yy392,yymsp[0].minor.yy392);} +{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy524,yymsp[-2].minor.yy294,yymsp[0].minor.yy294);} break; case 31: /* ccons ::= DEFAULT LP expr RP */ -{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy18,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);} +{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy524,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);} break; case 32: /* ccons ::= DEFAULT PLUS term scanpt */ -{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy18,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy392);} +{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy524,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy294);} break; case 33: /* ccons ::= DEFAULT MINUS term scanpt */ { - Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[-1].minor.yy18, 0); - sqlite3AddDefaultValue(pParse,p,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy392); + Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[-1].minor.yy524, 0); + sqlite3AddDefaultValue(pParse,p,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy294); } break; case 34: /* ccons ::= DEFAULT scanpt ID|INDEXED */ @@ -149681,170 +151455,170 @@ static YYACTIONTYPE yy_reduce( } break; case 35: /* ccons ::= NOT NULL onconf */ -{sqlite3AddNotNull(pParse, yymsp[0].minor.yy70);} +{sqlite3AddNotNull(pParse, yymsp[0].minor.yy494);} break; case 36: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */ -{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy70,yymsp[0].minor.yy70,yymsp[-2].minor.yy70);} +{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy494,yymsp[0].minor.yy494,yymsp[-2].minor.yy494);} break; case 37: /* ccons ::= UNIQUE onconf */ -{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy70,0,0,0,0, +{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy494,0,0,0,0, SQLITE_IDXTYPE_UNIQUE);} break; case 38: /* ccons ::= CHECK LP expr RP */ -{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy18);} +{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy524);} break; case 39: /* ccons ::= REFERENCES nm eidlist_opt refargs */ -{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy420,yymsp[0].minor.yy70);} +{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy434,yymsp[0].minor.yy494);} break; case 40: /* ccons ::= defer_subclause */ -{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy70);} +{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy494);} break; case 41: /* ccons ::= COLLATE ID|STRING */ {sqlite3AddCollateType(pParse, &yymsp[0].minor.yy0);} break; case 44: /* refargs ::= */ -{ yymsp[1].minor.yy70 = OE_None*0x0101; /* EV: R-19803-45884 */} +{ yymsp[1].minor.yy494 = OE_None*0x0101; /* EV: R-19803-45884 */} break; case 45: /* refargs ::= refargs refarg */ -{ yymsp[-1].minor.yy70 = (yymsp[-1].minor.yy70 & ~yymsp[0].minor.yy111.mask) | yymsp[0].minor.yy111.value; } +{ yymsp[-1].minor.yy494 = (yymsp[-1].minor.yy494 & ~yymsp[0].minor.yy355.mask) | yymsp[0].minor.yy355.value; } break; case 46: /* refarg ::= MATCH nm */ -{ yymsp[-1].minor.yy111.value = 0; yymsp[-1].minor.yy111.mask = 0x000000; } +{ yymsp[-1].minor.yy355.value = 0; yymsp[-1].minor.yy355.mask = 0x000000; } break; case 47: /* refarg ::= ON INSERT refact */ -{ yymsp[-2].minor.yy111.value = 0; yymsp[-2].minor.yy111.mask = 0x000000; } +{ yymsp[-2].minor.yy355.value = 0; yymsp[-2].minor.yy355.mask = 0x000000; } break; case 48: /* refarg ::= ON DELETE refact */ -{ yymsp[-2].minor.yy111.value = yymsp[0].minor.yy70; yymsp[-2].minor.yy111.mask = 0x0000ff; } +{ yymsp[-2].minor.yy355.value = yymsp[0].minor.yy494; yymsp[-2].minor.yy355.mask = 0x0000ff; } break; case 49: /* refarg ::= ON UPDATE refact */ -{ yymsp[-2].minor.yy111.value = yymsp[0].minor.yy70<<8; yymsp[-2].minor.yy111.mask = 0x00ff00; } +{ yymsp[-2].minor.yy355.value = yymsp[0].minor.yy494<<8; yymsp[-2].minor.yy355.mask = 0x00ff00; } break; case 50: /* refact ::= SET NULL */ -{ yymsp[-1].minor.yy70 = OE_SetNull; /* EV: R-33326-45252 */} +{ yymsp[-1].minor.yy494 = OE_SetNull; /* EV: R-33326-45252 */} break; case 51: /* refact ::= SET DEFAULT */ -{ yymsp[-1].minor.yy70 = OE_SetDflt; /* EV: R-33326-45252 */} +{ yymsp[-1].minor.yy494 = OE_SetDflt; /* EV: R-33326-45252 */} break; case 52: /* refact ::= CASCADE */ -{ yymsp[0].minor.yy70 = OE_Cascade; /* EV: R-33326-45252 */} +{ yymsp[0].minor.yy494 = OE_Cascade; /* EV: R-33326-45252 */} break; case 53: /* refact ::= RESTRICT */ -{ yymsp[0].minor.yy70 = OE_Restrict; /* EV: R-33326-45252 */} +{ yymsp[0].minor.yy494 = OE_Restrict; /* EV: R-33326-45252 */} break; case 54: /* refact ::= NO ACTION */ -{ yymsp[-1].minor.yy70 = OE_None; /* EV: R-33326-45252 */} +{ yymsp[-1].minor.yy494 = OE_None; /* EV: R-33326-45252 */} break; case 55: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ -{yymsp[-2].minor.yy70 = 0;} +{yymsp[-2].minor.yy494 = 0;} break; case 56: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ case 71: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==71); case 156: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==156); -{yymsp[-1].minor.yy70 = yymsp[0].minor.yy70;} +{yymsp[-1].minor.yy494 = yymsp[0].minor.yy494;} break; case 58: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */ case 75: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==75); case 198: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==198); case 201: /* in_op ::= NOT IN */ yytestcase(yyruleno==201); case 227: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==227); -{yymsp[-1].minor.yy70 = 1;} +{yymsp[-1].minor.yy494 = 1;} break; case 59: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ -{yymsp[-1].minor.yy70 = 0;} +{yymsp[-1].minor.yy494 = 0;} break; case 61: /* tconscomma ::= COMMA */ {pParse->constraintName.n = 0;} break; case 63: /* tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ -{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy420,yymsp[0].minor.yy70,yymsp[-2].minor.yy70,0);} +{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy434,yymsp[0].minor.yy494,yymsp[-2].minor.yy494,0);} break; case 64: /* tcons ::= UNIQUE LP sortlist RP onconf */ -{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy420,yymsp[0].minor.yy70,0,0,0,0, +{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy434,yymsp[0].minor.yy494,0,0,0,0, SQLITE_IDXTYPE_UNIQUE);} break; case 65: /* tcons ::= CHECK LP expr RP onconf */ -{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy18);} +{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy524);} break; case 66: /* tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ { - sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy420, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy420, yymsp[-1].minor.yy70); - sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy70); + sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy434, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy434, yymsp[-1].minor.yy494); + sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy494); } break; case 68: /* onconf ::= */ case 70: /* orconf ::= */ yytestcase(yyruleno==70); -{yymsp[1].minor.yy70 = OE_Default;} +{yymsp[1].minor.yy494 = OE_Default;} break; case 69: /* onconf ::= ON CONFLICT resolvetype */ -{yymsp[-2].minor.yy70 = yymsp[0].minor.yy70;} +{yymsp[-2].minor.yy494 = yymsp[0].minor.yy494;} break; case 72: /* resolvetype ::= IGNORE */ -{yymsp[0].minor.yy70 = OE_Ignore;} +{yymsp[0].minor.yy494 = OE_Ignore;} break; case 73: /* resolvetype ::= REPLACE */ case 157: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==157); -{yymsp[0].minor.yy70 = OE_Replace;} +{yymsp[0].minor.yy494 = OE_Replace;} break; case 74: /* cmd ::= DROP TABLE ifexists fullname */ { - sqlite3DropTable(pParse, yymsp[0].minor.yy135, 0, yymsp[-1].minor.yy70); + sqlite3DropTable(pParse, yymsp[0].minor.yy483, 0, yymsp[-1].minor.yy494); } break; case 77: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ { - sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy420, yymsp[0].minor.yy489, yymsp[-7].minor.yy70, yymsp[-5].minor.yy70); + sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy434, yymsp[0].minor.yy457, yymsp[-7].minor.yy494, yymsp[-5].minor.yy494); } break; case 78: /* cmd ::= DROP VIEW ifexists fullname */ { - sqlite3DropTable(pParse, yymsp[0].minor.yy135, 1, yymsp[-1].minor.yy70); + sqlite3DropTable(pParse, yymsp[0].minor.yy483, 1, yymsp[-1].minor.yy494); } break; case 79: /* cmd ::= select */ { SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0}; - sqlite3Select(pParse, yymsp[0].minor.yy489, &dest); - sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy489); + sqlite3Select(pParse, yymsp[0].minor.yy457, &dest); + sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy457); } break; case 80: /* select ::= WITH wqlist selectnowith */ { - Select *p = yymsp[0].minor.yy489; + Select *p = yymsp[0].minor.yy457; if( p ){ - p->pWith = yymsp[-1].minor.yy449; + p->pWith = yymsp[-1].minor.yy59; parserDoubleLinkSelect(pParse, p); }else{ - sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy449); + sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy59); } - yymsp[-2].minor.yy489 = p; + yymsp[-2].minor.yy457 = p; } break; case 81: /* select ::= WITH RECURSIVE wqlist selectnowith */ { - Select *p = yymsp[0].minor.yy489; + Select *p = yymsp[0].minor.yy457; if( p ){ - p->pWith = yymsp[-1].minor.yy449; + p->pWith = yymsp[-1].minor.yy59; parserDoubleLinkSelect(pParse, p); }else{ - sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy449); + sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy59); } - yymsp[-3].minor.yy489 = p; + yymsp[-3].minor.yy457 = p; } break; case 82: /* select ::= selectnowith */ { - Select *p = yymsp[0].minor.yy489; + Select *p = yymsp[0].minor.yy457; if( p ){ parserDoubleLinkSelect(pParse, p); } - yymsp[0].minor.yy489 = p; /*A-overwrites-X*/ + yymsp[0].minor.yy457 = p; /*A-overwrites-X*/ } break; case 83: /* selectnowith ::= selectnowith multiselect_op oneselect */ { - Select *pRhs = yymsp[0].minor.yy489; - Select *pLhs = yymsp[-2].minor.yy489; + Select *pRhs = yymsp[0].minor.yy457; + Select *pLhs = yymsp[-2].minor.yy457; if( pRhs && pRhs->pPrior ){ SrcList *pFrom; Token x; @@ -149854,63 +151628,63 @@ static YYACTIONTYPE yy_reduce( pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0); } if( pRhs ){ - pRhs->op = (u8)yymsp[-1].minor.yy70; + pRhs->op = (u8)yymsp[-1].minor.yy494; pRhs->pPrior = pLhs; if( ALWAYS(pLhs) ) pLhs->selFlags &= ~SF_MultiValue; pRhs->selFlags &= ~SF_MultiValue; - if( yymsp[-1].minor.yy70!=TK_ALL ) pParse->hasCompound = 1; + if( yymsp[-1].minor.yy494!=TK_ALL ) pParse->hasCompound = 1; }else{ sqlite3SelectDelete(pParse->db, pLhs); } - yymsp[-2].minor.yy489 = pRhs; + yymsp[-2].minor.yy457 = pRhs; } break; case 84: /* multiselect_op ::= UNION */ case 86: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==86); -{yymsp[0].minor.yy70 = yymsp[0].major; /*A-overwrites-OP*/} +{yymsp[0].minor.yy494 = yymsp[0].major; /*A-overwrites-OP*/} break; case 85: /* multiselect_op ::= UNION ALL */ -{yymsp[-1].minor.yy70 = TK_ALL;} +{yymsp[-1].minor.yy494 = TK_ALL;} break; case 87: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ { - yymsp[-8].minor.yy489 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy420,yymsp[-5].minor.yy135,yymsp[-4].minor.yy18,yymsp[-3].minor.yy420,yymsp[-2].minor.yy18,yymsp[-1].minor.yy420,yymsp[-7].minor.yy70,yymsp[0].minor.yy18); + yymsp[-8].minor.yy457 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy434,yymsp[-5].minor.yy483,yymsp[-4].minor.yy524,yymsp[-3].minor.yy434,yymsp[-2].minor.yy524,yymsp[-1].minor.yy434,yymsp[-7].minor.yy494,yymsp[0].minor.yy524); } break; case 88: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ { - yymsp[-9].minor.yy489 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy420,yymsp[-6].minor.yy135,yymsp[-5].minor.yy18,yymsp[-4].minor.yy420,yymsp[-3].minor.yy18,yymsp[-1].minor.yy420,yymsp[-8].minor.yy70,yymsp[0].minor.yy18); - if( yymsp[-9].minor.yy489 ){ - yymsp[-9].minor.yy489->pWinDefn = yymsp[-2].minor.yy327; + yymsp[-9].minor.yy457 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy434,yymsp[-6].minor.yy483,yymsp[-5].minor.yy524,yymsp[-4].minor.yy434,yymsp[-3].minor.yy524,yymsp[-1].minor.yy434,yymsp[-8].minor.yy494,yymsp[0].minor.yy524); + if( yymsp[-9].minor.yy457 ){ + yymsp[-9].minor.yy457->pWinDefn = yymsp[-2].minor.yy295; }else{ - sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy327); + sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy295); } } break; case 89: /* values ::= VALUES LP nexprlist RP */ { - yymsp[-3].minor.yy489 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy420,0,0,0,0,0,SF_Values,0); + yymsp[-3].minor.yy457 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy434,0,0,0,0,0,SF_Values,0); } break; case 90: /* values ::= values COMMA LP nexprlist RP */ { - Select *pRight, *pLeft = yymsp[-4].minor.yy489; - pRight = sqlite3SelectNew(pParse,yymsp[-1].minor.yy420,0,0,0,0,0,SF_Values|SF_MultiValue,0); + Select *pRight, *pLeft = yymsp[-4].minor.yy457; + pRight = sqlite3SelectNew(pParse,yymsp[-1].minor.yy434,0,0,0,0,0,SF_Values|SF_MultiValue,0); if( ALWAYS(pLeft) ) pLeft->selFlags &= ~SF_MultiValue; if( pRight ){ pRight->op = TK_ALL; pRight->pPrior = pLeft; - yymsp[-4].minor.yy489 = pRight; + yymsp[-4].minor.yy457 = pRight; }else{ - yymsp[-4].minor.yy489 = pLeft; + yymsp[-4].minor.yy457 = pLeft; } } break; case 91: /* distinct ::= DISTINCT */ -{yymsp[0].minor.yy70 = SF_Distinct;} +{yymsp[0].minor.yy494 = SF_Distinct;} break; case 92: /* distinct ::= ALL */ -{yymsp[0].minor.yy70 = SF_All;} +{yymsp[0].minor.yy494 = SF_All;} break; case 94: /* sclp ::= */ case 127: /* orderby_opt ::= */ yytestcase(yyruleno==127); @@ -149918,19 +151692,19 @@ static YYACTIONTYPE yy_reduce( case 214: /* exprlist ::= */ yytestcase(yyruleno==214); case 217: /* paren_exprlist ::= */ yytestcase(yyruleno==217); case 222: /* eidlist_opt ::= */ yytestcase(yyruleno==222); -{yymsp[1].minor.yy420 = 0;} +{yymsp[1].minor.yy434 = 0;} break; case 95: /* selcollist ::= sclp scanpt expr scanpt as */ { - yymsp[-4].minor.yy420 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy420, yymsp[-2].minor.yy18); - if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy420, &yymsp[0].minor.yy0, 1); - sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy420,yymsp[-3].minor.yy392,yymsp[-1].minor.yy392); + yymsp[-4].minor.yy434 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy434, yymsp[-2].minor.yy524); + if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy434, &yymsp[0].minor.yy0, 1); + sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy434,yymsp[-3].minor.yy294,yymsp[-1].minor.yy294); } break; case 96: /* selcollist ::= sclp scanpt STAR */ { Expr *p = sqlite3Expr(pParse->db, TK_ASTERISK, 0); - yymsp[-2].minor.yy420 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy420, p); + yymsp[-2].minor.yy434 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy434, p); } break; case 97: /* selcollist ::= sclp scanpt nm DOT STAR */ @@ -149938,70 +151712,76 @@ static YYACTIONTYPE yy_reduce( Expr *pRight = sqlite3PExpr(pParse, TK_ASTERISK, 0, 0); Expr *pLeft = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1); Expr *pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight); - yymsp[-4].minor.yy420 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy420, pDot); + yymsp[-4].minor.yy434 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy434, pDot); } break; case 98: /* as ::= AS nm */ case 109: /* dbnm ::= DOT nm */ yytestcase(yyruleno==109); - case 236: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==236); - case 237: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==237); + case 238: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==238); + case 239: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==239); {yymsp[-1].minor.yy0 = yymsp[0].minor.yy0;} break; case 100: /* from ::= */ -{yymsp[1].minor.yy135 = sqlite3DbMallocZero(pParse->db, sizeof(*yymsp[1].minor.yy135));} +{yymsp[1].minor.yy483 = sqlite3DbMallocZero(pParse->db, sizeof(*yymsp[1].minor.yy483));} break; case 101: /* from ::= FROM seltablist */ { - yymsp[-1].minor.yy135 = yymsp[0].minor.yy135; - sqlite3SrcListShiftJoinType(yymsp[-1].minor.yy135); + yymsp[-1].minor.yy483 = yymsp[0].minor.yy483; + sqlite3SrcListShiftJoinType(yymsp[-1].minor.yy483); } break; case 102: /* stl_prefix ::= seltablist joinop */ { - if( ALWAYS(yymsp[-1].minor.yy135 && yymsp[-1].minor.yy135->nSrc>0) ) yymsp[-1].minor.yy135->a[yymsp[-1].minor.yy135->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy70; + if( ALWAYS(yymsp[-1].minor.yy483 && yymsp[-1].minor.yy483->nSrc>0) ) yymsp[-1].minor.yy483->a[yymsp[-1].minor.yy483->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy494; } break; case 103: /* stl_prefix ::= */ -{yymsp[1].minor.yy135 = 0;} +{yymsp[1].minor.yy483 = 0;} break; case 104: /* seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ { - yymsp[-6].minor.yy135 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy135,&yymsp[-5].minor.yy0,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,0,yymsp[-1].minor.yy18,yymsp[0].minor.yy48); - sqlite3SrcListIndexedBy(pParse, yymsp[-6].minor.yy135, &yymsp[-2].minor.yy0); + yymsp[-6].minor.yy483 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy483,&yymsp[-5].minor.yy0,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,0,yymsp[-1].minor.yy524,yymsp[0].minor.yy62); + sqlite3SrcListIndexedBy(pParse, yymsp[-6].minor.yy483, &yymsp[-2].minor.yy0); } break; case 105: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */ { - yymsp[-8].minor.yy135 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-8].minor.yy135,&yymsp[-7].minor.yy0,&yymsp[-6].minor.yy0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy18,yymsp[0].minor.yy48); - sqlite3SrcListFuncArgs(pParse, yymsp[-8].minor.yy135, yymsp[-4].minor.yy420); + yymsp[-8].minor.yy483 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-8].minor.yy483,&yymsp[-7].minor.yy0,&yymsp[-6].minor.yy0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy524,yymsp[0].minor.yy62); + sqlite3SrcListFuncArgs(pParse, yymsp[-8].minor.yy483, yymsp[-4].minor.yy434); } break; case 106: /* seltablist ::= stl_prefix LP select RP as on_opt using_opt */ { - yymsp[-6].minor.yy135 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy135,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy489,yymsp[-1].minor.yy18,yymsp[0].minor.yy48); + yymsp[-6].minor.yy483 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy483,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy457,yymsp[-1].minor.yy524,yymsp[0].minor.yy62); } break; case 107: /* seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ { - if( yymsp[-6].minor.yy135==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy18==0 && yymsp[0].minor.yy48==0 ){ - yymsp[-6].minor.yy135 = yymsp[-4].minor.yy135; - }else if( yymsp[-4].minor.yy135->nSrc==1 ){ - yymsp[-6].minor.yy135 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy135,0,0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy18,yymsp[0].minor.yy48); - if( yymsp[-6].minor.yy135 ){ - struct SrcList_item *pNew = &yymsp[-6].minor.yy135->a[yymsp[-6].minor.yy135->nSrc-1]; - struct SrcList_item *pOld = yymsp[-4].minor.yy135->a; + if( yymsp[-6].minor.yy483==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy524==0 && yymsp[0].minor.yy62==0 ){ + yymsp[-6].minor.yy483 = yymsp[-4].minor.yy483; + }else if( yymsp[-4].minor.yy483->nSrc==1 ){ + yymsp[-6].minor.yy483 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy483,0,0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy524,yymsp[0].minor.yy62); + if( yymsp[-6].minor.yy483 ){ + struct SrcList_item *pNew = &yymsp[-6].minor.yy483->a[yymsp[-6].minor.yy483->nSrc-1]; + struct SrcList_item *pOld = yymsp[-4].minor.yy483->a; pNew->zName = pOld->zName; pNew->zDatabase = pOld->zDatabase; pNew->pSelect = pOld->pSelect; + if( pOld->fg.isTabFunc ){ + pNew->u1.pFuncArg = pOld->u1.pFuncArg; + pOld->u1.pFuncArg = 0; + pOld->fg.isTabFunc = 0; + pNew->fg.isTabFunc = 1; + } pOld->zName = pOld->zDatabase = 0; pOld->pSelect = 0; } - sqlite3SrcListDelete(pParse->db, yymsp[-4].minor.yy135); + sqlite3SrcListDelete(pParse->db, yymsp[-4].minor.yy483); }else{ Select *pSubquery; - sqlite3SrcListShiftJoinType(yymsp[-4].minor.yy135); - pSubquery = sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy135,0,0,0,0,SF_NestedFrom,0); - yymsp[-6].minor.yy135 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy135,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy18,yymsp[0].minor.yy48); + sqlite3SrcListShiftJoinType(yymsp[-4].minor.yy483); + pSubquery = sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy483,0,0,0,0,SF_NestedFrom,0); + yymsp[-6].minor.yy483 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy483,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy524,yymsp[0].minor.yy62); } } break; @@ -150011,53 +151791,54 @@ static YYACTIONTYPE yy_reduce( break; case 110: /* fullname ::= nm */ { - yylhsminor.yy135 = sqlite3SrcListAppend(pParse->db,0,&yymsp[0].minor.yy0,0); - if( IN_RENAME_OBJECT && yylhsminor.yy135 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy135->a[0].zName, &yymsp[0].minor.yy0); + yylhsminor.yy483 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); + if( IN_RENAME_OBJECT && yylhsminor.yy483 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy483->a[0].zName, &yymsp[0].minor.yy0); } - yymsp[0].minor.yy135 = yylhsminor.yy135; + yymsp[0].minor.yy483 = yylhsminor.yy483; break; case 111: /* fullname ::= nm DOT nm */ { - yylhsminor.yy135 = sqlite3SrcListAppend(pParse->db,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); - if( IN_RENAME_OBJECT && yylhsminor.yy135 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy135->a[0].zName, &yymsp[0].minor.yy0); + yylhsminor.yy483 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); + if( IN_RENAME_OBJECT && yylhsminor.yy483 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy483->a[0].zName, &yymsp[0].minor.yy0); } - yymsp[-2].minor.yy135 = yylhsminor.yy135; + yymsp[-2].minor.yy483 = yylhsminor.yy483; break; case 112: /* xfullname ::= nm */ -{yymsp[0].minor.yy135 = sqlite3SrcListAppend(pParse->db,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/} +{yymsp[0].minor.yy483 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/} break; case 113: /* xfullname ::= nm DOT nm */ -{yymsp[-2].minor.yy135 = sqlite3SrcListAppend(pParse->db,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/} +{yymsp[-2].minor.yy483 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/} break; case 114: /* xfullname ::= nm DOT nm AS nm */ { - yymsp[-4].minor.yy135 = sqlite3SrcListAppend(pParse->db,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/ - if( yymsp[-4].minor.yy135 ) yymsp[-4].minor.yy135->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); + yymsp[-4].minor.yy483 = sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/ + if( yymsp[-4].minor.yy483 ) yymsp[-4].minor.yy483->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); } break; case 115: /* xfullname ::= nm AS nm */ { - yymsp[-2].minor.yy135 = sqlite3SrcListAppend(pParse->db,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/ - if( yymsp[-2].minor.yy135 ) yymsp[-2].minor.yy135->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); + yymsp[-2].minor.yy483 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/ + if( yymsp[-2].minor.yy483 ) yymsp[-2].minor.yy483->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); } break; case 116: /* joinop ::= COMMA|JOIN */ -{ yymsp[0].minor.yy70 = JT_INNER; } +{ yymsp[0].minor.yy494 = JT_INNER; } break; case 117: /* joinop ::= JOIN_KW JOIN */ -{yymsp[-1].minor.yy70 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/} +{yymsp[-1].minor.yy494 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/} break; case 118: /* joinop ::= JOIN_KW nm JOIN */ -{yymsp[-2].minor.yy70 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/} +{yymsp[-2].minor.yy494 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/} break; case 119: /* joinop ::= JOIN_KW nm nm JOIN */ -{yymsp[-3].minor.yy70 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/} +{yymsp[-3].minor.yy494 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/} break; case 120: /* on_opt ::= ON expr */ case 137: /* having_opt ::= HAVING expr */ yytestcase(yyruleno==137); case 144: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==144); case 210: /* case_else ::= ELSE expr */ yytestcase(yyruleno==210); -{yymsp[-1].minor.yy18 = yymsp[0].minor.yy18;} + case 231: /* vinto ::= INTO expr */ yytestcase(yyruleno==231); +{yymsp[-1].minor.yy524 = yymsp[0].minor.yy524;} break; case 121: /* on_opt ::= */ case 136: /* having_opt ::= */ yytestcase(yyruleno==136); @@ -150065,7 +151846,8 @@ static YYACTIONTYPE yy_reduce( case 143: /* where_opt ::= */ yytestcase(yyruleno==143); case 211: /* case_else ::= */ yytestcase(yyruleno==211); case 213: /* case_operand ::= */ yytestcase(yyruleno==213); -{yymsp[1].minor.yy18 = 0;} + case 232: /* vinto ::= */ yytestcase(yyruleno==232); +{yymsp[1].minor.yy524 = 0;} break; case 123: /* indexed_opt ::= INDEXED BY nm */ {yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;} @@ -150074,119 +151856,119 @@ static YYACTIONTYPE yy_reduce( {yymsp[-1].minor.yy0.z=0; yymsp[-1].minor.yy0.n=1;} break; case 125: /* using_opt ::= USING LP idlist RP */ -{yymsp[-3].minor.yy48 = yymsp[-1].minor.yy48;} +{yymsp[-3].minor.yy62 = yymsp[-1].minor.yy62;} break; case 126: /* using_opt ::= */ case 158: /* idlist_opt ::= */ yytestcase(yyruleno==158); -{yymsp[1].minor.yy48 = 0;} +{yymsp[1].minor.yy62 = 0;} break; case 128: /* orderby_opt ::= ORDER BY sortlist */ case 135: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==135); -{yymsp[-2].minor.yy420 = yymsp[0].minor.yy420;} +{yymsp[-2].minor.yy434 = yymsp[0].minor.yy434;} break; case 129: /* sortlist ::= sortlist COMMA expr sortorder */ { - yymsp[-3].minor.yy420 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy420,yymsp[-1].minor.yy18); - sqlite3ExprListSetSortOrder(yymsp[-3].minor.yy420,yymsp[0].minor.yy70); + yymsp[-3].minor.yy434 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy434,yymsp[-1].minor.yy524); + sqlite3ExprListSetSortOrder(yymsp[-3].minor.yy434,yymsp[0].minor.yy494); } break; case 130: /* sortlist ::= expr sortorder */ { - yymsp[-1].minor.yy420 = sqlite3ExprListAppend(pParse,0,yymsp[-1].minor.yy18); /*A-overwrites-Y*/ - sqlite3ExprListSetSortOrder(yymsp[-1].minor.yy420,yymsp[0].minor.yy70); + yymsp[-1].minor.yy434 = sqlite3ExprListAppend(pParse,0,yymsp[-1].minor.yy524); /*A-overwrites-Y*/ + sqlite3ExprListSetSortOrder(yymsp[-1].minor.yy434,yymsp[0].minor.yy494); } break; case 131: /* sortorder ::= ASC */ -{yymsp[0].minor.yy70 = SQLITE_SO_ASC;} +{yymsp[0].minor.yy494 = SQLITE_SO_ASC;} break; case 132: /* sortorder ::= DESC */ -{yymsp[0].minor.yy70 = SQLITE_SO_DESC;} +{yymsp[0].minor.yy494 = SQLITE_SO_DESC;} break; case 133: /* sortorder ::= */ -{yymsp[1].minor.yy70 = SQLITE_SO_UNDEFINED;} +{yymsp[1].minor.yy494 = SQLITE_SO_UNDEFINED;} break; case 139: /* limit_opt ::= LIMIT expr */ -{yymsp[-1].minor.yy18 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy18,0);} +{yymsp[-1].minor.yy524 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy524,0);} break; case 140: /* limit_opt ::= LIMIT expr OFFSET expr */ -{yymsp[-3].minor.yy18 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy18,yymsp[0].minor.yy18);} +{yymsp[-3].minor.yy524 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy524,yymsp[0].minor.yy524);} break; case 141: /* limit_opt ::= LIMIT expr COMMA expr */ -{yymsp[-3].minor.yy18 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy18,yymsp[-2].minor.yy18);} +{yymsp[-3].minor.yy524 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy524,yymsp[-2].minor.yy524);} break; case 142: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt */ { - sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy135, &yymsp[-1].minor.yy0); - sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy135,yymsp[0].minor.yy18,0,0); + sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy483, &yymsp[-1].minor.yy0); + sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy483,yymsp[0].minor.yy524,0,0); } break; case 145: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt */ { - sqlite3SrcListIndexedBy(pParse, yymsp[-4].minor.yy135, &yymsp[-3].minor.yy0); - sqlite3ExprListCheckLength(pParse,yymsp[-1].minor.yy420,"set list"); - sqlite3Update(pParse,yymsp[-4].minor.yy135,yymsp[-1].minor.yy420,yymsp[0].minor.yy18,yymsp[-5].minor.yy70,0,0,0); + sqlite3SrcListIndexedBy(pParse, yymsp[-4].minor.yy483, &yymsp[-3].minor.yy0); + sqlite3ExprListCheckLength(pParse,yymsp[-1].minor.yy434,"set list"); + sqlite3Update(pParse,yymsp[-4].minor.yy483,yymsp[-1].minor.yy434,yymsp[0].minor.yy524,yymsp[-5].minor.yy494,0,0,0); } break; case 146: /* setlist ::= setlist COMMA nm EQ expr */ { - yymsp[-4].minor.yy420 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy420, yymsp[0].minor.yy18); - sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy420, &yymsp[-2].minor.yy0, 1); + yymsp[-4].minor.yy434 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy434, yymsp[0].minor.yy524); + sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy434, &yymsp[-2].minor.yy0, 1); } break; case 147: /* setlist ::= setlist COMMA LP idlist RP EQ expr */ { - yymsp[-6].minor.yy420 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy420, yymsp[-3].minor.yy48, yymsp[0].minor.yy18); + yymsp[-6].minor.yy434 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy434, yymsp[-3].minor.yy62, yymsp[0].minor.yy524); } break; case 148: /* setlist ::= nm EQ expr */ { - yylhsminor.yy420 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy18); - sqlite3ExprListSetName(pParse, yylhsminor.yy420, &yymsp[-2].minor.yy0, 1); + yylhsminor.yy434 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy524); + sqlite3ExprListSetName(pParse, yylhsminor.yy434, &yymsp[-2].minor.yy0, 1); } - yymsp[-2].minor.yy420 = yylhsminor.yy420; + yymsp[-2].minor.yy434 = yylhsminor.yy434; break; case 149: /* setlist ::= LP idlist RP EQ expr */ { - yymsp[-4].minor.yy420 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy48, yymsp[0].minor.yy18); + yymsp[-4].minor.yy434 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy62, yymsp[0].minor.yy524); } break; case 150: /* cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ { - sqlite3Insert(pParse, yymsp[-3].minor.yy135, yymsp[-1].minor.yy489, yymsp[-2].minor.yy48, yymsp[-5].minor.yy70, yymsp[0].minor.yy340); + sqlite3Insert(pParse, yymsp[-3].minor.yy483, yymsp[-1].minor.yy457, yymsp[-2].minor.yy62, yymsp[-5].minor.yy494, yymsp[0].minor.yy136); } break; case 151: /* cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */ { - sqlite3Insert(pParse, yymsp[-3].minor.yy135, 0, yymsp[-2].minor.yy48, yymsp[-5].minor.yy70, 0); + sqlite3Insert(pParse, yymsp[-3].minor.yy483, 0, yymsp[-2].minor.yy62, yymsp[-5].minor.yy494, 0); } break; case 152: /* upsert ::= */ -{ yymsp[1].minor.yy340 = 0; } +{ yymsp[1].minor.yy136 = 0; } break; case 153: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */ -{ yymsp[-10].minor.yy340 = sqlite3UpsertNew(pParse->db,yymsp[-7].minor.yy420,yymsp[-5].minor.yy18,yymsp[-1].minor.yy420,yymsp[0].minor.yy18);} +{ yymsp[-10].minor.yy136 = sqlite3UpsertNew(pParse->db,yymsp[-7].minor.yy434,yymsp[-5].minor.yy524,yymsp[-1].minor.yy434,yymsp[0].minor.yy524);} break; case 154: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */ -{ yymsp[-7].minor.yy340 = sqlite3UpsertNew(pParse->db,yymsp[-4].minor.yy420,yymsp[-2].minor.yy18,0,0); } +{ yymsp[-7].minor.yy136 = sqlite3UpsertNew(pParse->db,yymsp[-4].minor.yy434,yymsp[-2].minor.yy524,0,0); } break; case 155: /* upsert ::= ON CONFLICT DO NOTHING */ -{ yymsp[-3].minor.yy340 = sqlite3UpsertNew(pParse->db,0,0,0,0); } +{ yymsp[-3].minor.yy136 = sqlite3UpsertNew(pParse->db,0,0,0,0); } break; case 159: /* idlist_opt ::= LP idlist RP */ -{yymsp[-2].minor.yy48 = yymsp[-1].minor.yy48;} +{yymsp[-2].minor.yy62 = yymsp[-1].minor.yy62;} break; case 160: /* idlist ::= idlist COMMA nm */ -{yymsp[-2].minor.yy48 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy48,&yymsp[0].minor.yy0);} +{yymsp[-2].minor.yy62 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy62,&yymsp[0].minor.yy0);} break; case 161: /* idlist ::= nm */ -{yymsp[0].minor.yy48 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/} +{yymsp[0].minor.yy62 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/} break; case 162: /* expr ::= LP expr RP */ -{yymsp[-2].minor.yy18 = yymsp[-1].minor.yy18;} +{yymsp[-2].minor.yy524 = yymsp[-1].minor.yy524;} break; case 163: /* expr ::= ID|INDEXED */ case 164: /* expr ::= JOIN_KW */ yytestcase(yyruleno==164); -{yymsp[0].minor.yy18=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/} +{yymsp[0].minor.yy524=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/} break; case 165: /* expr ::= nm DOT nm */ { @@ -150196,9 +151978,9 @@ static YYACTIONTYPE yy_reduce( sqlite3RenameTokenMap(pParse, (void*)temp2, &yymsp[0].minor.yy0); sqlite3RenameTokenMap(pParse, (void*)temp1, &yymsp[-2].minor.yy0); } - yylhsminor.yy18 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2); + yylhsminor.yy524 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2); } - yymsp[-2].minor.yy18 = yylhsminor.yy18; + yymsp[-2].minor.yy524 = yylhsminor.yy524; break; case 166: /* expr ::= nm DOT nm DOT nm */ { @@ -150210,26 +151992,26 @@ static YYACTIONTYPE yy_reduce( sqlite3RenameTokenMap(pParse, (void*)temp3, &yymsp[0].minor.yy0); sqlite3RenameTokenMap(pParse, (void*)temp2, &yymsp[-2].minor.yy0); } - yylhsminor.yy18 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4); + yylhsminor.yy524 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4); } - yymsp[-4].minor.yy18 = yylhsminor.yy18; + yymsp[-4].minor.yy524 = yylhsminor.yy524; break; case 167: /* term ::= NULL|FLOAT|BLOB */ case 168: /* term ::= STRING */ yytestcase(yyruleno==168); -{yymsp[0].minor.yy18=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/} +{yymsp[0].minor.yy524=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/} break; case 169: /* term ::= INTEGER */ { - yylhsminor.yy18 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1); + yylhsminor.yy524 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1); } - yymsp[0].minor.yy18 = yylhsminor.yy18; + yymsp[0].minor.yy524 = yylhsminor.yy524; break; case 170: /* expr ::= VARIABLE */ { if( !(yymsp[0].minor.yy0.z[0]=='#' && sqlite3Isdigit(yymsp[0].minor.yy0.z[1])) ){ u32 n = yymsp[0].minor.yy0.n; - yymsp[0].minor.yy18 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0); - sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy18, n); + yymsp[0].minor.yy524 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0); + sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy524, n); }else{ /* When doing a nested parse, one can include terms in an expression ** that look like this: #1 #2 ... These terms refer to registers @@ -150238,63 +152020,63 @@ static YYACTIONTYPE yy_reduce( assert( t.n>=2 ); if( pParse->nested==0 ){ sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t); - yymsp[0].minor.yy18 = 0; + yymsp[0].minor.yy524 = 0; }else{ - yymsp[0].minor.yy18 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0); - if( yymsp[0].minor.yy18 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy18->iTable); + yymsp[0].minor.yy524 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0); + if( yymsp[0].minor.yy524 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy524->iTable); } } } break; case 171: /* expr ::= expr COLLATE ID|STRING */ { - yymsp[-2].minor.yy18 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy18, &yymsp[0].minor.yy0, 1); + yymsp[-2].minor.yy524 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy524, &yymsp[0].minor.yy0, 1); } break; case 172: /* expr ::= CAST LP expr AS typetoken RP */ { - yymsp[-5].minor.yy18 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1); - sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy18, yymsp[-3].minor.yy18, 0); + yymsp[-5].minor.yy524 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1); + sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy524, yymsp[-3].minor.yy524, 0); } break; case 173: /* expr ::= ID|INDEXED LP distinct exprlist RP */ { - yylhsminor.yy18 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy420, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy70); + yylhsminor.yy524 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy434, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy494); } - yymsp[-4].minor.yy18 = yylhsminor.yy18; + yymsp[-4].minor.yy524 = yylhsminor.yy524; break; case 174: /* expr ::= ID|INDEXED LP STAR RP */ { - yylhsminor.yy18 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0); + yylhsminor.yy524 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0); } - yymsp[-3].minor.yy18 = yylhsminor.yy18; + yymsp[-3].minor.yy524 = yylhsminor.yy524; break; case 175: /* expr ::= ID|INDEXED LP distinct exprlist RP over_clause */ { - yylhsminor.yy18 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy420, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy70); - sqlite3WindowAttach(pParse, yylhsminor.yy18, yymsp[0].minor.yy327); + yylhsminor.yy524 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy434, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy494); + sqlite3WindowAttach(pParse, yylhsminor.yy524, yymsp[0].minor.yy295); } - yymsp[-5].minor.yy18 = yylhsminor.yy18; + yymsp[-5].minor.yy524 = yylhsminor.yy524; break; case 176: /* expr ::= ID|INDEXED LP STAR RP over_clause */ { - yylhsminor.yy18 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0); - sqlite3WindowAttach(pParse, yylhsminor.yy18, yymsp[0].minor.yy327); + yylhsminor.yy524 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0); + sqlite3WindowAttach(pParse, yylhsminor.yy524, yymsp[0].minor.yy295); } - yymsp[-4].minor.yy18 = yylhsminor.yy18; + yymsp[-4].minor.yy524 = yylhsminor.yy524; break; case 177: /* term ::= CTIME_KW */ { - yylhsminor.yy18 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0); + yylhsminor.yy524 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0); } - yymsp[0].minor.yy18 = yylhsminor.yy18; + yymsp[0].minor.yy524 = yylhsminor.yy524; break; case 178: /* expr ::= LP nexprlist COMMA expr RP */ { - ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy420, yymsp[-1].minor.yy18); - yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0); - if( yymsp[-4].minor.yy18 ){ - yymsp[-4].minor.yy18->x.pList = pList; + ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy434, yymsp[-1].minor.yy524); + yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0); + if( yymsp[-4].minor.yy524 ){ + yymsp[-4].minor.yy524->x.pList = pList; }else{ sqlite3ExprListDelete(pParse->db, pList); } @@ -150308,7 +152090,7 @@ static YYACTIONTYPE yy_reduce( case 184: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==184); case 185: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==185); case 186: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==186); -{yymsp[-2].minor.yy18=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy18,yymsp[0].minor.yy18);} +{yymsp[-2].minor.yy524=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy524,yymsp[0].minor.yy524);} break; case 187: /* likeop ::= NOT LIKE_KW|MATCH */ {yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.n|=0x80000000; /*yymsp[-1].minor.yy0-overwrite-yymsp[0].minor.yy0*/} @@ -150318,11 +152100,11 @@ static YYACTIONTYPE yy_reduce( ExprList *pList; int bNot = yymsp[-1].minor.yy0.n & 0x80000000; yymsp[-1].minor.yy0.n &= 0x7fffffff; - pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy18); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy18); - yymsp[-2].minor.yy18 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); - if( bNot ) yymsp[-2].minor.yy18 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy18, 0); - if( yymsp[-2].minor.yy18 ) yymsp[-2].minor.yy18->flags |= EP_InfixFunc; + pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy524); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy524); + yymsp[-2].minor.yy524 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); + if( bNot ) yymsp[-2].minor.yy524 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy524, 0); + if( yymsp[-2].minor.yy524 ) yymsp[-2].minor.yy524->flags |= EP_InfixFunc; } break; case 189: /* expr ::= expr likeop expr ESCAPE expr */ @@ -150330,62 +152112,62 @@ static YYACTIONTYPE yy_reduce( ExprList *pList; int bNot = yymsp[-3].minor.yy0.n & 0x80000000; yymsp[-3].minor.yy0.n &= 0x7fffffff; - pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy18); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy18); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy18); - yymsp[-4].minor.yy18 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0); - if( bNot ) yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy18, 0); - if( yymsp[-4].minor.yy18 ) yymsp[-4].minor.yy18->flags |= EP_InfixFunc; + pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy524); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy524); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy524); + yymsp[-4].minor.yy524 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0); + if( bNot ) yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy524, 0); + if( yymsp[-4].minor.yy524 ) yymsp[-4].minor.yy524->flags |= EP_InfixFunc; } break; case 190: /* expr ::= expr ISNULL|NOTNULL */ -{yymsp[-1].minor.yy18 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy18,0);} +{yymsp[-1].minor.yy524 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy524,0);} break; case 191: /* expr ::= expr NOT NULL */ -{yymsp[-2].minor.yy18 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy18,0);} +{yymsp[-2].minor.yy524 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy524,0);} break; case 192: /* expr ::= expr IS expr */ { - yymsp[-2].minor.yy18 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy18,yymsp[0].minor.yy18); - binaryToUnaryIfNull(pParse, yymsp[0].minor.yy18, yymsp[-2].minor.yy18, TK_ISNULL); + yymsp[-2].minor.yy524 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy524,yymsp[0].minor.yy524); + binaryToUnaryIfNull(pParse, yymsp[0].minor.yy524, yymsp[-2].minor.yy524, TK_ISNULL); } break; case 193: /* expr ::= expr IS NOT expr */ { - yymsp[-3].minor.yy18 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy18,yymsp[0].minor.yy18); - binaryToUnaryIfNull(pParse, yymsp[0].minor.yy18, yymsp[-3].minor.yy18, TK_NOTNULL); + yymsp[-3].minor.yy524 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy524,yymsp[0].minor.yy524); + binaryToUnaryIfNull(pParse, yymsp[0].minor.yy524, yymsp[-3].minor.yy524, TK_NOTNULL); } break; case 194: /* expr ::= NOT expr */ case 195: /* expr ::= BITNOT expr */ yytestcase(yyruleno==195); -{yymsp[-1].minor.yy18 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy18, 0);/*A-overwrites-B*/} +{yymsp[-1].minor.yy524 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy524, 0);/*A-overwrites-B*/} break; case 196: /* expr ::= PLUS|MINUS expr */ { - yymsp[-1].minor.yy18 = sqlite3PExpr(pParse, yymsp[-1].major==TK_PLUS ? TK_UPLUS : TK_UMINUS, yymsp[0].minor.yy18, 0); + yymsp[-1].minor.yy524 = sqlite3PExpr(pParse, yymsp[-1].major==TK_PLUS ? TK_UPLUS : TK_UMINUS, yymsp[0].minor.yy524, 0); /*A-overwrites-B*/ } break; case 197: /* between_op ::= BETWEEN */ case 200: /* in_op ::= IN */ yytestcase(yyruleno==200); -{yymsp[0].minor.yy70 = 0;} +{yymsp[0].minor.yy494 = 0;} break; case 199: /* expr ::= expr between_op expr AND expr */ { - ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy18); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy18); - yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy18, 0); - if( yymsp[-4].minor.yy18 ){ - yymsp[-4].minor.yy18->x.pList = pList; + ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy524); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy524); + yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy524, 0); + if( yymsp[-4].minor.yy524 ){ + yymsp[-4].minor.yy524->x.pList = pList; }else{ sqlite3ExprListDelete(pParse->db, pList); } - if( yymsp[-3].minor.yy70 ) yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy18, 0); + if( yymsp[-3].minor.yy494 ) yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy524, 0); } break; case 202: /* expr ::= expr in_op LP exprlist RP */ { - if( yymsp[-1].minor.yy420==0 ){ + if( yymsp[-1].minor.yy434==0 ){ /* Expressions of the form ** ** expr1 IN () @@ -150394,9 +152176,11 @@ static YYACTIONTYPE yy_reduce( ** simplify to constants 0 (false) and 1 (true), respectively, ** regardless of the value of expr1. */ - sqlite3ExprDelete(pParse->db, yymsp[-4].minor.yy18); - yymsp[-4].minor.yy18 = sqlite3ExprAlloc(pParse->db, TK_INTEGER,&sqlite3IntTokens[yymsp[-3].minor.yy70],1); - }else if( yymsp[-1].minor.yy420->nExpr==1 ){ + if( IN_RENAME_OBJECT==0 ){ + sqlite3ExprDelete(pParse->db, yymsp[-4].minor.yy524); + yymsp[-4].minor.yy524 = sqlite3ExprAlloc(pParse->db, TK_INTEGER,&sqlite3IntTokens[yymsp[-3].minor.yy494],1); + } + }else if( yymsp[-1].minor.yy434->nExpr==1 ){ /* Expressions of the form: ** ** expr1 IN (?1) @@ -150413,199 +152197,199 @@ static YYACTIONTYPE yy_reduce( ** affinity or the collating sequence to use for comparison. Otherwise, ** the semantics would be subtly different from IN or NOT IN. */ - Expr *pRHS = yymsp[-1].minor.yy420->a[0].pExpr; - yymsp[-1].minor.yy420->a[0].pExpr = 0; - sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy420); + Expr *pRHS = yymsp[-1].minor.yy434->a[0].pExpr; + yymsp[-1].minor.yy434->a[0].pExpr = 0; + sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy434); /* pRHS cannot be NULL because a malloc error would have been detected ** before now and control would have never reached this point */ if( ALWAYS(pRHS) ){ pRHS->flags &= ~EP_Collate; pRHS->flags |= EP_Generic; } - yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, yymsp[-3].minor.yy70 ? TK_NE : TK_EQ, yymsp[-4].minor.yy18, pRHS); + yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, yymsp[-3].minor.yy494 ? TK_NE : TK_EQ, yymsp[-4].minor.yy524, pRHS); }else{ - yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy18, 0); - if( yymsp[-4].minor.yy18 ){ - yymsp[-4].minor.yy18->x.pList = yymsp[-1].minor.yy420; - sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy18); + yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy524, 0); + if( yymsp[-4].minor.yy524 ){ + yymsp[-4].minor.yy524->x.pList = yymsp[-1].minor.yy434; + sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy524); }else{ - sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy420); + sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy434); } - if( yymsp[-3].minor.yy70 ) yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy18, 0); + if( yymsp[-3].minor.yy494 ) yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy524, 0); } } break; case 203: /* expr ::= LP select RP */ { - yymsp[-2].minor.yy18 = sqlite3PExpr(pParse, TK_SELECT, 0, 0); - sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy18, yymsp[-1].minor.yy489); + yymsp[-2].minor.yy524 = sqlite3PExpr(pParse, TK_SELECT, 0, 0); + sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy524, yymsp[-1].minor.yy457); } break; case 204: /* expr ::= expr in_op LP select RP */ { - yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy18, 0); - sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy18, yymsp[-1].minor.yy489); - if( yymsp[-3].minor.yy70 ) yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy18, 0); + yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy524, 0); + sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy524, yymsp[-1].minor.yy457); + if( yymsp[-3].minor.yy494 ) yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy524, 0); } break; case 205: /* expr ::= expr in_op nm dbnm paren_exprlist */ { - SrcList *pSrc = sqlite3SrcListAppend(pParse->db, 0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0); + SrcList *pSrc = sqlite3SrcListAppend(pParse, 0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0); Select *pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0); - if( yymsp[0].minor.yy420 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy420); - yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy18, 0); - sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy18, pSelect); - if( yymsp[-3].minor.yy70 ) yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy18, 0); + if( yymsp[0].minor.yy434 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy434); + yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy524, 0); + sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy524, pSelect); + if( yymsp[-3].minor.yy494 ) yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy524, 0); } break; case 206: /* expr ::= EXISTS LP select RP */ { Expr *p; - p = yymsp[-3].minor.yy18 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0); - sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy489); + p = yymsp[-3].minor.yy524 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0); + sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy457); } break; case 207: /* expr ::= CASE case_operand case_exprlist case_else END */ { - yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy18, 0); - if( yymsp[-4].minor.yy18 ){ - yymsp[-4].minor.yy18->x.pList = yymsp[-1].minor.yy18 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy420,yymsp[-1].minor.yy18) : yymsp[-2].minor.yy420; - sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy18); + yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy524, 0); + if( yymsp[-4].minor.yy524 ){ + yymsp[-4].minor.yy524->x.pList = yymsp[-1].minor.yy524 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy434,yymsp[-1].minor.yy524) : yymsp[-2].minor.yy434; + sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy524); }else{ - sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy420); - sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy18); + sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy434); + sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy524); } } break; case 208: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */ { - yymsp[-4].minor.yy420 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy420, yymsp[-2].minor.yy18); - yymsp[-4].minor.yy420 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy420, yymsp[0].minor.yy18); + yymsp[-4].minor.yy434 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy434, yymsp[-2].minor.yy524); + yymsp[-4].minor.yy434 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy434, yymsp[0].minor.yy524); } break; case 209: /* case_exprlist ::= WHEN expr THEN expr */ { - yymsp[-3].minor.yy420 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy18); - yymsp[-3].minor.yy420 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy420, yymsp[0].minor.yy18); + yymsp[-3].minor.yy434 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy524); + yymsp[-3].minor.yy434 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy434, yymsp[0].minor.yy524); } break; case 212: /* case_operand ::= expr */ -{yymsp[0].minor.yy18 = yymsp[0].minor.yy18; /*A-overwrites-X*/} +{yymsp[0].minor.yy524 = yymsp[0].minor.yy524; /*A-overwrites-X*/} break; case 215: /* nexprlist ::= nexprlist COMMA expr */ -{yymsp[-2].minor.yy420 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy420,yymsp[0].minor.yy18);} +{yymsp[-2].minor.yy434 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy434,yymsp[0].minor.yy524);} break; case 216: /* nexprlist ::= expr */ -{yymsp[0].minor.yy420 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy18); /*A-overwrites-Y*/} +{yymsp[0].minor.yy434 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy524); /*A-overwrites-Y*/} break; case 218: /* paren_exprlist ::= LP exprlist RP */ case 223: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==223); -{yymsp[-2].minor.yy420 = yymsp[-1].minor.yy420;} +{yymsp[-2].minor.yy434 = yymsp[-1].minor.yy434;} break; case 219: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ { sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, - sqlite3SrcListAppend(pParse->db,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy420, yymsp[-10].minor.yy70, - &yymsp[-11].minor.yy0, yymsp[0].minor.yy18, SQLITE_SO_ASC, yymsp[-8].minor.yy70, SQLITE_IDXTYPE_APPDEF); + sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy434, yymsp[-10].minor.yy494, + &yymsp[-11].minor.yy0, yymsp[0].minor.yy524, SQLITE_SO_ASC, yymsp[-8].minor.yy494, SQLITE_IDXTYPE_APPDEF); if( IN_RENAME_OBJECT && pParse->pNewIndex ){ sqlite3RenameTokenMap(pParse, pParse->pNewIndex->zName, &yymsp[-4].minor.yy0); } } break; case 220: /* uniqueflag ::= UNIQUE */ - case 260: /* raisetype ::= ABORT */ yytestcase(yyruleno==260); -{yymsp[0].minor.yy70 = OE_Abort;} + case 262: /* raisetype ::= ABORT */ yytestcase(yyruleno==262); +{yymsp[0].minor.yy494 = OE_Abort;} break; case 221: /* uniqueflag ::= */ -{yymsp[1].minor.yy70 = OE_None;} +{yymsp[1].minor.yy494 = OE_None;} break; case 224: /* eidlist ::= eidlist COMMA nm collate sortorder */ { - yymsp[-4].minor.yy420 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy420, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy70, yymsp[0].minor.yy70); + yymsp[-4].minor.yy434 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy434, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy494, yymsp[0].minor.yy494); } break; case 225: /* eidlist ::= nm collate sortorder */ { - yymsp[-2].minor.yy420 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy70, yymsp[0].minor.yy70); /*A-overwrites-Y*/ + yymsp[-2].minor.yy434 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy494, yymsp[0].minor.yy494); /*A-overwrites-Y*/ } break; case 228: /* cmd ::= DROP INDEX ifexists fullname */ -{sqlite3DropIndex(pParse, yymsp[0].minor.yy135, yymsp[-1].minor.yy70);} +{sqlite3DropIndex(pParse, yymsp[0].minor.yy483, yymsp[-1].minor.yy494);} break; - case 229: /* cmd ::= VACUUM */ -{sqlite3Vacuum(pParse,0);} + case 229: /* cmd ::= VACUUM vinto */ +{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy524);} break; - case 230: /* cmd ::= VACUUM nm */ -{sqlite3Vacuum(pParse,&yymsp[0].minor.yy0);} + case 230: /* cmd ::= VACUUM nm vinto */ +{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy524);} break; - case 231: /* cmd ::= PRAGMA nm dbnm */ + case 233: /* cmd ::= PRAGMA nm dbnm */ {sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);} break; - case 232: /* cmd ::= PRAGMA nm dbnm EQ nmnum */ + case 234: /* cmd ::= PRAGMA nm dbnm EQ nmnum */ {sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,0);} break; - case 233: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */ + case 235: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */ {sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,0);} break; - case 234: /* cmd ::= PRAGMA nm dbnm EQ minus_num */ + case 236: /* cmd ::= PRAGMA nm dbnm EQ minus_num */ {sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,1);} break; - case 235: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */ + case 237: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */ {sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,1);} break; - case 238: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ + case 240: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ { Token all; all.z = yymsp[-3].minor.yy0.z; all.n = (int)(yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n; - sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy207, &all); + sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy455, &all); } break; - case 239: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + case 241: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ { - sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy70, yymsp[-4].minor.yy34.a, yymsp[-4].minor.yy34.b, yymsp[-2].minor.yy135, yymsp[0].minor.yy18, yymsp[-10].minor.yy70, yymsp[-8].minor.yy70); + sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy494, yymsp[-4].minor.yy90.a, yymsp[-4].minor.yy90.b, yymsp[-2].minor.yy483, yymsp[0].minor.yy524, yymsp[-10].minor.yy494, yymsp[-8].minor.yy494); yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/ } break; - case 240: /* trigger_time ::= BEFORE|AFTER */ -{ yymsp[0].minor.yy70 = yymsp[0].major; /*A-overwrites-X*/ } + case 242: /* trigger_time ::= BEFORE|AFTER */ +{ yymsp[0].minor.yy494 = yymsp[0].major; /*A-overwrites-X*/ } break; - case 241: /* trigger_time ::= INSTEAD OF */ -{ yymsp[-1].minor.yy70 = TK_INSTEAD;} + case 243: /* trigger_time ::= INSTEAD OF */ +{ yymsp[-1].minor.yy494 = TK_INSTEAD;} break; - case 242: /* trigger_time ::= */ -{ yymsp[1].minor.yy70 = TK_BEFORE; } + case 244: /* trigger_time ::= */ +{ yymsp[1].minor.yy494 = TK_BEFORE; } break; - case 243: /* trigger_event ::= DELETE|INSERT */ - case 244: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==244); -{yymsp[0].minor.yy34.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy34.b = 0;} + case 245: /* trigger_event ::= DELETE|INSERT */ + case 246: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==246); +{yymsp[0].minor.yy90.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy90.b = 0;} break; - case 245: /* trigger_event ::= UPDATE OF idlist */ -{yymsp[-2].minor.yy34.a = TK_UPDATE; yymsp[-2].minor.yy34.b = yymsp[0].minor.yy48;} + case 247: /* trigger_event ::= UPDATE OF idlist */ +{yymsp[-2].minor.yy90.a = TK_UPDATE; yymsp[-2].minor.yy90.b = yymsp[0].minor.yy62;} break; - case 246: /* when_clause ::= */ - case 265: /* key_opt ::= */ yytestcase(yyruleno==265); - case 307: /* filter_opt ::= */ yytestcase(yyruleno==307); -{ yymsp[1].minor.yy18 = 0; } + case 248: /* when_clause ::= */ + case 267: /* key_opt ::= */ yytestcase(yyruleno==267); + case 315: /* filter_opt ::= */ yytestcase(yyruleno==315); +{ yymsp[1].minor.yy524 = 0; } break; - case 247: /* when_clause ::= WHEN expr */ - case 266: /* key_opt ::= KEY expr */ yytestcase(yyruleno==266); -{ yymsp[-1].minor.yy18 = yymsp[0].minor.yy18; } + case 249: /* when_clause ::= WHEN expr */ + case 268: /* key_opt ::= KEY expr */ yytestcase(yyruleno==268); +{ yymsp[-1].minor.yy524 = yymsp[0].minor.yy524; } break; - case 248: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + case 250: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ { - assert( yymsp[-2].minor.yy207!=0 ); - yymsp[-2].minor.yy207->pLast->pNext = yymsp[-1].minor.yy207; - yymsp[-2].minor.yy207->pLast = yymsp[-1].minor.yy207; + assert( yymsp[-2].minor.yy455!=0 ); + yymsp[-2].minor.yy455->pLast->pNext = yymsp[-1].minor.yy455; + yymsp[-2].minor.yy455->pLast = yymsp[-1].minor.yy455; } break; - case 249: /* trigger_cmd_list ::= trigger_cmd SEMI */ + case 251: /* trigger_cmd_list ::= trigger_cmd SEMI */ { - assert( yymsp[-1].minor.yy207!=0 ); - yymsp[-1].minor.yy207->pLast = yymsp[-1].minor.yy207; + assert( yymsp[-1].minor.yy455!=0 ); + yymsp[-1].minor.yy455->pLast = yymsp[-1].minor.yy455; } break; - case 250: /* trnm ::= nm DOT nm */ + case 252: /* trnm ::= nm DOT nm */ { yymsp[-2].minor.yy0 = yymsp[0].minor.yy0; sqlite3ErrorMsg(pParse, @@ -150613,312 +152397,334 @@ static YYACTIONTYPE yy_reduce( "statements within triggers"); } break; - case 251: /* tridxby ::= INDEXED BY nm */ + case 253: /* tridxby ::= INDEXED BY nm */ { sqlite3ErrorMsg(pParse, "the INDEXED BY clause is not allowed on UPDATE or DELETE statements " "within triggers"); } break; - case 252: /* tridxby ::= NOT INDEXED */ + case 254: /* tridxby ::= NOT INDEXED */ { sqlite3ErrorMsg(pParse, "the NOT INDEXED clause is not allowed on UPDATE or DELETE statements " "within triggers"); } break; - case 253: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */ -{yylhsminor.yy207 = sqlite3TriggerUpdateStep(pParse, &yymsp[-5].minor.yy0, yymsp[-2].minor.yy420, yymsp[-1].minor.yy18, yymsp[-6].minor.yy70, yymsp[-7].minor.yy0.z, yymsp[0].minor.yy392);} - yymsp[-7].minor.yy207 = yylhsminor.yy207; + case 255: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */ +{yylhsminor.yy455 = sqlite3TriggerUpdateStep(pParse, &yymsp[-5].minor.yy0, yymsp[-2].minor.yy434, yymsp[-1].minor.yy524, yymsp[-6].minor.yy494, yymsp[-7].minor.yy0.z, yymsp[0].minor.yy294);} + yymsp[-7].minor.yy455 = yylhsminor.yy455; break; - case 254: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ + case 256: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ { - yylhsminor.yy207 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy48,yymsp[-2].minor.yy489,yymsp[-6].minor.yy70,yymsp[-1].minor.yy340,yymsp[-7].minor.yy392,yymsp[0].minor.yy392);/*yylhsminor.yy207-overwrites-yymsp[-6].minor.yy70*/ + yylhsminor.yy455 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy62,yymsp[-2].minor.yy457,yymsp[-6].minor.yy494,yymsp[-1].minor.yy136,yymsp[-7].minor.yy294,yymsp[0].minor.yy294);/*yylhsminor.yy455-overwrites-yymsp[-6].minor.yy494*/ } - yymsp[-7].minor.yy207 = yylhsminor.yy207; + yymsp[-7].minor.yy455 = yylhsminor.yy455; break; - case 255: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ -{yylhsminor.yy207 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy18, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy392);} - yymsp[-5].minor.yy207 = yylhsminor.yy207; + case 257: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ +{yylhsminor.yy455 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy524, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy294);} + yymsp[-5].minor.yy455 = yylhsminor.yy455; break; - case 256: /* trigger_cmd ::= scanpt select scanpt */ -{yylhsminor.yy207 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy489, yymsp[-2].minor.yy392, yymsp[0].minor.yy392); /*yylhsminor.yy207-overwrites-yymsp[-1].minor.yy489*/} - yymsp[-2].minor.yy207 = yylhsminor.yy207; + case 258: /* trigger_cmd ::= scanpt select scanpt */ +{yylhsminor.yy455 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy457, yymsp[-2].minor.yy294, yymsp[0].minor.yy294); /*yylhsminor.yy455-overwrites-yymsp[-1].minor.yy457*/} + yymsp[-2].minor.yy455 = yylhsminor.yy455; break; - case 257: /* expr ::= RAISE LP IGNORE RP */ + case 259: /* expr ::= RAISE LP IGNORE RP */ { - yymsp[-3].minor.yy18 = sqlite3PExpr(pParse, TK_RAISE, 0, 0); - if( yymsp[-3].minor.yy18 ){ - yymsp[-3].minor.yy18->affinity = OE_Ignore; + yymsp[-3].minor.yy524 = sqlite3PExpr(pParse, TK_RAISE, 0, 0); + if( yymsp[-3].minor.yy524 ){ + yymsp[-3].minor.yy524->affinity = OE_Ignore; } } break; - case 258: /* expr ::= RAISE LP raisetype COMMA nm RP */ + case 260: /* expr ::= RAISE LP raisetype COMMA nm RP */ { - yymsp[-5].minor.yy18 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1); - if( yymsp[-5].minor.yy18 ) { - yymsp[-5].minor.yy18->affinity = (char)yymsp[-3].minor.yy70; + yymsp[-5].minor.yy524 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1); + if( yymsp[-5].minor.yy524 ) { + yymsp[-5].minor.yy524->affinity = (char)yymsp[-3].minor.yy494; } } break; - case 259: /* raisetype ::= ROLLBACK */ -{yymsp[0].minor.yy70 = OE_Rollback;} + case 261: /* raisetype ::= ROLLBACK */ +{yymsp[0].minor.yy494 = OE_Rollback;} break; - case 261: /* raisetype ::= FAIL */ -{yymsp[0].minor.yy70 = OE_Fail;} + case 263: /* raisetype ::= FAIL */ +{yymsp[0].minor.yy494 = OE_Fail;} break; - case 262: /* cmd ::= DROP TRIGGER ifexists fullname */ + case 264: /* cmd ::= DROP TRIGGER ifexists fullname */ { - sqlite3DropTrigger(pParse,yymsp[0].minor.yy135,yymsp[-1].minor.yy70); + sqlite3DropTrigger(pParse,yymsp[0].minor.yy483,yymsp[-1].minor.yy494); } break; - case 263: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + case 265: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ { - sqlite3Attach(pParse, yymsp[-3].minor.yy18, yymsp[-1].minor.yy18, yymsp[0].minor.yy18); + sqlite3Attach(pParse, yymsp[-3].minor.yy524, yymsp[-1].minor.yy524, yymsp[0].minor.yy524); } break; - case 264: /* cmd ::= DETACH database_kw_opt expr */ + case 266: /* cmd ::= DETACH database_kw_opt expr */ { - sqlite3Detach(pParse, yymsp[0].minor.yy18); + sqlite3Detach(pParse, yymsp[0].minor.yy524); } break; - case 267: /* cmd ::= REINDEX */ + case 269: /* cmd ::= REINDEX */ {sqlite3Reindex(pParse, 0, 0);} break; - case 268: /* cmd ::= REINDEX nm dbnm */ + case 270: /* cmd ::= REINDEX nm dbnm */ {sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} break; - case 269: /* cmd ::= ANALYZE */ + case 271: /* cmd ::= ANALYZE */ {sqlite3Analyze(pParse, 0, 0);} break; - case 270: /* cmd ::= ANALYZE nm dbnm */ + case 272: /* cmd ::= ANALYZE nm dbnm */ {sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} break; - case 271: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ + case 273: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ { - sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy135,&yymsp[0].minor.yy0); + sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy483,&yymsp[0].minor.yy0); } break; - case 272: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + case 274: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ { yymsp[-1].minor.yy0.n = (int)(pParse->sLastToken.z-yymsp[-1].minor.yy0.z) + pParse->sLastToken.n; sqlite3AlterFinishAddColumn(pParse, &yymsp[-1].minor.yy0); } break; - case 273: /* add_column_fullname ::= fullname */ + case 275: /* add_column_fullname ::= fullname */ { disableLookaside(pParse); - sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy135); + sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy483); } break; - case 274: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ + case 276: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ { - sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy135, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0); + sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy483, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0); } break; - case 275: /* cmd ::= create_vtab */ + case 277: /* cmd ::= create_vtab */ {sqlite3VtabFinishParse(pParse,0);} break; - case 276: /* cmd ::= create_vtab LP vtabarglist RP */ + case 278: /* cmd ::= create_vtab LP vtabarglist RP */ {sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);} break; - case 277: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + case 279: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ { - sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy70); + sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy494); } break; - case 278: /* vtabarg ::= */ + case 280: /* vtabarg ::= */ {sqlite3VtabArgInit(pParse);} break; - case 279: /* vtabargtoken ::= ANY */ - case 280: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==280); - case 281: /* lp ::= LP */ yytestcase(yyruleno==281); + case 281: /* vtabargtoken ::= ANY */ + case 282: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==282); + case 283: /* lp ::= LP */ yytestcase(yyruleno==283); {sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);} break; - case 282: /* with ::= WITH wqlist */ - case 283: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==283); -{ sqlite3WithPush(pParse, yymsp[0].minor.yy449, 1); } + case 284: /* with ::= WITH wqlist */ + case 285: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==285); +{ sqlite3WithPush(pParse, yymsp[0].minor.yy59, 1); } break; - case 284: /* wqlist ::= nm eidlist_opt AS LP select RP */ + case 286: /* wqlist ::= nm eidlist_opt AS LP select RP */ { - yymsp[-5].minor.yy449 = sqlite3WithAdd(pParse, 0, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy420, yymsp[-1].minor.yy489); /*A-overwrites-X*/ + yymsp[-5].minor.yy59 = sqlite3WithAdd(pParse, 0, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy434, yymsp[-1].minor.yy457); /*A-overwrites-X*/ } break; - case 285: /* wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */ + case 287: /* wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */ { - yymsp[-7].minor.yy449 = sqlite3WithAdd(pParse, yymsp[-7].minor.yy449, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy420, yymsp[-1].minor.yy489); + yymsp[-7].minor.yy59 = sqlite3WithAdd(pParse, yymsp[-7].minor.yy59, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy434, yymsp[-1].minor.yy457); } break; - case 286: /* windowdefn_list ::= windowdefn */ -{ yylhsminor.yy327 = yymsp[0].minor.yy327; } - yymsp[0].minor.yy327 = yylhsminor.yy327; + case 288: /* windowdefn_list ::= windowdefn */ +{ yylhsminor.yy295 = yymsp[0].minor.yy295; } + yymsp[0].minor.yy295 = yylhsminor.yy295; break; - case 287: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ + case 289: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ { - assert( yymsp[0].minor.yy327!=0 ); - yymsp[0].minor.yy327->pNextWin = yymsp[-2].minor.yy327; - yylhsminor.yy327 = yymsp[0].minor.yy327; + assert( yymsp[0].minor.yy295!=0 ); + sqlite3WindowChain(pParse, yymsp[0].minor.yy295, yymsp[-2].minor.yy295); + yymsp[0].minor.yy295->pNextWin = yymsp[-2].minor.yy295; + yylhsminor.yy295 = yymsp[0].minor.yy295; } - yymsp[-2].minor.yy327 = yylhsminor.yy327; + yymsp[-2].minor.yy295 = yylhsminor.yy295; break; - case 288: /* windowdefn ::= nm AS window */ + case 290: /* windowdefn ::= nm AS LP window RP */ { - if( ALWAYS(yymsp[0].minor.yy327) ){ - yymsp[0].minor.yy327->zName = sqlite3DbStrNDup(pParse->db, yymsp[-2].minor.yy0.z, yymsp[-2].minor.yy0.n); + if( ALWAYS(yymsp[-1].minor.yy295) ){ + yymsp[-1].minor.yy295->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n); } - yylhsminor.yy327 = yymsp[0].minor.yy327; + yylhsminor.yy295 = yymsp[-1].minor.yy295; } - yymsp[-2].minor.yy327 = yylhsminor.yy327; + yymsp[-4].minor.yy295 = yylhsminor.yy295; break; - case 289: /* window ::= LP part_opt orderby_opt frame_opt RP */ + case 291: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */ { - yymsp[-4].minor.yy327 = yymsp[-1].minor.yy327; - if( ALWAYS(yymsp[-4].minor.yy327) ){ - yymsp[-4].minor.yy327->pPartition = yymsp[-3].minor.yy420; - yymsp[-4].minor.yy327->pOrderBy = yymsp[-2].minor.yy420; - } + yymsp[-4].minor.yy295 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy295, yymsp[-2].minor.yy434, yymsp[-1].minor.yy434, 0); } break; - case 290: /* part_opt ::= PARTITION BY nexprlist */ -{ yymsp[-2].minor.yy420 = yymsp[0].minor.yy420; } + case 292: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ +{ + yylhsminor.yy295 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy295, yymsp[-2].minor.yy434, yymsp[-1].minor.yy434, &yymsp[-5].minor.yy0); +} + yymsp[-5].minor.yy295 = yylhsminor.yy295; break; - case 291: /* part_opt ::= */ -{ yymsp[1].minor.yy420 = 0; } + case 293: /* window ::= ORDER BY sortlist frame_opt */ +{ + yymsp[-3].minor.yy295 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy295, 0, yymsp[-1].minor.yy434, 0); +} + break; + case 294: /* window ::= nm ORDER BY sortlist frame_opt */ +{ + yylhsminor.yy295 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy295, 0, yymsp[-1].minor.yy434, &yymsp[-4].minor.yy0); +} + yymsp[-4].minor.yy295 = yylhsminor.yy295; break; - case 292: /* frame_opt ::= */ + case 295: /* window ::= frame_opt */ +{ + yylhsminor.yy295 = yymsp[0].minor.yy295; +} + yymsp[0].minor.yy295 = yylhsminor.yy295; + break; + case 296: /* window ::= nm frame_opt */ +{ + yylhsminor.yy295 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy295, 0, 0, &yymsp[-1].minor.yy0); +} + yymsp[-1].minor.yy295 = yylhsminor.yy295; + break; + case 297: /* frame_opt ::= */ { - yymsp[1].minor.yy327 = sqlite3WindowAlloc(pParse, TK_RANGE, TK_UNBOUNDED, 0, TK_CURRENT, 0); + yymsp[1].minor.yy295 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0); } break; - case 293: /* frame_opt ::= range_or_rows frame_bound_s */ + case 298: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ { - yylhsminor.yy327 = sqlite3WindowAlloc(pParse, yymsp[-1].minor.yy70, yymsp[0].minor.yy119.eType, yymsp[0].minor.yy119.pExpr, TK_CURRENT, 0); + yylhsminor.yy295 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy494, yymsp[-1].minor.yy201.eType, yymsp[-1].minor.yy201.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy238); } - yymsp[-1].minor.yy327 = yylhsminor.yy327; + yymsp[-2].minor.yy295 = yylhsminor.yy295; break; - case 294: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e */ + case 299: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ { - yylhsminor.yy327 = sqlite3WindowAlloc(pParse, yymsp[-4].minor.yy70, yymsp[-2].minor.yy119.eType, yymsp[-2].minor.yy119.pExpr, yymsp[0].minor.yy119.eType, yymsp[0].minor.yy119.pExpr); + yylhsminor.yy295 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy494, yymsp[-3].minor.yy201.eType, yymsp[-3].minor.yy201.pExpr, yymsp[-1].minor.yy201.eType, yymsp[-1].minor.yy201.pExpr, yymsp[0].minor.yy238); } - yymsp[-4].minor.yy327 = yylhsminor.yy327; + yymsp[-5].minor.yy295 = yylhsminor.yy295; break; - case 295: /* range_or_rows ::= RANGE */ -{ yymsp[0].minor.yy70 = TK_RANGE; } + case 301: /* frame_bound_s ::= frame_bound */ + case 303: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==303); +{yylhsminor.yy201 = yymsp[0].minor.yy201;} + yymsp[0].minor.yy201 = yylhsminor.yy201; break; - case 296: /* range_or_rows ::= ROWS */ -{ yymsp[0].minor.yy70 = TK_ROWS; } + case 302: /* frame_bound_s ::= UNBOUNDED PRECEDING */ + case 304: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==304); + case 306: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==306); +{yylhsminor.yy201.eType = yymsp[-1].major; yylhsminor.yy201.pExpr = 0;} + yymsp[-1].minor.yy201 = yylhsminor.yy201; break; - case 297: /* frame_bound_s ::= frame_bound */ - case 299: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==299); -{ yylhsminor.yy119 = yymsp[0].minor.yy119; } - yymsp[0].minor.yy119 = yylhsminor.yy119; + case 305: /* frame_bound ::= expr PRECEDING|FOLLOWING */ +{yylhsminor.yy201.eType = yymsp[0].major; yylhsminor.yy201.pExpr = yymsp[-1].minor.yy524;} + yymsp[-1].minor.yy201 = yylhsminor.yy201; break; - case 298: /* frame_bound_s ::= UNBOUNDED PRECEDING */ - case 300: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==300); -{yymsp[-1].minor.yy119.eType = TK_UNBOUNDED; yymsp[-1].minor.yy119.pExpr = 0;} + case 307: /* frame_exclude_opt ::= */ +{yymsp[1].minor.yy238 = 0;} break; - case 301: /* frame_bound ::= expr PRECEDING */ -{ yylhsminor.yy119.eType = TK_PRECEDING; yylhsminor.yy119.pExpr = yymsp[-1].minor.yy18; } - yymsp[-1].minor.yy119 = yylhsminor.yy119; + case 308: /* frame_exclude_opt ::= EXCLUDE frame_exclude */ +{yymsp[-1].minor.yy238 = yymsp[0].minor.yy238;} break; - case 302: /* frame_bound ::= CURRENT ROW */ -{ yymsp[-1].minor.yy119.eType = TK_CURRENT ; yymsp[-1].minor.yy119.pExpr = 0; } + case 309: /* frame_exclude ::= NO OTHERS */ + case 310: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==310); +{yymsp[-1].minor.yy238 = yymsp[-1].major; /*A-overwrites-X*/} break; - case 303: /* frame_bound ::= expr FOLLOWING */ -{ yylhsminor.yy119.eType = TK_FOLLOWING; yylhsminor.yy119.pExpr = yymsp[-1].minor.yy18; } - yymsp[-1].minor.yy119 = yylhsminor.yy119; + case 311: /* frame_exclude ::= GROUP|TIES */ +{yymsp[0].minor.yy238 = yymsp[0].major; /*A-overwrites-X*/} break; - case 304: /* window_clause ::= WINDOW windowdefn_list */ -{ yymsp[-1].minor.yy327 = yymsp[0].minor.yy327; } + case 312: /* window_clause ::= WINDOW windowdefn_list */ +{ yymsp[-1].minor.yy295 = yymsp[0].minor.yy295; } break; - case 305: /* over_clause ::= filter_opt OVER window */ + case 313: /* over_clause ::= filter_opt OVER LP window RP */ { - yylhsminor.yy327 = yymsp[0].minor.yy327; - assert( yylhsminor.yy327!=0 ); - yylhsminor.yy327->pFilter = yymsp[-2].minor.yy18; + yylhsminor.yy295 = yymsp[-1].minor.yy295; + assert( yylhsminor.yy295!=0 ); + yylhsminor.yy295->pFilter = yymsp[-4].minor.yy524; } - yymsp[-2].minor.yy327 = yylhsminor.yy327; + yymsp[-4].minor.yy295 = yylhsminor.yy295; break; - case 306: /* over_clause ::= filter_opt OVER nm */ + case 314: /* over_clause ::= filter_opt OVER nm */ { - yylhsminor.yy327 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); - if( yylhsminor.yy327 ){ - yylhsminor.yy327->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n); - yylhsminor.yy327->pFilter = yymsp[-2].minor.yy18; + yylhsminor.yy295 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); + if( yylhsminor.yy295 ){ + yylhsminor.yy295->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n); + yylhsminor.yy295->pFilter = yymsp[-2].minor.yy524; }else{ - sqlite3ExprDelete(pParse->db, yymsp[-2].minor.yy18); + sqlite3ExprDelete(pParse->db, yymsp[-2].minor.yy524); } } - yymsp[-2].minor.yy327 = yylhsminor.yy327; + yymsp[-2].minor.yy295 = yylhsminor.yy295; break; - case 308: /* filter_opt ::= FILTER LP WHERE expr RP */ -{ yymsp[-4].minor.yy18 = yymsp[-1].minor.yy18; } + case 316: /* filter_opt ::= FILTER LP WHERE expr RP */ +{ yymsp[-4].minor.yy524 = yymsp[-1].minor.yy524; } break; default: - /* (309) input ::= cmdlist */ yytestcase(yyruleno==309); - /* (310) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==310); - /* (311) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=311); - /* (312) ecmd ::= SEMI */ yytestcase(yyruleno==312); - /* (313) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==313); - /* (314) ecmd ::= explain cmdx */ yytestcase(yyruleno==314); - /* (315) trans_opt ::= */ yytestcase(yyruleno==315); - /* (316) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==316); - /* (317) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==317); - /* (318) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==318); - /* (319) savepoint_opt ::= */ yytestcase(yyruleno==319); - /* (320) cmd ::= create_table create_table_args */ yytestcase(yyruleno==320); - /* (321) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==321); - /* (322) columnlist ::= columnname carglist */ yytestcase(yyruleno==322); - /* (323) nm ::= ID|INDEXED */ yytestcase(yyruleno==323); - /* (324) nm ::= STRING */ yytestcase(yyruleno==324); - /* (325) nm ::= JOIN_KW */ yytestcase(yyruleno==325); - /* (326) typetoken ::= typename */ yytestcase(yyruleno==326); - /* (327) typename ::= ID|STRING */ yytestcase(yyruleno==327); - /* (328) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=328); - /* (329) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=329); - /* (330) carglist ::= carglist ccons */ yytestcase(yyruleno==330); - /* (331) carglist ::= */ yytestcase(yyruleno==331); - /* (332) ccons ::= NULL onconf */ yytestcase(yyruleno==332); - /* (333) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==333); - /* (334) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==334); - /* (335) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=335); - /* (336) tconscomma ::= */ yytestcase(yyruleno==336); - /* (337) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=337); - /* (338) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=338); - /* (339) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=339); - /* (340) oneselect ::= values */ yytestcase(yyruleno==340); - /* (341) sclp ::= selcollist COMMA */ yytestcase(yyruleno==341); - /* (342) as ::= ID|STRING */ yytestcase(yyruleno==342); - /* (343) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=343); - /* (344) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==344); - /* (345) exprlist ::= nexprlist */ yytestcase(yyruleno==345); - /* (346) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=346); - /* (347) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=347); - /* (348) nmnum ::= ON */ yytestcase(yyruleno==348); - /* (349) nmnum ::= DELETE */ yytestcase(yyruleno==349); - /* (350) nmnum ::= DEFAULT */ yytestcase(yyruleno==350); - /* (351) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==351); - /* (352) foreach_clause ::= */ yytestcase(yyruleno==352); - /* (353) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==353); - /* (354) trnm ::= nm */ yytestcase(yyruleno==354); - /* (355) tridxby ::= */ yytestcase(yyruleno==355); - /* (356) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==356); - /* (357) database_kw_opt ::= */ yytestcase(yyruleno==357); - /* (358) kwcolumn_opt ::= */ yytestcase(yyruleno==358); - /* (359) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==359); - /* (360) vtabarglist ::= vtabarg */ yytestcase(yyruleno==360); - /* (361) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==361); - /* (362) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==362); - /* (363) anylist ::= */ yytestcase(yyruleno==363); - /* (364) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==364); - /* (365) anylist ::= anylist ANY */ yytestcase(yyruleno==365); - /* (366) with ::= */ yytestcase(yyruleno==366); + /* (317) input ::= cmdlist */ yytestcase(yyruleno==317); + /* (318) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==318); + /* (319) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=319); + /* (320) ecmd ::= SEMI */ yytestcase(yyruleno==320); + /* (321) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==321); + /* (322) ecmd ::= explain cmdx */ yytestcase(yyruleno==322); + /* (323) trans_opt ::= */ yytestcase(yyruleno==323); + /* (324) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==324); + /* (325) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==325); + /* (326) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==326); + /* (327) savepoint_opt ::= */ yytestcase(yyruleno==327); + /* (328) cmd ::= create_table create_table_args */ yytestcase(yyruleno==328); + /* (329) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==329); + /* (330) columnlist ::= columnname carglist */ yytestcase(yyruleno==330); + /* (331) nm ::= ID|INDEXED */ yytestcase(yyruleno==331); + /* (332) nm ::= STRING */ yytestcase(yyruleno==332); + /* (333) nm ::= JOIN_KW */ yytestcase(yyruleno==333); + /* (334) typetoken ::= typename */ yytestcase(yyruleno==334); + /* (335) typename ::= ID|STRING */ yytestcase(yyruleno==335); + /* (336) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=336); + /* (337) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=337); + /* (338) carglist ::= carglist ccons */ yytestcase(yyruleno==338); + /* (339) carglist ::= */ yytestcase(yyruleno==339); + /* (340) ccons ::= NULL onconf */ yytestcase(yyruleno==340); + /* (341) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==341); + /* (342) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==342); + /* (343) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=343); + /* (344) tconscomma ::= */ yytestcase(yyruleno==344); + /* (345) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=345); + /* (346) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=346); + /* (347) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=347); + /* (348) oneselect ::= values */ yytestcase(yyruleno==348); + /* (349) sclp ::= selcollist COMMA */ yytestcase(yyruleno==349); + /* (350) as ::= ID|STRING */ yytestcase(yyruleno==350); + /* (351) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=351); + /* (352) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==352); + /* (353) exprlist ::= nexprlist */ yytestcase(yyruleno==353); + /* (354) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=354); + /* (355) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=355); + /* (356) nmnum ::= ON */ yytestcase(yyruleno==356); + /* (357) nmnum ::= DELETE */ yytestcase(yyruleno==357); + /* (358) nmnum ::= DEFAULT */ yytestcase(yyruleno==358); + /* (359) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==359); + /* (360) foreach_clause ::= */ yytestcase(yyruleno==360); + /* (361) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==361); + /* (362) trnm ::= nm */ yytestcase(yyruleno==362); + /* (363) tridxby ::= */ yytestcase(yyruleno==363); + /* (364) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==364); + /* (365) database_kw_opt ::= */ yytestcase(yyruleno==365); + /* (366) kwcolumn_opt ::= */ yytestcase(yyruleno==366); + /* (367) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==367); + /* (368) vtabarglist ::= vtabarg */ yytestcase(yyruleno==368); + /* (369) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==369); + /* (370) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==370); + /* (371) anylist ::= */ yytestcase(yyruleno==371); + /* (372) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==372); + /* (373) anylist ::= anylist ANY */ yytestcase(yyruleno==373); + /* (374) with ::= */ yytestcase(yyruleno==374); break; /********** End reduce actions ************************************************/ }; - assert( yyruleno=SQLITE_N_KEYWORD ) return SQLITE_ERROR; *pzName = zKWText + aKWOffset[i]; @@ -152095,73 +153905,6 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){ return i; } -#ifdef SQLITE_ENABLE_NORMALIZE -/* -** Return the length (in bytes) of the token that begins at z[0]. -** Store the token type in *tokenType before returning. If flags has -** SQLITE_TOKEN_NORMALIZE flag enabled, use the identifier token type -** for keywords. Add SQLITE_TOKEN_QUOTED to flags if the token was -** actually a quoted identifier. Add SQLITE_TOKEN_KEYWORD to flags -** if the token was recognized as a keyword; this is useful when the -** SQLITE_TOKEN_NORMALIZE flag is used, because it enables the caller -** to differentiate between a keyword being treated as an identifier -** (for normalization purposes) and an actual identifier. -*/ -SQLITE_PRIVATE int sqlite3GetTokenNormalized( - const unsigned char *z, - int *tokenType, - int *flags -){ - int n; - unsigned char iClass = aiClass[*z]; - if( iClass==CC_KYWD ){ - int i; - for(i=1; aiClass[z[i]]<=CC_KYWD; i++){} - if( IdChar(z[i]) ){ - /* This token started out using characters that can appear in keywords, - ** but z[i] is a character not allowed within keywords, so this must - ** be an identifier instead */ - i++; - while( IdChar(z[i]) ){ i++; } - *tokenType = TK_ID; - return i; - } - *tokenType = TK_ID; - n = keywordCode((char*)z, i, tokenType); - /* If the token is no longer considered to be an identifier, then it is a - ** keyword of some kind. Make the token back into an identifier and then - ** set the SQLITE_TOKEN_KEYWORD flag. Several non-identifier tokens are - ** used verbatim, including IN, IS, NOT, and NULL. */ - switch( *tokenType ){ - case TK_ID: { - /* do nothing, handled by caller */ - break; - } - case TK_IN: - case TK_IS: - case TK_NOT: - case TK_NULL: { - *flags |= SQLITE_TOKEN_KEYWORD; - break; - } - default: { - *tokenType = TK_ID; - *flags |= SQLITE_TOKEN_KEYWORD; - break; - } - } - }else{ - n = sqlite3GetToken(z, tokenType); - /* If the token is considered to be an identifier and the character class - ** of the first character is a quote, set the SQLITE_TOKEN_QUOTED flag. */ - if( *tokenType==TK_ID && (iClass==CC_QUOTE || iClass==CC_QUOTE2) ){ - *flags |= SQLITE_TOKEN_QUOTED; - } - } - return n; -} -#endif /* SQLITE_ENABLE_NORMALIZE */ - /* ** Run the parser on the given SQL string. The parser structure is ** passed in. An SQLITE_ status code is returned. If an error occurs @@ -152180,6 +153923,7 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr #ifdef sqlite3Parser_ENGINEALWAYSONSTACK yyParser sEngine; /* Space to hold the Lemon-generated Parser object */ #endif + VVA_ONLY( u8 startedWithOom = db->mallocFailed ); assert( zSql!=0 ); mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH]; @@ -152189,7 +153933,14 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr pParse->rc = SQLITE_OK; pParse->zTail = zSql; assert( pzErrMsg!=0 ); - /* sqlite3ParserTrace(stdout, "parser: "); */ +#ifdef SQLITE_DEBUG + if( db->flags & SQLITE_ParserTrace ){ + printf("parser: [[[%s]]]\n", zSql); + sqlite3ParserTrace(stdout, "parser: "); + }else{ + sqlite3ParserTrace(0, 0); + } +#endif #ifdef sqlite3Parser_ENGINEALWAYSONSTACK pEngine = &sEngine; sqlite3ParserInit(pEngine, pParse); @@ -152204,6 +153955,8 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr assert( pParse->pNewTrigger==0 ); assert( pParse->nVar==0 ); assert( pParse->pVList==0 ); + pParse->pParentParse = db->pParse; + db->pParse = pParse; while( 1 ){ n = sqlite3GetToken((u8*)zSql, &tokenType); mxSqlLen -= n; @@ -152260,7 +154013,8 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr sqlite3Parser(pEngine, tokenType, pParse->sLastToken); lastTokenParsed = tokenType; zSql += n; - if( pParse->rc!=SQLITE_OK || db->mallocFailed ) break; + assert( db->mallocFailed==0 || pParse->rc!=SQLITE_OK || startedWithOom ); + if( pParse->rc!=SQLITE_OK ) break; } assert( nErr==0 ); #ifdef YYTRACKMAXSTACKDEPTH @@ -152328,10 +154082,147 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr pParse->pZombieTab = p->pNextZombie; sqlite3DeleteTable(db, p); } + db->pParse = pParse->pParentParse; + pParse->pParentParse = 0; assert( nErr==0 || pParse->rc!=SQLITE_OK ); return nErr; } + +#ifdef SQLITE_ENABLE_NORMALIZE +/* +** Insert a single space character into pStr if the current string +** ends with an identifier +*/ +static void addSpaceSeparator(sqlite3_str *pStr){ + if( pStr->nChar && sqlite3IsIdChar(pStr->zText[pStr->nChar-1]) ){ + sqlite3_str_append(pStr, " ", 1); + } +} + +/* +** Compute a normalization of the SQL given by zSql[0..nSql-1]. Return +** the normalization in space obtained from sqlite3DbMalloc(). Or return +** NULL if anything goes wrong or if zSql is NULL. +*/ +SQLITE_PRIVATE char *sqlite3Normalize( + Vdbe *pVdbe, /* VM being reprepared */ + const char *zSql /* The original SQL string */ +){ + sqlite3 *db; /* The database connection */ + int i; /* Next unread byte of zSql[] */ + int n; /* length of current token */ + int tokenType; /* type of current token */ + int prevType = 0; /* Previous non-whitespace token */ + int nParen; /* Number of nested levels of parentheses */ + int iStartIN; /* Start of RHS of IN operator in z[] */ + int nParenAtIN; /* Value of nParent at start of RHS of IN operator */ + int j; /* Bytes of normalized SQL generated so far */ + sqlite3_str *pStr; /* The normalized SQL string under construction */ + + db = sqlite3VdbeDb(pVdbe); + tokenType = -1; + nParen = iStartIN = nParenAtIN = 0; + pStr = sqlite3_str_new(db); + assert( pStr!=0 ); /* sqlite3_str_new() never returns NULL */ + for(i=0; zSql[i] && pStr->accError==0; i+=n){ + if( tokenType!=TK_SPACE ){ + prevType = tokenType; + } + n = sqlite3GetToken((unsigned char*)zSql+i, &tokenType); + if( NEVER(n<=0) ) break; + switch( tokenType ){ + case TK_SPACE: { + break; + } + case TK_NULL: { + if( prevType==TK_IS || prevType==TK_NOT ){ + sqlite3_str_append(pStr, " NULL", 5); + break; + } + /* Fall through */ + } + case TK_STRING: + case TK_INTEGER: + case TK_FLOAT: + case TK_VARIABLE: + case TK_BLOB: { + sqlite3_str_append(pStr, "?", 1); + break; + } + case TK_LP: { + nParen++; + if( prevType==TK_IN ){ + iStartIN = pStr->nChar; + nParenAtIN = nParen; + } + sqlite3_str_append(pStr, "(", 1); + break; + } + case TK_RP: { + if( iStartIN>0 && nParen==nParenAtIN ){ + assert( pStr->nChar>=iStartIN ); + pStr->nChar = iStartIN+1; + sqlite3_str_append(pStr, "?,?,?", 5); + iStartIN = 0; + } + nParen--; + sqlite3_str_append(pStr, ")", 1); + break; + } + case TK_ID: { + iStartIN = 0; + j = pStr->nChar; + if( sqlite3Isquote(zSql[i]) ){ + char *zId = sqlite3DbStrNDup(db, zSql+i, n); + int nId; + int eType = 0; + if( zId==0 ) break; + sqlite3Dequote(zId); + if( zSql[i]=='"' && sqlite3VdbeUsesDoubleQuotedString(pVdbe, zId) ){ + sqlite3_str_append(pStr, "?", 1); + sqlite3DbFree(db, zId); + break; + } + nId = sqlite3Strlen30(zId); + if( sqlite3GetToken((u8*)zId, &eType)==nId && eType==TK_ID ){ + addSpaceSeparator(pStr); + sqlite3_str_append(pStr, zId, nId); + }else{ + sqlite3_str_appendf(pStr, "\"%w\"", zId); + } + sqlite3DbFree(db, zId); + }else{ + addSpaceSeparator(pStr); + sqlite3_str_append(pStr, zSql+i, n); + } + while( jnChar ){ + pStr->zText[j] = sqlite3Tolower(pStr->zText[j]); + j++; + } + break; + } + case TK_SELECT: { + iStartIN = 0; + /* fall through */ + } + default: { + if( sqlite3IsIdChar(zSql[i]) ) addSpaceSeparator(pStr); + j = pStr->nChar; + sqlite3_str_append(pStr, zSql+i, n); + while( jnChar ){ + pStr->zText[j] = sqlite3Toupper(pStr->zText[j]); + j++; + } + break; + } + } + } + if( tokenType!=TK_SEMI ) sqlite3_str_append(pStr, ";", 1); + return sqlite3_str_finish(pStr); +} +#endif /* SQLITE_ENABLE_NORMALIZE */ + /************** End of tokenize.c ********************************************/ /************** Begin file complete.c ****************************************/ /* @@ -153377,6 +155268,13 @@ SQLITE_API int sqlite3_config(int op, ...){ } #endif /* SQLITE_ENABLE_SORTER_REFERENCES */ +#ifdef SQLITE_ENABLE_DESERIALIZE + case SQLITE_CONFIG_MEMDB_MAXSIZE: { + sqlite3GlobalConfig.mxMemdbSize = va_arg(ap, sqlite3_int64); + break; + } +#endif /* SQLITE_ENABLE_DESERIALIZE */ + default: { rc = SQLITE_ERROR; break; @@ -153422,7 +155320,7 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){ pStart = 0; }else if( pBuf==0 ){ sqlite3BeginBenignMalloc(); - pStart = sqlite3Malloc( sz*cnt ); /* IMP: R-61949-35727 */ + pStart = sqlite3Malloc( sz*(sqlite3_int64)cnt ); /* IMP: R-61949-35727 */ sqlite3EndBenignMalloc(); if( pStart ) cnt = sqlite3MallocSize(pStart)/sz; }else{ @@ -153560,6 +155458,8 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){ { SQLITE_DBCONFIG_TRIGGER_EQP, SQLITE_TriggerEQP }, { SQLITE_DBCONFIG_RESET_DATABASE, SQLITE_ResetDatabase }, { SQLITE_DBCONFIG_DEFENSIVE, SQLITE_Defensive }, + { SQLITE_DBCONFIG_WRITABLE_SCHEMA, SQLITE_WriteSchema| + SQLITE_NoSchemaError }, }; unsigned int i; rc = SQLITE_ERROR; /* IMP: R-42790-23372 */ @@ -153567,11 +155467,11 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){ if( aFlagOp[i].op==op ){ int onoff = va_arg(ap, int); int *pRes = va_arg(ap, int*); - u32 oldFlags = db->flags; + u64 oldFlags = db->flags; if( onoff>0 ){ db->flags |= aFlagOp[i].mask; }else if( onoff==0 ){ - db->flags &= ~aFlagOp[i].mask; + db->flags &= ~(u64)aFlagOp[i].mask; } if( oldFlags!=db->flags ){ sqlite3ExpirePreparedStatements(db, 0); @@ -154034,7 +155934,7 @@ SQLITE_PRIVATE void sqlite3RollbackAll(sqlite3 *db, int tripCode){ /* Any deferred constraint violations have now been resolved. */ db->nDeferredCons = 0; db->nDeferredImmCons = 0; - db->flags &= ~SQLITE_DeferFKs; + db->flags &= ~(u64)SQLITE_DeferFKs; /* If one has been configured, invoke the rollback-hook callback */ if( db->xRollbackCallback && (inTrans || !db->autoCommit) ){ @@ -154776,6 +156676,8 @@ SQLITE_API void *sqlite3_profile( pOld = db->pProfileArg; db->xProfile = xProfile; db->pProfileArg = pArg; + db->mTrace &= SQLITE_TRACE_NONLEGACY_MASK; + if( db->xProfile ) db->mTrace |= SQLITE_TRACE_XPROFILE; sqlite3_mutex_leave(db->mutex); return pOld; } @@ -155127,7 +157029,7 @@ SQLITE_API const char *sqlite3_errmsg(sqlite3 *db){ z = sqlite3ErrStr(SQLITE_NOMEM_BKPT); }else{ testcase( db->pErr==0 ); - z = (char*)sqlite3_value_text(db->pErr); + z = db->errCode ? (char*)sqlite3_value_text(db->pErr) : 0; assert( !db->mallocFailed ); if( z==0 ){ z = sqlite3ErrStr(db->errCode); @@ -155657,6 +157559,40 @@ SQLITE_PRIVATE int sqlite3ParseUri( return rc; } +#if defined(SQLITE_HAS_CODEC) +/* +** Process URI filename query parameters relevant to the SQLite Encryption +** Extension. Return true if any of the relevant query parameters are +** seen and return false if not. +*/ +SQLITE_PRIVATE int sqlite3CodecQueryParameters( + sqlite3 *db, /* Database connection */ + const char *zDb, /* Which schema is being created/attached */ + const char *zUri /* URI filename */ +){ + const char *zKey; + if( (zKey = sqlite3_uri_parameter(zUri, "hexkey"))!=0 && zKey[0] ){ + u8 iByte; + int i; + char zDecoded[40]; + for(i=0, iByte=0; i=0 ); return 5; @@ -158878,13 +160824,18 @@ static int fts3DestroyMethod(sqlite3_vtab *pVtab){ sqlite3 *db = p->db; /* Database handle */ /* Drop the shadow tables */ - if( p->zContentTbl==0 ){ - fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_content'", zDb, p->zName); - } - fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_segments'", zDb,p->zName); - fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_segdir'", zDb, p->zName); - fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_docsize'", zDb, p->zName); - fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_stat'", zDb, p->zName); + fts3DbExec(&rc, db, + "DROP TABLE IF EXISTS %Q.'%q_segments';" + "DROP TABLE IF EXISTS %Q.'%q_segdir';" + "DROP TABLE IF EXISTS %Q.'%q_docsize';" + "DROP TABLE IF EXISTS %Q.'%q_stat';" + "%s DROP TABLE IF EXISTS %Q.'%q_content';", + zDb, p->zName, + zDb, p->zName, + zDb, p->zName, + zDb, p->zName, + (p->zContentTbl ? "--" : ""), zDb,p->zName + ); /* If everything has worked, invoke fts3DisconnectMethod() to free the ** memory associated with the Fts3Table structure and return SQLITE_OK. @@ -159116,10 +161067,10 @@ static void fts3Appendf( ** memory. */ static char *fts3QuoteId(char const *zInput){ - int nRet; + sqlite3_int64 nRet; char *zRet; nRet = 2 + (int)strlen(zInput)*2 + 1; - zRet = sqlite3_malloc(nRet); + zRet = sqlite3_malloc64(nRet); if( zRet ){ int i; char *z = zRet; @@ -159300,7 +161251,7 @@ static int fts3PrefixParameter( } } - aIndex = sqlite3_malloc(sizeof(struct Fts3Index) * nIndex); + aIndex = sqlite3_malloc64(sizeof(struct Fts3Index) * nIndex); *apIndex = aIndex; if( !aIndex ){ return SQLITE_NOMEM; @@ -159379,7 +161330,7 @@ static int fts3ContentColumns( if( rc==SQLITE_OK ){ const char **azCol; /* Output array */ - int nStr = 0; /* Size of all column names (incl. 0x00) */ + sqlite3_int64 nStr = 0; /* Size of all column names (incl. 0x00) */ int nCol; /* Number of table columns */ int i; /* Used to iterate through columns */ @@ -159389,11 +161340,11 @@ static int fts3ContentColumns( nCol = sqlite3_column_count(pStmt); for(i=0; i=0 && nSuffix>=0 ); - if( nPrefix>zCsr-zNode || nSuffix>zEnd-zCsr ){ + if( nPrefix>zCsr-zNode || nSuffix>zEnd-zCsr || nSuffix==0 ){ rc = FTS_CORRUPT_VTAB; goto finish_scan; } @@ -160452,7 +162403,7 @@ static int fts3PutColNumber(char **pp, int iCol){ ** updated appropriately. The caller is responsible for insuring ** that there is enough space in *pp to hold the complete output. */ -static void fts3PoslistMerge( +static int fts3PoslistMerge( char **pp, /* Output buffer */ char **pp1, /* Left input list */ char **pp2 /* Right input list */ @@ -160465,11 +162416,17 @@ static void fts3PoslistMerge( int iCol1; /* The current column index in pp1 */ int iCol2; /* The current column index in pp2 */ - if( *p1==POS_COLUMN ) fts3GetVarint32(&p1[1], &iCol1); + if( *p1==POS_COLUMN ){ + fts3GetVarint32(&p1[1], &iCol1); + if( iCol1==0 ) return FTS_CORRUPT_VTAB; + } else if( *p1==POS_END ) iCol1 = POSITION_LIST_END; else iCol1 = 0; - if( *p2==POS_COLUMN ) fts3GetVarint32(&p2[1], &iCol2); + if( *p2==POS_COLUMN ){ + fts3GetVarint32(&p2[1], &iCol2); + if( iCol2==0 ) return FTS_CORRUPT_VTAB; + } else if( *p2==POS_END ) iCol2 = POSITION_LIST_END; else iCol2 = 0; @@ -160517,6 +162474,7 @@ static void fts3PoslistMerge( *pp = p; *pp1 = p1 + 1; *pp2 = p2 + 1; + return SQLITE_OK; } /* @@ -160581,10 +162539,9 @@ static int fts3PoslistPhraseMerge( p += sqlite3Fts3PutVarint(p, iCol1); } - assert( *p1!=POS_END && *p1!=POS_COLUMN ); - assert( *p2!=POS_END && *p2!=POS_COLUMN ); fts3GetDeltaVarint(&p1, &iPos1); iPos1 -= 2; fts3GetDeltaVarint(&p2, &iPos2); iPos2 -= 2; + if( iPos1<0 || iPos2<0 ) break; while( 1 ){ if( iPos2==iPos1+nToken @@ -160810,6 +162767,7 @@ static int fts3DoclistOrMerge( char *a2, int n2, /* Second doclist */ char **paOut, int *pnOut /* OUT: Malloc'd doclist */ ){ + int rc = SQLITE_OK; sqlite3_int64 i1 = 0; sqlite3_int64 i2 = 0; sqlite3_int64 iPrev = 0; @@ -160853,7 +162811,7 @@ static int fts3DoclistOrMerge( ** A symetric argument may be made if the doclists are in descending ** order. */ - aOut = sqlite3_malloc(n1+n2+FTS3_VARINT_MAX-1); + aOut = sqlite3_malloc64((i64)n1+n2+FTS3_VARINT_MAX-1+FTS3_BUFFER_PADDING); if( !aOut ) return SQLITE_NOMEM; p = aOut; @@ -160864,7 +162822,8 @@ static int fts3DoclistOrMerge( if( p2 && p1 && iDiff==0 ){ fts3PutDeltaVarint3(&p, bDescDoclist, &iPrev, &bFirstOut, i1); - fts3PoslistMerge(&p, &p1, &p2); + rc = fts3PoslistMerge(&p, &p1, &p2); + if( rc ) break; fts3GetDeltaVarint3(&p1, pEnd1, bDescDoclist, &i1); fts3GetDeltaVarint3(&p2, pEnd2, bDescDoclist, &i2); }else if( !p2 || (p1 && iDiff<0) ){ @@ -160878,10 +162837,16 @@ static int fts3DoclistOrMerge( } } + if( rc!=SQLITE_OK ){ + sqlite3_free(aOut); + p = aOut = 0; + }else{ + assert( (p-aOut)<=n1+n2+FTS3_VARINT_MAX-1 ); + memset(&aOut[(p-aOut)], 0, FTS3_BUFFER_PADDING); + } *paOut = aOut; *pnOut = (int)(p-aOut); - assert( *pnOut<=n1+n2+FTS3_VARINT_MAX-1 ); - return SQLITE_OK; + return rc; } /* @@ -160916,7 +162881,7 @@ static int fts3DoclistPhraseMerge( assert( nDist>0 ); if( bDescDoclist ){ - aOut = sqlite3_malloc(*pnRight + FTS3_VARINT_MAX); + aOut = sqlite3_malloc64((sqlite3_int64)*pnRight + FTS3_VARINT_MAX); if( aOut==0 ) return SQLITE_NOMEM; }else{ aOut = aRight; @@ -161100,6 +163065,7 @@ static int fts3TermSelectMerge( pTS->anOutput[0] = nDoclist; if( pTS->aaOutput[0] ){ memcpy(pTS->aaOutput[0], aDoclist, nDoclist); + memset(&pTS->aaOutput[0][nDoclist], 0, FTS3_VARINT_MAX); }else{ return SQLITE_NOMEM; } @@ -161151,8 +163117,8 @@ static int fts3SegReaderCursorAppend( ){ if( (pCsr->nSegment%16)==0 ){ Fts3SegReader **apNew; - int nByte = (pCsr->nSegment + 16)*sizeof(Fts3SegReader*); - apNew = (Fts3SegReader **)sqlite3_realloc(pCsr->apSegment, nByte); + sqlite3_int64 nByte = (pCsr->nSegment + 16)*sizeof(Fts3SegReader*); + apNew = (Fts3SegReader **)sqlite3_realloc64(pCsr->apSegment, nByte); if( !apNew ){ sqlite3Fts3SegReaderFree(pNew); return SQLITE_NOMEM; @@ -161216,7 +163182,7 @@ static int fts3SegReaderCursor( /* If zTerm is not NULL, and this segment is not stored entirely on its ** root node, the range of leaves scanned can be reduced. Do this. */ - if( iStartBlock && zTerm ){ + if( iStartBlock && zTerm && zRoot ){ sqlite3_int64 *pi = (isPrefix ? &iLeavesEndBlock : 0); rc = fts3SelectLeaf(p, zTerm, nTerm, zRoot, nRoot, &iStartBlock, pi); if( rc!=SQLITE_OK ) goto finished; @@ -162158,7 +164124,6 @@ static int fts3RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){ Fts3Table *p = (Fts3Table*)pVtab; UNUSED_PARAMETER(iSavepoint); assert( p->inTransaction ); - assert( p->mxSavepoint >= iSavepoint ); TESTONLY( p->mxSavepoint = iSavepoint ); sqlite3Fts3PendingTermsClear(p); return SQLITE_OK; @@ -162933,9 +164898,10 @@ static int fts3EvalIncrPhraseNext( if( bEof==0 ){ int nList = 0; int nByte = a[p->nToken-1].nList; - char *aDoclist = sqlite3_malloc(nByte+1); + char *aDoclist = sqlite3_malloc(nByte+FTS3_BUFFER_PADDING); if( !aDoclist ) return SQLITE_NOMEM; memcpy(aDoclist, a[p->nToken-1].pList, nByte+1); + memset(&aDoclist[nByte], 0, FTS3_BUFFER_PADDING); for(i=0; i<(p->nToken-1); i++){ if( a[i].bIgnore==0 ){ @@ -163326,7 +165292,7 @@ static int fts3EvalStart(Fts3Cursor *pCsr){ if( rc==SQLITE_OK && nToken>1 && pTab->bFts4 ){ Fts3TokenAndCost *aTC; Fts3Expr **apOr; - aTC = (Fts3TokenAndCost *)sqlite3_malloc( + aTC = (Fts3TokenAndCost *)sqlite3_malloc64( sizeof(Fts3TokenAndCost) * nToken + sizeof(Fts3Expr *) * nOr * 2 ); @@ -163637,7 +165603,7 @@ static int fts3EvalNearTest(Fts3Expr *pExpr, int *pRc){ && (pExpr->pParent==0 || pExpr->pParent->eType!=FTSQUERY_NEAR) ){ Fts3Expr *p; - int nTmp = 0; /* Bytes of temp space */ + sqlite3_int64 nTmp = 0; /* Bytes of temp space */ char *aTmp; /* Temp space for PoslistNearMerge() */ /* Allocate temporary working space. */ @@ -163646,7 +165612,7 @@ static int fts3EvalNearTest(Fts3Expr *pExpr, int *pRc){ nTmp += p->pRight->pPhrase->doclist.nList; } nTmp += p->pPhrase->doclist.nList; - aTmp = sqlite3_malloc(nTmp*2); + aTmp = sqlite3_malloc64(nTmp*2); if( !aTmp ){ *pRc = SQLITE_NOMEM; res = 0; @@ -163916,15 +165882,14 @@ static void fts3EvalRestart( ** found in Fts3Expr.pPhrase->doclist.pList for each of the phrase ** expression nodes. */ -static void fts3EvalUpdateCounts(Fts3Expr *pExpr){ +static void fts3EvalUpdateCounts(Fts3Expr *pExpr, int nCol){ if( pExpr ){ Fts3Phrase *pPhrase = pExpr->pPhrase; if( pPhrase && pPhrase->doclist.pList ){ int iCol = 0; char *p = pPhrase->doclist.pList; - assert( *p ); - while( 1 ){ + do{ u8 c = 0; int iCnt = 0; while( 0xFE & (*p | c) ){ @@ -163940,11 +165905,11 @@ static void fts3EvalUpdateCounts(Fts3Expr *pExpr){ if( *p==0x00 ) break; p++; p += fts3GetVarint32(p, &iCol); - } + }while( iColpLeft); - fts3EvalUpdateCounts(pExpr->pRight); + fts3EvalUpdateCounts(pExpr->pLeft, nCol); + fts3EvalUpdateCounts(pExpr->pRight, nCol); } } @@ -163988,7 +165953,7 @@ static int fts3EvalGatherStats( for(p=pRoot; p; p=p->pLeft){ Fts3Expr *pE = (p->eType==FTSQUERY_PHRASE?p:p->pRight); assert( pE->aMI==0 ); - pE->aMI = (u32 *)sqlite3_malloc(pTab->nColumn * 3 * sizeof(u32)); + pE->aMI = (u32 *)sqlite3_malloc64(pTab->nColumn * 3 * sizeof(u32)); if( !pE->aMI ) return SQLITE_NOMEM; memset(pE->aMI, 0, pTab->nColumn * 3 * sizeof(u32)); } @@ -164014,7 +165979,7 @@ static int fts3EvalGatherStats( ); if( rc==SQLITE_OK && pCsr->isEof==0 ){ - fts3EvalUpdateCounts(pRoot); + fts3EvalUpdateCounts(pRoot, pTab->nColumn); } } @@ -164364,7 +166329,7 @@ static int fts3auxConnectMethod( char const *zFts3; /* Name of fts3 table */ int nDb; /* Result of strlen(zDb) */ int nFts3; /* Result of strlen(zFts3) */ - int nByte; /* Bytes of space to allocate here */ + sqlite3_int64 nByte; /* Bytes of space to allocate here */ int rc; /* value returned by declare_vtab() */ Fts3auxTable *p; /* Virtual table object to return */ @@ -164396,7 +166361,7 @@ static int fts3auxConnectMethod( if( rc!=SQLITE_OK ) return rc; nByte = sizeof(Fts3auxTable) + sizeof(Fts3Table) + nDb + nFts3 + 2; - p = (Fts3auxTable *)sqlite3_malloc(nByte); + p = (Fts3auxTable *)sqlite3_malloc64(nByte); if( !p ) return SQLITE_NOMEM; memset(p, 0, nByte); @@ -164546,7 +166511,7 @@ static int fts3auxCloseMethod(sqlite3_vtab_cursor *pCursor){ static int fts3auxGrowStatArray(Fts3auxCursor *pCsr, int nSize){ if( nSize>pCsr->nStat ){ struct Fts3auxColstats *aNew; - aNew = (struct Fts3auxColstats *)sqlite3_realloc(pCsr->aStat, + aNew = (struct Fts3auxColstats *)sqlite3_realloc64(pCsr->aStat, sizeof(struct Fts3auxColstats) * nSize ); if( aNew==0 ) return SQLITE_NOMEM; @@ -164714,15 +166679,15 @@ static int fts3auxFilterMethod( assert( (iEq==0 && iGe==-1) || (iEq==-1 && iGe==0) ); if( zStr ){ pCsr->filter.zTerm = sqlite3_mprintf("%s", zStr); - pCsr->filter.nTerm = sqlite3_value_bytes(apVal[0]); if( pCsr->filter.zTerm==0 ) return SQLITE_NOMEM; + pCsr->filter.nTerm = (int)strlen(pCsr->filter.zTerm); } } if( iLe>=0 ){ pCsr->zStop = sqlite3_mprintf("%s", sqlite3_value_text(apVal[iLe])); - pCsr->nStop = sqlite3_value_bytes(apVal[iLe]); if( pCsr->zStop==0 ) return SQLITE_NOMEM; + pCsr->nStop = (int)strlen(pCsr->zStop); } if( iLangid>=0 ){ @@ -164974,8 +166939,8 @@ static int fts3isspace(char c){ ** zero the memory before returning a pointer to it. If unsuccessful, ** return NULL. */ -static void *fts3MallocZero(int nByte){ - void *pRet = sqlite3_malloc(nByte); +static void *fts3MallocZero(sqlite3_int64 nByte){ + void *pRet = sqlite3_malloc64(nByte); if( pRet ) memset(pRet, 0, nByte); return pRet; } @@ -165050,7 +167015,7 @@ static int getNextToken( if( rc==SQLITE_OK ){ const char *zToken; int nToken = 0, iStart = 0, iEnd = 0, iPosition = 0; - int nByte; /* total space to allocate */ + sqlite3_int64 nByte; /* total space to allocate */ rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition); if( rc==SQLITE_OK ){ @@ -165104,8 +167069,8 @@ static int getNextToken( ** Enlarge a memory allocation. If an out-of-memory allocation occurs, ** then free the old allocation. */ -static void *fts3ReallocOrFree(void *pOrig, int nNew){ - void *pRet = sqlite3_realloc(pOrig, nNew); +static void *fts3ReallocOrFree(void *pOrig, sqlite3_int64 nNew){ + void *pRet = sqlite3_realloc64(pOrig, nNew); if( !pRet ){ sqlite3_free(pOrig); } @@ -165349,7 +167314,6 @@ static int getNextNode( int nConsumed = 0; pParse->nNest++; rc = fts3ExprParse(pParse, zInput+1, nInput-1, ppExpr, &nConsumed); - if( rc==SQLITE_OK && !*ppExpr ){ rc = SQLITE_DONE; } *pnConsumed = (int)(zInput - z) + 1 + nConsumed; return rc; }else if( *zInput==')' ){ @@ -165648,7 +167612,7 @@ static int fts3ExprBalance(Fts3Expr **pp, int nMaxDepth){ if( rc==SQLITE_OK ){ if( (eType==FTSQUERY_AND || eType==FTSQUERY_OR) ){ Fts3Expr **apLeaf; - apLeaf = (Fts3Expr **)sqlite3_malloc(sizeof(Fts3Expr *) * nMaxDepth); + apLeaf = (Fts3Expr **)sqlite3_malloc64(sizeof(Fts3Expr *) * nMaxDepth); if( 0==apLeaf ){ rc = SQLITE_NOMEM; }else{ @@ -166068,7 +168032,7 @@ static void fts3ExprTestCommon( zExpr = (const char *)sqlite3_value_text(argv[1]); nExpr = sqlite3_value_bytes(argv[1]); nCol = argc-2; - azCol = (char **)sqlite3_malloc(nCol*sizeof(char *)); + azCol = (char **)sqlite3_malloc64(nCol*sizeof(char *)); if( !azCol ){ sqlite3_result_error_nomem(context); goto exprtest_out; @@ -166182,8 +168146,8 @@ SQLITE_PRIVATE int sqlite3Fts3ExprInitTestInterface(sqlite3 *db, Fts3Hash *pHash /* ** Malloc and Free functions */ -static void *fts3HashMalloc(int n){ - void *p = sqlite3_malloc(n); +static void *fts3HashMalloc(sqlite3_int64 n){ + void *p = sqlite3_malloc64(n); if( p ){ memset(p, 0, n); } @@ -167277,7 +169241,7 @@ static void fts3TokenizerFunc( nName = sqlite3_value_bytes(argv[0])+1; if( argc==2 ){ - if( fts3TokenizerEnabled(context) ){ + if( fts3TokenizerEnabled(context) || sqlite3_value_frombind(argv[1]) ){ void *pOld; int n = sqlite3_value_bytes(argv[1]); if( zName==0 || n!=sizeof(pPtr) ){ @@ -167304,7 +169268,9 @@ static void fts3TokenizerFunc( return; } } - sqlite3_result_blob(context, (void *)&pPtr, sizeof(pPtr), SQLITE_TRANSIENT); + if( fts3TokenizerEnabled(context) || sqlite3_value_frombind(argv[0]) ){ + sqlite3_result_blob(context, (void *)&pPtr, sizeof(pPtr), SQLITE_TRANSIENT); + } } SQLITE_PRIVATE int sqlite3Fts3IsIdChar(char c){ @@ -167392,8 +169358,8 @@ SQLITE_PRIVATE int sqlite3Fts3InitTokenizer( int iArg = 0; z = &z[n+1]; while( zzInput = sqlite3_malloc(nByte+1); + pCsr->zInput = sqlite3_malloc64(nByte+1); if( pCsr->zInput==0 ){ rc = SQLITE_NOMEM; }else{ @@ -168808,10 +170774,12 @@ static int fts3SqlStmt( pStmt = p->aStmt[eStmt]; if( !pStmt ){ + int f = SQLITE_PREPARE_PERSISTENT|SQLITE_PREPARE_NO_VTAB; char *zSql; if( eStmt==SQL_CONTENT_INSERT ){ zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName, p->zWriteExprlist); }else if( eStmt==SQL_SELECT_CONTENT_BY_ROWID ){ + f &= ~SQLITE_PREPARE_NO_VTAB; zSql = sqlite3_mprintf(azSql[eStmt], p->zReadExprlist); }else{ zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName); @@ -168819,8 +170787,7 @@ static int fts3SqlStmt( if( !zSql ){ rc = SQLITE_NOMEM; }else{ - rc = sqlite3_prepare_v3(p->db, zSql, -1, SQLITE_PREPARE_PERSISTENT, - &pStmt, NULL); + rc = sqlite3_prepare_v3(p->db, zSql, -1, f, &pStmt, NULL); sqlite3_free(zSql); assert( rc==SQLITE_OK || pStmt==0 ); p->aStmt[eStmt] = pStmt; @@ -168978,7 +170945,7 @@ static sqlite3_int64 getAbsoluteLevel( int iLevel /* Level of segments */ ){ sqlite3_int64 iBase; /* First absolute level for iLangid/iIndex */ - assert( iLangid>=0 ); + assert_fts3_nc( iLangid>=0 ); assert( p->nIndex>0 ); assert( iIndex>=0 && iIndexnIndex ); @@ -169759,7 +171726,9 @@ static int fts3SegReaderNext( /* If iCurrentBlock>=iLeafEndBlock, this is an EOF condition. All leaf ** blocks have already been traversed. */ - assert( pReader->iCurrentBlock<=pReader->iLeafEndBlock ); +#ifdef CORRUPT_DB + assert( pReader->iCurrentBlock<=pReader->iLeafEndBlock || CORRUPT_DB ); +#endif if( pReader->iCurrentBlock>=pReader->iLeafEndBlock ){ return SQLITE_OK; } @@ -169820,7 +171789,7 @@ static int fts3SegReaderNext( ** b-tree node. And that the final byte of the doclist is 0x00. If either ** of these statements is untrue, then the data structure is corrupt. */ - if( (&pReader->aNode[pReader->nNode] - pReader->aDoclist)nDoclist + if( pReader->nDoclist > pReader->nNode-(pReader->aDoclist-pReader->aNode) || (pReader->nPopulate==0 && pReader->aDoclist[pReader->nDoclist-1]) ){ return FTS_CORRUPT_VTAB; @@ -170020,8 +171989,13 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderNew( Fts3SegReader *pReader; /* Newly allocated SegReader object */ int nExtra = 0; /* Bytes to allocate segment root node */ - assert( iStartLeaf<=iEndLeaf ); + assert( zRoot!=0 || nRoot==0 ); +#ifdef CORRUPT_DB + assert( zRoot!=0 || CORRUPT_DB ); +#endif + if( iStartLeaf==0 ){ + if( iEndLeaf!=0 ) return FTS_CORRUPT_VTAB; nExtra = nRoot + FTS3_NODE_PADDING; } @@ -170041,7 +172015,7 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderNew( pReader->aNode = (char *)&pReader[1]; pReader->rootOnly = 1; pReader->nNode = nRoot; - memcpy(pReader->aNode, zRoot, nRoot); + if( nRoot ) memcpy(pReader->aNode, zRoot, nRoot); memset(&pReader->aNode[nRoot], 0, FTS3_NODE_PADDING); }else{ pReader->iCurrentBlock = iStartLeaf-1; @@ -170156,8 +172130,9 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderPending( } if( nElem>0 ){ - int nByte = sizeof(Fts3SegReader) + (nElem+1)*sizeof(Fts3HashElem *); - pReader = (Fts3SegReader *)sqlite3_malloc(nByte); + sqlite3_int64 nByte; + nByte = sizeof(Fts3SegReader) + (nElem+1)*sizeof(Fts3HashElem *); + pReader = (Fts3SegReader *)sqlite3_malloc64(nByte); if( !pReader ){ rc = SQLITE_NOMEM; }else{ @@ -170661,6 +172636,11 @@ static int fts3SegWriterAdd( nPrefix = fts3PrefixCompress(pWriter->zTerm, pWriter->nTerm, zTerm, nTerm); nSuffix = nTerm-nPrefix; + /* If nSuffix is zero or less, then zTerm/nTerm must be a prefix of + ** pWriter->zTerm/pWriter->nTerm. i.e. must be equal to or less than when + ** compared with BINARY collation. This indicates corruption. */ + if( nSuffix<=0 ) return FTS_CORRUPT_VTAB; + /* Figure out how many bytes are required by this new entry */ nReq = sqlite3Fts3VarintLen(nPrefix) + /* varint containing prefix size */ sqlite3Fts3VarintLen(nSuffix) + /* varint containing suffix size */ @@ -171368,7 +173348,9 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderStep( }else{ iDelta = iDocid - iPrev; } - assert( iDelta>0 || (nDoclist==0 && iDelta==iDocid) ); + if( iDelta<=0 && (nDoclist>0 || iDelta!=iDocid) ){ + return FTS_CORRUPT_VTAB; + } assert( nDoclist>0 || iDelta==iDocid ); nByte = sqlite3Fts3VarintLen(iDelta) + (isRequirePos?nList+1:0); @@ -171634,8 +173616,10 @@ static int fts3SegmentMerge( if( rc!=SQLITE_OK ) goto finished; assert( csr.nSegment>0 ); - assert( iNewLevel>=getAbsoluteLevel(p, iLangid, iIndex, 0) ); - assert( iNewLevel=getAbsoluteLevel(p, iLangid, iIndex, 0) ); + assert_fts3_nc( + iNewLevelnColumn ); + pBlob = sqlite3_malloc64( 10*(sqlite3_int64)p->nColumn ); if( pBlob==0 ){ *pRC = SQLITE_NOMEM; return; @@ -171810,7 +173796,7 @@ static void fts3UpdateDocTotals( const int nStat = p->nColumn+2; if( *pRC ) return; - a = sqlite3_malloc( (sizeof(u32)+10)*nStat ); + a = sqlite3_malloc64( (sizeof(u32)+10)*(sqlite3_int64)nStat ); if( a==0 ){ *pRC = SQLITE_NOMEM; return; @@ -171931,8 +173917,8 @@ static int fts3DoRebuild(Fts3Table *p){ } if( rc==SQLITE_OK ){ - int nByte = sizeof(u32) * (p->nColumn+1)*3; - aSz = (u32 *)sqlite3_malloc(nByte); + sqlite3_int64 nByte = sizeof(u32) * ((sqlite3_int64)p->nColumn+1)*3; + aSz = (u32 *)sqlite3_malloc64(nByte); if( aSz==0 ){ rc = SQLITE_NOMEM; }else{ @@ -171998,12 +173984,12 @@ static int fts3IncrmergeCsr( ){ int rc; /* Return Code */ sqlite3_stmt *pStmt = 0; /* Statement used to read %_segdir entry */ - int nByte; /* Bytes allocated at pCsr->apSegment[] */ + sqlite3_int64 nByte; /* Bytes allocated at pCsr->apSegment[] */ /* Allocate space for the Fts3MultiSegReader.aCsr[] array */ memset(pCsr, 0, sizeof(*pCsr)); nByte = sizeof(Fts3SegReader *) * nSeg; - pCsr->apSegment = (Fts3SegReader **)sqlite3_malloc(nByte); + pCsr->apSegment = (Fts3SegReader **)sqlite3_malloc64(nByte); if( pCsr->apSegment==0 ){ rc = SQLITE_NOMEM; @@ -172147,7 +174133,7 @@ static int nodeReaderNext(NodeReader *p){ p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &nSuffix); if( nPrefix>p->iOff || nSuffix>p->nNode-p->iOff ){ - return SQLITE_CORRUPT_VTAB; + return FTS_CORRUPT_VTAB; } blobGrowBuffer(&p->term, nPrefix+nSuffix, &rc); if( rc==SQLITE_OK ){ @@ -172157,7 +174143,7 @@ static int nodeReaderNext(NodeReader *p){ if( p->iChild==0 ){ p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &p->nDoclist); if( (p->nNode-p->iOff)nDoclist ){ - return SQLITE_CORRUPT_VTAB; + return FTS_CORRUPT_VTAB; } p->aDoclist = &p->aNode[p->iOff]; p->iOff += p->nDoclist; @@ -173983,7 +175969,7 @@ SQLITE_PRIVATE int sqlite3Fts3UpdateMethod( } /* Allocate space to hold the change in document sizes */ - aSzDel = sqlite3_malloc( sizeof(aSzDel[0])*(p->nColumn+1)*2 ); + aSzDel = sqlite3_malloc64(sizeof(aSzDel[0])*((sqlite3_int64)p->nColumn+1)*2); if( aSzDel==0 ){ rc = SQLITE_NOMEM; goto update_out; @@ -174237,17 +176223,19 @@ struct StrBuffer { /* ** Allocate a two-slot MatchinfoBuffer object. */ -static MatchinfoBuffer *fts3MIBufferNew(int nElem, const char *zMatchinfo){ +static MatchinfoBuffer *fts3MIBufferNew(size_t nElem, const char *zMatchinfo){ MatchinfoBuffer *pRet; - int nByte = sizeof(u32) * (2*nElem + 1) + sizeof(MatchinfoBuffer); - int nStr = (int)strlen(zMatchinfo); + sqlite3_int64 nByte = sizeof(u32) * (2*(sqlite3_int64)nElem + 1) + + sizeof(MatchinfoBuffer); + sqlite3_int64 nStr = strlen(zMatchinfo); - pRet = sqlite3_malloc(nByte + nStr+1); + pRet = sqlite3_malloc64(nByte + nStr+1); if( pRet ){ memset(pRet, 0, nByte); pRet->aMatchinfo[0] = (u8*)(&pRet->aMatchinfo[1]) - (u8*)pRet; - pRet->aMatchinfo[1+nElem] = pRet->aMatchinfo[0] + sizeof(u32)*(nElem+1); - pRet->nElem = nElem; + pRet->aMatchinfo[1+nElem] = pRet->aMatchinfo[0] + + sizeof(u32)*((int)nElem+1); + pRet->nElem = (int)nElem; pRet->zMatchinfo = ((char*)pRet) + nByte; memcpy(pRet->zMatchinfo, zMatchinfo, nStr+1); pRet->aRef[0] = 1; @@ -174287,7 +176275,7 @@ static void (*fts3MIBufferAlloc(MatchinfoBuffer *p, u32 **paOut))(void*){ aOut = &p->aMatchinfo[p->nElem+2]; xRet = fts3MIBufferFree; }else{ - aOut = (u32*)sqlite3_malloc(p->nElem * sizeof(u32)); + aOut = (u32*)sqlite3_malloc64(p->nElem * sizeof(u32)); if( aOut ){ xRet = sqlite3_free; if( p->bGlobal ) memcpy(aOut, &p->aMatchinfo[1], p->nElem*sizeof(u32)); @@ -174538,11 +176526,12 @@ static void fts3SnippetDetails( char *pCsr = pPhrase->pTail; int iCsr = pPhrase->iTail; - while( iCsr<(iStart+pIter->nSnippet) ){ + while( iCsr<(iStart+pIter->nSnippet) && iCsr>=iStart ){ int j; u64 mPhrase = (u64)1 << i; u64 mPos = (u64)1 << (iCsr - iStart); - assert( iCsr>=iStart ); + assert( iCsr>=iStart && (iCsr - iStart)<=64 ); + assert( i>=0 && i<=64 ); if( (mCover|mCovered)&mPhrase ){ iScore++; }else{ @@ -174584,11 +176573,14 @@ static int fts3SnippetFindPositions(Fts3Expr *pExpr, int iPhrase, void *ctx){ int iFirst = 0; pPhrase->pList = pCsr; fts3GetDeltaPosition(&pCsr, &iFirst); - assert( iFirst>=0 ); - pPhrase->pHead = pCsr; - pPhrase->pTail = pCsr; - pPhrase->iHead = iFirst; - pPhrase->iTail = iFirst; + if( iFirst<0 ){ + rc = FTS_CORRUPT_VTAB; + }else{ + pPhrase->pHead = pCsr; + pPhrase->pTail = pCsr; + pPhrase->iHead = iFirst; + pPhrase->iTail = iFirst; + } }else{ assert( rc!=SQLITE_OK || ( pPhrase->pList==0 && pPhrase->pHead==0 && pPhrase->pTail==0 @@ -174625,7 +176617,7 @@ static int fts3BestSnippet( int rc; /* Return Code */ int nList; /* Number of phrases in expression */ SnippetIter sIter; /* Iterates through snippet candidates */ - int nByte; /* Number of bytes of space to allocate */ + sqlite3_int64 nByte; /* Number of bytes of space to allocate */ int iBestScore = -1; /* Best snippet score found so far */ int i; /* Loop counter */ @@ -174643,7 +176635,7 @@ static int fts3BestSnippet( ** the required space using malloc(). */ nByte = sizeof(SnippetPhrase) * nList; - sIter.aPhrase = (SnippetPhrase *)sqlite3_malloc(nByte); + sIter.aPhrase = (SnippetPhrase *)sqlite3_malloc64(nByte); if( !sIter.aPhrase ){ return SQLITE_NOMEM; } @@ -174713,8 +176705,8 @@ static int fts3StringAppend( ** appended data. */ if( pStr->n+nAppend+1>=pStr->nAlloc ){ - int nAlloc = pStr->nAlloc+nAppend+100; - char *zNew = sqlite3_realloc(pStr->z, nAlloc); + sqlite3_int64 nAlloc = pStr->nAlloc+(sqlite3_int64)nAppend+100; + char *zNew = sqlite3_realloc64(pStr->z, nAlloc); if( !zNew ){ return SQLITE_NOMEM; } @@ -174769,6 +176761,7 @@ static int fts3SnippetShift( for(nLeft=0; !(hlmask & ((u64)1 << nLeft)); nLeft++); for(nRight=0; !(hlmask & ((u64)1 << (nSnippet-1-nRight))); nRight++); + assert( (nSnippet-1-nRight)<=63 && (nSnippet-1-nRight)>=0 ); nDesired = (nLeft-nRight)/2; /* Ideally, the start of the snippet should be pushed forward in the @@ -174961,7 +176954,7 @@ static int fts3ColumnlistCount(char **ppCollist){ /* ** This function gathers 'y' or 'b' data for a single phrase. */ -static void fts3ExprLHits( +static int fts3ExprLHits( Fts3Expr *pExpr, /* Phrase expression node */ MatchInfo *p /* Matchinfo context */ ){ @@ -174991,25 +176984,29 @@ static void fts3ExprLHits( if( *pIter!=0x01 ) break; pIter++; pIter += fts3GetVarint32(pIter, &iCol); + if( iCol>=p->nCol ) return FTS_CORRUPT_VTAB; } + return SQLITE_OK; } /* ** Gather the results for matchinfo directives 'y' and 'b'. */ -static void fts3ExprLHitGather( +static int fts3ExprLHitGather( Fts3Expr *pExpr, MatchInfo *p ){ + int rc = SQLITE_OK; assert( (pExpr->pLeft==0)==(pExpr->pRight==0) ); if( pExpr->bEof==0 && pExpr->iDocid==p->pCursor->iPrevId ){ if( pExpr->pLeft ){ - fts3ExprLHitGather(pExpr->pLeft, p); - fts3ExprLHitGather(pExpr->pRight, p); + rc = fts3ExprLHitGather(pExpr->pLeft, p); + if( rc==SQLITE_OK ) rc = fts3ExprLHitGather(pExpr->pRight, p); }else{ - fts3ExprLHits(pExpr, p); + rc = fts3ExprLHits(pExpr, p); } } + return rc; } /* @@ -175099,8 +177096,8 @@ static int fts3MatchinfoCheck( return SQLITE_ERROR; } -static int fts3MatchinfoSize(MatchInfo *pInfo, char cArg){ - int nVal; /* Number of integers output by cArg */ +static size_t fts3MatchinfoSize(MatchInfo *pInfo, char cArg){ + size_t nVal; /* Number of integers output by cArg */ switch( cArg ){ case FTS3_MATCHINFO_NDOC: @@ -175226,11 +177223,12 @@ static int fts3MatchinfoLcs(Fts3Cursor *pCsr, MatchInfo *pInfo){ int i; int iCol; int nToken = 0; + int rc = SQLITE_OK; /* Allocate and populate the array of LcsIterator objects. The array ** contains one element for each matchable phrase in the query. **/ - aIter = sqlite3_malloc(sizeof(LcsIterator) * pCsr->nPhrase); + aIter = sqlite3_malloc64(sizeof(LcsIterator) * pCsr->nPhrase); if( !aIter ) return SQLITE_NOMEM; memset(aIter, 0, sizeof(LcsIterator) * pCsr->nPhrase); (void)fts3ExprIterate(pCsr->pExpr, fts3MatchinfoLcsCb, (void*)aIter); @@ -175246,13 +177244,16 @@ static int fts3MatchinfoLcs(Fts3Cursor *pCsr, MatchInfo *pInfo){ int nLive = 0; /* Number of iterators in aIter not at EOF */ for(i=0; inPhrase; i++){ - int rc; LcsIterator *pIt = &aIter[i]; rc = sqlite3Fts3EvalPhrasePoslist(pCsr, pIt->pExpr, iCol, &pIt->pRead); - if( rc!=SQLITE_OK ) return rc; + if( rc!=SQLITE_OK ) goto matchinfo_lcs_out; if( pIt->pRead ){ pIt->iPos = pIt->iPosOffset; - fts3LcsIteratorAdvance(&aIter[i]); + fts3LcsIteratorAdvance(pIt); + if( pIt->pRead==0 ){ + rc = FTS_CORRUPT_VTAB; + goto matchinfo_lcs_out; + } nLive++; } } @@ -175284,8 +177285,9 @@ static int fts3MatchinfoLcs(Fts3Cursor *pCsr, MatchInfo *pInfo){ pInfo->aMatchinfo[iCol] = nLcs; } + matchinfo_lcs_out: sqlite3_free(aIter); - return SQLITE_OK; + return rc; } /* @@ -175379,9 +177381,9 @@ static int fts3MatchinfoValues( case FTS3_MATCHINFO_LHITS_BM: case FTS3_MATCHINFO_LHITS: { - int nZero = fts3MatchinfoSize(pInfo, zArg[i]) * sizeof(u32); + size_t nZero = fts3MatchinfoSize(pInfo, zArg[i]) * sizeof(u32); memset(pInfo->aMatchinfo, 0, nZero); - fts3ExprLHitGather(pCsr->pExpr, pInfo); + rc = fts3ExprLHitGather(pCsr->pExpr, pInfo); break; } @@ -175448,7 +177450,7 @@ static void fts3GetMatchinfo( ** initialize those elements that are constant for every row. */ if( pCsr->pMIBuffer==0 ){ - int nMatchinfo = 0; /* Number of u32 elements in match-info */ + size_t nMatchinfo = 0; /* Number of u32 elements in match-info */ int i; /* Used to iterate through zArg */ /* Determine the number of phrases in the query */ @@ -175533,6 +177535,10 @@ SQLITE_PRIVATE void sqlite3Fts3Snippet( return; } + /* Limit the snippet length to 64 tokens. */ + if( nToken<-64 ) nToken = -64; + if( nToken>+64 ) nToken = +64; + for(nSnippet=1; 1; nSnippet++){ int iSnip; /* Loop counter 0..nSnippet-1 */ @@ -175634,7 +177640,7 @@ static int fts3ExprTermOffsetInit(Fts3Expr *pExpr, int iPhrase, void *ctx){ nTerm = pExpr->pPhrase->nToken; if( pList ){ fts3GetDeltaPosition(&pList, &iPos); - assert( iPos>=0 ); + assert_fts3_nc( iPos>=0 ); } for(iTerm=0; iTermpList) ){ pTerm->pList = 0; }else{ @@ -175900,7 +177906,7 @@ typedef struct unicode_cursor unicode_cursor; struct unicode_tokenizer { sqlite3_tokenizer base; - int bRemoveDiacritic; + int eRemoveDiacritic; int nException; int *aiException; }; @@ -175973,7 +177979,7 @@ static int unicodeAddExceptions( int *aNew; /* New aiException[] array */ int nNew; /* Number of valid entries in array aNew[] */ - aNew = sqlite3_realloc(p->aiException, (p->nException+nEntry)*sizeof(int)); + aNew = sqlite3_realloc64(p->aiException,(p->nException+nEntry)*sizeof(int)); if( aNew==0 ) return SQLITE_NOMEM; nNew = p->nException; @@ -176045,17 +178051,20 @@ static int unicodeCreate( pNew = (unicode_tokenizer *) sqlite3_malloc(sizeof(unicode_tokenizer)); if( pNew==NULL ) return SQLITE_NOMEM; memset(pNew, 0, sizeof(unicode_tokenizer)); - pNew->bRemoveDiacritic = 1; + pNew->eRemoveDiacritic = 1; for(i=0; rc==SQLITE_OK && ibRemoveDiacritic = 1; + pNew->eRemoveDiacritic = 1; } else if( n==19 && memcmp("remove_diacritics=0", z, 19)==0 ){ - pNew->bRemoveDiacritic = 0; + pNew->eRemoveDiacritic = 0; + } + else if( n==19 && memcmp("remove_diacritics=2", z, 19)==0 ){ + pNew->eRemoveDiacritic = 2; } else if( n>=11 && memcmp("tokenchars=", z, 11)==0 ){ rc = unicodeAddExceptions(pNew, 1, &z[11], n-11); @@ -176159,7 +178168,7 @@ static int unicodeNext( /* Grow the output buffer if required. */ if( (zOut-pCsr->zToken)>=(pCsr->nAlloc-4) ){ - char *zNew = sqlite3_realloc(pCsr->zToken, pCsr->nAlloc+64); + char *zNew = sqlite3_realloc64(pCsr->zToken, pCsr->nAlloc+64); if( !zNew ) return SQLITE_NOMEM; zOut = &zNew[zOut - pCsr->zToken]; pCsr->zToken = zNew; @@ -176168,7 +178177,7 @@ static int unicodeNext( /* Write the folded case of the last character read to the output */ zEnd = z; - iOut = sqlite3FtsUnicodeFold((int)iCode, p->bRemoveDiacritic); + iOut = sqlite3FtsUnicodeFold((int)iCode, p->eRemoveDiacritic); if( iOut ){ WRITE_UTF8(zOut, iOut); } @@ -176213,7 +178222,7 @@ SQLITE_PRIVATE void sqlite3Fts3UnicodeTokenizer(sqlite3_tokenizer_module const * /************** End of fts3_unicode.c ****************************************/ /************** Begin file fts3_unicode2.c ***********************************/ /* -** 2012 May 25 +** 2012-05-25 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: @@ -176373,32 +178382,48 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeIsalnum(int c){ ** E"). The resuls of passing a codepoint that corresponds to an ** uppercase letter are undefined. */ -static int remove_diacritic(int c){ +static int remove_diacritic(int c, int bComplex){ unsigned short aDia[] = { 0, 1797, 1848, 1859, 1891, 1928, 1940, 1995, 2024, 2040, 2060, 2110, 2168, 2206, 2264, 2286, 2344, 2383, 2472, 2488, 2516, 2596, 2668, 2732, 2782, 2842, 2894, 2954, 2984, 3000, 3028, 3336, - 3456, 3696, 3712, 3728, 3744, 3896, 3912, 3928, - 3968, 4008, 4040, 4106, 4138, 4170, 4202, 4234, - 4266, 4296, 4312, 4344, 4408, 4424, 4472, 4504, - 6148, 6198, 6264, 6280, 6360, 6429, 6505, 6529, - 61448, 61468, 61534, 61592, 61642, 61688, 61704, 61726, - 61784, 61800, 61836, 61880, 61914, 61948, 61998, 62122, - 62154, 62200, 62218, 62302, 62364, 62442, 62478, 62536, - 62554, 62584, 62604, 62640, 62648, 62656, 62664, 62730, - 62924, 63050, 63082, 63274, 63390, + 3456, 3696, 3712, 3728, 3744, 3766, 3832, 3896, + 3912, 3928, 3944, 3968, 4008, 4040, 4056, 4106, + 4138, 4170, 4202, 4234, 4266, 4296, 4312, 4344, + 4408, 4424, 4442, 4472, 4488, 4504, 6148, 6198, + 6264, 6280, 6360, 6429, 6505, 6529, 61448, 61468, + 61512, 61534, 61592, 61610, 61642, 61672, 61688, 61704, + 61726, 61784, 61800, 61816, 61836, 61880, 61896, 61914, + 61948, 61998, 62062, 62122, 62154, 62184, 62200, 62218, + 62252, 62302, 62364, 62410, 62442, 62478, 62536, 62554, + 62584, 62604, 62640, 62648, 62656, 62664, 62730, 62766, + 62830, 62890, 62924, 62974, 63032, 63050, 63082, 63118, + 63182, 63242, 63274, 63310, 63368, 63390, }; - char aChar[] = { - '\0', 'a', 'c', 'e', 'i', 'n', 'o', 'u', 'y', 'y', 'a', 'c', - 'd', 'e', 'e', 'g', 'h', 'i', 'j', 'k', 'l', 'n', 'o', 'r', - 's', 't', 'u', 'u', 'w', 'y', 'z', 'o', 'u', 'a', 'i', 'o', - 'u', 'g', 'k', 'o', 'j', 'g', 'n', 'a', 'e', 'i', 'o', 'r', - 'u', 's', 't', 'h', 'a', 'e', 'o', 'y', '\0', '\0', '\0', '\0', - '\0', '\0', '\0', '\0', 'a', 'b', 'd', 'd', 'e', 'f', 'g', 'h', - 'h', 'i', 'k', 'l', 'l', 'm', 'n', 'p', 'r', 'r', 's', 't', - 'u', 'v', 'w', 'w', 'x', 'y', 'z', 'h', 't', 'w', 'y', 'a', - 'e', 'i', 'o', 'u', 'y', +#define HIBIT ((unsigned char)0x80) + unsigned char aChar[] = { + '\0', 'a', 'c', 'e', 'i', 'n', + 'o', 'u', 'y', 'y', 'a', 'c', + 'd', 'e', 'e', 'g', 'h', 'i', + 'j', 'k', 'l', 'n', 'o', 'r', + 's', 't', 'u', 'u', 'w', 'y', + 'z', 'o', 'u', 'a', 'i', 'o', + 'u', 'u'|HIBIT, 'a'|HIBIT, 'g', 'k', 'o', + 'o'|HIBIT, 'j', 'g', 'n', 'a'|HIBIT, 'a', + 'e', 'i', 'o', 'r', 'u', 's', + 't', 'h', 'a', 'e', 'o'|HIBIT, 'o', + 'o'|HIBIT, 'y', '\0', '\0', '\0', '\0', + '\0', '\0', '\0', '\0', 'a', 'b', + 'c'|HIBIT, 'd', 'd', 'e'|HIBIT, 'e', 'e'|HIBIT, + 'f', 'g', 'h', 'h', 'i', 'i'|HIBIT, + 'k', 'l', 'l'|HIBIT, 'l', 'm', 'n', + 'o'|HIBIT, 'p', 'r', 'r'|HIBIT, 'r', 's', + 's'|HIBIT, 't', 'u', 'u'|HIBIT, 'v', 'w', + 'w', 'x', 'y', 'z', 'h', 't', + 'w', 'y', 'a', 'a'|HIBIT, 'a'|HIBIT, 'a'|HIBIT, + 'e', 'e'|HIBIT, 'e'|HIBIT, 'i', 'o', 'o'|HIBIT, + 'o'|HIBIT, 'o'|HIBIT, 'u', 'u'|HIBIT, 'u'|HIBIT, 'y', }; unsigned int key = (((unsigned int)c)<<3) | 0x00000007; @@ -176415,7 +178440,8 @@ static int remove_diacritic(int c){ } } assert( key>=aDia[iRes] ); - return ((c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c : (int)aChar[iRes]); + if( bComplex==0 && (aChar[iRes] & 0x80) ) return c; + return (c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c : ((int)aChar[iRes] & 0x7F); } @@ -176428,8 +178454,8 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int c){ unsigned int mask1 = 0x000361F8; if( c<768 || c>817 ) return 0; return (c < 768+32) ? - (mask0 & (1 << (c-768))) : - (mask1 & (1 << (c-768-32))); + (mask0 & ((unsigned int)1 << (c-768))) : + (mask1 & ((unsigned int)1 << (c-768-32))); } @@ -176442,7 +178468,7 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int c){ ** The results are undefined if the value passed to this function ** is less than zero. */ -SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int c, int bRemoveDiacritic){ +SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int c, int eRemoveDiacritic){ /* Each entry in the following array defines a rule for folding a range ** of codepoints to lower case. The rule applies to a range of nRange ** codepoints starting at codepoint iCode. @@ -176565,7 +178591,9 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int c, int bRemoveDiacritic){ assert( ret>0 ); } - if( bRemoveDiacritic ) ret = remove_diacritic(ret); + if( eRemoveDiacritic ){ + ret = remove_diacritic(ret, eRemoveDiacritic==2); + } } else if( c>=66560 && c<66600 ){ @@ -177272,7 +179300,7 @@ static JSON_NOINLINE int jsonParseAddNodeExpand( assert( pParse->nNode>=pParse->nAlloc ); if( pParse->oom ) return -1; nNew = pParse->nAlloc*2 + 10; - pNew = sqlite3_realloc(pParse->aNode, sizeof(JsonNode)*nNew); + pNew = sqlite3_realloc64(pParse->aNode, sizeof(JsonNode)*nNew); if( pNew==0 ){ pParse->oom = 1; return -1; @@ -177546,7 +179574,7 @@ static void jsonParseFillInParentage(JsonParse *pParse, u32 i, u32 iParent){ static int jsonParseFindParents(JsonParse *pParse){ u32 *aUp; assert( pParse->aUp==0 ); - aUp = pParse->aUp = sqlite3_malloc( sizeof(u32)*pParse->nNode ); + aUp = pParse->aUp = sqlite3_malloc64( sizeof(u32)*pParse->nNode ); if( aUp==0 ){ pParse->oom = 1; return SQLITE_NOMEM; @@ -177608,7 +179636,7 @@ static JsonParse *jsonParseCached( pMatch->iHold = iMaxHold+1; return pMatch; } - p = sqlite3_malloc( sizeof(*p) + nJson + 1 ); + p = sqlite3_malloc64( sizeof(*p) + nJson + 1 ); if( p==0 ){ sqlite3_result_error_nomem(pCtx); return 0; @@ -179253,6 +181281,9 @@ struct Rtree { u8 inWrTrans; /* True if inside write transaction */ u8 nAux; /* # of auxiliary columns in %_rowid */ u8 nAuxNotNull; /* Number of initial not-null aux columns */ +#ifdef SQLITE_DEBUG + u8 bCorrupt; /* Shadow table corruption detected */ +#endif int iDepth; /* Current depth of the r-tree structure */ char *zDb; /* Name of database containing r-tree table */ char *zName; /* Name of r-tree table */ @@ -179312,6 +181343,15 @@ struct Rtree { # define RTREE_ZERO 0.0 #endif +/* +** Set the Rtree.bCorrupt flag +*/ +#ifdef SQLITE_DEBUG +# define RTREE_IS_CORRUPT(X) ((X)->bCorrupt = 1) +#else +# define RTREE_IS_CORRUPT(X) +#endif + /* ** When doing a search of an r-tree, instances of the following structure ** record intermediate results from the tree walk. @@ -179678,8 +181718,8 @@ static void nodeZero(Rtree *pRtree, RtreeNode *p){ ** Given a node number iNode, return the corresponding key to use ** in the Rtree.aHash table. */ -static int nodeHash(i64 iNode){ - return iNode % HASHSIZE; +static unsigned int nodeHash(i64 iNode){ + return ((unsigned)iNode) % HASHSIZE; } /* @@ -179724,7 +181764,7 @@ static void nodeHashDelete(Rtree *pRtree, RtreeNode *pNode){ */ static RtreeNode *nodeNew(Rtree *pRtree, RtreeNode *pParent){ RtreeNode *pNode; - pNode = (RtreeNode *)sqlite3_malloc(sizeof(RtreeNode) + pRtree->iNodeSize); + pNode = (RtreeNode *)sqlite3_malloc64(sizeof(RtreeNode) + pRtree->iNodeSize); if( pNode ){ memset(pNode, 0, sizeof(RtreeNode) + pRtree->iNodeSize); pNode->zData = (u8 *)&pNode[1]; @@ -179748,6 +181788,18 @@ static void nodeBlobReset(Rtree *pRtree){ } } +/* +** Check to see if pNode is the same as pParent or any of the parents +** of pParent. +*/ +static int nodeInParentChain(const RtreeNode *pNode, const RtreeNode *pParent){ + do{ + if( pNode==pParent ) return 1; + pParent = pParent->pParent; + }while( pParent ); + return 0; +} + /* ** Obtain a reference to an r-tree node. */ @@ -179766,6 +181818,10 @@ static int nodeAcquire( if( (pNode = nodeHashLookup(pRtree, iNode))!=0 ){ assert( !pParent || !pNode->pParent || pNode->pParent==pParent ); if( pParent && !pNode->pParent ){ + if( nodeInParentChain(pNode, pParent) ){ + RTREE_IS_CORRUPT(pRtree); + return SQLITE_CORRUPT_VTAB; + } pParent->nRef++; pNode->pParent = pParent; } @@ -179796,9 +181852,12 @@ static int nodeAcquire( *ppNode = 0; /* If unable to open an sqlite3_blob on the desired row, that can only ** be because the shadow tables hold erroneous data. */ - if( rc==SQLITE_ERROR ) rc = SQLITE_CORRUPT_VTAB; + if( rc==SQLITE_ERROR ){ + rc = SQLITE_CORRUPT_VTAB; + RTREE_IS_CORRUPT(pRtree); + } }else if( pRtree->iNodeSize==sqlite3_blob_bytes(pRtree->pNodeBlob) ){ - pNode = (RtreeNode *)sqlite3_malloc(sizeof(RtreeNode)+pRtree->iNodeSize); + pNode = (RtreeNode *)sqlite3_malloc64(sizeof(RtreeNode)+pRtree->iNodeSize); if( !pNode ){ rc = SQLITE_NOMEM; }else{ @@ -179811,7 +181870,6 @@ static int nodeAcquire( pNode->pNext = 0; rc = sqlite3_blob_read(pRtree->pNodeBlob, pNode->zData, pRtree->iNodeSize, 0); - nodeReference(pParent); } } @@ -179825,6 +181883,7 @@ static int nodeAcquire( pRtree->iDepth = readInt16(pNode->zData); if( pRtree->iDepth>RTREE_MAX_DEPTH ){ rc = SQLITE_CORRUPT_VTAB; + RTREE_IS_CORRUPT(pRtree); } } @@ -179835,14 +181894,17 @@ static int nodeAcquire( if( pNode && rc==SQLITE_OK ){ if( NCELL(pNode)>((pRtree->iNodeSize-4)/pRtree->nBytesPerCell) ){ rc = SQLITE_CORRUPT_VTAB; + RTREE_IS_CORRUPT(pRtree); } } if( rc==SQLITE_OK ){ if( pNode!=0 ){ + nodeReference(pParent); nodeHashInsert(pRtree, pNode); }else{ rc = SQLITE_CORRUPT_VTAB; + RTREE_IS_CORRUPT(pRtree); } *ppNode = pNode; }else{ @@ -180068,7 +182130,7 @@ static void rtreeRelease(Rtree *pRtree){ pRtree->inWrTrans = 0; assert( pRtree->nCursor==0 ); nodeBlobReset(pRtree); - assert( pRtree->nNodeRef==0 ); + assert( pRtree->nNodeRef==0 || pRtree->bCorrupt ); sqlite3_finalize(pRtree->pWriteNode); sqlite3_finalize(pRtree->pDeleteNode); sqlite3_finalize(pRtree->pReadRowid); @@ -180127,7 +182189,7 @@ static int rtreeOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ Rtree *pRtree = (Rtree *)pVTab; RtreeCursor *pCsr; - pCsr = (RtreeCursor *)sqlite3_malloc(sizeof(RtreeCursor)); + pCsr = (RtreeCursor *)sqlite3_malloc64(sizeof(RtreeCursor)); if( pCsr ){ memset(pCsr, 0, sizeof(RtreeCursor)); pCsr->base.pVtab = pVTab; @@ -180400,6 +182462,7 @@ static int nodeRowidIndex( return SQLITE_OK; } } + RTREE_IS_CORRUPT(pRtree); return SQLITE_CORRUPT_VTAB; } @@ -180493,7 +182556,7 @@ static RtreeSearchPoint *rtreeEnqueue( RtreeSearchPoint *pNew; if( pCur->nPoint>=pCur->nPointAlloc ){ int nNew = pCur->nPointAlloc*2 + 8; - pNew = sqlite3_realloc(pCur->aPoint, nNew*sizeof(pCur->aPoint[0])); + pNew = sqlite3_realloc64(pCur->aPoint, nNew*sizeof(pCur->aPoint[0])); if( pNew==0 ) return 0; pCur->aPoint = pNew; pCur->nPointAlloc = nNew; @@ -180895,7 +182958,7 @@ static int rtreeFilter( */ rc = nodeAcquire(pRtree, 1, 0, &pRoot); if( rc==SQLITE_OK && argc>0 ){ - pCsr->aConstraint = sqlite3_malloc(sizeof(RtreeConstraint)*argc); + pCsr->aConstraint = sqlite3_malloc64(sizeof(RtreeConstraint)*argc); pCsr->nConstraint = argc; if( !pCsr->aConstraint ){ rc = SQLITE_NOMEM; @@ -181040,20 +183103,20 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ ){ u8 op; switch( p->op ){ - case SQLITE_INDEX_CONSTRAINT_EQ: op = RTREE_EQ; break; - case SQLITE_INDEX_CONSTRAINT_GT: op = RTREE_GT; break; - case SQLITE_INDEX_CONSTRAINT_LE: op = RTREE_LE; break; - case SQLITE_INDEX_CONSTRAINT_LT: op = RTREE_LT; break; - case SQLITE_INDEX_CONSTRAINT_GE: op = RTREE_GE; break; - default: - assert( p->op==SQLITE_INDEX_CONSTRAINT_MATCH ); - op = RTREE_MATCH; - break; + case SQLITE_INDEX_CONSTRAINT_EQ: op = RTREE_EQ; break; + case SQLITE_INDEX_CONSTRAINT_GT: op = RTREE_GT; break; + case SQLITE_INDEX_CONSTRAINT_LE: op = RTREE_LE; break; + case SQLITE_INDEX_CONSTRAINT_LT: op = RTREE_LT; break; + case SQLITE_INDEX_CONSTRAINT_GE: op = RTREE_GE; break; + case SQLITE_INDEX_CONSTRAINT_MATCH: op = RTREE_MATCH; break; + default: op = 0; break; + } + if( op ){ + zIdxStr[iIdx++] = op; + zIdxStr[iIdx++] = (char)(p->iColumn - 1 + '0'); + pIdxInfo->aConstraintUsage[ii].argvIndex = (iIdx/2); + pIdxInfo->aConstraintUsage[ii].omit = 1; } - zIdxStr[iIdx++] = op; - zIdxStr[iIdx++] = (char)(p->iColumn - 1 + '0'); - pIdxInfo->aConstraintUsage[ii].argvIndex = (iIdx/2); - pIdxInfo->aConstraintUsage[ii].omit = 1; } } @@ -181089,11 +183152,11 @@ static RtreeDValue cellArea(Rtree *pRtree, RtreeCell *p){ #endif { switch( pRtree->nDim ){ - case 5: area = p->aCoord[9].i - p->aCoord[8].i; - case 4: area *= p->aCoord[7].i - p->aCoord[6].i; - case 3: area *= p->aCoord[5].i - p->aCoord[4].i; - case 2: area *= p->aCoord[3].i - p->aCoord[2].i; - default: area *= p->aCoord[1].i - p->aCoord[0].i; + case 5: area = (i64)p->aCoord[9].i - (i64)p->aCoord[8].i; + case 4: area *= (i64)p->aCoord[7].i - (i64)p->aCoord[6].i; + case 3: area *= (i64)p->aCoord[5].i - (i64)p->aCoord[4].i; + case 2: area *= (i64)p->aCoord[3].i - (i64)p->aCoord[2].i; + default: area *= (i64)p->aCoord[1].i - (i64)p->aCoord[0].i; } } return area; @@ -181262,12 +183325,14 @@ static int AdjustTree( RtreeCell *pCell /* This cell was just inserted */ ){ RtreeNode *p = pNode; + int cnt = 0; while( p->pParent ){ RtreeNode *pParent = p->pParent; RtreeCell cell; int iCell; - if( nodeParentIndex(pRtree, p, &iCell) ){ + if( (++cnt)>1000 || nodeParentIndex(pRtree, p, &iCell) ){ + RTREE_IS_CORRUPT(pRtree); return SQLITE_CORRUPT_VTAB; } @@ -181464,9 +183529,9 @@ static int splitNodeStartree( int iBestSplit = 0; RtreeDValue fBestMargin = RTREE_ZERO; - int nByte = (pRtree->nDim+1)*(sizeof(int*)+nCell*sizeof(int)); + sqlite3_int64 nByte = (pRtree->nDim+1)*(sizeof(int*)+nCell*sizeof(int)); - aaSorted = (int **)sqlite3_malloc(nByte); + aaSorted = (int **)sqlite3_malloc64(nByte); if( !aaSorted ){ return SQLITE_NOMEM; } @@ -181587,7 +183652,7 @@ static int SplitNode( /* Allocate an array and populate it with a copy of pCell and ** all cells from node pLeft. Then zero the original node. */ - aCell = sqlite3_malloc((sizeof(RtreeCell)+sizeof(int))*(nCell+1)); + aCell = sqlite3_malloc64((sizeof(RtreeCell)+sizeof(int))*(nCell+1)); if( !aCell ){ rc = SQLITE_NOMEM; goto splitnode_out; @@ -181735,7 +183800,10 @@ static int fixLeafParent(Rtree *pRtree, RtreeNode *pLeaf){ } rc = sqlite3_reset(pRtree->pReadParent); if( rc==SQLITE_OK ) rc = rc2; - if( rc==SQLITE_OK && !pChild->pParent ) rc = SQLITE_CORRUPT_VTAB; + if( rc==SQLITE_OK && !pChild->pParent ){ + RTREE_IS_CORRUPT(pRtree); + rc = SQLITE_CORRUPT_VTAB; + } pChild = pChild->pParent; } return rc; @@ -181875,7 +183943,7 @@ static int Reinsert( /* Allocate the buffers used by this operation. The allocation is ** relinquished before this function returns. */ - aCell = (RtreeCell *)sqlite3_malloc(n * ( + aCell = (RtreeCell *)sqlite3_malloc64(n * ( sizeof(RtreeCell) + /* aCell array */ sizeof(int) + /* aOrder array */ sizeof(int) + /* aSpare array */ @@ -182049,8 +184117,12 @@ static int rtreeDeleteRowid(Rtree *pRtree, sqlite3_int64 iDelete){ rc = findLeafNode(pRtree, iDelete, &pLeaf, 0); } +#ifdef CORRUPT_DB + assert( pLeaf!=0 || rc!=SQLITE_OK || CORRUPT_DB ); +#endif + /* Delete the cell in question from the leaf node. */ - if( rc==SQLITE_OK ){ + if( rc==SQLITE_OK && pLeaf ){ int rc2; rc = nodeRowidIndex(pRtree, pLeaf, iDelete, &iCell); if( rc==SQLITE_OK ){ @@ -182322,7 +184394,7 @@ static int rtreeUpdate( rc = rc2; } } - if( pRtree->nAux ){ + if( rc==SQLITE_OK && pRtree->nAux ){ sqlite3_stmt *pUp = pRtree->pWriteAux; int jj; sqlite3_bind_int64(pUp, 1, *pRowid); @@ -182520,6 +184592,7 @@ static int rtreeSqlInit( }; sqlite3_stmt **appStmt[N_STATEMENT]; int i; + const int f = SQLITE_PREPARE_PERSISTENT|SQLITE_PREPARE_NO_VTAB; pRtree->db = db; @@ -182576,8 +184649,7 @@ static int rtreeSqlInit( } zSql = sqlite3_mprintf(zFormat, zDb, zPrefix); if( zSql ){ - rc = sqlite3_prepare_v3(db, zSql, -1, SQLITE_PREPARE_PERSISTENT, - appStmt[i], 0); + rc = sqlite3_prepare_v3(db, zSql, -1, f, appStmt[i], 0); }else{ rc = SQLITE_NOMEM; } @@ -182607,8 +184679,7 @@ static int rtreeSqlInit( if( zSql==0 ){ rc = SQLITE_NOMEM; }else{ - rc = sqlite3_prepare_v3(db, zSql, -1, SQLITE_PREPARE_PERSISTENT, - &pRtree->pWriteAux, 0); + rc = sqlite3_prepare_v3(db, zSql, -1, f, &pRtree->pWriteAux, 0); sqlite3_free(zSql); } } @@ -182684,6 +184755,7 @@ static int getNodeSize( *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); }else if( pRtree->iNodeSize<(512-64) ){ rc = SQLITE_CORRUPT_VTAB; + RTREE_IS_CORRUPT(pRtree); *pzErr = sqlite3_mprintf("undersize RTree blobs in \"%q_node\"", pRtree->zName); } @@ -182739,7 +184811,7 @@ static int rtreeInit( /* Allocate the sqlite3_vtab structure */ nDb = (int)strlen(argv[1]); nName = (int)strlen(argv[2]); - pRtree = (Rtree *)sqlite3_malloc(sizeof(Rtree)+nDb+nName+2); + pRtree = (Rtree *)sqlite3_malloc64(sizeof(Rtree)+nDb+nName+2); if( !pRtree ){ return SQLITE_NOMEM; } @@ -182836,49 +184908,45 @@ rtreeInit_fail: ** *2 coordinates. */ static void rtreenode(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){ - char *zText = 0; RtreeNode node; Rtree tree; int ii; + int nData; + int errCode; + sqlite3_str *pOut; UNUSED_PARAMETER(nArg); memset(&node, 0, sizeof(RtreeNode)); memset(&tree, 0, sizeof(Rtree)); tree.nDim = (u8)sqlite3_value_int(apArg[0]); + if( tree.nDim<1 || tree.nDim>5 ) return; tree.nDim2 = tree.nDim*2; tree.nBytesPerCell = 8 + 8 * tree.nDim; node.zData = (u8 *)sqlite3_value_blob(apArg[1]); + nData = sqlite3_value_bytes(apArg[1]); + if( nData<4 ) return; + if( nData0 ) sqlite3_str_append(pOut, " ", 1); + sqlite3_str_appendf(pOut, "{%lld", cell.iRowid); for(jj=0; jjrc==SQLITE_OK ); - if( pCheck->pGetNode==0 ){ + if( pCheck->rc==SQLITE_OK && pCheck->pGetNode==0 ){ pCheck->pGetNode = rtreeCheckPrepare(pCheck, "SELECT data FROM %Q.'%q_node' WHERE nodeno=?", pCheck->zDb, pCheck->zTab @@ -183020,7 +185087,7 @@ static u8 *rtreeCheckGetNode(RtreeCheck *pCheck, i64 iNode, int *pnNode){ if( sqlite3_step(pCheck->pGetNode)==SQLITE_ROW ){ int nNode = sqlite3_column_bytes(pCheck->pGetNode, 0); const u8 *pNode = (const u8*)sqlite3_column_blob(pCheck->pGetNode, 0); - pRet = sqlite3_malloc(nNode); + pRet = sqlite3_malloc64(nNode); if( pRet==0 ){ pCheck->rc = SQLITE_NOMEM; }else{ @@ -183499,6 +185566,14 @@ struct GeoPoly { */ #define GEOPOLY_SZ(N) (sizeof(GeoPoly) + sizeof(GeoCoord)*2*((N)-4)) +/* Macros to access coordinates of a GeoPoly. +** We have to use these macros, rather than just say p->a[i] in order +** to silence (incorrect) UBSAN warnings if the array index is too large. +*/ +#define GeoX(P,I) (((GeoCoord*)(P)->a)[(I)*2]) +#define GeoY(P,I) (((GeoCoord*)(P)->a)[(I)*2+1]) + + /* ** State of a parse of a GeoJSON input. */ @@ -183636,7 +185711,7 @@ static GeoPoly *geopolyParseJson(const unsigned char *z, int *pRc){ GeoPoly *pOut; int x = 1; s.nVertex--; /* Remove the redundant vertex at the end */ - pOut = sqlite3_malloc64( GEOPOLY_SZ(s.nVertex) ); + pOut = sqlite3_malloc64( GEOPOLY_SZ((sqlite3_int64)s.nVertex) ); x = 1; if( pOut==0 ) goto parse_json_err; pOut->nVertex = s.nVertex; @@ -183691,8 +185766,9 @@ static GeoPoly *geopolyFuncParam( memcpy(p->hdr, a, nByte); if( a[0] != *(unsigned char*)&x ){ int ii; - for(ii=0; iia[ii]); + for(ii=0; iihdr[0] ^= 1; } @@ -183751,9 +185827,9 @@ static void geopolyJsonFunc( int i; sqlite3_str_append(x, "[", 1); for(i=0; inVertex; i++){ - sqlite3_str_appendf(x, "[%!g,%!g],", p->a[i*2], p->a[i*2+1]); + sqlite3_str_appendf(x, "[%!g,%!g],", GeoX(p,i), GeoY(p,i)); } - sqlite3_str_appendf(x, "[%!g,%!g]]", p->a[0], p->a[1]); + sqlite3_str_appendf(x, "[%!g,%!g]]", GeoX(p,0), GeoY(p,0)); sqlite3_result_text(context, sqlite3_str_finish(x), -1, sqlite3_free); sqlite3_free(p); } @@ -183770,7 +185846,9 @@ static void geopolySvgFunc( int argc, sqlite3_value **argv ){ - GeoPoly *p = geopolyFuncParam(context, argv[0], 0); + GeoPoly *p; + if( argc<1 ) return; + p = geopolyFuncParam(context, argv[0], 0); if( p ){ sqlite3 *db = sqlite3_context_db_handle(context); sqlite3_str *x = sqlite3_str_new(db); @@ -183778,10 +185856,10 @@ static void geopolySvgFunc( char cSep = '\''; sqlite3_str_appendf(x, "a[i*2], p->a[i*2+1]); + sqlite3_str_appendf(x, "%c%g,%g", cSep, GeoX(p,i), GeoY(p,i)); cSep = ' '; } - sqlite3_str_appendf(x, " %g,%g'", p->a[0], p->a[1]); + sqlite3_str_appendf(x, " %g,%g'", GeoX(p,0), GeoY(p,0)); for(i=1; inVertex; ii++){ - x0 = p->a[ii*2]; - y0 = p->a[ii*2+1]; + x0 = GeoX(p,ii); + y0 = GeoY(p,ii); x1 = (GeoCoord)(A*x0 + B*y0 + E); y1 = (GeoCoord)(C*x0 + D*y0 + F); - p->a[ii*2] = x1; - p->a[ii*2+1] = y1; + GeoX(p,ii) = x1; + GeoY(p,ii) = y1; } sqlite3_result_blob(context, p->hdr, 4+8*p->nVertex, SQLITE_TRANSIENT); @@ -183850,12 +185928,12 @@ static double geopolyArea(GeoPoly *p){ double rArea = 0.0; int ii; for(ii=0; iinVertex-1; ii++){ - rArea += (p->a[ii*2] - p->a[ii*2+2]) /* (x0 - x1) */ - * (p->a[ii*2+1] + p->a[ii*2+3]) /* (y0 + y1) */ + rArea += (GeoX(p,ii) - GeoX(p,ii+1)) /* (x0 - x1) */ + * (GeoY(p,ii) + GeoY(p,ii+1)) /* (y0 + y1) */ * 0.5; } - rArea += (p->a[ii*2] - p->a[0]) /* (xN - x0) */ - * (p->a[ii*2+1] + p->a[1]) /* (yN + y0) */ + rArea += (GeoX(p,ii) - GeoX(p,0)) /* (xN - x0) */ + * (GeoY(p,ii) + GeoY(p,0)) /* (yN + y0) */ * 0.5; return rArea; } @@ -183902,13 +185980,13 @@ static void geopolyCcwFunc( if( p ){ if( geopolyArea(p)<0.0 ){ int ii, jj; - for(ii=2, jj=p->nVertex*2 - 2; iia[ii]; - p->a[ii] = p->a[jj]; - p->a[jj] = t; - t = p->a[ii+1]; - p->a[ii+1] = p->a[jj+1]; - p->a[jj+1] = t; + for(ii=1, jj=p->nVertex-1; iihdr, @@ -183968,8 +186046,8 @@ static void geopolyRegularFunc( p->hdr[3] = n&0xff; for(i=0; ia[i*2] = x - r*geopolySine(rAngle-0.5*GEOPOLY_PI); - p->a[i*2+1] = y + r*geopolySine(rAngle); + GeoX(p,i) = x - r*geopolySine(rAngle-0.5*GEOPOLY_PI); + GeoY(p,i) = y + r*geopolySine(rAngle); } sqlite3_result_blob(context, p->hdr, 4+8*n, SQLITE_TRANSIENT); sqlite3_free(p); @@ -184006,20 +186084,20 @@ static GeoPoly *geopolyBBox( } if( p ){ int ii; - mnX = mxX = p->a[0]; - mnY = mxY = p->a[1]; + mnX = mxX = GeoX(p,0); + mnY = mxY = GeoY(p,0); for(ii=1; iinVertex; ii++){ - double r = p->a[ii*2]; + double r = GeoX(p,ii); if( rmxX ) mxX = (float)r; - r = p->a[ii*2+1]; + r = GeoY(p,ii); if( rmxY ) mxY = (float)r; } if( pRc ) *pRc = SQLITE_OK; if( aCoord==0 ){ geopolyBboxFill: - pOut = sqlite3_realloc(p, GEOPOLY_SZ(4)); + pOut = sqlite3_realloc64(p, GEOPOLY_SZ(4)); if( pOut==0 ){ sqlite3_free(p); if( context ) sqlite3_result_error_nomem(context); @@ -184032,14 +186110,14 @@ static GeoPoly *geopolyBBox( pOut->hdr[1] = 0; pOut->hdr[2] = 0; pOut->hdr[3] = 4; - pOut->a[0] = mnX; - pOut->a[1] = mnY; - pOut->a[2] = mxX; - pOut->a[3] = mnY; - pOut->a[4] = mxX; - pOut->a[5] = mxY; - pOut->a[6] = mnX; - pOut->a[7] = mxY; + GeoX(pOut,0) = mnX; + GeoY(pOut,0) = mnY; + GeoX(pOut,1) = mxX; + GeoY(pOut,1) = mnY; + GeoX(pOut,2) = mxX; + GeoY(pOut,2) = mxY; + GeoX(pOut,3) = mnX; + GeoY(pOut,3) = mxY; }else{ sqlite3_free(p); aCoord[0].f = mnX; @@ -184177,14 +186255,14 @@ static void geopolyContainsPointFunc( int ii; if( p1==0 ) return; for(ii=0; iinVertex-1; ii++){ - v = pointBeneathLine(x0,y0,p1->a[ii*2],p1->a[ii*2+1], - p1->a[ii*2+2],p1->a[ii*2+3]); + v = pointBeneathLine(x0,y0,GeoX(p1,ii), GeoY(p1,ii), + GeoX(p1,ii+1),GeoY(p1,ii+1)); if( v==2 ) break; cnt += v; } if( v!=2 ){ - v = pointBeneathLine(x0,y0,p1->a[ii*2],p1->a[ii*2+1], - p1->a[0],p1->a[1]); + v = pointBeneathLine(x0,y0,GeoX(p1,ii), GeoY(p1,ii), + GeoX(p1,0), GeoY(p1,0)); } if( v==2 ){ sqlite3_result_int(context, 1); @@ -184306,10 +186384,10 @@ static void geopolyAddSegments( unsigned int i; GeoCoord *x; for(i=0; i<(unsigned)pPoly->nVertex-1; i++){ - x = pPoly->a + (i*2); + x = &GeoX(pPoly,i); geopolyAddOneSegment(p, x[0], x[1], x[2], x[3], side, i); } - x = pPoly->a + (i*2); + x = &GeoX(pPoly,i); geopolyAddOneSegment(p, x[0], x[1], pPoly->a[0], pPoly->a[1], side, i); } @@ -184415,9 +186493,9 @@ static GeoSegment *geopolySortSegmentsByYAndC(GeoSegment *pList){ ** Determine the overlap between two polygons */ static int geopolyOverlap(GeoPoly *p1, GeoPoly *p2){ - int nVertex = p1->nVertex + p2->nVertex + 2; + sqlite3_int64 nVertex = p1->nVertex + p2->nVertex + 2; GeoOverlap *p; - int nByte; + sqlite3_int64 nByte; GeoEvent *pThisEvent; double rX; int rc = 0; @@ -184429,7 +186507,7 @@ static int geopolyOverlap(GeoPoly *p1, GeoPoly *p2){ nByte = sizeof(GeoEvent)*nVertex*2 + sizeof(GeoSegment)*nVertex + sizeof(GeoOverlap); - p = sqlite3_malloc( nByte ); + p = sqlite3_malloc64( nByte ); if( p==0 ) return -1; p->aEvent = (GeoEvent*)&p[1]; p->aSegment = (GeoSegment*)&p->aEvent[nVertex*2]; @@ -184588,8 +186666,8 @@ static int geopolyInit( ){ int rc = SQLITE_OK; Rtree *pRtree; - int nDb; /* Length of string argv[1] */ - int nName; /* Length of string argv[2] */ + sqlite3_int64 nDb; /* Length of string argv[1] */ + sqlite3_int64 nName; /* Length of string argv[2] */ sqlite3_str *pSql; char *zSql; int ii; @@ -184597,9 +186675,9 @@ static int geopolyInit( sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1); /* Allocate the sqlite3_vtab structure */ - nDb = (int)strlen(argv[1]); - nName = (int)strlen(argv[2]); - pRtree = (Rtree *)sqlite3_malloc(sizeof(Rtree)+nDb+nName+2); + nDb = strlen(argv[1]); + nName = strlen(argv[2]); + pRtree = (Rtree *)sqlite3_malloc64(sizeof(Rtree)+nDb+nName+2); if( !pRtree ){ return SQLITE_NOMEM; } @@ -185254,12 +187332,12 @@ static void rtreeMatchArgFree(void *pArg){ static void geomCallback(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){ RtreeGeomCallback *pGeomCtx = (RtreeGeomCallback *)sqlite3_user_data(ctx); RtreeMatchArg *pBlob; - int nBlob; + sqlite3_int64 nBlob; int memErr = 0; nBlob = sizeof(RtreeMatchArg) + (nArg-1)*sizeof(RtreeDValue) + nArg*sizeof(sqlite3_value*); - pBlob = (RtreeMatchArg *)sqlite3_malloc(nBlob); + pBlob = (RtreeMatchArg *)sqlite3_malloc64(nBlob); if( !pBlob ){ sqlite3_result_error_nomem(ctx); }else{ @@ -185970,7 +188048,7 @@ static int icuCreate( if( argc>0 ){ n = strlen(argv[0])+1; } - p = (IcuTokenizer *)sqlite3_malloc(sizeof(IcuTokenizer)+n); + p = (IcuTokenizer *)sqlite3_malloc64(sizeof(IcuTokenizer)+n); if( !p ){ return SQLITE_NOMEM; } @@ -186027,7 +188105,7 @@ static int icuOpen( nInput = strlen(zInput); } nChar = nInput+1; - pCsr = (IcuCursor *)sqlite3_malloc( + pCsr = (IcuCursor *)sqlite3_malloc64( sizeof(IcuCursor) + /* IcuCursor */ ((nChar+3)&~3) * sizeof(UChar) + /* IcuCursor.aChar[] */ (nChar+1) * sizeof(int) /* IcuCursor.aOffset[] */ @@ -186599,7 +188677,11 @@ SQLITE_API sqlite3rbu *sqlite3rbu_open( ** name of the state database is "-vacuum", where ** is the name of the target database file. In this case, on UNIX, if the ** state database is not already present in the file-system, it is created -** with the same permissions as the target db is made. +** with the same permissions as the target db is made. +** +** With an RBU vacuum, it is an SQLITE_MISUSE error if the name of the +** state database ends with "-vactmp". This name is reserved for internal +** use. ** ** This function does not delete the state database after an RBU vacuum ** is completed, even if it created it. However, if the call to @@ -187020,6 +189102,11 @@ struct RbuUpdateStmt { ** it points to an array of flags nTblCol elements in size. The flag is ** set for each column that is either a part of the PK or a part of an ** index. Or clear otherwise. +** +** If there are one or more partial indexes on the table, all fields of +** this array set set to 1. This is because in that case, the module has +** no way to tell which fields will be required to add and remove entries +** from the partial indexes. ** */ struct RbuObjIter { @@ -187464,6 +189551,7 @@ static void rbuFossilDeltaFunc( }else{ nOut2 = rbuDeltaApply(aOrig, nOrig, aDelta, nDelta, aOut); if( nOut2!=nOut ){ + sqlite3_free(aOut); sqlite3_result_error(context, "corrupt fossil delta", -1); }else{ sqlite3_result_blob(context, aOut, nOut, sqlite3_free); @@ -187814,7 +189902,7 @@ static int rbuMPrintfExec(sqlite3rbu *p, sqlite3 *db, const char *zFmt, ...){ ** immediately without attempting the allocation or modifying the stored ** error code. */ -static void *rbuMalloc(sqlite3rbu *p, int nByte){ +static void *rbuMalloc(sqlite3rbu *p, sqlite3_int64 nByte){ void *pRet = 0; if( p->rc==SQLITE_OK ){ assert( nByte>0 ); @@ -187835,7 +189923,7 @@ static void *rbuMalloc(sqlite3rbu *p, int nByte){ ** error code in the RBU handle passed as the first argument. */ static void rbuAllocateIterArrays(sqlite3rbu *p, RbuObjIter *pIter, int nCol){ - int nByte = (2*sizeof(char*) + sizeof(int) + 3*sizeof(u8)) * nCol; + sqlite3_int64 nByte = (2*sizeof(char*) + sizeof(int) + 3*sizeof(u8)) * nCol; char **azNew; azNew = (char**)rbuMalloc(p, nByte); @@ -188029,8 +190117,12 @@ static void rbuObjIterCacheIndexedCols(sqlite3rbu *p, RbuObjIter *pIter){ pIter->nIndex = 0; while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pList) ){ const char *zIdx = (const char*)sqlite3_column_text(pList, 1); + int bPartial = sqlite3_column_int(pList, 4); sqlite3_stmt *pXInfo = 0; if( zIdx==0 ) break; + if( bPartial ){ + memset(pIter->abIndexed, 0x01, sizeof(u8)*pIter->nTblCol); + } p->rc = prepareFreeAndCollectError(p->dbMain, &pXInfo, &p->zErrmsg, sqlite3_mprintf("PRAGMA main.index_xinfo = %Q", zIdx) ); @@ -188475,7 +190567,7 @@ static char *rbuObjIterGetSetlist( */ static char *rbuObjIterGetBindlist(sqlite3rbu *p, int nBind){ char *zRet = 0; - int nByte = nBind*2 + 1; + sqlite3_int64 nByte = 2*(sqlite3_int64)nBind + 1; zRet = (char*)rbuMalloc(p, nByte); if( zRet ){ @@ -188737,6 +190829,62 @@ static void rbuTmpInsertFunc( } } +static char *rbuObjIterGetIndexWhere(sqlite3rbu *p, RbuObjIter *pIter){ + sqlite3_stmt *pStmt = 0; + int rc = p->rc; + char *zRet = 0; + + if( rc==SQLITE_OK ){ + rc = prepareAndCollectError(p->dbMain, &pStmt, &p->zErrmsg, + "SELECT trim(sql) FROM sqlite_master WHERE type='index' AND name=?" + ); + } + if( rc==SQLITE_OK ){ + int rc2; + rc = sqlite3_bind_text(pStmt, 1, pIter->zIdx, -1, SQLITE_STATIC); + if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ + const char *zSql = (const char*)sqlite3_column_text(pStmt, 0); + if( zSql ){ + int nParen = 0; /* Number of open parenthesis */ + int i; + for(i=0; zSql[i]; i++){ + char c = zSql[i]; + if( c=='(' ){ + nParen++; + } + else if( c==')' ){ + nParen--; + if( nParen==0 ){ + i++; + break; + } + }else if( c=='"' || c=='\'' || c=='`' ){ + for(i++; 1; i++){ + if( zSql[i]==c ){ + if( zSql[i+1]!=c ) break; + i++; + } + } + }else if( c=='[' ){ + for(i++; 1; i++){ + if( zSql[i]==']' ) break; + } + } + } + if( zSql[i] ){ + zRet = rbuStrndup(&zSql[i], &rc); + } + } + } + + rc2 = sqlite3_finalize(pStmt); + if( rc==SQLITE_OK ) rc = rc2; + } + + p->rc = rc; + return zRet; +} + /* ** Ensure that the SQLite statement handles required to update the ** target database object currently indicated by the iterator passed @@ -188766,6 +190914,7 @@ static int rbuObjIterPrepareAll( char *zImposterPK = 0; /* Primary key declaration for imposter */ char *zWhere = 0; /* WHERE clause on PK columns */ char *zBind = 0; + char *zPart = 0; int nBind = 0; assert( pIter->eType!=RBU_PK_VTAB ); @@ -188773,6 +190922,7 @@ static int rbuObjIterPrepareAll( p, pIter, &zImposterCols, &zImposterPK, &zWhere, &nBind ); zBind = rbuObjIterGetBindlist(p, nBind); + zPart = rbuObjIterGetIndexWhere(p, pIter); /* Create the imposter table used to write to this index. */ sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 0, 1); @@ -188805,28 +190955,30 @@ static int rbuObjIterPrepareAll( char *zSql; if( rbuIsVacuum(p) ){ zSql = sqlite3_mprintf( - "SELECT %s, 0 AS rbu_control FROM '%q' ORDER BY %s%s", + "SELECT %s, 0 AS rbu_control FROM '%q' %s ORDER BY %s%s", zCollist, pIter->zDataTbl, - zCollist, zLimit + zPart, zCollist, zLimit ); }else if( pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE ){ zSql = sqlite3_mprintf( - "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' ORDER BY %s%s", + "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' %s ORDER BY %s%s", zCollist, p->zStateDb, pIter->zDataTbl, - zCollist, zLimit + zPart, zCollist, zLimit ); }else{ zSql = sqlite3_mprintf( - "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' " + "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' %s " "UNION ALL " "SELECT %s, rbu_control FROM '%q' " - "WHERE typeof(rbu_control)='integer' AND rbu_control!=1 " + "%s %s typeof(rbu_control)='integer' AND rbu_control!=1 " "ORDER BY %s%s", - zCollist, p->zStateDb, pIter->zDataTbl, + zCollist, p->zStateDb, pIter->zDataTbl, zPart, zCollist, pIter->zDataTbl, + zPart, + (zPart ? "AND" : "WHERE"), zCollist, zLimit ); } @@ -188837,6 +190989,7 @@ static int rbuObjIterPrepareAll( sqlite3_free(zImposterPK); sqlite3_free(zWhere); sqlite3_free(zBind); + sqlite3_free(zPart); }else{ int bRbuRowid = (pIter->eType==RBU_PK_VTAB) ||(pIter->eType==RBU_PK_NONE) @@ -189257,7 +191410,7 @@ static void rbuOpenDatabase(sqlite3rbu *p, int *pbRetry){ if( *zExtra=='\0' ) zExtra = 0; } - zTarget = sqlite3_mprintf("file:%s-vacuum?rbu_memory=1%s%s", + zTarget = sqlite3_mprintf("file:%s-vactmp?rbu_memory=1%s%s", sqlite3_db_filename(p->dbRbu, "main"), (zExtra==0 ? "" : "&"), (zExtra==0 ? "" : zExtra) ); @@ -190523,6 +192676,12 @@ SQLITE_API sqlite3rbu *sqlite3rbu_vacuum( const char *zState ){ if( zTarget==0 ){ return rbuMisuseError(); } + if( zState ){ + int n = strlen(zState); + if( n>=7 && 0==memcmp("-vactmp", &zState[n-7], 7) ){ + return rbuMisuseError(); + } + } /* TODO: Check that both arguments are non-NULL */ return openRbuHandle(0, zTarget, zState); } @@ -190719,7 +192878,10 @@ SQLITE_API int sqlite3rbu_savestate(sqlite3rbu *p){ if( p->eStage==RBU_STAGE_OAL ){ assert( rc!=SQLITE_DONE ); if( rc==SQLITE_OK ) rc = sqlite3_exec(p->dbRbu, "COMMIT", 0, 0, 0); - if( rc==SQLITE_OK ) rc = sqlite3_exec(p->dbRbu, "BEGIN IMMEDIATE", 0, 0, 0); + if( rc==SQLITE_OK ){ + const char *zBegin = rbuIsVacuum(p) ? "BEGIN" : "BEGIN IMMEDIATE"; + rc = sqlite3_exec(p->dbRbu, zBegin, 0, 0, 0); + } if( rc==SQLITE_OK ) rc = sqlite3_exec(p->dbMain, "BEGIN IMMEDIATE", 0, 0,0); } @@ -191261,7 +193423,7 @@ static int rbuVfsShmMap( assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB) ); if( eStage==RBU_STAGE_OAL || eStage==RBU_STAGE_MOVE ){ if( iRegion<=p->nShm ){ - int nByte = (iRegion+1) * sizeof(char*); + sqlite3_int64 nByte = (iRegion+1) * sizeof(char*); char **apNew = (char**)sqlite3_realloc64(p->apShm, nByte); if( apNew==0 ){ rc = SQLITE_NOMEM; @@ -192250,6 +194412,10 @@ statNextRestart: goto statNextRestart; /* Tail recursion */ } pCsr->iPage++; + if( pCsr->iPage>=ArraySize(pCsr->aPage) ){ + statResetCsr(pCsr); + return SQLITE_CORRUPT_BKPT; + } assert( p==&pCsr->aPage[pCsr->iPage-1] ); if( p->iCell==p->nCell ){ @@ -192321,7 +194487,6 @@ static int statFilter( StatTable *pTab = (StatTable*)(pCursor->pVtab); char *zSql; int rc = SQLITE_OK; - char *zMaster; if( idxNum==1 ){ const char *zDbase = (const char*)sqlite3_value_text(argv[0]); @@ -192337,13 +194502,12 @@ static int statFilter( statResetCsr(pCsr); sqlite3_finalize(pCsr->pStmt); pCsr->pStmt = 0; - zMaster = pCsr->iDb==1 ? "sqlite_temp_master" : "sqlite_master"; zSql = sqlite3_mprintf( "SELECT 'sqlite_master' AS name, 1 AS rootpage, 'table' AS type" " UNION ALL " "SELECT name, rootpage, type" - " FROM \"%w\".%s WHERE rootpage!=0" - " ORDER BY name", pTab->db->aDb[pCsr->iDb].zDbSName, zMaster); + " FROM \"%w\".sqlite_master WHERE rootpage!=0" + " ORDER BY name", pTab->db->aDb[pCsr->iDb].zDbSName); if( zSql==0 ){ return SQLITE_NOMEM_BKPT; }else{ @@ -193231,7 +195395,7 @@ static void sessionPutI64(u8 *aBuf, sqlite3_int64 i){ static int sessionSerializeValue( u8 *aBuf, /* If non-NULL, write serialized value here */ sqlite3_value *pValue, /* Value to serialize */ - int *pnWrite /* IN/OUT: Increment by bytes written */ + sqlite3_int64 *pnWrite /* IN/OUT: Increment by bytes written */ ){ int nByte; /* Size of serialized value in bytes */ @@ -193770,9 +195934,9 @@ static int sessionGrowHash(int bPatchset, SessionTable *pTab){ if( pTab->nChange==0 || pTab->nEntry>=(pTab->nChange/2) ){ int i; SessionChange **apNew; - int nNew = (pTab->nChange ? pTab->nChange : 128) * 2; + sqlite3_int64 nNew = 2*(sqlite3_int64)(pTab->nChange ? pTab->nChange : 128); - apNew = (SessionChange **)sqlite3_malloc(sizeof(SessionChange *) * nNew); + apNew = (SessionChange **)sqlite3_malloc64(sizeof(SessionChange *) * nNew); if( apNew==0 ){ if( pTab->nChange==0 ){ return SQLITE_ERROR; @@ -193838,7 +196002,7 @@ static int sessionTableInfo( char *zPragma; sqlite3_stmt *pStmt; int rc; - int nByte; + sqlite3_int64 nByte; int nDbCol = 0; int nThis; int i; @@ -193881,7 +196045,7 @@ static int sessionTableInfo( if( rc==SQLITE_OK ){ nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1); - pAlloc = sqlite3_malloc(nByte); + pAlloc = sqlite3_malloc64(nByte); if( pAlloc==0 ){ rc = SQLITE_NOMEM; } @@ -194022,7 +196186,7 @@ static void sessionPreupdateOneChange( int iHash; int bNull = 0; int rc = SQLITE_OK; - SessionStat1Ctx stat1 = {0}; + SessionStat1Ctx stat1 = {{0,0,0,0,0},0}; if( pSession->rc ) return; @@ -194079,7 +196243,7 @@ static void sessionPreupdateOneChange( ** this is an SQLITE_UPDATE or SQLITE_DELETE), or just the PK ** values (if this is an INSERT). */ SessionChange *pChange; /* New change object */ - int nByte; /* Number of bytes to allocate */ + sqlite3_int64 nByte; /* Number of bytes to allocate */ int i; /* Used to iterate through columns */ assert( rc==SQLITE_OK ); @@ -194104,7 +196268,7 @@ static void sessionPreupdateOneChange( } /* Allocate the change object */ - pChange = (SessionChange *)sqlite3_malloc(nByte); + pChange = (SessionChange *)sqlite3_malloc64(nByte); if( !pChange ){ rc = SQLITE_NOMEM; goto error_out; @@ -194548,7 +196712,7 @@ SQLITE_API int sqlite3session_create( *ppSession = 0; /* Allocate and populate the new session object. */ - pNew = (sqlite3_session *)sqlite3_malloc(sizeof(sqlite3_session) + nDb + 1); + pNew = (sqlite3_session *)sqlite3_malloc64(sizeof(sqlite3_session) + nDb + 1); if( !pNew ) return SQLITE_NOMEM; memset(pNew, 0, sizeof(sqlite3_session)); pNew->db = db; @@ -194667,7 +196831,7 @@ SQLITE_API int sqlite3session_attach( if( !pTab ){ /* Allocate new SessionTable object. */ - pTab = (SessionTable *)sqlite3_malloc(sizeof(SessionTable) + nName + 1); + pTab = (SessionTable *)sqlite3_malloc64(sizeof(SessionTable) + nName + 1); if( !pTab ){ rc = SQLITE_NOMEM; }else{ @@ -194697,7 +196861,7 @@ SQLITE_API int sqlite3session_attach( ** If successful, return zero. Otherwise, if an OOM condition is encountered, ** set *pRc to SQLITE_NOMEM and return non-zero. */ -static int sessionBufferGrow(SessionBuffer *p, int nByte, int *pRc){ +static int sessionBufferGrow(SessionBuffer *p, size_t nByte, int *pRc){ if( *pRc==SQLITE_OK && p->nAlloc-p->nBufnAlloc ? p->nAlloc : 128; @@ -194727,7 +196891,7 @@ static int sessionBufferGrow(SessionBuffer *p, int nByte, int *pRc){ static void sessionAppendValue(SessionBuffer *p, sqlite3_value *pVal, int *pRc){ int rc = *pRc; if( rc==SQLITE_OK ){ - int nByte = 0; + sqlite3_int64 nByte = 0; rc = sessionSerializeValue(0, pVal, &nByte); sessionBufferGrow(p, nByte, &rc); if( rc==SQLITE_OK ){ @@ -195603,7 +197767,7 @@ static int sessionValueSetStr( ** argument to sqlite3ValueSetStr() and have the copy created ** automatically. But doing so makes it difficult to detect any OOM ** error. Hence the code to create the copy externally. */ - u8 *aCopy = sqlite3_malloc(nData+1); + u8 *aCopy = sqlite3_malloc64((sqlite3_int64)nData+1); if( aCopy==0 ) return SQLITE_NOMEM; memcpy(aCopy, aData, nData); sqlite3ValueSetStr(pVal, nData, (char*)aCopy, enc, sqlite3_free); @@ -195815,7 +197979,7 @@ static int sessionChangesetReadTblhdr(sqlite3_changeset_iter *p){ } if( rc==SQLITE_OK ){ - int iPK = sizeof(sqlite3_value*)*p->nCol*2; + size_t iPK = sizeof(sqlite3_value*)*p->nCol*2; memset(p->tblhdr.aBuf, 0, iPK); memcpy(&p->tblhdr.aBuf[iPK], &p->in.aData[p->in.iNext], nCopy); p->in.iNext += nCopy; @@ -196216,7 +198380,7 @@ static int sessionChangesetInvert( int iCol; if( 0==apVal ){ - apVal = (sqlite3_value **)sqlite3_malloc(sizeof(apVal[0])*nCol*2); + apVal = (sqlite3_value **)sqlite3_malloc64(sizeof(apVal[0])*nCol*2); if( 0==apVal ){ rc = SQLITE_NOMEM; goto finished_invert; @@ -196730,7 +198894,7 @@ static int sessionSeekToRow( } /* -** This function is called from within sqlite3changset_apply_v2() when +** This function is called from within sqlite3changeset_apply_v2() when ** a conflict is encountered and resolved using conflict resolution ** mode eType (either SQLITE_CHANGESET_OMIT or SQLITE_CHANGESET_REPLACE).. ** It adds a conflict resolution record to the buffer in @@ -197119,7 +199283,7 @@ static int sessionRetryConstraints( rc = sessionChangesetStart(&pIter2, 0, 0, cons.nBuf, cons.aBuf, 0); if( rc==SQLITE_OK ){ - int nByte = 2*pApply->nCol*sizeof(sqlite3_value*); + size_t nByte = 2*pApply->nCol*sizeof(sqlite3_value*); int rc2; pIter2->bPatchset = bPatchset; pIter2->zTab = (char*)zTab; @@ -197489,7 +199653,7 @@ static int sessionChangeMerge( int rc = SQLITE_OK; if( !pExist ){ - pNew = (SessionChange *)sqlite3_malloc(sizeof(SessionChange) + nRec); + pNew = (SessionChange *)sqlite3_malloc64(sizeof(SessionChange) + nRec); if( !pNew ){ return SQLITE_NOMEM; } @@ -197522,8 +199686,8 @@ static int sessionChangeMerge( if( pExist->op==SQLITE_DELETE && pExist->bIndirect ){ *ppNew = pExist; }else{ - int nByte = nRec + pExist->nRecord + sizeof(SessionChange); - pNew = (SessionChange*)sqlite3_malloc(nByte); + sqlite3_int64 nByte = nRec + pExist->nRecord + sizeof(SessionChange); + pNew = (SessionChange*)sqlite3_malloc64(nByte); if( pNew==0 ){ rc = SQLITE_NOMEM; }else{ @@ -197583,14 +199747,14 @@ static int sessionChangeMerge( assert( pNew==0 ); }else{ u8 *aExist = pExist->aRecord; - int nByte; + sqlite3_int64 nByte; u8 *aCsr; /* Allocate a new SessionChange object. Ensure that the aRecord[] ** buffer of the new object is large enough to hold any record that ** may be generated by combining the input records. */ nByte = sizeof(SessionChange) + pExist->nRecord + nRec; - pNew = (SessionChange *)sqlite3_malloc(nByte); + pNew = (SessionChange *)sqlite3_malloc64(nByte); if( !pNew ){ sqlite3_free(pExist); return SQLITE_NOMEM; @@ -197696,7 +199860,7 @@ static int sessionChangesetToHash( if( !pTab ){ SessionTable **ppTab; - pTab = sqlite3_malloc(sizeof(SessionTable) + nCol + nNew+1); + pTab = sqlite3_malloc64(sizeof(SessionTable) + nCol + nNew+1); if( !pTab ){ rc = SQLITE_NOMEM; break; @@ -198470,12 +200634,8 @@ struct Fts5PhraseIter { ** ** Usually, output parameter *piPhrase is set to the phrase number, *piCol ** to the column in which it occurs and *piOff the token offset of the -** first token of the phrase. The exception is if the table was created -** with the offsets=0 option specified. In this case *piOff is always -** set to -1. -** -** Returns SQLITE_OK if successful, or an error code (i.e. SQLITE_NOMEM) -** if an error occurs. +** first token of the phrase. Returns SQLITE_OK if successful, or an error +** code (i.e. SQLITE_NOMEM) if an error occurs. ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. @@ -198516,7 +200676,7 @@ struct Fts5PhraseIter { ** Save the pointer passed as the second argument as the extension functions ** "auxiliary data". The pointer may then be retrieved by the current or any ** future invocation of the same fts5 extension function made as part of -** of the same MATCH query using the xGetAuxdata() API. +** the same MATCH query using the xGetAuxdata() API. ** ** Each extension function is allocated a single auxiliary data slot for ** each FTS query (MATCH expression). If the extension function is invoked @@ -198531,7 +200691,7 @@ struct Fts5PhraseIter { ** The xDelete callback, if one is specified, is also invoked on the ** auxiliary data pointer after the FTS5 query has finished. ** -** If an error (e.g. an OOM condition) occurs within this function, an +** If an error (e.g. an OOM condition) occurs within this function, ** the auxiliary data is set to NULL and an error code returned. If the ** xDelete parameter was not NULL, it is invoked on the auxiliary data ** pointer before returning. @@ -198764,11 +200924,11 @@ struct Fts5ExtensionApi { ** the tokenizer substitutes "first" for "1st" and the query works ** as expected. ** -**
  • By adding multiple synonyms for a single term to the FTS index. -** In this case, when tokenizing query text, the tokenizer may -** provide multiple synonyms for a single term within the document. -** FTS5 then queries the index for each synonym individually. For -** example, faced with the query: +**
  • By querying the index for all synonyms of each query term +** separately. In this case, when tokenizing query text, the +** tokenizer may provide multiple synonyms for a single term +** within the document. FTS5 then queries the index for each +** synonym individually. For example, faced with the query: ** ** ** ... MATCH 'first place' @@ -198792,7 +200952,7 @@ struct Fts5ExtensionApi { ** "place". ** ** This way, even if the tokenizer does not provide synonyms -** when tokenizing query text (it should not - to do would be +** when tokenizing query text (it should not - to do so would be ** inefficient), it doesn't matter if the user queries for ** 'first + place' or '1st + place', as there are entries in the ** FTS index corresponding to both forms of the first token. @@ -199017,6 +201177,12 @@ SQLITE_API extern int sqlite3_fts5_may_be_corrupt; # define assert_nc(x) assert(x) #endif +/* +** A version of memcmp() that does not cause asan errors if one of the pointer +** parameters is NULL and the number of bytes to compare is zero. +*/ +#define fts5Memcmp(s1, s2, n) ((n)==0 ? 0 : memcmp((s1), (s2), (n))) + /* Mark a function parameter as unused, to suppress nuisance compiler ** warnings. */ #ifndef UNUSED_PARAM @@ -199204,7 +201370,7 @@ static void sqlite3Fts5Put32(u8*, int); static int sqlite3Fts5Get32(const u8*); #define FTS5_POS2COLUMN(iPos) (int)(iPos >> 32) -#define FTS5_POS2OFFSET(iPos) (int)(iPos & 0xFFFFFFFF) +#define FTS5_POS2OFFSET(iPos) (int)(iPos & 0x7FFFFFFF) typedef struct Fts5PoslistReader Fts5PoslistReader; struct Fts5PoslistReader { @@ -199239,7 +201405,7 @@ static int sqlite3Fts5PoslistNext64( ); /* Malloc utility */ -static void *sqlite3Fts5MallocZero(int *pRc, int nByte); +static void *sqlite3Fts5MallocZero(int *pRc, sqlite3_int64 nByte); static char *sqlite3Fts5Strndup(int *pRc, const char *pIn, int nIn); /* Character set tests (like isspace(), isalpha() etc.) */ @@ -199450,8 +201616,18 @@ static int sqlite3Fts5PutVarint(unsigned char *p, u64 v); /************************************************************************** -** Interface to code in fts5.c. +** Interface to code in fts5_main.c. +*/ + +/* +** Virtual-table object. */ +typedef struct Fts5Table Fts5Table; +struct Fts5Table { + sqlite3_vtab base; /* Base class used by SQLite core */ + Fts5Config *pConfig; /* Virtual table configuration */ + Fts5Index *pIndex; /* Full-text index */ +}; static int sqlite3Fts5GetTokenizer( Fts5Global*, @@ -199462,7 +201638,9 @@ static int sqlite3Fts5GetTokenizer( char **pzErr ); -static Fts5Index *sqlite3Fts5IndexFromCsrid(Fts5Global*, i64, Fts5Config **); +static Fts5Table *sqlite3Fts5TableFromCsrid(Fts5Global*, i64); + +static int sqlite3Fts5FlushToDisk(Fts5Table*); /* ** End of interface to code in fts5.c. @@ -199495,8 +201673,9 @@ static void sqlite3Fts5HashClear(Fts5Hash*); static int sqlite3Fts5HashQuery( Fts5Hash*, /* Hash table to query */ + int nPre, const char *pTerm, int nTerm, /* Query term */ - const u8 **ppDoclist, /* OUT: Pointer to doclist for pTerm */ + void **ppObj, /* OUT: Pointer to doclist for pTerm */ int *pnDoclist /* OUT: Size of doclist in bytes */ ); @@ -199718,7 +201897,7 @@ static int sqlite3Fts5UnicodeIsdiacritic(int c); static int sqlite3Fts5UnicodeFold(int c, int bRemoveDiacritic); static int sqlite3Fts5UnicodeCatParse(const char*, u8*); -static int sqlite3Fts5UnicodeCategory(int iCode); +static int sqlite3Fts5UnicodeCategory(u32 iCode); static void sqlite3Fts5UnicodeAscii(u8*, u8*); /* ** End of interface to code in fts5_unicode2.c. @@ -200622,41 +202801,70 @@ static void fts5yy_shift( fts5yyTraceShift(fts5yypParser, fts5yyNewState, "Shift"); } -/* The following table contains information about every rule that -** is used during the reduce. -*/ -static const struct { - fts5YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */ - signed char nrhs; /* Negative of the number of RHS symbols in the rule */ -} fts5yyRuleInfo[] = { - { 16, -1 }, /* (0) input ::= expr */ - { 20, -4 }, /* (1) colset ::= MINUS LCP colsetlist RCP */ - { 20, -3 }, /* (2) colset ::= LCP colsetlist RCP */ - { 20, -1 }, /* (3) colset ::= STRING */ - { 20, -2 }, /* (4) colset ::= MINUS STRING */ - { 21, -2 }, /* (5) colsetlist ::= colsetlist STRING */ - { 21, -1 }, /* (6) colsetlist ::= STRING */ - { 17, -3 }, /* (7) expr ::= expr AND expr */ - { 17, -3 }, /* (8) expr ::= expr OR expr */ - { 17, -3 }, /* (9) expr ::= expr NOT expr */ - { 17, -5 }, /* (10) expr ::= colset COLON LP expr RP */ - { 17, -3 }, /* (11) expr ::= LP expr RP */ - { 17, -1 }, /* (12) expr ::= exprlist */ - { 19, -1 }, /* (13) exprlist ::= cnearset */ - { 19, -2 }, /* (14) exprlist ::= exprlist cnearset */ - { 18, -1 }, /* (15) cnearset ::= nearset */ - { 18, -3 }, /* (16) cnearset ::= colset COLON nearset */ - { 22, -1 }, /* (17) nearset ::= phrase */ - { 22, -2 }, /* (18) nearset ::= CARET phrase */ - { 22, -5 }, /* (19) nearset ::= STRING LP nearphrases neardist_opt RP */ - { 23, -1 }, /* (20) nearphrases ::= phrase */ - { 23, -2 }, /* (21) nearphrases ::= nearphrases phrase */ - { 25, 0 }, /* (22) neardist_opt ::= */ - { 25, -2 }, /* (23) neardist_opt ::= COMMA STRING */ - { 24, -4 }, /* (24) phrase ::= phrase PLUS STRING star_opt */ - { 24, -2 }, /* (25) phrase ::= STRING star_opt */ - { 26, -1 }, /* (26) star_opt ::= STAR */ - { 26, 0 }, /* (27) star_opt ::= */ +/* For rule J, fts5yyRuleInfoLhs[J] contains the symbol on the left-hand side +** of that rule */ +static const fts5YYCODETYPE fts5yyRuleInfoLhs[] = { + 16, /* (0) input ::= expr */ + 20, /* (1) colset ::= MINUS LCP colsetlist RCP */ + 20, /* (2) colset ::= LCP colsetlist RCP */ + 20, /* (3) colset ::= STRING */ + 20, /* (4) colset ::= MINUS STRING */ + 21, /* (5) colsetlist ::= colsetlist STRING */ + 21, /* (6) colsetlist ::= STRING */ + 17, /* (7) expr ::= expr AND expr */ + 17, /* (8) expr ::= expr OR expr */ + 17, /* (9) expr ::= expr NOT expr */ + 17, /* (10) expr ::= colset COLON LP expr RP */ + 17, /* (11) expr ::= LP expr RP */ + 17, /* (12) expr ::= exprlist */ + 19, /* (13) exprlist ::= cnearset */ + 19, /* (14) exprlist ::= exprlist cnearset */ + 18, /* (15) cnearset ::= nearset */ + 18, /* (16) cnearset ::= colset COLON nearset */ + 22, /* (17) nearset ::= phrase */ + 22, /* (18) nearset ::= CARET phrase */ + 22, /* (19) nearset ::= STRING LP nearphrases neardist_opt RP */ + 23, /* (20) nearphrases ::= phrase */ + 23, /* (21) nearphrases ::= nearphrases phrase */ + 25, /* (22) neardist_opt ::= */ + 25, /* (23) neardist_opt ::= COMMA STRING */ + 24, /* (24) phrase ::= phrase PLUS STRING star_opt */ + 24, /* (25) phrase ::= STRING star_opt */ + 26, /* (26) star_opt ::= STAR */ + 26, /* (27) star_opt ::= */ +}; + +/* For rule J, fts5yyRuleInfoNRhs[J] contains the negative of the number +** of symbols on the right-hand side of that rule. */ +static const signed char fts5yyRuleInfoNRhs[] = { + -1, /* (0) input ::= expr */ + -4, /* (1) colset ::= MINUS LCP colsetlist RCP */ + -3, /* (2) colset ::= LCP colsetlist RCP */ + -1, /* (3) colset ::= STRING */ + -2, /* (4) colset ::= MINUS STRING */ + -2, /* (5) colsetlist ::= colsetlist STRING */ + -1, /* (6) colsetlist ::= STRING */ + -3, /* (7) expr ::= expr AND expr */ + -3, /* (8) expr ::= expr OR expr */ + -3, /* (9) expr ::= expr NOT expr */ + -5, /* (10) expr ::= colset COLON LP expr RP */ + -3, /* (11) expr ::= LP expr RP */ + -1, /* (12) expr ::= exprlist */ + -1, /* (13) exprlist ::= cnearset */ + -2, /* (14) exprlist ::= exprlist cnearset */ + -1, /* (15) cnearset ::= nearset */ + -3, /* (16) cnearset ::= colset COLON nearset */ + -1, /* (17) nearset ::= phrase */ + -2, /* (18) nearset ::= CARET phrase */ + -5, /* (19) nearset ::= STRING LP nearphrases neardist_opt RP */ + -1, /* (20) nearphrases ::= phrase */ + -2, /* (21) nearphrases ::= nearphrases phrase */ + 0, /* (22) neardist_opt ::= */ + -2, /* (23) neardist_opt ::= COMMA STRING */ + -4, /* (24) phrase ::= phrase PLUS STRING star_opt */ + -2, /* (25) phrase ::= STRING star_opt */ + -1, /* (26) star_opt ::= STAR */ + 0, /* (27) star_opt ::= */ }; static void fts5yy_accept(fts5yyParser*); /* Forward Declaration */ @@ -200688,7 +202896,7 @@ static fts5YYACTIONTYPE fts5yy_reduce( fts5yymsp = fts5yypParser->fts5yytos; #ifndef NDEBUG if( fts5yyTraceFILE && fts5yyruleno<(int)(sizeof(fts5yyRuleName)/sizeof(fts5yyRuleName[0])) ){ - fts5yysize = fts5yyRuleInfo[fts5yyruleno].nrhs; + fts5yysize = fts5yyRuleInfoNRhs[fts5yyruleno]; if( fts5yysize ){ fprintf(fts5yyTraceFILE, "%sReduce %d [%s], go to state %d.\n", fts5yyTracePrompt, @@ -200703,7 +202911,7 @@ static fts5YYACTIONTYPE fts5yy_reduce( /* Check that the stack is large enough to grow by a single entry ** if the RHS of the rule is empty. This ensures that there is room ** enough on the stack to push the LHS value */ - if( fts5yyRuleInfo[fts5yyruleno].nrhs==0 ){ + if( fts5yyRuleInfoNRhs[fts5yyruleno]==0 ){ #ifdef fts5YYTRACKMAXSTACKDEPTH if( (int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack)>fts5yypParser->fts5yyhwm ){ fts5yypParser->fts5yyhwm++; @@ -200887,9 +203095,9 @@ static fts5YYACTIONTYPE fts5yy_reduce( break; /********** End reduce actions ************************************************/ }; - assert( fts5yyrulenozOut = sqlite3_mprintf("%z%.*s", p->zOut, n, z); if( p->zOut==0 ) *pRc = SQLITE_NOMEM; @@ -201452,7 +203660,7 @@ static int fts5SentenceFinderAdd(Fts5SFinder *p, int iAdd){ int nNew = p->nFirstAlloc ? p->nFirstAlloc*2 : 64; int *aNew; - aNew = (int*)sqlite3_realloc(p->aFirst, nNew*sizeof(int)); + aNew = (int*)sqlite3_realloc64(p->aFirst, nNew*sizeof(int)); if( aNew==0 ) return SQLITE_NOMEM; p->aFirst = aNew; p->nFirstAlloc = nNew; @@ -201519,11 +203727,12 @@ static int fts5SnippetScore( int nInst; int nScore = 0; int iLast = 0; + sqlite3_int64 iEnd = (sqlite3_int64)iPos + nToken; rc = pApi->xInstCount(pFts, &nInst); for(i=0; ixInst(pFts, i, &ip, &ic, &iOff); - if( rc==SQLITE_OK && ic==iCol && iOff>=iPos && iOff<(iPos+nToken) ){ + if( rc==SQLITE_OK && ic==iCol && iOff>=iPos && iOffnDocsize ) iAdj = nDocsize - nToken; if( iAdj<0 ) iAdj = 0; - *piPos = iAdj; + *piPos = (int)iAdj; } return rc; @@ -201626,7 +203835,9 @@ static void fts5SnippetFunction( int jj; rc = pApi->xInst(pFts, ii, &ip, &ic, &io); - if( ic!=i || rc!=SQLITE_OK ) continue; + if( ic!=i ) continue; + if( io>nDocsize ) rc = FTS5_CORRUPT; + if( rc!=SQLITE_OK ) continue; memset(aSeen, 0, nPhrase); rc = fts5SnippetScore(pApi, pFts, nDocsize, aSeen, i, io, nToken, &nScore, &iAdj @@ -201752,17 +203963,17 @@ static int fts5Bm25GetData( int nPhrase; /* Number of phrases in query */ sqlite3_int64 nRow = 0; /* Number of rows in table */ sqlite3_int64 nToken = 0; /* Number of tokens in table */ - int nByte; /* Bytes of space to allocate */ + sqlite3_int64 nByte; /* Bytes of space to allocate */ int i; /* Allocate the Fts5Bm25Data object */ nPhrase = pApi->xPhraseCount(pFts); nByte = sizeof(Fts5Bm25Data) + nPhrase*2*sizeof(double); - p = (Fts5Bm25Data*)sqlite3_malloc(nByte); + p = (Fts5Bm25Data*)sqlite3_malloc64(nByte); if( p==0 ){ rc = SQLITE_NOMEM; }else{ - memset(p, 0, nByte); + memset(p, 0, (size_t)nByte); p->nPhrase = nPhrase; p->aIDF = (double*)&p[1]; p->aFreq = &p->aIDF[nPhrase]; @@ -201770,6 +203981,7 @@ static int fts5Bm25GetData( /* Calculate the average document length for this FTS5 table */ if( rc==SQLITE_OK ) rc = pApi->xRowCount(pFts, &nRow); + assert( rc!=SQLITE_OK || nRow>0 ); if( rc==SQLITE_OK ) rc = pApi->xColumnTotalSize(pFts, -1, &nToken); if( rc==SQLITE_OK ) p->avgdl = (double)nToken / (double)nRow; @@ -201895,8 +204107,6 @@ static int sqlite3Fts5AuxInit(fts5_api *pApi){ return rc; } - - /* ** 2014 May 31 ** @@ -201916,17 +204126,17 @@ static int sqlite3Fts5AuxInit(fts5_api *pApi){ static int sqlite3Fts5BufferSize(int *pRc, Fts5Buffer *pBuf, u32 nByte){ if( (u32)pBuf->nSpacenSpace ? pBuf->nSpace : 64; + u64 nNew = pBuf->nSpace ? pBuf->nSpace : 64; u8 *pNew; while( nNewp, nNew); + pNew = sqlite3_realloc64(pBuf->p, nNew); if( pNew==0 ){ *pRc = SQLITE_NOMEM; return 1; }else{ - pBuf->nSpace = nNew; + pBuf->nSpace = (int)nNew; pBuf->p = pNew; } } @@ -201951,7 +204161,7 @@ static void sqlite3Fts5Put32(u8 *aBuf, int iVal){ } static int sqlite3Fts5Get32(const u8 *aBuf){ - return (aBuf[0] << 24) + (aBuf[1] << 16) + (aBuf[2] << 8) + aBuf[3]; + return (int)((((u32)aBuf[0])<<24) + (aBuf[1]<<16) + (aBuf[2]<<8) + aBuf[3]); } /* @@ -202082,7 +204292,7 @@ static int sqlite3Fts5PoslistNext64( iOff = ((i64)iVal) << 32; fts5FastGetVarint32(a, i, iVal); } - *piOff = iOff + (iVal-2); + *piOff = iOff + ((iVal-2) & 0x7FFFFFFF); *pi = i; return 0; } @@ -202143,14 +204353,14 @@ static int sqlite3Fts5PoslistWriterAppend( return SQLITE_OK; } -static void *sqlite3Fts5MallocZero(int *pRc, int nByte){ +static void *sqlite3Fts5MallocZero(int *pRc, sqlite3_int64 nByte){ void *pRet = 0; if( *pRc==SQLITE_OK ){ - pRet = sqlite3_malloc(nByte); + pRet = sqlite3_malloc64(nByte); if( pRet==0 ){ if( nByte>0 ) *pRc = SQLITE_NOMEM; }else{ - memset(pRet, 0, nByte); + memset(pRet, 0, (size_t)nByte); } } return pRet; @@ -202589,7 +204799,7 @@ static int fts5ConfigParseSpecial( if( sqlite3_strnicmp("tokenize", zCmd, nCmd)==0 ){ const char *p = (const char*)zArg; - int nArg = (int)strlen(zArg) + 1; + sqlite3_int64 nArg = strlen(zArg) + 1; char **azArg = sqlite3Fts5MallocZero(&rc, sizeof(char*) * nArg); char *pDel = sqlite3Fts5MallocZero(&rc, nArg * 2); char *pSpace = pDel; @@ -202619,7 +204829,7 @@ static int fts5ConfigParseSpecial( rc = SQLITE_ERROR; }else{ rc = sqlite3Fts5GetTokenizer(pGlobal, - (const char**)azArg, nArg, &pConfig->pTok, &pConfig->pTokApi, + (const char**)azArg, (int)nArg, &pConfig->pTok, &pConfig->pTokApi, pzErr ); } @@ -202719,8 +204929,8 @@ static const char *fts5ConfigGobbleWord( ){ const char *zRet = 0; - int nIn = (int)strlen(zIn); - char *zOut = sqlite3_malloc(nIn+1); + sqlite3_int64 nIn = strlen(zIn); + char *zOut = sqlite3_malloc64(nIn+1); assert( *pRc==SQLITE_OK ); *pbQuoted = 0; @@ -202729,7 +204939,7 @@ static const char *fts5ConfigGobbleWord( if( zOut==0 ){ *pRc = SQLITE_NOMEM; }else{ - memcpy(zOut, zIn, nIn+1); + memcpy(zOut, zIn, (size_t)(nIn+1)); if( fts5_isopenquote(zOut[0]) ){ int ii = fts5Dequote(zOut); zRet = &zIn[ii]; @@ -202823,7 +205033,7 @@ static int sqlite3Fts5ConfigParse( int rc = SQLITE_OK; /* Return code */ Fts5Config *pRet; /* New object to return */ int i; - int nByte; + sqlite3_int64 nByte; *ppOut = pRet = (Fts5Config*)sqlite3_malloc(sizeof(Fts5Config)); if( pRet==0 ) return SQLITE_NOMEM; @@ -203467,7 +205677,7 @@ static int fts5ExprGetToken( return tok; } -static void *fts5ParseAlloc(u64 t){ return sqlite3_malloc((int)t); } +static void *fts5ParseAlloc(u64 t){ return sqlite3_malloc64((sqlite3_int64)t);} static void fts5ParseFree(void *p){ sqlite3_free(p); } static int sqlite3Fts5ExprNew( @@ -203612,8 +205822,8 @@ static int fts5ExprSynonymList( if( sqlite3Fts5IterEof(pIter)==0 && pIter->iRowid==iRowid ){ if( pIter->nData==0 ) continue; if( nIter==nAlloc ){ - int nByte = sizeof(Fts5PoslistReader) * nAlloc * 2; - Fts5PoslistReader *aNew = (Fts5PoslistReader*)sqlite3_malloc(nByte); + sqlite3_int64 nByte = sizeof(Fts5PoslistReader) * nAlloc * 2; + Fts5PoslistReader *aNew = (Fts5PoslistReader*)sqlite3_malloc64(nByte); if( aNew==0 ){ rc = SQLITE_NOMEM; goto synonym_poslist_out; @@ -203693,8 +205903,8 @@ static int fts5ExprPhraseIsMatch( /* If the aStatic[] array is not large enough, allocate a large array ** using sqlite3_malloc(). This approach could be improved upon. */ if( pPhrase->nTerm>ArraySize(aStatic) ){ - int nByte = sizeof(Fts5PoslistReader) * pPhrase->nTerm; - aIter = (Fts5PoslistReader*)sqlite3_malloc(nByte); + sqlite3_int64 nByte = sizeof(Fts5PoslistReader) * pPhrase->nTerm; + aIter = (Fts5PoslistReader*)sqlite3_malloc64(nByte); if( !aIter ) return SQLITE_NOMEM; } memset(aIter, 0, sizeof(Fts5PoslistReader) * pPhrase->nTerm); @@ -203828,7 +206038,7 @@ static int fts5ExprNearIsMatch(int *pRc, Fts5ExprNearset *pNear){ /* If the aStatic[] array is not large enough, allocate a large array ** using sqlite3_malloc(). This approach could be improved upon. */ if( pNear->nPhrase>ArraySize(aStatic) ){ - int nByte = sizeof(Fts5NearTrimmer) * pNear->nPhrase; + sqlite3_int64 nByte = sizeof(Fts5NearTrimmer) * pNear->nPhrase; a = (Fts5NearTrimmer*)sqlite3Fts5MallocZero(&rc, nByte); }else{ memset(aStatic, 0, sizeof(aStatic)); @@ -204737,18 +206947,20 @@ static Fts5ExprNearset *sqlite3Fts5ParseNearset( return pNear; } if( pNear==0 ){ - int nByte = sizeof(Fts5ExprNearset) + SZALLOC * sizeof(Fts5ExprPhrase*); - pRet = sqlite3_malloc(nByte); + sqlite3_int64 nByte; + nByte = sizeof(Fts5ExprNearset) + SZALLOC * sizeof(Fts5ExprPhrase*); + pRet = sqlite3_malloc64(nByte); if( pRet==0 ){ pParse->rc = SQLITE_NOMEM; }else{ - memset(pRet, 0, nByte); + memset(pRet, 0, (size_t)nByte); } }else if( (pNear->nPhrase % SZALLOC)==0 ){ int nNew = pNear->nPhrase + SZALLOC; - int nByte = sizeof(Fts5ExprNearset) + nNew * sizeof(Fts5ExprPhrase*); + sqlite3_int64 nByte; - pRet = (Fts5ExprNearset*)sqlite3_realloc(pNear, nByte); + nByte = sizeof(Fts5ExprNearset) + nNew * sizeof(Fts5ExprPhrase*); + pRet = (Fts5ExprNearset*)sqlite3_realloc64(pNear, nByte); if( pRet==0 ){ pParse->rc = SQLITE_NOMEM; } @@ -204812,12 +207024,12 @@ static int fts5ParseTokenize( if( pPhrase && pPhrase->nTerm>0 && (tflags & FTS5_TOKEN_COLOCATED) ){ Fts5ExprTerm *pSyn; - int nByte = sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer) + nToken+1; - pSyn = (Fts5ExprTerm*)sqlite3_malloc(nByte); + sqlite3_int64 nByte = sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer) + nToken+1; + pSyn = (Fts5ExprTerm*)sqlite3_malloc64(nByte); if( pSyn==0 ){ rc = SQLITE_NOMEM; }else{ - memset(pSyn, 0, nByte); + memset(pSyn, 0, (size_t)nByte); pSyn->zTerm = ((char*)pSyn) + sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer); memcpy(pSyn->zTerm, pToken, nToken); pSyn->pSynonym = pPhrase->aTerm[pPhrase->nTerm-1].pSynonym; @@ -204829,7 +207041,7 @@ static int fts5ParseTokenize( Fts5ExprPhrase *pNew; int nNew = SZALLOC + (pPhrase ? pPhrase->nTerm : 0); - pNew = (Fts5ExprPhrase*)sqlite3_realloc(pPhrase, + pNew = (Fts5ExprPhrase*)sqlite3_realloc64(pPhrase, sizeof(Fts5ExprPhrase) + sizeof(Fts5ExprTerm) * nNew ); if( pNew==0 ){ @@ -204915,9 +207127,9 @@ static Fts5ExprPhrase *sqlite3Fts5ParseTerm( if( pAppend==0 ){ if( (pParse->nPhrase % 8)==0 ){ - int nByte = sizeof(Fts5ExprPhrase*) * (pParse->nPhrase + 8); + sqlite3_int64 nByte = sizeof(Fts5ExprPhrase*) * (pParse->nPhrase + 8); Fts5ExprPhrase **apNew; - apNew = (Fts5ExprPhrase**)sqlite3_realloc(pParse->apPhrase, nByte); + apNew = (Fts5ExprPhrase**)sqlite3_realloc64(pParse->apPhrase, nByte); if( apNew==0 ){ pParse->rc = SQLITE_NOMEM; fts5ExprPhraseFree(sCtx.pPhrase); @@ -204972,10 +207184,12 @@ static int sqlite3Fts5ExprClonePhrase( if( rc==SQLITE_OK ){ Fts5Colset *pColsetOrig = pOrig->pNode->pNear->pColset; if( pColsetOrig ){ - int nByte = sizeof(Fts5Colset) + (pColsetOrig->nCol-1) * sizeof(int); - Fts5Colset *pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte); + sqlite3_int64 nByte; + Fts5Colset *pColset; + nByte = sizeof(Fts5Colset) + (pColsetOrig->nCol-1) * sizeof(int); + pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte); if( pColset ){ - memcpy(pColset, pColsetOrig, nByte); + memcpy(pColset, pColsetOrig, (size_t)nByte); } pNew->pRoot->pNear->pColset = pColset; } @@ -205093,7 +207307,7 @@ static Fts5Colset *fts5ParseColset( assert( pParse->rc==SQLITE_OK ); assert( iCol>=0 && iColpConfig->nCol ); - pNew = sqlite3_realloc(p, sizeof(Fts5Colset) + sizeof(int)*nCol); + pNew = sqlite3_realloc64(p, sizeof(Fts5Colset) + sizeof(int)*nCol); if( pNew==0 ){ pParse->rc = SQLITE_NOMEM; }else{ @@ -205189,10 +207403,10 @@ static Fts5Colset *sqlite3Fts5ParseColset( static Fts5Colset *fts5CloneColset(int *pRc, Fts5Colset *pOrig){ Fts5Colset *pRet; if( pOrig ){ - int nByte = sizeof(Fts5Colset) + (pOrig->nCol-1) * sizeof(int); + sqlite3_int64 nByte = sizeof(Fts5Colset) + (pOrig->nCol-1) * sizeof(int); pRet = (Fts5Colset*)sqlite3Fts5MallocZero(pRc, nByte); if( pRet ){ - memcpy(pRet, pOrig, nByte); + memcpy(pRet, pOrig, (size_t)nByte); } }else{ pRet = 0; @@ -205343,7 +207557,7 @@ static Fts5ExprNode *sqlite3Fts5ParseNode( if( pParse->rc==SQLITE_OK ){ int nChild = 0; /* Number of children of returned node */ - int nByte; /* Bytes of space to allocate for this node */ + sqlite3_int64 nByte; /* Bytes of space to allocate for this node */ assert( (eType!=FTS5_STRING && !pNear) || (eType==FTS5_STRING && !pLeft && !pRight) @@ -205475,7 +207689,7 @@ static Fts5ExprNode *sqlite3Fts5ParseImplicitAnd( } static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){ - int nByte = 0; + sqlite3_int64 nByte = 0; Fts5ExprTerm *p; char *zQuoted; @@ -205483,7 +207697,7 @@ static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){ for(p=pTerm; p; p=p->pSynonym){ nByte += (int)strlen(pTerm->zTerm) * 2 + 3 + 2; } - zQuoted = sqlite3_malloc(nByte); + zQuoted = sqlite3_malloc64(nByte); if( zQuoted ){ int i = 0; @@ -205723,7 +207937,7 @@ static void fts5ExprFunction( } nConfig = 3 + (nArg-iArg); - azConfig = (const char**)sqlite3_malloc(sizeof(char*) * nConfig); + azConfig = (const char**)sqlite3_malloc64(sizeof(char*) * nConfig); if( azConfig==0 ){ sqlite3_result_error_nomem(pCtx); return; @@ -205809,7 +208023,7 @@ static void fts5ExprIsAlnum( sqlite3Fts5UnicodeCatParse("N*", aArr); sqlite3Fts5UnicodeCatParse("Co", aArr); iCode = sqlite3_value_int(apVal[0]); - sqlite3_result_int(pCtx, aArr[sqlite3Fts5UnicodeCategory(iCode)]); + sqlite3_result_int(pCtx, aArr[sqlite3Fts5UnicodeCategory((u32)iCode)]); } static void fts5ExprFold( @@ -205904,7 +208118,7 @@ struct Fts5PoslistPopulator { static Fts5PoslistPopulator *sqlite3Fts5ExprClearPoslists(Fts5Expr *pExpr, int bLive){ Fts5PoslistPopulator *pRet; - pRet = sqlite3_malloc(sizeof(Fts5PoslistPopulator)*pExpr->nPhrase); + pRet = sqlite3_malloc64(sizeof(Fts5PoslistPopulator)*pExpr->nPhrase); if( pRet ){ int i; memset(pRet, 0, sizeof(Fts5PoslistPopulator)*pExpr->nPhrase); @@ -206104,7 +208318,6 @@ static int sqlite3Fts5ExprPhraseCollist( return rc; } - /* ** 2014 August 11 ** @@ -206197,20 +208410,20 @@ static int sqlite3Fts5HashNew(Fts5Config *pConfig, Fts5Hash **ppNew, int *pnByte if( pNew==0 ){ rc = SQLITE_NOMEM; }else{ - int nByte; + sqlite3_int64 nByte; memset(pNew, 0, sizeof(Fts5Hash)); pNew->pnByte = pnByte; pNew->eDetail = pConfig->eDetail; pNew->nSlot = 1024; nByte = sizeof(Fts5HashEntry*) * pNew->nSlot; - pNew->aSlot = (Fts5HashEntry**)sqlite3_malloc(nByte); + pNew->aSlot = (Fts5HashEntry**)sqlite3_malloc64(nByte); if( pNew->aSlot==0 ){ sqlite3_free(pNew); *ppNew = 0; rc = SQLITE_NOMEM; }else{ - memset(pNew->aSlot, 0, nByte); + memset(pNew->aSlot, 0, (size_t)nByte); } } return rc; @@ -206272,7 +208485,7 @@ static int fts5HashResize(Fts5Hash *pHash){ Fts5HashEntry **apNew; Fts5HashEntry **apOld = pHash->aSlot; - apNew = (Fts5HashEntry**)sqlite3_malloc(nNew*sizeof(Fts5HashEntry*)); + apNew = (Fts5HashEntry**)sqlite3_malloc64(nNew*sizeof(Fts5HashEntry*)); if( !apNew ) return SQLITE_NOMEM; memset(apNew, 0, nNew*sizeof(Fts5HashEntry*)); @@ -206294,19 +208507,25 @@ static int fts5HashResize(Fts5Hash *pHash){ return SQLITE_OK; } -static void fts5HashAddPoslistSize(Fts5Hash *pHash, Fts5HashEntry *p){ +static int fts5HashAddPoslistSize( + Fts5Hash *pHash, + Fts5HashEntry *p, + Fts5HashEntry *p2 +){ + int nRet = 0; if( p->iSzPoslist ){ - u8 *pPtr = (u8*)p; + u8 *pPtr = p2 ? (u8*)p2 : (u8*)p; + int nData = p->nData; if( pHash->eDetail==FTS5_DETAIL_NONE ){ - assert( p->nData==p->iSzPoslist ); + assert( nData==p->iSzPoslist ); if( p->bDel ){ - pPtr[p->nData++] = 0x00; + pPtr[nData++] = 0x00; if( p->bContent ){ - pPtr[p->nData++] = 0x00; + pPtr[nData++] = 0x00; } } }else{ - int nSz = (p->nData - p->iSzPoslist - 1); /* Size in bytes */ + int nSz = (nData - p->iSzPoslist - 1); /* Size in bytes */ int nPos = nSz*2 + p->bDel; /* Value of nPos field */ assert( p->bDel==0 || p->bDel==1 ); @@ -206316,14 +208535,19 @@ static void fts5HashAddPoslistSize(Fts5Hash *pHash, Fts5HashEntry *p){ int nByte = sqlite3Fts5GetVarintLen((u32)nPos); memmove(&pPtr[p->iSzPoslist + nByte], &pPtr[p->iSzPoslist + 1], nSz); sqlite3Fts5PutVarint(&pPtr[p->iSzPoslist], nPos); - p->nData += (nByte-1); + nData += (nByte-1); } } - p->iSzPoslist = 0; - p->bDel = 0; - p->bContent = 0; + nRet = nData - p->nData; + if( p2==0 ){ + p->iSzPoslist = 0; + p->bDel = 0; + p->bContent = 0; + p->nData = nData; + } } + return nRet; } /* @@ -206366,7 +208590,7 @@ static int sqlite3Fts5HashWrite( if( p==0 ){ /* Figure out how much space to allocate */ char *zKey; - int nByte = sizeof(Fts5HashEntry) + (nToken+1) + 1 + 64; + sqlite3_int64 nByte = sizeof(Fts5HashEntry) + (nToken+1) + 1 + 64; if( nByte<128 ) nByte = 128; /* Grow the Fts5Hash.aSlot[] array if necessary. */ @@ -206377,10 +208601,10 @@ static int sqlite3Fts5HashWrite( } /* Allocate new Fts5HashEntry and add it to the hash table. */ - p = (Fts5HashEntry*)sqlite3_malloc(nByte); + p = (Fts5HashEntry*)sqlite3_malloc64(nByte); if( !p ) return SQLITE_NOMEM; memset(p, 0, sizeof(Fts5HashEntry)); - p->nAlloc = nByte; + p->nAlloc = (int)nByte; zKey = fts5EntryKey(p); zKey[0] = bByte; memcpy(&zKey[1], pToken, nToken); @@ -206416,12 +208640,12 @@ static int sqlite3Fts5HashWrite( ** + 5 bytes for the new position offset (32-bit max). */ if( (p->nAlloc - p->nData) < (9 + 4 + 1 + 3 + 5) ){ - int nNew = p->nAlloc * 2; + sqlite3_int64 nNew = p->nAlloc * 2; Fts5HashEntry *pNew; Fts5HashEntry **pp; - pNew = (Fts5HashEntry*)sqlite3_realloc(p, nNew); + pNew = (Fts5HashEntry*)sqlite3_realloc64(p, nNew); if( pNew==0 ) return SQLITE_NOMEM; - pNew->nAlloc = nNew; + pNew->nAlloc = (int)nNew; for(pp=&pHash->aSlot[iHash]; *pp!=p; pp=&(*pp)->pHashNext); *pp = pNew; p = pNew; @@ -206435,7 +208659,7 @@ static int sqlite3Fts5HashWrite( /* If this is a new rowid, append the 4-byte size field for the previous ** entry, and the new rowid for this entry. */ if( iRowid!=p->iRowid ){ - fts5HashAddPoslistSize(pHash, p); + fts5HashAddPoslistSize(pHash, p, 0); p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iRowid - p->iRowid); p->iRowid = iRowid; bNew = 1; @@ -206545,14 +208769,16 @@ static int fts5HashEntrySort( int i; *ppSorted = 0; - ap = sqlite3_malloc(sizeof(Fts5HashEntry*) * nMergeSlot); + ap = sqlite3_malloc64(sizeof(Fts5HashEntry*) * nMergeSlot); if( !ap ) return SQLITE_NOMEM; memset(ap, 0, sizeof(Fts5HashEntry*) * nMergeSlot); for(iSlot=0; iSlotnSlot; iSlot++){ Fts5HashEntry *pIter; for(pIter=pHash->aSlot[iSlot]; pIter; pIter=pIter->pHashNext){ - if( pTerm==0 || 0==memcmp(fts5EntryKey(pIter), pTerm, nTerm) ){ + if( pTerm==0 + || (pIter->nKey+1>=nTerm && 0==memcmp(fts5EntryKey(pIter), pTerm, nTerm)) + ){ Fts5HashEntry *pEntry = pIter; pEntry->pScanNext = 0; for(i=0; ap[i]; i++){ @@ -206580,8 +208806,9 @@ static int fts5HashEntrySort( */ static int sqlite3Fts5HashQuery( Fts5Hash *pHash, /* Hash table to query */ + int nPre, const char *pTerm, int nTerm, /* Query term */ - const u8 **ppDoclist, /* OUT: Pointer to doclist for pTerm */ + void **ppOut, /* OUT: Pointer to new object */ int *pnDoclist /* OUT: Size of doclist in bytes */ ){ unsigned int iHash = fts5HashKey(pHash->nSlot, (const u8*)pTerm, nTerm); @@ -206590,15 +208817,25 @@ static int sqlite3Fts5HashQuery( for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){ zKey = fts5EntryKey(p); - if( memcmp(zKey, pTerm, nTerm)==0 && zKey[nTerm]==0 ) break; + assert( p->nKey+1==(int)strlen(zKey) ); + if( nTerm==p->nKey+1 && memcmp(zKey, pTerm, nTerm)==0 ) break; } if( p ){ - fts5HashAddPoslistSize(pHash, p); - *ppDoclist = (const u8*)&zKey[nTerm+1]; - *pnDoclist = p->nData - (sizeof(Fts5HashEntry) + nTerm + 1); + int nHashPre = sizeof(Fts5HashEntry) + nTerm + 1; + int nList = p->nData - nHashPre; + u8 *pRet = (u8*)(*ppOut = sqlite3_malloc64(nPre + nList + 10)); + if( pRet ){ + Fts5HashEntry *pFaux = (Fts5HashEntry*)&pRet[nPre-nHashPre]; + memcpy(&pRet[nPre], &((u8*)p)[nHashPre], nList); + nList += fts5HashAddPoslistSize(pHash, p, pFaux); + *pnDoclist = nList; + }else{ + *pnDoclist = 0; + return SQLITE_NOMEM; + } }else{ - *ppDoclist = 0; + *ppOut = 0; *pnDoclist = 0; } @@ -206631,7 +208868,7 @@ static void sqlite3Fts5HashScanEntry( if( (p = pHash->pScan) ){ char *zKey = fts5EntryKey(p); int nTerm = (int)strlen(zKey); - fts5HashAddPoslistSize(pHash, p); + fts5HashAddPoslistSize(pHash, p, 0); *pzTerm = zKey; *ppDoclist = (const u8*)&zKey[nTerm+1]; *pnDoclist = p->nData - (sizeof(Fts5HashEntry) + nTerm + 1); @@ -206642,7 +208879,6 @@ static void sqlite3Fts5HashScanEntry( } } - /* ** 2014 May 31 ** @@ -207157,7 +209393,6 @@ struct Fts5Iter { Fts5IndexIter base; /* Base class containing output vars */ Fts5Index *pIndex; /* Index that owns this iterator */ - Fts5Structure *pStruct; /* Database structure for this iterator */ Fts5Buffer poslist; /* Buffer containing current poslist */ Fts5Colset *pColset; /* Restrict matches to these columns */ @@ -207218,7 +209453,7 @@ static u16 fts5GetU16(const u8 *aIn){ ** If an OOM error is encountered, return NULL and set the error code in ** the Fts5Index handle passed as the first argument. */ -static void *fts5IdxMalloc(Fts5Index *p, int nByte){ +static void *fts5IdxMalloc(Fts5Index *p, sqlite3_int64 nByte){ return sqlite3Fts5MallocZero(&p->rc, nByte); } @@ -207252,7 +209487,7 @@ static int fts5BufferCompareBlob( */ static int fts5BufferCompare(Fts5Buffer *pLeft, Fts5Buffer *pRight){ int nCmp = MIN(pLeft->n, pRight->n); - int res = memcmp(pLeft->p, pRight->p, nCmp); + int res = fts5Memcmp(pLeft->p, pRight->p, nCmp); return (res==0 ? (pLeft->n - pRight->n) : res); } @@ -207318,8 +209553,8 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){ if( rc==SQLITE_OK ){ u8 *aOut = 0; /* Read blob data into this buffer */ int nByte = sqlite3_blob_bytes(p->pReader); - int nAlloc = sizeof(Fts5Data) + nByte + FTS5_DATA_PADDING; - pRet = (Fts5Data*)sqlite3_malloc(nAlloc); + sqlite3_int64 nAlloc = sizeof(Fts5Data) + nByte + FTS5_DATA_PADDING; + pRet = (Fts5Data*)sqlite3_malloc64(nAlloc); if( pRet ){ pRet->nn = nByte; aOut = pRet->p = (u8*)&pRet[1]; @@ -207335,6 +209570,7 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){ pRet = 0; }else{ /* TODO1: Fix this */ + pRet->p[nByte] = 0x00; pRet->szLeaf = fts5GetU16(&pRet->p[2]); } } @@ -207374,7 +209610,8 @@ static int fts5IndexPrepareStmt( if( p->rc==SQLITE_OK ){ if( zSql ){ p->rc = sqlite3_prepare_v3(p->pConfig->db, zSql, -1, - SQLITE_PREPARE_PERSISTENT, ppStmt, 0); + SQLITE_PREPARE_PERSISTENT|SQLITE_PREPARE_NO_VTAB, + ppStmt, 0); }else{ p->rc = SQLITE_NOMEM; } @@ -207415,23 +209652,12 @@ static void fts5DataDelete(Fts5Index *p, i64 iFirst, i64 iLast){ if( p->rc!=SQLITE_OK ) return; if( p->pDeleter==0 ){ - int rc; Fts5Config *pConfig = p->pConfig; char *zSql = sqlite3_mprintf( "DELETE FROM '%q'.'%q_data' WHERE id>=? AND id<=?", pConfig->zDb, pConfig->zName ); - if( zSql==0 ){ - rc = SQLITE_NOMEM; - }else{ - rc = sqlite3_prepare_v3(pConfig->db, zSql, -1, - SQLITE_PREPARE_PERSISTENT, &p->pDeleter, 0); - sqlite3_free(zSql); - } - if( rc!=SQLITE_OK ){ - p->rc = rc; - return; - } + if( fts5IndexPrepareStmt(p, &p->pDeleter, zSql) ) return; } sqlite3_bind_int64(p->pDeleter, 1, iFirst); @@ -207503,7 +209729,7 @@ static int fts5StructureDecode( int iLvl; int nLevel = 0; int nSegment = 0; - int nByte; /* Bytes of space to allocate at pRet */ + sqlite3_int64 nByte; /* Bytes of space to allocate at pRet */ Fts5Structure *pRet = 0; /* Structure object to return */ /* Grab the cookie value */ @@ -207514,6 +209740,11 @@ static int fts5StructureDecode( ** structure record. */ i += fts5GetVarint32(&pData[i], nLevel); i += fts5GetVarint32(&pData[i], nSegment); + if( nLevel>FTS5_MAX_SEGMENT || nLevel<0 + || nSegment>FTS5_MAX_SEGMENT || nSegment<0 + ){ + return FTS5_CORRUPT; + } nByte = ( sizeof(Fts5Structure) + /* Main structure */ sizeof(Fts5StructureLevel) * (nLevel-1) /* aLevel[] array */ @@ -207536,25 +209767,35 @@ static int fts5StructureDecode( }else{ i += fts5GetVarint32(&pData[i], pLvl->nMerge); i += fts5GetVarint32(&pData[i], nTotal); - assert( nTotal>=pLvl->nMerge ); + if( nTotalnMerge ) rc = FTS5_CORRUPT; pLvl->aSeg = (Fts5StructureSegment*)sqlite3Fts5MallocZero(&rc, nTotal * sizeof(Fts5StructureSegment) ); + nSegment -= nTotal; } if( rc==SQLITE_OK ){ pLvl->nSeg = nTotal; for(iSeg=0; iSegaSeg[iSeg]; if( i>=nData ){ rc = FTS5_CORRUPT; break; } - i += fts5GetVarint32(&pData[i], pLvl->aSeg[iSeg].iSegid); - i += fts5GetVarint32(&pData[i], pLvl->aSeg[iSeg].pgnoFirst); - i += fts5GetVarint32(&pData[i], pLvl->aSeg[iSeg].pgnoLast); + i += fts5GetVarint32(&pData[i], pSeg->iSegid); + i += fts5GetVarint32(&pData[i], pSeg->pgnoFirst); + i += fts5GetVarint32(&pData[i], pSeg->pgnoLast); + if( pSeg->pgnoLastpgnoFirst ){ + rc = FTS5_CORRUPT; + break; + } } + if( iLvl>0 && pLvl[-1].nMerge && nTotal==0 ) rc = FTS5_CORRUPT; + if( iLvl==nLevel-1 && pLvl->nMerge ) rc = FTS5_CORRUPT; } } + if( nSegment!=0 && rc==SQLITE_OK ) rc = FTS5_CORRUPT; + if( rc!=SQLITE_OK ){ fts5StructureRelease(pRet); pRet = 0; @@ -207572,12 +209813,12 @@ static void fts5StructureAddLevel(int *pRc, Fts5Structure **ppStruct){ if( *pRc==SQLITE_OK ){ Fts5Structure *pStruct = *ppStruct; int nLevel = pStruct->nLevel; - int nByte = ( + sqlite3_int64 nByte = ( sizeof(Fts5Structure) + /* Main structure */ sizeof(Fts5StructureLevel) * (nLevel+1) /* aLevel[] array */ ); - pStruct = sqlite3_realloc(pStruct, nByte); + pStruct = sqlite3_realloc64(pStruct, nByte); if( pStruct ){ memset(&pStruct->aLevel[nLevel], 0, sizeof(Fts5StructureLevel)); pStruct->nLevel++; @@ -207602,10 +209843,10 @@ static void fts5StructureExtendLevel( if( *pRc==SQLITE_OK ){ Fts5StructureLevel *pLvl = &pStruct->aLevel[iLvl]; Fts5StructureSegment *aNew; - int nByte; + sqlite3_int64 nByte; nByte = (pLvl->nSeg + nExtra) * sizeof(Fts5StructureSegment); - aNew = sqlite3_realloc(pLvl->aSeg, nByte); + aNew = sqlite3_realloc64(pLvl->aSeg, nByte); if( aNew ){ if( bInsert==0 ){ memset(&aNew[pLvl->nSeg], 0, sizeof(Fts5StructureSegment) * nExtra); @@ -208119,10 +210360,10 @@ static Fts5DlidxIter *fts5DlidxIterInit( int bDone = 0; for(i=0; p->rc==SQLITE_OK && bDone==0; i++){ - int nByte = sizeof(Fts5DlidxIter) + i * sizeof(Fts5DlidxLvl); + sqlite3_int64 nByte = sizeof(Fts5DlidxIter) + i * sizeof(Fts5DlidxLvl); Fts5DlidxIter *pNew; - pNew = (Fts5DlidxIter*)sqlite3_realloc(pIter, nByte); + pNew = (Fts5DlidxIter*)sqlite3_realloc64(pIter, nByte); if( pNew==0 ){ p->rc = SQLITE_NOMEM; }else{ @@ -208292,12 +210533,13 @@ static void fts5SegIterLoadTerm(Fts5Index *p, Fts5SegIter *pIter, int nKeep){ int nNew; /* Bytes of new data */ iOff += fts5GetVarint32(&a[iOff], nNew); - if( iOff+nNew>pIter->pLeaf->nn ){ + if( iOff+nNew>pIter->pLeaf->szLeaf || nKeep>pIter->term.n || nNew==0 ){ p->rc = FTS5_CORRUPT; return; } pIter->term.n = nKeep; fts5BufferAppendBlob(&p->rc, &pIter->term, nNew, &a[iOff]); + assert( pIter->term.n<=pIter->term.nSpace ); iOff += nNew; pIter->iTermLeafOffset = iOff; pIter->iTermLeafPgno = pIter->iLeafPgno; @@ -208362,7 +210604,7 @@ static void fts5SegIterInit( if( p->rc==SQLITE_OK ){ pIter->iLeafOffset = 4; assert_nc( pIter->pLeaf->nn>4 ); - assert( fts5LeafFirstTermOff(pIter->pLeaf)==4 ); + assert_nc( fts5LeafFirstTermOff(pIter->pLeaf)==4 ); pIter->iPgidxOff = pIter->pLeaf->szLeaf+1; fts5SegIterLoadTerm(p, pIter, 0); fts5SegIterLoadNPos(p, pIter); @@ -208418,7 +210660,7 @@ static void fts5SegIterReverseInitPage(Fts5Index *p, Fts5SegIter *pIter){ /* If necessary, grow the pIter->aRowidOffset[] array. */ if( iRowidOffset>=pIter->nRowidOffset ){ int nNew = pIter->nRowidOffset + 8; - int *aNew = (int*)sqlite3_realloc(pIter->aRowidOffset, nNew*sizeof(int)); + int *aNew = (int*)sqlite3_realloc64(pIter->aRowidOffset,nNew*sizeof(int)); if( aNew==0 ){ p->rc = SQLITE_NOMEM; break; @@ -208872,10 +211114,10 @@ static void fts5LeafSeek( int szLeaf = pIter->pLeaf->szLeaf; int n = pIter->pLeaf->nn; - int nMatch = 0; - int nKeep = 0; - int nNew = 0; - int iTermOff; + u32 nMatch = 0; + u32 nKeep = 0; + u32 nNew = 0; + u32 iTermOff; int iPgidx; /* Current offset in pgidx */ int bEndOfPage = 0; @@ -208899,15 +211141,15 @@ static void fts5LeafSeek( assert( nKeep>=nMatch ); if( nKeep==nMatch ){ - int nCmp; - int i; - nCmp = MIN(nNew, nTerm-nMatch); + u32 nCmp; + u32 i; + nCmp = (u32)MIN(nNew, nTerm-nMatch); for(i=0; ipLeaf->p[iPgidx], iOff); if( iOff<4 || iOff>=pIter->pLeaf->szLeaf ){ p->rc = FTS5_CORRUPT; + return; }else{ nKeep = 0; iTermOff = iOff; @@ -208963,8 +211206,11 @@ static void fts5LeafSeek( } search_success: - pIter->iLeafOffset = iOff + nNew; + if( pIter->iLeafOffset>n || nNew<1 ){ + p->rc = FTS5_CORRUPT; + return; + } pIter->iTermLeafOffset = pIter->iLeafOffset; pIter->iTermLeafPgno = pIter->iLeafPgno; @@ -209071,7 +211317,7 @@ static void fts5SegIterSeekInit( ** 4) the FTS5INDEX_QUERY_SCAN flag was set and the iterator points ** to an entry with a term greater than or equal to (pTerm/nTerm). */ - assert( p->rc!=SQLITE_OK /* 1 */ + assert_nc( p->rc!=SQLITE_OK /* 1 */ || pIter->pLeaf==0 /* 2 */ || fts5BufferCompareBlob(&pIter->term, pTerm, nTerm)==0 /* 3 */ || (bGe && fts5BufferCompareBlob(&pIter->term, pTerm, nTerm)>0) /* 4 */ @@ -209092,31 +211338,40 @@ static void fts5SegIterHashInit( int flags, /* Mask of FTS5INDEX_XXX flags */ Fts5SegIter *pIter /* Object to populate */ ){ - const u8 *pList = 0; int nList = 0; const u8 *z = 0; int n = 0; + Fts5Data *pLeaf = 0; assert( p->pHash ); assert( p->rc==SQLITE_OK ); if( pTerm==0 || (flags & FTS5INDEX_QUERY_SCAN) ){ + const u8 *pList = 0; + p->rc = sqlite3Fts5HashScanInit(p->pHash, (const char*)pTerm, nTerm); sqlite3Fts5HashScanEntry(p->pHash, (const char**)&z, &pList, &nList); n = (z ? (int)strlen((const char*)z) : 0); + if( pList ){ + pLeaf = fts5IdxMalloc(p, sizeof(Fts5Data)); + if( pLeaf ){ + pLeaf->p = (u8*)pList; + } + } }else{ - pIter->flags |= FTS5_SEGITER_ONETERM; - sqlite3Fts5HashQuery(p->pHash, (const char*)pTerm, nTerm, &pList, &nList); + p->rc = sqlite3Fts5HashQuery(p->pHash, sizeof(Fts5Data), + (const char*)pTerm, nTerm, (void**)&pLeaf, &nList + ); + if( pLeaf ){ + pLeaf->p = (u8*)&pLeaf[1]; + } z = pTerm; n = nTerm; + pIter->flags |= FTS5_SEGITER_ONETERM; } - if( pList ){ - Fts5Data *pLeaf; + if( pLeaf ){ sqlite3Fts5BufferSet(&p->rc, &pIter->term, n, z); - pLeaf = fts5IdxMalloc(p, sizeof(Fts5Data)); - if( pLeaf==0 ) return; - pLeaf->p = (u8*)pList; pLeaf->nn = pLeaf->szLeaf = nList; pIter->pLeaf = pLeaf; pIter->iLeafOffset = fts5GetVarint(pLeaf->p, (u64*)&pIter->iRowid); @@ -209169,7 +211424,7 @@ static void fts5AssertComparisonResult( assert( pRes->iFirst==i1 ); }else{ int nMin = MIN(p1->term.n, p2->term.n); - int res = memcmp(p1->term.p, p2->term.p, nMin); + int res = fts5Memcmp(p1->term.p, p2->term.p, nMin); if( res==0 ) res = p1->term.n - p2->term.n; if( res==0 ){ @@ -209269,8 +211524,8 @@ static int fts5MultiIterDoCompare(Fts5Iter *pIter, int iOut){ }else{ int res = fts5BufferCompare(&p1->term, &p2->term); if( res==0 ){ - assert( i2>i1 ); - assert( i2!=0 ); + assert_nc( i2>i1 ); + assert_nc( i2!=0 ); pRes->bTermEq = 1; if( p1->iRowid==p2->iRowid ){ p1->bDel = p2->bDel; @@ -209392,7 +211647,6 @@ static void fts5MultiIterFree(Fts5Iter *pIter){ for(i=0; inSeg; i++){ fts5SegIterClear(&pIter->aSeg[i]); } - fts5StructureRelease(pIter->pStruct); fts5BufferFree(&pIter->poslist); sqlite3_free(pIter); } @@ -209740,7 +211994,8 @@ static void fts5SegiterPoslist( Fts5Colset *pColset, Fts5Buffer *pBuf ){ - if( 0==fts5BufferGrow(&p->rc, pBuf, pSeg->nPos) ){ + if( 0==fts5BufferGrow(&p->rc, pBuf, pSeg->nPos+FTS5_DATA_ZERO_PADDING) ){ + memset(&pBuf->p[pBuf->n+pSeg->nPos], 0, FTS5_DATA_ZERO_PADDING); if( pColset==0 ){ fts5ChunkIterate(p, pSeg, (void*)pBuf, fts5PoslistCallback); }else{ @@ -210038,9 +212293,7 @@ static void fts5MultiIterNew( if( pNew==0 ) return; pNew->bRev = (0!=(flags & FTS5INDEX_QUERY_DESC)); pNew->bSkipEmpty = (0!=(flags & FTS5INDEX_QUERY_SKIPEMPTY)); - pNew->pStruct = pStruct; pNew->pColset = pColset; - fts5StructureRef(pStruct); if( (flags & FTS5INDEX_QUERY_NOOUTPUT)==0 ){ fts5IterSetOutputCb(&p->rc, pNew); } @@ -210218,24 +212471,24 @@ static int fts5AllocateSegid(Fts5Index *p, Fts5Structure *pStruct){ for(iLvl=0; iLvlnLevel; iLvl++){ for(iSeg=0; iSegaLevel[iLvl].nSeg; iSeg++){ int iId = pStruct->aLevel[iLvl].aSeg[iSeg].iSegid; - if( iId<=FTS5_MAX_SEGMENT ){ - aUsed[(iId-1) / 32] |= 1 << ((iId-1) % 32); + if( iId<=FTS5_MAX_SEGMENT && iId>0 ){ + aUsed[(iId-1) / 32] |= (u32)1 << ((iId-1) % 32); } } } for(i=0; aUsed[i]==0xFFFFFFFF; i++); mask = aUsed[i]; - for(iSegid=0; mask & (1 << iSegid); iSegid++); + for(iSegid=0; mask & ((u32)1 << iSegid); iSegid++); iSegid += 1 + i*32; #ifdef SQLITE_DEBUG for(iLvl=0; iLvlnLevel; iLvl++){ for(iSeg=0; iSegaLevel[iLvl].nSeg; iSeg++){ - assert( iSegid!=pStruct->aLevel[iLvl].aSeg[iSeg].iSegid ); + assert_nc( iSegid!=pStruct->aLevel[iLvl].aSeg[iSeg].iSegid ); } } - assert( iSegid>0 && iSegid<=FTS5_MAX_SEGMENT ); + assert_nc( iSegid>0 && iSegid<=FTS5_MAX_SEGMENT ); { sqlite3_stmt *pIdxSelect = fts5IdxSelectStmt(p); @@ -210243,7 +212496,7 @@ static int fts5AllocateSegid(Fts5Index *p, Fts5Structure *pStruct){ u8 aBlob[2] = {0xff, 0xff}; sqlite3_bind_int(pIdxSelect, 1, iSegid); sqlite3_bind_blob(pIdxSelect, 2, aBlob, 2, SQLITE_STATIC); - assert( sqlite3_step(pIdxSelect)!=SQLITE_ROW ); + assert_nc( sqlite3_step(pIdxSelect)!=SQLITE_ROW ); p->rc = sqlite3_reset(pIdxSelect); sqlite3_bind_null(pIdxSelect, 2); } @@ -210313,13 +212566,13 @@ static int fts5WriteDlidxGrow( int nLvl ){ if( p->rc==SQLITE_OK && nLvl>=pWriter->nDlidx ){ - Fts5DlidxWriter *aDlidx = (Fts5DlidxWriter*)sqlite3_realloc( + Fts5DlidxWriter *aDlidx = (Fts5DlidxWriter*)sqlite3_realloc64( pWriter->aDlidx, sizeof(Fts5DlidxWriter) * nLvl ); if( aDlidx==0 ){ p->rc = SQLITE_NOMEM; }else{ - int nByte = sizeof(Fts5DlidxWriter) * (nLvl - pWriter->nDlidx); + size_t nByte = sizeof(Fts5DlidxWriter) * (nLvl - pWriter->nDlidx); memset(&aDlidx[pWriter->nDlidx], 0, nByte); pWriter->aDlidx = aDlidx; pWriter->nDlidx = nLvl; @@ -210392,8 +212645,10 @@ static void fts5WriteBtreeTerm( int nTerm, const u8 *pTerm /* First term on new page */ ){ fts5WriteFlushBtree(p, pWriter); - fts5BufferSet(&p->rc, &pWriter->btterm, nTerm, pTerm); - pWriter->iBtPage = pWriter->writer.pgno; + if( p->rc==SQLITE_OK ){ + fts5BufferSet(&p->rc, &pWriter->btterm, nTerm, pTerm); + pWriter->iBtPage = pWriter->writer.pgno; + } } /* @@ -210544,6 +212799,7 @@ static void fts5WriteAppendTerm( int nPrefix; /* Bytes of prefix compression for term */ Fts5PageWriter *pPage = &pWriter->writer; Fts5Buffer *pPgidx = &pWriter->writer.pgidx; + int nMin = MIN(pPage->term.n, nTerm); assert( p->rc==SQLITE_OK ); assert( pPage->buf.n>=4 ); @@ -210553,6 +212809,7 @@ static void fts5WriteAppendTerm( if( (pPage->buf.n + pPgidx->n + nTerm + 2)>=p->pConfig->pgsz ){ if( pPage->buf.n>4 ){ fts5WriteFlushLeaf(p, pWriter); + if( p->rc!=SQLITE_OK ) return; } fts5BufferGrow(&p->rc, &pPage->buf, nTerm+FTS5_DATA_PADDING); } @@ -210585,13 +212842,14 @@ static void fts5WriteAppendTerm( ** inefficient, but still correct. */ int n = nTerm; if( pPage->term.n ){ - n = 1 + fts5PrefixCompress(pPage->term.n, pPage->term.p, pTerm); + n = 1 + fts5PrefixCompress(nMin, pPage->term.p, pTerm); } fts5WriteBtreeTerm(p, pWriter, n, pTerm); + if( p->rc!=SQLITE_OK ) return; pPage = &pWriter->writer; } }else{ - nPrefix = fts5PrefixCompress(pPage->term.n, pPage->term.p, pTerm); + nPrefix = fts5PrefixCompress(nMin, pPage->term.p, pTerm); fts5BufferAppendVarint(&p->rc, &pPage->buf, nPrefix); } @@ -210638,7 +212896,7 @@ static void fts5WriteAppendRowid( if( pWriter->bFirstRowidInDoclist || pWriter->bFirstRowidInPage ){ fts5BufferAppendVarint(&p->rc, &pPage->buf, iRowid); }else{ - assert( p->rc || iRowid>pWriter->iPrevRowid ); + assert_nc( p->rc || iRowid>pWriter->iPrevRowid ); fts5BufferAppendVarint(&p->rc, &pPage->buf, iRowid - pWriter->iPrevRowid); } pWriter->iPrevRowid = iRowid; @@ -210760,7 +213018,7 @@ static void fts5TrimSegments(Fts5Index *p, Fts5Iter *pIter){ int i; Fts5Buffer buf; memset(&buf, 0, sizeof(Fts5Buffer)); - for(i=0; inSeg; i++){ + for(i=0; inSeg && p->rc==SQLITE_OK; i++){ Fts5SegIter *pSeg = &pIter->aSeg[i]; if( pSeg->pSeg==0 ){ /* no-op */ @@ -210778,35 +213036,44 @@ static void fts5TrimSegments(Fts5Index *p, Fts5Iter *pIter){ u8 aHdr[4] = {0x00, 0x00, 0x00, 0x00}; iLeafRowid = FTS5_SEGMENT_ROWID(iId, pSeg->iTermLeafPgno); - pData = fts5DataRead(p, iLeafRowid); + pData = fts5LeafRead(p, iLeafRowid); if( pData ){ - fts5BufferZero(&buf); - fts5BufferGrow(&p->rc, &buf, pData->nn); - fts5BufferAppendBlob(&p->rc, &buf, sizeof(aHdr), aHdr); - fts5BufferAppendVarint(&p->rc, &buf, pSeg->term.n); - fts5BufferAppendBlob(&p->rc, &buf, pSeg->term.n, pSeg->term.p); - fts5BufferAppendBlob(&p->rc, &buf, pData->szLeaf-iOff, &pData->p[iOff]); - if( p->rc==SQLITE_OK ){ - /* Set the szLeaf field */ - fts5PutU16(&buf.p[2], (u16)buf.n); - } + if( iOff>pData->szLeaf ){ + /* This can occur if the pages that the segments occupy overlap - if + ** a single page has been assigned to more than one segment. In + ** this case a prior iteration of this loop may have corrupted the + ** segment currently being trimmed. */ + p->rc = FTS5_CORRUPT; + }else{ + fts5BufferZero(&buf); + fts5BufferGrow(&p->rc, &buf, pData->nn); + fts5BufferAppendBlob(&p->rc, &buf, sizeof(aHdr), aHdr); + fts5BufferAppendVarint(&p->rc, &buf, pSeg->term.n); + fts5BufferAppendBlob(&p->rc, &buf, pSeg->term.n, pSeg->term.p); + fts5BufferAppendBlob(&p->rc, &buf, pData->szLeaf-iOff,&pData->p[iOff]); + if( p->rc==SQLITE_OK ){ + /* Set the szLeaf field */ + fts5PutU16(&buf.p[2], (u16)buf.n); + } - /* Set up the new page-index array */ - fts5BufferAppendVarint(&p->rc, &buf, 4); - if( pSeg->iLeafPgno==pSeg->iTermLeafPgno - && pSeg->iEndofDoclistszLeaf - ){ - int nDiff = pData->szLeaf - pSeg->iEndofDoclist; - fts5BufferAppendVarint(&p->rc, &buf, buf.n - 1 - nDiff - 4); - fts5BufferAppendBlob(&p->rc, &buf, - pData->nn - pSeg->iPgidxOff, &pData->p[pSeg->iPgidxOff] - ); - } + /* Set up the new page-index array */ + fts5BufferAppendVarint(&p->rc, &buf, 4); + if( pSeg->iLeafPgno==pSeg->iTermLeafPgno + && pSeg->iEndofDoclistszLeaf + && pSeg->iPgidxOff<=pData->nn + ){ + int nDiff = pData->szLeaf - pSeg->iEndofDoclist; + fts5BufferAppendVarint(&p->rc, &buf, buf.n - 1 - nDiff - 4); + fts5BufferAppendBlob(&p->rc, &buf, + pData->nn - pSeg->iPgidxOff, &pData->p[pSeg->iPgidxOff] + ); + } + pSeg->pSeg->pgnoFirst = pSeg->iTermLeafPgno; + fts5DataDelete(p, FTS5_SEGMENT_ROWID(iId, 1), iLeafRowid); + fts5DataWrite(p, iLeafRowid, buf.p, buf.n); + } fts5DataRelease(pData); - pSeg->pSeg->pgnoFirst = pSeg->iTermLeafPgno; - fts5DataDelete(p, FTS5_SEGMENT_ROWID(iId, 1), iLeafRowid); - fts5DataWrite(p, iLeafRowid, buf.p, buf.n); } } } @@ -210898,7 +213165,7 @@ static void fts5IndexMergeLevel( const u8 *pTerm; pTerm = fts5MultiIterTerm(pIter, &nTerm); - if( nTerm!=term.n || memcmp(pTerm, term.p, nTerm) ){ + if( nTerm!=term.n || fts5Memcmp(pTerm, term.p, nTerm) ){ if( pnRem && writer.nLeafWritten>nRem ){ break; } @@ -211153,6 +213420,7 @@ static void fts5FlushOneHash(Fts5Index *p){ /* Write the term for this entry to disk. */ sqlite3Fts5HashScanEntry(pHash, &zTerm, &pDoclist, &nDoclist); fts5WriteAppendTerm(p, &writer, (int)strlen(zTerm), (const u8*)zTerm); + if( p->rc!=SQLITE_OK ) break; assert( writer.bFirstRowidInPage==0 ); if( pgsz>=(pBuf->n + pPgidx->n + nDoclist + 1) ){ @@ -211175,6 +213443,7 @@ static void fts5FlushOneHash(Fts5Index *p){ pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowid); writer.bFirstRowidInPage = 0; fts5WriteDlidxAppend(p, &writer, iRowid); + if( p->rc!=SQLITE_OK ) break; }else{ pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iDelta); } @@ -211232,7 +213501,7 @@ static void fts5FlushOneHash(Fts5Index *p){ /* TODO2: Doclist terminator written here. */ /* pBuf->p[pBuf->n++] = '\0'; */ assert( pBuf->n<=pBuf->nSpace ); - sqlite3Fts5HashScanNext(pHash); + if( p->rc==SQLITE_OK ) sqlite3Fts5HashScanNext(pHash); } sqlite3Fts5HashClear(pHash); fts5WriteFinish(p, &writer, &pgnoLast); @@ -211276,7 +213545,7 @@ static Fts5Structure *fts5IndexOptimizeStruct( Fts5Structure *pStruct ){ Fts5Structure *pNew = 0; - int nByte = sizeof(Fts5Structure); + sqlite3_int64 nByte = sizeof(Fts5Structure); int nSeg = pStruct->nSegment; int i; @@ -211406,11 +213675,13 @@ static void fts5AppendPoslist( Fts5Buffer *pBuf ){ int nData = pMulti->base.nData; + int nByte = nData + 9 + 9 + FTS5_DATA_ZERO_PADDING; assert( nData>0 ); - if( p->rc==SQLITE_OK && 0==fts5BufferGrow(&p->rc, pBuf, nData+9+9) ){ + if( p->rc==SQLITE_OK && 0==fts5BufferGrow(&p->rc, pBuf, nByte) ){ fts5BufferSafeAppendVarint(pBuf, iDelta); fts5BufferSafeAppendVarint(pBuf, nData*2); fts5BufferSafeAppendBlob(pBuf, pMulti->base.pData, nData); + memset(&pBuf->p[pBuf->n], 0, FTS5_DATA_ZERO_PADDING); } } @@ -211591,6 +213862,8 @@ static void fts5MergePrefixLists( int iOff2 = 0; u8 *a1 = &i1.aPoslist[i1.nSize]; u8 *a2 = &i2.aPoslist[i2.nSize]; + int nCopy; + u8 *aCopy; i64 iPrev = 0; Fts5PoslistWriter writer; @@ -211622,7 +213895,7 @@ static void fts5MergePrefixLists( sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1, &iPos1); if( iPos1<0 ) break; }else{ - assert( iPos2!=iPrev ); + assert_nc( iPos2!=iPrev ); sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos2); sqlite3Fts5PoslistNext64(a2, i2.nPoslist, &iOff2, &iPos2); if( iPos2<0 ) break; @@ -211634,11 +213907,16 @@ static void fts5MergePrefixLists( if( iPos1!=iPrev ){ sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos1); } - fts5BufferSafeAppendBlob(&tmp, &a1[iOff1], i1.nPoslist-iOff1); + aCopy = &a1[iOff1]; + nCopy = i1.nPoslist - iOff1; }else{ assert( iPos2>=0 && iPos2!=iPrev ); sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos2); - fts5BufferSafeAppendBlob(&tmp, &a2[iOff2], i2.nPoslist-iOff2); + aCopy = &a2[iOff2]; + nCopy = i2.nPoslist - iOff2; + } + if( nCopy>0 ){ + fts5BufferSafeAppendBlob(&tmp, aCopy, nCopy); } /* WRITEPOSLISTSIZE */ @@ -211646,6 +213924,7 @@ static void fts5MergePrefixLists( fts5BufferSafeAppendBlob(&out, tmp.p, tmp.n); fts5DoclistIterNext(&i1); fts5DoclistIterNext(&i2); + assert( out.n<=(p1->n+p2->n+9) ); if( i1.aPoslist==0 || i2.aPoslist==0 ) break; } } @@ -211747,7 +214026,7 @@ static void fts5SetupPrefixIter( } fts5MultiIterFree(p1); - pData = fts5IdxMalloc(p, sizeof(Fts5Data) + doclist.n); + pData = fts5IdxMalloc(p, sizeof(Fts5Data)+doclist.n+FTS5_DATA_ZERO_PADDING); if( pData ){ pData->p = (u8*)&pData[1]; pData->nn = pData->szLeaf = doclist.n; @@ -212509,11 +214788,11 @@ static void fts5IndexIntegrityCheckSegment( iOff = fts5LeafFirstTermOff(pLeaf); iRowidOff = fts5LeafFirstRowidOff(pLeaf); - if( iRowidOff>=iOff ){ + if( iRowidOff>=iOff || iOff>=pLeaf->szLeaf ){ p->rc = FTS5_CORRUPT; }else{ iOff += fts5GetVarint32(&pLeaf->p[iOff], nTerm); - res = memcmp(&pLeaf->p[iOff], zIdxTerm, MIN(nTerm, nIdxTerm)); + res = fts5Memcmp(&pLeaf->p[iOff], zIdxTerm, MIN(nTerm, nIdxTerm)); if( res==0 ) res = nTerm - nIdxTerm; if( res<0 ) p->rc = FTS5_CORRUPT; } @@ -212908,7 +215187,7 @@ static void fts5DecodeFunction( u8 *a = 0; Fts5Buffer s; /* Build up text to return here */ int rc = SQLITE_OK; /* Return code */ - int nSpace = 0; + sqlite3_int64 nSpace = 0; int eDetailNone = (sqlite3_user_data(pCtx)!=0); assert( nArg==2 ); @@ -212924,8 +215203,7 @@ static void fts5DecodeFunction( nSpace = n + FTS5_DATA_ZERO_PADDING; a = (u8*)sqlite3Fts5MallocZero(&rc, nSpace); if( a==0 ) goto decode_out; - memcpy(a, aBlob, n); - + if( n>0 ) memcpy(a, aBlob, n); fts5DecodeRowid(iRowid, &iSegid, &bDlidx, &iHeight, &iPgno); @@ -213020,6 +215298,9 @@ static void fts5DecodeFunction( iPgidxOff = szLeaf = fts5GetU16(&a[2]); if( iPgidxOffn ){ + rc = FTS5_CORRUPT; + goto decode_out; } } @@ -213031,14 +215312,22 @@ static void fts5DecodeFunction( }else{ iOff = szLeaf; } + if( iOff>n ){ + rc = FTS5_CORRUPT; + goto decode_out; + } fts5DecodePoslist(&rc, &s, &a[4], iOff-4); /* Decode any more doclist data that appears on the page before the ** first term. */ nDoclist = (iTermOff ? iTermOff : szLeaf) - iOff; + if( nDoclist+iOff>n ){ + rc = FTS5_CORRUPT; + goto decode_out; + } fts5DecodeDoclist(&rc, &s, &a[iOff], nDoclist); - while( iPgidxOffszLeaf ){ + rc = FTS5_CORRUPT; + break; + } if( bFirst==0 ){ iOff += fts5GetVarint32(&a[iOff], nByte); + if( nByte>term.n ){ + rc = FTS5_CORRUPT; + break; + } term.n = nByte; } iOff += fts5GetVarint32(&a[iOff], nByte); + if( iOff+nByte>n ){ + rc = FTS5_CORRUPT; + break; + } fts5BufferAppendBlob(&rc, &term, nByte, &a[iOff]); iOff += nByte; @@ -213182,8 +215483,8 @@ SQLITE_API int sqlite3_fts5_may_be_corrupt = 1; typedef struct Fts5Auxdata Fts5Auxdata; typedef struct Fts5Auxiliary Fts5Auxiliary; typedef struct Fts5Cursor Fts5Cursor; +typedef struct Fts5FullTable Fts5FullTable; typedef struct Fts5Sorter Fts5Sorter; -typedef struct Fts5Table Fts5Table; typedef struct Fts5TokenizerModule Fts5TokenizerModule; /* @@ -213264,13 +215565,8 @@ struct Fts5TokenizerModule { Fts5TokenizerModule *pNext; /* Next registered tokenizer module */ }; -/* -** Virtual-table object. -*/ -struct Fts5Table { - sqlite3_vtab base; /* Base class used by SQLite core */ - Fts5Config *pConfig; /* Virtual table configuration */ - Fts5Index *pIndex; /* Full-text index */ +struct Fts5FullTable { + Fts5Table p; /* Public class members from fts5Int.h */ Fts5Storage *pStorage; /* Document store */ Fts5Global *pGlobal; /* Global (connection wide) data */ Fts5Cursor *pSortCsr; /* Sort data from this cursor */ @@ -213408,7 +215704,7 @@ struct Fts5Auxdata { #define FTS5_SAVEPOINT 5 #define FTS5_RELEASE 6 #define FTS5_ROLLBACKTO 7 -static void fts5CheckTransactionState(Fts5Table *p, int op, int iSavepoint){ +static void fts5CheckTransactionState(Fts5FullTable *p, int op, int iSavepoint){ switch( op ){ case FTS5_BEGIN: assert( p->ts.eState==0 ); @@ -213447,7 +215743,7 @@ static void fts5CheckTransactionState(Fts5Table *p, int op, int iSavepoint){ case FTS5_ROLLBACKTO: assert( p->ts.eState==1 ); - assert( iSavepoint>=0 ); + assert( iSavepoint>=-1 ); assert( iSavepoint<=p->ts.iSavepoint ); p->ts.iSavepoint = iSavepoint; break; @@ -213460,18 +215756,18 @@ static void fts5CheckTransactionState(Fts5Table *p, int op, int iSavepoint){ /* ** Return true if pTab is a contentless table. */ -static int fts5IsContentless(Fts5Table *pTab){ - return pTab->pConfig->eContent==FTS5_CONTENT_NONE; +static int fts5IsContentless(Fts5FullTable *pTab){ + return pTab->p.pConfig->eContent==FTS5_CONTENT_NONE; } /* ** Delete a virtual table handle allocated by fts5InitVtab(). */ -static void fts5FreeVtab(Fts5Table *pTab){ +static void fts5FreeVtab(Fts5FullTable *pTab){ if( pTab ){ - sqlite3Fts5IndexClose(pTab->pIndex); + sqlite3Fts5IndexClose(pTab->p.pIndex); sqlite3Fts5StorageClose(pTab->pStorage); - sqlite3Fts5ConfigFree(pTab->pConfig); + sqlite3Fts5ConfigFree(pTab->p.pConfig); sqlite3_free(pTab); } } @@ -213480,7 +215776,7 @@ static void fts5FreeVtab(Fts5Table *pTab){ ** The xDisconnect() virtual table method. */ static int fts5DisconnectMethod(sqlite3_vtab *pVtab){ - fts5FreeVtab((Fts5Table*)pVtab); + fts5FreeVtab((Fts5FullTable*)pVtab); return SQLITE_OK; } @@ -213491,7 +215787,7 @@ static int fts5DestroyMethod(sqlite3_vtab *pVtab){ Fts5Table *pTab = (Fts5Table*)pVtab; int rc = sqlite3Fts5DropAll(pTab->pConfig); if( rc==SQLITE_OK ){ - fts5FreeVtab((Fts5Table*)pVtab); + fts5FreeVtab((Fts5FullTable*)pVtab); } return rc; } @@ -213520,28 +215816,28 @@ static int fts5InitVtab( const char **azConfig = (const char**)argv; int rc = SQLITE_OK; /* Return code */ Fts5Config *pConfig = 0; /* Results of parsing argc/argv */ - Fts5Table *pTab = 0; /* New virtual table object */ + Fts5FullTable *pTab = 0; /* New virtual table object */ /* Allocate the new vtab object and parse the configuration */ - pTab = (Fts5Table*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Table)); + pTab = (Fts5FullTable*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5FullTable)); if( rc==SQLITE_OK ){ rc = sqlite3Fts5ConfigParse(pGlobal, db, argc, azConfig, &pConfig, pzErr); assert( (rc==SQLITE_OK && *pzErr==0) || pConfig==0 ); } if( rc==SQLITE_OK ){ - pTab->pConfig = pConfig; + pTab->p.pConfig = pConfig; pTab->pGlobal = pGlobal; } /* Open the index sub-system */ if( rc==SQLITE_OK ){ - rc = sqlite3Fts5IndexOpen(pConfig, bCreate, &pTab->pIndex, pzErr); + rc = sqlite3Fts5IndexOpen(pConfig, bCreate, &pTab->p.pIndex, pzErr); } /* Open the storage sub-system */ if( rc==SQLITE_OK ){ rc = sqlite3Fts5StorageOpen( - pConfig, pTab->pIndex, bCreate, &pTab->pStorage, pzErr + pConfig, pTab->p.pIndex, bCreate, &pTab->pStorage, pzErr ); } @@ -213554,8 +215850,8 @@ static int fts5InitVtab( if( rc==SQLITE_OK ){ assert( pConfig->pzErrmsg==0 ); pConfig->pzErrmsg = pzErr; - rc = sqlite3Fts5IndexLoadConfig(pTab->pIndex); - sqlite3Fts5IndexRollback(pTab->pIndex); + rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex); + sqlite3Fts5IndexRollback(pTab->p.pIndex); pConfig->pzErrmsg = 0; } @@ -213768,7 +216064,7 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ return SQLITE_OK; } -static int fts5NewTransaction(Fts5Table *pTab){ +static int fts5NewTransaction(Fts5FullTable *pTab){ Fts5Cursor *pCsr; for(pCsr=pTab->pGlobal->pCsr; pCsr; pCsr=pCsr->pNext){ if( pCsr->base.pVtab==(sqlite3_vtab*)pTab ) return SQLITE_OK; @@ -213780,19 +216076,19 @@ static int fts5NewTransaction(Fts5Table *pTab){ ** Implementation of xOpen method. */ static int fts5OpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){ - Fts5Table *pTab = (Fts5Table*)pVTab; - Fts5Config *pConfig = pTab->pConfig; + Fts5FullTable *pTab = (Fts5FullTable*)pVTab; + Fts5Config *pConfig = pTab->p.pConfig; Fts5Cursor *pCsr = 0; /* New cursor object */ - int nByte; /* Bytes of space to allocate */ + sqlite3_int64 nByte; /* Bytes of space to allocate */ int rc; /* Return code */ rc = fts5NewTransaction(pTab); if( rc==SQLITE_OK ){ nByte = sizeof(Fts5Cursor) + pConfig->nCol * sizeof(int); - pCsr = (Fts5Cursor*)sqlite3_malloc(nByte); + pCsr = (Fts5Cursor*)sqlite3_malloc64(nByte); if( pCsr ){ Fts5Global *pGlobal = pTab->pGlobal; - memset(pCsr, 0, nByte); + memset(pCsr, 0, (size_t)nByte); pCsr->aColumnSize = (int*)&pCsr[1]; pCsr->pNext = pGlobal->pCsr; pGlobal->pCsr = pCsr; @@ -213827,7 +216123,7 @@ static void fts5CsrNewrow(Fts5Cursor *pCsr){ } static void fts5FreeCursorComponents(Fts5Cursor *pCsr){ - Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); + Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab); Fts5Auxdata *pData; Fts5Auxdata *pNext; @@ -213871,7 +216167,7 @@ static void fts5FreeCursorComponents(Fts5Cursor *pCsr){ */ static int fts5CloseMethod(sqlite3_vtab_cursor *pCursor){ if( pCursor ){ - Fts5Table *pTab = (Fts5Table*)(pCursor->pVtab); + Fts5FullTable *pTab = (Fts5FullTable*)(pCursor->pVtab); Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; Fts5Cursor **pp; @@ -213928,7 +216224,7 @@ static int fts5SorterNext(Fts5Cursor *pCsr){ ** Set the FTS5CSR_REQUIRE_RESEEK flag on all FTS5_PLAN_MATCH cursors ** open on table pTab. */ -static void fts5TripCursors(Fts5Table *pTab){ +static void fts5TripCursors(Fts5FullTable *pTab){ Fts5Cursor *pCsr; for(pCsr=pTab->pGlobal->pCsr; pCsr; pCsr=pCsr->pNext){ if( pCsr->ePlan==FTS5_PLAN_MATCH @@ -213955,11 +216251,11 @@ static int fts5CursorReseek(Fts5Cursor *pCsr, int *pbSkip){ int rc = SQLITE_OK; assert( *pbSkip==0 ); if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_RESEEK) ){ - Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); + Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab); int bDesc = pCsr->bDesc; i64 iRowid = sqlite3Fts5ExprRowid(pCsr->pExpr); - rc = sqlite3Fts5ExprFirst(pCsr->pExpr, pTab->pIndex, iRowid, bDesc); + rc = sqlite3Fts5ExprFirst(pCsr->pExpr, pTab->p.pIndex, iRowid, bDesc); if( rc==SQLITE_OK && iRowid!=sqlite3Fts5ExprRowid(pCsr->pExpr) ){ *pbSkip = 1; } @@ -214056,20 +216352,24 @@ static int fts5PrepareStatement( return rc; } -static int fts5CursorFirstSorted(Fts5Table *pTab, Fts5Cursor *pCsr, int bDesc){ - Fts5Config *pConfig = pTab->pConfig; +static int fts5CursorFirstSorted( + Fts5FullTable *pTab, + Fts5Cursor *pCsr, + int bDesc +){ + Fts5Config *pConfig = pTab->p.pConfig; Fts5Sorter *pSorter; int nPhrase; - int nByte; + sqlite3_int64 nByte; int rc; const char *zRank = pCsr->zRank; const char *zRankArgs = pCsr->zRankArgs; nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr); nByte = sizeof(Fts5Sorter) + sizeof(int) * (nPhrase-1); - pSorter = (Fts5Sorter*)sqlite3_malloc(nByte); + pSorter = (Fts5Sorter*)sqlite3_malloc64(nByte); if( pSorter==0 ) return SQLITE_NOMEM; - memset(pSorter, 0, nByte); + memset(pSorter, 0, (size_t)nByte); pSorter->nIdx = nPhrase; /* TODO: It would be better to have some system for reusing statement @@ -214104,10 +216404,10 @@ static int fts5CursorFirstSorted(Fts5Table *pTab, Fts5Cursor *pCsr, int bDesc){ return rc; } -static int fts5CursorFirst(Fts5Table *pTab, Fts5Cursor *pCsr, int bDesc){ +static int fts5CursorFirst(Fts5FullTable *pTab, Fts5Cursor *pCsr, int bDesc){ int rc; Fts5Expr *pExpr = pCsr->pExpr; - rc = sqlite3Fts5ExprFirst(pExpr, pTab->pIndex, pCsr->iFirstRowid, bDesc); + rc = sqlite3Fts5ExprFirst(pExpr, pTab->p.pIndex, pCsr->iFirstRowid, bDesc); if( sqlite3Fts5ExprEof(pExpr) ){ CsrFlagSet(pCsr, FTS5CSR_EOF); } @@ -214122,7 +216422,7 @@ static int fts5CursorFirst(Fts5Table *pTab, Fts5Cursor *pCsr, int bDesc){ ** parameters. */ static int fts5SpecialMatch( - Fts5Table *pTab, + Fts5FullTable *pTab, Fts5Cursor *pCsr, const char *zQuery ){ @@ -214133,18 +216433,18 @@ static int fts5SpecialMatch( while( z[0]==' ' ) z++; for(n=0; z[n] && z[n]!=' '; n++); - assert( pTab->base.zErrMsg==0 ); + assert( pTab->p.base.zErrMsg==0 ); pCsr->ePlan = FTS5_PLAN_SPECIAL; if( 0==sqlite3_strnicmp("reads", z, n) ){ - pCsr->iSpecial = sqlite3Fts5IndexReads(pTab->pIndex); + pCsr->iSpecial = sqlite3Fts5IndexReads(pTab->p.pIndex); } else if( 0==sqlite3_strnicmp("id", z, n) ){ pCsr->iSpecial = pCsr->iCsrId; } else{ /* An unrecognized directive. Return an error message. */ - pTab->base.zErrMsg = sqlite3_mprintf("unknown special query: %.*s", n, z); + pTab->p.base.zErrMsg = sqlite3_mprintf("unknown special query: %.*s", n, z); rc = SQLITE_ERROR; } @@ -214156,7 +216456,7 @@ static int fts5SpecialMatch( ** pTab. If one is found, return a pointer to the corresponding Fts5Auxiliary ** structure. Otherwise, if no such function exists, return NULL. */ -static Fts5Auxiliary *fts5FindAuxiliary(Fts5Table *pTab, const char *zName){ +static Fts5Auxiliary *fts5FindAuxiliary(Fts5FullTable *pTab, const char *zName){ Fts5Auxiliary *pAux; for(pAux=pTab->pGlobal->pAux; pAux; pAux=pAux->pNext){ @@ -214169,8 +216469,8 @@ static Fts5Auxiliary *fts5FindAuxiliary(Fts5Table *pTab, const char *zName){ static int fts5FindRankFunction(Fts5Cursor *pCsr){ - Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); - Fts5Config *pConfig = pTab->pConfig; + Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab); + Fts5Config *pConfig = pTab->p.pConfig; int rc = SQLITE_OK; Fts5Auxiliary *pAux = 0; const char *zRank = pCsr->zRank; @@ -214186,7 +216486,7 @@ static int fts5FindRankFunction(Fts5Cursor *pCsr){ assert( rc==SQLITE_OK || pCsr->pRankArgStmt==0 ); if( rc==SQLITE_OK ){ if( SQLITE_ROW==sqlite3_step(pStmt) ){ - int nByte; + sqlite3_int64 nByte; pCsr->nRankArg = sqlite3_column_count(pStmt); nByte = sizeof(sqlite3_value*)*pCsr->nRankArg; pCsr->apRankArg = (sqlite3_value**)sqlite3Fts5MallocZero(&rc, nByte); @@ -214208,8 +216508,8 @@ static int fts5FindRankFunction(Fts5Cursor *pCsr){ if( rc==SQLITE_OK ){ pAux = fts5FindAuxiliary(pTab, zRank); if( pAux==0 ){ - assert( pTab->base.zErrMsg==0 ); - pTab->base.zErrMsg = sqlite3_mprintf("no such function: %s", zRank); + assert( pTab->p.base.zErrMsg==0 ); + pTab->p.base.zErrMsg = sqlite3_mprintf("no such function: %s", zRank); rc = SQLITE_ERROR; } } @@ -214284,8 +216584,8 @@ static int fts5FilterMethod( int nVal, /* Number of elements in apVal */ sqlite3_value **apVal /* Arguments for the indexing scheme */ ){ - Fts5Table *pTab = (Fts5Table*)(pCursor->pVtab); - Fts5Config *pConfig = pTab->pConfig; + Fts5FullTable *pTab = (Fts5FullTable*)(pCursor->pVtab); + Fts5Config *pConfig = pTab->p.pConfig; Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; int rc = SQLITE_OK; /* Error code */ int iVal = 0; /* Counter for apVal[] */ @@ -214314,8 +216614,8 @@ static int fts5FilterMethod( assert( pCsr->zRank==0 ); assert( pCsr->zRankArgs==0 ); - assert( pzErrmsg==0 || pzErrmsg==&pTab->base.zErrMsg ); - pConfig->pzErrmsg = &pTab->base.zErrMsg; + assert( pzErrmsg==0 || pzErrmsg==&pTab->p.base.zErrMsg ); + pConfig->pzErrmsg = &pTab->p.base.zErrMsg; /* Decode the arguments passed through to this function. ** @@ -214381,7 +216681,7 @@ static int fts5FilterMethod( ** but a request for an internal parameter. */ rc = fts5SpecialMatch(pTab, pCsr, &zExpr[1]); }else{ - char **pzErr = &pTab->base.zErrMsg; + char **pzErr = &pTab->p.base.zErrMsg; rc = sqlite3Fts5ExprNew(pConfig, iCol, zExpr, &pCsr->pExpr, pzErr); if( rc==SQLITE_OK ){ if( bOrderByRank ){ @@ -214404,7 +216704,7 @@ static int fts5FilterMethod( ** by rowid (ePlan==FTS5_PLAN_ROWID). */ pCsr->ePlan = (pRowidEq ? FTS5_PLAN_ROWID : FTS5_PLAN_SCAN); rc = sqlite3Fts5StorageStmt( - pTab->pStorage, fts5StmtType(pCsr), &pCsr->pStmt, &pTab->base.zErrMsg + pTab->pStorage, fts5StmtType(pCsr), &pCsr->pStmt, &pTab->p.base.zErrMsg ); if( rc==SQLITE_OK ){ if( pCsr->ePlan==FTS5_PLAN_ROWID ){ @@ -214487,12 +216787,12 @@ static int fts5SeekCursor(Fts5Cursor *pCsr, int bErrormsg){ /* If the cursor does not yet have a statement handle, obtain one now. */ if( pCsr->pStmt==0 ){ - Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); + Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab); int eStmt = fts5StmtType(pCsr); rc = sqlite3Fts5StorageStmt( - pTab->pStorage, eStmt, &pCsr->pStmt, (bErrormsg?&pTab->base.zErrMsg:0) + pTab->pStorage, eStmt, &pCsr->pStmt, (bErrormsg?&pTab->p.base.zErrMsg:0) ); - assert( rc!=SQLITE_OK || pTab->base.zErrMsg==0 ); + assert( rc!=SQLITE_OK || pTab->p.base.zErrMsg==0 ); assert( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_CONTENT) ); } @@ -214514,11 +216814,11 @@ static int fts5SeekCursor(Fts5Cursor *pCsr, int bErrormsg){ return rc; } -static void fts5SetVtabError(Fts5Table *p, const char *zFormat, ...){ +static void fts5SetVtabError(Fts5FullTable *p, const char *zFormat, ...){ va_list ap; /* ... printf arguments */ va_start(ap, zFormat); - assert( p->base.zErrMsg==0 ); - p->base.zErrMsg = sqlite3_vmprintf(zFormat, ap); + assert( p->p.base.zErrMsg==0 ); + p->p.base.zErrMsg = sqlite3_vmprintf(zFormat, ap); va_end(ap); } @@ -214538,11 +216838,11 @@ static void fts5SetVtabError(Fts5Table *p, const char *zFormat, ...){ ** more commands are added to this function. */ static int fts5SpecialInsert( - Fts5Table *pTab, /* Fts5 table object */ + Fts5FullTable *pTab, /* Fts5 table object */ const char *zCmd, /* Text inserted into table-name column */ sqlite3_value *pVal /* Value inserted into rank column */ ){ - Fts5Config *pConfig = pTab->pConfig; + Fts5Config *pConfig = pTab->p.pConfig; int rc = SQLITE_OK; int bError = 0; @@ -214577,9 +216877,9 @@ static int fts5SpecialInsert( pConfig->bPrefixIndex = sqlite3_value_int(pVal); #endif }else{ - rc = sqlite3Fts5IndexLoadConfig(pTab->pIndex); + rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex); if( rc==SQLITE_OK ){ - rc = sqlite3Fts5ConfigSetValue(pTab->pConfig, zCmd, pVal, &bError); + rc = sqlite3Fts5ConfigSetValue(pTab->p.pConfig, zCmd, pVal, &bError); } if( rc==SQLITE_OK ){ if( bError ){ @@ -214593,7 +216893,7 @@ static int fts5SpecialInsert( } static int fts5SpecialDelete( - Fts5Table *pTab, + Fts5FullTable *pTab, sqlite3_value **apVal ){ int rc = SQLITE_OK; @@ -214607,7 +216907,7 @@ static int fts5SpecialDelete( static void fts5StorageInsert( int *pRc, - Fts5Table *pTab, + Fts5FullTable *pTab, sqlite3_value **apVal, i64 *piRowid ){ @@ -214641,8 +216941,8 @@ static int fts5UpdateMethod( sqlite3_value **apVal, /* Array of arguments */ sqlite_int64 *pRowid /* OUT: The affected (or effected) rowid */ ){ - Fts5Table *pTab = (Fts5Table*)pVtab; - Fts5Config *pConfig = pTab->pConfig; + Fts5FullTable *pTab = (Fts5FullTable*)pVtab; + Fts5Config *pConfig = pTab->p.pConfig; int eType0; /* value_type() of apVal[0] */ int rc = SQLITE_OK; /* Return code */ @@ -214651,12 +216951,11 @@ static int fts5UpdateMethod( assert( pVtab->zErrMsg==0 ); assert( nArg==1 || nArg==(2+pConfig->nCol+2) ); - assert( nArg==1 - || sqlite3_value_type(apVal[1])==SQLITE_INTEGER - || sqlite3_value_type(apVal[1])==SQLITE_NULL + assert( sqlite3_value_type(apVal[0])==SQLITE_INTEGER + || sqlite3_value_type(apVal[0])==SQLITE_NULL ); - assert( pTab->pConfig->pzErrmsg==0 ); - pTab->pConfig->pzErrmsg = &pTab->base.zErrMsg; + assert( pTab->p.pConfig->pzErrmsg==0 ); + pTab->p.pConfig->pzErrmsg = &pTab->p.base.zErrMsg; /* Put any active cursors into REQUIRE_SEEK state. */ fts5TripCursors(pTab); @@ -214697,7 +216996,7 @@ static int fts5UpdateMethod( /* Filter out attempts to run UPDATE or DELETE on contentless tables. ** This is not suported. */ if( eType0==SQLITE_INTEGER && fts5IsContentless(pTab) ){ - pTab->base.zErrMsg = sqlite3_mprintf( + pTab->p.base.zErrMsg = sqlite3_mprintf( "cannot %s contentless fts5 table: %s", (nArg>1 ? "UPDATE" : "DELETE from"), pConfig->zName ); @@ -214710,46 +217009,52 @@ static int fts5UpdateMethod( rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, 0); } - /* INSERT */ - else if( eType0!=SQLITE_INTEGER ){ - /* If this is a REPLACE, first remove the current entry (if any) */ - if( eConflict==SQLITE_REPLACE - && sqlite3_value_type(apVal[1])==SQLITE_INTEGER - ){ - i64 iNew = sqlite3_value_int64(apVal[1]); /* Rowid to delete */ - rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0); + /* INSERT or UPDATE */ + else{ + int eType1 = sqlite3_value_numeric_type(apVal[1]); + + if( eType1!=SQLITE_INTEGER && eType1!=SQLITE_NULL ){ + rc = SQLITE_MISMATCH; } - fts5StorageInsert(&rc, pTab, apVal, pRowid); - } - /* UPDATE */ - else{ - i64 iOld = sqlite3_value_int64(apVal[0]); /* Old rowid */ - i64 iNew = sqlite3_value_int64(apVal[1]); /* New rowid */ - if( iOld!=iNew ){ - if( eConflict==SQLITE_REPLACE ){ - rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0); - if( rc==SQLITE_OK ){ - rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0); - } - fts5StorageInsert(&rc, pTab, apVal, pRowid); - }else{ - rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, apVal, pRowid); - if( rc==SQLITE_OK ){ + else if( eType0!=SQLITE_INTEGER ){ + /* If this is a REPLACE, first remove the current entry (if any) */ + if( eConflict==SQLITE_REPLACE && eType1==SQLITE_INTEGER ){ + i64 iNew = sqlite3_value_int64(apVal[1]); /* Rowid to delete */ + rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0); + } + fts5StorageInsert(&rc, pTab, apVal, pRowid); + } + + /* UPDATE */ + else{ + i64 iOld = sqlite3_value_int64(apVal[0]); /* Old rowid */ + i64 iNew = sqlite3_value_int64(apVal[1]); /* New rowid */ + if( eType1==SQLITE_INTEGER && iOld!=iNew ){ + if( eConflict==SQLITE_REPLACE ){ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0); + if( rc==SQLITE_OK ){ + rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0); + } + fts5StorageInsert(&rc, pTab, apVal, pRowid); + }else{ + rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, apVal, pRowid); + if( rc==SQLITE_OK ){ + rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0); + } + if( rc==SQLITE_OK ){ + rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal,*pRowid); + } } - if( rc==SQLITE_OK ){ - rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal, *pRowid); - } + }else{ + rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0); + fts5StorageInsert(&rc, pTab, apVal, pRowid); } - }else{ - rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0); - fts5StorageInsert(&rc, pTab, apVal, pRowid); } } } - pTab->pConfig->pzErrmsg = 0; + pTab->p.pConfig->pzErrmsg = 0; return rc; } @@ -214758,12 +217063,12 @@ static int fts5UpdateMethod( */ static int fts5SyncMethod(sqlite3_vtab *pVtab){ int rc; - Fts5Table *pTab = (Fts5Table*)pVtab; + Fts5FullTable *pTab = (Fts5FullTable*)pVtab; fts5CheckTransactionState(pTab, FTS5_SYNC, 0); - pTab->pConfig->pzErrmsg = &pTab->base.zErrMsg; + pTab->p.pConfig->pzErrmsg = &pTab->p.base.zErrMsg; fts5TripCursors(pTab); rc = sqlite3Fts5StorageSync(pTab->pStorage); - pTab->pConfig->pzErrmsg = 0; + pTab->p.pConfig->pzErrmsg = 0; return rc; } @@ -214771,8 +217076,8 @@ static int fts5SyncMethod(sqlite3_vtab *pVtab){ ** Implementation of xBegin() method. */ static int fts5BeginMethod(sqlite3_vtab *pVtab){ - fts5CheckTransactionState((Fts5Table*)pVtab, FTS5_BEGIN, 0); - fts5NewTransaction((Fts5Table*)pVtab); + fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_BEGIN, 0); + fts5NewTransaction((Fts5FullTable*)pVtab); return SQLITE_OK; } @@ -214783,7 +217088,7 @@ static int fts5BeginMethod(sqlite3_vtab *pVtab){ */ static int fts5CommitMethod(sqlite3_vtab *pVtab){ UNUSED_PARAM(pVtab); /* Call below is a no-op for NDEBUG builds */ - fts5CheckTransactionState((Fts5Table*)pVtab, FTS5_COMMIT, 0); + fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_COMMIT, 0); return SQLITE_OK; } @@ -214793,7 +217098,7 @@ static int fts5CommitMethod(sqlite3_vtab *pVtab){ */ static int fts5RollbackMethod(sqlite3_vtab *pVtab){ int rc; - Fts5Table *pTab = (Fts5Table*)pVtab; + Fts5FullTable *pTab = (Fts5FullTable*)pVtab; fts5CheckTransactionState(pTab, FTS5_ROLLBACK, 0); rc = sqlite3Fts5StorageRollback(pTab->pStorage); return rc; @@ -214817,13 +217122,13 @@ static int fts5ApiColumnTotalSize( sqlite3_int64 *pnToken ){ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; - Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); + Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab); return sqlite3Fts5StorageSize(pTab->pStorage, iCol, pnToken); } static int fts5ApiRowCount(Fts5Context *pCtx, i64 *pnRow){ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; - Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); + Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab); return sqlite3Fts5StorageRowCount(pTab->pStorage, pnRow); } @@ -214858,7 +217163,9 @@ static int fts5ApiColumnText( ){ int rc = SQLITE_OK; Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; - if( fts5IsContentless((Fts5Table*)(pCsr->base.pVtab)) ){ + if( fts5IsContentless((Fts5FullTable*)(pCsr->base.pVtab)) + || pCsr->ePlan==FTS5_PLAN_SPECIAL + ){ *pz = 0; *pn = 0; }else{ @@ -214927,10 +217234,11 @@ static int fts5CacheInstArray(Fts5Cursor *pCsr){ int rc = SQLITE_OK; Fts5PoslistReader *aIter; /* One iterator for each phrase */ int nIter; /* Number of iterators/phrases */ + int nCol = ((Fts5Table*)pCsr->base.pVtab)->pConfig->nCol; nIter = sqlite3Fts5ExprPhraseCount(pCsr->pExpr); if( pCsr->aInstIter==0 ){ - int nByte = sizeof(Fts5PoslistReader) * nIter; + sqlite3_int64 nByte = sizeof(Fts5PoslistReader) * nIter; pCsr->aInstIter = (Fts5PoslistReader*)sqlite3Fts5MallocZero(&rc, nByte); } aIter = pCsr->aInstIter; @@ -214965,7 +217273,7 @@ static int fts5CacheInstArray(Fts5Cursor *pCsr){ nInst++; if( nInst>=pCsr->nInstAlloc ){ pCsr->nInstAlloc = pCsr->nInstAlloc ? pCsr->nInstAlloc*2 : 32; - aInst = (int*)sqlite3_realloc( + aInst = (int*)sqlite3_realloc64( pCsr->aInst, pCsr->nInstAlloc*sizeof(int)*3 ); if( aInst ){ @@ -214980,6 +217288,10 @@ static int fts5CacheInstArray(Fts5Cursor *pCsr){ aInst[0] = iBest; aInst[1] = FTS5_POS2COLUMN(aIter[iBest].iPos); aInst[2] = FTS5_POS2OFFSET(aIter[iBest].iPos); + if( aInst[1]<0 || aInst[1]>=nCol ){ + rc = FTS5_CORRUPT; + break; + } sqlite3Fts5PoslistReaderNext(&aIter[iBest]); } } @@ -215052,8 +217364,8 @@ static int fts5ColumnSizeCb( static int fts5ApiColumnSize(Fts5Context *pCtx, int iCol, int *pnToken){ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; - Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); - Fts5Config *pConfig = pTab->pConfig; + Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab); + Fts5Config *pConfig = pTab->p.pConfig; int rc = SQLITE_OK; if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_DOCSIZE) ){ @@ -215309,7 +217621,7 @@ static int fts5ApiQueryPhrase( int(*xCallback)(const Fts5ExtensionApi*, Fts5Context*, void*) ){ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; - Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); + Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab); int rc; Fts5Cursor *pNew = 0; @@ -215386,25 +217698,19 @@ static void fts5ApiCallback( /* -** Given cursor id iId, return a pointer to the corresponding Fts5Index +** Given cursor id iId, return a pointer to the corresponding Fts5Table ** object. Or NULL If the cursor id does not exist. -** -** If successful, set *ppConfig to point to the associated config object -** before returning. */ -static Fts5Index *sqlite3Fts5IndexFromCsrid( +static Fts5Table *sqlite3Fts5TableFromCsrid( Fts5Global *pGlobal, /* FTS5 global context for db handle */ - i64 iCsrId, /* Id of cursor to find */ - Fts5Config **ppConfig /* OUT: Configuration object */ + i64 iCsrId /* Id of cursor to find */ ){ Fts5Cursor *pCsr; - Fts5Table *pTab; - pCsr = fts5CursorFromCsrid(pGlobal, iCsrId); - pTab = (Fts5Table*)pCsr->base.pVtab; - *ppConfig = pTab->pConfig; - - return pTab->pIndex; + if( pCsr ){ + return (Fts5Table*)pCsr->base.pVtab; + } + return 0; } /* @@ -215484,8 +217790,8 @@ static int fts5ColumnMethod( sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */ int iCol /* Index of column to read value from */ ){ - Fts5Table *pTab = (Fts5Table*)(pCursor->pVtab); - Fts5Config *pConfig = pTab->pConfig; + Fts5FullTable *pTab = (Fts5FullTable*)(pCursor->pVtab); + Fts5Config *pConfig = pTab->p.pConfig; Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; int rc = SQLITE_OK; @@ -215537,7 +217843,7 @@ static int fts5FindFunctionMethod( void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), /* OUT: Result */ void **ppArg /* OUT: User data for *pxFunc */ ){ - Fts5Table *pTab = (Fts5Table*)pVtab; + Fts5FullTable *pTab = (Fts5FullTable*)pVtab; Fts5Auxiliary *pAux; UNUSED_PARAM(nUnused); @@ -215559,21 +217865,24 @@ static int fts5RenameMethod( sqlite3_vtab *pVtab, /* Virtual table handle */ const char *zName /* New name of table */ ){ - Fts5Table *pTab = (Fts5Table*)pVtab; + Fts5FullTable *pTab = (Fts5FullTable*)pVtab; return sqlite3Fts5StorageRename(pTab->pStorage, zName); } +static int sqlite3Fts5FlushToDisk(Fts5Table *pTab){ + fts5TripCursors((Fts5FullTable*)pTab); + return sqlite3Fts5StorageSync(((Fts5FullTable*)pTab)->pStorage); +} + /* ** The xSavepoint() method. ** ** Flush the contents of the pending-terms table to disk. */ static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){ - Fts5Table *pTab = (Fts5Table*)pVtab; UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */ - fts5CheckTransactionState(pTab, FTS5_SAVEPOINT, iSavepoint); - fts5TripCursors(pTab); - return sqlite3Fts5StorageSync(pTab->pStorage); + fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_SAVEPOINT, iSavepoint); + return sqlite3Fts5FlushToDisk((Fts5Table*)pVtab); } /* @@ -215582,11 +217891,9 @@ static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){ ** This is a no-op. */ static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){ - Fts5Table *pTab = (Fts5Table*)pVtab; UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */ - fts5CheckTransactionState(pTab, FTS5_RELEASE, iSavepoint); - fts5TripCursors(pTab); - return sqlite3Fts5StorageSync(pTab->pStorage); + fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_RELEASE, iSavepoint); + return sqlite3Fts5FlushToDisk((Fts5Table*)pVtab); } /* @@ -215595,7 +217902,7 @@ static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){ ** Discard the contents of the pending terms table. */ static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){ - Fts5Table *pTab = (Fts5Table*)pVtab; + Fts5FullTable *pTab = (Fts5FullTable*)pVtab; UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */ fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint); fts5TripCursors(pTab); @@ -215616,14 +217923,14 @@ static int fts5CreateAux( int rc = sqlite3_overload_function(pGlobal->db, zName, -1); if( rc==SQLITE_OK ){ Fts5Auxiliary *pAux; - int nName; /* Size of zName in bytes, including \0 */ - int nByte; /* Bytes of space to allocate */ + sqlite3_int64 nName; /* Size of zName in bytes, including \0 */ + sqlite3_int64 nByte; /* Bytes of space to allocate */ - nName = (int)strlen(zName) + 1; + nName = strlen(zName) + 1; nByte = sizeof(Fts5Auxiliary) + nName; - pAux = (Fts5Auxiliary*)sqlite3_malloc(nByte); + pAux = (Fts5Auxiliary*)sqlite3_malloc64(nByte); if( pAux ){ - memset(pAux, 0, nByte); + memset(pAux, 0, (size_t)nByte); pAux->zFunc = (char*)&pAux[1]; memcpy(pAux->zFunc, zName, nName); pAux->pGlobal = pGlobal; @@ -215653,15 +217960,15 @@ static int fts5CreateTokenizer( ){ Fts5Global *pGlobal = (Fts5Global*)pApi; Fts5TokenizerModule *pNew; - int nName; /* Size of zName and its \0 terminator */ - int nByte; /* Bytes of space to allocate */ + sqlite3_int64 nName; /* Size of zName and its \0 terminator */ + sqlite3_int64 nByte; /* Bytes of space to allocate */ int rc = SQLITE_OK; - nName = (int)strlen(zName) + 1; + nName = strlen(zName) + 1; nByte = sizeof(Fts5TokenizerModule) + nName; - pNew = (Fts5TokenizerModule*)sqlite3_malloc(nByte); + pNew = (Fts5TokenizerModule*)sqlite3_malloc64(nByte); if( pNew ){ - memset(pNew, 0, nByte); + memset(pNew, 0, (size_t)nByte); pNew->zName = (char*)&pNew[1]; memcpy(pNew->zName, zName, nName); pNew->pUserData = pUserData; @@ -215796,7 +218103,7 @@ static void fts5SourceIdFunc( ){ assert( nArg==0 ); UNUSED_PARAM2(nArg, apUnused); - sqlite3_result_text(pCtx, "fts5: 2018-12-01 12:34:55 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238b4f9", -1, SQLITE_TRANSIENT); + sqlite3_result_text(pCtx, "fts5: 2019-04-16 19:49:53 884b4b7e502b4e991677b53971277adfaf0a04a284f8e483e2553d0f83156b50", -1, SQLITE_TRANSIENT); } /* @@ -216045,7 +218352,7 @@ static int fts5StorageGetStmt( char *zBind; int i; - zBind = sqlite3_malloc(1 + nCol*2); + zBind = sqlite3_malloc64(1 + nCol*2); if( zBind ){ for(i=0; idb, zSql, -1, - SQLITE_PREPARE_PERSISTENT, &p->aStmt[eStmt], 0); + int f = SQLITE_PREPARE_PERSISTENT; + if( eStmt>FTS5_STMT_LOOKUP ) f |= SQLITE_PREPARE_NO_VTAB; + rc = sqlite3_prepare_v3(pC->db, zSql, -1, f, &p->aStmt[eStmt], 0); sqlite3_free(zSql); if( rc!=SQLITE_OK && pzErrMsg ){ *pzErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pC->db)); @@ -216211,14 +218519,14 @@ static int sqlite3Fts5StorageOpen( ){ int rc = SQLITE_OK; Fts5Storage *p; /* New object */ - int nByte; /* Bytes of space to allocate */ + sqlite3_int64 nByte; /* Bytes of space to allocate */ nByte = sizeof(Fts5Storage) /* Fts5Storage object */ + pConfig->nCol * sizeof(i64); /* Fts5Storage.aTotalSize[] */ - *pp = p = (Fts5Storage*)sqlite3_malloc(nByte); + *pp = p = (Fts5Storage*)sqlite3_malloc64(nByte); if( !p ) return SQLITE_NOMEM; - memset(p, 0, nByte); + memset(p, 0, (size_t)nByte); p->aTotalSize = (i64*)&p[1]; p->pConfig = pConfig; p->pIndex = pIndex; @@ -216226,7 +218534,7 @@ static int sqlite3Fts5StorageOpen( if( bCreate ){ if( pConfig->eContent==FTS5_CONTENT_NORMAL ){ int nDefn = 32 + pConfig->nCol*10; - char *zDefn = sqlite3_malloc(32 + pConfig->nCol * 10); + char *zDefn = sqlite3_malloc64(32 + (sqlite3_int64)pConfig->nCol * 10); if( zDefn==0 ){ rc = SQLITE_NOMEM; }else{ @@ -216517,7 +218825,7 @@ static int sqlite3Fts5StorageRebuild(Fts5Storage *p){ Fts5Config *pConfig = p->pConfig; sqlite3_stmt *pScan = 0; Fts5InsertCtx ctx; - int rc; + int rc, rc2; memset(&ctx, 0, sizeof(Fts5InsertCtx)); ctx.pStorage = p; @@ -216556,6 +218864,8 @@ static int sqlite3Fts5StorageRebuild(Fts5Storage *p){ } } sqlite3_free(buf.p); + rc2 = sqlite3_reset(pScan); + if( rc==SQLITE_OK ) rc = rc2; /* Write the averages record */ if( rc==SQLITE_OK ){ @@ -216805,7 +219115,7 @@ static int sqlite3Fts5StorageIntegrity(Fts5Storage *p){ memset(&ctx, 0, sizeof(Fts5IntegrityCtx)); ctx.pConfig = p->pConfig; - aTotalSize = (i64*)sqlite3_malloc(pConfig->nCol * (sizeof(int)+sizeof(i64))); + aTotalSize = (i64*)sqlite3_malloc64(pConfig->nCol*(sizeof(int)+sizeof(i64))); if( !aTotalSize ) return SQLITE_NOMEM; aColSize = (int*)&aTotalSize[pConfig->nCol]; memset(aTotalSize, 0, sizeof(i64) * pConfig->nCol); @@ -217005,7 +219315,13 @@ static int sqlite3Fts5StorageSize(Fts5Storage *p, int iCol, i64 *pnToken){ static int sqlite3Fts5StorageRowCount(Fts5Storage *p, i64 *pnRow){ int rc = fts5StorageLoadTotals(p, 0); if( rc==SQLITE_OK ){ + /* nTotalRow being zero does not necessarily indicate a corrupt + ** database - it might be that the FTS5 table really does contain zero + ** rows. However this function is only called from the xRowCount() API, + ** and there is no way for that API to be invoked if the table contains + ** no rows. Hence the FTS5_CORRUPT return. */ *pnRow = p->nTotalRow; + if( p->nTotalRow<=0 ) rc = FTS5_CORRUPT; } return rc; } @@ -217215,7 +219531,7 @@ static int fts5AsciiTokenize( nByte = ie-is; if( nByte>nFold ){ if( pFold!=aFold ) sqlite3_free(pFold); - pFold = sqlite3_malloc(nByte*2); + pFold = sqlite3_malloc64((sqlite3_int64)nByte*2); if( pFold==0 ){ rc = SQLITE_NOMEM; break; @@ -217297,13 +219613,18 @@ struct Unicode61Tokenizer { unsigned char aTokenChar[128]; /* ASCII range token characters */ char *aFold; /* Buffer to fold text into */ int nFold; /* Size of aFold[] in bytes */ - int bRemoveDiacritic; /* True if remove_diacritics=1 is set */ + int eRemoveDiacritic; /* True if remove_diacritics=1 is set */ int nException; int *aiException; unsigned char aCategory[32]; /* True for token char categories */ }; +/* Values for eRemoveDiacritic (must match internals of fts5_unicode2.c) */ +#define FTS5_REMOVE_DIACRITICS_NONE 0 +#define FTS5_REMOVE_DIACRITICS_SIMPLE 1 +#define FTS5_REMOVE_DIACRITICS_COMPLEX 2 + static int fts5UnicodeAddExceptions( Unicode61Tokenizer *p, /* Tokenizer object */ const char *z, /* Characters to treat as exceptions */ @@ -217314,13 +219635,14 @@ static int fts5UnicodeAddExceptions( int *aNew; if( n>0 ){ - aNew = (int*)sqlite3_realloc(p->aiException, (n+p->nException)*sizeof(int)); + aNew = (int*)sqlite3_realloc64(p->aiException, + (n+p->nException)*sizeof(int)); if( aNew ){ int nNew = p->nException; const unsigned char *zCsr = (const unsigned char*)z; const unsigned char *zTerm = (const unsigned char*)&z[n]; while( zCsriCode ) break; + if( (u32)aNew[i]>iCode ) break; } memmove(&aNew[i+1], &aNew[i], (nNew-i)*sizeof(int)); aNew[i] = iCode; @@ -217424,9 +219746,9 @@ static int fts5UnicodeCreate( int i; memset(p, 0, sizeof(Unicode61Tokenizer)); - p->bRemoveDiacritic = 1; + p->eRemoveDiacritic = FTS5_REMOVE_DIACRITICS_SIMPLE; p->nFold = 64; - p->aFold = sqlite3_malloc(p->nFold * sizeof(char)); + p->aFold = sqlite3_malloc64(p->nFold * sizeof(char)); if( p->aFold==0 ){ rc = SQLITE_NOMEM; } @@ -217445,10 +219767,15 @@ static int fts5UnicodeCreate( for(i=0; rc==SQLITE_OK && ieRemoveDiacritic = (zArg[0] - '0'); + assert( p->eRemoveDiacritic==FTS5_REMOVE_DIACRITICS_NONE + || p->eRemoveDiacritic==FTS5_REMOVE_DIACRITICS_SIMPLE + || p->eRemoveDiacritic==FTS5_REMOVE_DIACRITICS_COMPLEX + ); } - p->bRemoveDiacritic = (zArg[0]=='1'); }else if( 0==sqlite3_stricmp(azArg[i], "tokenchars") ){ rc = fts5UnicodeAddExceptions(p, zArg, 1); @@ -217482,7 +219809,7 @@ static int fts5UnicodeCreate( */ static int fts5UnicodeIsAlnum(Unicode61Tokenizer *p, int iCode){ return ( - p->aCategory[sqlite3Fts5UnicodeCategory(iCode)] + p->aCategory[sqlite3Fts5UnicodeCategory((u32)iCode)] ^ fts5UnicodeIsException(p, iCode) ); } @@ -217511,7 +219838,7 @@ static int fts5UnicodeTokenize( /* Each iteration of this loop gobbles up a contiguous run of separators, ** then the next token. */ while( rc==SQLITE_OK ){ - int iCode; /* non-ASCII codepoint read from input */ + u32 iCode; /* non-ASCII codepoint read from input */ char *zOut = aFold; int is; int ie; @@ -217543,7 +219870,7 @@ static int fts5UnicodeTokenize( /* Grow the output buffer so that there is sufficient space to fit the ** largest possible utf-8 character. */ if( zOut>pEnd ){ - aFold = sqlite3_malloc(nFold*2); + aFold = sqlite3_malloc64((sqlite3_int64)nFold*2); if( aFold==0 ){ rc = SQLITE_NOMEM; goto tokenize_done; @@ -217562,7 +219889,7 @@ static int fts5UnicodeTokenize( READ_UTF8(zCsr, zTerm, iCode); if( fts5UnicodeIsAlnum(p,iCode)||sqlite3Fts5UnicodeIsdiacritic(iCode) ){ non_ascii_tokenchar: - iCode = sqlite3Fts5UnicodeFold(iCode, p->bRemoveDiacritic); + iCode = sqlite3Fts5UnicodeFold(iCode, p->eRemoveDiacritic); if( iCode ) WRITE_UTF8(zOut, iCode); }else{ break; @@ -218338,10 +220665,8 @@ static int sqlite3Fts5TokenizerInit(fts5_api *pApi){ return rc; } - - /* -** 2012 May 25 +** 2012-05-25 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: @@ -218370,32 +220695,48 @@ static int sqlite3Fts5TokenizerInit(fts5_api *pApi){ ** E"). The resuls of passing a codepoint that corresponds to an ** uppercase letter are undefined. */ -static int fts5_remove_diacritic(int c){ +static int fts5_remove_diacritic(int c, int bComplex){ unsigned short aDia[] = { 0, 1797, 1848, 1859, 1891, 1928, 1940, 1995, 2024, 2040, 2060, 2110, 2168, 2206, 2264, 2286, 2344, 2383, 2472, 2488, 2516, 2596, 2668, 2732, 2782, 2842, 2894, 2954, 2984, 3000, 3028, 3336, - 3456, 3696, 3712, 3728, 3744, 3896, 3912, 3928, - 3968, 4008, 4040, 4106, 4138, 4170, 4202, 4234, - 4266, 4296, 4312, 4344, 4408, 4424, 4472, 4504, - 6148, 6198, 6264, 6280, 6360, 6429, 6505, 6529, - 61448, 61468, 61534, 61592, 61642, 61688, 61704, 61726, - 61784, 61800, 61836, 61880, 61914, 61948, 61998, 62122, - 62154, 62200, 62218, 62302, 62364, 62442, 62478, 62536, - 62554, 62584, 62604, 62640, 62648, 62656, 62664, 62730, - 62924, 63050, 63082, 63274, 63390, + 3456, 3696, 3712, 3728, 3744, 3766, 3832, 3896, + 3912, 3928, 3944, 3968, 4008, 4040, 4056, 4106, + 4138, 4170, 4202, 4234, 4266, 4296, 4312, 4344, + 4408, 4424, 4442, 4472, 4488, 4504, 6148, 6198, + 6264, 6280, 6360, 6429, 6505, 6529, 61448, 61468, + 61512, 61534, 61592, 61610, 61642, 61672, 61688, 61704, + 61726, 61784, 61800, 61816, 61836, 61880, 61896, 61914, + 61948, 61998, 62062, 62122, 62154, 62184, 62200, 62218, + 62252, 62302, 62364, 62410, 62442, 62478, 62536, 62554, + 62584, 62604, 62640, 62648, 62656, 62664, 62730, 62766, + 62830, 62890, 62924, 62974, 63032, 63050, 63082, 63118, + 63182, 63242, 63274, 63310, 63368, 63390, }; - char aChar[] = { - '\0', 'a', 'c', 'e', 'i', 'n', 'o', 'u', 'y', 'y', 'a', 'c', - 'd', 'e', 'e', 'g', 'h', 'i', 'j', 'k', 'l', 'n', 'o', 'r', - 's', 't', 'u', 'u', 'w', 'y', 'z', 'o', 'u', 'a', 'i', 'o', - 'u', 'g', 'k', 'o', 'j', 'g', 'n', 'a', 'e', 'i', 'o', 'r', - 'u', 's', 't', 'h', 'a', 'e', 'o', 'y', '\0', '\0', '\0', '\0', - '\0', '\0', '\0', '\0', 'a', 'b', 'd', 'd', 'e', 'f', 'g', 'h', - 'h', 'i', 'k', 'l', 'l', 'm', 'n', 'p', 'r', 'r', 's', 't', - 'u', 'v', 'w', 'w', 'x', 'y', 'z', 'h', 't', 'w', 'y', 'a', - 'e', 'i', 'o', 'u', 'y', +#define HIBIT ((unsigned char)0x80) + unsigned char aChar[] = { + '\0', 'a', 'c', 'e', 'i', 'n', + 'o', 'u', 'y', 'y', 'a', 'c', + 'd', 'e', 'e', 'g', 'h', 'i', + 'j', 'k', 'l', 'n', 'o', 'r', + 's', 't', 'u', 'u', 'w', 'y', + 'z', 'o', 'u', 'a', 'i', 'o', + 'u', 'u'|HIBIT, 'a'|HIBIT, 'g', 'k', 'o', + 'o'|HIBIT, 'j', 'g', 'n', 'a'|HIBIT, 'a', + 'e', 'i', 'o', 'r', 'u', 's', + 't', 'h', 'a', 'e', 'o'|HIBIT, 'o', + 'o'|HIBIT, 'y', '\0', '\0', '\0', '\0', + '\0', '\0', '\0', '\0', 'a', 'b', + 'c'|HIBIT, 'd', 'd', 'e'|HIBIT, 'e', 'e'|HIBIT, + 'f', 'g', 'h', 'h', 'i', 'i'|HIBIT, + 'k', 'l', 'l'|HIBIT, 'l', 'm', 'n', + 'o'|HIBIT, 'p', 'r', 'r'|HIBIT, 'r', 's', + 's'|HIBIT, 't', 'u', 'u'|HIBIT, 'v', 'w', + 'w', 'x', 'y', 'z', 'h', 't', + 'w', 'y', 'a', 'a'|HIBIT, 'a'|HIBIT, 'a'|HIBIT, + 'e', 'e'|HIBIT, 'e'|HIBIT, 'i', 'o', 'o'|HIBIT, + 'o'|HIBIT, 'o'|HIBIT, 'u', 'u'|HIBIT, 'u'|HIBIT, 'y', }; unsigned int key = (((unsigned int)c)<<3) | 0x00000007; @@ -218412,7 +220753,8 @@ static int fts5_remove_diacritic(int c){ } } assert( key>=aDia[iRes] ); - return ((c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c : (int)aChar[iRes]); + if( bComplex==0 && (aChar[iRes] & 0x80) ) return c; + return (c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c : ((int)aChar[iRes] & 0x7F); } @@ -218425,8 +220767,8 @@ static int sqlite3Fts5UnicodeIsdiacritic(int c){ unsigned int mask1 = 0x000361F8; if( c<768 || c>817 ) return 0; return (c < 768+32) ? - (mask0 & (1 << (c-768))) : - (mask1 & (1 << (c-768-32))); + (mask0 & ((unsigned int)1 << (c-768))) : + (mask1 & ((unsigned int)1 << (c-768-32))); } @@ -218439,7 +220781,7 @@ static int sqlite3Fts5UnicodeIsdiacritic(int c){ ** The results are undefined if the value passed to this function ** is less than zero. */ -static int sqlite3Fts5UnicodeFold(int c, int bRemoveDiacritic){ +static int sqlite3Fts5UnicodeFold(int c, int eRemoveDiacritic){ /* Each entry in the following array defines a rule for folding a range ** of codepoints to lower case. The rule applies to a range of nRange ** codepoints starting at codepoint iCode. @@ -218562,7 +220904,9 @@ static int sqlite3Fts5UnicodeFold(int c, int bRemoveDiacritic){ assert( ret>0 ); } - if( bRemoveDiacritic ) ret = fts5_remove_diacritic(ret); + if( eRemoveDiacritic ){ + ret = fts5_remove_diacritic(ret, eRemoveDiacritic==2); + } } else if( c>=66560 && c<66600 ){ @@ -218573,12 +220917,6 @@ static int sqlite3Fts5UnicodeFold(int c, int bRemoveDiacritic){ } -#if 0 -static int sqlite3Fts5UnicodeNCat(void) { - return 32; -} -#endif - static int sqlite3Fts5UnicodeCatParse(const char *zCat, u8 *aArray){ aArray[0] = 1; switch( zCat[0] ){ @@ -219060,7 +221398,7 @@ static u16 aFts5UnicodeData[] = { 34, 3074, 7692, 63, 63, }; -static int sqlite3Fts5UnicodeCategory(int iCode) { +static int sqlite3Fts5UnicodeCategory(u32 iCode) { int iRes = -1; int iHi; int iLo; @@ -219104,7 +221442,6 @@ static void sqlite3Fts5UnicodeAscii(u8 *aArray, u8 *aAscii){ } } - /* ** 2015 May 30 ** @@ -219183,7 +221520,7 @@ static int sqlite3Fts5GetVarint32(const unsigned char *p, u32 *v){ u8 n; p -= 2; n = sqlite3Fts5GetVarint(p, &v64); - *v = (u32)v64; + *v = ((u32)v64) & 0x7FFFFFFF; assert( n>3 && n<=9 ); return n; } @@ -219450,7 +221787,6 @@ static int sqlite3Fts5GetVarintLen(u32 iVal){ return 5; } - /* ** 2015 May 08 ** @@ -219508,7 +221844,7 @@ struct Fts5VocabTable { struct Fts5VocabCursor { sqlite3_vtab_cursor base; sqlite3_stmt *pStmt; /* Statement holding lock on pIndex */ - Fts5Index *pIndex; /* Associated FTS5 index */ + Fts5Table *pFts5; /* Associated FTS5 table */ int bEof; /* True if this cursor is at EOF */ Fts5IndexIter *pIter; /* Term/rowid iterator object */ @@ -219517,7 +221853,6 @@ struct Fts5VocabCursor { char *zLeTerm; /* (term <= $zLeTerm) paramater, or NULL */ /* These are used by 'col' tables only */ - Fts5Config *pConfig; /* Fts5 table configuration */ int iCol; i64 *aCnt; i64 *aDoc; @@ -219780,8 +222115,7 @@ static int fts5VocabOpenMethod( sqlite3_vtab_cursor **ppCsr ){ Fts5VocabTable *pTab = (Fts5VocabTable*)pVTab; - Fts5Index *pIndex = 0; - Fts5Config *pConfig = 0; + Fts5Table *pFts5 = 0; Fts5VocabCursor *pCsr = 0; int rc = SQLITE_OK; sqlite3_stmt *pStmt = 0; @@ -219800,31 +222134,34 @@ static int fts5VocabOpenMethod( if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){ i64 iId = sqlite3_column_int64(pStmt, 0); - pIndex = sqlite3Fts5IndexFromCsrid(pTab->pGlobal, iId, &pConfig); + pFts5 = sqlite3Fts5TableFromCsrid(pTab->pGlobal, iId); } - if( rc==SQLITE_OK && pIndex==0 ){ - rc = sqlite3_finalize(pStmt); - pStmt = 0; - if( rc==SQLITE_OK ){ - pVTab->zErrMsg = sqlite3_mprintf( - "no such fts5 table: %s.%s", pTab->zFts5Db, pTab->zFts5Tbl - ); - rc = SQLITE_ERROR; + if( rc==SQLITE_OK ){ + if( pFts5==0 ){ + rc = sqlite3_finalize(pStmt); + pStmt = 0; + if( rc==SQLITE_OK ){ + pVTab->zErrMsg = sqlite3_mprintf( + "no such fts5 table: %s.%s", pTab->zFts5Db, pTab->zFts5Tbl + ); + rc = SQLITE_ERROR; + } + }else{ + rc = sqlite3Fts5FlushToDisk(pFts5); } } if( rc==SQLITE_OK ){ - int nByte = pConfig->nCol * sizeof(i64) * 2 + sizeof(Fts5VocabCursor); + int nByte = pFts5->pConfig->nCol * sizeof(i64)*2 + sizeof(Fts5VocabCursor); pCsr = (Fts5VocabCursor*)sqlite3Fts5MallocZero(&rc, nByte); } if( pCsr ){ - pCsr->pIndex = pIndex; + pCsr->pFts5 = pFts5; pCsr->pStmt = pStmt; - pCsr->pConfig = pConfig; pCsr->aCnt = (i64*)&pCsr[1]; - pCsr->aDoc = &pCsr->aCnt[pConfig->nCol]; + pCsr->aDoc = &pCsr->aCnt[pFts5->pConfig->nCol]; }else{ sqlite3_finalize(pStmt); } @@ -219840,6 +222177,7 @@ static void fts5VocabResetCursor(Fts5VocabCursor *pCsr){ sqlite3_free(pCsr->zLeTerm); pCsr->nLeTerm = -1; pCsr->zLeTerm = 0; + pCsr->bEof = 0; } /* @@ -219878,7 +222216,7 @@ static int fts5VocabInstanceNewTerm(Fts5VocabCursor *pCsr){ } static int fts5VocabInstanceNext(Fts5VocabCursor *pCsr){ - int eDetail = pCsr->pConfig->eDetail; + int eDetail = pCsr->pFts5->pConfig->eDetail; int rc = SQLITE_OK; Fts5IndexIter *pIter = pCsr->pIter; i64 *pp = &pCsr->iInstPos; @@ -219913,7 +222251,7 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){ Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor; Fts5VocabTable *pTab = (Fts5VocabTable*)pCursor->pVtab; int rc = SQLITE_OK; - int nCol = pCsr->pConfig->nCol; + int nCol = pCsr->pFts5->pConfig->nCol; pCsr->rowid++; @@ -219935,6 +222273,7 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){ int nTerm; zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm); + assert( nTerm>=0 ); if( pCsr->nLeTerm>=0 ){ int nCmp = MIN(nTerm, pCsr->nLeTerm); int bCmp = memcmp(pCsr->zLeTerm, zTerm, nCmp); @@ -219951,7 +222290,7 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){ assert( pTab->eType==FTS5_VOCAB_COL || pTab->eType==FTS5_VOCAB_ROW ); while( rc==SQLITE_OK ){ - int eDetail = pCsr->pConfig->eDetail; + int eDetail = pCsr->pFts5->pConfig->eDetail; const u8 *pPos; int nPos; /* Position list */ i64 iPos = 0; /* 64-bit position read from poslist */ int iOff = 0; /* Current offset within position list */ @@ -219974,7 +222313,6 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){ int iCol = -1; while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){ int ii = FTS5_POS2COLUMN(iPos); - pCsr->aCnt[ii]++; if( iCol!=ii ){ if( ii>=nCol ){ rc = FTS5_CORRUPT; @@ -219983,6 +222321,7 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){ pCsr->aDoc[ii]++; iCol = ii; } + pCsr->aCnt[ii]++; } }else if( eDetail==FTS5_DETAIL_COLUMNS ){ while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff,&iPos) ){ @@ -220011,7 +222350,9 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){ if( rc==SQLITE_OK ){ zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm); - if( nTerm!=pCsr->term.n || memcmp(zTerm, pCsr->term.p, nTerm) ){ + if( nTerm!=pCsr->term.n + || (nTerm>0 && memcmp(zTerm, pCsr->term.p, nTerm)) + ){ break; } if( sqlite3Fts5IterEof(pCsr->pIter) ) break; @@ -220022,7 +222363,7 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){ if( rc==SQLITE_OK && pCsr->bEof==0 && pTab->eType==FTS5_VOCAB_COL ){ while( pCsr->aDoc[pCsr->iCol]==0 ) pCsr->iCol++; - assert( pCsr->iColpConfig->nCol ); + assert( pCsr->iColpFts5->pConfig->nCol ); } return rc; } @@ -220069,6 +222410,7 @@ static int fts5VocabFilterMethod( } if( pLe ){ const char *zCopy = (const char *)sqlite3_value_text(pLe); + if( zCopy==0 ) zCopy = ""; pCsr->nLeTerm = sqlite3_value_bytes(pLe); pCsr->zLeTerm = sqlite3_malloc(pCsr->nLeTerm+1); if( pCsr->zLeTerm==0 ){ @@ -220080,14 +222422,15 @@ static int fts5VocabFilterMethod( } if( rc==SQLITE_OK ){ - rc = sqlite3Fts5IndexQuery(pCsr->pIndex, zTerm, nTerm, f, 0, &pCsr->pIter); + Fts5Index *pIndex = pCsr->pFts5->pIndex; + rc = sqlite3Fts5IndexQuery(pIndex, zTerm, nTerm, f, 0, &pCsr->pIter); } if( rc==SQLITE_OK && eType==FTS5_VOCAB_INSTANCE ){ rc = fts5VocabInstanceNewTerm(pCsr); } - if( rc==SQLITE_OK - && !pCsr->bEof - && (eType!=FTS5_VOCAB_INSTANCE || pCsr->pConfig->eDetail!=FTS5_DETAIL_NONE) + if( rc==SQLITE_OK && !pCsr->bEof + && (eType!=FTS5_VOCAB_INSTANCE + || pCsr->pFts5->pConfig->eDetail!=FTS5_DETAIL_NONE) ){ rc = fts5VocabNextMethod(pCursor); } @@ -220110,7 +222453,7 @@ static int fts5VocabColumnMethod( int iCol /* Index of column to read value from */ ){ Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor; - int eDetail = pCsr->pConfig->eDetail; + int eDetail = pCsr->pFts5->pConfig->eDetail; int eType = ((Fts5VocabTable*)(pCursor->pVtab))->eType; i64 iVal = 0; @@ -220122,7 +222465,7 @@ static int fts5VocabColumnMethod( assert( iCol==1 || iCol==2 || iCol==3 ); if( iCol==1 ){ if( eDetail!=FTS5_DETAIL_NONE ){ - const char *z = pCsr->pConfig->azCol[pCsr->iCol]; + const char *z = pCsr->pFts5->pConfig->azCol[pCsr->iCol]; sqlite3_result_text(pCtx, z, -1, SQLITE_STATIC); } }else if( iCol==2 ){ @@ -220150,8 +222493,8 @@ static int fts5VocabColumnMethod( }else if( eDetail==FTS5_DETAIL_COLUMNS ){ ii = (int)pCsr->iInstPos; } - if( ii>=0 && iipConfig->nCol ){ - const char *z = pCsr->pConfig->azCol[ii]; + if( ii>=0 && iipFts5->pConfig->nCol ){ + const char *z = pCsr->pFts5->pConfig->azCol[ii]; sqlite3_result_text(pCtx, z, -1, SQLITE_STATIC); } break; @@ -220524,9 +222867,9 @@ SQLITE_API int sqlite3_stmt_init( #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ /************** End of stmt.c ************************************************/ -#if __LINE__!=220527 +#if __LINE__!=222870 #undef SQLITE_SOURCE_ID -#define SQLITE_SOURCE_ID "2018-12-01 12:34:55 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238alt2" +#define SQLITE_SOURCE_ID "2019-04-16 19:49:53 884b4b7e502b4e991677b53971277adfaf0a04a284f8e483e2553d0f8315alt2" #endif /* Return the source-id for this library */ SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } diff --git a/src/3rdparty/sqlite/sqlite3.h b/src/3rdparty/sqlite/sqlite3.h index f36ae57a64..fadfe1e152 100644 --- a/src/3rdparty/sqlite/sqlite3.h +++ b/src/3rdparty/sqlite/sqlite3.h @@ -123,9 +123,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.26.0" -#define SQLITE_VERSION_NUMBER 3026000 -#define SQLITE_SOURCE_ID "2018-12-01 12:34:55 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238b4f9" +#define SQLITE_VERSION "3.28.0" +#define SQLITE_VERSION_NUMBER 3028000 +#define SQLITE_SOURCE_ID "2019-04-16 19:49:53 884b4b7e502b4e991677b53971277adfaf0a04a284f8e483e2553d0f83156b50" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -189,6 +189,9 @@ SQLITE_API int sqlite3_libversion_number(void); #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS SQLITE_API int sqlite3_compileoption_used(const char *zOptName); SQLITE_API const char *sqlite3_compileoption_get(int N); +#else +# define sqlite3_compileoption_used(X) 0 +# define sqlite3_compileoption_get(X) ((void*)0) #endif /* @@ -823,6 +826,15 @@ struct sqlite3_io_methods { ** file space based on this hint in order to help writes to the database ** file run faster. ** +**
  • [[SQLITE_FCNTL_SIZE_LIMIT]] +** The [SQLITE_FCNTL_SIZE_LIMIT] opcode is used by in-memory VFS that +** implements [sqlite3_deserialize()] to set an upper bound on the size +** of the in-memory database. The argument is a pointer to a [sqlite3_int64]. +** If the integer pointed to is negative, then it is filled in with the +** current limit. Otherwise the limit is set to the larger of the value +** of the integer pointed to and the current database size. The integer +** pointed to is set to the new limit. +** **
  • [[SQLITE_FCNTL_CHUNK_SIZE]] ** The [SQLITE_FCNTL_CHUNK_SIZE] opcode is used to request that the VFS ** extends and truncates the database file in chunks of a size specified @@ -1131,6 +1143,7 @@ struct sqlite3_io_methods { #define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33 #define SQLITE_FCNTL_LOCK_TIMEOUT 34 #define SQLITE_FCNTL_DATA_VERSION 35 +#define SQLITE_FCNTL_SIZE_LIMIT 36 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE @@ -1972,6 +1985,17 @@ struct sqlite3_mem_methods { ** negative value for this option restores the default behaviour. ** This option is only available if SQLite is compiled with the ** [SQLITE_ENABLE_SORTER_REFERENCES] compile-time option. +** +** [[SQLITE_CONFIG_MEMDB_MAXSIZE]] +**
    SQLITE_CONFIG_MEMDB_MAXSIZE +**
    The SQLITE_CONFIG_MEMDB_MAXSIZE option accepts a single parameter +** [sqlite3_int64] parameter which is the default maximum size for an in-memory +** database created using [sqlite3_deserialize()]. This default maximum +** size can be adjusted up or down for individual databases using the +** [SQLITE_FCNTL_SIZE_LIMIT] [sqlite3_file_control|file-control]. If this +** configuration setting is never used, then the default maximum is determined +** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that +** compile-time option is not set, then the default maximum is 1073741824. ** */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ @@ -2002,6 +2026,7 @@ struct sqlite3_mem_methods { #define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */ #define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */ #define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */ +#define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */ /* ** CAPI3REF: Database Connection Configuration Options @@ -2064,8 +2089,8 @@ struct sqlite3_mem_methods { ** ** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]] **
    SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER
    -**
    ^This option is used to enable or disable the two-argument -** version of the [fts3_tokenizer()] function which is part of the +**
    ^This option is used to enable or disable the +** [fts3_tokenizer()] function which is part of the ** [FTS3] full-text search engine extension. ** There should be two additional arguments. ** The first argument is an integer which is 0 to disable fts3_tokenizer() or @@ -2177,6 +2202,17 @@ struct sqlite3_mem_methods { **
  • Direct writes to [shadow tables]. ** **
  • +** +** [[SQLITE_DBCONFIG_WRITABLE_SCHEMA]]
    SQLITE_DBCONFIG_WRITABLE_SCHEMA
    +**
    The SQLITE_DBCONFIG_WRITABLE_SCHEMA option activates or deactivates the +** "writable_schema" flag. This has the same effect and is logically equivalent +** to setting [PRAGMA writable_schema=ON] or [PRAGMA writable_schema=OFF]. +** The first argument to this setting is an integer which is 0 to disable +** the writable_schema, positive to enable writable_schema, or negative to +** leave the setting unchanged. The second parameter is a pointer to an +** integer into which is written 0 or 1 to indicate whether the writable_schema +** is enabled or disabled following this call. +**
    ** */ #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ @@ -2190,7 +2226,8 @@ struct sqlite3_mem_methods { #define SQLITE_DBCONFIG_TRIGGER_EQP 1008 /* int int* */ #define SQLITE_DBCONFIG_RESET_DATABASE 1009 /* int int* */ #define SQLITE_DBCONFIG_DEFENSIVE 1010 /* int int* */ -#define SQLITE_DBCONFIG_MAX 1010 /* Largest DBCONFIG */ +#define SQLITE_DBCONFIG_WRITABLE_SCHEMA 1011 /* int int* */ +#define SQLITE_DBCONFIG_MAX 1011 /* Largest DBCONFIG */ /* ** CAPI3REF: Enable Or Disable Extended Result Codes @@ -2347,7 +2384,7 @@ SQLITE_API int sqlite3_changes(sqlite3*); ** not. ^Changes to a view that are intercepted by INSTEAD OF triggers ** are not counted. ** -** This the [sqlite3_total_changes(D)] interface only reports the number +** The [sqlite3_total_changes(D)] interface only reports the number ** of rows that changed due to SQL statement run against database ** connection D. Any changes by other database connections are ignored. ** To detect changes against a database file from other database @@ -2991,9 +3028,9 @@ SQLITE_API int sqlite3_set_authorizer( ** time is in units of nanoseconds, however the current implementation ** is only capable of millisecond resolution so the six least significant ** digits in the time are meaningless. Future versions of SQLite -** might provide greater resolution on the profiler callback. The -** sqlite3_profile() function is considered experimental and is -** subject to change in future versions of SQLite. +** might provide greater resolution on the profiler callback. Invoking +** either [sqlite3_trace()] or [sqlite3_trace_v2()] will cancel the +** profile callback. */ SQLITE_API SQLITE_DEPRECATED void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*); @@ -3407,6 +3444,8 @@ SQLITE_API int sqlite3_open_v2( ** is not a database file pathname pointer that SQLite passed into the xOpen ** VFS method, then the behavior of this routine is undefined and probably ** undesirable. +** +** See the [URI filename] documentation for additional information. */ SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam); SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault); @@ -3629,18 +3668,23 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); ** deplete the limited store of lookaside memory. Future versions of ** SQLite may act on this hint differently. ** -** [[SQLITE_PREPARE_NORMALIZE]] ^(
    SQLITE_PREPARE_NORMALIZE
    -**
    The SQLITE_PREPARE_NORMALIZE flag indicates that a normalized -** representation of the SQL statement should be calculated and then -** associated with the prepared statement, which can be obtained via -** the [sqlite3_normalized_sql()] interface.)^ The semantics used to -** normalize a SQL statement are unspecified and subject to change. -** At a minimum, literal values will be replaced with suitable -** placeholders. +** [[SQLITE_PREPARE_NORMALIZE]]
    SQLITE_PREPARE_NORMALIZE
    +**
    The SQLITE_PREPARE_NORMALIZE flag is a no-op. This flag used +** to be required for any prepared statement that wanted to use the +** [sqlite3_normalized_sql()] interface. However, the +** [sqlite3_normalized_sql()] interface is now available to all +** prepared statements, regardless of whether or not they use this +** flag. +** +** [[SQLITE_PREPARE_NO_VTAB]]
    SQLITE_PREPARE_NO_VTAB
    +**
    The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler +** to return an error (error code SQLITE_ERROR) if the statement uses +** any virtual tables. ** */ #define SQLITE_PREPARE_PERSISTENT 0x01 #define SQLITE_PREPARE_NORMALIZE 0x02 +#define SQLITE_PREPARE_NO_VTAB 0x04 /* ** CAPI3REF: Compiling An SQL Statement @@ -3865,6 +3909,18 @@ SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt); */ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); +/* +** CAPI3REF: Query The EXPLAIN Setting For A Prepared Statement +** METHOD: sqlite3_stmt +** +** ^The sqlite3_stmt_isexplain(S) interface returns 1 if the +** prepared statement S is an EXPLAIN statement, or 2 if the +** statement S is an EXPLAIN QUERY PLAN. +** ^The sqlite3_stmt_isexplain(S) interface returns 0 if S is +** an ordinary statement or a NULL pointer. +*/ +SQLITE_API int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt); + /* ** CAPI3REF: Determine If A Prepared Statement Has Been Reset ** METHOD: sqlite3_stmt @@ -4004,7 +4060,9 @@ typedef struct sqlite3_context sqlite3_context; ** ^The fifth argument to the BLOB and string binding interfaces ** is a destructor used to dispose of the BLOB or ** string after SQLite has finished with it. ^The destructor is called -** to dispose of the BLOB or string even if the call to bind API fails. +** to dispose of the BLOB or string even if the call to the bind API fails, +** except the destructor is not called if the third parameter is a NULL +** pointer or the fourth parameter is negative. ** ^If the fifth argument is ** the special value [SQLITE_STATIC], then SQLite assumes that the ** information is in static, unmanaged space and does not need to be freed. @@ -4921,6 +4979,8 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6 ** sqlite3_value_nochange   ** →  True if the column is unchanged in an UPDATE ** against a virtual table. +** sqlite3_value_frombind   +** →  True if value originated from a [bound parameter] ** ** ** Details: @@ -4982,6 +5042,11 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6 ** than within an [xUpdate] method call for an UPDATE statement, then ** the return value is arbitrary and meaningless. ** +** ^The sqlite3_value_frombind(X) interface returns non-zero if the +** value X originated from one of the [sqlite3_bind_int|sqlite3_bind()] +** interfaces. ^If X comes from an SQL literal value, or a table column, +** and expression, then sqlite3_value_frombind(X) returns zero. +** ** Please pay particular attention to the fact that the pointer returned ** from [sqlite3_value_blob()], [sqlite3_value_text()], or ** [sqlite3_value_text16()] can be invalidated by a subsequent call to @@ -5027,6 +5092,7 @@ SQLITE_API int sqlite3_value_bytes16(sqlite3_value*); SQLITE_API int sqlite3_value_type(sqlite3_value*); SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); SQLITE_API int sqlite3_value_nochange(sqlite3_value*); +SQLITE_API int sqlite3_value_frombind(sqlite3_value*); /* ** CAPI3REF: Finding The Subtype Of SQL Values @@ -5762,7 +5828,7 @@ SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); ** associated with database N of connection D. ^The main database file ** has the name "main". If there is no attached database N on the database ** connection D, or if database N is a temporary or in-memory database, then -** a NULL pointer is returned. +** this function will return either a NULL pointer or an empty string. ** ** ^The filename returned by this function is the output of the ** xFullPathname method of the [VFS]. ^In other words, the filename @@ -9996,7 +10062,7 @@ SQLITE_API int sqlite3changeset_next(sqlite3_changeset_iter *pIter); ** sqlite3changeset_next() is called on the iterator or until the ** conflict-handler function returns. If pnCol is not NULL, then *pnCol is ** set to the number of columns in the table affected by the change. If -** pbIncorrect is not NULL, then *pbIndirect is set to true (1) if the change +** pbIndirect is not NULL, then *pbIndirect is set to true (1) if the change ** is an indirect change, or false (0) otherwise. See the documentation for ** [sqlite3session_indirect()] for a description of direct and indirect ** changes. Finally, if pOp is not NULL, then *pOp is set to one of @@ -10863,7 +10929,7 @@ SQLITE_API int sqlite3rebaser_configure( ** in size. This function allocates and populates a buffer with a copy ** of the changeset rebased rebased according to the configuration of the ** rebaser object passed as the first argument. If successful, (*ppOut) -** is set to point to the new buffer containing the rebased changset and +** is set to point to the new buffer containing the rebased changeset and ** (*pnOut) to its size in bytes and SQLITE_OK returned. It is the ** responsibility of the caller to eventually free the new buffer using ** sqlite3_free(). Otherwise, if an error occurs, (*ppOut) and (*pnOut) @@ -11230,12 +11296,8 @@ struct Fts5PhraseIter { ** ** Usually, output parameter *piPhrase is set to the phrase number, *piCol ** to the column in which it occurs and *piOff the token offset of the -** first token of the phrase. The exception is if the table was created -** with the offsets=0 option specified. In this case *piOff is always -** set to -1. -** -** Returns SQLITE_OK if successful, or an error code (i.e. SQLITE_NOMEM) -** if an error occurs. +** first token of the phrase. Returns SQLITE_OK if successful, or an error +** code (i.e. SQLITE_NOMEM) if an error occurs. ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. @@ -11276,7 +11338,7 @@ struct Fts5PhraseIter { ** Save the pointer passed as the second argument as the extension functions ** "auxiliary data". The pointer may then be retrieved by the current or any ** future invocation of the same fts5 extension function made as part of -** of the same MATCH query using the xGetAuxdata() API. +** the same MATCH query using the xGetAuxdata() API. ** ** Each extension function is allocated a single auxiliary data slot for ** each FTS query (MATCH expression). If the extension function is invoked @@ -11291,7 +11353,7 @@ struct Fts5PhraseIter { ** The xDelete callback, if one is specified, is also invoked on the ** auxiliary data pointer after the FTS5 query has finished. ** -** If an error (e.g. an OOM condition) occurs within this function, an +** If an error (e.g. an OOM condition) occurs within this function, ** the auxiliary data is set to NULL and an error code returned. If the ** xDelete parameter was not NULL, it is invoked on the auxiliary data ** pointer before returning. @@ -11524,11 +11586,11 @@ struct Fts5ExtensionApi { ** the tokenizer substitutes "first" for "1st" and the query works ** as expected. ** -**
  • By adding multiple synonyms for a single term to the FTS index. -** In this case, when tokenizing query text, the tokenizer may -** provide multiple synonyms for a single term within the document. -** FTS5 then queries the index for each synonym individually. For -** example, faced with the query: +**
  • By querying the index for all synonyms of each query term +** separately. In this case, when tokenizing query text, the +** tokenizer may provide multiple synonyms for a single term +** within the document. FTS5 then queries the index for each +** synonym individually. For example, faced with the query: ** ** ** ... MATCH 'first place' @@ -11552,7 +11614,7 @@ struct Fts5ExtensionApi { ** "place". ** ** This way, even if the tokenizer does not provide synonyms -** when tokenizing query text (it should not - to do would be +** when tokenizing query text (it should not - to do so would be ** inefficient), it doesn't matter if the user queries for ** 'first + place' or '1st + place', as there are entries in the ** FTS index corresponding to both forms of the first token. -- cgit v1.2.3 From 93642992ae779f3d8c864e5680ff2dfdfa9d0331 Mon Sep 17 00:00:00 2001 From: Paul Wicking Date: Tue, 11 Jun 2019 12:07:55 +0200 Subject: Doc: Correct documentation of QFuture::cancel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: QTBUG-76305 Change-Id: I192a7f0bc2c15e532bc6d51c7e9c39561ae3436c Reviewed-by: Topi Reiniö Reviewed-by: Jędrzej Nowacki --- src/concurrent/qtconcurrentrun.cpp | 16 ++++++++-------- src/corelib/thread/qfuture.qdoc | 8 ++++---- src/corelib/thread/qfuturewatcher.cpp | 10 +++++----- 3 files changed, 17 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/concurrent/qtconcurrentrun.cpp b/src/concurrent/qtconcurrentrun.cpp index bbd1f5ec62..d9867e1f1a 100644 --- a/src/concurrent/qtconcurrentrun.cpp +++ b/src/concurrent/qtconcurrentrun.cpp @@ -138,10 +138,10 @@ T is the same type as the return value of \a function. Non-void return values can be accessed via the QFuture::result() function. - Note that the QFuture returned by QtConcurrent::run() does not support - canceling, pausing, or progress reporting. The QFuture returned can only - be used to query for the running/finished status and the return value of - the function. + \note The QFuture returned can only be used to query for the + running/finished status and the return value of the function. In particular, + canceling or pausing can be issued only if the computations behind the future + has not been started. \sa {Concurrent Run} */ @@ -157,10 +157,10 @@ T is the same type as the return value of \a function. Non-void return values can be accessed via the QFuture::result() function. - Note that the QFuture returned by QtConcurrent::run() does not support - canceling, pausing, or progress reporting. The QFuture returned can only - be used to query for the running/finished status and the return value of - the function. + \note The QFuture returned can only be used to query for the + running/finished status and the return value of the function. In particular, + canceling or pausing can be issued only if the computations behind the future + has not been started. \sa {Concurrent Run} */ diff --git a/src/corelib/thread/qfuture.qdoc b/src/corelib/thread/qfuture.qdoc index 7db65dacd3..076725e19c 100644 --- a/src/corelib/thread/qfuture.qdoc +++ b/src/corelib/thread/qfuture.qdoc @@ -55,8 +55,8 @@ instance, the computation can be canceled with the cancel() function. To pause the computation, use the setPaused() function or one of the pause(), resume(), or togglePaused() convenience functions. Be aware that not all - asynchronous computations can be canceled or paused. For example, the - future returned by QtConcurrent::run() cannot be canceled; but the + running asynchronous computations can be canceled or paused. For example, + the future returned by QtConcurrent::run() cannot be canceled; but the future returned by QtConcurrent::mappedReduced() can. Progress information is provided by the progressValue(), @@ -133,8 +133,8 @@ Any QFutureWatcher object that is watching this future will not deliver progress and result ready signals on a canceled future. - Be aware that not all asynchronous computations can be canceled. For - example, the future returned by QtConcurrent::run() cannot be canceled; + Be aware that not all running asynchronous computations can be canceled. + For example, the future returned by QtConcurrent::run() cannot be canceled; but the future returned by QtConcurrent::mappedReduced() can. */ diff --git a/src/corelib/thread/qfuturewatcher.cpp b/src/corelib/thread/qfuturewatcher.cpp index faeb6b3a28..8e90d043ef 100644 --- a/src/corelib/thread/qfuturewatcher.cpp +++ b/src/corelib/thread/qfuturewatcher.cpp @@ -84,8 +84,8 @@ QT_BEGIN_NAMESPACE \snippet code/src_corelib_thread_qfuturewatcher.cpp 0 - Be aware that not all asynchronous computations can be canceled or paused. - For example, the future returned by QtConcurrent::run() cannot be + Be aware that not all running asynchronous computations can be canceled or + paused. For example, the future returned by QtConcurrent::run() cannot be canceled; but the future returned by QtConcurrent::mappedReduced() can. QFutureWatcher is specialized to not contain any of the result @@ -124,9 +124,9 @@ QFutureWatcherBase::QFutureWatcherBase(QObject *parent) progressRangeChanged(), progressTextChanged(), resultReadyAt(), and resultsReadyAt() signals. - Be aware that not all asynchronous computations can be canceled. For - example, the QFuture returned by QtConcurrent::run() cannot be canceled; - but the QFuture returned by QtConcurrent::mappedReduced() can. + Be aware that not all running asynchronous computations can be canceled. + For example, the QFuture returned by QtConcurrent::run() cannot be + canceled; but the QFuture returned by QtConcurrent::mappedReduced() can. */ void QFutureWatcherBase::cancel() { -- cgit v1.2.3 From 9b6928b7cc6f12638ae625c67ecf437cfc694498 Mon Sep 17 00:00:00 2001 From: Val Doroshchuk Date: Fri, 31 May 2019 14:15:53 +0200 Subject: Fix crash when app is going to shutdown but conf manager is requested MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the app is finished and going to shutdown, qNetworkConfigurationManagerPrivate() returns nullptr. Change-Id: I01915021d8698802b3a1d0dee43203cd3d4aba74 Task-number: QTBUG-76090 Reviewed-by: Mårten Nordheim --- src/network/bearer/qnetworkconfigmanager.cpp | 27 ++++++++++++++------------- src/network/bearer/qnetworksession.cpp | 3 ++- src/plugins/bearer/qnetworksession_impl.cpp | 13 +++++++------ 3 files changed, 23 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/network/bearer/qnetworkconfigmanager.cpp b/src/network/bearer/qnetworkconfigmanager.cpp index 81b5e01d6a..cd87c3669c 100644 --- a/src/network/bearer/qnetworkconfigmanager.cpp +++ b/src/network/bearer/qnetworkconfigmanager.cpp @@ -233,19 +233,20 @@ QNetworkConfigurationManager::QNetworkConfigurationManager(QObject *parent) : QObject(parent) { QNetworkConfigurationManagerPrivate *priv = qNetworkConfigurationManagerPrivate(); - - connect(priv, SIGNAL(configurationAdded(QNetworkConfiguration)), - this, SIGNAL(configurationAdded(QNetworkConfiguration))); - connect(priv, SIGNAL(configurationRemoved(QNetworkConfiguration)), - this, SIGNAL(configurationRemoved(QNetworkConfiguration))); - connect(priv, SIGNAL(configurationChanged(QNetworkConfiguration)), - this, SIGNAL(configurationChanged(QNetworkConfiguration))); - connect(priv, SIGNAL(onlineStateChanged(bool)), - this, SIGNAL(onlineStateChanged(bool))); - connect(priv, SIGNAL(configurationUpdateComplete()), - this, SIGNAL(updateCompleted())); - - priv->enablePolling(); + if (priv) { + connect(priv, SIGNAL(configurationAdded(QNetworkConfiguration)), + this, SIGNAL(configurationAdded(QNetworkConfiguration))); + connect(priv, SIGNAL(configurationRemoved(QNetworkConfiguration)), + this, SIGNAL(configurationRemoved(QNetworkConfiguration))); + connect(priv, SIGNAL(configurationChanged(QNetworkConfiguration)), + this, SIGNAL(configurationChanged(QNetworkConfiguration))); + connect(priv, SIGNAL(onlineStateChanged(bool)), + this, SIGNAL(onlineStateChanged(bool))); + connect(priv, SIGNAL(configurationUpdateComplete()), + this, SIGNAL(updateCompleted())); + + priv->enablePolling(); + } } /*! diff --git a/src/network/bearer/qnetworksession.cpp b/src/network/bearer/qnetworksession.cpp index 471d322998..1636bcee97 100644 --- a/src/network/bearer/qnetworksession.cpp +++ b/src/network/bearer/qnetworksession.cpp @@ -258,7 +258,8 @@ QNetworkSession::QNetworkSession(const QNetworkConfiguration &connectionConfig, // invalid configuration if (!connectionConfig.identifier().isEmpty()) { - const auto engines = qNetworkConfigurationManagerPrivate()->engines(); + auto priv = qNetworkConfigurationManagerPrivate(); + const auto engines = priv ? priv->engines() : QList(); for (QBearerEngine *engine : engines) { if (engine->hasIdentifier(connectionConfig.identifier())) { d = engine->createSessionBackend(); diff --git a/src/plugins/bearer/qnetworksession_impl.cpp b/src/plugins/bearer/qnetworksession_impl.cpp index 847479047f..903525a204 100644 --- a/src/plugins/bearer/qnetworksession_impl.cpp +++ b/src/plugins/bearer/qnetworksession_impl.cpp @@ -56,12 +56,13 @@ QT_BEGIN_NAMESPACE static QBearerEngineImpl *getEngineFromId(const QString &id) { QNetworkConfigurationManagerPrivate *priv = qNetworkConfigurationManagerPrivate(); - - const auto engines = priv->engines(); - for (QBearerEngine *engine : engines) { - QBearerEngineImpl *engineImpl = qobject_cast(engine); - if (engineImpl && engineImpl->hasIdentifier(id)) - return engineImpl; + if (priv) { + const auto engines = priv->engines(); + for (QBearerEngine *engine : engines) { + QBearerEngineImpl *engineImpl = qobject_cast(engine); + if (engineImpl && engineImpl->hasIdentifier(id)) + return engineImpl; + } } return 0; -- cgit v1.2.3 From d8efc8d718e3b3a0464f321e740541f5b221a5d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Nordheim?= Date: Thu, 13 Dec 2018 15:39:26 +0100 Subject: QSslSocket: add and set the TLSv1.3-specific PSK callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If this callback is not set then OpenSSL will call the callback used for <= TLS 1.2 unconditionally when connecting. If using PSK it will call it again later once the preshared key is needed. We don't currently handle the TLSv1.3 PSK, but we definitely should. But for now we can work around it - when psk_use_session_callback is called we simply change the PSK callback to a dummy function whose only purpose is to restore the old callback. This is mostly done to keep behavior the same as it is now for users (and to keep our tests running). Later we can add a new signal and handle this new feature properly. Task-number: QTBUG-67463 Change-Id: I4aca4ae73ec4be7c4f82a85e8864de103f35a834 Reviewed-by: Simo Fält --- src/network/ssl/qsslsocket_openssl.cpp | 56 ++++++++++++++++++++++++ src/network/ssl/qsslsocket_openssl11_symbols_p.h | 6 +++ src/network/ssl/qsslsocket_openssl_symbols.cpp | 2 + 3 files changed, 64 insertions(+) (limited to 'src') diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index b5b098502d..c8bc6e069a 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -136,6 +136,55 @@ static unsigned int q_ssl_psk_server_callback(SSL *ssl, Q_ASSERT(d); return d->tlsPskServerCallback(identity, psk, max_psk_len); } + +#ifdef TLS1_3_VERSION +#ifndef OPENSSL_NO_PSK +static unsigned int q_ssl_psk_restore_client(SSL *ssl, + const char *hint, + char *identity, unsigned int max_identity_len, + unsigned char *psk, unsigned int max_psk_len) +{ + Q_UNUSED(hint); + Q_UNUSED(identity); + Q_UNUSED(max_identity_len); + Q_UNUSED(psk); + Q_UNUSED(max_psk_len); + +#ifdef QT_DEBUG + QSslSocketBackendPrivate *d = reinterpret_cast(q_SSL_get_ex_data(ssl, QSslSocketBackendPrivate::s_indexForSSLExtraData)); + Q_ASSERT(d); + Q_ASSERT(d->mode == QSslSocket::SslClientMode); +#endif + q_SSL_set_psk_client_callback(ssl, &q_ssl_psk_client_callback); + + return 0; +} +#endif // !OPENSSL_NO_PSK + +static int q_ssl_psk_use_session_callback(SSL *ssl, const EVP_MD *md, const unsigned char **id, + size_t *idlen, SSL_SESSION **sess) +{ + Q_UNUSED(ssl); + Q_UNUSED(md); + Q_UNUSED(id); + Q_UNUSED(idlen); + Q_UNUSED(sess); + +#ifndef OPENSSL_NO_PSK +#ifdef QT_DEBUG + QSslSocketBackendPrivate *d = reinterpret_cast(q_SSL_get_ex_data(ssl, QSslSocketBackendPrivate::s_indexForSSLExtraData)); + Q_ASSERT(d); + Q_ASSERT(d->mode == QSslSocket::SslClientMode); +#endif + + // Temporarily rebind the psk because it will be called next. The function will restore it. + q_SSL_set_psk_client_callback(ssl, &q_ssl_psk_restore_client); +#endif + + return 1; // need to return 1 or else "the connection setup fails." +} +#endif // TLS1_3_VERSION + #endif } // extern "C" @@ -411,6 +460,13 @@ bool QSslSocketBackendPrivate::initSslContext() q_SSL_set_psk_server_callback(ssl, &q_ssl_psk_server_callback); } #endif +#if OPENSSL_VERSION_NUMBER >= 0x10101006L + // Set the client callback for TLSv1.3 PSK + if (mode == QSslSocket::SslClientMode + && QSslSocket::sslLibraryBuildVersionNumber() >= 0x10101006L) { + q_SSL_set_psk_use_session_callback(ssl, &q_ssl_psk_use_session_callback); + } +#endif // openssl version >= 0x10101006L return true; } diff --git a/src/network/ssl/qsslsocket_openssl11_symbols_p.h b/src/network/ssl/qsslsocket_openssl11_symbols_p.h index fae007e12d..ec7e7ea1b8 100644 --- a/src/network/ssl/qsslsocket_openssl11_symbols_p.h +++ b/src/network/ssl/qsslsocket_openssl11_symbols_p.h @@ -178,4 +178,10 @@ void q_BIO_set_shutdown(BIO *a, int shut); #define q_SSL_CTX_set_max_proto_version(ctx, version) \ q_SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MAX_PROTO_VERSION, version, nullptr) +extern "C" { +typedef int (*q_SSL_psk_use_session_cb_func_t)(SSL *, const EVP_MD *, const unsigned char **, size_t *, + SSL_SESSION **); +} +void q_SSL_set_psk_use_session_callback(SSL *s, q_SSL_psk_use_session_cb_func_t); + #endif diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp index 299df6b685..6a5ab91669 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols.cpp +++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp @@ -163,6 +163,7 @@ DEFINEFUNC(int, SSL_session_reused, SSL *a, a, return 0, return) DEFINEFUNC2(unsigned long, SSL_CTX_set_options, SSL_CTX *ctx, ctx, unsigned long op, op, return 0, return) #ifdef TLS1_3_VERSION DEFINEFUNC2(int, SSL_CTX_set_ciphersuites, SSL_CTX *ctx, ctx, const char *str, str, return 0, return) +DEFINEFUNC2(void, SSL_set_psk_use_session_callback, SSL *ssl, ssl, q_SSL_psk_use_session_cb_func_t callback, callback, return, DUMMYARG) #endif DEFINEFUNC3(size_t, SSL_get_client_random, SSL *a, a, unsigned char *out, out, size_t outlen, outlen, return 0, return) DEFINEFUNC3(size_t, SSL_SESSION_get_master_key, const SSL_SESSION *ses, ses, unsigned char *out, out, size_t outlen, outlen, return 0, return) @@ -980,6 +981,7 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(SSL_CTX_set_options) #ifdef TLS1_3_VERSION RESOLVEFUNC(SSL_CTX_set_ciphersuites) + RESOLVEFUNC(SSL_set_psk_use_session_callback) #endif // TLS 1.3 or OpenSSL > 1.1.1 RESOLVEFUNC(SSL_get_client_random) RESOLVEFUNC(SSL_SESSION_get_master_key) -- cgit v1.2.3 From 09a2a9bc4a4a5c9ef7849d3f30243cd8e2d389d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Mon, 18 Mar 2019 13:37:35 +0100 Subject: Cocoa: always send queued user input events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User input events will be queued if processEvents() is called with the ExcludeUserInputEvents flag. User code then expect that the queued events will be sent when the corresponding exec() call returns. We were sending queued user input event at the beginning of processEvents(). However, the cocoa event dispatcher also has a mode where it makes a blocking call to [NSApp run], in which case processEvents() never returns during event processing. This means we don’t get to call the queued-event-sending code. Factor out the queued-event-sending code to a new sendQueuedUserInputEvents() function. Call it from postedEventsSourceCallback() to make sure the queue is emptied after the ExcludeUserInputEvents processEvents() call is done. Task-number: QTBUG-69687 Change-Id: I4ff554ef4d39a69356736c33a650886b56bfdb4c Reviewed-by: Timur Pocheptsov Reviewed-by: Richard Moe Gustavsen --- .../platforms/cocoa/qcocoaeventdispatcher.h | 1 + .../platforms/cocoa/qcocoaeventdispatcher.mm | 31 +++++++++++++++------- 2 files changed, 22 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h index 9771cd0289..69587a24be 100644 --- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h +++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h @@ -191,6 +191,7 @@ public: static void waitingObserverCallback(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info); static void firstLoopEntry(CFRunLoopObserverRef ref, CFRunLoopActivity activity, void *info); + bool sendQueuedUserInputEvents(); void processPostedEvents(); }; diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm index 84ffadea83..d3bb0711f0 100644 --- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm +++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm @@ -377,16 +377,9 @@ bool QCocoaEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags) NSEvent* event = nil; // First, send all previously excluded input events, if any: - if (!excludeUserEvents) { - while (!d->queuedUserInputEvents.isEmpty()) { - event = static_cast(d->queuedUserInputEvents.takeFirst()); - if (!filterNativeEvent("NSEvent", event, nullptr)) { - [NSApp sendEvent:event]; - retVal = true; - } - [event release]; - } - } + if (d->sendQueuedUserInputEvents()) + retVal = true; + // If Qt is used as a plugin, or as an extension in a native cocoa // application, we should not run or stop NSApplication; This will be @@ -843,6 +836,23 @@ void QCocoaEventDispatcherPrivate::waitingObserverCallback(CFRunLoopObserverRef, emit static_cast(info)->awake(); } +bool QCocoaEventDispatcherPrivate::sendQueuedUserInputEvents() +{ + Q_Q(QCocoaEventDispatcher); + if (processEventsFlags & QEventLoop::ExcludeUserInputEvents) + return false; + bool didSendEvent = false; + while (!queuedUserInputEvents.isEmpty()) { + NSEvent *event = static_cast(queuedUserInputEvents.takeFirst()); + if (!q->filterNativeEvent("NSEvent", event, nullptr)) { + [NSApp sendEvent:event]; + didSendEvent = true; + } + [event release]; + } + return didSendEvent; +} + void QCocoaEventDispatcherPrivate::processPostedEvents() { if (blockSendPostedEvents) { @@ -896,6 +906,7 @@ void QCocoaEventDispatcherPrivate::postedEventsSourceCallback(void *info) d->maybeCancelWaitForMoreEvents(); return; } + d->sendQueuedUserInputEvents(); d->processPostedEvents(); d->maybeCancelWaitForMoreEvents(); } -- cgit v1.2.3 From 35ebb614bd2b3dd279cd96fd6439543ca00b03eb Mon Sep 17 00:00:00 2001 From: Topi Reinio Date: Fri, 7 Jun 2019 20:35:22 +0200 Subject: Doc: Fix typos in QDateTime docs Change-Id: Ibff4555cbd1e980333acd88c697021b4a74998a8 Reviewed-by: Marc Mutz Reviewed-by: Paul Wicking Reviewed-by: Edward Welbourne --- src/corelib/tools/qdatetime.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/corelib/tools/qdatetime.cpp b/src/corelib/tools/qdatetime.cpp index b0e443c3dc..9b0ed18742 100644 --- a/src/corelib/tools/qdatetime.cpp +++ b/src/corelib/tools/qdatetime.cpp @@ -1900,7 +1900,7 @@ int QTime::msecsTo(const QTime &t) const 24 hours each time midnight passes; and, beside this, changes in it may not correspond to elapsed time, if a daylight-saving transition intervenes. - \sa QDateTime::currentDateTime(), QDateTime::curentDateTimeUtc() + \sa QDateTime::currentDateTime(), QDateTime::currentDateTimeUtc() */ #if QT_CONFIG(datestring) @@ -3030,7 +3030,7 @@ inline qint64 QDateTimePrivate::zoneMSecsToEpochMSecs(qint64 zoneMSecs, const QT datetime by adding a number of seconds, days, months, or years. QDateTime can describe datetimes with respect to \l{Qt::LocalTime}{local - time}, to \l{Qt::UTC}{UTC}, to a specified \l{{Qt::OffsetFromUTC}{offset + time}, to \l{Qt::UTC}{UTC}, to a specified \l{Qt::OffsetFromUTC}{offset from UTC} or to a specified \l{{Qt::TimeZone}{time zone}, in conjunction with the QTimeZone class. For example, a time zone of "Europe/Berlin" will apply the daylight-saving rules as used in Germany since 1970. In contrast, -- cgit v1.2.3 From 89a25a3ef169cb42c54ef1ddcb28f70b24f923ab Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Sun, 9 Jun 2019 20:01:36 +0200 Subject: Don't count all engines as "in use" in cache Back in 36cb3f3f655a9090c82de609010cbfb88651a0f3, we started properly reference counting font engines as they were entered into the font cache. Prior to this, ref.load == 0 would mean that the engine was essentially owned by the cache. When the change was made, the condition that an engine must be in use if its reference is != 0 remained, and the result of this was used to calculate the limit for when the cache should be flushed. Since this limit was miscalculated, the cache would keep growing, even if it only contained unused font engines. [ChangeLog][QtGui][Text] Fixed a bug which could cause the font cache to grow larger than it was supposed to. Task-number: QTBUG-76219 Change-Id: I4d1541756f3bdf5bd9b0301bf47c6db2e220716a Reviewed-by: Konstantin Ritt --- src/gui/text/qfont.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp index 69255fd59d..fe4fa4929a 100644 --- a/src/gui/text/qfont.cpp +++ b/src/gui/text/qfont.cpp @@ -2970,7 +2970,7 @@ void QFontCache::decreaseCache() it.value().data->ref.load(), engineCacheCount.value(it.value().data), it.value().data->cache_cost); - if (it.value().data->ref.load() != 0) + if (it.value().data->ref.load() > engineCacheCount.value(it.value().data)) in_use_cost += it.value().data->cache_cost / engineCacheCount.value(it.value().data); } -- cgit v1.2.3 From 3af55691bc17b2473212343739736e7e1c275fc9 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Tue, 11 Jun 2019 15:20:49 +0200 Subject: Add neglected connect()s to Q{Date,Time}Edit constructors Pointed out by Daniel Teske. This amends commit c3e1abad4e141e6e9d876e5cff194c473a2654eb. Change-Id: Ia6c6f41bf28e846152f9f86322f20a1b99e57201 Reviewed-by: David Faure --- src/widgets/widgets/qdatetimeedit.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/widgets/widgets/qdatetimeedit.cpp b/src/widgets/widgets/qdatetimeedit.cpp index 41d9faa5c2..acab768d75 100644 --- a/src/widgets/widgets/qdatetimeedit.cpp +++ b/src/widgets/widgets/qdatetimeedit.cpp @@ -1530,7 +1530,7 @@ void QDateTimeEdit::mousePressEvent(QMouseEvent *event) QTimeEdit::QTimeEdit(QWidget *parent) : QDateTimeEdit(QDATETIMEEDIT_TIME_MIN, QVariant::Time, parent) { - connect(this, SIGNAL(timeChanged(QTime)), SIGNAL(userTimeChanged(QTime))); + connect(this, &QTimeEdit::timeChanged, this, &QTimeEdit::userTimeChanged); } /*! @@ -1541,6 +1541,7 @@ QTimeEdit::QTimeEdit(QWidget *parent) QTimeEdit::QTimeEdit(const QTime &time, QWidget *parent) : QDateTimeEdit(time, QVariant::Time, parent) { + connect(this, &QTimeEdit::timeChanged, this, &QTimeEdit::userTimeChanged); } /*! @@ -1599,7 +1600,7 @@ QTimeEdit::~QTimeEdit() QDateEdit::QDateEdit(QWidget *parent) : QDateTimeEdit(QDATETIMEEDIT_DATE_INITIAL, QVariant::Date, parent) { - connect(this, SIGNAL(dateChanged(QDate)), SIGNAL(userDateChanged(QDate))); + connect(this, &QDateEdit::dateChanged, this, &QDateEdit::userDateChanged); } /*! @@ -1610,6 +1611,7 @@ QDateEdit::QDateEdit(QWidget *parent) QDateEdit::QDateEdit(const QDate &date, QWidget *parent) : QDateTimeEdit(date, QVariant::Date, parent) { + connect(this, &QDateEdit::dateChanged, this, &QDateEdit::userDateChanged); } /*! -- cgit v1.2.3 From 2e064637125b1604f7fcd4e6528c43a43487e471 Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Wed, 10 Apr 2019 15:47:42 +0200 Subject: Windows: Call ReleaseDC for the private's displayContext Change-Id: Ib5ee1bbe9037ceb13562eadb754c2a5f095b7f87 Reviewed-by: Friedemann Kleint --- src/plugins/platforms/windows/qwindowscontext.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index 76747b7956..de533cab08 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -317,6 +317,8 @@ QWindowsContext::~QWindowsContext() OleUninitialize(); d->m_screenManager.clearScreens(); // Order: Potentially calls back to the windows. + if (d->m_displayContext) + ReleaseDC(nullptr, d->m_displayContext); m_instance = nullptr; } -- cgit v1.2.3 From 0e2c013a4523915a0af37fe1f338a4f66b07f91d Mon Sep 17 00:00:00 2001 From: Martin Smith Date: Tue, 7 May 2019 10:56:08 +0200 Subject: doc: Add dontdocument.qdoc files Each module that has publically declared classes or structs that are not meant to be documented is given a dontdocument.qdoc file to tell qdoc that these classes are not meant to be documentented. Then qdoc will not print warnings about missing \class comments for these classes and structs. Change-Id: I9195f0b546032e1c7642c9da34d85a0a4a9bfb08 Reviewed-by: Paul Wicking --- src/corelib/doc/src/dontdocument.qdoc | 41 +++++++++++++++++ src/dbus/doc/src/dontdocument.qdoc | 30 ++++++++++++ src/gui/doc/src/dontdocument.qdoc | 66 +++++++++++++++++++++++++++ src/network/doc/src/dontdocument.qdoc | 30 ++++++++++++ src/platformheaders/doc/src/dontdocument.qdoc | 30 ++++++++++++ src/printsupport/doc/src/dontdocument.qdoc | 30 ++++++++++++ src/sql/doc/src/dontdocument.qdoc | 30 ++++++++++++ src/testlib/doc/src/dontdocument.qdoc | 32 +++++++++++++ src/widgets/doc/src/dontdocument.qdoc | 30 ++++++++++++ src/xml/doc/src/dontdocument.qdoc | 30 ++++++++++++ 10 files changed, 349 insertions(+) create mode 100644 src/corelib/doc/src/dontdocument.qdoc create mode 100644 src/dbus/doc/src/dontdocument.qdoc create mode 100644 src/gui/doc/src/dontdocument.qdoc create mode 100644 src/network/doc/src/dontdocument.qdoc create mode 100644 src/platformheaders/doc/src/dontdocument.qdoc create mode 100644 src/printsupport/doc/src/dontdocument.qdoc create mode 100644 src/sql/doc/src/dontdocument.qdoc create mode 100644 src/testlib/doc/src/dontdocument.qdoc create mode 100644 src/widgets/doc/src/dontdocument.qdoc create mode 100644 src/xml/doc/src/dontdocument.qdoc (limited to 'src') diff --git a/src/corelib/doc/src/dontdocument.qdoc b/src/corelib/doc/src/dontdocument.qdoc new file mode 100644 index 0000000000..19ca7db299 --- /dev/null +++ b/src/corelib/doc/src/dontdocument.qdoc @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \dontdocument (QMacAutoReleasePool QIncompatibleFlag QGenericAtomicOps QAtomicTraits + QAtomicOps QBasicAtomicInteger QBasicAtomicPointer QBasicMutex QInternal + QArgument QReturnArgument QArrayData QTypedArrayData QStaticByteArrayData + QByteRef QStaticStringData QListSpecialMethods QListData QScopedPointerDeleter + QScopedPointerArrayDeleter QScopedPointerPodDeleter QScopedPointerObjectDeleteLater + QMetaTypeId2 QObjectData QObjectUserData QMapNodeBase QMapNode QMapDataBase + QMapData QHashData QHashNode QArrayDataPointer QTextStreamManipulator + QContiguousCacheData QContiguousCacheTypedData QNoDebug QUrlTwoFlags + QCborValueRef qfloat16 QDeferredDeleteEvent QSpecialInteger QLittleEndianStorageType + QBigEndianStorageType QFactoryInterface QFutureWatcherBase QJsonValuePtr + QJsonValueRefPtr QLinkedListNode QAbstractConcatenable QStringBuilderCommon + QTextCodec::ConverterState QThreadStorageData) +*/ diff --git a/src/dbus/doc/src/dontdocument.qdoc b/src/dbus/doc/src/dontdocument.qdoc new file mode 100644 index 0000000000..bbb8acb53c --- /dev/null +++ b/src/dbus/doc/src/dontdocument.qdoc @@ -0,0 +1,30 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \dontdocument (QTypeInfo QMetaTypeId QDBusAbstractInterfaceBase QDBusPendingReplyData QMetaTypeId2) +*/ diff --git a/src/gui/doc/src/dontdocument.qdoc b/src/gui/doc/src/dontdocument.qdoc new file mode 100644 index 0000000000..b360acefc1 --- /dev/null +++ b/src/gui/doc/src/dontdocument.qdoc @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \dontdocument (QTypeInfo QScreenOrientationChangeEvent QApplicationStateChangeEvent + QImageTextKeyLang QMetaTypeId QAbstractUndoItem + QOpenGLVersionStatus + QOpenGLVersionFunctionsBackend + QOpenGLVersionFunctionsStorage + QOpenGLFunctions_1_0_CoreBackend + QOpenGLFunctions_1_1_CoreBackend + QOpenGLFunctions_1_2_CoreBackend + QOpenGLFunctions_1_3_CoreBackend + QOpenGLFunctions_1_4_CoreBackend + QOpenGLFunctions_1_5_CoreBackend + QOpenGLFunctions_2_0_CoreBackend + QOpenGLFunctions_2_1_CoreBackend + QOpenGLFunctions_3_0_CoreBackend + QOpenGLFunctions_3_1_CoreBackend + QOpenGLFunctions_3_2_CoreBackend + QOpenGLFunctions_3_3_CoreBackend + QOpenGLFunctions_4_0_CoreBackend + QOpenGLFunctions_4_1_CoreBackend + QOpenGLFunctions_4_2_CoreBackend + QOpenGLFunctions_4_3_CoreBackend + QOpenGLFunctions_4_4_CoreBackend + QOpenGLFunctions_4_5_CoreBackend + QOpenGLFunctions_1_0_DeprecatedBackend + QOpenGLFunctions_1_1_DeprecatedBackend + QOpenGLFunctions_1_2_DeprecatedBackend + QOpenGLFunctions_1_3_DeprecatedBackend + QOpenGLFunctions_1_4_DeprecatedBackend + QOpenGLFunctions_2_0_DeprecatedBackend + QOpenGLFunctions_3_0_DeprecatedBackend + QOpenGLFunctions_3_3_DeprecatedBackend + QOpenGLFunctions_4_5_DeprecatedBackend + QTextFrameLayoutData QPlatformDropQtResponse QPlatformDragQtResponse + QPlatformOffscreenSurface QColorDialogOptions QFontDialogOptions + QFileDialogOptions QMessageDialogOptions QMessageDialogOptions::CustomButton + QPlatformSessionManager QPlatformIntegrationPlugin QPlatformMenuItem + QPlatformMenu QPlatformMenuBar QPlatformTextureList) +*/ diff --git a/src/network/doc/src/dontdocument.qdoc b/src/network/doc/src/dontdocument.qdoc new file mode 100644 index 0000000000..fe2e54b34c --- /dev/null +++ b/src/network/doc/src/dontdocument.qdoc @@ -0,0 +1,30 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \dontdocument (QTypeInfo QMetaTypeId QIPv6Address) +*/ diff --git a/src/platformheaders/doc/src/dontdocument.qdoc b/src/platformheaders/doc/src/dontdocument.qdoc new file mode 100644 index 0000000000..dc02c6473e --- /dev/null +++ b/src/platformheaders/doc/src/dontdocument.qdoc @@ -0,0 +1,30 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \dontdocument (QMetaTypeId) +*/ diff --git a/src/printsupport/doc/src/dontdocument.qdoc b/src/printsupport/doc/src/dontdocument.qdoc new file mode 100644 index 0000000000..74552b4e99 --- /dev/null +++ b/src/printsupport/doc/src/dontdocument.qdoc @@ -0,0 +1,30 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \dontdocument (QMetaTypeId QPlatformPrintDevice QPlatformPrinterSupportPlugin) +*/ diff --git a/src/sql/doc/src/dontdocument.qdoc b/src/sql/doc/src/dontdocument.qdoc new file mode 100644 index 0000000000..0193ace4b4 --- /dev/null +++ b/src/sql/doc/src/dontdocument.qdoc @@ -0,0 +1,30 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \dontdocument (QTypeInfo) +*/ diff --git a/src/testlib/doc/src/dontdocument.qdoc b/src/testlib/doc/src/dontdocument.qdoc new file mode 100644 index 0000000000..59270c7a4f --- /dev/null +++ b/src/testlib/doc/src/dontdocument.qdoc @@ -0,0 +1,32 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \dontdocument (QTestEventLoop QTestData QEventSizeOfChecker QSpontaneKeyEvent + QTestEvent QTestKeyEvent QTestKeyClicksEvent QTestMouseEvent + QTestDelayEvent QMetaTypeId) +*/ diff --git a/src/widgets/doc/src/dontdocument.qdoc b/src/widgets/doc/src/dontdocument.qdoc new file mode 100644 index 0000000000..9de8b5e62d --- /dev/null +++ b/src/widgets/doc/src/dontdocument.qdoc @@ -0,0 +1,30 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \dontdocument (QTypeInfo QMetaTypeId) +*/ diff --git a/src/xml/doc/src/dontdocument.qdoc b/src/xml/doc/src/dontdocument.qdoc new file mode 100644 index 0000000000..0193ace4b4 --- /dev/null +++ b/src/xml/doc/src/dontdocument.qdoc @@ -0,0 +1,30 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \dontdocument (QTypeInfo) +*/ -- cgit v1.2.3 From cc4c0b43a54d9606f491c193df381a424ae696bf Mon Sep 17 00:00:00 2001 From: Ryan Chu Date: Fri, 3 May 2019 16:14:19 +0200 Subject: QDataStream: Fix inconsistent results of iostream with QPalette objects The value of NColorRoles got changed since 5.11. It introduced one more role called "PlaceholderText" in the ColorRole enumeration. When using QDataStream (5.12) to read QPalette objects from a file written by 5.9 (<5.11), the processing results are inconsistent. Fixes: QTBUG-74885 Change-Id: I14d57f9603a26e5890b4fd57c7e464c5b38eb3f2 Reviewed-by: Eirik Aavitsland --- src/gui/kernel/qpalette.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/gui/kernel/qpalette.cpp b/src/gui/kernel/qpalette.cpp index 9ccfb9b819..c6805dd4e6 100644 --- a/src/gui/kernel/qpalette.cpp +++ b/src/gui/kernel/qpalette.cpp @@ -1006,6 +1006,8 @@ QDataStream &operator<<(QDataStream &s, const QPalette &p) max = QPalette::HighlightedText + 1; else if (s.version() <= QDataStream::Qt_4_3) max = QPalette::AlternateBase + 1; + else if (s.version() <= QDataStream::Qt_5_11) + max = QPalette::ToolTipText + 1; for (int r = 0; r < max; r++) s << p.d->br[grp][r]; } @@ -1046,6 +1048,9 @@ QDataStream &operator>>(QDataStream &s, QPalette &p) } else if (s.version() <= QDataStream::Qt_4_3) { p = QPalette(); max = QPalette::AlternateBase + 1; + } else if (s.version() <= QDataStream::Qt_5_11) { + p = QPalette(); + max = QPalette::ToolTipText + 1; } QBrush tmp; -- cgit v1.2.3 From ebddd02896709d5856dc73f1a4f1479133c7ed2f Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Fri, 24 May 2019 14:32:51 +0200 Subject: QMacStyle - fix the separator's color in the Dark theme MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's too dark as it is now. Since there is no similar UI element in AppKit, we take the same color as the toolbar's handle has (looks OK-eysh with 'Dark' and there is no reason to have the separator with a color different from the bar handle, both elements essentialy are lines of dots). Fixes: QTBUG-72759 Change-Id: I28277f80174a1c4c0af17961aba8ed6135aa3189 Reviewed-by: Morten Johan Sørvig --- src/plugins/styles/mac/qmacstyle_mac.mm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/plugins/styles/mac/qmacstyle_mac.mm b/src/plugins/styles/mac/qmacstyle_mac.mm index cf7cf18c17..ee5556ec9e 100644 --- a/src/plugins/styles/mac/qmacstyle_mac.mm +++ b/src/plugins/styles/mac/qmacstyle_mac.mm @@ -3152,7 +3152,9 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai theStroker.setCapStyle(Qt::FlatCap); theStroker.setDashPattern(QVector() << 1 << 2); path = theStroker.createStroke(path); - p->fillPath(path, QColor(0, 0, 0, 119)); + const auto dark = qt_mac_applicationIsInDarkMode() ? opt->palette.dark().color().darker() + : QColor(0, 0, 0, 119); + p->fillPath(path, dark); } break; case PE_FrameWindow: -- cgit v1.2.3 From c04bd30de072793faee5166cff866a4c4e0a9dd7 Mon Sep 17 00:00:00 2001 From: Eirik Aavitsland Date: Wed, 8 May 2019 15:24:14 +0200 Subject: Guard against numerical overflow when processing QPainterPaths Many operations on and with QPainterPaths do calculations on the path coordinates, e.g. computing the distance between points, which may cause numerical overflow for extreme coordinate values. This patch introduces a limit on the coordinate values and extends the previous check against nan/inf coordinates to also check against out of range coordinates. Fixes: QTBUG-75574 Change-Id: I3a2fa88bfc6a9f19934c43d3dbbfb41855c78107 Reviewed-by: Allan Sandfeld Jensen --- src/gui/painting/qpainterpath.cpp | 57 ++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/gui/painting/qpainterpath.cpp b/src/gui/painting/qpainterpath.cpp index c5ccf0003d..9b82b768d4 100644 --- a/src/gui/painting/qpainterpath.cpp +++ b/src/gui/painting/qpainterpath.cpp @@ -71,6 +71,24 @@ QT_BEGIN_NAMESPACE +static inline bool isValidCoord(qreal c) +{ + if (sizeof(qreal) >= sizeof(double)) + return qIsFinite(c) && fabs(c) < 1e128; + else + return qIsFinite(c) && fabsf(float(c)) < 1e16f; +} + +static bool hasValidCoords(QPointF p) +{ + return isValidCoord(p.x()) && isValidCoord(p.y()); +} + +static bool hasValidCoords(QRectF r) +{ + return isValidCoord(r.x()) && isValidCoord(r.y()) && isValidCoord(r.width()) && isValidCoord(r.height()); +} + struct QPainterPathPrivateDeleter { static inline void cleanup(QPainterPathPrivate *d) @@ -675,9 +693,9 @@ void QPainterPath::moveTo(const QPointF &p) printf("QPainterPath::moveTo() (%.2f,%.2f)\n", p.x(), p.y()); #endif - if (!qt_is_finite(p.x()) || !qt_is_finite(p.y())) { + if (!hasValidCoords(p)) { #ifndef QT_NO_DEBUG - qWarning("QPainterPath::moveTo: Adding point where x or y is NaN or Inf, ignoring call"); + qWarning("QPainterPath::moveTo: Adding point with invalid coordinates, ignoring call"); #endif return; } @@ -725,9 +743,9 @@ void QPainterPath::lineTo(const QPointF &p) printf("QPainterPath::lineTo() (%.2f,%.2f)\n", p.x(), p.y()); #endif - if (!qt_is_finite(p.x()) || !qt_is_finite(p.y())) { + if (!hasValidCoords(p)) { #ifndef QT_NO_DEBUG - qWarning("QPainterPath::lineTo: Adding point where x or y is NaN or Inf, ignoring call"); + qWarning("QPainterPath::lineTo: Adding point with invalid coordinates, ignoring call"); #endif return; } @@ -784,10 +802,9 @@ void QPainterPath::cubicTo(const QPointF &c1, const QPointF &c2, const QPointF & c1.x(), c1.y(), c2.x(), c2.y(), e.x(), e.y()); #endif - if (!qt_is_finite(c1.x()) || !qt_is_finite(c1.y()) || !qt_is_finite(c2.x()) || !qt_is_finite(c2.y()) - || !qt_is_finite(e.x()) || !qt_is_finite(e.y())) { + if (!hasValidCoords(c1) || !hasValidCoords(c2) || !hasValidCoords(e)) { #ifndef QT_NO_DEBUG - qWarning("QPainterPath::cubicTo: Adding point where x or y is NaN or Inf, ignoring call"); + qWarning("QPainterPath::cubicTo: Adding point with invalid coordinates, ignoring call"); #endif return; } @@ -841,9 +858,9 @@ void QPainterPath::quadTo(const QPointF &c, const QPointF &e) c.x(), c.y(), e.x(), e.y()); #endif - if (!qt_is_finite(c.x()) || !qt_is_finite(c.y()) || !qt_is_finite(e.x()) || !qt_is_finite(e.y())) { + if (!hasValidCoords(c) || !hasValidCoords(e)) { #ifndef QT_NO_DEBUG - qWarning("QPainterPath::quadTo: Adding point where x or y is NaN or Inf, ignoring call"); + qWarning("QPainterPath::quadTo: Adding point with invalid coordinates, ignoring call"); #endif return; } @@ -912,10 +929,9 @@ void QPainterPath::arcTo(const QRectF &rect, qreal startAngle, qreal sweepLength rect.x(), rect.y(), rect.width(), rect.height(), startAngle, sweepLength); #endif - if ((!qt_is_finite(rect.x()) && !qt_is_finite(rect.y())) || !qt_is_finite(rect.width()) || !qt_is_finite(rect.height()) - || !qt_is_finite(startAngle) || !qt_is_finite(sweepLength)) { + if (!hasValidCoords(rect) || !isValidCoord(startAngle) || !isValidCoord(sweepLength)) { #ifndef QT_NO_DEBUG - qWarning("QPainterPath::arcTo: Adding arc where a parameter is NaN or Inf, ignoring call"); + qWarning("QPainterPath::arcTo: Adding point with invalid coordinates, ignoring call"); #endif return; } @@ -1018,9 +1034,9 @@ QPointF QPainterPath::currentPosition() const */ void QPainterPath::addRect(const QRectF &r) { - if (!qt_is_finite(r.x()) || !qt_is_finite(r.y()) || !qt_is_finite(r.width()) || !qt_is_finite(r.height())) { + if (!hasValidCoords(r)) { #ifndef QT_NO_DEBUG - qWarning("QPainterPath::addRect: Adding rect where a parameter is NaN or Inf, ignoring call"); + qWarning("QPainterPath::addRect: Adding point with invalid coordinates, ignoring call"); #endif return; } @@ -1098,10 +1114,9 @@ void QPainterPath::addPolygon(const QPolygonF &polygon) */ void QPainterPath::addEllipse(const QRectF &boundingRect) { - if (!qt_is_finite(boundingRect.x()) || !qt_is_finite(boundingRect.y()) - || !qt_is_finite(boundingRect.width()) || !qt_is_finite(boundingRect.height())) { + if (!hasValidCoords(boundingRect)) { #ifndef QT_NO_DEBUG - qWarning("QPainterPath::addEllipse: Adding ellipse where a parameter is NaN or Inf, ignoring call"); + qWarning("QPainterPath::addEllipse: Adding point with invalid coordinates, ignoring call"); #endif return; } @@ -2446,6 +2461,7 @@ QDataStream &operator<<(QDataStream &s, const QPainterPath &p) */ QDataStream &operator>>(QDataStream &s, QPainterPath &p) { + bool errorDetected = false; int size; s >> size; @@ -2464,10 +2480,11 @@ QDataStream &operator>>(QDataStream &s, QPainterPath &p) s >> x; s >> y; Q_ASSERT(type >= 0 && type <= 3); - if (!qt_is_finite(x) || !qt_is_finite(y)) { + if (!isValidCoord(qreal(x)) || !isValidCoord(qreal(y))) { #ifndef QT_NO_DEBUG - qWarning("QDataStream::operator>>: NaN or Inf element found in path, skipping it"); + qWarning("QDataStream::operator>>: Invalid QPainterPath coordinates read, skipping it"); #endif + errorDetected = true; continue; } QPainterPath::Element elm = { qreal(x), qreal(y), QPainterPath::ElementType(type) }; @@ -2480,6 +2497,8 @@ QDataStream &operator>>(QDataStream &s, QPainterPath &p) p.d_func()->fillRule = Qt::FillRule(fillRule); p.d_func()->dirtyBounds = true; p.d_func()->dirtyControlBounds = true; + if (errorDetected) + p = QPainterPath(); // Better than to return path with possibly corrupt datastructure, which would likely cause crash return s; } #endif // QT_NO_DATASTREAM -- cgit v1.2.3 From b9f96cacc99c8a242f45f4581843a6b1c67501f4 Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Mon, 27 May 2019 19:00:09 +0200 Subject: QRegExp: remove an out of bounds access into QString ... spotted with the brand-new checks for that in QCharRef. The rx[i] == ~~~ check is clearly wrong, as rx is the regexp we're building and `i` was not supposed to index into it. The intended meaning was wc[i] == ~~~, testing if we were seeing the closing bracket of a character set. We need to check for that immediately for dealing with the special syntax of []...] where the ] belongs to the character set (it can't be the closing one as character sets cannot be empty). Fix and add a regression test. Bonus: this code was almost unchanged since 2009. Change-Id: I958cd87fc25558e9d202d18b3dd4a35d0db16d8d Reviewed-by: Marc Mutz Reviewed-by: hjk --- src/corelib/tools/qregexp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/corelib/tools/qregexp.cpp b/src/corelib/tools/qregexp.cpp index 87b30c952e..ef24c952eb 100644 --- a/src/corelib/tools/qregexp.cpp +++ b/src/corelib/tools/qregexp.cpp @@ -825,7 +825,7 @@ static QString wc2rx(const QString &wc_str, const bool enableEscaping) if (wc[i] == QLatin1Char('^')) rx += wc[i++]; if (i < wclen) { - if (rx[i] == QLatin1Char(']')) + if (wc[i] == QLatin1Char(']')) rx += wc[i++]; while (i < wclen && wc[i] != QLatin1Char(']')) { if (wc[i] == QLatin1Char('\\')) -- cgit v1.2.3 From 978987981ac1ad0689e042cc241c1732496b59bb Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Wed, 22 May 2019 14:56:58 +0200 Subject: Automatically sysrootify $$[FOO/get] properties If automatic sysrootification is in effect (SysrootifyPrefix=true in qt.conf) then the qmake property variants $$[FOO] and $$[FOO/get] must be sysrootified. The latter was never sysrootified. All other variants (src, dev, raw) are supposed to be without sysroot. Flesh out a sysrootify function and readabilitify the code a bit while we're at it. Fixes: QTBUG-71673 Change-Id: Ifcbce8c035b9da447da9d6937edd5a4aa84573ba Reviewed-by: Oliver Wolff --- src/corelib/global/qlibraryinfo.cpp | 35 ++++++++++++++++++++++------------- src/corelib/global/qlibraryinfo.h | 1 + 2 files changed, 23 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/corelib/global/qlibraryinfo.cpp b/src/corelib/global/qlibraryinfo.cpp index 4119012d85..d19e54154e 100644 --- a/src/corelib/global/qlibraryinfo.cpp +++ b/src/corelib/global/qlibraryinfo.cpp @@ -432,7 +432,24 @@ void QLibraryInfo::reload() { QLibraryInfoPrivate::reload(); } -#endif + +void QLibraryInfo::sysrootify(QString *path) +{ + if (!QVariant::fromValue(rawLocation(SysrootifyPrefixPath, FinalPaths)).toBool()) + return; + + const QString sysroot = rawLocation(SysrootPath, FinalPaths); + if (sysroot.isEmpty()) + return; + + if (path->length() > 2 && path->at(1) == QLatin1Char(':') + && (path->at(2) == QLatin1Char('/') || path->at(2) == QLatin1Char('\\'))) { + path->replace(0, 2, sysroot); // Strip out the drive on Windows targets + } else { + path->prepend(sysroot); + } +} +#endif // QT_BUILD_QMAKE /*! Returns the location specified by \a loc. @@ -444,18 +461,8 @@ QLibraryInfo::location(LibraryLocation loc) QString ret = rawLocation(loc, FinalPaths); // Automatically prepend the sysroot to target paths - if (loc < SysrootPath || loc > LastHostPath) { - QString sysroot = rawLocation(SysrootPath, FinalPaths); - if (!sysroot.isEmpty() - && QVariant::fromValue(rawLocation(SysrootifyPrefixPath, FinalPaths)).toBool()) { - if (ret.length() > 2 && ret.at(1) == QLatin1Char(':') - && (ret.at(2) == QLatin1Char('/') || ret.at(2) == QLatin1Char('\\'))) { - ret.replace(0, 2, sysroot); // Strip out the drive on Windows targets - } else { - ret.prepend(sysroot); - } - } - } + if (loc < SysrootPath || loc > LastHostPath) + sysrootify(&ret); return ret; } @@ -598,6 +605,8 @@ QLibraryInfo::rawLocation(LibraryLocation loc, PathGroup group) } else { // we make any other path absolute to the prefix directory baseDir = rawLocation(PrefixPath, group); + if (group == EffectivePaths) + sysrootify(&baseDir); } #else if (loc == PrefixPath) { diff --git a/src/corelib/global/qlibraryinfo.h b/src/corelib/global/qlibraryinfo.h index 80fc5bd4fc..9414af9b7d 100644 --- a/src/corelib/global/qlibraryinfo.h +++ b/src/corelib/global/qlibraryinfo.h @@ -107,6 +107,7 @@ public: enum PathGroup { FinalPaths, EffectivePaths, EffectiveSourcePaths, DevicePaths }; static QString rawLocation(LibraryLocation, PathGroup); static void reload(); + static void sysrootify(QString *path); #endif static QStringList platformPluginArguments(const QString &platformName); -- cgit v1.2.3 From 0f417efe0fb37d03d2c1e98958d4213d263c90c0 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Tue, 28 May 2019 14:22:07 +0200 Subject: Windows QPA: Fix QGuiApplication::topLevelAt() with screen recorder applications MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Special applications like screen recorders can create special, invisible windows which are detected by the ChildWindowFromPointEx() as used in QWindowsContext::findPlatformWindowAt(). Fall back to WindowFromPoint() which skips those in case nothing is found. Fixes: QTBUG-40815 Change-Id: Idb5253c412fb4522c844edf5eadedc6e0fad3979 Reviewed-by: André de la Rocha --- src/plugins/platforms/windows/qwindowscontext.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src') diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index 55e7e32979..e7ac01e411 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -749,6 +749,12 @@ QWindowsWindow *QWindowsContext::findPlatformWindowAt(HWND parent, QWindowsWindow *result = nullptr; const POINT screenPoint = { screenPointIn.x(), screenPointIn.y() }; while (findPlatformWindowHelper(screenPoint, cwex_flags, this, &parent, &result)) {} + // QTBUG-40815: ChildWindowFromPointEx() can hit on special windows from + // screen recorder applications like ScreenToGif. Fall back to WindowFromPoint(). + if (result == nullptr) { + if (const HWND window = WindowFromPoint(screenPoint)) + result = findPlatformWindow(window); + } return result; } -- cgit v1.2.3 From c150c7a566234a7bf69fa114d493ac9839c57ad5 Mon Sep 17 00:00:00 2001 From: Andre de la Rocha Date: Tue, 28 May 2019 18:24:33 +0200 Subject: Windows QPA: Optimize code that gets window under pointer It's not necessary to call QWindowsScreen::windowAt() for every mouse message received, but only when the mouse is captured, like it's done in the legacy mouse handler. Change-Id: Ib1035921291d22a32dfa3a619815a3f4ff9b3622 Reviewed-by: Friedemann Kleint --- src/plugins/platforms/windows/qwindowspointerhandler.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/plugins/platforms/windows/qwindowspointerhandler.cpp b/src/plugins/platforms/windows/qwindowspointerhandler.cpp index fd3d711470..8b88007949 100644 --- a/src/plugins/platforms/windows/qwindowspointerhandler.cpp +++ b/src/plugins/platforms/windows/qwindowspointerhandler.cpp @@ -269,7 +269,10 @@ static Qt::MouseButtons queryMouseButtons() static QWindow *getWindowUnderPointer(QWindow *window, QPoint globalPos) { - QWindow *currentWindowUnderPointer = QWindowsScreen::windowAt(globalPos, CWP_SKIPINVISIBLE | CWP_SKIPTRANSPARENT); + QWindowsWindow *platformWindow = static_cast(window->handle()); + + QWindow *currentWindowUnderPointer = platformWindow->hasMouseCapture() ? + QWindowsScreen::windowAt(globalPos, CWP_SKIPINVISIBLE | CWP_SKIPTRANSPARENT) : window; while (currentWindowUnderPointer && currentWindowUnderPointer->flags() & Qt::WindowTransparentForInput) currentWindowUnderPointer = currentWindowUnderPointer->parent(); -- cgit v1.2.3 From 4df554c652568805424e3fa256380c7310ccac72 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Fri, 24 May 2019 14:56:59 +0200 Subject: Windows QPA: Fix clang warnings about narrowing conversions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Iba173b45fb77918694fc2c7506885fdeef9f6064 Reviewed-by: André de la Rocha --- src/plugins/platforms/direct2d/qwindowsdirect2dhelpers.h | 7 +++++-- src/plugins/platforms/windows/qwindowscontext.cpp | 5 +++-- src/plugins/platforms/windows/qwindowsscreen.cpp | 2 +- src/plugins/platforms/windows/qwindowswindow.cpp | 10 ++++++---- .../platforms/windows/uiautomation/qwindowsuiautils.cpp | 2 +- 5 files changed, 16 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dhelpers.h b/src/plugins/platforms/direct2d/qwindowsdirect2dhelpers.h index babe646fd8..6d880e24cf 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dhelpers.h +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dhelpers.h @@ -69,12 +69,15 @@ inline D2D1_RECT_F to_d2d_rect_f(const QRectF &qrect) inline D2D1_SIZE_U to_d2d_size_u(const QSizeF &qsize) { - return D2D1::SizeU(qsize.width(), qsize.height()); + + return D2D1::SizeU(UINT32(qRound(qsize.width())), + UINT32(qRound(qsize.height()))); } inline D2D1_SIZE_U to_d2d_size_u(const QSize &qsize) { - return D2D1::SizeU(qsize.width(), qsize.height()); + return D2D1::SizeU(UINT32(qsize.width()), + UINT32(qsize.height())); } inline D2D1_POINT_2F to_d2d_point_2f(const QPointF &qpoint) diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index e7ac01e411..76747b7956 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -931,7 +931,7 @@ bool QWindowsContext::systemParametersInfo(unsigned action, unsigned param, void bool QWindowsContext::systemParametersInfoForScreen(unsigned action, unsigned param, void *out, const QPlatformScreen *screen) { - return systemParametersInfo(action, param, out, screen ? screen->logicalDpi().first : 0); + return systemParametersInfo(action, param, out, screen ? unsigned(screen->logicalDpi().first) : 0u); } bool QWindowsContext::systemParametersInfoForWindow(unsigned action, unsigned param, void *out, @@ -950,7 +950,8 @@ bool QWindowsContext::nonClientMetrics(NONCLIENTMETRICS *ncm, unsigned dpi) bool QWindowsContext::nonClientMetricsForScreen(NONCLIENTMETRICS *ncm, const QPlatformScreen *screen) { - return nonClientMetrics(ncm, screen ? screen->logicalDpi().first : 0); + const int dpi = screen ? qRound(screen->logicalDpi().first) : 0; + return nonClientMetrics(ncm, unsigned(dpi)); } bool QWindowsContext::nonClientMetricsForWindow(NONCLIENTMETRICS *ncm, const QPlatformWindow *win) diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp index b28a113ce6..cc0f3c1a6e 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.cpp +++ b/src/plugins/platforms/windows/qwindowsscreen.cpp @@ -91,7 +91,7 @@ static bool monitorData(HMONITOR hMonitor, QWindowsScreenData *data) } else { if (const HDC hdc = CreateDC(info.szDevice, nullptr, nullptr, nullptr)) { const QDpi dpi = monitorDPI(hMonitor); - data->dpi = dpi.first ? dpi : deviceDPI(hdc); + data->dpi = dpi.first > 0 ? dpi : deviceDPI(hdc); data->depth = GetDeviceCaps(hdc, BITSPIXEL); data->format = data->depth == 16 ? QImage::Format_RGB16 : QImage::Format_RGB32; data->physicalSizeMM = QSizeF(GetDeviceCaps(hdc, HORZSIZE), GetDeviceCaps(hdc, VERTSIZE)); diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index 3bf424d0ac..eb521dac52 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -851,10 +851,12 @@ static QSize toNativeSizeConstrained(QSize dip, const QWindow *w) { if (QHighDpiScaling::isActive()) { const qreal factor = QHighDpiScaling::factor(w); - if (dip.width() > 0 && dip.width() < QWINDOWSIZE_MAX) - dip.rwidth() *= factor; - if (dip.height() > 0 && dip.height() < QWINDOWSIZE_MAX) - dip.rheight() *= factor; + if (!qFuzzyCompare(factor, qreal(1))) { + if (dip.width() > 0 && dip.width() < QWINDOWSIZE_MAX) + dip.setWidth(qRound(qreal(dip.width()) * factor)); + if (dip.height() > 0 && dip.height() < QWINDOWSIZE_MAX) + dip.setHeight(qRound(qreal(dip.height()) * factor)); + } } return dip; } diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp index 7980826b49..ab04384616 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp @@ -114,7 +114,7 @@ void setVariantBool(bool value, VARIANT *variant) void setVariantDouble(double value, VARIANT *variant) { variant->vt = VT_R8; - variant->boolVal = value; + variant->dblVal = value; } BSTR bStrFromQString(const QString &value) -- cgit v1.2.3 From a67b25067ee78e0c2bc4e77a4c61af43528d9bc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Mon, 27 May 2019 18:36:13 +0200 Subject: Rename QMacScopedObserver to the more fittingly QMacNotificationObserver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I1d8280fe88871572a3a27e612de49717b3b9ef77 Reviewed-by: Tor Arne Vestbø --- src/corelib/kernel/qcore_mac_p.h | 16 ++++++++-------- src/plugins/platforms/cocoa/qcocoaglcontext.h | 2 +- src/plugins/platforms/cocoa/qcocoaglcontext.mm | 8 ++++---- src/plugins/platforms/cocoa/qcocoaintegration.h | 2 +- src/plugins/platforms/cocoa/qcocoaintegration.mm | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/corelib/kernel/qcore_mac_p.h b/src/corelib/kernel/qcore_mac_p.h index f96e7358a2..2428faed15 100644 --- a/src/corelib/kernel/qcore_mac_p.h +++ b/src/corelib/kernel/qcore_mac_p.h @@ -295,13 +295,13 @@ QT_MAC_WEAK_IMPORT(_os_activity_current); // ------------------------------------------------------------------------- #if defined( __OBJC__) -class QMacScopedObserver +class QMacNotificationObserver { public: - QMacScopedObserver() {} + QMacNotificationObserver() {} template - QMacScopedObserver(id object, NSNotificationName name, Functor callback) { + QMacNotificationObserver(id object, NSNotificationName name, Functor callback) { observer = [[NSNotificationCenter defaultCenter] addObserverForName:name object:object queue:nil usingBlock:^(NSNotification *) { callback(); @@ -309,13 +309,13 @@ public: ]; } - QMacScopedObserver(const QMacScopedObserver& other) = delete; - QMacScopedObserver(QMacScopedObserver&& other) : observer(other.observer) { + QMacNotificationObserver(const QMacNotificationObserver& other) = delete; + QMacNotificationObserver(QMacNotificationObserver&& other) : observer(other.observer) { other.observer = nil; } - QMacScopedObserver &operator=(const QMacScopedObserver& other) = delete; - QMacScopedObserver &operator=(QMacScopedObserver&& other) { + QMacNotificationObserver &operator=(const QMacNotificationObserver& other) = delete; + QMacNotificationObserver &operator=(QMacNotificationObserver&& other) { if (this != &other) { remove(); observer = other.observer; @@ -329,7 +329,7 @@ public: [[NSNotificationCenter defaultCenter] removeObserver:observer]; observer = nil; } - ~QMacScopedObserver() { remove(); } + ~QMacNotificationObserver() { remove(); } private: id observer = nil; diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.h b/src/plugins/platforms/cocoa/qcocoaglcontext.h index eefb1cd0fb..c1041ac2da 100644 --- a/src/plugins/platforms/cocoa/qcocoaglcontext.h +++ b/src/plugins/platforms/cocoa/qcocoaglcontext.h @@ -80,7 +80,7 @@ private: NSOpenGLContext *m_shareContext = nil; QSurfaceFormat m_format; bool m_didCheckForSoftwareContext = false; - QVarLengthArray m_updateObservers; + QVarLengthArray m_updateObservers; QAtomicInt m_needsUpdate = false; }; diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.mm b/src/plugins/platforms/cocoa/qcocoaglcontext.mm index 93d95b38ea..d0100d0410 100644 --- a/src/plugins/platforms/cocoa/qcocoaglcontext.mm +++ b/src/plugins/platforms/cocoa/qcocoaglcontext.mm @@ -404,13 +404,13 @@ bool QCocoaGLContext::setDrawable(QPlatformSurface *surface) m_updateObservers.clear(); if (view.layer) { - m_updateObservers.append(QMacScopedObserver(view, NSViewFrameDidChangeNotification, updateCallback)); - m_updateObservers.append(QMacScopedObserver(view.window, NSWindowDidChangeScreenNotification, updateCallback)); + m_updateObservers.append(QMacNotificationObserver(view, NSViewFrameDidChangeNotification, updateCallback)); + m_updateObservers.append(QMacNotificationObserver(view.window, NSWindowDidChangeScreenNotification, updateCallback)); } else { - m_updateObservers.append(QMacScopedObserver(view, NSViewGlobalFrameDidChangeNotification, updateCallback)); + m_updateObservers.append(QMacNotificationObserver(view, NSViewGlobalFrameDidChangeNotification, updateCallback)); } - m_updateObservers.append(QMacScopedObserver([NSApplication sharedApplication], + m_updateObservers.append(QMacNotificationObserver([NSApplication sharedApplication], NSApplicationDidChangeScreenParametersNotification, updateCallback)); // If any of the observers fire at this point it's fine. We check the diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.h b/src/plugins/platforms/cocoa/qcocoaintegration.h index 04cb4e1226..ecbd19c9a2 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.h +++ b/src/plugins/platforms/cocoa/qcocoaintegration.h @@ -144,7 +144,7 @@ private: #endif QScopedPointer mPlatformTheme; QList mScreens; - QMacScopedObserver m_screensObserver; + QMacNotificationObserver m_screensObserver; #ifndef QT_NO_CLIPBOARD QCocoaClipboard *mCocoaClipboard; #endif diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index cbbf6b115e..232c74b1e9 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -207,7 +207,7 @@ QCocoaIntegration::QCocoaIntegration(const QStringList ¶mList) // which will resolve to an actual value and result in screen invalidation. cocoaApplication.presentationOptions = NSApplicationPresentationDefault; - m_screensObserver = QMacScopedObserver([NSApplication sharedApplication], + m_screensObserver = QMacNotificationObserver([NSApplication sharedApplication], NSApplicationDidChangeScreenParametersNotification, [&]() { updateScreens(); }); updateScreens(); -- cgit v1.2.3 From 1eac947ce2c1d63bd04a94939c4f04e9086913c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Wed, 22 May 2019 14:01:41 +0200 Subject: Work around crash where a destroyed window becomes focus_window MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clear QGuiApplication::focus_window (again) in the QWindow destructor. Task-number: QTBUG-75326 Change-Id: Ief00b6adfb267fcc7e3881fd728e12df07fc1094 Reviewed-by: Christian Andersen Reviewed-by: Tor Arne Vestbø --- src/gui/kernel/qwindow.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src') diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp index bcd8351619..a19df4da0f 100644 --- a/src/gui/kernel/qwindow.cpp +++ b/src/gui/kernel/qwindow.cpp @@ -218,6 +218,12 @@ QWindow::~QWindow() QGuiApplicationPrivate::window_list.removeAll(this); if (!QGuiApplicationPrivate::is_app_closing) QGuiApplicationPrivate::instance()->modalWindowList.removeOne(this); + + // focus_window is normally cleared in destroy(), but the window may in + // some cases end up becoming the focus window again. Clear it again + // here as a workaround. See QTBUG-75326. + if (QGuiApplicationPrivate::focus_window == this) + QGuiApplicationPrivate::focus_window = 0; } void QWindowPrivate::init(QScreen *targetScreen) -- cgit v1.2.3 From 364240ed11f5f72341d9e2fa5009baecb4398b1b Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Thu, 30 May 2019 19:06:02 +0200 Subject: macOS: Enable VK_MVK_macos_surface on the instance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reports say some systems get "Failed to find vkCreateMacOSSurfaceMVK" even though MoltenVK and the corresponding extensionare there. While not reproducable on single GPU Intel systems, it could be that not enabling the extension on the Vulkan instance is causing this. Not opting in to the extension is incorrect in theory anyway. So fix it in cocoa as well, similarly to how other platform plugins do this already. Task-number: QTBUG-76117 Change-Id: I75220f3582a700ce0037003086123d3d38524648 Reviewed-by: Tor Arne Vestbø Reviewed-by: Mike Krus --- src/plugins/platforms/cocoa/qcocoavulkaninstance.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/plugins/platforms/cocoa/qcocoavulkaninstance.mm b/src/plugins/platforms/cocoa/qcocoavulkaninstance.mm index b00fde6c6f..7ce78ee738 100644 --- a/src/plugins/platforms/cocoa/qcocoavulkaninstance.mm +++ b/src/plugins/platforms/cocoa/qcocoavulkaninstance.mm @@ -54,7 +54,7 @@ QCocoaVulkanInstance::~QCocoaVulkanInstance() void QCocoaVulkanInstance::createOrAdoptInstance() { - initInstance(m_instance, QByteArrayList()); + initInstance(m_instance, QByteArrayList() << QByteArrayLiteral("VK_MVK_macos_surface")); } VkSurfaceKHR *QCocoaVulkanInstance::createSurface(QWindow *window) -- cgit v1.2.3 From 81b33a8252d8a69277782f51c9c7401c7009c72e Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Mon, 27 May 2019 14:35:18 +0200 Subject: Fusion: Fill the rect with base brush when drawing the combobox frame Instead of doing this directly inside the PE_FrameLineEdit as this can be used directly when a stylesheet is set the rect of the lineedit used in the combobox in fusion style is filled directly with the base brush. This ensures it still renders the background correctly when using fusion style, but enables stylesheets to work as it will not override that if the background is set in a stylesheet for a combobox. Fixes: QTBUG-75816 Change-Id: I50a0600b500088ebcf1d70a02f9c74c6040d34d9 Reviewed-by: Friedemann Kleint Reviewed-by: Richard Moe Gustavsen --- src/widgets/styles/qfusionstyle.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/widgets/styles/qfusionstyle.cpp b/src/widgets/styles/qfusionstyle.cpp index c565932889..64954dc833 100644 --- a/src/widgets/styles/qfusionstyle.cpp +++ b/src/widgets/styles/qfusionstyle.cpp @@ -2769,8 +2769,16 @@ void QFusionStyle::drawComplexControl(ComplexControl control, const QStyleOption buttonOption.state &= ~State_MouseOver; } - if (comboBox->frame) + if (comboBox->frame) { + cachePainter.save(); + cachePainter.setRenderHint(QPainter::Antialiasing, true); + cachePainter.translate(0.5, 0.5); + cachePainter.setPen(Qt::NoPen); + cachePainter.setBrush(buttonOption.palette.base()); + cachePainter.drawRoundedRect(rect.adjusted(0, 0, -1, -1), 2, 2); + cachePainter.restore(); proxy()->drawPrimitive(PE_FrameLineEdit, &buttonOption, &cachePainter, widget); + } // Draw button clipped cachePainter.save(); -- cgit v1.2.3 From 90c683e41cd978b55de57cb424a986db3b637fcc Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Wed, 15 May 2019 15:06:20 +0200 Subject: Q(Plain)TextEdit: Observe color hints from style sheet depending on focus state Ask the style sheet style to adapt the palette in WidgetTextControl::getPaintContext() as is done for QLineEdit. Use the palette color in QPlainTextEdit. Change-Id: I67758716b66feaeac8c2433c2a4d3744cd0d5327 Fixes: QTBUG-72100 Reviewed-by: Christian Ehrlicher Reviewed-by: Richard Moe Gustavsen --- src/widgets/widgets/qplaintextedit.cpp | 1 + src/widgets/widgets/qwidgettextcontrol.cpp | 12 ++++++++++++ 2 files changed, 13 insertions(+) (limited to 'src') diff --git a/src/widgets/widgets/qplaintextedit.cpp b/src/widgets/widgets/qplaintextedit.cpp index 0b5e69b6c3..3b5132a8c8 100644 --- a/src/widgets/widgets/qplaintextedit.cpp +++ b/src/widgets/widgets/qplaintextedit.cpp @@ -1957,6 +1957,7 @@ void QPlainTextEdit::paintEvent(QPaintEvent *e) } QAbstractTextDocumentLayout::PaintContext context = getPaintContext(); + painter.setPen(context.palette.text().color()); while (block.isValid()) { diff --git a/src/widgets/widgets/qwidgettextcontrol.cpp b/src/widgets/widgets/qwidgettextcontrol.cpp index 6b8ce63380..ee757bf870 100644 --- a/src/widgets/widgets/qwidgettextcontrol.cpp +++ b/src/widgets/widgets/qwidgettextcontrol.cpp @@ -64,6 +64,9 @@ #include "private/qtextdocument_p.h" #include "qtextlist.h" #include "private/qwidgettextcontrol_p.h" +#if QT_CONFIG(style_stylesheet) +# include "private/qstylesheetstyle_p.h" +#endif #if QT_CONFIG(graphicsview) #include "qgraphicssceneevent.h" #endif @@ -3187,6 +3190,15 @@ QAbstractTextDocumentLayout::PaintContext QWidgetTextControl::getPaintContext(QW ctx.selections = d->extraSelections; ctx.palette = d->palette; +#if QT_CONFIG(style_stylesheet) + if (widget) { + if (auto cssStyle = qt_styleSheet(widget->style())) { + QStyleOption option; + option.initFrom(widget); + cssStyle->styleSheetPalette(widget, &option, &ctx.palette); + } + } +#endif // style_stylesheet if (d->cursorOn && d->isEnabled) { if (d->hideCursor) ctx.cursorPosition = -1; -- cgit v1.2.3 From 7029ecade96876d08af7d576e0b81417502acbaa Mon Sep 17 00:00:00 2001 From: Liang Qi Date: Mon, 20 May 2019 11:37:27 +0200 Subject: cmake: correct version dependency for qt5_add_big_resources qt5_add_big_resources is only available if using CMake 3.9 and later. This amends cdccd0222bbed1954d5d7fe0da9d2308c202f3b1. Task-number: QTBUG-55680 Task-number: QTBUG-75806 Change-Id: Ibba7af6ee7edfb226368937d543b7ec5cc93eb16 Reviewed-by: Alexandru Croitor Reviewed-by: Kai Koehne --- src/corelib/Qt5CoreMacros.cmake | 5 +++++ src/corelib/doc/src/cmake-macros.qdoc | 2 ++ 2 files changed, 7 insertions(+) (limited to 'src') diff --git a/src/corelib/Qt5CoreMacros.cmake b/src/corelib/Qt5CoreMacros.cmake index 7ae5e4fd16..3a60b8e0d2 100644 --- a/src/corelib/Qt5CoreMacros.cmake +++ b/src/corelib/Qt5CoreMacros.cmake @@ -296,6 +296,9 @@ endfunction() # qt5_add_big_resources(outfiles inputfile ... ) function(QT5_ADD_BIG_RESOURCES outfiles ) + if (CMAKE_VERSION VERSION_LESS 3.9) + message(FATAL_ERROR, "qt5_add_big_resources requires CMake 3.9 or newer") + endif() set(options) set(oneValueArgs) @@ -326,6 +329,8 @@ function(QT5_ADD_BIG_RESOURCES outfiles ) set_target_properties(rcc_object_${outfilename} PROPERTIES AUTOMOC OFF) set_target_properties(rcc_object_${outfilename} PROPERTIES AUTOUIC OFF) add_dependencies(rcc_object_${outfilename} big_resources_${outfilename}) + # The modification of TARGET_OBJECTS needs the following change in cmake + # https://gitlab.kitware.com/cmake/cmake/commit/93c89bc75ceee599ba7c08b8fe1ac5104942054f add_custom_command(OUTPUT ${outfile} COMMAND ${Qt5Core_RCC_EXECUTABLE} ARGS ${rcc_options} --name ${outfilename} --pass 2 --temp $ --output ${outfile} ${infile} diff --git a/src/corelib/doc/src/cmake-macros.qdoc b/src/corelib/doc/src/cmake-macros.qdoc index 6140e8be44..7fb133020f 100644 --- a/src/corelib/doc/src/cmake-macros.qdoc +++ b/src/corelib/doc/src/cmake-macros.qdoc @@ -131,6 +131,8 @@ files (\c .o, \c .obj) files instead of C++ source code. This allows to embed bigger resources, where compiling to C++ sources and then to binaries would be too time consuming or memory intensive. +Note that this macro is only available if using \c{CMake} 3.9 or later. + \section1 Arguments You can set additional \c{OPTIONS} that should be added to the \c{rcc} calls. -- cgit v1.2.3 From 48b707224efaa54b4dfd831cfd8fbe9006f99588 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Tue, 14 May 2019 12:37:40 +0200 Subject: Fix invalid vptr during destruction of animations Fixes UBSAN warnings about objects used after partial destruction. Change-Id: Iceea083a77d47335ef595c0ff97b87f35f42e56f Reviewed-by: Ulf Hermann --- src/corelib/animation/qabstractanimation.cpp | 2 ++ src/corelib/animation/qanimationgroup.cpp | 25 +++++++++++++++++++++- src/corelib/animation/qanimationgroup_p.h | 2 ++ .../animation/qsequentialanimationgroup.cpp | 3 ++- 4 files changed, 30 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/corelib/animation/qabstractanimation.cpp b/src/corelib/animation/qabstractanimation.cpp index b507049cc4..8a9d86b7c5 100644 --- a/src/corelib/animation/qabstractanimation.cpp +++ b/src/corelib/animation/qabstractanimation.cpp @@ -1067,6 +1067,8 @@ QAbstractAnimation::~QAbstractAnimation() if (oldState == QAbstractAnimation::Running) QAnimationTimer::unregisterAnimation(this); } + if (d->group) + d->group->removeAnimation(this); } /*! diff --git a/src/corelib/animation/qanimationgroup.cpp b/src/corelib/animation/qanimationgroup.cpp index f47d99eb68..ed40817222 100644 --- a/src/corelib/animation/qanimationgroup.cpp +++ b/src/corelib/animation/qanimationgroup.cpp @@ -113,6 +113,11 @@ QAnimationGroup::QAnimationGroup(QAnimationGroupPrivate &dd, QObject *parent) */ QAnimationGroup::~QAnimationGroup() { + Q_D(QAnimationGroup); + // We need to clear the animations now while we are still a valid QAnimationGroup. + // If we wait until ~QObject() the QAbstractAnimation's pointer back to us would + // point to a QObject, not a valid QAnimationGroup. + d->clear(true); } /*! @@ -256,7 +261,7 @@ QAbstractAnimation *QAnimationGroup::takeAnimation(int index) void QAnimationGroup::clear() { Q_D(QAnimationGroup); - qDeleteAll(d->animations); + d->clear(false); } /*! @@ -284,6 +289,24 @@ bool QAnimationGroup::event(QEvent *event) return QAbstractAnimation::event(event); } +void QAnimationGroupPrivate::clear(bool onDestruction) +{ + const QList animationsCopy = animations; // taking a copy + animations.clear(); + // Clearing backwards so the indices doesn't change while we remove animations. + for (int i = animationsCopy.count() - 1; i >= 0; --i) { + QAbstractAnimation *animation = animationsCopy.at(i); + animation->setParent(nullptr); + QAbstractAnimationPrivate::get(animation)->group = nullptr; + // If we are in ~QAnimationGroup() it is not safe to called the virtual + // animationRemoved method, which can still be a method in a + // QAnimationGroupPrivate derived class that assumes q_ptr is still + // a valid derived class of QAnimationGroup. + if (!onDestruction) + animationRemoved(i, animation); + delete animation; + } +} void QAnimationGroupPrivate::animationRemoved(int index, QAbstractAnimation *) { diff --git a/src/corelib/animation/qanimationgroup_p.h b/src/corelib/animation/qanimationgroup_p.h index ff759d11fd..d0b2e9f9bb 100644 --- a/src/corelib/animation/qanimationgroup_p.h +++ b/src/corelib/animation/qanimationgroup_p.h @@ -73,6 +73,8 @@ public: virtual void animationInsertedAt(int) { } virtual void animationRemoved(int, QAbstractAnimation *); + void clear(bool onDestruction); + void disconnectUncontrolledAnimation(QAbstractAnimation *anim) { //0 for the signal here because we might be called from the animation destructor diff --git a/src/corelib/animation/qsequentialanimationgroup.cpp b/src/corelib/animation/qsequentialanimationgroup.cpp index 150e74d7d6..66e346a2fe 100644 --- a/src/corelib/animation/qsequentialanimationgroup.cpp +++ b/src/corelib/animation/qsequentialanimationgroup.cpp @@ -532,7 +532,8 @@ void QSequentialAnimationGroupPrivate::animationRemoved(int index, QAbstractAnim Q_Q(QSequentialAnimationGroup); QAnimationGroupPrivate::animationRemoved(index, anim); - Q_ASSERT(currentAnimation); // currentAnimation should always be set + if (!currentAnimation) + return; if (actualDuration.size() > index) actualDuration.removeAt(index); -- cgit v1.2.3 From 825b474363167cfd91509e15b8846e11b8560dac Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Mon, 27 May 2019 23:02:23 +0200 Subject: Windows: Use the first entry in the requested families if available If the font request has the families list set then it should use the first entry in that if the face name is empty as this will be more accurate than just the whole family setting which may contain a comma separated list of family names. Fixes: QTBUG-75333 Change-Id: Iccc9cde741544af5263cb318da56178adf34299b Reviewed-by: Allan Sandfeld Jensen --- src/platformsupport/fontdatabases/windows/qwindowsfontdatabase.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase.cpp b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase.cpp index c456f01b28..c523e799e6 100644 --- a/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase.cpp +++ b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase.cpp @@ -1818,7 +1818,7 @@ LOGFONT QWindowsFontDatabase::fontDefToLOGFONT(const QFontDef &request, const QS QString fam = faceName; if (fam.isEmpty()) - fam = request.family; + fam = request.families.size() > 0 ? request.families.at(0) : request.family; if (Q_UNLIKELY(fam.size() >= LF_FACESIZE)) { qCritical("%s: Family name '%s' is too long.", __FUNCTION__, qPrintable(fam)); fam.truncate(LF_FACESIZE - 1); -- cgit v1.2.3 From 1dc1a032a84d1cc408623ba5b77a0acf50a58eea Mon Sep 17 00:00:00 2001 From: Samuel Gaist Date: Tue, 21 May 2019 22:02:56 +0200 Subject: doc: Improve the Coordinate System rectangle coordinate examples QRect and QLine share a constructor with the same signature but with different meanings. This patch improves the coordinate examples to show the possibilities and make things clearer. Change-Id: I76a95ca226c12968ae5b15d4eb12fc2965cb57d2 Reviewed-by: Paul Wicking Reviewed-by: Luca Beldi Reviewed-by: Sze Howe Koh --- src/gui/doc/snippets/code/doc_src_coordsys.cpp | 2 ++ src/gui/doc/src/coordsys.qdoc | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/gui/doc/snippets/code/doc_src_coordsys.cpp b/src/gui/doc/snippets/code/doc_src_coordsys.cpp index 7c53d9e731..147c146b44 100644 --- a/src/gui/doc/snippets/code/doc_src_coordsys.cpp +++ b/src/gui/doc/snippets/code/doc_src_coordsys.cpp @@ -52,6 +52,7 @@ QPainter painter(this); painter.setPen(Qt::darkGreen); +// Using the (x y w h) overload painter.drawRect(1, 2, 6, 4); //! [0] @@ -69,6 +70,7 @@ QPainter painter(this); painter.setRenderHint( QPainter::Antialiasing); painter.setPen(Qt::darkGreen); +// Using the (x y w h) overload painter.drawRect(1, 2, 6, 4); //! [2] diff --git a/src/gui/doc/src/coordsys.qdoc b/src/gui/doc/src/coordsys.qdoc index 1856428361..1018b2122b 100644 --- a/src/gui/doc/src/coordsys.qdoc +++ b/src/gui/doc/src/coordsys.qdoc @@ -70,8 +70,15 @@ \li \inlineimage coordinatesystem-rect.png \li \inlineimage coordinatesystem-line.png \row - \li QRect(1, 2, 6, 4) + \li QRect(QPoint(1, 2), QPoint(7, 6)) + \li QLine(QPoint(2, 7), QPoint(6, 1)) + \row + \li \li QLine(2, 7, 6, 1) + \row + \li QRect(QPoint(1, 2), QSize(6, 4)) + \row + \li QRect(1, 2, 6, 4) \endtable \section2 Aliased Painting -- cgit v1.2.3 From 9a47768b46f5e5eed407b70dfa9183fa1d21e242 Mon Sep 17 00:00:00 2001 From: Mikhail Svetkin Date: Mon, 27 May 2019 14:27:49 +0200 Subject: macOS: Fix reported mouse event buttons Use m_buttons instead of currentlyPressedMouseButtons. The latter returns the state of devices combined with synthesized events at the moment, independent of which events have been delivered via the event stream, so this method is not suitable for tracking. Task-number: QTBUG-74057 Task-number: QTBUG-74121 Change-Id: Iabf99ada6c3d25a995c9ddf895059b70833a9051 Reviewed-by: Gatis Paeglis --- src/plugins/platforms/cocoa/qnsview_mouse.mm | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/cocoa/qnsview_mouse.mm b/src/plugins/platforms/cocoa/qnsview_mouse.mm index d4419b42a4..a887cb841d 100644 --- a/src/plugins/platforms/cocoa/qnsview_mouse.mm +++ b/src/plugins/platforms/cocoa/qnsview_mouse.mm @@ -277,20 +277,19 @@ nativeDrag->setLastMouseEvent(theEvent, self); const auto modifiers = [QNSView convertKeyModifiers:theEvent.modifierFlags]; - const auto buttons = currentlyPressedMouseButtons(); auto button = cocoaButton2QtButton(theEvent); if (button == Qt::LeftButton && m_sendUpAsRightButton) button = Qt::RightButton; const auto eventType = cocoaEvent2QtMouseEvent(theEvent); if (eventType == QEvent::MouseMove) - qCDebug(lcQpaMouse) << eventType << "at" << qtWindowPoint << "with" << buttons; + qCDebug(lcQpaMouse) << eventType << "at" << qtWindowPoint << "with" << m_buttons; else - qCInfo(lcQpaMouse) << eventType << "of" << button << "at" << qtWindowPoint << "with" << buttons; + qCInfo(lcQpaMouse) << eventType << "of" << button << "at" << qtWindowPoint << "with" << m_buttons; QWindowSystemInterface::handleMouseEvent(targetView->m_platformWindow->window(), timestamp, qtWindowPoint, qtScreenPoint, - buttons, button, eventType, modifiers); + m_buttons, button, eventType, modifiers); } - (bool)handleMouseDownEvent:(NSEvent *)theEvent -- cgit v1.2.3 From b53994de5f1b641ca2f6c79048339ff34fe14b55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Wed, 29 May 2019 16:36:09 +0200 Subject: macOS: Use QMacNotificationObserver over manual notification handling This also fixes a bug where we were implicitly capturing this inside the block, which meant that we would crash if the theme was recreated. The capture is now tied to the lifetime of QCocoaTheme. Change-Id: I37df8e6c0b33bf41e76d66be3cf29576041a7546 Reviewed-by: Richard Moe Gustavsen --- src/plugins/platforms/cocoa/qcocoatheme.h | 3 +++ src/plugins/platforms/cocoa/qcocoatheme.mm | 6 +++--- src/plugins/styles/mac/qmacstyle_mac.mm | 28 +++++++++++----------------- 3 files changed, 17 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/cocoa/qcocoatheme.h b/src/plugins/platforms/cocoa/qcocoatheme.h index c42fa7d2e8..63227ed6c1 100644 --- a/src/plugins/platforms/cocoa/qcocoatheme.h +++ b/src/plugins/platforms/cocoa/qcocoatheme.h @@ -45,6 +45,8 @@ Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QCocoaThemeAppAppearanceObserver)); +#include + QT_BEGIN_NAMESPACE class QPalette; @@ -82,6 +84,7 @@ public: private: mutable QPalette *m_systemPalette; + QMacNotificationObserver m_systemColorObserver; mutable QHash m_palettes; mutable QHash m_fonts; QT_MANGLE_NAMESPACE(QCocoaThemeAppAppearanceObserver) *m_appearanceObserver; diff --git a/src/plugins/platforms/cocoa/qcocoatheme.mm b/src/plugins/platforms/cocoa/qcocoatheme.mm index efe670abed..ba93560689 100644 --- a/src/plugins/platforms/cocoa/qcocoatheme.mm +++ b/src/plugins/platforms/cocoa/qcocoatheme.mm @@ -133,10 +133,10 @@ QCocoaTheme::QCocoaTheme() m_appearanceObserver = [[QCocoaThemeAppAppearanceObserver alloc] initWithTheme:this]; #endif - [[NSNotificationCenter defaultCenter] addObserverForName:NSSystemColorsDidChangeNotification - object:nil queue:nil usingBlock:^(NSNotification *) { + m_systemColorObserver = QMacNotificationObserver(nil, + NSSystemColorsDidChangeNotification, [this] { handleSystemThemeChange(); - }]; + }); } QCocoaTheme::~QCocoaTheme() diff --git a/src/plugins/styles/mac/qmacstyle_mac.mm b/src/plugins/styles/mac/qmacstyle_mac.mm index ee5556ec9e..f88e3203bc 100644 --- a/src/plugins/styles/mac/qmacstyle_mac.mm +++ b/src/plugins/styles/mac/qmacstyle_mac.mm @@ -161,18 +161,6 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(NotificationReceiver); return self; } -- (void)scrollBarStyleDidChange:(NSNotification *)notification -{ - Q_UNUSED(notification); - - // purge destroyed scroll bars: - QMacStylePrivate::scrollBars.removeAll(QPointer()); - - QEvent event(QEvent::StyleChange); - for (const auto &o : QMacStylePrivate::scrollBars) - QCoreApplication::sendEvent(o, &event); -} - - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { @@ -2095,11 +2083,18 @@ QMacStyle::QMacStyle() Q_D(QMacStyle); QMacAutoReleasePool pool; + static QMacNotificationObserver scrollbarStyleObserver(nil, + NSPreferredScrollerStyleDidChangeNotification, []() { + // Purge destroyed scroll bars + QMacStylePrivate::scrollBars.removeAll(QPointer()); + + QEvent event(QEvent::StyleChange); + for (const auto &o : QMacStylePrivate::scrollBars) + QCoreApplication::sendEvent(o, &event); + }); + d->receiver = [[NotificationReceiver alloc] initWithPrivateStyle:d]; - [[NSNotificationCenter defaultCenter] addObserver:d->receiver - selector:@selector(scrollBarStyleDidChange:) - name:NSPreferredScrollerStyleDidChangeNotification - object:nil]; + #if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14) if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave) { [NSApplication.sharedApplication addObserver:d->receiver forKeyPath:@"effectiveAppearance" @@ -2113,7 +2108,6 @@ QMacStyle::~QMacStyle() Q_D(QMacStyle); QMacAutoReleasePool pool; - [[NSNotificationCenter defaultCenter] removeObserver:d->receiver]; #if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14) if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave) [NSApplication.sharedApplication removeObserver:d->receiver forKeyPath:@"effectiveAppearance"]; -- cgit v1.2.3 From 6f40331058071e628e83e74c7f2032e41b1a3f3d Mon Sep 17 00:00:00 2001 From: Mikhail Svetkin Date: Mon, 27 May 2019 15:45:29 +0200 Subject: macOS: Fix QMouseEvent::button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cocoaButton2QtButton(NSEvent *event) did not handle NSEventTypeLeftMouseDragged, NSEventTypeRightMouseDragged, NSEventTypeOtherMouseDragged. Task-number: QTBUG-74763 Change-Id: I9f48230599f16400b49edbff392f712eb1fff782 Reviewed-by: Tor Arne Vestbø --- src/plugins/platforms/cocoa/qcocoahelpers.mm | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm index 9c705616ba..d36a7f6d09 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.mm +++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm @@ -297,13 +297,12 @@ Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum) */ Qt::MouseButton cocoaButton2QtButton(NSEvent *event) { - switch (event.type) { - case NSEventTypeMouseMoved: + if (cocoaEvent2QtMouseEvent(event) == QEvent::MouseMove) return Qt::NoButton; + switch (event.type) { case NSEventTypeRightMouseUp: case NSEventTypeRightMouseDown: - case NSEventTypeRightMouseDragged: return Qt::RightButton; default: -- cgit v1.2.3 From 8c7589d992a5615fa3a98f67d098d5a2fca579cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Arve=20S=C3=A6ther?= Date: Wed, 29 May 2019 18:02:19 +0200 Subject: Do not strip off the fragment and query in the qfileselector This is needed for cases where we use e.g. "file:///test.html?query#Fragment". The fragment and query were already preserved for the qrc scheme. This fixes it for the file scheme. Change-Id: I5713e4a25372fdd55ac255b1c6228b4dea419244 Reviewed-by: Shawn Rutledge --- src/corelib/io/qfileselector.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'src') diff --git a/src/corelib/io/qfileselector.cpp b/src/corelib/io/qfileselector.cpp index ce06c8e00b..500b475d1d 100644 --- a/src/corelib/io/qfileselector.cpp +++ b/src/corelib/io/qfileselector.cpp @@ -228,7 +228,18 @@ QUrl QFileSelector::select(const QUrl &filePath) const QString selectedPath = d->select(equivalentPath); ret.setPath(selectedPath.remove(0, scheme.size())); } else { + // we need to store the original query and fragment, since toLocalFile() will strip it off + QString frag; + if (ret.hasFragment()) + frag = ret.fragment(); + QString query; + if (ret.hasQuery()) + query= ret.query(); ret = QUrl::fromLocalFile(d->select(ret.toLocalFile())); + if (!frag.isNull()) + ret.setFragment(frag); + if (!query.isNull()) + ret.setQuery(query); } return ret; } -- cgit v1.2.3 From ff4ef79e0ddc1310262d2db60e6f9ba51df9dc6c Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Fri, 24 May 2019 14:20:43 +0200 Subject: Overhaul Q(Date|Time)+ documentation Various things were out of date, misdescribed or just plain wrong. Change-Id: I11b7bd419604067fce2577a42882ebf126629016 Reviewed-by: Paul Wicking --- src/corelib/tools/qdatetime.cpp | 199 +++++++++++++++++++++------------------- 1 file changed, 103 insertions(+), 96 deletions(-) (limited to 'src') diff --git a/src/corelib/tools/qdatetime.cpp b/src/corelib/tools/qdatetime.cpp index 3cba786865..b0e443c3dc 100644 --- a/src/corelib/tools/qdatetime.cpp +++ b/src/corelib/tools/qdatetime.cpp @@ -326,19 +326,17 @@ static int fromOffsetString(const QStringRef &offsetString, bool *valid) Q_DECL_ \brief The QDate class provides date functions. - A QDate object encodes a calendar date, i.e. year, month, and day numbers, - in the proleptic Gregorian calendar by default. It can read the current date - from the system clock. It provides functions for comparing dates, and for - manipulating dates. For example, it is possible to add and subtract days, - months, and years to dates. + A QDate object represents a particular date. This can be expressed as a + calendar date, i.e. year, month, and day numbers, in the proleptic Gregorian + calendar. A QDate object is typically created by giving the year, month, and day - numbers explicitly. Note that QDate interprets two digit years as presented, - i.e., as years 0 through 99, without adding any offset. A QDate can also be - constructed with the static function currentDate(), which creates a QDate - object containing the system clock's date. An explicit date can also be set - using setDate(). The fromString() function returns a QDate given a string - and a date format which is used to interpret the date within the string. + numbers explicitly. Note that QDate interprets year numbers less than 100 as + presented, i.e., as years 1 through 99, without adding any offset. The + static function currentDate() creates a QDate object containing the date + read from the system clock. An explicit date can also be set using + setDate(). The fromString() function returns a QDate given a string and a + date format which is used to interpret the date within the string. The year(), month(), and day() functions provide access to the year, month, and day numbers. Also, dayOfWeek() and dayOfYear() @@ -372,7 +370,7 @@ static int fromOffsetString(const QStringRef &offsetString, bool *valid) Q_DECL_ every day in a contiguous range, with 24 November 4714 BCE in the Gregorian calendar being Julian Day 0 (1 January 4713 BCE in the Julian calendar). As well as being an efficient and accurate way of storing an absolute date, - it is suitable for converting a Date into other calendar systems such as + it is suitable for converting a date into other calendar systems such as Hebrew, Islamic or Chinese. The Julian Day number can be obtained using QDate::toJulianDay() and can be set using QDate::fromJulianDay(). @@ -1434,12 +1432,10 @@ bool QDate::isLeapYear(int y) Unlike QDateTime, QTime knows nothing about time zones or daylight-saving time (DST). - A QTime object is typically created either by giving the number - of hours, minutes, seconds, and milliseconds explicitly, or by - using the static function currentTime(), which creates a QTime - object that contains the system's local time. Note that the - accuracy depends on the accuracy of the underlying operating - system; not all systems provide 1-millisecond accuracy. + A QTime object is typically created either by giving the number of hours, + minutes, seconds, and milliseconds explicitly, or by using the static + function currentTime(), which creates a QTime object that represents the + system's local time. The hour(), minute(), second(), and msec() functions provide access to the number of hours, minutes, seconds, and milliseconds @@ -1899,6 +1895,12 @@ int QTime::msecsTo(const QTime &t) const Note that the accuracy depends on the accuracy of the underlying operating system; not all systems provide 1-millisecond accuracy. + + Furthermore, currentTime() only increases within each day; it shall drop by + 24 hours each time midnight passes; and, beside this, changes in it may not + correspond to elapsed time, if a daylight-saving transition intervenes. + + \sa QDateTime::currentDateTime(), QDateTime::curentDateTimeUtc() */ #if QT_CONFIG(datestring) @@ -3027,15 +3029,30 @@ inline qint64 QDateTimePrivate::zoneMSecsToEpochMSecs(qint64 zoneMSecs, const QT provides functions for comparing datetimes and for manipulating a datetime by adding a number of seconds, days, months, or years. - A QDateTime object is typically created either by giving a date - and time explicitly in the constructor, or by using the static - function currentDateTime() that returns a QDateTime object set - to the system clock's time. The date and time can be changed with - setDate() and setTime(). A datetime can also be set using the - setTime_t() function that takes a POSIX-standard "number of - seconds since 00:00:00 on January 1, 1970" value. The fromString() - function returns a QDateTime, given a string and a date format - used to interpret the date within the string. + QDateTime can describe datetimes with respect to \l{Qt::LocalTime}{local + time}, to \l{Qt::UTC}{UTC}, to a specified \l{{Qt::OffsetFromUTC}{offset + from UTC} or to a specified \l{{Qt::TimeZone}{time zone}, in conjunction + with the QTimeZone class. For example, a time zone of "Europe/Berlin" will + apply the daylight-saving rules as used in Germany since 1970. In contrast, + an offset from UTC of +3600 seconds is one hour ahead of UTC (usually + written in ISO standard notation as "UTC+01:00"), with no daylight-saving + offset or changes. When using either local time or a specified time zone, + time-zone transitions such as the starts and ends of daylight-saving time + (DST) are taken into account. The choice of system used to represent a + datetime is described as its "timespec". + + A QDateTime object is typically created either by giving a date and time + explicitly in the constructor, or by using a static function such as + currentDateTime() or fromMSecsSinceEpoch(). The date and time can be changed + with setDate() and setTime(). A datetime can also be set using the + setMSecsSinceEpoch() function that takes the time, in milliseconds, since + 00:00:00 on January 1, 1970. The fromString() function returns a QDateTime, + given a string and a date format used to interpret the date within the + string. + + QDateTime::currentDateTime() returns a QDateTime that expresses the current + time with respect to local time. QDateTime::currentDateTimeUtc() returns a + QDateTime that expresses the current time with respect to UTC. The date() and time() functions provide access to the date and time parts of the datetime. The same information is provided in @@ -3046,18 +3063,20 @@ inline qint64 QDateTimePrivate::zoneMSecsToEpochMSecs(qint64 zoneMSecs, const QT later. You can increment (or decrement) a datetime by a given number of - milliseconds using addMSecs(), seconds using addSecs(), or days - using addDays(). Similarly, you can use addMonths() and addYears(). - The daysTo() function returns the number of days between two datetimes, - secsTo() returns the number of seconds between two datetimes, and - msecsTo() returns the number of milliseconds between two datetimes. - - QDateTime can store datetimes as \l{Qt::LocalTime}{local time} or - as \l{Qt::UTC}{UTC}. QDateTime::currentDateTime() returns a - QDateTime expressed as local time; use toUTC() to convert it to - UTC. You can also use timeSpec() to find out if a QDateTime - object stores a UTC time or a local time. Operations such as - addSecs() and secsTo() are aware of daylight-saving time (DST). + milliseconds using addMSecs(), seconds using addSecs(), or days using + addDays(). Similarly, you can use addMonths() and addYears(). The daysTo() + function returns the number of days between two datetimes, secsTo() returns + the number of seconds between two datetimes, and msecsTo() returns the + number of milliseconds between two datetimes. These operations are aware of + daylight-saving time (DST) and other time-zone transitions, where + applicable. + + Use toTimeSpec() to express a datetime in local time or UTC, + toOffsetFromUtc() to express in terms of an offset from UTC, or toTimeZone() + to express it with respect to a general time zone. You can use timeSpec() to + find out what time-spec a QDateTime object stores its time relative to. When + that is Qt::TimeZone, you can use timeZone() to find out which zone it is + using. \note QDateTime does not account for leap seconds. @@ -3071,67 +3090,55 @@ inline qint64 QDateTimePrivate::zoneMSecsToEpochMSecs(qint64 zoneMSecs, const QT \section2 Range of Valid Dates - The range of valid values able to be stored in QDateTime is dependent on - the internal storage implementation. QDateTime is currently stored in a - qint64 as a serial msecs value encoding the date and time. This restricts - the date range to about +/- 292 million years, compared to the QDate range - of +/- 2 billion years. Care must be taken when creating a QDateTime with - extreme values that you do not overflow the storage. The exact range of - supported values varies depending on the Qt::TimeSpec and time zone. + The range of values that QDateTime can represent is dependent on the + internal storage implementation. QDateTime is currently stored in a qint64 + as a serial msecs value encoding the date and time. This restricts the date + range to about +/- 292 million years, compared to the QDate range of +/- 2 + billion years. Care must be taken when creating a QDateTime with extreme + values that you do not overflow the storage. The exact range of supported + values varies depending on the Qt::TimeSpec and time zone. + + \section2 Use of Timezones + + QDateTime uses the system's time zone information to determine the current + local time zone and its offset from UTC. If the system is not configured + correctly or not up-to-date, QDateTime will give wrong results. - \section2 Use of System Timezone + QDateTime likewise uses system-provided information to determine the offsets + of other timezones from UTC. If this information is incomplete or out of + date, QDateTime will give wrong results. See the QTimeZone documentation for + more details. - QDateTime uses the system's time zone information to determine the - offset of local time from UTC. If the system is not configured - correctly or not up-to-date, QDateTime will give wrong results as - well. + On modern Unix systems, this means QDateTime usually has accurate + information about historical transitions (including DST, see below) whenever + possible. On Windows, where the system doesn't support historical timezone + data, historical accuracy is not maintained with respect to timezone + transitions, notably including DST. \section2 Daylight-Saving Time (DST) - QDateTime takes into account the system's time zone information - when dealing with DST. On modern Unix systems, this means it - applies the correct historical DST data whenever possible. On - Windows, where the system doesn't support historical DST data, - historical accuracy is not maintained with respect to DST. - - The range of valid dates taking DST into account is 1970-01-01 to - the present, and rules are in place for handling DST correctly - until 2037-12-31, but these could change. For dates falling - outside that range, QDateTime makes a \e{best guess} using the - rules for year 1970 or 2037, but we can't guarantee accuracy. This - means QDateTime doesn't take into account changes in a locale's - time zone before 1970, even if the system's time zone database - supports that information. - - QDateTime takes into consideration the Standard Time to Daylight-Saving Time - transition. For example if the transition is at 2am and the clock goes - forward to 3am, then there is a "missing" hour from 02:00:00 to 02:59:59.999 - which QDateTime considers to be invalid. Any date maths performed - will take this missing hour into account and return a valid result. - - \section2 Offset From UTC - - A Qt::TimeSpec of Qt::OffsetFromUTC is also supported. This allows you - to define a QDateTime relative to UTC at a fixed offset of a given number - of seconds from UTC. For example, an offset of +3600 seconds is one hour - ahead of UTC and is usually written in ISO standard notation as - "UTC+01:00". Daylight-Saving Time never applies with this TimeSpec. - - There is no explicit size restriction to the offset seconds, but there is - an implicit limit imposed when using the toString() and fromString() - methods which use a format of [+|-]hh:mm, effectively limiting the range - to +/- 99 hours and 59 minutes and whole minutes only. Note that currently - no time zone lies outside the range of +/- 14 hours. - - \section2 Time Zone Support - - A Qt::TimeSpec of Qt::TimeZone is also supported in conjunction with the - QTimeZone class. This allows you to define a datetime in a named time zone - adhering to a consistent set of daylight-saving transition rules. For - example a time zone of "Europe/Berlin" will apply the daylight-saving - rules as used in Germany since 1970. Note that the transition rules - applied depend on the platform support. See the QTimeZone documentation - for more details. + QDateTime takes into account transitions between Standard Time and + Daylight-Saving Time. For example, if the transition is at 2am and the clock + goes forward to 3am, then there is a "missing" hour from 02:00:00 to + 02:59:59.999 which QDateTime considers to be invalid. Any date arithmetic + performed will take this missing hour into account and return a valid + result. For example, adding one minute to 01:59:59 will get 03:00:00. + + The range of valid dates taking DST into account is 1970-01-01 to the + present, and rules are in place for handling DST correctly until 2037-12-31, + but these could change. For dates falling outside that range, QDateTime + makes a \e{best guess} using the rules for year 1970 or 2037, but we can't + guarantee accuracy. This means QDateTime doesn't take into account changes + in a time zone before 1970, even if the system's time zone database provides + that information. + + \section2 Offsets From UTC + + There is no explicit size restriction on an offset from UTC, but there is an + implicit limit imposed when using the toString() and fromString() methods + which use a [+|-]hh:mm format, effectively limiting the range to +/- 99 + hours and 59 minutes and whole minutes only. Note that currently no time + zone lies outside the range of +/- 14 hours. \sa QDate, QTime, QDateTimeEdit, QTimeZone */ @@ -4254,7 +4261,7 @@ qint64 QDateTime::msecsTo(const QDateTime &other) const Example: \snippet code/src_corelib_tools_qdatetime.cpp 16 - \sa timeSpec(), toTimeZone(), toUTC(), toLocalTime() + \sa timeSpec(), toTimeZone(), toOffsetFromUtc() */ QDateTime QDateTime::toTimeSpec(Qt::TimeSpec spec) const -- cgit v1.2.3 From 4ebac33644d5db0b727680f1dacb18616daafe86 Mon Sep 17 00:00:00 2001 From: Damien Caliste Date: Mon, 20 May 2019 15:44:18 +0200 Subject: Detect system time zone from linked symlinks [ChangeLog][QtCore][QTimeZone] The IANA timezone database backend now properly follows symlinks even when they point to variable locations like /run or /var (useful when /etc is mounted read-only). Fixes: QTBUG-75936 Fixes: QTBUG-75527 Change-Id: If0dc2bfa20659e76c3bd062c75597a9ad01ad954 Reviewed-by: Edward Welbourne Reviewed-by: Thiago Macieira --- src/corelib/tools/qtimezoneprivate_tz.cpp | 43 +++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/corelib/tools/qtimezoneprivate_tz.cpp b/src/corelib/tools/qtimezoneprivate_tz.cpp index 7d85bc077d..57bc000af5 100644 --- a/src/corelib/tools/qtimezoneprivate_tz.cpp +++ b/src/corelib/tools/qtimezoneprivate_tz.cpp @@ -51,6 +51,12 @@ #include "qlocale_tools_p.h" #include +#include +#include +#if !defined(Q_OS_INTEGRITY) +#include // to use MAXSYMLINKS constant +#endif +#include // to use _SC_SYMLOOP_MAX constant QT_BEGIN_NAMESPACE @@ -1045,6 +1051,27 @@ QTimeZonePrivate::Data QTzTimeZonePrivate::previousTransition(qint64 beforeMSecs return last > m_tranTimes.cbegin() ? dataForTzTransition(*--last) : invalidData(); } +static long getSymloopMax() +{ +#if defined(SYMLOOP_MAX) + return SYMLOOP_MAX; // if defined, at runtime it can only be greater than this, so this is a safe bet +#else + errno = 0; + long result = sysconf(_SC_SYMLOOP_MAX); + if (result >= 0) + return result; + // result is -1, meaning either error or no limit + Q_ASSERT(!errno); // ... but it can't be an error, POSIX mandates _SC_SYMLOOP_MAX + + // therefore we can make up our own limit +# if defined(MAXSYMLINKS) + return MAXSYMLINKS; +# else + return 8; +# endif +#endif +} + // TODO Could cache the value and monitor the required files for any changes QByteArray QTzTimeZonePrivate::systemTimeZoneId() const { @@ -1062,12 +1089,18 @@ QByteArray QTzTimeZonePrivate::systemTimeZoneId() const // On most distros /etc/localtime is a symlink to a real file so extract name from the path if (ianaId.isEmpty()) { - const QString path = QFile::symLinkTarget(QStringLiteral("/etc/localtime")); - if (!path.isEmpty()) { + const QLatin1String zoneinfo("/zoneinfo/"); + QString path = QFile::symLinkTarget(QStringLiteral("/etc/localtime")); + int index = -1; + long iteration = getSymloopMax(); + // Symlink may point to another symlink etc. before being under zoneinfo/ + // We stop on the first path under /zoneinfo/, even if it is itself a + // symlink, like America/Montreal pointing to America/Toronto + while (iteration-- > 0 && !path.isEmpty() && (index = path.indexOf(zoneinfo)) < 0) + path = QFile::symLinkTarget(path); + if (index >= 0) { // /etc/localtime is a symlink to the current TZ file, so extract from path - int index = path.indexOf(QLatin1String("/zoneinfo/")); - if (index != -1) - ianaId = path.mid(index + 10).toUtf8(); + ianaId = path.mid(index + zoneinfo.size()).toUtf8(); } } -- cgit v1.2.3 From 804a18ef0ec4a054778e223c8cec6ee26dec3f76 Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Wed, 15 May 2019 13:42:16 +0200 Subject: Document that QHeaderView rendered with themed style might ignore roles While this is generally true for widgets, QHeaderView's documentation about how appearance related data roles are respected can be misleading. Fixes: QTBUG-31804 Change-Id: I93c6562e59ecf771d938d282723169202ac15bc2 Reviewed-by: Richard Moe Gustavsen --- src/widgets/itemviews/qheaderview.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/widgets/itemviews/qheaderview.cpp b/src/widgets/itemviews/qheaderview.cpp index 7bfa51337d..ed418f143c 100644 --- a/src/widgets/itemviews/qheaderview.cpp +++ b/src/widgets/itemviews/qheaderview.cpp @@ -146,7 +146,10 @@ static const int maxSizeSection = 1048575; // since section size is in a bitfiel Not all \l{Qt::}{ItemDataRole}s will have an effect on a QHeaderView. If you need to draw other roles, you can subclass QHeaderView and reimplement \l{QHeaderView::}{paintEvent()}. - QHeaderView respects the following item data roles: + QHeaderView respects the following item data roles, unless they are + in conflict with the style (which can happen for styles that follow + the desktop theme): + \l{Qt::}{TextAlignmentRole}, \l{Qt::}{DisplayRole}, \l{Qt::}{FontRole}, \l{Qt::}{DecorationRole}, \l{Qt::}{ForegroundRole}, and \l{Qt::}{BackgroundRole}. -- cgit v1.2.3 From 2dfe0f8f6854f518e8ffb23e17cc99400355cc3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Wed, 29 May 2019 17:00:40 +0200 Subject: macOS: Introduce QMacKeyValueObserver and use in theme and style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I2d21c883628933543ae5a66b694ff7503119bc4a Reviewed-by: Tor Arne Vestbø --- src/corelib/kernel/qcore_mac_objc.mm | 31 +++++++++++++++++ src/corelib/kernel/qcore_mac_p.h | 55 +++++++++++++++++++++++++++++ src/plugins/platforms/cocoa/qcocoatheme.h | 2 +- src/plugins/platforms/cocoa/qcocoatheme.mm | 56 +++++------------------------- src/plugins/styles/mac/qmacstyle_mac.mm | 55 ++++------------------------- src/plugins/styles/mac/qmacstyle_mac_p_p.h | 4 +-- 6 files changed, 105 insertions(+), 98 deletions(-) (limited to 'src') diff --git a/src/corelib/kernel/qcore_mac_objc.mm b/src/corelib/kernel/qcore_mac_objc.mm index 4550891e2a..9139b372a8 100644 --- a/src/corelib/kernel/qcore_mac_objc.mm +++ b/src/corelib/kernel/qcore_mac_objc.mm @@ -514,5 +514,36 @@ Q_CONSTRUCTOR_FUNCTION(qt_apple_check_os_version); // ------------------------------------------------------------------------- +void QMacKeyValueObserver::addObserver() +{ + [object addObserver:observer forKeyPath:keyPath + options:NSKeyValueObservingOptionNew context:callback.get()]; +} + +void QMacKeyValueObserver::removeObserver() { + if (object) + [object removeObserver:observer forKeyPath:keyPath context:callback.get()]; + object = nil; +} + +KeyValueObserver *QMacKeyValueObserver::observer = [[KeyValueObserver alloc] init]; + +QT_END_NAMESPACE +@implementation KeyValueObserver +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object + change:(NSDictionary *)change context:(void *)context +{ + Q_UNUSED(keyPath); + Q_UNUSED(object); + Q_UNUSED(change); + + (*reinterpret_cast(context))(); +} +@end +QT_BEGIN_NAMESPACE + +// ------------------------------------------------------------------------- + + QT_END_NAMESPACE diff --git a/src/corelib/kernel/qcore_mac_p.h b/src/corelib/kernel/qcore_mac_p.h index 2428faed15..8f4c96bbd6 100644 --- a/src/corelib/kernel/qcore_mac_p.h +++ b/src/corelib/kernel/qcore_mac_p.h @@ -67,6 +67,7 @@ #ifdef __OBJC__ #include +#include #endif #include "qstring.h" @@ -334,6 +335,60 @@ public: private: id observer = nil; }; + +QT_END_NAMESPACE +@interface QT_MANGLE_NAMESPACE(KeyValueObserver) : NSObject +@end +QT_NAMESPACE_ALIAS_OBJC_CLASS(KeyValueObserver); +QT_BEGIN_NAMESPACE + +class Q_CORE_EXPORT QMacKeyValueObserver +{ +public: + using Callback = std::function; + + QMacKeyValueObserver() {} + + // Note: QMacKeyValueObserver must not outlive the object observed! + QMacKeyValueObserver(id object, NSString *keyPath, Callback callback) + : object(object), keyPath(keyPath), callback(new Callback(callback)) { addObserver(); } + + QMacKeyValueObserver(const QMacKeyValueObserver &other) + : QMacKeyValueObserver(other.object, other.keyPath, *other.callback.get()) {} + + QMacKeyValueObserver(QMacKeyValueObserver &&other) { swap(other, *this); } + + ~QMacKeyValueObserver() { removeObserver(); } + + QMacKeyValueObserver &operator=(const QMacKeyValueObserver &other) { + QMacKeyValueObserver tmp(other); + swap(tmp, *this); + return *this; + } + + QMacKeyValueObserver &operator=(QMacKeyValueObserver &&other) { + QMacKeyValueObserver tmp(std::move(other)); + swap(tmp, *this); + return *this; + } + + void removeObserver(); + +private: + void swap(QMacKeyValueObserver &first, QMacKeyValueObserver &second) { + std::swap(first.object, second.object); + std::swap(first.keyPath, second.keyPath); + std::swap(first.callback, second.callback); + } + + void addObserver(); + + id object = nil; + NSString *keyPath = nullptr; + std::unique_ptr callback; + + static KeyValueObserver *observer; +}; #endif // ------------------------------------------------------------------------- diff --git a/src/plugins/platforms/cocoa/qcocoatheme.h b/src/plugins/platforms/cocoa/qcocoatheme.h index 63227ed6c1..788b616e78 100644 --- a/src/plugins/platforms/cocoa/qcocoatheme.h +++ b/src/plugins/platforms/cocoa/qcocoatheme.h @@ -87,7 +87,7 @@ private: QMacNotificationObserver m_systemColorObserver; mutable QHash m_palettes; mutable QHash m_fonts; - QT_MANGLE_NAMESPACE(QCocoaThemeAppAppearanceObserver) *m_appearanceObserver; + QMacKeyValueObserver m_appearanceObserver; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoatheme.mm b/src/plugins/platforms/cocoa/qcocoatheme.mm index ba93560689..7c10456824 100644 --- a/src/plugins/platforms/cocoa/qcocoatheme.mm +++ b/src/plugins/platforms/cocoa/qcocoatheme.mm @@ -80,57 +80,22 @@ #include -#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14) -@interface QT_MANGLE_NAMESPACE(QCocoaThemeAppAppearanceObserver) : NSObject -@property (readonly, nonatomic) QCocoaTheme *theme; -- (instancetype)initWithTheme:(QCocoaTheme *)theme; -@end - -QT_NAMESPACE_ALIAS_OBJC_CLASS(QCocoaThemeAppAppearanceObserver); - -@implementation QCocoaThemeAppAppearanceObserver -- (instancetype)initWithTheme:(QCocoaTheme *)theme -{ - if ((self = [super init])) { - _theme = theme; - [NSApp addObserver:self forKeyPath:@"effectiveAppearance" options:NSKeyValueObservingOptionNew context:nullptr]; - } - return self; -} - -- (void)dealloc -{ - [NSApp removeObserver:self forKeyPath:@"effectiveAppearance"]; - [super dealloc]; -} - -- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object - change:(NSDictionary *)change context:(void *)context -{ - Q_UNUSED(change); - Q_UNUSED(context); - - Q_ASSERT(object == NSApp); - Q_ASSERT([keyPath isEqualToString:@"effectiveAppearance"]); - - if (__builtin_available(macOS 10.14, *)) - NSAppearance.currentAppearance = NSApp.effectiveAppearance; - - self.theme->handleSystemThemeChange(); -} -@end -#endif // QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14) - QT_BEGIN_NAMESPACE const char *QCocoaTheme::name = "cocoa"; QCocoaTheme::QCocoaTheme() - : m_systemPalette(nullptr), m_appearanceObserver(nil) + : m_systemPalette(nullptr) { #if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14) - if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave) - m_appearanceObserver = [[QCocoaThemeAppAppearanceObserver alloc] initWithTheme:this]; + if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave) { + m_appearanceObserver = QMacKeyValueObserver(NSApp, @"effectiveAppearance", [this] { + if (__builtin_available(macOS 10.14, *)) + NSAppearance.currentAppearance = NSApp.effectiveAppearance; + + handleSystemThemeChange(); + }); + } #endif m_systemColorObserver = QMacNotificationObserver(nil, @@ -141,9 +106,6 @@ QCocoaTheme::QCocoaTheme() QCocoaTheme::~QCocoaTheme() { - if (m_appearanceObserver) - [m_appearanceObserver release]; - reset(); qDeleteAll(m_fonts); } diff --git a/src/plugins/styles/mac/qmacstyle_mac.mm b/src/plugins/styles/mac/qmacstyle_mac.mm index f88e3203bc..c1f88149bb 100644 --- a/src/plugins/styles/mac/qmacstyle_mac.mm +++ b/src/plugins/styles/mac/qmacstyle_mac.mm @@ -144,41 +144,6 @@ static QWindow *qt_getWindow(const QWidget *widget) return widget ? widget->window()->windowHandle() : 0; } -@interface QT_MANGLE_NAMESPACE(NotificationReceiver) : NSObject -@end - -QT_NAMESPACE_ALIAS_OBJC_CLASS(NotificationReceiver); - -@implementation NotificationReceiver -{ - QMacStylePrivate *privateStyle; -} - -- (instancetype)initWithPrivateStyle:(QMacStylePrivate *)style -{ - if (self = [super init]) - privateStyle = style; - return self; -} - -- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object - change:(NSDictionary *)change context:(void *)context -{ - Q_UNUSED(keyPath); - Q_UNUSED(object); - Q_UNUSED(change); - Q_UNUSED(context); - - Q_ASSERT([keyPath isEqualToString:@"effectiveAppearance"]); - Q_ASSERT(object == NSApp); - - for (NSView *b : privateStyle->cocoaControls) - [b release]; - privateStyle->cocoaControls.clear(); -} - -@end - @interface QT_MANGLE_NAMESPACE(QIndeterminateProgressIndicator) : NSProgressIndicator @property (readonly, nonatomic) NSInteger animators; @@ -2080,7 +2045,6 @@ void QMacStylePrivate::resolveCurrentNSView(QWindow *window) const QMacStyle::QMacStyle() : QCommonStyle(*new QMacStylePrivate) { - Q_D(QMacStyle); QMacAutoReleasePool pool; static QMacNotificationObserver scrollbarStyleObserver(nil, @@ -2093,26 +2057,21 @@ QMacStyle::QMacStyle() QCoreApplication::sendEvent(o, &event); }); - d->receiver = [[NotificationReceiver alloc] initWithPrivateStyle:d]; - #if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14) + Q_D(QMacStyle); + // FIXME: Tie this logic into theme change, or even polish/unpolish if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave) { - [NSApplication.sharedApplication addObserver:d->receiver forKeyPath:@"effectiveAppearance" - options:NSKeyValueObservingOptionNew context:nullptr]; + d->appearanceObserver = QMacKeyValueObserver(NSApp, @"effectiveAppearance", [&d] { + for (NSView *b : d->cocoaControls) + [b release]; + d->cocoaControls.clear(); + }); } #endif } QMacStyle::~QMacStyle() { - Q_D(QMacStyle); - QMacAutoReleasePool pool; - -#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14) - if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave) - [NSApplication.sharedApplication removeObserver:d->receiver forKeyPath:@"effectiveAppearance"]; -#endif - [d->receiver release]; } void QMacStyle::polish(QPalette &) diff --git a/src/plugins/styles/mac/qmacstyle_mac_p_p.h b/src/plugins/styles/mac/qmacstyle_mac_p_p.h index dd99cf4bb5..6b3f525adc 100644 --- a/src/plugins/styles/mac/qmacstyle_mac_p_p.h +++ b/src/plugins/styles/mac/qmacstyle_mac_p_p.h @@ -149,7 +149,6 @@ Q_FORWARD_DECLARE_MUTABLE_CG_TYPE(CGContext); Q_FORWARD_DECLARE_OBJC_CLASS(NSView); Q_FORWARD_DECLARE_OBJC_CLASS(NSCell); -Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(NotificationReceiver)); QT_BEGIN_NAMESPACE @@ -288,13 +287,14 @@ public: static QVector > scrollBars; mutable QPointer focusWidget; - QT_MANGLE_NAMESPACE(NotificationReceiver) *receiver; mutable NSView *backingStoreNSView; mutable QHash cocoaControls; mutable QHash cocoaCells; QFont smallSystemFont; QFont miniSystemFont; + + QMacKeyValueObserver appearanceObserver; }; QT_END_NAMESPACE -- cgit v1.2.3 From 7ff7d73f6ac68227500fa95731ce0f04626e600b Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Fri, 7 Jun 2019 11:40:35 +0200 Subject: moc: Fix indentation of generated code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clang warns about misleading indentation when parsing moc-generated files. Amends 4ed39bed4e119792a8da9445691ba16d5beac30a. Change-Id: Ie8c5b38a28316cb2541304eb712ad2ca60be0e42 Reviewed-by: Jörg Bornemann --- src/tools/moc/generator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp index fd76646f4c..12ffd6ae95 100644 --- a/src/tools/moc/generator.cpp +++ b/src/tools/moc/generator.cpp @@ -1001,7 +1001,7 @@ void Generator::generateMetacall() needUser |= p.user.endsWith(')'); } - fprintf(out, "\n#ifndef QT_NO_PROPERTIES\n "); + fprintf(out, "\n#ifndef QT_NO_PROPERTIES\n "); if (needElse) fprintf(out, "else "); fprintf(out, -- cgit v1.2.3 From 7fc67e09f3f0c6c14392702efb01bd861a410fb7 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 5 Jun 2019 15:31:31 +0200 Subject: doc: Correct several QEnterEvent accessors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's cursor position not widget position, pos() is relative to the widget, and 3 of these accessors return QPointF. Amends e6ddae07e1e571a7a6e0c531b961dbddcd217643. Task-number: QTBUG-36985 Change-Id: Ide437f7496824f8cdd0d03fa38ad7b573e30feaa Reviewed-by: Jan Arve Sæther --- src/gui/kernel/qevent.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index 7123c4d8ba..e7a320f3a4 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -97,35 +97,35 @@ QEnterEvent::~QEnterEvent() /*! \fn QPoint QEnterEvent::globalPos() const - Returns the global position of the widget \e{at the time of the event}. + Returns the global position of the mouse cursor \e{at the time of the event}. */ /*! \fn int QEnterEvent::globalX() const - Returns the global position on the X-axis of the mouse cursor relative to the the widget. + Returns the global position on the X-axis of the mouse cursor \e{at the time of the event}. */ /*! \fn int QEnterEvent::globalY() const - Returns the global position on the Y-axis of the mouse cursor relative to the the widget. + Returns the global position on the Y-axis of the mouse cursor \e{at the time of the event}. */ /*! - \fn QPoint QEnterEvent::localPos() const + \fn QPointF QEnterEvent::localPos() const Returns the mouse cursor's position relative to the receiving widget. */ /*! \fn QPoint QEnterEvent::pos() const - Returns the position of the mouse cursor in global screen coordinates. + Returns the position of the mouse cursor relative to the receiving widget. */ /*! - \fn QPoint QEnterEvent::screenPos() const + \fn QPointF QEnterEvent::screenPos() const Returns the position of the mouse cursor relative to the receiving screen. */ /*! - \fn QPoint QEnterEvent::windowPos() const + \fn QPointF QEnterEvent::windowPos() const Returns the position of the mouse cursor relative to the receiving window. */ -- cgit v1.2.3 From a4f79313f065815451610dabdbbb33b8a35f0a86 Mon Sep 17 00:00:00 2001 From: Eirik Aavitsland Date: Mon, 3 Jun 2019 14:45:07 +0200 Subject: Fix: QTextDocument::find backward search crossing paragraphs If the start position of a backward string search was the at the start of a paragraph, the code would start searching at an illegal position (at the eol character) in the preceding paragraph. That caused that whole paragraph to be skipped, so any matches there would not be found. Fix by making sure the search starts at legal position. Fixes: QTBUG-48035 Change-Id: Id6c0159b6613ec75ec617a0a57096ceef2b4cbd0 Reviewed-by: Konstantin Ritt --- src/gui/text/qtextdocument.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp index 4f187c6701..c2020f3f86 100644 --- a/src/gui/text/qtextdocument.cpp +++ b/src/gui/text/qtextdocument.cpp @@ -1358,6 +1358,8 @@ QTextCursor QTextDocument::find(const QString &subString, int from, FindFlags op blockOffset = 0; } } else { + if (blockOffset == block.length() - 1) + --blockOffset; // make sure to skip end-of-paragraph character while (block.isValid()) { if (findInBlock(block, subString, blockOffset, options, &cursor)) return cursor; -- cgit v1.2.3 From 83dccf00ccf75da27551b68652d5c3c1b4f7ebff Mon Sep 17 00:00:00 2001 From: Eirik Aavitsland Date: Thu, 11 Apr 2019 15:29:44 +0200 Subject: Fix printing of table headers in multi-frame QTextDocuments The calculation of page position of table headers would only work correctly for tables in the root frame of a QTextDocument. Fix by including the relative positions of subframes. Fixes: QTBUG-59000 Change-Id: I2cc7e21bddf806f7f5f9b0675ac014c339ba2453 Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/gui/text/qtextdocumentlayout.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/gui/text/qtextdocumentlayout.cpp b/src/gui/text/qtextdocumentlayout.cpp index bed0a2c450..cf02e2f9c8 100644 --- a/src/gui/text/qtextdocumentlayout.cpp +++ b/src/gui/text/qtextdocumentlayout.cpp @@ -970,8 +970,14 @@ void QTextDocumentLayoutPrivate::drawFrame(const QPointF &offset, QPainter *pain if (pageHeight <= 0) pageHeight = QFIXED_MAX; - const int tableStartPage = (td->position.y / pageHeight).truncate(); - const int tableEndPage = ((td->position.y + td->size.height) / pageHeight).truncate(); + QFixed absYPos = td->position.y; + QTextFrame *parentFrame = table->parentFrame(); + while (parentFrame) { + absYPos += data(parentFrame)->position.y; + parentFrame = parentFrame->parentFrame(); + } + const int tableStartPage = (absYPos / pageHeight).truncate(); + const int tableEndPage = ((absYPos + td->size.height) / pageHeight).truncate(); qreal border = td->border.toReal(); drawFrameDecoration(painter, frame, fd, context.clip, frameRect); -- cgit v1.2.3 From ac68ef1efc27669e9c8fc255298345d72d198d68 Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Fri, 3 May 2019 13:45:53 +0200 Subject: Don't duplicate font family names in the fallback list Fixes: QTBUG-75333 Change-Id: Iaaf4b13d50c6b9b52e629b81d5e9cbc552a0202c Reviewed-by: Allan Sandfeld Jensen --- src/gui/text/qfontdatabase.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/gui/text/qfontdatabase.cpp b/src/gui/text/qfontdatabase.cpp index fa9573441a..6dad5196e8 100644 --- a/src/gui/text/qfontdatabase.cpp +++ b/src/gui/text/qfontdatabase.cpp @@ -707,7 +707,8 @@ static QStringList familyList(const QFontDef &req) if ((str.startsWith(QLatin1Char('"')) && str.endsWith(QLatin1Char('"'))) || (str.startsWith(QLatin1Char('\'')) && str.endsWith(QLatin1Char('\'')))) str = str.mid(1, str.length() - 2); - family_list << str.toString(); + if (!family_list.contains(str)) + family_list << str.toString(); } } // append the substitute list for each family in family_list -- cgit v1.2.3 From 9c196b40d10f992b5ecc71f2e0093cff2308ed88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Tue, 7 May 2019 23:26:28 +0200 Subject: wasm: remove qDebug Change-Id: Ic9812421b2a79a33bb138f448fe132dab141b724 Reviewed-by: Lorn Potter --- src/plugins/platforms/wasm/qwasmeventtranslator.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp index 6a02a457a0..7126c00354 100644 --- a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp +++ b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp @@ -337,8 +337,6 @@ QWasmEventTranslator::QWasmEventTranslator(QWasmScreen *screen) void QWasmEventTranslator::initEventHandlers() { - qDebug() << "QWasmEventTranslator::initEventHandlers"; - QByteArray _canvasId = screen()->canvasId().toUtf8(); const char *canvasId = _canvasId.constData(); -- cgit v1.2.3 From 0b693175e4e70cead730a245cffa66304998196a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Sun, 7 Apr 2019 12:32:51 +0200 Subject: wasm: make idealThreadCount() work on all threads navigator.hardwareConcurrency can be accessed from the main thread only. Read and cache the value on QCoreApplication initialization. Change-Id: I731f7f356ce106c7107977783d4b763326af06b6 Reviewed-by: Lorn Potter --- src/corelib/kernel/qcoreapplication.cpp | 5 +++++ src/corelib/thread/qthread_p.h | 3 +++ src/corelib/thread/qthread_unix.cpp | 10 +++++----- 3 files changed, 13 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index 69b2a9bf41..5c0bf93acc 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -119,6 +119,7 @@ #ifdef Q_OS_WASM #include +#include #endif #ifdef QT_BOOTSTRAPPED @@ -800,6 +801,10 @@ void QCoreApplicationPrivate::init() Module.print(err); }); ); + +#if QT_CONFIG(thread) + QThreadPrivate::idealThreadCount = emscripten::val::global("navigator")["hardwareConcurrency"].as(); +#endif #endif // Store app name/version (so they're still available after QCoreApplication is destroyed) diff --git a/src/corelib/thread/qthread_p.h b/src/corelib/thread/qthread_p.h index 7d9442ab79..57e6c995c5 100644 --- a/src/corelib/thread/qthread_p.h +++ b/src/corelib/thread/qthread_p.h @@ -195,6 +195,9 @@ public: int waiters; bool terminationEnabled, terminatePending; #endif // Q_OS_WIN +#ifdef Q_OS_WASM + static int idealThreadCount; +#endif QThreadData *data; static QAbstractEventDispatcher *createEventDispatcher(QThreadData *data); diff --git a/src/corelib/thread/qthread_unix.cpp b/src/corelib/thread/qthread_unix.cpp index a13f8ca215..ea78b0a147 100644 --- a/src/corelib/thread/qthread_unix.cpp +++ b/src/corelib/thread/qthread_unix.cpp @@ -100,10 +100,6 @@ #include #endif -#if defined(Q_OS_WASM) -#include -#endif - QT_BEGIN_NAMESPACE #if QT_CONFIG(thread) @@ -454,6 +450,10 @@ Qt::HANDLE QThread::currentThreadId() Q_DECL_NOTHROW # define _SC_NPROCESSORS_ONLN 84 #endif +#ifdef Q_OS_WASM +int QThreadPrivate::idealThreadCount = 1; +#endif + int QThread::idealThreadCount() Q_DECL_NOTHROW { int cores = 1; @@ -503,7 +503,7 @@ int QThread::idealThreadCount() Q_DECL_NOTHROW cores = 1; # endif #elif defined(Q_OS_WASM) - cores = emscripten::val::global("navigator")["hardwareConcurrency"].as(); + cores = QThreadPrivate::idealThreadCount; #else // the rest: Linux, Solaris, AIX, Tru64 cores = (int)sysconf(_SC_NPROCESSORS_ONLN); -- cgit v1.2.3 From fc3e8514144535db22c431251bc0feea99cf72e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Thu, 11 Apr 2019 11:44:36 +0200 Subject: macOS: disable threaded OpenGL (unconditionally) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can’t determine when the app will use layer-backed views without having access to the QWindow and NSView instance, so make the conservative choice of always returning false for ThreadedOpenGL. Task-number: QTBUG-74820 Change-Id: If2779b17eead78ce1929ccebc3bd8fc0eb00c4b5 Reviewed-by: Tor Arne Vestbø --- src/plugins/platforms/cocoa/qcocoaintegration.mm | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index fb3d05d3e4..855b42657d 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -347,11 +347,13 @@ bool QCocoaIntegration::hasCapability(QPlatformIntegration::Capability cap) cons switch (cap) { #ifndef QT_NO_OPENGL case ThreadedOpenGL: - // AppKit expects rendering to happen on the main thread, and we can - // easily end up in situations where rendering on secondary threads - // will result in visual artifacts, bugs, or even deadlocks, when - // building with SDK 10.14 or higher which enbles view layer-backing. - return QMacVersion::buildSDK() < QOperatingSystemVersion(QOperatingSystemVersion::MacOSMojave); + // Qt's threaded OpenGL implementation does not work well for layer-backed + // views, where we can easily end up in situations where rendering on secondary + // threads will result in visual artifacts, bugs, or even deadlocks. It is + // not possible to determine here if the the app will be using layer-backed + // views (it can opt-in using SDK 10.14+ on macOS 10.4+, or by setting + // NSWindow flags such as NSWindowStyleMaskFullSizeContentView). + return false; case OpenGL: case BufferQueueingOpenGL: #endif -- cgit v1.2.3 From 22c3c55de44b9b900b5e4d7040a9c3f906d0bfa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Fri, 26 Apr 2019 15:33:49 +0200 Subject: wasm: install one browser window resize handler Install one browser window resize handler instead of per-canvas resize handlers, which avoids having to uninstall on QScreen destruction. Task-number: QTBUG-75463 Change-Id: I8345262a906ed735f8e9e146f1e963f515cf0d25 Reviewed-by: Lorn Potter --- .../platforms/wasm/qwasmeventtranslator.cpp | 18 ------------------ src/plugins/platforms/wasm/qwasmeventtranslator.h | 2 -- src/plugins/platforms/wasm/qwasmintegration.cpp | 22 ++++++++++++++++++++++ src/plugins/platforms/wasm/qwasmintegration.h | 1 + 4 files changed, 23 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp index 7126c00354..f586c5378e 100644 --- a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp +++ b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp @@ -375,9 +375,6 @@ void QWasmEventTranslator::initEventHandlers() emscripten_set_touchend_callback(canvasId, (void *)this, 1, &touchCallback); emscripten_set_touchmove_callback(canvasId, (void *)this, 1, &touchCallback); emscripten_set_touchcancel_callback(canvasId, (void *)this, 1, &touchCallback); - - emscripten_set_resize_callback(nullptr, (void *)this, 1, uiEvent_cb); // Note: handles browser window resize - } template @@ -909,19 +906,4 @@ bool QWasmEventTranslator::processKeyboard(int eventType, const EmscriptenKeyboa return accepted; } -int QWasmEventTranslator::uiEvent_cb(int eventType, const EmscriptenUiEvent *e, void *userData) -{ - Q_UNUSED(e) - QWasmEventTranslator *eventTranslator = static_cast(userData); - - if (eventType == EMSCRIPTEN_EVENT_RESIZE) { - // This resize event is called when the HTML window is resized. Depending - // on the page layout the the canvas might also have been resized, so we - // update the Qt screen size (and canvas render size). - eventTranslator->screen()->updateQScreenAndCanvasRenderSize(); - } - - return 0; -} - QT_END_NAMESPACE diff --git a/src/plugins/platforms/wasm/qwasmeventtranslator.h b/src/plugins/platforms/wasm/qwasmeventtranslator.h index d6043072ba..1655b7226a 100644 --- a/src/plugins/platforms/wasm/qwasmeventtranslator.h +++ b/src/plugins/platforms/wasm/qwasmeventtranslator.h @@ -57,8 +57,6 @@ public: static int touchCallback(int eventType, const EmscriptenTouchEvent *ev, void *userData); - static int uiEvent_cb(int eventType, const EmscriptenUiEvent *e, void *userData); - void processEvents(); void initEventHandlers(); int handleTouch(int eventType, const EmscriptenTouchEvent *touchEvent); diff --git a/src/plugins/platforms/wasm/qwasmintegration.cpp b/src/plugins/platforms/wasm/qwasmintegration.cpp index e601d553f2..703b0fde0b 100644 --- a/src/plugins/platforms/wasm/qwasmintegration.cpp +++ b/src/plugins/platforms/wasm/qwasmintegration.cpp @@ -115,6 +115,21 @@ QWasmIntegration::QWasmIntegration() } emscripten::val::global("window").set("onbeforeunload", val::module_property("qtBrowserBeforeUnload")); + + // install browser window resize handler + auto onWindowResize = [](int eventType, const EmscriptenUiEvent *e, void *userData) -> int { + Q_UNUSED(eventType); + Q_UNUSED(e); + Q_UNUSED(userData); + + // This resize event is called when the HTML window is resized. Depending + // on the page layout the the canvas(es) might also have been resized, so we + // update the Qt screen sizes (and canvas render sizes). + if (QWasmIntegration *integration = QWasmIntegration::get()) + integration->resizeAllScreens(); + return 0; + }; + emscripten_set_resize_callback(nullptr, nullptr, 1, onWindowResize); } QWasmIntegration::~QWasmIntegration() @@ -245,4 +260,11 @@ void QWasmIntegration::resizeScreen(const QString &canvasId) m_screens.value(canvasId)->updateQScreenAndCanvasRenderSize(); } +void QWasmIntegration::resizeAllScreens() +{ + qDebug() << "resizeAllScreens"; + for (QWasmScreen *screen : m_screens) + screen->updateQScreenAndCanvasRenderSize(); +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/wasm/qwasmintegration.h b/src/plugins/platforms/wasm/qwasmintegration.h index 11d8d0f7f5..d5011db067 100644 --- a/src/plugins/platforms/wasm/qwasmintegration.h +++ b/src/plugins/platforms/wasm/qwasmintegration.h @@ -81,6 +81,7 @@ public: void addScreen(const QString &canvasId); void removeScreen(const QString &canvasId); void resizeScreen(const QString &canvasId); + void resizeAllScreens(); private: mutable QWasmFontDatabase *m_fontDb; -- cgit v1.2.3 From 6d7c7a5ddafc8005969d4d99dc0c92b49ac4bcb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Sat, 27 Apr 2019 00:27:19 +0200 Subject: wasm: prevent crash on null backingstore pointer We can get draw calls in between creating the platform window and the platform backing store. Task-number: QTBUG-75463 Change-Id: If0b67d40fac84e466f204ec23a267aa4c6121cbd Reviewed-by: Lorn Potter --- src/plugins/platforms/wasm/qwasmcompositor.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/plugins/platforms/wasm/qwasmcompositor.cpp b/src/plugins/platforms/wasm/qwasmcompositor.cpp index e6a69c4814..b3234197d0 100644 --- a/src/plugins/platforms/wasm/qwasmcompositor.cpp +++ b/src/plugins/platforms/wasm/qwasmcompositor.cpp @@ -251,6 +251,8 @@ void QWasmCompositor::blit(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, void QWasmCompositor::drawWindowContent(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window) { QWasmBackingStore *backingStore = window->backingStore(); + if (!backingStore) + return; QOpenGLTexture const *texture = backingStore->getUpdatedTexture(); -- cgit v1.2.3 From 00de44701d1496377632681c5228af4abfea0033 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Sat, 27 Apr 2019 00:30:20 +0200 Subject: wasm: use correct coordinates when blitting The target position is the position on the canvas, not the global position on the page. Task-number: QTBUG-75463 Change-Id: I4ea2c9afacd2065fa975f6fa2e6a93d98f637854 Reviewed-by: Lorn Potter --- src/plugins/platforms/wasm/qwasmcompositor.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/wasm/qwasmcompositor.cpp b/src/plugins/platforms/wasm/qwasmcompositor.cpp index b3234197d0..9d99c21195 100644 --- a/src/plugins/platforms/wasm/qwasmcompositor.cpp +++ b/src/plugins/platforms/wasm/qwasmcompositor.cpp @@ -255,8 +255,9 @@ void QWasmCompositor::drawWindowContent(QOpenGLTextureBlitter *blitter, QWasmScr return; QOpenGLTexture const *texture = backingStore->getUpdatedTexture(); - - blit(blitter, screen, texture, window->geometry()); + QPoint windowCanvasPosition = window->geometry().topLeft() - screen->geometry().topLeft(); + QRect windowCanvasGeometry = QRect(windowCanvasPosition, window->geometry().size()); + blit(blitter, screen, texture, windowCanvasGeometry); } QPalette QWasmCompositor::makeWindowPalette() -- cgit v1.2.3 From 4e0f26289244d587714abf8adda932cce5697925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Mon, 29 Apr 2019 01:11:25 +0200 Subject: wasm: add QWasmOffscreenSurface This no-op implementation is sufficient to support OpenGL cleanup use cases, where the OpenGL context needs to be made current at times where we don't have a window available. A specific requirement on WebAssembly is that the context is tied to one specific screen; which is an extra requirement on QWasmOffscreenSurface, compared to the other platforms. Task-number: QTBUG-75463 Change-Id: Ie3658cb235bf342be66f19dfe981e3a56a90e1b6 Reviewed-by: Lorn Potter --- src/plugins/platforms/wasm/qwasmintegration.cpp | 6 +++ src/plugins/platforms/wasm/qwasmintegration.h | 1 + .../platforms/wasm/qwasmoffscreensurface.cpp | 41 ++++++++++++++++++ src/plugins/platforms/wasm/qwasmoffscreensurface.h | 49 ++++++++++++++++++++++ src/plugins/platforms/wasm/wasm.pro | 6 ++- 5 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 src/plugins/platforms/wasm/qwasmoffscreensurface.cpp create mode 100644 src/plugins/platforms/wasm/qwasmoffscreensurface.h (limited to 'src') diff --git a/src/plugins/platforms/wasm/qwasmintegration.cpp b/src/plugins/platforms/wasm/qwasmintegration.cpp index 703b0fde0b..0d22d31c25 100644 --- a/src/plugins/platforms/wasm/qwasmintegration.cpp +++ b/src/plugins/platforms/wasm/qwasmintegration.cpp @@ -35,6 +35,7 @@ #include "qwasmtheme.h" #include "qwasmclipboard.h" #include "qwasmservices.h" +#include "qwasmoffscreensurface.h" #include "qwasmwindow.h" #ifndef QT_NO_OPENGL @@ -188,6 +189,11 @@ QPlatformOpenGLContext *QWasmIntegration::createPlatformOpenGLContext(QOpenGLCon } #endif +QPlatformOffscreenSurface *QWasmIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const +{ + return new QWasmOffscrenSurface(surface); +} + QPlatformFontDatabase *QWasmIntegration::fontDatabase() const { if (m_fontDb == nullptr) diff --git a/src/plugins/platforms/wasm/qwasmintegration.h b/src/plugins/platforms/wasm/qwasmintegration.h index d5011db067..dc1cc72382 100644 --- a/src/plugins/platforms/wasm/qwasmintegration.h +++ b/src/plugins/platforms/wasm/qwasmintegration.h @@ -65,6 +65,7 @@ public: #ifndef QT_NO_OPENGL QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const override; #endif + QPlatformOffscreenSurface *createPlatformOffscreenSurface(QOffscreenSurface *surface) const override; QPlatformFontDatabase *fontDatabase() const override; QAbstractEventDispatcher *createEventDispatcher() const override; QVariant styleHint(QPlatformIntegration::StyleHint hint) const override; diff --git a/src/plugins/platforms/wasm/qwasmoffscreensurface.cpp b/src/plugins/platforms/wasm/qwasmoffscreensurface.cpp new file mode 100644 index 0000000000..a205e5ddea --- /dev/null +++ b/src/plugins/platforms/wasm/qwasmoffscreensurface.cpp @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwasmoffscreensurface.h" + +QWasmOffscrenSurface::QWasmOffscrenSurface(QOffscreenSurface *offscreenSurface) + :QPlatformOffscreenSurface(offscreenSurface) +{ + +} + +QWasmOffscrenSurface::~QWasmOffscrenSurface() +{ + +} diff --git a/src/plugins/platforms/wasm/qwasmoffscreensurface.h b/src/plugins/platforms/wasm/qwasmoffscreensurface.h new file mode 100644 index 0000000000..9d3e805be0 --- /dev/null +++ b/src/plugins/platforms/wasm/qwasmoffscreensurface.h @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWASMOFFSCREENSURFACE_H +#define QWASMOFFSCREENSURFACE_H + +#include + +QT_BEGIN_NAMESPACE + +class QOffscreenSurface; +class QWasmOffscrenSurface : public QPlatformOffscreenSurface +{ +public: + explicit QWasmOffscrenSurface(QOffscreenSurface *offscreenSurface); + ~QWasmOffscrenSurface(); +private: + +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/wasm/wasm.pro b/src/plugins/platforms/wasm/wasm.pro index e8728d9dba..c28df8f893 100644 --- a/src/plugins/platforms/wasm/wasm.pro +++ b/src/plugins/platforms/wasm/wasm.pro @@ -20,7 +20,8 @@ SOURCES = \ qwasmopenglcontext.cpp \ qwasmtheme.cpp \ qwasmclipboard.cpp \ - qwasmservices.cpp + qwasmservices.cpp \ + qwasmoffscreensurface.cpp HEADERS = \ qwasmintegration.h \ @@ -35,7 +36,8 @@ HEADERS = \ qwasmopenglcontext.h \ qwasmtheme.h \ qwasmclipboard.h \ - qwasmservices.h + qwasmservices.h \ + qwasmoffscreensurface.h wasmfonts.files = \ ../../../3rdparty/wasm/Vera.ttf \ -- cgit v1.2.3 From c8c4819b7b2f20c2972c31c49547ce5b59fe1240 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Mon, 29 Apr 2019 00:35:42 +0200 Subject: wasm: controlled screen destruction Freeing OpenGL resources requires a current context, which (on wasm) requires a screen. Add a destroy() functions to QWasmScreen, QWasmCompositor, QWasmWindow, and QWasmBackingStore which facilitates OpenGL cleanup before we start deleting screen objects. Task-number: QTBUG-75463 Change-Id: I9954b536416b9147965c74459ccad838d1578778 Reviewed-by: Lorn Potter --- src/plugins/platforms/wasm/qwasmbackingstore.cpp | 6 +++++ src/plugins/platforms/wasm/qwasmbackingstore.h | 1 + src/plugins/platforms/wasm/qwasmcompositor.cpp | 29 ++++++++++++++++++++++-- src/plugins/platforms/wasm/qwasmcompositor.h | 1 + src/plugins/platforms/wasm/qwasmintegration.cpp | 4 +++- src/plugins/platforms/wasm/qwasmscreen.cpp | 5 ++++ src/plugins/platforms/wasm/qwasmscreen.h | 1 + src/plugins/platforms/wasm/qwasmwindow.cpp | 6 +++++ src/plugins/platforms/wasm/qwasmwindow.h | 1 + 9 files changed, 51 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/wasm/qwasmbackingstore.cpp b/src/plugins/platforms/wasm/qwasmbackingstore.cpp index 5b40c44807..e8eda2605f 100644 --- a/src/plugins/platforms/wasm/qwasmbackingstore.cpp +++ b/src/plugins/platforms/wasm/qwasmbackingstore.cpp @@ -55,6 +55,12 @@ QWasmBackingStore::~QWasmBackingStore() { } +void QWasmBackingStore::destroy() +{ + if (m_texture->isCreated()) + m_texture->destroy(); +} + QPaintDevice *QWasmBackingStore::paintDevice() { return &m_image; diff --git a/src/plugins/platforms/wasm/qwasmbackingstore.h b/src/plugins/platforms/wasm/qwasmbackingstore.h index 6ffa262e3d..4bca83c457 100644 --- a/src/plugins/platforms/wasm/qwasmbackingstore.h +++ b/src/plugins/platforms/wasm/qwasmbackingstore.h @@ -44,6 +44,7 @@ class QWasmBackingStore : public QPlatformBackingStore public: QWasmBackingStore(QWasmCompositor *compositor, QWindow *window); ~QWasmBackingStore(); + void destroy(); QPaintDevice *paintDevice() override; diff --git a/src/plugins/platforms/wasm/qwasmcompositor.cpp b/src/plugins/platforms/wasm/qwasmcompositor.cpp index 9d99c21195..6d211667be 100644 --- a/src/plugins/platforms/wasm/qwasmcompositor.cpp +++ b/src/plugins/platforms/wasm/qwasmcompositor.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -71,6 +72,28 @@ QWasmCompositor::QWasmCompositor(QWasmScreen *screen) QWasmCompositor::~QWasmCompositor() { delete m_frameBuffer; + destroy(); +} + +void QWasmCompositor::destroy() +{ + // Destroy OpenGL resources. This is done here in a separate function + // which can be called while screen() still returns a valid screen + // (which it might not, during destruction). A valid QScreen is + // a requirement for QOffscreenSurface on Wasm since the native + // context is tied to a single canvas. + if (m_context) { + QOffscreenSurface offScreenSurface(screen()->screen()); + offScreenSurface.setFormat(m_context->format()); + offScreenSurface.create(); + m_context->makeCurrent(&offScreenSurface); + for (QWasmWindow *window : m_windowStack) + window->destroy(); + m_blitter.reset(nullptr); + m_context.reset(nullptr); + } + + m_isEnabled = false; // prevent frame() from creating a new m_context } void QWasmCompositor::setEnabled(bool enabled) @@ -653,7 +676,7 @@ void QWasmCompositor::frame() m_needComposit = false; - if (m_windowStack.empty() || !screen()) + if (!m_isEnabled || m_windowStack.empty() || !screen()) return; QWasmWindow *someWindow = nullptr; @@ -676,7 +699,9 @@ void QWasmCompositor::frame() m_context->create(); } - m_context->makeCurrent(someWindow->window()); + bool ok = m_context->makeCurrent(someWindow->window()); + if (!ok) + return; if (!m_blitter->isCreated()) m_blitter->create(); diff --git a/src/plugins/platforms/wasm/qwasmcompositor.h b/src/plugins/platforms/wasm/qwasmcompositor.h index 3104573073..98f4a79b27 100644 --- a/src/plugins/platforms/wasm/qwasmcompositor.h +++ b/src/plugins/platforms/wasm/qwasmcompositor.h @@ -64,6 +64,7 @@ class QWasmCompositor : public QObject public: QWasmCompositor(QWasmScreen *screen); ~QWasmCompositor(); + void destroy(); enum QWasmSubControl { SC_None = 0x00000000, diff --git a/src/plugins/platforms/wasm/qwasmintegration.cpp b/src/plugins/platforms/wasm/qwasmintegration.cpp index 0d22d31c25..93d3b5f76d 100644 --- a/src/plugins/platforms/wasm/qwasmintegration.cpp +++ b/src/plugins/platforms/wasm/qwasmintegration.cpp @@ -258,7 +258,9 @@ void QWasmIntegration::addScreen(const QString &canvasId) void QWasmIntegration::removeScreen(const QString &canvasId) { - QWindowSystemInterface::handleScreenRemoved(m_screens.take(canvasId)); + QWasmScreen *exScreen = m_screens.take(canvasId); + exScreen->destroy(); // clean up before deleting the screen + QWindowSystemInterface::handleScreenRemoved(exScreen); } void QWasmIntegration::resizeScreen(const QString &canvasId) diff --git a/src/plugins/platforms/wasm/qwasmscreen.cpp b/src/plugins/platforms/wasm/qwasmscreen.cpp index a26cafa900..74d47555fc 100644 --- a/src/plugins/platforms/wasm/qwasmscreen.cpp +++ b/src/plugins/platforms/wasm/qwasmscreen.cpp @@ -57,7 +57,12 @@ QWasmScreen::QWasmScreen(const QString &canvasId) QWasmScreen::~QWasmScreen() { + destroy(); +} +void QWasmScreen::destroy() +{ + m_compositor->destroy(); } QWasmScreen *QWasmScreen::get(QPlatformScreen *screen) diff --git a/src/plugins/platforms/wasm/qwasmscreen.h b/src/plugins/platforms/wasm/qwasmscreen.h index 82d2a83edb..4ad4ffa9ad 100644 --- a/src/plugins/platforms/wasm/qwasmscreen.h +++ b/src/plugins/platforms/wasm/qwasmscreen.h @@ -52,6 +52,7 @@ class QWasmScreen : public QObject, public QPlatformScreen public: QWasmScreen(const QString &canvasId); ~QWasmScreen(); + void destroy(); static QWasmScreen *get(QPlatformScreen *screen); static QWasmScreen *get(QScreen *screen); diff --git a/src/plugins/platforms/wasm/qwasmwindow.cpp b/src/plugins/platforms/wasm/qwasmwindow.cpp index 39797cb09d..594db65cfd 100644 --- a/src/plugins/platforms/wasm/qwasmwindow.cpp +++ b/src/plugins/platforms/wasm/qwasmwindow.cpp @@ -65,6 +65,12 @@ QWasmWindow::~QWasmWindow() m_compositor->removeWindow(this); } +void QWasmWindow::destroy() +{ + if (m_backingStore) + m_backingStore->destroy(); +} + void QWasmWindow::initialize() { QRect rect = windowGeometry(); diff --git a/src/plugins/platforms/wasm/qwasmwindow.h b/src/plugins/platforms/wasm/qwasmwindow.h index cbbce99aeb..a098172649 100644 --- a/src/plugins/platforms/wasm/qwasmwindow.h +++ b/src/plugins/platforms/wasm/qwasmwindow.h @@ -58,6 +58,7 @@ public: QWasmWindow(QWindow *w, QWasmCompositor *compositor, QWasmBackingStore *backingStore); ~QWasmWindow(); + void destroy(); void initialize() override; -- cgit v1.2.3 From 77216c5c5295a38cc432135e31529335c18c5a4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Mon, 29 Apr 2019 01:19:14 +0200 Subject: wasm: rewrite/simplify OpenglContext MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don’t need the contextLost callback since we can poll for the “lost” status in isValid() Recreating the native context is not very helpful, since it destroys all current context state. Remove this logic. Support makeCurrent() on different surfaces, as long as they refer to the same screen. Create the native context (and record which screen) on the first call to makeCurrent() Task-number: QTBUG-75463 Change-Id: I6eb830df14578ffdbed5b0505fe860ce433e4f9b Reviewed-by: Lorn Potter --- src/plugins/platforms/wasm/qwasmopenglcontext.cpp | 59 +++++++++++------------ src/plugins/platforms/wasm/qwasmopenglcontext.h | 6 +-- 2 files changed, 30 insertions(+), 35 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/wasm/qwasmopenglcontext.cpp b/src/plugins/platforms/wasm/qwasmopenglcontext.cpp index ae43e2ebf0..0eaffdb77e 100644 --- a/src/plugins/platforms/wasm/qwasmopenglcontext.cpp +++ b/src/plugins/platforms/wasm/qwasmopenglcontext.cpp @@ -41,40 +41,31 @@ QWasmOpenGLContext::QWasmOpenGLContext(const QSurfaceFormat &format) QWasmOpenGLContext::~QWasmOpenGLContext() { - if (m_context) + if (m_context) { emscripten_webgl_destroy_context(m_context); + m_context = 0; + } } -void QWasmOpenGLContext::maybeRecreateEmscriptenContext(QPlatformSurface *surface) +bool QWasmOpenGLContext::maybeCreateEmscriptenContext(QPlatformSurface *surface) { - // Native emscripten contexts are tied to a single surface. Recreate - // the context if the surface is changed. - if (surface != m_surface) { - m_surface = surface; - - // Destroy existing context - if (m_context) - emscripten_webgl_destroy_context(m_context); - - // Create new context - const QString canvasId = QWasmScreen::get(surface->screen())->canvasId(); - m_context = createEmscriptenContext(canvasId, m_requestedFormat); - - // Register context-lost callback. - auto callback = [](int eventType, const void *reserved, void *userData) -> EM_BOOL - { - Q_UNUSED(eventType); - Q_UNUSED(reserved); - // The application may get contex-lost if e.g. moved to the background. Set - // m_contextLost which will make isValid() return false. Application code will - // then detect this and recrate the the context, resulting in a new QWasmOpenGLContext - // instance. - reinterpret_cast(userData)->m_contextLost = true; - return true; - }; - bool capture = true; - emscripten_set_webglcontextlost_callback(canvasId.toLocal8Bit().constData(), this, capture, callback); - } + // Native emscripten/WebGL contexts are tied to a single screen/canvas. The first + // call to this function creates a native canvas for the given screen, subsequent + // calls verify that the surface is on/off the same screen. + QPlatformScreen *screen = surface->screen(); + if (m_context && !screen) + return false; // Alternative: return true to support makeCurrent on QOffScreenSurface with + // no screen. However, Qt likes to substitute QGuiApplication::primaryScreen() + // for null screens, which foils this plan. + if (!screen) + return false; + if (m_context) + return m_screen == screen; + + QString canvasId = QWasmScreen::get(screen)->canvasId(); + m_context = createEmscriptenContext(canvasId, m_requestedFormat); + m_screen = screen; + return true; } EMSCRIPTEN_WEBGL_CONTEXT_HANDLE QWasmOpenGLContext::createEmscriptenContext(const QString &canvasId, QSurfaceFormat format) @@ -113,7 +104,9 @@ GLuint QWasmOpenGLContext::defaultFramebufferObject(QPlatformSurface *surface) c bool QWasmOpenGLContext::makeCurrent(QPlatformSurface *surface) { - maybeRecreateEmscriptenContext(surface); + bool ok = maybeCreateEmscriptenContext(surface); + if (!ok) + return false; return emscripten_webgl_make_context_current(m_context) == EMSCRIPTEN_RESULT_SUCCESS; } @@ -136,7 +129,9 @@ bool QWasmOpenGLContext::isSharing() const bool QWasmOpenGLContext::isValid() const { - return (m_contextLost == false); + // Note: we get isValid() calls before we see the surface and can + // create a native context, so no context is also a valid state. + return !m_context || !emscripten_is_webgl_context_lost(m_context); } QFunctionPointer QWasmOpenGLContext::getProcAddress(const char *procName) diff --git a/src/plugins/platforms/wasm/qwasmopenglcontext.h b/src/plugins/platforms/wasm/qwasmopenglcontext.h index 126b596a7e..d27007e8ea 100644 --- a/src/plugins/platforms/wasm/qwasmopenglcontext.h +++ b/src/plugins/platforms/wasm/qwasmopenglcontext.h @@ -34,6 +34,7 @@ QT_BEGIN_NAMESPACE +class QPlatformScreen; class QWasmOpenGLContext : public QPlatformOpenGLContext { public: @@ -50,12 +51,11 @@ public: QFunctionPointer getProcAddress(const char *procName) override; private: - void maybeRecreateEmscriptenContext(QPlatformSurface *surface); + bool maybeCreateEmscriptenContext(QPlatformSurface *surface); static EMSCRIPTEN_WEBGL_CONTEXT_HANDLE createEmscriptenContext(const QString &canvasId, QSurfaceFormat format); - bool m_contextLost = false; QSurfaceFormat m_requestedFormat; - QPlatformSurface *m_surface = nullptr; + QPlatformScreen *m_screen = nullptr; EMSCRIPTEN_WEBGL_CONTEXT_HANDLE m_context = 0; }; -- cgit v1.2.3 From 1c7b5c20a5f1c2fabf9df7ee636014f5ba0ab114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Mon, 29 Apr 2019 09:52:55 +0200 Subject: QOpenGLWindow: set QOffScreenSurface screen Required by QWasmOpenGLContext. Task-number: QTBUG-75463 Change-Id: Ie3cb80b50d7c909e6f46a6dec19644bf27cd41e7 Reviewed-by: Lorn Potter --- src/gui/kernel/qopenglwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/gui/kernel/qopenglwindow.cpp b/src/gui/kernel/qopenglwindow.cpp index 8b052d92ae..022a47c919 100644 --- a/src/gui/kernel/qopenglwindow.cpp +++ b/src/gui/kernel/qopenglwindow.cpp @@ -440,7 +440,7 @@ void QOpenGLWindow::makeCurrent() d->context->makeCurrent(this); } else { if (!d->offscreenSurface) { - d->offscreenSurface.reset(new QOffscreenSurface); + d->offscreenSurface.reset(new QOffscreenSurface(screen())); d->offscreenSurface->setFormat(d->context->format()); d->offscreenSurface->create(); } -- cgit v1.2.3 From 3803b41eaecb6c1686aba88b4226486507aca094 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Tue, 14 May 2019 14:02:32 +0200 Subject: Revert "macOS: disable threaded OpenGL (unconditionally)" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit fc3e8514144535db22c431251bc0feea99cf72e2. The patch introduces a crash in Qt Quick in the test tst_QQuickWindow::multipleWindows() on macOS 10.12. Reverting this will cause dead locks on older macOS versions for users who opt in to using layer backed mode, so we should bring this back as soon as a fix can be found for the crash. But in order to proceed with qt5.git integration, we revert it for now. Task-number: QTBUG-75782 Change-Id: I57f6b2918c3fc4b4e58a8c39b24a19e2d796a4f4 Reviewed-by: Tor Arne Vestbø --- src/plugins/platforms/cocoa/qcocoaintegration.mm | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index 855b42657d..fb3d05d3e4 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -347,13 +347,11 @@ bool QCocoaIntegration::hasCapability(QPlatformIntegration::Capability cap) cons switch (cap) { #ifndef QT_NO_OPENGL case ThreadedOpenGL: - // Qt's threaded OpenGL implementation does not work well for layer-backed - // views, where we can easily end up in situations where rendering on secondary - // threads will result in visual artifacts, bugs, or even deadlocks. It is - // not possible to determine here if the the app will be using layer-backed - // views (it can opt-in using SDK 10.14+ on macOS 10.4+, or by setting - // NSWindow flags such as NSWindowStyleMaskFullSizeContentView). - return false; + // AppKit expects rendering to happen on the main thread, and we can + // easily end up in situations where rendering on secondary threads + // will result in visual artifacts, bugs, or even deadlocks, when + // building with SDK 10.14 or higher which enbles view layer-backing. + return QMacVersion::buildSDK() < QOperatingSystemVersion(QOperatingSystemVersion::MacOSMojave); case OpenGL: case BufferQueueingOpenGL: #endif -- cgit v1.2.3 From 8bcc6f111b1c8f0e5c9c9da18313a16559de63a5 Mon Sep 17 00:00:00 2001 From: Christian Ehrlicher Date: Thu, 14 Mar 2019 20:12:08 +0100 Subject: QComboBox: add QT_DEPRECATED_X() for deprecated functions QT_DEPRECATED_X() was not added with d6d33f0b80dd85043c71f71a3ed5485d6014e6c4 for the deprecated QComboBox functions - Add them now. Change-Id: I8d4ea08766ae6ff052dfccac6c3f35ecf34affb7 Reviewed-by: Richard Moe Gustavsen --- src/widgets/widgets/qcombobox.cpp | 2 +- src/widgets/widgets/qcombobox.h | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/widgets/widgets/qcombobox.cpp b/src/widgets/widgets/qcombobox.cpp index 9a403e8eef..a93c72a933 100644 --- a/src/widgets/widgets/qcombobox.cpp +++ b/src/widgets/widgets/qcombobox.cpp @@ -1509,7 +1509,7 @@ int QComboBox::maxCount() const /*! \obsolete - Use setCompleter() instead. + Use completer() instead. */ bool QComboBox::autoCompletion() const { diff --git a/src/widgets/widgets/qcombobox.h b/src/widgets/widgets/qcombobox.h index 64fbebb3c5..33686e547d 100644 --- a/src/widgets/widgets/qcombobox.h +++ b/src/widgets/widgets/qcombobox.h @@ -96,10 +96,13 @@ public: #if QT_CONFIG(completer) #if QT_DEPRECATED_SINCE(5, 13) + QT_DEPRECATED_X("Use completer() instead.") bool autoCompletion() const; + QT_DEPRECATED_X("Use setCompleter() instead.") void setAutoCompletion(bool enable); - + QT_DEPRECATED_X("Use completer()->caseSensitivity() instead.") Qt::CaseSensitivity autoCompletionCaseSensitivity() const; + QT_DEPRECATED_X("Use completer()->setCaseSensitivity() instead.") void setAutoCompletionCaseSensitivity(Qt::CaseSensitivity sensitivity); #endif #endif -- cgit v1.2.3 From b0493998c391688022df7502892bd9b42cfdab7b Mon Sep 17 00:00:00 2001 From: wangChuan Date: Tue, 21 May 2019 16:08:25 +0800 Subject: QTextBoundaryFinder: don't break after uppercase followed by comma [ChangeLog][QtCore][QTextBoundaryFinder] Sentence breaking now no longer breaks between uppercase letters and comma. This is a deviation from the Unicode specification, but produces less surprising behavior. Fixes: QTBUG-75857 Change-Id: If1e78b3be3f20250d01100353ea7da6110985f82 Reviewed-by: Edward Welbourne Reviewed-by: Konstantin Ritt --- src/corelib/tools/qunicodetools.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/corelib/tools/qunicodetools.cpp b/src/corelib/tools/qunicodetools.cpp index c9d0868fef..0a2bea28e2 100644 --- a/src/corelib/tools/qunicodetools.cpp +++ b/src/corelib/tools/qunicodetools.cpp @@ -327,7 +327,7 @@ static const uchar breakTable[BAfter + 1][QUnicodeTables::NumSentenceBreakClasse // Any CR LF Sep Extend Sp Lower Upper OLetter Numeric ATerm SContinue STerm Close { Initial, BAfterC, BAfter , BAfter , Initial, Initial, Lower , Upper , Initial, Initial, ATerm , Initial, STerm , Initial }, // Initial { Initial, BAfterC, BAfter , BAfter , Lower , Initial, Initial, Initial, Initial, Initial, LUATerm, Initial, STerm , Initial }, // Lower - { Initial, BAfterC, BAfter , BAfter , Upper , Initial, Initial, Upper , Initial, Initial, LUATerm, STerm , STerm , Initial }, // Upper + { Initial, BAfterC, BAfter , BAfter , Upper , Initial, Initial, Upper , Initial, Initial, LUATerm, Initial, STerm , Initial }, // Upper { Lookup , BAfterC, BAfter , BAfter , LUATerm, ACS , Initial, Upper , Break , Initial, ATerm , STerm , STerm , ATermC }, // LUATerm { Lookup , BAfterC, BAfter , BAfter , ATerm , ACS , Initial, Break , Break , Initial, ATerm , STerm , STerm , ATermC }, // ATerm -- cgit v1.2.3 From 762d4afdc17f96804a83391551fcc7cad26a9319 Mon Sep 17 00:00:00 2001 From: Lorn Potter Date: Mon, 27 May 2019 11:08:47 +1000 Subject: wasm: fix crash in case network js event becomes null or undefined MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit also fix data progress Task-number: QTBUG-75489 Change-Id: I5222fda64d258a6ae78ba0ca20194b81c289c27e Reviewed-by: Morten Johan Sørvig --- src/network/access/qnetworkreplywasmimpl.cpp | 53 ++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/network/access/qnetworkreplywasmimpl.cpp b/src/network/access/qnetworkreplywasmimpl.cpp index 9f8a42ad89..9f6422a107 100644 --- a/src/network/access/qnetworkreplywasmimpl.cpp +++ b/src/network/access/qnetworkreplywasmimpl.cpp @@ -59,6 +59,9 @@ using namespace emscripten; static void q_requestErrorCallback(val event) { + if (event.isNull() || event.isUndefined()) + return; + val xhr = event["target"]; quintptr func = xhr["data-handler"].as(); @@ -77,19 +80,24 @@ static void q_requestErrorCallback(val event) static void q_progressCallback(val event) { + if (event.isNull() || event.isUndefined()) + return; + val xhr = event["target"]; QNetworkReplyWasmImplPrivate *reply = reinterpret_cast(xhr["data-handler"].as()); Q_ASSERT(reply); - if (xhr["lengthComputable"].as() && xhr["status"].as() < 400) - reply->emitDataReadProgress(xhr["loaded"].as(), xhr["total"].as()); - + if (xhr["status"].as() < 400) + reply->emitDataReadProgress(event["loaded"].as(), event["total"].as()); } static void q_loadCallback(val event) { + if (event.isNull() || event.isUndefined()) + return; + val xhr = event["target"]; QNetworkReplyWasmImplPrivate *reply = @@ -121,6 +129,7 @@ static void q_loadCallback(val event) reader.set("data-handler", xhr["data-handler"]); reader.call("readAsArrayBuffer", blob); + val::global("Module").delete_(reader); } @@ -136,6 +145,9 @@ static void q_loadCallback(val event) static void q_responseHeadersCallback(val event) { + if (event.isNull() || event.isUndefined()) + return; + val xhr = event["target"]; if (xhr["readyState"].as() == 2) { // HEADERS_RECEIVED @@ -152,12 +164,18 @@ static void q_responseHeadersCallback(val event) static void q_readBinary(val event) { + if (event.isNull() || event.isUndefined()) + return; + val fileReader = event["target"]; QNetworkReplyWasmImplPrivate *reply = reinterpret_cast(fileReader["data-handler"].as()); Q_ASSERT(reply); + if (reply->state == QNetworkReplyPrivate::Finished || reply->state == QNetworkReplyPrivate::Aborted) + return; + // Set up source typed array val result = fileReader["result"]; // ArrayBuffer val Uint8Array = val::global("Uint8Array"); @@ -171,6 +189,10 @@ static void q_readBinary(val event) reinterpret_cast(buffer.data()), size); destinationTypedArray.call("set", sourceTypedArray); reply->dataReceived(buffer, buffer.size()); + + event.delete_(fileReader); + Uint8Array.delete_(sourceTypedArray); + QCoreApplication::processEvents(); } @@ -194,14 +216,21 @@ QNetworkReplyWasmImplPrivate::QNetworkReplyWasmImplPrivate() QNetworkReplyWasmImplPrivate::~QNetworkReplyWasmImplPrivate() { + m_xhr.set("onerror", val::null()); + m_xhr.set("onload", val::null()); + m_xhr.set("onprogress", val::null()); + m_xhr.set("onreadystatechange", val::null()); + m_xhr.set("data-handler", val::null()); } -QNetworkReplyWasmImpl::~QNetworkReplyWasmImpl() +QNetworkReplyWasmImpl::QNetworkReplyWasmImpl(QObject *parent) + : QNetworkReply(*new QNetworkReplyWasmImplPrivate(), parent) { + Q_D( QNetworkReplyWasmImpl); + d->state = QNetworkReplyPrivate::Idle; } -QNetworkReplyWasmImpl::QNetworkReplyWasmImpl(QObject *parent) - : QNetworkReply(*new QNetworkReplyWasmImplPrivate(), parent) +QNetworkReplyWasmImpl::~QNetworkReplyWasmImpl() { } @@ -226,19 +255,23 @@ QByteArray QNetworkReplyWasmImpl::methodName() const void QNetworkReplyWasmImpl::close() { + QNetworkReply::close(); setFinished(true); emit finished(); - - QNetworkReply::close(); } void QNetworkReplyWasmImpl::abort() { - Q_D(const QNetworkReplyWasmImpl); - setError( QNetworkReply::OperationCanceledError, QStringLiteral("Operation canceled")); + Q_D( QNetworkReplyWasmImpl); + if (d->state == QNetworkReplyPrivate::Finished || d->state == QNetworkReplyPrivate::Aborted) + return; + + setError(QNetworkReply::OperationCanceledError, QStringLiteral("Operation canceled")); + d->doAbort(); close(); + d->state = QNetworkReplyPrivate::Aborted; } qint64 QNetworkReplyWasmImpl::bytesAvailable() const -- cgit v1.2.3 From 8d2df3002b3a252686bf094566746cd31dc5d99b Mon Sep 17 00:00:00 2001 From: Martin Smith Date: Wed, 12 Jun 2019 15:37:06 +0200 Subject: doc: Fix tr() documentation issue The declaration of tr() in the Q_OBJECT macro interferes with the tr() declaration in the QObject class. This update fixes that bug by resetting QT_TR_FUNCTIONS to be empty and by ensuring that the tr() declaration in class QObject is seen by clang when qdoc is running. Change-Id: If55339fc417f3eee1a1e1ce3df75a18af443d630 Task-number: QTBUG-75864 Reviewed-by: Paul Wicking --- src/corelib/kernel/qobject.h | 3 ++- src/corelib/kernel/qobjectdefs.h | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/corelib/kernel/qobject.h b/src/corelib/kernel/qobject.h index 63c5a9ad73..fc1d7c956c 100644 --- a/src/corelib/kernel/qobject.h +++ b/src/corelib/kernel/qobject.h @@ -117,6 +117,7 @@ public: class Q_CORE_EXPORT QObject { Q_OBJECT + Q_PROPERTY(QString objectName READ objectName WRITE setObjectName NOTIFY objectNameChanged) Q_DECLARE_PRIVATE(QObject) @@ -127,7 +128,7 @@ public: virtual bool event(QEvent *event); virtual bool eventFilter(QObject *watched, QEvent *event); -#if defined(QT_NO_TRANSLATION) +#if defined(QT_NO_TRANSLATION) || defined(Q_CLANG_QDOC) static QString tr(const char *sourceText, const char * = nullptr, int = -1) { return QString::fromUtf8(sourceText); } #if QT_DEPRECATED_SINCE(5, 0) diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h index 059bb44e10..b84e87959a 100644 --- a/src/corelib/kernel/qobjectdefs.h +++ b/src/corelib/kernel/qobjectdefs.h @@ -146,6 +146,10 @@ class QString; # define QT_TR_FUNCTIONS #endif +#ifdef Q_CLANG_QDOC +#define QT_TR_FUNCTIONS +#endif + // ### Qt6: remove #define Q_OBJECT_CHECK /* empty, unused since Qt 5.2 */ -- cgit v1.2.3 From df9f9fb368cc2dc17c57030e1df51804c2f2265d Mon Sep 17 00:00:00 2001 From: Lorn Potter Date: Thu, 30 May 2019 18:30:17 +1000 Subject: wasm: handle mouse events even when not over a window MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the mouse button is held and mouse moves from over the window, that window would never register the button up event, and cause issue like being able to move a dialog around by simply moving the mouse around. Change-Id: I1363ac9c9f4113a79bf6863668ba74b90b1cea4a Fixes: QTBUG-75951 Reviewed-by: Morten Johan Sørvig --- src/plugins/platforms/wasm/qwasmeventtranslator.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp index f586c5378e..3895646b89 100644 --- a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp +++ b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp @@ -552,9 +552,12 @@ void QWasmEventTranslator::processMouse(int eventType, const EmscriptenMouseEven Qt::KeyboardModifiers modifiers = translateMouseEventModifier(mouseEvent); QWindow *window2 = screen()->compositor()->windowAt(globalPoint, 5); - if (window2 == nullptr) - return; - lastWindow = window2; + + if (window2 == nullptr) { + window2 = lastWindow; + } else { + lastWindow = window2; + } QPoint localPoint = window2->mapFromGlobal(globalPoint); bool interior = window2->geometry().contains(globalPoint); @@ -616,7 +619,7 @@ void QWasmEventTranslator::processMouse(int eventType, const EmscriptenMouseEven } if (resizeMode != QWasmWindow::ResizeNone && !(htmlWindow->m_windowState & Qt::WindowFullScreen)) { - QPoint delta = QPoint(mouseEvent->canvasX, mouseEvent->canvasY) - resizePoint; + QPoint delta = QPoint(mouseEvent->targetX, mouseEvent->targetY) - resizePoint; resizeWindow(draggedWindow, resizeMode, resizeStartRect, delta); } } -- cgit v1.2.3 From 29425f1e7776c3fcea3b041484d97683a57b431d Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Mon, 3 Jun 2019 16:12:47 +0200 Subject: Update documentation regarding import/export restrictions for OpenSSL From Qt 5.12.5 and 5.13.0 on we ship OpenSSL 1.1.x libraries with Qt through the binary installers, as Qt is a general purpose toolkit and as such not subject to import/export restrictions. However, application developers still have to take care of the relevant requirements and compliance before distributing OpenSSL. Change-Id: I1c3622116eadda270d638becfa23a5493976e919 Fixes: QTBUG-75814 Reviewed-by: Paul Wicking Reviewed-by: Akseli Salovaara --- src/network/doc/src/ssl.qdoc | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/network/doc/src/ssl.qdoc b/src/network/doc/src/ssl.qdoc index a3af1d0477..e485a1b393 100644 --- a/src/network/doc/src/ssl.qdoc +++ b/src/network/doc/src/ssl.qdoc @@ -77,11 +77,12 @@ \section1 Import and Export Restrictions - Due to import and export restrictions in some parts of the world, we - are unable to supply the OpenSSL Toolkit with Qt packages. Developers wishing - to use SSL communication in their deployed applications should either ensure - that their users have the appropriate libraries installed, or they should - consult a suitably qualified legal professional to ensure that applications - using code from the OpenSSL project are correctly certified for import - and export in relevant regions of the world. + Qt binary installers include the OpenSSL libraries used by QtNetwork. However, + those are not automatically deployed with applications that are built with Qt. + Import and export restrictions apply for some types of software, and for + some parts of the world. Developers wishing to use SSL communication in their + deployed applications should either ensure that their users have the appropriate + libraries installed, or they should consult a suitably qualified legal + professional to ensure that applications using code from the OpenSSL project + are correctly certified for import and export in relevant regions of the world. */ -- cgit v1.2.3 From e67ae0d62f4fdf0bd3cd95a7e9443c90c361e58d Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Wed, 5 Jun 2019 20:35:54 +0200 Subject: Update SQLite to 3.28.0 [ChangeLog][Third-Party Code] Updated bundled SQLite to version 3.28.0 Change-Id: If2a893c1dd5d5f97032a0a9b1f7ff78b9e715038 Reviewed-by: Jani Heikkinen --- src/3rdparty/sqlite/qt_attribution.json | 4 +- src/3rdparty/sqlite/sqlite3.c | 8689 +++++++++++++++++-------------- src/3rdparty/sqlite/sqlite3.h | 61 +- 3 files changed, 4909 insertions(+), 3845 deletions(-) (limited to 'src') diff --git a/src/3rdparty/sqlite/qt_attribution.json b/src/3rdparty/sqlite/qt_attribution.json index 30f266ee23..e6647b2700 100644 --- a/src/3rdparty/sqlite/qt_attribution.json +++ b/src/3rdparty/sqlite/qt_attribution.json @@ -6,8 +6,8 @@ "Description": "SQLite is a small C library that implements a self-contained, embeddable, zero-configuration SQL database engine.", "Homepage": "https://www.sqlite.org/", - "Version": "3.27.1", - "DownloadLocation": "https://www.sqlite.org/2018/sqlite-amalgamation-3270100.zip", + "Version": "3.28.0", + "DownloadLocation": "https://www.sqlite.org/2019/sqlite-amalgamation-3280000.zip", "License": "Public Domain", "Copyright": "The authors disclaim copyright to the source code. However, a license can be obtained if needed." } diff --git a/src/3rdparty/sqlite/sqlite3.c b/src/3rdparty/sqlite/sqlite3.c index 70e84b589c..440429527d 100644 --- a/src/3rdparty/sqlite/sqlite3.c +++ b/src/3rdparty/sqlite/sqlite3.c @@ -1,6 +1,6 @@ /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite -** version 3.27.1. By combining all the individual C code files into this +** version 3.28.0. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a single translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements @@ -1162,9 +1162,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.27.1" -#define SQLITE_VERSION_NUMBER 3027001 -#define SQLITE_SOURCE_ID "2019-02-08 13:17:39 0eca3dd3d38b31c92b49ca2d311128b74584714d9e7de895b1a6286ef959a1dd" +#define SQLITE_VERSION "3.28.0" +#define SQLITE_VERSION_NUMBER 3028000 +#define SQLITE_SOURCE_ID "2019-04-16 19:49:53 884b4b7e502b4e991677b53971277adfaf0a04a284f8e483e2553d0f83156b50" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -1228,6 +1228,9 @@ SQLITE_API int sqlite3_libversion_number(void); #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS SQLITE_API int sqlite3_compileoption_used(const char *zOptName); SQLITE_API const char *sqlite3_compileoption_get(int N); +#else +# define sqlite3_compileoption_used(X) 0 +# define sqlite3_compileoption_get(X) ((void*)0) #endif /* @@ -3125,8 +3128,8 @@ struct sqlite3_mem_methods { ** ** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]] **
    SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER
    -**
    ^This option is used to enable or disable the two-argument -** version of the [fts3_tokenizer()] function which is part of the +**
    ^This option is used to enable or disable the +** [fts3_tokenizer()] function which is part of the ** [FTS3] full-text search engine extension. ** There should be two additional arguments. ** The first argument is an integer which is 0 to disable fts3_tokenizer() or @@ -3238,6 +3241,17 @@ struct sqlite3_mem_methods { **
  • Direct writes to [shadow tables]. ** **
  • +** +** [[SQLITE_DBCONFIG_WRITABLE_SCHEMA]]
    SQLITE_DBCONFIG_WRITABLE_SCHEMA
    +**
    The SQLITE_DBCONFIG_WRITABLE_SCHEMA option activates or deactivates the +** "writable_schema" flag. This has the same effect and is logically equivalent +** to setting [PRAGMA writable_schema=ON] or [PRAGMA writable_schema=OFF]. +** The first argument to this setting is an integer which is 0 to disable +** the writable_schema, positive to enable writable_schema, or negative to +** leave the setting unchanged. The second parameter is a pointer to an +** integer into which is written 0 or 1 to indicate whether the writable_schema +** is enabled or disabled following this call. +**
    ** */ #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ @@ -3251,7 +3265,8 @@ struct sqlite3_mem_methods { #define SQLITE_DBCONFIG_TRIGGER_EQP 1008 /* int int* */ #define SQLITE_DBCONFIG_RESET_DATABASE 1009 /* int int* */ #define SQLITE_DBCONFIG_DEFENSIVE 1010 /* int int* */ -#define SQLITE_DBCONFIG_MAX 1010 /* Largest DBCONFIG */ +#define SQLITE_DBCONFIG_WRITABLE_SCHEMA 1011 /* int int* */ +#define SQLITE_DBCONFIG_MAX 1011 /* Largest DBCONFIG */ /* ** CAPI3REF: Enable Or Disable Extended Result Codes @@ -3408,7 +3423,7 @@ SQLITE_API int sqlite3_changes(sqlite3*); ** not. ^Changes to a view that are intercepted by INSTEAD OF triggers ** are not counted. ** -** This the [sqlite3_total_changes(D)] interface only reports the number +** The [sqlite3_total_changes(D)] interface only reports the number ** of rows that changed due to SQL statement run against database ** connection D. Any changes by other database connections are ignored. ** To detect changes against a database file from other database @@ -4933,6 +4948,18 @@ SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt); */ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); +/* +** CAPI3REF: Query The EXPLAIN Setting For A Prepared Statement +** METHOD: sqlite3_stmt +** +** ^The sqlite3_stmt_isexplain(S) interface returns 1 if the +** prepared statement S is an EXPLAIN statement, or 2 if the +** statement S is an EXPLAIN QUERY PLAN. +** ^The sqlite3_stmt_isexplain(S) interface returns 0 if S is +** an ordinary statement or a NULL pointer. +*/ +SQLITE_API int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt); + /* ** CAPI3REF: Determine If A Prepared Statement Has Been Reset ** METHOD: sqlite3_stmt @@ -5072,7 +5099,9 @@ typedef struct sqlite3_context sqlite3_context; ** ^The fifth argument to the BLOB and string binding interfaces ** is a destructor used to dispose of the BLOB or ** string after SQLite has finished with it. ^The destructor is called -** to dispose of the BLOB or string even if the call to bind API fails. +** to dispose of the BLOB or string even if the call to the bind API fails, +** except the destructor is not called if the third parameter is a NULL +** pointer or the fourth parameter is negative. ** ^If the fifth argument is ** the special value [SQLITE_STATIC], then SQLite assumes that the ** information is in static, unmanaged space and does not need to be freed. @@ -5989,6 +6018,8 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6 ** sqlite3_value_nochange   ** →  True if the column is unchanged in an UPDATE ** against a virtual table. +** sqlite3_value_frombind   +** →  True if value originated from a [bound parameter] ** ** ** Details: @@ -6050,6 +6081,11 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6 ** than within an [xUpdate] method call for an UPDATE statement, then ** the return value is arbitrary and meaningless. ** +** ^The sqlite3_value_frombind(X) interface returns non-zero if the +** value X originated from one of the [sqlite3_bind_int|sqlite3_bind()] +** interfaces. ^If X comes from an SQL literal value, or a table column, +** and expression, then sqlite3_value_frombind(X) returns zero. +** ** Please pay particular attention to the fact that the pointer returned ** from [sqlite3_value_blob()], [sqlite3_value_text()], or ** [sqlite3_value_text16()] can be invalidated by a subsequent call to @@ -6095,6 +6131,7 @@ SQLITE_API int sqlite3_value_bytes16(sqlite3_value*); SQLITE_API int sqlite3_value_type(sqlite3_value*); SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); SQLITE_API int sqlite3_value_nochange(sqlite3_value*); +SQLITE_API int sqlite3_value_frombind(sqlite3_value*); /* ** CAPI3REF: Finding The Subtype Of SQL Values @@ -6830,7 +6867,7 @@ SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); ** associated with database N of connection D. ^The main database file ** has the name "main". If there is no attached database N on the database ** connection D, or if database N is a temporary or in-memory database, then -** a NULL pointer is returned. +** this function will return either a NULL pointer or an empty string. ** ** ^The filename returned by this function is the output of the ** xFullPathname method of the [VFS]. ^In other words, the filename @@ -11931,7 +11968,7 @@ SQLITE_API int sqlite3rebaser_configure( ** in size. This function allocates and populates a buffer with a copy ** of the changeset rebased rebased according to the configuration of the ** rebaser object passed as the first argument. If successful, (*ppOut) -** is set to point to the new buffer containing the rebased changset and +** is set to point to the new buffer containing the rebased changeset and ** (*pnOut) to its size in bytes and SQLITE_OK returned. It is the ** responsibility of the caller to eventually free the new buffer using ** sqlite3_free(). Otherwise, if an error occurs, (*ppOut) and (*pnOut) @@ -12340,7 +12377,7 @@ struct Fts5PhraseIter { ** Save the pointer passed as the second argument as the extension functions ** "auxiliary data". The pointer may then be retrieved by the current or any ** future invocation of the same fts5 extension function made as part of -** of the same MATCH query using the xGetAuxdata() API. +** the same MATCH query using the xGetAuxdata() API. ** ** Each extension function is allocated a single auxiliary data slot for ** each FTS query (MATCH expression). If the extension function is invoked @@ -12355,7 +12392,7 @@ struct Fts5PhraseIter { ** The xDelete callback, if one is specified, is also invoked on the ** auxiliary data pointer after the FTS5 query has finished. ** -** If an error (e.g. an OOM condition) occurs within this function, an +** If an error (e.g. an OOM condition) occurs within this function, ** the auxiliary data is set to NULL and an error code returned. If the ** xDelete parameter was not NULL, it is invoked on the auxiliary data ** pointer before returning. @@ -13381,7 +13418,7 @@ struct Hash { unsigned int count; /* Number of entries in this table */ HashElem *first; /* The first element of the array */ struct _ht { /* the hash table */ - int count; /* Number of entries with this hash */ + unsigned int count; /* Number of entries with this hash */ HashElem *chain; /* Pointer to first entry with this hash */ } *ht; }; @@ -13522,99 +13559,94 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*); #define TK_PRECEDING 85 #define TK_RANGE 86 #define TK_UNBOUNDED 87 -#define TK_REINDEX 88 -#define TK_RENAME 89 -#define TK_CTIME_KW 90 -#define TK_ANY 91 -#define TK_BITAND 92 -#define TK_BITOR 93 -#define TK_LSHIFT 94 -#define TK_RSHIFT 95 -#define TK_PLUS 96 -#define TK_MINUS 97 -#define TK_STAR 98 -#define TK_SLASH 99 -#define TK_REM 100 -#define TK_CONCAT 101 -#define TK_COLLATE 102 -#define TK_BITNOT 103 -#define TK_ON 104 -#define TK_INDEXED 105 -#define TK_STRING 106 -#define TK_JOIN_KW 107 -#define TK_CONSTRAINT 108 -#define TK_DEFAULT 109 -#define TK_NULL 110 -#define TK_PRIMARY 111 -#define TK_UNIQUE 112 -#define TK_CHECK 113 -#define TK_REFERENCES 114 -#define TK_AUTOINCR 115 -#define TK_INSERT 116 -#define TK_DELETE 117 -#define TK_UPDATE 118 -#define TK_SET 119 -#define TK_DEFERRABLE 120 -#define TK_FOREIGN 121 -#define TK_DROP 122 -#define TK_UNION 123 -#define TK_ALL 124 -#define TK_EXCEPT 125 -#define TK_INTERSECT 126 -#define TK_SELECT 127 -#define TK_VALUES 128 -#define TK_DISTINCT 129 -#define TK_DOT 130 -#define TK_FROM 131 -#define TK_JOIN 132 -#define TK_USING 133 -#define TK_ORDER 134 -#define TK_GROUP 135 -#define TK_HAVING 136 -#define TK_LIMIT 137 -#define TK_WHERE 138 -#define TK_INTO 139 -#define TK_NOTHING 140 -#define TK_FLOAT 141 -#define TK_BLOB 142 -#define TK_INTEGER 143 -#define TK_VARIABLE 144 -#define TK_CASE 145 -#define TK_WHEN 146 -#define TK_THEN 147 -#define TK_ELSE 148 -#define TK_INDEX 149 -#define TK_ALTER 150 -#define TK_ADD 151 -#define TK_WINDOW 152 -#define TK_OVER 153 -#define TK_FILTER 154 -#define TK_TRUEFALSE 155 -#define TK_ISNOT 156 -#define TK_FUNCTION 157 -#define TK_COLUMN 158 -#define TK_AGG_FUNCTION 159 -#define TK_AGG_COLUMN 160 -#define TK_UMINUS 161 -#define TK_UPLUS 162 -#define TK_TRUTH 163 -#define TK_REGISTER 164 -#define TK_VECTOR 165 -#define TK_SELECT_COLUMN 166 -#define TK_IF_NULL_ROW 167 -#define TK_ASTERISK 168 -#define TK_SPAN 169 -#define TK_END_OF_FILE 170 -#define TK_UNCLOSED_STRING 171 -#define TK_SPACE 172 -#define TK_ILLEGAL 173 - -/* The token codes above must all fit in 8 bits */ -#define TKFLG_MASK 0xff - -/* Flags that can be added to a token code when it is not -** being stored in a u8: */ -#define TKFLG_DONTFOLD 0x100 /* Omit constant folding optimizations */ +#define TK_EXCLUDE 88 +#define TK_GROUPS 89 +#define TK_OTHERS 90 +#define TK_TIES 91 +#define TK_REINDEX 92 +#define TK_RENAME 93 +#define TK_CTIME_KW 94 +#define TK_ANY 95 +#define TK_BITAND 96 +#define TK_BITOR 97 +#define TK_LSHIFT 98 +#define TK_RSHIFT 99 +#define TK_PLUS 100 +#define TK_MINUS 101 +#define TK_STAR 102 +#define TK_SLASH 103 +#define TK_REM 104 +#define TK_CONCAT 105 +#define TK_COLLATE 106 +#define TK_BITNOT 107 +#define TK_ON 108 +#define TK_INDEXED 109 +#define TK_STRING 110 +#define TK_JOIN_KW 111 +#define TK_CONSTRAINT 112 +#define TK_DEFAULT 113 +#define TK_NULL 114 +#define TK_PRIMARY 115 +#define TK_UNIQUE 116 +#define TK_CHECK 117 +#define TK_REFERENCES 118 +#define TK_AUTOINCR 119 +#define TK_INSERT 120 +#define TK_DELETE 121 +#define TK_UPDATE 122 +#define TK_SET 123 +#define TK_DEFERRABLE 124 +#define TK_FOREIGN 125 +#define TK_DROP 126 +#define TK_UNION 127 +#define TK_ALL 128 +#define TK_EXCEPT 129 +#define TK_INTERSECT 130 +#define TK_SELECT 131 +#define TK_VALUES 132 +#define TK_DISTINCT 133 +#define TK_DOT 134 +#define TK_FROM 135 +#define TK_JOIN 136 +#define TK_USING 137 +#define TK_ORDER 138 +#define TK_GROUP 139 +#define TK_HAVING 140 +#define TK_LIMIT 141 +#define TK_WHERE 142 +#define TK_INTO 143 +#define TK_NOTHING 144 +#define TK_FLOAT 145 +#define TK_BLOB 146 +#define TK_INTEGER 147 +#define TK_VARIABLE 148 +#define TK_CASE 149 +#define TK_WHEN 150 +#define TK_THEN 151 +#define TK_ELSE 152 +#define TK_INDEX 153 +#define TK_ALTER 154 +#define TK_ADD 155 +#define TK_WINDOW 156 +#define TK_OVER 157 +#define TK_FILTER 158 +#define TK_TRUEFALSE 159 +#define TK_ISNOT 160 +#define TK_FUNCTION 161 +#define TK_COLUMN 162 +#define TK_AGG_FUNCTION 163 +#define TK_AGG_COLUMN 164 +#define TK_UMINUS 165 +#define TK_UPLUS 166 +#define TK_TRUTH 167 +#define TK_REGISTER 168 +#define TK_VECTOR 169 +#define TK_SELECT_COLUMN 170 +#define TK_IF_NULL_ROW 171 +#define TK_ASTERISK 172 +#define TK_SPAN 173 +#define TK_SPACE 174 +#define TK_ILLEGAL 175 /************** End of parse.h ***********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ @@ -14546,9 +14578,6 @@ struct BtreePayload { SQLITE_PRIVATE int sqlite3BtreeInsert(BtCursor*, const BtreePayload *pPayload, int flags, int seekResult); SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor*, int *pRes); -#ifndef SQLITE_OMIT_WINDOWFUNC -SQLITE_PRIVATE void sqlite3BtreeSkipNext(BtCursor*); -#endif SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor*, int *pRes); SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor*, int flags); SQLITE_PRIVATE int sqlite3BtreeEof(BtCursor*); @@ -14906,25 +14935,25 @@ typedef struct VdbeOpList VdbeOpList; #define OP_Offset 89 /* synopsis: r[P3] = sqlite_offset(P1) */ #define OP_Column 90 /* synopsis: r[P3]=PX */ #define OP_Affinity 91 /* synopsis: affinity(r[P1@P2]) */ -#define OP_BitAnd 92 /* same as TK_BITAND, synopsis: r[P3]=r[P1]&r[P2] */ -#define OP_BitOr 93 /* same as TK_BITOR, synopsis: r[P3]=r[P1]|r[P2] */ -#define OP_ShiftLeft 94 /* same as TK_LSHIFT, synopsis: r[P3]=r[P2]<>r[P1] */ -#define OP_Add 96 /* same as TK_PLUS, synopsis: r[P3]=r[P1]+r[P2] */ -#define OP_Subtract 97 /* same as TK_MINUS, synopsis: r[P3]=r[P2]-r[P1] */ -#define OP_Multiply 98 /* same as TK_STAR, synopsis: r[P3]=r[P1]*r[P2] */ -#define OP_Divide 99 /* same as TK_SLASH, synopsis: r[P3]=r[P2]/r[P1] */ -#define OP_Remainder 100 /* same as TK_REM, synopsis: r[P3]=r[P2]%r[P1] */ -#define OP_Concat 101 /* same as TK_CONCAT, synopsis: r[P3]=r[P2]+r[P1] */ -#define OP_MakeRecord 102 /* synopsis: r[P3]=mkrec(r[P1@P2]) */ -#define OP_BitNot 103 /* same as TK_BITNOT, synopsis: r[P2]= ~r[P1] */ -#define OP_Count 104 /* synopsis: r[P2]=count() */ -#define OP_ReadCookie 105 -#define OP_String8 106 /* same as TK_STRING, synopsis: r[P2]='P4' */ -#define OP_SetCookie 107 -#define OP_ReopenIdx 108 /* synopsis: root=P2 iDb=P3 */ -#define OP_OpenRead 109 /* synopsis: root=P2 iDb=P3 */ -#define OP_OpenWrite 110 /* synopsis: root=P2 iDb=P3 */ +#define OP_MakeRecord 92 /* synopsis: r[P3]=mkrec(r[P1@P2]) */ +#define OP_Count 93 /* synopsis: r[P2]=count() */ +#define OP_ReadCookie 94 +#define OP_SetCookie 95 +#define OP_BitAnd 96 /* same as TK_BITAND, synopsis: r[P3]=r[P1]&r[P2] */ +#define OP_BitOr 97 /* same as TK_BITOR, synopsis: r[P3]=r[P1]|r[P2] */ +#define OP_ShiftLeft 98 /* same as TK_LSHIFT, synopsis: r[P3]=r[P2]<>r[P1] */ +#define OP_Add 100 /* same as TK_PLUS, synopsis: r[P3]=r[P1]+r[P2] */ +#define OP_Subtract 101 /* same as TK_MINUS, synopsis: r[P3]=r[P2]-r[P1] */ +#define OP_Multiply 102 /* same as TK_STAR, synopsis: r[P3]=r[P1]*r[P2] */ +#define OP_Divide 103 /* same as TK_SLASH, synopsis: r[P3]=r[P2]/r[P1] */ +#define OP_Remainder 104 /* same as TK_REM, synopsis: r[P3]=r[P2]%r[P1] */ +#define OP_Concat 105 /* same as TK_CONCAT, synopsis: r[P3]=r[P2]+r[P1] */ +#define OP_ReopenIdx 106 /* synopsis: root=P2 iDb=P3 */ +#define OP_BitNot 107 /* same as TK_BITNOT, synopsis: r[P2]= ~r[P1] */ +#define OP_OpenRead 108 /* synopsis: root=P2 iDb=P3 */ +#define OP_OpenWrite 109 /* synopsis: root=P2 iDb=P3 */ +#define OP_String8 110 /* same as TK_STRING, synopsis: r[P2]='P4' */ #define OP_OpenDup 111 #define OP_OpenAutoindex 112 /* synopsis: nColumn=P2 */ #define OP_OpenEphemeral 113 /* synopsis: nColumn=P2 */ @@ -14937,57 +14966,56 @@ typedef struct VdbeOpList VdbeOpList; #define OP_Sequence 120 /* synopsis: r[P2]=cursor[P1].ctr++ */ #define OP_NewRowid 121 /* synopsis: r[P2]=rowid */ #define OP_Insert 122 /* synopsis: intkey=r[P3] data=r[P2] */ -#define OP_InsertInt 123 /* synopsis: intkey=P3 data=r[P2] */ -#define OP_Delete 124 -#define OP_ResetCount 125 -#define OP_SorterCompare 126 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */ -#define OP_SorterData 127 /* synopsis: r[P2]=data */ -#define OP_RowData 128 /* synopsis: r[P2]=data */ -#define OP_Rowid 129 /* synopsis: r[P2]=rowid */ -#define OP_NullRow 130 -#define OP_SeekEnd 131 -#define OP_SorterInsert 132 /* synopsis: key=r[P2] */ -#define OP_IdxInsert 133 /* synopsis: key=r[P2] */ -#define OP_IdxDelete 134 /* synopsis: key=r[P2@P3] */ -#define OP_DeferredSeek 135 /* synopsis: Move P3 to P1.rowid if needed */ -#define OP_IdxRowid 136 /* synopsis: r[P2]=rowid */ -#define OP_Destroy 137 -#define OP_Clear 138 -#define OP_ResetSorter 139 -#define OP_CreateBtree 140 /* synopsis: r[P2]=root iDb=P1 flags=P3 */ -#define OP_Real 141 /* same as TK_FLOAT, synopsis: r[P2]=P4 */ -#define OP_SqlExec 142 -#define OP_ParseSchema 143 -#define OP_LoadAnalysis 144 -#define OP_DropTable 145 -#define OP_DropIndex 146 -#define OP_DropTrigger 147 -#define OP_IntegrityCk 148 -#define OP_RowSetAdd 149 /* synopsis: rowset(P1)=r[P2] */ -#define OP_Param 150 -#define OP_FkCounter 151 /* synopsis: fkctr[P1]+=P2 */ -#define OP_MemMax 152 /* synopsis: r[P1]=max(r[P1],r[P2]) */ -#define OP_OffsetLimit 153 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */ -#define OP_AggInverse 154 /* synopsis: accum=r[P3] inverse(r[P2@P5]) */ -#define OP_AggStep 155 /* synopsis: accum=r[P3] step(r[P2@P5]) */ -#define OP_AggStep1 156 /* synopsis: accum=r[P3] step(r[P2@P5]) */ -#define OP_AggValue 157 /* synopsis: r[P3]=value N=P2 */ -#define OP_AggFinal 158 /* synopsis: accum=r[P1] N=P2 */ -#define OP_Expire 159 -#define OP_TableLock 160 /* synopsis: iDb=P1 root=P2 write=P3 */ -#define OP_VBegin 161 -#define OP_VCreate 162 -#define OP_VDestroy 163 -#define OP_VOpen 164 -#define OP_VColumn 165 /* synopsis: r[P3]=vcolumn(P2) */ -#define OP_VRename 166 -#define OP_Pagecount 167 -#define OP_MaxPgcnt 168 -#define OP_Trace 169 -#define OP_CursorHint 170 -#define OP_Noop 171 -#define OP_Explain 172 -#define OP_Abortable 173 +#define OP_Delete 123 +#define OP_ResetCount 124 +#define OP_SorterCompare 125 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */ +#define OP_SorterData 126 /* synopsis: r[P2]=data */ +#define OP_RowData 127 /* synopsis: r[P2]=data */ +#define OP_Rowid 128 /* synopsis: r[P2]=rowid */ +#define OP_NullRow 129 +#define OP_SeekEnd 130 +#define OP_SorterInsert 131 /* synopsis: key=r[P2] */ +#define OP_IdxInsert 132 /* synopsis: key=r[P2] */ +#define OP_IdxDelete 133 /* synopsis: key=r[P2@P3] */ +#define OP_DeferredSeek 134 /* synopsis: Move P3 to P1.rowid if needed */ +#define OP_IdxRowid 135 /* synopsis: r[P2]=rowid */ +#define OP_Destroy 136 +#define OP_Clear 137 +#define OP_ResetSorter 138 +#define OP_CreateBtree 139 /* synopsis: r[P2]=root iDb=P1 flags=P3 */ +#define OP_SqlExec 140 +#define OP_ParseSchema 141 +#define OP_LoadAnalysis 142 +#define OP_DropTable 143 +#define OP_DropIndex 144 +#define OP_Real 145 /* same as TK_FLOAT, synopsis: r[P2]=P4 */ +#define OP_DropTrigger 146 +#define OP_IntegrityCk 147 +#define OP_RowSetAdd 148 /* synopsis: rowset(P1)=r[P2] */ +#define OP_Param 149 +#define OP_FkCounter 150 /* synopsis: fkctr[P1]+=P2 */ +#define OP_MemMax 151 /* synopsis: r[P1]=max(r[P1],r[P2]) */ +#define OP_OffsetLimit 152 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */ +#define OP_AggInverse 153 /* synopsis: accum=r[P3] inverse(r[P2@P5]) */ +#define OP_AggStep 154 /* synopsis: accum=r[P3] step(r[P2@P5]) */ +#define OP_AggStep1 155 /* synopsis: accum=r[P3] step(r[P2@P5]) */ +#define OP_AggValue 156 /* synopsis: r[P3]=value N=P2 */ +#define OP_AggFinal 157 /* synopsis: accum=r[P1] N=P2 */ +#define OP_Expire 158 +#define OP_TableLock 159 /* synopsis: iDb=P1 root=P2 write=P3 */ +#define OP_VBegin 160 +#define OP_VCreate 161 +#define OP_VDestroy 162 +#define OP_VOpen 163 +#define OP_VColumn 164 /* synopsis: r[P3]=vcolumn(P2) */ +#define OP_VRename 165 +#define OP_Pagecount 166 +#define OP_MaxPgcnt 167 +#define OP_Trace 168 +#define OP_CursorHint 169 +#define OP_Noop 170 +#define OP_Explain 171 +#define OP_Abortable 172 /* Properties such as "out2" or "jump" that are specified in ** comments following the "case" for each opcode in the vdbe.c @@ -15011,17 +15039,17 @@ typedef struct VdbeOpList VdbeOpList; /* 64 */ 0x00, 0x00, 0x02, 0x02, 0x08, 0x00, 0x10, 0x10,\ /* 72 */ 0x10, 0x10, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10,\ /* 80 */ 0x10, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00,\ -/* 88 */ 0x12, 0x20, 0x00, 0x00, 0x26, 0x26, 0x26, 0x26,\ -/* 96 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x00, 0x12,\ -/* 104 */ 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,\ +/* 88 */ 0x12, 0x20, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00,\ +/* 96 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,\ +/* 104 */ 0x26, 0x26, 0x00, 0x12, 0x00, 0x00, 0x10, 0x00,\ /* 112 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ /* 120 */ 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ -/* 128 */ 0x00, 0x10, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00,\ -/* 136 */ 0x10, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00,\ -/* 144 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x10, 0x00,\ -/* 152 */ 0x04, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ -/* 160 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,\ -/* 168 */ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,} +/* 128 */ 0x10, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x10,\ +/* 136 */ 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,\ +/* 144 */ 0x00, 0x10, 0x00, 0x00, 0x06, 0x10, 0x00, 0x04,\ +/* 152 */ 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ +/* 160 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10,\ +/* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00,} /* The sqlite3P2Values() routine is able to run faster if it knows ** the value of the largest JUMP opcode. The smaller the maximum @@ -16326,6 +16354,7 @@ struct sqlite3 { void (*xRollbackCallback)(void*); /* Invoked at every commit. */ void *pUpdateArg; void (*xUpdateCallback)(void*,int, const char*,const char*,sqlite_int64); + Parse *pParse; /* Current parse */ #ifdef SQLITE_ENABLE_PREUPDATE_HOOK void *pPreUpdateArg; /* First argument to xPreUpdateCallback */ void (*xPreUpdateCallback)( /* Registered using sqlite3_preupdate_hook() */ @@ -16459,7 +16488,8 @@ struct sqlite3 { #define DBFLAG_SchemaChange 0x0001 /* Uncommitted Hash table changes */ #define DBFLAG_PreferBuiltin 0x0002 /* Preference to built-in funcs */ #define DBFLAG_Vacuum 0x0004 /* Currently in a VACUUM */ -#define DBFLAG_SchemaKnownOk 0x0008 /* Schema is known to be valid */ +#define DBFLAG_VacuumInto 0x0008 /* Currently running VACUUM INTO */ +#define DBFLAG_SchemaKnownOk 0x0010 /* Schema is known to be valid */ /* ** Bits of the sqlite3.dbOptFlags field that are used by the @@ -16467,7 +16497,7 @@ struct sqlite3 { ** selectively disable various optimizations. */ #define SQLITE_QueryFlattener 0x0001 /* Query flattening */ - /* 0x0002 available for reuse */ +#define SQLITE_WindowFunc 0x0002 /* Use xInverse for window functions */ #define SQLITE_GroupByOrder 0x0004 /* GROUPBY cover of ORDERBY */ #define SQLITE_FactorOutConst 0x0008 /* Constant factoring */ #define SQLITE_DistinctOpt 0x0010 /* DISTINCT using indexes */ @@ -16585,7 +16615,6 @@ struct FuncDestructor { #define SQLITE_FUNC_AFFINITY 0x4000 /* Built-in affinity() function */ #define SQLITE_FUNC_OFFSET 0x8000 /* Built-in sqlite_offset() function */ #define SQLITE_FUNC_WINDOW 0x00010000 /* Built-in window-only function */ -#define SQLITE_FUNC_WINDOW_SIZE 0x20000 /* Requires partition size as arg. */ #define SQLITE_FUNC_INTERNAL 0x00040000 /* For use by NestedParse() only */ /* @@ -17391,12 +17420,16 @@ struct Expr { /* ** The following are the meanings of bits in the Expr.flags field. +** Value restrictions: +** +** EP_Agg == NC_HasAgg == SF_HasAgg +** EP_Win == NC_HasWin */ #define EP_FromJoin 0x000001 /* Originates in ON/USING clause of outer join */ -#define EP_Agg 0x000002 /* Contains one or more aggregate functions */ +#define EP_Distinct 0x000002 /* Aggregate function with DISTINCT keyword */ #define EP_HasFunc 0x000004 /* Contains one or more functions of any kind */ #define EP_FixedCol 0x000008 /* TK_Column with a known fixed value */ -#define EP_Distinct 0x000010 /* Aggregate function with DISTINCT keyword */ +#define EP_Agg 0x000010 /* Contains one or more aggregate functions */ #define EP_VarSelect 0x000020 /* pSelect is correlated, not constant */ #define EP_DblQuoted 0x000040 /* token.z was originally in "..." */ #define EP_InfixFunc 0x000080 /* True for an infix function: LIKE, GLOB, etc */ @@ -17407,7 +17440,7 @@ struct Expr { #define EP_Skip 0x001000 /* COLLATE, AS, or UNLIKELY */ #define EP_Reduced 0x002000 /* Expr struct EXPR_REDUCEDSIZE bytes only */ #define EP_TokenOnly 0x004000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */ -#define EP_Static 0x008000 /* Held in memory not obtained from malloc() */ +#define EP_Win 0x008000 /* Contains window functions */ #define EP_MemToken 0x010000 /* Need to sqlite3DbFree() Expr.zToken */ #define EP_NoReduce 0x020000 /* Cannot EXPRDUP_REDUCE this Expr */ #define EP_Unlikely 0x040000 /* unlikely() or likelihood() function */ @@ -17419,6 +17452,7 @@ struct Expr { #define EP_WinFunc 0x1000000 /* TK_FUNCTION with Expr.y.pWin set */ #define EP_Subrtn 0x2000000 /* Uses Expr.y.sub. TK_IN, _SELECT, or _EXISTS */ #define EP_Quoted 0x4000000 /* TK_ID was originally quoted */ +#define EP_Static 0x8000000 /* Held in memory not obtained from malloc() */ /* ** The EP_Propagate mask is a set of properties that automatically propagate @@ -17658,8 +17692,9 @@ struct NameContext { ** Allowed values for the NameContext, ncFlags field. ** ** Value constraints (all checked via assert()): -** NC_HasAgg == SF_HasAgg +** NC_HasAgg == SF_HasAgg == EP_Agg ** NC_MinMaxAgg == SF_MinMaxAgg == SQLITE_FUNC_MINMAX +** NC_HasWin == EP_Win ** */ #define NC_AllowAgg 0x0001 /* Aggregate functions are allowed here */ @@ -17675,6 +17710,7 @@ struct NameContext { #define NC_MinMaxAgg 0x1000 /* min/max aggregates seen. See note above */ #define NC_Complex 0x2000 /* True if a function or subquery seen */ #define NC_AllowWin 0x4000 /* Window functions are allowed here */ +#define NC_HasWin 0x8000 /* One or more window functions seen */ /* ** An instance of the following object describes a single ON CONFLICT @@ -17989,6 +18025,7 @@ struct Parse { AutoincInfo *pAinc; /* Information about AUTOINCREMENT counters */ Parse *pToplevel; /* Parse structure for main program (or NULL) */ Table *pTriggerTab; /* Table triggers are being coded for */ + Parse *pParentParse; /* Parent parser if this parser is nested */ int addrCrTab; /* Address of OP_CreateBtree opcode on CREATE TABLE */ u32 nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */ u32 oldmask; /* Mask of old.* columns referenced */ @@ -18429,7 +18466,7 @@ struct TreeView { #endif /* SQLITE_DEBUG */ /* -** This object is used in varioius ways, all related to window functions +** This object is used in various ways, all related to window functions ** ** (1) A single instance of this structure is attached to the ** the Expr.pWin field for each window function in an expression tree. @@ -18444,15 +18481,18 @@ struct TreeView { ** object on a linked list attached to Select.pWinDefn. ** ** The uses (1) and (2) are really the same Window object that just happens -** to be accessible in two different ways. Use (3) is are separate objects. +** to be accessible in two different ways. Use case (3) are separate objects. */ struct Window { char *zName; /* Name of window (may be NULL) */ + char *zBase; /* Name of base window for chaining (may be NULL) */ ExprList *pPartition; /* PARTITION BY clause */ ExprList *pOrderBy; /* ORDER BY clause */ - u8 eType; /* TK_RANGE or TK_ROWS */ + u8 eFrmType; /* TK_RANGE, TK_GROUPS, TK_ROWS, or 0 */ u8 eStart; /* UNBOUNDED, CURRENT, PRECEDING or FOLLOWING */ u8 eEnd; /* UNBOUNDED, CURRENT, PRECEDING or FOLLOWING */ + u8 bImplicitFrame; /* True if frame was implicitly specified */ + u8 eExclude; /* TK_NO, TK_CURRENT, TK_TIES, TK_GROUP, or 0 */ Expr *pStart; /* Expression for " PRECEDING" */ Expr *pEnd; /* Expression for " FOLLOWING" */ Window *pNextWin; /* Next window function belonging to this SELECT */ @@ -18463,17 +18503,19 @@ struct Window { int regResult; int csrApp; /* Function cursor (used by min/max) */ int regApp; /* Function register (also used by min/max) */ - int regPart; /* First in a set of registers holding PARTITION BY - ** and ORDER BY values for the window */ + int regPart; /* Array of registers for PARTITION BY values */ Expr *pOwner; /* Expression object this window is attached to */ int nBufferCol; /* Number of columns in buffer table */ int iArgCol; /* Offset of first argument for this function */ + int regOne; /* Register containing constant value 1 */ + int regStartRowid; + int regEndRowid; }; #ifndef SQLITE_OMIT_WINDOWFUNC SQLITE_PRIVATE void sqlite3WindowDelete(sqlite3*, Window*); SQLITE_PRIVATE void sqlite3WindowListDelete(sqlite3 *db, Window *p); -SQLITE_PRIVATE Window *sqlite3WindowAlloc(Parse*, int, int, Expr*, int , Expr*); +SQLITE_PRIVATE Window *sqlite3WindowAlloc(Parse*, int, int, Expr*, int , Expr*, u8); SQLITE_PRIVATE void sqlite3WindowAttach(Parse*, Expr*, Window*); SQLITE_PRIVATE int sqlite3WindowCompare(Parse*, Window*, Window*); SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse*, Window*); @@ -18484,6 +18526,8 @@ SQLITE_PRIVATE void sqlite3WindowUpdate(Parse*, Window*, Window*, FuncDef*); SQLITE_PRIVATE Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p); SQLITE_PRIVATE Window *sqlite3WindowListDup(sqlite3 *db, Window *p); SQLITE_PRIVATE void sqlite3WindowFunctions(void); +SQLITE_PRIVATE void sqlite3WindowChain(Parse*, Window*, Window*); +SQLITE_PRIVATE Window *sqlite3WindowAssemble(Parse*, Window*, ExprList*, ExprList*, Token*); #else # define sqlite3WindowDelete(a,b) # define sqlite3WindowFunctions() @@ -18713,6 +18757,7 @@ SQLITE_PRIVATE void sqlite3TreeViewWinFunc(TreeView*, const Window*, u8); SQLITE_PRIVATE void sqlite3SetString(char **, sqlite3*, const char*); SQLITE_PRIVATE void sqlite3ErrorMsg(Parse*, const char*, ...); +SQLITE_PRIVATE int sqlite3ErrorToParser(sqlite3*,int); SQLITE_PRIVATE void sqlite3Dequote(char*); SQLITE_PRIVATE void sqlite3DequoteExpr(Expr*); SQLITE_PRIVATE void sqlite3TokenInit(Token*,char*); @@ -19174,7 +19219,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameColumn(Parse*, SrcList*, Token*, Token*); SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *, int *); SQLITE_PRIVATE void sqlite3NestedParse(Parse*, const char*, ...); SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3*, int); -SQLITE_PRIVATE void sqlite3CodeRhsOfIN(Parse*, Expr*, int, int); +SQLITE_PRIVATE void sqlite3CodeRhsOfIN(Parse*, Expr*, int); SQLITE_PRIVATE int sqlite3CodeSubselect(Parse*, Expr*); SQLITE_PRIVATE void sqlite3SelectPrep(Parse*, Select*, NameContext*); SQLITE_PRIVATE void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p); @@ -20136,11 +20181,11 @@ struct sqlite3_value { #define MEM_Real 0x0008 /* Value is a real number */ #define MEM_Blob 0x0010 /* Value is a BLOB */ #define MEM_AffMask 0x001f /* Mask of affinity bits */ -/* Available 0x0020 */ +#define MEM_FromBind 0x0020 /* Value originates from sqlite3_bind() */ /* Available 0x0040 */ #define MEM_Undefined 0x0080 /* Value is undefined */ #define MEM_Cleared 0x0100 /* NULL set by OP_Null, not from data */ -#define MEM_TypeMask 0xc1ff /* Mask of type bits */ +#define MEM_TypeMask 0xc1df /* Mask of type bits */ /* Whenever Mem contains a valid string or blob representation, one of @@ -20172,6 +20217,12 @@ struct sqlite3_value { #define MemSetTypeFlag(p, f) \ ((p)->flags = ((p)->flags&~(MEM_TypeMask|MEM_Zero))|f) +/* +** True if Mem X is a NULL-nochng type. +*/ +#define MemNullNochng(X) \ + ((X)->flags==(MEM_Null|MEM_Zero) && (X)->n==0 && (X)->u.nZero==0) + /* ** Return true if a memory cell is not marked as invalid. This macro ** is for use inside assert() statements only. @@ -27121,6 +27172,9 @@ SQLITE_PRIVATE void sqlite3OomFault(sqlite3 *db){ db->u1.isInterrupted = 1; } db->lookaside.bDisable++; + if( db->pParse ){ + db->pParse->rc = SQLITE_NOMEM_BKPT; + } } } @@ -27314,7 +27368,8 @@ static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){ static void setStrAccumError(StrAccum *p, u8 eError){ assert( eError==SQLITE_NOMEM || eError==SQLITE_TOOBIG ); p->accError = eError; - p->nAlloc = 0; + if( p->mxAlloc ) sqlite3_str_reset(p); + if( eError==SQLITE_TOOBIG ) sqlite3ErrorToParser(p->db, eError); } /* @@ -27344,6 +27399,7 @@ static char *getTextArg(PrintfArguments *p){ */ static char *printfTempBuf(sqlite3_str *pAccum, sqlite3_int64 n){ char *z; + if( pAccum->accError ) return 0; if( n>pAccum->nAlloc && n>pAccum->mxAlloc ){ setStrAccumError(pAccum, SQLITE_TOOBIG); return 0; @@ -28063,9 +28119,8 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){ return 0; } if( p->mxAlloc==0 ){ - N = p->nAlloc - p->nChar - 1; setStrAccumError(p, SQLITE_TOOBIG); - return N; + return p->nAlloc - p->nChar - 1; }else{ char *zOld = isMalloced(p) ? p->zText : 0; i64 szNew = p->nChar; @@ -28137,7 +28192,7 @@ SQLITE_API void sqlite3_str_append(sqlite3_str *p, const char *z, int N){ assert( z!=0 || N==0 ); assert( p->zText!=0 || p->nChar==0 || p->accError ); assert( N>=0 ); - assert( p->accError==0 || p->nAlloc==0 ); + assert( p->accError==0 || p->nAlloc==0 || p->mxAlloc==0 ); if( p->nChar+N >= p->nAlloc ){ enlargeAndAppend(p,z,N); }else if( N ){ @@ -28770,24 +28825,62 @@ SQLITE_PRIVATE void sqlite3TreeViewBound( ** Generate a human-readable explanation for a Window object */ SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u8 more){ + int nElement = 0; + if( pWin->pFilter ){ + sqlite3TreeViewItem(pView, "FILTER", 1); + sqlite3TreeViewExpr(pView, pWin->pFilter, 0); + sqlite3TreeViewPop(pView); + } pView = sqlite3TreeViewPush(pView, more); if( pWin->zName ){ - sqlite3TreeViewLine(pView, "OVER %s", pWin->zName); + sqlite3TreeViewLine(pView, "OVER %s (%p)", pWin->zName, pWin); }else{ - sqlite3TreeViewLine(pView, "OVER"); + sqlite3TreeViewLine(pView, "OVER (%p)", pWin); + } + if( pWin->zBase ) nElement++; + if( pWin->pOrderBy ) nElement++; + if( pWin->eFrmType ) nElement++; + if( pWin->eExclude ) nElement++; + if( pWin->zBase ){ + sqlite3TreeViewPush(pView, (--nElement)>0); + sqlite3TreeViewLine(pView, "window: %s", pWin->zBase); + sqlite3TreeViewPop(pView); } if( pWin->pPartition ){ - sqlite3TreeViewExprList(pView, pWin->pPartition, 1, "PARTITION-BY"); + sqlite3TreeViewExprList(pView, pWin->pPartition, nElement>0,"PARTITION-BY"); } if( pWin->pOrderBy ){ - sqlite3TreeViewExprList(pView, pWin->pOrderBy, 1, "ORDER-BY"); - } - if( pWin->eType ){ - sqlite3TreeViewItem(pView, pWin->eType==TK_RANGE ? "RANGE" : "ROWS", 0); + sqlite3TreeViewExprList(pView, pWin->pOrderBy, (--nElement)>0, "ORDER-BY"); + } + if( pWin->eFrmType ){ + char zBuf[30]; + const char *zFrmType = "ROWS"; + if( pWin->eFrmType==TK_RANGE ) zFrmType = "RANGE"; + if( pWin->eFrmType==TK_GROUPS ) zFrmType = "GROUPS"; + sqlite3_snprintf(sizeof(zBuf),zBuf,"%s%s",zFrmType, + pWin->bImplicitFrame ? " (implied)" : ""); + sqlite3TreeViewItem(pView, zBuf, (--nElement)>0); sqlite3TreeViewBound(pView, pWin->eStart, pWin->pStart, 1); sqlite3TreeViewBound(pView, pWin->eEnd, pWin->pEnd, 0); sqlite3TreeViewPop(pView); } + if( pWin->eExclude ){ + char zBuf[30]; + const char *zExclude; + switch( pWin->eExclude ){ + case TK_NO: zExclude = "NO OTHERS"; break; + case TK_CURRENT: zExclude = "CURRENT ROW"; break; + case TK_GROUP: zExclude = "GROUP"; break; + case TK_TIES: zExclude = "TIES"; break; + default: + sqlite3_snprintf(sizeof(zBuf),zBuf,"invalid(%d)", pWin->eExclude); + zExclude = zBuf; + break; + } + sqlite3TreeViewPush(pView, 0); + sqlite3TreeViewLine(pView, "EXCLUDE %s", zExclude); + sqlite3TreeViewPop(pView); + } sqlite3TreeViewPop(pView); } #endif /* SQLITE_OMIT_WINDOWFUNC */ @@ -29767,11 +29860,11 @@ SQLITE_PRIVATE u32 sqlite3Utf8Read( ** encoding, or if *pMem does not contain a string value. */ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){ - int len; /* Maximum length of output string in bytes */ - unsigned char *zOut; /* Output buffer */ - unsigned char *zIn; /* Input iterator */ - unsigned char *zTerm; /* End of input */ - unsigned char *z; /* Output iterator */ + sqlite3_int64 len; /* Maximum length of output string in bytes */ + unsigned char *zOut; /* Output buffer */ + unsigned char *zIn; /* Input iterator */ + unsigned char *zTerm; /* End of input */ + unsigned char *z; /* Output iterator */ unsigned int c; assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); @@ -29820,14 +29913,14 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desired ** nul-terminator. */ pMem->n &= ~1; - len = pMem->n * 2 + 1; + len = 2 * (sqlite3_int64)pMem->n + 1; }else{ /* When converting from UTF-8 to UTF-16 the maximum growth is caused ** when a 1-byte UTF-8 character is translated into a 2-byte UTF-16 ** character. Two bytes are required in the output buffer for the ** nul-terminator. */ - len = pMem->n * 2 + 2; + len = 2 * (sqlite3_int64)pMem->n + 2; } /* Set zIn to point at the start of the input buffer and zTerm to point 1 @@ -30134,15 +30227,23 @@ SQLITE_PRIVATE void sqlite3Coverage(int x){ #endif /* -** Give a callback to the test harness that can be used to simulate faults -** in places where it is difficult or expensive to do so purely by means -** of inputs. +** Calls to sqlite3FaultSim() are used to simulate a failure during testing, +** or to bypass normal error detection during testing in order to let +** execute proceed futher downstream. +** +** In deployment, sqlite3FaultSim() *always* return SQLITE_OK (0). The +** sqlite3FaultSim() function only returns non-zero during testing. ** -** The intent of the integer argument is to let the fault simulator know -** which of multiple sqlite3FaultSim() calls has been hit. +** During testing, if the test harness has set a fault-sim callback using +** a call to sqlite3_test_control(SQLITE_TESTCTRL_FAULT_INSTALL), then +** each call to sqlite3FaultSim() is relayed to that application-supplied +** callback and the integer return value form the application-supplied +** callback is returned by sqlite3FaultSim(). ** -** Return whatever integer value the test callback returns, or return -** SQLITE_OK if no test callback is installed. +** The integer argument to sqlite3FaultSim() is a code to identify which +** sqlite3FaultSim() instance is being invoked. Each call to sqlite3FaultSim() +** should have a unique code. To prevent legacy testing applications from +** breaking, the codes should not be changed or reused. */ #ifndef SQLITE_UNTESTABLE SQLITE_PRIVATE int sqlite3FaultSim(int iTest){ @@ -30327,6 +30428,19 @@ SQLITE_PRIVATE void sqlite3ErrorMsg(Parse *pParse, const char *zFormat, ...){ } } +/* +** If database connection db is currently parsing SQL, then transfer +** error code errCode to that parser if the parser has not already +** encountered some other kind of error. +*/ +SQLITE_PRIVATE int sqlite3ErrorToParser(sqlite3 *db, int errCode){ + Parse *pParse; + if( db==0 || (pParse = db->pParse)==0 ) return errCode; + pParse->rc = errCode; + pParse->nErr++; + return errCode; +} + /* ** Convert an SQL-style quoted string into a normal string by removing ** the quote characters. The conversion is done in-place. If the @@ -31678,7 +31792,7 @@ SQLITE_PRIVATE VList *sqlite3VListAdd( assert( pIn==0 || pIn[0]>=3 ); /* Verify ok to add new elements */ if( pIn==0 || pIn[1]+nInt > pIn[0] ){ /* Enlarge the allocation */ - int nAlloc = (pIn ? pIn[0]*2 : 10) + nInt; + sqlite3_int64 nAlloc = (pIn ? 2*(sqlite3_int64)pIn[0] : 10) + nInt; VList *pOut = sqlite3DbRealloc(db, pIn, nAlloc*sizeof(int)); if( pOut==0 ) return pIn; if( pIn==0 ) pOut[1] = 2; @@ -31884,7 +31998,7 @@ static HashElem *findElementWithHash( unsigned int *pHash /* Write the hash value here */ ){ HashElem *elem; /* Used to loop thru the element list */ - int count; /* Number of elements left to test */ + unsigned int count; /* Number of elements left to test */ unsigned int h; /* The computed hash */ static HashElem nullElement = { 0, 0, 0, 0 }; @@ -31932,8 +32046,8 @@ static void removeElementGivenHash( if( pEntry->chain==elem ){ pEntry->chain = elem->next; } + assert( pEntry->count>0 ); pEntry->count--; - assert( pEntry->count>=0 ); } sqlite3_free( elem ); pH->count--; @@ -32108,25 +32222,25 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ /* 89 */ "Offset" OpHelp("r[P3] = sqlite_offset(P1)"), /* 90 */ "Column" OpHelp("r[P3]=PX"), /* 91 */ "Affinity" OpHelp("affinity(r[P1@P2])"), - /* 92 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"), - /* 93 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"), - /* 94 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<>r[P1]"), - /* 96 */ "Add" OpHelp("r[P3]=r[P1]+r[P2]"), - /* 97 */ "Subtract" OpHelp("r[P3]=r[P2]-r[P1]"), - /* 98 */ "Multiply" OpHelp("r[P3]=r[P1]*r[P2]"), - /* 99 */ "Divide" OpHelp("r[P3]=r[P2]/r[P1]"), - /* 100 */ "Remainder" OpHelp("r[P3]=r[P2]%r[P1]"), - /* 101 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"), - /* 102 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"), - /* 103 */ "BitNot" OpHelp("r[P2]= ~r[P1]"), - /* 104 */ "Count" OpHelp("r[P2]=count()"), - /* 105 */ "ReadCookie" OpHelp(""), - /* 106 */ "String8" OpHelp("r[P2]='P4'"), - /* 107 */ "SetCookie" OpHelp(""), - /* 108 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"), - /* 109 */ "OpenRead" OpHelp("root=P2 iDb=P3"), - /* 110 */ "OpenWrite" OpHelp("root=P2 iDb=P3"), + /* 92 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"), + /* 93 */ "Count" OpHelp("r[P2]=count()"), + /* 94 */ "ReadCookie" OpHelp(""), + /* 95 */ "SetCookie" OpHelp(""), + /* 96 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"), + /* 97 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"), + /* 98 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<>r[P1]"), + /* 100 */ "Add" OpHelp("r[P3]=r[P1]+r[P2]"), + /* 101 */ "Subtract" OpHelp("r[P3]=r[P2]-r[P1]"), + /* 102 */ "Multiply" OpHelp("r[P3]=r[P1]*r[P2]"), + /* 103 */ "Divide" OpHelp("r[P3]=r[P2]/r[P1]"), + /* 104 */ "Remainder" OpHelp("r[P3]=r[P2]%r[P1]"), + /* 105 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"), + /* 106 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"), + /* 107 */ "BitNot" OpHelp("r[P2]= ~r[P1]"), + /* 108 */ "OpenRead" OpHelp("root=P2 iDb=P3"), + /* 109 */ "OpenWrite" OpHelp("root=P2 iDb=P3"), + /* 110 */ "String8" OpHelp("r[P2]='P4'"), /* 111 */ "OpenDup" OpHelp(""), /* 112 */ "OpenAutoindex" OpHelp("nColumn=P2"), /* 113 */ "OpenEphemeral" OpHelp("nColumn=P2"), @@ -32139,57 +32253,56 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ /* 120 */ "Sequence" OpHelp("r[P2]=cursor[P1].ctr++"), /* 121 */ "NewRowid" OpHelp("r[P2]=rowid"), /* 122 */ "Insert" OpHelp("intkey=r[P3] data=r[P2]"), - /* 123 */ "InsertInt" OpHelp("intkey=P3 data=r[P2]"), - /* 124 */ "Delete" OpHelp(""), - /* 125 */ "ResetCount" OpHelp(""), - /* 126 */ "SorterCompare" OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"), - /* 127 */ "SorterData" OpHelp("r[P2]=data"), - /* 128 */ "RowData" OpHelp("r[P2]=data"), - /* 129 */ "Rowid" OpHelp("r[P2]=rowid"), - /* 130 */ "NullRow" OpHelp(""), - /* 131 */ "SeekEnd" OpHelp(""), - /* 132 */ "SorterInsert" OpHelp("key=r[P2]"), - /* 133 */ "IdxInsert" OpHelp("key=r[P2]"), - /* 134 */ "IdxDelete" OpHelp("key=r[P2@P3]"), - /* 135 */ "DeferredSeek" OpHelp("Move P3 to P1.rowid if needed"), - /* 136 */ "IdxRowid" OpHelp("r[P2]=rowid"), - /* 137 */ "Destroy" OpHelp(""), - /* 138 */ "Clear" OpHelp(""), - /* 139 */ "ResetSorter" OpHelp(""), - /* 140 */ "CreateBtree" OpHelp("r[P2]=root iDb=P1 flags=P3"), - /* 141 */ "Real" OpHelp("r[P2]=P4"), - /* 142 */ "SqlExec" OpHelp(""), - /* 143 */ "ParseSchema" OpHelp(""), - /* 144 */ "LoadAnalysis" OpHelp(""), - /* 145 */ "DropTable" OpHelp(""), - /* 146 */ "DropIndex" OpHelp(""), - /* 147 */ "DropTrigger" OpHelp(""), - /* 148 */ "IntegrityCk" OpHelp(""), - /* 149 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"), - /* 150 */ "Param" OpHelp(""), - /* 151 */ "FkCounter" OpHelp("fkctr[P1]+=P2"), - /* 152 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"), - /* 153 */ "OffsetLimit" OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"), - /* 154 */ "AggInverse" OpHelp("accum=r[P3] inverse(r[P2@P5])"), - /* 155 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"), - /* 156 */ "AggStep1" OpHelp("accum=r[P3] step(r[P2@P5])"), - /* 157 */ "AggValue" OpHelp("r[P3]=value N=P2"), - /* 158 */ "AggFinal" OpHelp("accum=r[P1] N=P2"), - /* 159 */ "Expire" OpHelp(""), - /* 160 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"), - /* 161 */ "VBegin" OpHelp(""), - /* 162 */ "VCreate" OpHelp(""), - /* 163 */ "VDestroy" OpHelp(""), - /* 164 */ "VOpen" OpHelp(""), - /* 165 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), - /* 166 */ "VRename" OpHelp(""), - /* 167 */ "Pagecount" OpHelp(""), - /* 168 */ "MaxPgcnt" OpHelp(""), - /* 169 */ "Trace" OpHelp(""), - /* 170 */ "CursorHint" OpHelp(""), - /* 171 */ "Noop" OpHelp(""), - /* 172 */ "Explain" OpHelp(""), - /* 173 */ "Abortable" OpHelp(""), + /* 123 */ "Delete" OpHelp(""), + /* 124 */ "ResetCount" OpHelp(""), + /* 125 */ "SorterCompare" OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"), + /* 126 */ "SorterData" OpHelp("r[P2]=data"), + /* 127 */ "RowData" OpHelp("r[P2]=data"), + /* 128 */ "Rowid" OpHelp("r[P2]=rowid"), + /* 129 */ "NullRow" OpHelp(""), + /* 130 */ "SeekEnd" OpHelp(""), + /* 131 */ "SorterInsert" OpHelp("key=r[P2]"), + /* 132 */ "IdxInsert" OpHelp("key=r[P2]"), + /* 133 */ "IdxDelete" OpHelp("key=r[P2@P3]"), + /* 134 */ "DeferredSeek" OpHelp("Move P3 to P1.rowid if needed"), + /* 135 */ "IdxRowid" OpHelp("r[P2]=rowid"), + /* 136 */ "Destroy" OpHelp(""), + /* 137 */ "Clear" OpHelp(""), + /* 138 */ "ResetSorter" OpHelp(""), + /* 139 */ "CreateBtree" OpHelp("r[P2]=root iDb=P1 flags=P3"), + /* 140 */ "SqlExec" OpHelp(""), + /* 141 */ "ParseSchema" OpHelp(""), + /* 142 */ "LoadAnalysis" OpHelp(""), + /* 143 */ "DropTable" OpHelp(""), + /* 144 */ "DropIndex" OpHelp(""), + /* 145 */ "Real" OpHelp("r[P2]=P4"), + /* 146 */ "DropTrigger" OpHelp(""), + /* 147 */ "IntegrityCk" OpHelp(""), + /* 148 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"), + /* 149 */ "Param" OpHelp(""), + /* 150 */ "FkCounter" OpHelp("fkctr[P1]+=P2"), + /* 151 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"), + /* 152 */ "OffsetLimit" OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"), + /* 153 */ "AggInverse" OpHelp("accum=r[P3] inverse(r[P2@P5])"), + /* 154 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"), + /* 155 */ "AggStep1" OpHelp("accum=r[P3] step(r[P2@P5])"), + /* 156 */ "AggValue" OpHelp("r[P3]=value N=P2"), + /* 157 */ "AggFinal" OpHelp("accum=r[P1] N=P2"), + /* 158 */ "Expire" OpHelp(""), + /* 159 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"), + /* 160 */ "VBegin" OpHelp(""), + /* 161 */ "VCreate" OpHelp(""), + /* 162 */ "VDestroy" OpHelp(""), + /* 163 */ "VOpen" OpHelp(""), + /* 164 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), + /* 165 */ "VRename" OpHelp(""), + /* 166 */ "Pagecount" OpHelp(""), + /* 167 */ "MaxPgcnt" OpHelp(""), + /* 168 */ "Trace" OpHelp(""), + /* 169 */ "CursorHint" OpHelp(""), + /* 170 */ "Noop" OpHelp(""), + /* 171 */ "Explain" OpHelp(""), + /* 172 */ "Abortable" OpHelp(""), }; return azName[i]; } @@ -49018,9 +49131,7 @@ static void pcache1FreePage(PgHdr1 *p){ ** exists, this function falls back to sqlite3Malloc(). */ SQLITE_PRIVATE void *sqlite3PageMalloc(int sz){ - /* During rebalance operations on a corrupt database file, it is sometimes - ** (rarely) possible to overread the temporary page buffer by a few bytes. - ** Enlarge the allocation slightly so that this does not cause problems. */ + assert( sz<=65536+8 ); /* These allocations are never very large */ return pcache1Alloc(sz); } @@ -51304,6 +51415,9 @@ static const unsigned char aJournalMagic[] = { SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){ if( pPager->fd->pMethods==0 ) return 0; if( sqlite3PCacheIsDirty(pPager->pPCache) ) return 0; +#ifdef SQLITE_HAS_CODEC + if( pPager->xCodec!=0 ) return 0; +#endif #ifndef SQLITE_OMIT_WAL if( pPager->pWal ){ u32 iRead = 0; @@ -54253,8 +54367,14 @@ SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager *pPager, u32 *pPageSize, int nR rc = sqlite3OsFileSize(pPager->fd, &nByte); } if( rc==SQLITE_OK ){ - pNew = (char *)sqlite3PageMalloc(pageSize); - if( !pNew ) rc = SQLITE_NOMEM_BKPT; + /* 8 bytes of zeroed overrun space is sufficient so that the b-tree + * cell header parser will never run off the end of the allocation */ + pNew = (char *)sqlite3PageMalloc(pageSize+8); + if( !pNew ){ + rc = SQLITE_NOMEM_BKPT; + }else{ + memset(pNew+pageSize, 0, 8); + } } if( rc==SQLITE_OK ){ @@ -57635,8 +57755,12 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i */ pPg->flags &= ~PGHDR_NEED_SYNC; pPgOld = sqlite3PagerLookup(pPager, pgno); - assert( !pPgOld || pPgOld->nRef==1 ); + assert( !pPgOld || pPgOld->nRef==1 || CORRUPT_DB ); if( pPgOld ){ + if( pPgOld->nRef>1 ){ + sqlite3PagerUnrefNotNull(pPgOld); + return SQLITE_CORRUPT_BKPT; + } pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC); if( pPager->tempFile ){ /* Do not discard pages from an in-memory database since we might @@ -58164,7 +58288,7 @@ SQLITE_PRIVATE int sqlite3PagerSnapshotCheck(Pager *pPager, sqlite3_snapshot *pS */ SQLITE_PRIVATE void sqlite3PagerSnapshotUnlock(Pager *pPager){ assert( pPager->pWal ); - return sqlite3WalSnapshotUnlock(pPager->pWal); + sqlite3WalSnapshotUnlock(pPager->pWal); } #endif /* SQLITE_ENABLE_SNAPSHOT */ @@ -58765,7 +58889,7 @@ static SQLITE_NOINLINE int walIndexPageRealloc( /* Enlarge the pWal->apWiData[] array if required */ if( pWal->nWiData<=iPage ){ - int nByte = sizeof(u32*)*(iPage+1); + sqlite3_int64 nByte = sizeof(u32*)*(iPage+1); volatile u32 **apNew; apNew = (volatile u32 **)sqlite3_realloc64((void *)pWal->apWiData, nByte); if( !apNew ){ @@ -58869,6 +58993,7 @@ static void walChecksumBytes( assert( nByte>=8 ); assert( (nByte&0x00000007)==0 ); + assert( nByte<=65536 ); if( nativeCksum ){ do { @@ -59176,6 +59301,7 @@ static void walCleanupHash(Wal *pWal){ int iLimit = 0; /* Zero values greater than this */ int nByte; /* Number of bytes to zero in aPgno[] */ int i; /* Used to iterate through aHash[] */ + int rc; /* Return code form walHashGet() */ assert( pWal->writeLock ); testcase( pWal->hdr.mxFrame==HASHTABLE_NPAGE_ONE-1 ); @@ -59186,11 +59312,12 @@ static void walCleanupHash(Wal *pWal){ /* Obtain pointers to the hash-table and page-number array containing ** the entry that corresponds to frame pWal->hdr.mxFrame. It is guaranteed - ** that the page said hash-table and array reside on is already mapped. + ** that the page said hash-table and array reside on is already mapped.(1) */ assert( pWal->nWiData>walFramePage(pWal->hdr.mxFrame) ); assert( pWal->apWiData[walFramePage(pWal->hdr.mxFrame)] ); - walHashGet(pWal, walFramePage(pWal->hdr.mxFrame), &sLoc); + rc = walHashGet(pWal, walFramePage(pWal->hdr.mxFrame), &sLoc); + if( NEVER(rc) ) return; /* Defense-in-depth, in case (1) above is wrong */ /* Zero all hash-table entries that correspond to frame numbers greater ** than pWal->hdr.mxFrame. @@ -59804,7 +59931,7 @@ static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){ WalIterator *p; /* Return value */ int nSegment; /* Number of segments to merge */ u32 iLast; /* Last frame in log */ - int nByte; /* Number of bytes to allocate */ + sqlite3_int64 nByte; /* Number of bytes to allocate */ int i; /* Iterator variable */ ht_slot *aTmp; /* Temp space used by merge-sort */ int rc = SQLITE_OK; /* Return Code */ @@ -62340,7 +62467,7 @@ struct MemPage { u16 maxLocal; /* Copy of BtShared.maxLocal or BtShared.maxLeaf */ u16 minLocal; /* Copy of BtShared.minLocal or BtShared.minLeaf */ u16 cellOffset; /* Index in aData of first cell pointer */ - u16 nFree; /* Number of free bytes on the page */ + int nFree; /* Number of free bytes on the page. -1 for unknown */ u16 nCell; /* Number of cells on this page, local and ovfl */ u16 maskPage; /* Mask for page offset */ u16 aiOvfl[4]; /* Insert the i-th overflow cell before the aiOvfl-th @@ -63894,14 +64021,18 @@ moveto_done: */ static int btreeRestoreCursorPosition(BtCursor *pCur){ int rc; - int skipNext; + int skipNext = 0; assert( cursorOwnsBtShared(pCur) ); assert( pCur->eState>=CURSOR_REQUIRESEEK ); if( pCur->eState==CURSOR_FAULT ){ return pCur->skipNext; } pCur->eState = CURSOR_INVALID; - rc = btreeMoveto(pCur, pCur->pKey, pCur->nKey, 0, &skipNext); + if( sqlite3FaultSim(410) ){ + rc = SQLITE_IOERR; + }else{ + rc = btreeMoveto(pCur, pCur->pKey, pCur->nKey, 0, &skipNext); + } if( rc==SQLITE_OK ){ sqlite3_free(pCur->pKey); pCur->pKey = 0; @@ -64482,7 +64613,7 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ hdr = pPage->hdrOffset; cellOffset = pPage->cellOffset; nCell = pPage->nCell; - assert( nCell==get2byte(&data[hdr+3]) ); + assert( nCell==get2byte(&data[hdr+3]) || CORRUPT_DB ); iCellFirst = cellOffset + 2*nCell; usableSize = pPage->pBt->usableSize; @@ -64493,11 +64624,7 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ ** reconstruct the entire page. */ if( (int)data[hdr+7]<=nMaxFrag ){ int iFree = get2byte(&data[hdr+1]); - - /* If the initial freeblock offset were out of bounds, that would - ** have been detected by btreeInitPage() when it was computing the - ** number of free bytes on the page. */ - assert( iFree<=usableSize-4 ); + if( iFree>usableSize-4 ) return SQLITE_CORRUPT_PAGE(pPage); if( iFree ){ int iFree2 = get2byte(&data[iFree]); if( iFree2>usableSize-4 ) return SQLITE_CORRUPT_PAGE(pPage); @@ -64516,7 +64643,10 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ if( iFree2+sz2 > usableSize ) return SQLITE_CORRUPT_PAGE(pPage); memmove(&data[iFree+sz+sz2], &data[iFree+sz], iFree2-(iFree+sz)); sz += sz2; + }else if( iFree+sz>usableSize ){ + return SQLITE_CORRUPT_PAGE(pPage); } + cbrk = top+sz; assert( cbrk+(iFree-top) <= usableSize ); memmove(&data[cbrk], &data[top], iFree-top); @@ -64567,6 +64697,7 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ data[hdr+7] = 0; defragment_out: + assert( pPage->nFree>=0 ); if( data[hdr+7]+cbrk-iCellFirst!=pPage->nFree ){ return SQLITE_CORRUPT_PAGE(pPage); } @@ -64594,16 +64725,16 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ ** causes the fragmentation count to exceed 60. */ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){ - const int hdr = pPg->hdrOffset; - u8 * const aData = pPg->aData; - int iAddr = hdr + 1; - int pc = get2byte(&aData[iAddr]); - int x; - int usableSize = pPg->pBt->usableSize; - int size; /* Size of the free slot */ + const int hdr = pPg->hdrOffset; /* Offset to page header */ + u8 * const aData = pPg->aData; /* Page data */ + int iAddr = hdr + 1; /* Address of ptr to pc */ + int pc = get2byte(&aData[iAddr]); /* Address of a free slot */ + int x; /* Excess size of the slot */ + int maxPC = pPg->pBt->usableSize - nByte; /* Max address for a usable slot */ + int size; /* Size of the free slot */ assert( pc>0 ); - while( pc<=usableSize-4 ){ + while( pc<=maxPC ){ /* EVIDENCE-OF: R-22710-53328 The third and fourth bytes of each ** freeblock form a big-endian integer which is the size of the freeblock ** in bytes, including the 4-byte header. */ @@ -64611,10 +64742,7 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){ if( (x = size - nByte)>=0 ){ testcase( x==4 ); testcase( x==3 ); - if( size+pc > usableSize ){ - *pRc = SQLITE_CORRUPT_PAGE(pPg); - return 0; - }else if( x<4 ){ + if( x<4 ){ /* EVIDENCE-OF: R-11498-58022 In a well-formed b-tree page, the total ** number of bytes in fragments may not exceed 60. */ if( aData[hdr+7]>57 ) return 0; @@ -64623,21 +64751,31 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){ ** fragmented bytes within the page. */ memcpy(&aData[iAddr], &aData[pc], 2); aData[hdr+7] += (u8)x; + }else if( x+pc > maxPC ){ + /* This slot extends off the end of the usable part of the page */ + *pRc = SQLITE_CORRUPT_PAGE(pPg); + return 0; }else{ /* The slot remains on the free-list. Reduce its size to account - ** for the portion used by the new allocation. */ + ** for the portion used by the new allocation. */ put2byte(&aData[pc+2], x); } return &aData[pc + x]; } iAddr = pc; pc = get2byte(&aData[pc]); - if( pcmaxPC+nByte-4 ){ + /* The free slot chain extends off the end of the page */ *pRc = SQLITE_CORRUPT_PAGE(pPg); } - return 0; } @@ -64687,9 +64825,9 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ } } - /* If there is enough space between gap and top for one more cell pointer - ** array entry offset, and if the freelist is not empty, then search the - ** freelist looking for a free slot big enough to satisfy the request. + /* If there is enough space between gap and top for one more cell pointer, + ** and if the freelist is not empty, then search the + ** freelist looking for a slot big enough to satisfy the request. */ testcase( gap+2==top ); testcase( gap+1==top ); @@ -64711,6 +64849,7 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ testcase( gap+2+nByte==top ); if( gap+2+nByte>top ){ assert( pPage->nCell>0 || CORRUPT_DB ); + assert( pPage->nFree>=0 ); rc = defragmentPage(pPage, MIN(4, pPage->nFree - (2+nByte))); if( rc ) return rc; top = get2byteNotZero(&data[hdr+5]); @@ -64719,7 +64858,7 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ /* Allocate memory from the gap in between the cell pointer array - ** and the cell content area. The btreeInitPage() call has already + ** and the cell content area. The btreeComputeFreeSpace() call has already ** validated the freelist. Given that the freelist is valid, there ** is no way that the allocation can extend off the end of the page. ** The assert() below verifies the previous sentence. @@ -64738,7 +64877,7 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ ** ** Adjacent freeblocks are coalesced. ** -** Note that even though the freeblock list was checked by btreeInitPage(), +** Even though the freeblock list was checked by btreeComputeFreeSpace(), ** that routine will not detect overlap between cells or freeblocks. Nor ** does it detect cells or freeblocks that encrouch into the reserved bytes ** at the end of the page. So do additional corruption checks inside this @@ -64900,21 +65039,14 @@ static int decodeFlags(MemPage *pPage, int flagByte){ } /* -** Initialize the auxiliary information for a disk block. -** -** Return SQLITE_OK on success. If we see that the page does -** not contain a well-formed database page, then return -** SQLITE_CORRUPT. Note that a return of SQLITE_OK does not -** guarantee that the page is well-formed. It only shows that -** we failed to detect any corruption. +** Compute the amount of freespace on the page. In other words, fill +** in the pPage->nFree field. */ -static int btreeInitPage(MemPage *pPage){ +static int btreeComputeFreeSpace(MemPage *pPage){ int pc; /* Address of a freeblock within pPage->aData[] */ u8 hdr; /* Offset to beginning of page header */ u8 *data; /* Equal to pPage->aData */ - BtShared *pBt; /* The main btree structure */ int usableSize; /* Amount of usable space on each page */ - u16 cellOffset; /* Offset from start of page to first cell pointer */ int nFree; /* Number of unused bytes on the page */ int top; /* First byte of the cell content area */ int iCellFirst; /* First allowable cell or freeblock offset */ @@ -64926,71 +65058,18 @@ static int btreeInitPage(MemPage *pPage){ assert( pPage->pgno==sqlite3PagerPagenumber(pPage->pDbPage) ); assert( pPage == sqlite3PagerGetExtra(pPage->pDbPage) ); assert( pPage->aData == sqlite3PagerGetData(pPage->pDbPage) ); - assert( pPage->isInit==0 ); + assert( pPage->isInit==1 ); + assert( pPage->nFree<0 ); - pBt = pPage->pBt; + usableSize = pPage->pBt->usableSize; hdr = pPage->hdrOffset; data = pPage->aData; - /* EVIDENCE-OF: R-28594-02890 The one-byte flag at offset 0 indicating - ** the b-tree page type. */ - if( decodeFlags(pPage, data[hdr]) ){ - return SQLITE_CORRUPT_PAGE(pPage); - } - assert( pBt->pageSize>=512 && pBt->pageSize<=65536 ); - pPage->maskPage = (u16)(pBt->pageSize - 1); - pPage->nOverflow = 0; - usableSize = pBt->usableSize; - pPage->cellOffset = cellOffset = hdr + 8 + pPage->childPtrSize; - pPage->aDataEnd = &data[usableSize]; - pPage->aCellIdx = &data[cellOffset]; - pPage->aDataOfst = &data[pPage->childPtrSize]; /* EVIDENCE-OF: R-58015-48175 The two-byte integer at offset 5 designates ** the start of the cell content area. A zero value for this integer is ** interpreted as 65536. */ top = get2byteNotZero(&data[hdr+5]); - /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the - ** number of cells on the page. */ - pPage->nCell = get2byte(&data[hdr+3]); - if( pPage->nCell>MX_CELL(pBt) ){ - /* To many cells for a single page. The page must be corrupt */ - return SQLITE_CORRUPT_PAGE(pPage); - } - testcase( pPage->nCell==MX_CELL(pBt) ); - /* EVIDENCE-OF: R-24089-57979 If a page contains no cells (which is only - ** possible for a root page of a table that contains no rows) then the - ** offset to the cell content area will equal the page size minus the - ** bytes of reserved space. */ - assert( pPage->nCell>0 || top==usableSize || CORRUPT_DB ); - - /* A malformed database page might cause us to read past the end - ** of page when parsing a cell. - ** - ** The following block of code checks early to see if a cell extends - ** past the end of a page boundary and causes SQLITE_CORRUPT to be - ** returned if it does. - */ - iCellFirst = cellOffset + 2*pPage->nCell; + iCellFirst = hdr + 8 + pPage->childPtrSize + 2*pPage->nCell; iCellLast = usableSize - 4; - if( pBt->db->flags & SQLITE_CellSizeCk ){ - int i; /* Index into the cell pointer array */ - int sz; /* Size of a cell */ - - if( !pPage->leaf ) iCellLast--; - for(i=0; inCell; i++){ - pc = get2byteAligned(&data[cellOffset+i*2]); - testcase( pc==iCellFirst ); - testcase( pc==iCellLast ); - if( pciCellLast ){ - return SQLITE_CORRUPT_PAGE(pPage); - } - sz = pPage->xCellSize(pPage, &data[pc]); - testcase( pc+sz==usableSize ); - if( pc+sz>usableSize ){ - return SQLITE_CORRUPT_PAGE(pPage); - } - } - if( !pPage->leaf ) iCellLast++; - } /* Compute the total free space on the page ** EVIDENCE-OF: R-23588-34450 The two-byte integer at offset 1 gives the @@ -65038,7 +65117,100 @@ static int btreeInitPage(MemPage *pPage){ return SQLITE_CORRUPT_PAGE(pPage); } pPage->nFree = (u16)(nFree - iCellFirst); + return SQLITE_OK; +} + +/* +** Do additional sanity check after btreeInitPage() if +** PRAGMA cell_size_check=ON +*/ +static SQLITE_NOINLINE int btreeCellSizeCheck(MemPage *pPage){ + int iCellFirst; /* First allowable cell or freeblock offset */ + int iCellLast; /* Last possible cell or freeblock offset */ + int i; /* Index into the cell pointer array */ + int sz; /* Size of a cell */ + int pc; /* Address of a freeblock within pPage->aData[] */ + u8 *data; /* Equal to pPage->aData */ + int usableSize; /* Maximum usable space on the page */ + int cellOffset; /* Start of cell content area */ + + iCellFirst = pPage->cellOffset + 2*pPage->nCell; + usableSize = pPage->pBt->usableSize; + iCellLast = usableSize - 4; + data = pPage->aData; + cellOffset = pPage->cellOffset; + if( !pPage->leaf ) iCellLast--; + for(i=0; inCell; i++){ + pc = get2byteAligned(&data[cellOffset+i*2]); + testcase( pc==iCellFirst ); + testcase( pc==iCellLast ); + if( pciCellLast ){ + return SQLITE_CORRUPT_PAGE(pPage); + } + sz = pPage->xCellSize(pPage, &data[pc]); + testcase( pc+sz==usableSize ); + if( pc+sz>usableSize ){ + return SQLITE_CORRUPT_PAGE(pPage); + } + } + return SQLITE_OK; +} + +/* +** Initialize the auxiliary information for a disk block. +** +** Return SQLITE_OK on success. If we see that the page does +** not contain a well-formed database page, then return +** SQLITE_CORRUPT. Note that a return of SQLITE_OK does not +** guarantee that the page is well-formed. It only shows that +** we failed to detect any corruption. +*/ +static int btreeInitPage(MemPage *pPage){ + u8 *data; /* Equal to pPage->aData */ + BtShared *pBt; /* The main btree structure */ + + assert( pPage->pBt!=0 ); + assert( pPage->pBt->db!=0 ); + assert( sqlite3_mutex_held(pPage->pBt->mutex) ); + assert( pPage->pgno==sqlite3PagerPagenumber(pPage->pDbPage) ); + assert( pPage == sqlite3PagerGetExtra(pPage->pDbPage) ); + assert( pPage->aData == sqlite3PagerGetData(pPage->pDbPage) ); + assert( pPage->isInit==0 ); + + pBt = pPage->pBt; + data = pPage->aData + pPage->hdrOffset; + /* EVIDENCE-OF: R-28594-02890 The one-byte flag at offset 0 indicating + ** the b-tree page type. */ + if( decodeFlags(pPage, data[0]) ){ + return SQLITE_CORRUPT_PAGE(pPage); + } + assert( pBt->pageSize>=512 && pBt->pageSize<=65536 ); + pPage->maskPage = (u16)(pBt->pageSize - 1); + pPage->nOverflow = 0; + pPage->cellOffset = pPage->hdrOffset + 8 + pPage->childPtrSize; + pPage->aCellIdx = data + pPage->childPtrSize + 8; + pPage->aDataEnd = pPage->aData + pBt->usableSize; + pPage->aDataOfst = pPage->aData + pPage->childPtrSize; + /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the + ** number of cells on the page. */ + pPage->nCell = get2byte(&data[3]); + if( pPage->nCell>MX_CELL(pBt) ){ + /* To many cells for a single page. The page must be corrupt */ + return SQLITE_CORRUPT_PAGE(pPage); + } + testcase( pPage->nCell==MX_CELL(pBt) ); + /* EVIDENCE-OF: R-24089-57979 If a page contains no cells (which is only + ** possible for a root page of a table that contains no rows) then the + ** offset to the cell content area will equal the page size minus the + ** bytes of reserved space. */ + assert( pPage->nCell>0 + || get2byteNotZero(&data[5])==(int)pBt->usableSize + || CORRUPT_DB ); + pPage->nFree = -1; /* Indicate that this value is yet uncomputed */ pPage->isInit = 1; + if( pBt->db->flags & SQLITE_CellSizeCk ){ + return btreeCellSizeCheck(pPage); + } return SQLITE_OK; } @@ -65181,19 +65353,18 @@ static int getAndInitPage( if( pgno>btreePagecount(pBt) ){ rc = SQLITE_CORRUPT_BKPT; - goto getAndInitPage_error; + goto getAndInitPage_error1; } rc = sqlite3PagerGet(pBt->pPager, pgno, (DbPage**)&pDbPage, bReadOnly); if( rc ){ - goto getAndInitPage_error; + goto getAndInitPage_error1; } *ppPage = (MemPage*)sqlite3PagerGetExtra(pDbPage); if( (*ppPage)->isInit==0 ){ btreePageFromDbPage(pDbPage, pgno, pBt); rc = btreeInitPage(*ppPage); if( rc!=SQLITE_OK ){ - releasePage(*ppPage); - goto getAndInitPage_error; + goto getAndInitPage_error2; } } assert( (*ppPage)->pgno==pgno ); @@ -65203,12 +65374,13 @@ static int getAndInitPage( ** compatible with the root page. */ if( pCur && ((*ppPage)->nCell<1 || (*ppPage)->intKey!=pCur->curIntKey) ){ rc = SQLITE_CORRUPT_PGNO(pgno); - releasePage(*ppPage); - goto getAndInitPage_error; + goto getAndInitPage_error2; } return SQLITE_OK; -getAndInitPage_error: +getAndInitPage_error2: + releasePage(*ppPage); +getAndInitPage_error1: if( pCur ){ pCur->iPage--; pCur->pPage = pCur->apPage[pCur->iPage]; @@ -68289,23 +68461,6 @@ SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){ return rc; } -/* -** This function is a no-op if cursor pCur does not point to a valid row. -** Otherwise, if pCur is valid, configure it so that the next call to -** sqlite3BtreeNext() is a no-op. -*/ -#ifndef SQLITE_OMIT_WINDOWFUNC -SQLITE_PRIVATE void sqlite3BtreeSkipNext(BtCursor *pCur){ - /* We believe that the cursor must always be in the valid state when - ** this routine is called, but the proof is difficult, so we add an - ** ALWaYS() test just in case we are wrong. */ - if( ALWAYS(pCur->eState==CURSOR_VALID) ){ - pCur->eState = CURSOR_SKIPNEXT; - pCur->skipNext = 1; - } -} -#endif /* SQLITE_OMIT_WINDOWFUNC */ - /* Move the cursor to the last entry in the table. Return SQLITE_OK ** on success. Set *pRes to 0 if the cursor actually points to something ** or set *pRes to 1 if the table is empty. @@ -68571,7 +68726,7 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( sqlite3_free(pCellKey); goto moveto_finish; } - c = xRecordCompare(nCell, pCellKey, pIdxKey); + c = sqlite3VdbeRecordCompare(nCell, pCellKey, pIdxKey); sqlite3_free(pCellKey); } assert( @@ -69203,13 +69358,15 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){ MemPage *pPage1 = pBt->pPage1; /* Local reference to page 1 */ MemPage *pPage; /* Page being freed. May be NULL. */ int rc; /* Return Code */ - int nFree; /* Initial number of pages on free-list */ + u32 nFree; /* Initial number of pages on free-list */ assert( sqlite3_mutex_held(pBt->mutex) ); assert( CORRUPT_DB || iPage>1 ); assert( !pMemPage || pMemPage->pgno==iPage ); - if( iPage<2 ) return SQLITE_CORRUPT_BKPT; + if( iPage<2 || iPage>pBt->nPage ){ + return SQLITE_CORRUPT_BKPT; + } if( pMemPage ){ pPage = pMemPage; sqlite3PagerRef(pPage->pDbPage); @@ -69620,6 +69777,7 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){ assert( CORRUPT_DB || sz==cellSize(pPage, idx) ); assert( sqlite3PagerIswriteable(pPage->pDbPage) ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); + assert( pPage->nFree>=0 ); data = pPage->aData; ptr = &pPage->aCellIdx[2*idx]; pc = get2byte(ptr); @@ -69690,6 +69848,7 @@ static void insertCell( ** might be less than 8 (leaf-size + pointer) on the interior node. Hence ** the term after the || in the following assert(). */ assert( sz==pPage->xCellSize(pPage, pCell) || (sz==8 && iChild>0) ); + assert( pPage->nFree>=0 ); if( pPage->nOverflow || sz+2>pPage->nFree ){ if( pTemp ){ memcpy(pTemp, pCell, sz); @@ -69747,7 +69906,7 @@ static void insertCell( pPage->nCell++; /* increment the cell count */ if( (++data[pPage->hdrOffset+4])==0 ) data[pPage->hdrOffset+3]++; - assert( get2byte(&data[pPage->hdrOffset+3])==pPage->nCell ); + assert( get2byte(&data[pPage->hdrOffset+3])==pPage->nCell || CORRUPT_DB ); #ifndef SQLITE_OMIT_AUTOVACUUM if( pPage->pBt->autoVacuum ){ /* The cell may contain a pointer to an overflow page. If so, write @@ -69834,8 +69993,13 @@ static void insertCell( ** are used and they point to the leaf pages only, and the ixNx value are: ** ** ixNx[0] = Number of cells in Child-1. -** ixNx[1] = Number of cells in Child-1 and Child-2 + 1 for 1st divider. -** ixNx[2] = Number of cells in Child-1 and Child-2 + both divider cells +** ixNx[1] = Number of cells in Child-1 and Child-2. +** ixNx[2] = Total number of cells. +** +** Sometimes when deleting, a child page can have zero cells. In those +** cases, ixNx[] entries with higher indexes, and the corresponding apEnd[] +** entries, shift down. The end result is that each ixNx[] entry should +** be larger than the previous */ typedef struct CellArray CellArray; struct CellArray { @@ -70164,8 +70328,9 @@ static int editPage( int iCell = (iOld + pPg->aiOvfl[i]) - iNew; if( iCell>=0 && iCellaCellIdx[iCell * 2]; - assert( nCell>=iCell ); - memmove(&pCellptr[2], pCellptr, (nCell - iCell) * 2); + if( nCell>iCell ){ + memmove(&pCellptr[2], pCellptr, (nCell - iCell) * 2); + } nCell++; if( pageInsertArray( pPg, pBegin, &pData, pCellptr, @@ -70241,8 +70406,10 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){ assert( sqlite3_mutex_held(pPage->pBt->mutex) ); assert( sqlite3PagerIswriteable(pParent->pDbPage) ); assert( pPage->nOverflow==1 ); - + if( pPage->nCell==0 ) return SQLITE_CORRUPT_BKPT; /* dbfuzz001.test */ + assert( pPage->nFree>=0 ); + assert( pParent->nFree>=0 ); /* Allocate a new page. This page will become the right-sibling of ** pPage. Make the parent page writable, so that the new divider cell @@ -70412,6 +70579,7 @@ static void copyNodeContent(MemPage *pFrom, MemPage *pTo, int *pRC){ */ pTo->isInit = 0; rc = btreeInitPage(pTo); + if( rc==SQLITE_OK ) rc = btreeComputeFreeSpace(pTo); if( rc!=SQLITE_OK ){ *pRC = rc; return; @@ -70520,6 +70688,7 @@ static int balance_nonroot( if( !aOvflSpace ){ return SQLITE_NOMEM_BKPT; } + assert( pParent->nFree>=0 ); /* Find the sibling pages to balance. Also locate the cells in pParent ** that divide the siblings. An attempt is made to find NN siblings on @@ -70559,7 +70728,13 @@ static int balance_nonroot( memset(apOld, 0, (i+1)*sizeof(MemPage*)); goto balance_cleanup; } - nMaxCells += 1+apOld[i]->nCell+apOld[i]->nOverflow; + if( apOld[i]->nFree<0 ){ + rc = btreeComputeFreeSpace(apOld[i]); + if( rc ){ + memset(apOld, 0, (i)*sizeof(MemPage*)); + goto balance_cleanup; + } + } if( (i--)==0 ) break; if( pParent->nOverflow && i+nxDiv==pParent->aiOvfl[0] ){ @@ -70603,6 +70778,7 @@ static int balance_nonroot( /* Make nMaxCells a multiple of 4 in order to preserve 8-byte ** alignment */ + nMaxCells = nOld*(MX_CELL(pBt) + ArraySize(pParent->apOvfl)); nMaxCells = (nMaxCells + 3)&~3; /* @@ -70613,7 +70789,7 @@ static int balance_nonroot( + nMaxCells*sizeof(u16) /* b.szCell */ + pBt->pageSize; /* aSpace1 */ - assert( szScratch<=6*(int)pBt->pageSize ); + assert( szScratch<=7*(int)pBt->pageSize ); b.apCell = sqlite3StackAllocRaw(0, szScratch ); if( b.apCell==0 ){ rc = SQLITE_NOMEM_BKPT; @@ -70753,11 +70929,15 @@ static int balance_nonroot( MemPage *p = apOld[i]; b.apEnd[k] = p->aDataEnd; b.ixNx[k] = cntOld[i]; + if( k && b.ixNx[k]==b.ixNx[k-1] ){ + k--; /* Omit b.ixNx[] entry for child pages with no cells */ + } if( !leafData ){ k++; b.apEnd[k] = pParent->aDataEnd; b.ixNx[k] = cntOld[i]+1; } + assert( p->nFree>=0 ); szNew[i] = usableSpace - p->nFree; for(j=0; jnOverflow; j++){ szNew[i] += 2 + p->xCellSize(p, p->apOvfl[j]); @@ -70983,18 +71163,17 @@ static int balance_nonroot( if( ISAUTOVACUUM ){ MemPage *pOld; MemPage *pNew = pOld = apNew[0]; - u8 *aOld = pNew->aData; int cntOldNext = pNew->nCell + pNew->nOverflow; - int usableSize = pBt->usableSize; int iNew = 0; int iOld = 0; for(i=0; inCell + pOld->nOverflow + !leafData; - aOld = pOld->aData; } if( i==cntNew[iNew] ){ pNew = apNew[++iNew]; @@ -71009,7 +71188,7 @@ static int balance_nonroot( ** overflow cell), we can skip updating the pointer map entries. */ if( iOld>=nNew || pNew->pgno!=aPgno[iOld] - || !SQLITE_WITHIN(pCell,aOld,&aOld[usableSize]) + || !SQLITE_WITHIN(pCell,pOld->aData,pOld->aDataEnd) ){ if( !leafCorrection ){ ptrmapPut(pBt, get4byte(pCell), PTRMAP_BTREE, pNew->pgno, &rc); @@ -71160,7 +71339,8 @@ static int balance_nonroot( rc = defragmentPage(apNew[0], -1); testcase( rc!=SQLITE_OK ); assert( apNew[0]->nFree == - (get2byte(&apNew[0]->aData[5])-apNew[0]->cellOffset-apNew[0]->nCell*2) + (get2byteNotZero(&apNew[0]->aData[5]) - apNew[0]->cellOffset + - apNew[0]->nCell*2) || rc!=SQLITE_OK ); copyNodeContent(apNew[0], pParent, &rc); @@ -71259,7 +71439,7 @@ static int balance_deeper(MemPage *pRoot, MemPage **ppChild){ } assert( sqlite3PagerIswriteable(pChild->pDbPage) ); assert( sqlite3PagerIswriteable(pRoot->pDbPage) ); - assert( pChild->nCell==pRoot->nCell ); + assert( pChild->nCell==pRoot->nCell || CORRUPT_DB ); TRACE(("BALANCE: copy root %d into %d\n", pRoot->pgno, pChild->pgno)); @@ -71301,6 +71481,7 @@ static int balance(BtCursor *pCur){ int iPage = pCur->iPage; MemPage *pPage = pCur->pPage; + if( NEVER(pPage->nFree<0) && btreeComputeFreeSpace(pPage) ) break; if( iPage==0 ){ if( pPage->nOverflow ){ /* The root page of the b-tree is overfull. In this case call the @@ -71329,6 +71510,9 @@ static int balance(BtCursor *pCur){ int const iIdx = pCur->aiIdx[iPage-1]; rc = sqlite3PagerWrite(pParent->pDbPage); + if( rc==SQLITE_OK && pParent->nFree<0 ){ + rc = btreeComputeFreeSpace(pParent); + } if( rc==SQLITE_OK ){ #ifndef SQLITE_OMIT_QUICKBALANCE if( pPage->intKeyLeaf @@ -71675,6 +71859,10 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( pPage = pCur->pPage; assert( pPage->intKey || pX->nKey>=0 ); assert( pPage->leaf || !pPage->intKey ); + if( pPage->nFree<0 ){ + rc = btreeComputeFreeSpace(pPage); + if( rc ) return rc; + } TRACE(("INSERT: table=%d nkey=%lld ndata=%d page=%d %s\n", pCur->pgnoRoot, pX->nKey, pX->nData, pPage->pgno, @@ -71817,14 +72005,18 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ assert( pCur->curFlags & BTCF_WriteFlag ); assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); assert( !hasReadConflicts(p, pCur->pgnoRoot) ); - assert( pCur->ixpPage->nCell ); - assert( pCur->eState==CURSOR_VALID ); assert( (flags & ~(BTREE_SAVEPOSITION | BTREE_AUXDELETE))==0 ); + if( pCur->eState==CURSOR_REQUIRESEEK ){ + rc = btreeRestoreCursorPosition(pCur); + if( rc ) return rc; + } + assert( pCur->eState==CURSOR_VALID ); iCellDepth = pCur->iPage; iCellIdx = pCur->ix; pPage = pCur->pPage; pCell = findCell(pPage, iCellIdx); + if( pPage->nFree<0 && btreeComputeFreeSpace(pPage) ) return SQLITE_CORRUPT; /* If the bPreserve flag is set to true, then the cursor position must ** be preserved following this delete operation. If the current delete @@ -71895,6 +72087,10 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ Pgno n; unsigned char *pTmp; + if( pLeaf->nFree<0 ){ + rc = btreeComputeFreeSpace(pLeaf); + if( rc ) return rc; + } if( iCellDepthiPage-1 ){ n = pCur->apPage[iCellDepth+1]->pgno; }else{ @@ -72253,6 +72449,9 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){ assert( sqlite3BtreeHoldsMutex(p) ); assert( p->inTrans==TRANS_WRITE ); assert( iTable>=2 ); + if( iTable>btreePagecount(pBt) ){ + return SQLITE_CORRUPT_BKPT; + } rc = btreeGetPage(pBt, (Pgno)iTable, &pPage, 0); if( rc ) return rc; @@ -72601,10 +72800,10 @@ static void checkList( IntegrityCk *pCheck, /* Integrity checking context */ int isFreeList, /* True for a freelist. False for overflow page list */ int iPage, /* Page number for first page in the list */ - int N /* Expected number of pages in the list */ + u32 N /* Expected number of pages in the list */ ){ int i; - int expected = N; + u32 expected = N; int nErrAtStart = pCheck->nErr; while( iPage!=0 && pCheck->mxErr ){ DbPage *pOvflPage; @@ -72786,6 +72985,11 @@ static int checkTreePage( "btreeInitPage() returns error code %d", rc); goto end_of_check; } + if( (rc = btreeComputeFreeSpace(pPage))!=0 ){ + assert( rc==SQLITE_CORRUPT ); + checkAppendMsg(pCheck, "free space corruption", rc); + goto end_of_check; + } data = pPage->aData; hdr = pPage->hdrOffset; @@ -72858,7 +73062,7 @@ static int checkTreePage( /* Check the content overflow list */ if( info.nPayload>info.nLocal ){ - int nPage; /* Number of pages on the overflow chain */ + u32 nPage; /* Number of pages on the overflow chain */ Pgno pgnoOvfl; /* First page of the overflow chain */ assert( pc + info.nSize - 4 <= usableSize ); nPage = (info.nPayload - info.nLocal + usableSize - 5)/(usableSize - 4); @@ -72918,9 +73122,9 @@ static int checkTreePage( i = get2byte(&data[hdr+1]); while( i>0 ){ int size, j; - assert( (u32)i<=usableSize-4 ); /* Enforced by btreeInitPage() */ + assert( (u32)i<=usableSize-4 ); /* Enforced by btreeComputeFreeSpace() */ size = get2byte(&data[i+2]); - assert( (u32)(i+size)<=usableSize ); /* Enforced by btreeInitPage() */ + assert( (u32)(i+size)<=usableSize ); /* due to btreeComputeFreeSpace() */ btreeHeapInsert(heap, (((u32)i)<<16)|(i+size-1)); /* EVIDENCE-OF: R-58208-19414 The first 2 bytes of a freeblock are a ** big-endian integer which is the offset in the b-tree page of the next @@ -72929,8 +73133,8 @@ static int checkTreePage( j = get2byte(&data[i]); /* EVIDENCE-OF: R-06866-39125 Freeblocks are always connected in order of ** increasing offset. */ - assert( j==0 || j>i+size ); /* Enforced by btreeInitPage() */ - assert( (u32)j<=usableSize-4 ); /* Enforced by btreeInitPage() */ + assert( j==0 || j>i+size ); /* Enforced by btreeComputeFreeSpace() */ + assert( (u32)j<=usableSize-4 ); /* Enforced by btreeComputeFreeSpace() */ i = j; } /* Analyze the min-heap looking for overlap between cells and/or @@ -74274,7 +74478,7 @@ SQLITE_PRIVATE int sqlite3VdbeCheckMemInvariants(Mem *p){ ((p->flags&MEM_Static)!=0 ? 1 : 0) <= 1 ); /* No other bits set */ - assert( (p->flags & ~(MEM_Null|MEM_Term|MEM_Subtype + assert( (p->flags & ~(MEM_Null|MEM_Term|MEM_Subtype|MEM_FromBind |MEM_Dyn|MEM_Ephem|MEM_Static))==0 ); }else{ /* A pure NULL might have other flags, such as MEM_Static, MEM_Dyn, @@ -74395,8 +74599,7 @@ SQLITE_PRIVATE int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){ } /* -** Make sure pMem->z points to a writable allocation of at least -** min(n,32) bytes. +** Make sure pMem->z points to a writable allocation of at least n bytes. ** ** If the bPreserve argument is true, then copy of the content of ** pMem->z into the new allocation. pMem must be either a string or @@ -74415,7 +74618,6 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemGrow(Mem *pMem, int n, int bPre assert( pMem->szMalloc==0 || pMem->szMalloc==sqlite3DbMallocSize(pMem->db, pMem->zMalloc) ); - if( n<32 ) n = 32; if( pMem->szMalloc>0 && bPreserve && pMem->z==pMem->zMalloc ){ pMem->z = pMem->zMalloc = sqlite3DbReallocOrFree(pMem->db, pMem->z, n); bPreserve = 0; @@ -74517,13 +74719,15 @@ SQLITE_PRIVATE int sqlite3VdbeMemMakeWriteable(Mem *pMem){ SQLITE_PRIVATE int sqlite3VdbeMemExpandBlob(Mem *pMem){ int nByte; assert( pMem->flags & MEM_Zero ); - assert( pMem->flags&MEM_Blob ); + assert( (pMem->flags&MEM_Blob)!=0 || MemNullNochng(pMem) ); + testcase( sqlite3_value_nochange(pMem) ); assert( !sqlite3VdbeMemIsRowSet(pMem) ); assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); /* Set nByte to the number of bytes required to store the expanded blob. */ nByte = pMem->n + pMem->u.nZero; if( nByte<=0 ){ + if( (pMem->flags & MEM_Blob)==0 ) return SQLITE_OK; nByte = 1; } if( sqlite3VdbeMemGrow(pMem, nByte, 1) ){ @@ -75264,7 +75468,6 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetStr( assert( enc!=0 ); if( enc==SQLITE_UTF8 ){ nByte = 0x7fffffff & (int)strlen(z); - if( nByte>iLimit ) nByte = iLimit+1; }else{ for(nByte=0; nByte<=iLimit && (z[nByte] | z[nByte+1]); nByte+=2){} } @@ -75276,29 +75479,30 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetStr( ** management (one of MEM_Dyn or MEM_Static). */ if( xDel==SQLITE_TRANSIENT ){ - int nAlloc = nByte; + u32 nAlloc = nByte; if( flags&MEM_Term ){ nAlloc += (enc==SQLITE_UTF8?1:2); } if( nByte>iLimit ){ - return SQLITE_TOOBIG; + return sqlite3ErrorToParser(pMem->db, SQLITE_TOOBIG); } testcase( nAlloc==0 ); testcase( nAlloc==31 ); testcase( nAlloc==32 ); - if( sqlite3VdbeMemClearAndResize(pMem, MAX(nAlloc,32)) ){ + if( sqlite3VdbeMemClearAndResize(pMem, (int)MAX(nAlloc,32)) ){ return SQLITE_NOMEM_BKPT; } memcpy(pMem->z, z, nAlloc); - }else if( xDel==SQLITE_DYNAMIC ){ - sqlite3VdbeMemRelease(pMem); - pMem->zMalloc = pMem->z = (char *)z; - pMem->szMalloc = sqlite3DbMallocSize(pMem->db, pMem->zMalloc); }else{ sqlite3VdbeMemRelease(pMem); pMem->z = (char *)z; - pMem->xDel = xDel; - flags |= ((xDel==SQLITE_STATIC)?MEM_Static:MEM_Dyn); + if( xDel==SQLITE_DYNAMIC ){ + pMem->zMalloc = pMem->z; + pMem->szMalloc = sqlite3DbMallocSize(pMem->db, pMem->zMalloc); + }else{ + pMem->xDel = xDel; + flags |= ((xDel==SQLITE_STATIC)?MEM_Static:MEM_Dyn); + } } pMem->n = nByte; @@ -76266,9 +76470,11 @@ static int growOpArray(Vdbe *v, int nOp){ ** operation (without SQLITE_TEST_REALLOC_STRESS) is to double the current ** size of the op array or add 1KB of space, whichever is smaller. */ #ifdef SQLITE_TEST_REALLOC_STRESS - int nNew = (v->nOpAlloc>=512 ? v->nOpAlloc*2 : v->nOpAlloc+nOp); + sqlite3_int64 nNew = (v->nOpAlloc>=512 ? 2*(sqlite3_int64)v->nOpAlloc + : (sqlite3_int64)v->nOpAlloc+nOp); #else - int nNew = (v->nOpAlloc ? v->nOpAlloc*2 : (int)(1024/sizeof(Op))); + sqlite3_int64 nNew = (v->nOpAlloc ? 2*(sqlite3_int64)v->nOpAlloc + : (sqlite3_int64)(1024/sizeof(Op))); UNUSED_PARAMETER(nOp); #endif @@ -76748,6 +76954,7 @@ SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){ int opcode = pOp->opcode; if( opcode==OP_Destroy || opcode==OP_VUpdate || opcode==OP_VRename || opcode==OP_VDestroy + || (opcode==OP_Function0 && pOp->p4.pFunc->funcFlags&SQLITE_FUNC_INTERNAL) || ((opcode==OP_Halt || opcode==OP_HaltIfNull) && ((pOp->p1)!=SQLITE_OK && pOp->p2==OE_Abort)) ){ @@ -77055,7 +77262,7 @@ SQLITE_PRIVATE void sqlite3VdbeScanStatus( LogEst nEst, /* Estimated number of output rows */ const char *zName /* Name of table or index being scanned */ ){ - int nByte = (p->nScan+1) * sizeof(ScanStatus); + sqlite3_int64 nByte = (p->nScan+1) * sizeof(ScanStatus); ScanStatus *aNew; aNew = (ScanStatus*)sqlite3DbRealloc(p->db, p->aScan, nByte); if( aNew ){ @@ -78176,9 +78383,9 @@ SQLITE_PRIVATE void sqlite3VdbeIOTraceSql(Vdbe *p){ ** of a ReusableSpace object by the allocSpace() routine below. */ struct ReusableSpace { - u8 *pSpace; /* Available memory */ - int nFree; /* Bytes of available memory */ - int nNeeded; /* Total bytes that could not be allocated */ + u8 *pSpace; /* Available memory */ + sqlite3_int64 nFree; /* Bytes of available memory */ + sqlite3_int64 nNeeded; /* Total bytes that could not be allocated */ }; /* Try to allocate nByte bytes of 8-byte aligned bulk memory for pBuf @@ -78198,7 +78405,7 @@ struct ReusableSpace { static void *allocSpace( struct ReusableSpace *p, /* Bulk memory available for allocation */ void *pBuf, /* Pointer to a prior allocation */ - int nByte /* Bytes of memory needed */ + sqlite3_int64 nByte /* Bytes of memory needed */ ){ assert( EIGHT_BYTE_ALIGNMENT(p->pSpace) ); if( pBuf==0 ){ @@ -81155,7 +81362,7 @@ static SQLITE_NOINLINE void invokeProfileCallback(sqlite3 *db, Vdbe *p){ assert( p->zSql!=0 ); sqlite3OsCurrentTimeInt64(db->pVfs, &iNow); iElapse = (iNow - p->startTime)*1000000; -#ifndef SQLITE_OMIT_DEPRECATED +#ifndef SQLITE_OMIT_DEPRECATED if( db->xProfile ){ db->xProfile(db->pProfileArg, p->zSql, iElapse); } @@ -81363,6 +81570,11 @@ SQLITE_API int sqlite3_value_nochange(sqlite3_value *pVal){ return (pVal->flags&(MEM_Null|MEM_Zero))==(MEM_Null|MEM_Zero); } +/* Return true if a parameter value originated from an sqlite3_bind() */ +SQLITE_API int sqlite3_value_frombind(sqlite3_value *pVal){ + return (pVal->flags&MEM_FromBind)!=0; +} + /* Make a copy of an sqlite3_value object */ SQLITE_API sqlite3_value *sqlite3_value_dup(const sqlite3_value *pOrig){ @@ -82208,10 +82420,10 @@ SQLITE_API int sqlite3_column_type(sqlite3_stmt *pStmt, int i){ ** or a constant) then useTypes 2, 3, and 4 return NULL. */ static const void *columnName( - sqlite3_stmt *pStmt, - int N, - const void *(*xFunc)(Mem*), - int useType + sqlite3_stmt *pStmt, /* The statement */ + int N, /* Which column to get the name for */ + int useUtf16, /* True to return the name as UTF16 */ + int useType /* What type of name */ ){ const void *ret; Vdbe *p; @@ -82232,8 +82444,15 @@ static const void *columnName( N += useType*n; sqlite3_mutex_enter(db->mutex); assert( db->mallocFailed==0 ); - ret = xFunc(&p->aColName[N]); - /* A malloc may have failed inside of the xFunc() call. If this +#ifndef SQLITE_OMIT_UTF16 + if( useUtf16 ){ + ret = sqlite3_value_text16((sqlite3_value*)&p->aColName[N]); + }else +#endif + { + ret = sqlite3_value_text((sqlite3_value*)&p->aColName[N]); + } + /* A malloc may have failed inside of the _text() call. If this ** is the case, clear the mallocFailed flag and return NULL. */ if( db->mallocFailed ){ @@ -82250,13 +82469,11 @@ static const void *columnName( ** statement pStmt. */ SQLITE_API const char *sqlite3_column_name(sqlite3_stmt *pStmt, int N){ - return columnName( - pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_NAME); + return columnName(pStmt, N, 0, COLNAME_NAME); } #ifndef SQLITE_OMIT_UTF16 SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt *pStmt, int N){ - return columnName( - pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_NAME); + return columnName(pStmt, N, 1, COLNAME_NAME); } #endif @@ -82275,13 +82492,11 @@ SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt *pStmt, int N){ ** of the result set of SQL statement pStmt. */ SQLITE_API const char *sqlite3_column_decltype(sqlite3_stmt *pStmt, int N){ - return columnName( - pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_DECLTYPE); + return columnName(pStmt, N, 0, COLNAME_DECLTYPE); } #ifndef SQLITE_OMIT_UTF16 SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt *pStmt, int N){ - return columnName( - pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_DECLTYPE); + return columnName(pStmt, N, 1, COLNAME_DECLTYPE); } #endif /* SQLITE_OMIT_UTF16 */ #endif /* SQLITE_OMIT_DECLTYPE */ @@ -82293,13 +82508,11 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt *pStmt, int N){ ** anything else which is not an unambiguous reference to a database column. */ SQLITE_API const char *sqlite3_column_database_name(sqlite3_stmt *pStmt, int N){ - return columnName( - pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_DATABASE); + return columnName(pStmt, N, 0, COLNAME_DATABASE); } #ifndef SQLITE_OMIT_UTF16 SQLITE_API const void *sqlite3_column_database_name16(sqlite3_stmt *pStmt, int N){ - return columnName( - pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_DATABASE); + return columnName(pStmt, N, 1, COLNAME_DATABASE); } #endif /* SQLITE_OMIT_UTF16 */ @@ -82309,13 +82522,11 @@ SQLITE_API const void *sqlite3_column_database_name16(sqlite3_stmt *pStmt, int N ** anything else which is not an unambiguous reference to a database column. */ SQLITE_API const char *sqlite3_column_table_name(sqlite3_stmt *pStmt, int N){ - return columnName( - pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_TABLE); + return columnName(pStmt, N, 0, COLNAME_TABLE); } #ifndef SQLITE_OMIT_UTF16 SQLITE_API const void *sqlite3_column_table_name16(sqlite3_stmt *pStmt, int N){ - return columnName( - pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_TABLE); + return columnName(pStmt, N, 1, COLNAME_TABLE); } #endif /* SQLITE_OMIT_UTF16 */ @@ -82325,13 +82536,11 @@ SQLITE_API const void *sqlite3_column_table_name16(sqlite3_stmt *pStmt, int N){ ** anything else which is not an unambiguous reference to a database column. */ SQLITE_API const char *sqlite3_column_origin_name(sqlite3_stmt *pStmt, int N){ - return columnName( - pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_COLUMN); + return columnName(pStmt, N, 0, COLNAME_COLUMN); } #ifndef SQLITE_OMIT_UTF16 SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt *pStmt, int N){ - return columnName( - pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_COLUMN); + return columnName(pStmt, N, 1, COLNAME_COLUMN); } #endif /* SQLITE_OMIT_UTF16 */ #endif /* SQLITE_ENABLE_COLUMN_METADATA */ @@ -82699,6 +82908,14 @@ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt){ return pStmt ? ((Vdbe*)pStmt)->readOnly : 1; } +/* +** Return 1 if the statement is an EXPLAIN and return 2 if the +** statement is an EXPLAIN QUERY PLAN +*/ +SQLITE_API int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt){ + return pStmt ? ((Vdbe*)pStmt)->explain : 0; +} + /* ** Return true if the prepared statement is in need of being reset. */ @@ -83388,12 +83605,20 @@ SQLITE_API int sqlite3_found_count = 0; ** feature is used for test suite validation only and does not appear an ** production builds. ** -** M is an integer between 2 and 4. 2 indicates a ordinary two-way -** branch (I=0 means fall through and I=1 means taken). 3 indicates -** a 3-way branch where the third way is when one of the operands is -** NULL. 4 indicates the OP_Jump instruction which has three destinations -** depending on whether the first operand is less than, equal to, or greater -** than the second. +** M is the type of branch. I is the direction taken for this instance of +** the branch. +** +** M: 2 - two-way branch (I=0: fall-thru 1: jump ) +** 3 - two-way + NULL (I=0: fall-thru 1: jump 2: NULL ) +** 4 - OP_Jump (I=0: jump p1 1: jump p2 2: jump p3) +** +** In other words, if M is 2, then I is either 0 (for fall-through) or +** 1 (for when the branch is taken). If M is 3, the I is 0 for an +** ordinary fall-through, I is 1 if the branch was taken, and I is 2 +** if the result of comparison is NULL. For M=3, I=2 the jump may or +** may not be taken, depending on the SQLITE_JUMPIFNULL flags in p5. +** When M is 4, that means that an OP_Jump is being run. I is 0, 1, or 2 +** depending on if the operands are less than, equal, or greater than. ** ** iSrcLine is the source code line (from the __LINE__ macro) that ** generated the VDBE instruction combined with flag bits. The source @@ -83404,9 +83629,9 @@ SQLITE_API int sqlite3_found_count = 0; ** alternate branch are never taken. If a branch is never taken then ** flags should be 0x06 since only the fall-through approach is allowed. ** -** Bit 0x04 of the flags indicates an OP_Jump opcode that is only +** Bit 0x08 of the flags indicates an OP_Jump opcode that is only ** interested in equal or not-equal. In other words, I==0 and I==2 -** should be treated the same. +** should be treated as equivalent ** ** Since only a line number is retained, not the filename, this macro ** only works for amalgamation builds. But that is ok, since these macros @@ -83430,6 +83655,18 @@ SQLITE_API int sqlite3_found_count = 0; mNever = iSrcLine >> 24; assert( (I & mNever)==0 ); if( sqlite3GlobalConfig.xVdbeBranch==0 ) return; /*NO_TEST*/ + /* Invoke the branch coverage callback with three arguments: + ** iSrcLine - the line number of the VdbeCoverage() macro, with + ** flags removed. + ** I - Mask of bits 0x07 indicating which cases are are + ** fulfilled by this instance of the jump. 0x01 means + ** fall-thru, 0x02 means taken, 0x04 means NULL. Any + ** impossible cases (ex: if the comparison is never NULL) + ** are filled in automatically so that the coverage + ** measurement logic does not flag those impossible cases + ** as missed coverage. + ** M - Type of jump. Same as M argument above + */ I |= mNever; if( M==2 ) I |= 0x04; if( M==4 ){ @@ -83977,6 +84214,15 @@ SQLITE_PRIVATE int sqlite3VdbeExec( assert( p->magic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */ sqlite3VdbeEnter(p); +#ifndef SQLITE_OMIT_PROGRESS_CALLBACK + if( db->xProgress ){ + u32 iPrior = p->aCounter[SQLITE_STMTSTATUS_VM_STEP]; + assert( 0 < db->nProgressOps ); + nProgressLimit = db->nProgressOps - (iPrior % db->nProgressOps); + }else{ + nProgressLimit = 0xffffffff; + } +#endif if( p->rc==SQLITE_NOMEM ){ /* This happens if a malloc() inside a call to sqlite3_column_text() or ** sqlite3_column_text16() failed. */ @@ -83990,15 +84236,6 @@ SQLITE_PRIVATE int sqlite3VdbeExec( db->busyHandler.nBusy = 0; if( db->u1.isInterrupted ) goto abort_due_to_interrupt; sqlite3VdbeIOTraceSql(p); -#ifndef SQLITE_OMIT_PROGRESS_CALLBACK - if( db->xProgress ){ - u32 iPrior = p->aCounter[SQLITE_STMTSTATUS_VM_STEP]; - assert( 0 < db->nProgressOps ); - nProgressLimit = db->nProgressOps - (iPrior % db->nProgressOps); - }else{ - nProgressLimit = 0xffffffff; - } -#endif #ifdef SQLITE_DEBUG sqlite3BeginBenignMalloc(); if( p->pc==0 @@ -84174,10 +84411,11 @@ check_for_interrupt: ** If the progress callback returns non-zero, exit the virtual machine with ** a return code SQLITE_ABORT. */ - if( nVmStep>=nProgressLimit && db->xProgress!=0 ){ + while( nVmStep>=nProgressLimit && db->xProgress!=0 ){ assert( db->nProgressOps!=0 ); - nProgressLimit = nVmStep + db->nProgressOps - (nVmStep%db->nProgressOps); + nProgressLimit += db->nProgressOps; if( db->xProgress(db->pProgressArg) ){ + nProgressLimit = 0xffffffff; rc = SQLITE_INTERRUPT; goto abort_due_to_error; } @@ -84456,6 +84694,7 @@ case OP_String8: { /* same as TK_STRING, out2 */ if( encoding!=SQLITE_UTF8 ){ rc = sqlite3VdbeMemSetStr(pOut, pOp->p4.z, -1, SQLITE_UTF8, SQLITE_STATIC); assert( rc==SQLITE_OK || rc==SQLITE_TOOBIG ); + if( rc ) goto too_big; if( SQLITE_OK!=sqlite3VdbeChangeEncoding(pOut, encoding) ) goto no_mem; assert( pOut->szMalloc>0 && pOut->zMalloc==pOut->z ); assert( VdbeMemDynamic(pOut)==0 ); @@ -84468,7 +84707,6 @@ case OP_String8: { /* same as TK_STRING, out2 */ pOp->p4.z = pOut->z; pOp->p1 = pOut->n; } - testcase( rc==SQLITE_TOOBIG ); #endif if( pOp->p1>db->aLimit[SQLITE_LIMIT_LENGTH] ){ goto too_big; @@ -84590,7 +84828,10 @@ case OP_Variable: { /* out2 */ goto too_big; } pOut = &aMem[pOp->p2]; - sqlite3VdbeMemShallowCopy(pOut, pVar, MEM_Static); + if( VdbeMemDynamic(pOut) ) sqlite3VdbeMemSetNull(pOut); + memcpy(pOut, pVar, MEMCELLSIZE); + pOut->flags &= ~(MEM_Dyn|MEM_Ephem); + pOut->flags |= MEM_Static|MEM_FromBind; UPDATE_MAX_BLOBSIZE(pOut); break; } @@ -84723,18 +84964,6 @@ case OP_ResultRow: { assert( pOp->p1>0 ); assert( pOp->p1+pOp->p2<=(p->nMem+1 - p->nCursor)+1 ); -#ifndef SQLITE_OMIT_PROGRESS_CALLBACK - /* Run the progress counter just before returning. - */ - if( db->xProgress!=0 - && nVmStep>=nProgressLimit - && db->xProgress(db->pProgressArg)!=0 - ){ - rc = SQLITE_INTERRUPT; - goto abort_due_to_error; - } -#endif - /* If this statement has violated immediate foreign key constraints, do ** not return the number of rows modified. And do not RELEASE the statement ** transaction. It needs to be rolled back. */ @@ -85100,8 +85329,8 @@ case OP_MustBeInt: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; if( (pIn1->flags & MEM_Int)==0 ){ applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding); - VdbeBranchTaken((pIn1->flags&MEM_Int)==0, 2); if( (pIn1->flags & MEM_Int)==0 ){ + VdbeBranchTaken(1, 2); if( pOp->p2==0 ){ rc = SQLITE_MISMATCH; goto abort_due_to_error; @@ -85110,6 +85339,7 @@ case OP_MustBeInt: { /* jump, in1 */ } } } + VdbeBranchTaken(0, 2); MemSetTypeFlag(pIn1, MEM_Int); break; } @@ -85284,7 +85514,6 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ ** OP_Eq or OP_Ne) then take the jump or not depending on whether ** or not both operands are null. */ - assert( pOp->opcode==OP_Eq || pOp->opcode==OP_Ne ); assert( (flags1 & MEM_Cleared)==0 ); assert( (pOp->p5 & SQLITE_JUMPIFNULL)==0 || CORRUPT_DB ); testcase( (pOp->p5 & SQLITE_JUMPIFNULL)!=0 ); @@ -85293,7 +85522,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ ){ res = 0; /* Operands are equal */ }else{ - res = 1; /* Operands are not equal */ + res = ((flags3 & MEM_Null) ? -1 : +1); /* Operands are not equal */ } }else{ /* SQLITE_NULLEQ is clear and at least one operand is NULL, @@ -85411,7 +85640,7 @@ compare_op: pOut->u.i = res2; REGISTER_TRACE(pOp->p2, pOut); }else{ - VdbeBranchTaken(res!=0, (pOp->p5 & SQLITE_NULLEQ)?2:3); + VdbeBranchTaken(res2!=0, (pOp->p5 & SQLITE_NULLEQ)?2:3); if( res2 ){ goto jump_to_p2; } @@ -85961,15 +86190,15 @@ case OP_Column: { zEndHdr = zData + aOffset[0]; testcase( zHdr>=zEndHdr ); do{ - if( (t = zHdr[0])<0x80 ){ + if( (pC->aType[i] = t = zHdr[0])<0x80 ){ zHdr++; offset64 += sqlite3VdbeOneByteSerialTypeLen(t); }else{ zHdr += sqlite3GetVarint32(zHdr, &t); + pC->aType[i] = t; offset64 += sqlite3VdbeSerialTypeLen(t); } - pC->aType[i++] = t; - aOffset[i] = (u32)(offset64 & 0xffffffff); + aOffset[++i] = (u32)(offset64 & 0xffffffff); }while( i<=p2 && zHdrpKeyInfo = pOrig->pKeyInfo; pCx->isTable = pOrig->isTable; pCx->pgnoRoot = pOrig->pgnoRoot; + pCx->isOrdered = pOrig->isOrdered; rc = sqlite3BtreeCursor(pOrig->pBtx, pCx->pgnoRoot, BTREE_WRCSR, pCx->pKeyInfo, pCx->uc.pCursor); /* The sqlite3BtreeCursor() routine can only fail for the first cursor @@ -87935,14 +88165,7 @@ case OP_NewRowid: { /* out2 */ ** This instruction only works on tables. The equivalent instruction ** for indices is OP_IdxInsert. */ -/* Opcode: InsertInt P1 P2 P3 P4 P5 -** Synopsis: intkey=P3 data=r[P2] -** -** This works exactly like OP_Insert except that the key is the -** integer value P3, not the value of the integer stored in register P3. -*/ -case OP_Insert: -case OP_InsertInt: { +case OP_Insert: { Mem *pData; /* MEM cell holding data for the record to be inserted */ Mem *pKey; /* MEM cell holding key for the record */ VdbeCursor *pC; /* Cursor to table into which insert is written */ @@ -87963,16 +88186,11 @@ case OP_InsertInt: { REGISTER_TRACE(pOp->p2, pData); sqlite3VdbeIncrWriteCounter(p, pC); - if( pOp->opcode==OP_Insert ){ - pKey = &aMem[pOp->p3]; - assert( pKey->flags & MEM_Int ); - assert( memIsValid(pKey) ); - REGISTER_TRACE(pOp->p3, pKey); - x.nKey = pKey->u.i; - }else{ - assert( pOp->opcode==OP_InsertInt ); - x.nKey = pOp->p3; - } + pKey = &aMem[pOp->p3]; + assert( pKey->flags & MEM_Int ); + assert( memIsValid(pKey) ); + REGISTER_TRACE(pOp->p3, pKey); + x.nKey = pKey->u.i; if( pOp->p4type==P4_TABLE && HAS_UPDATE_HOOK(db) ){ assert( pC->iDb>=0 ); @@ -88492,7 +88710,7 @@ case OP_Sort: { /* jump */ p->aCounter[SQLITE_STMTSTATUS_SORT]++; /* Fall through into OP_Rewind */ } -/* Opcode: Rewind P1 P2 * * P5 +/* Opcode: Rewind P1 P2 * * * ** ** The next use of the Rowid or Column or Next instruction for P1 ** will refer to the first entry in the database table or index. @@ -88500,10 +88718,6 @@ case OP_Sort: { /* jump */ ** If the table or index is not empty, fall through to the following ** instruction. ** -** If P5 is non-zero and the table is not empty, then the "skip-next" -** flag is set on the cursor so that the next OP_Next instruction -** executed on it is a no-op. -** ** This opcode leaves the cursor configured to move in forward order, ** from the beginning toward the end. In other words, the cursor is ** configured to use Next, not Prev. @@ -88514,6 +88728,7 @@ case OP_Rewind: { /* jump */ int res; assert( pOp->p1>=0 && pOp->p1nCursor ); + assert( pOp->p5==0 ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( isSorter(pC)==(pOp->opcode==OP_SorterSort) ); @@ -88528,9 +88743,6 @@ case OP_Rewind: { /* jump */ pCrsr = pC->uc.pCursor; assert( pCrsr ); rc = sqlite3BtreeFirst(pCrsr, &res); -#ifndef SQLITE_OMIT_WINDOWFUNC - if( pOp->p5 ) sqlite3BtreeSkipNext(pCrsr); -#endif pC->deferredMoveto = 0; pC->cacheStatus = CACHE_STALE; } @@ -89540,8 +89752,7 @@ case OP_Program: { /* jump */ } #endif pOp = &aOp[-1]; - - break; + goto check_for_interrupt; } /* Opcode: Param P1 P2 * * * @@ -89913,6 +90124,7 @@ case OP_AggFinal: { assert( (pMem->flags & ~(MEM_Null|MEM_Agg))==0 ); #ifndef SQLITE_OMIT_WINDOWFUNC if( pOp->p3 ){ + memAboutToChange(p, &aMem[pOp->p3]); rc = sqlite3VdbeMemAggValue(pMem, &aMem[pOp->p3], pOp->p4.pFunc); pMem = &aMem[pOp->p3]; }else @@ -90950,7 +91162,16 @@ abort_due_to_error: ** release the mutexes on btrees that were acquired at the ** top. */ vdbe_return: - testcase( nVmStep>0 ); +#ifndef SQLITE_OMIT_PROGRESS_CALLBACK + while( nVmStep>=nProgressLimit && db->xProgress!=0 ){ + nProgressLimit += db->nProgressOps; + if( db->xProgress(db->pProgressArg) ){ + nProgressLimit = 0xffffffff; + rc = SQLITE_INTERRUPT; + goto abort_due_to_error; + } + } +#endif p->aCounter[SQLITE_STMTSTATUS_VM_STEP] += (int)nVmStep; sqlite3VdbeLeave(p); assert( rc!=SQLITE_OK || nExtraDelete==0 @@ -92037,7 +92258,7 @@ static int vdbePmaReadBlob( /* Extend the p->aAlloc[] allocation if required. */ if( p->nAllocnAlloc*2); + sqlite3_int64 nNew = MAX(128, 2*(sqlite3_int64)p->nAlloc); while( nByte>nNew ) nNew = nNew*2; aNew = sqlite3Realloc(p->aAlloc, nNew); if( !aNew ) return SQLITE_NOMEM_BKPT; @@ -93328,15 +93549,19 @@ SQLITE_PRIVATE int sqlite3VdbeSorterWrite( if( nMin>pSorter->nMemory ){ u8 *aNew; - int iListOff = (u8*)pSorter->list.pList - pSorter->list.aMemory; - int nNew = pSorter->nMemory * 2; + sqlite3_int64 nNew = 2 * (sqlite3_int64)pSorter->nMemory; + int iListOff = -1; + if( pSorter->list.pList ){ + iListOff = (u8*)pSorter->list.pList - pSorter->list.aMemory; + } while( nNew < nMin ) nNew = nNew*2; if( nNew > pSorter->mxPmaSize ) nNew = pSorter->mxPmaSize; if( nNew < nMin ) nNew = nMin; - aNew = sqlite3Realloc(pSorter->list.aMemory, nNew); if( !aNew ) return SQLITE_NOMEM_BKPT; - pSorter->list.pList = (SorterRecord*)&aNew[iListOff]; + if( iListOff>=0 ){ + pSorter->list.pList = (SorterRecord*)&aNew[iListOff]; + } pSorter->list.aMemory = aNew; pSorter->nMemory = nNew; } @@ -95323,6 +95548,10 @@ static int lookupName( sqlite3ErrorMsg(pParse, "misuse of aliased aggregate %s", zAs); return WRC_Abort; } + if( (pNC->ncFlags&NC_AllowWin)==0 && ExprHasProperty(pOrig, EP_Win) ){ + sqlite3ErrorMsg(pParse, "misuse of aliased window function %s",zAs); + return WRC_Abort; + } if( sqlite3ExprVectorSize(pOrig)!=1 ){ sqlite3ErrorMsg(pParse, "row value misused"); return WRC_Abort; @@ -95613,6 +95842,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ const char *zId; /* The function name. */ FuncDef *pDef; /* Information about the function */ u8 enc = ENC(pParse->db); /* The database encoding */ + int savedAllowFlags = (pNC->ncFlags & (NC_AllowAgg | NC_AllowWin)); assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); zId = pExpr->u.zToken; @@ -95734,8 +95964,11 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ pNC->nErr++; } if( is_agg ){ + /* Window functions may not be arguments of aggregate functions. + ** Or arguments of other window functions. But aggregate functions + ** may be arguments for window functions. */ #ifndef SQLITE_OMIT_WINDOWFUNC - pNC->ncFlags &= ~(pExpr->y.pWin ? NC_AllowWin : NC_AllowAgg); + pNC->ncFlags &= ~(NC_AllowWin | (!pExpr->y.pWin ? NC_AllowAgg : 0)); #else pNC->ncFlags &= ~NC_AllowAgg; #endif @@ -95756,7 +95989,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ pExpr->y.pWin->pNextWin = pSel->pWin; pSel->pWin = pExpr->y.pWin; } - pNC->ncFlags |= NC_AllowWin; + pNC->ncFlags |= NC_HasWin; }else #endif /* SQLITE_OMIT_WINDOWFUNC */ { @@ -95774,8 +96007,8 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ pNC2->ncFlags |= NC_HasAgg | (pDef->funcFlags & SQLITE_FUNC_MINMAX); } - pNC->ncFlags |= NC_AllowAgg; } + pNC->ncFlags |= savedAllowFlags; } /* FIX ME: Compute pExpr->affinity based on the expected return ** type of the function @@ -96131,6 +96364,38 @@ SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy( return 0; } +#ifndef SQLITE_OMIT_WINDOWFUNC +/* +** Walker callback for resolveRemoveWindows(). +*/ +static int resolveRemoveWindowsCb(Walker *pWalker, Expr *pExpr){ + if( ExprHasProperty(pExpr, EP_WinFunc) ){ + Window **pp; + for(pp=&pWalker->u.pSelect->pWin; *pp; pp=&(*pp)->pNextWin){ + if( *pp==pExpr->y.pWin ){ + *pp = (*pp)->pNextWin; + break; + } + } + } + return WRC_Continue; +} + +/* +** Remove any Window objects owned by the expression pExpr from the +** Select.pWin list of Select object pSelect. +*/ +static void resolveRemoveWindows(Select *pSelect, Expr *pExpr){ + Walker sWalker; + memset(&sWalker, 0, sizeof(Walker)); + sWalker.xExprCallback = resolveRemoveWindowsCb; + sWalker.u.pSelect = pSelect; + sqlite3WalkExpr(&sWalker, pExpr); +} +#else +# define resolveRemoveWindows(x,y) +#endif + /* ** pOrderBy is an ORDER BY or GROUP BY clause in SELECT statement pSelect. ** The Name context of the SELECT statement is pNC. zType is either @@ -96197,19 +96462,10 @@ static int resolveOrderGroupBy( } for(j=0; jpEList->nExpr; j++){ if( sqlite3ExprCompare(0, pE, pSelect->pEList->a[j].pExpr, -1)==0 ){ -#ifndef SQLITE_OMIT_WINDOWFUNC - if( ExprHasProperty(pE, EP_WinFunc) ){ - /* Since this window function is being changed into a reference - ** to the same window function the result set, remove the instance - ** of this window function from the Select.pWin list. */ - Window **pp; - for(pp=&pSelect->pWin; *pp; pp=&(*pp)->pNextWin){ - if( *pp==pE->y.pWin ){ - *pp = (*pp)->pNextWin; - } - } - } -#endif + /* Since this expresion is being changed into a reference + ** to an identical expression in the result set, remove all Window + ** objects belonging to the expression from the Select.pWin list. */ + resolveRemoveWindows(pSelect, pE); pItem->u.x.iOrderByCol = j+1; } } @@ -96289,7 +96545,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ */ for(i=0; ipSrc->nSrc; i++){ struct SrcList_item *pItem = &p->pSrc->a[i]; - if( pItem->pSelect ){ + if( pItem->pSelect && (pItem->pSelect->selFlags & SF_Resolved)==0 ){ NameContext *pNC; /* Used to iterate name contexts */ int nRef = 0; /* Refcount for pOuterNC and outer contexts */ const char *zSavedContext = pParse->zAuthContext; @@ -96421,6 +96677,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ } } +#ifndef SQLITE_OMIT_WINDOWFUNC if( IN_RENAME_OBJECT ){ Window *pWin; for(pWin=p->pWinDefn; pWin; pWin=pWin->pNextWin){ @@ -96431,6 +96688,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ } } } +#endif /* If this is part of a compound SELECT, check that it has the right ** number of expressions in the select list. */ @@ -96511,8 +96769,8 @@ SQLITE_PRIVATE int sqlite3ResolveExprNames( Walker w; if( pExpr==0 ) return SQLITE_OK; - savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg); - pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg); + savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin); + pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin); w.pParse = pNC->pParse; w.xExprCallback = resolveExprStep; w.xSelectCallback = resolveSelectStep; @@ -96528,9 +96786,11 @@ SQLITE_PRIVATE int sqlite3ResolveExprNames( #if SQLITE_MAX_EXPR_DEPTH>0 w.pParse->nHeight -= pExpr->nHeight; #endif - if( pNC->ncFlags & NC_HasAgg ){ - ExprSetProperty(pExpr, EP_Agg); - } + assert( EP_Agg==NC_HasAgg ); + assert( EP_Win==NC_HasWin ); + testcase( pNC->ncFlags & NC_HasAgg ); + testcase( pNC->ncFlags & NC_HasWin ); + ExprSetProperty(pExpr, pNC->ncFlags & (NC_HasAgg|NC_HasWin) ); pNC->ncFlags |= savedHasAgg; return pNC->nErr>0 || w.pParse->nErr>0; } @@ -97486,7 +97746,7 @@ SQLITE_PRIVATE Expr *sqlite3PExpr( p = sqlite3DbMallocRawNN(pParse->db, sizeof(Expr)); if( p ){ memset(p, 0, sizeof(Expr)); - p->op = op & TKFLG_MASK; + p->op = op & 0xff; p->iAgg = -1; } sqlite3ExprAttachSubtrees(pParse->db, p, pLeft, pRight); @@ -97951,7 +98211,7 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){ static With *withDup(sqlite3 *db, With *p){ With *pRet = 0; if( p ){ - int nByte = sizeof(*p) + sizeof(p->a[0]) * (p->nCte-1); + sqlite3_int64 nByte = sizeof(*p) + sizeof(p->a[0]) * (p->nCte-1); pRet = sqlite3DbMallocZero(db, nByte); if( pRet ){ int i; @@ -98216,7 +98476,7 @@ SQLITE_PRIVATE ExprList *sqlite3ExprListAppend( }else if( (pList->nExpr & (pList->nExpr-1))==0 ){ ExprList *pNew; pNew = sqlite3DbRealloc(db, pList, - sizeof(*pList)+(2*pList->nExpr - 1)*sizeof(pList->a[0])); + sizeof(*pList)+(2*(sqlite3_int64)pList->nExpr-1)*sizeof(pList->a[0])); if( pNew==0 ){ goto no_mem; } @@ -99181,14 +99441,11 @@ SQLITE_PRIVATE int sqlite3FindInIndex( eType = IN_INDEX_EPH; if( inFlags & IN_INDEX_LOOP ){ pParse->nQueryLoop = 0; - if( pX->pLeft->iColumn<0 && !ExprHasProperty(pX, EP_xIsSelect) ){ - eType = IN_INDEX_ROWID; - } }else if( prRhsHasNull ){ *prRhsHasNull = rMayHaveNull = ++pParse->nMem; } assert( pX->op==TK_IN ); - sqlite3CodeRhsOfIN(pParse, pX, iTab, eType==IN_INDEX_ROWID); + sqlite3CodeRhsOfIN(pParse, pX, iTab); if( rMayHaveNull ){ sqlite3SetHasNullFlag(v, iTab, rMayHaveNull); } @@ -99289,12 +99546,6 @@ SQLITE_PRIVATE void sqlite3VectorErrorMsg(Parse *pParse, Expr *pExpr){ ** however the cursor number returned might not be the same, as it might ** have been duplicated using OP_OpenDup. ** -** If parameter isRowid is non-zero, then LHS of the IN operator is guaranteed -** to be a non-null integer. In this case, the ephemeral table can be an -** table B-Tree that keyed by only integers. The more general cases uses -** an index B-Tree which can have arbitrary keys, but is slower to both -** read and write. -** ** If the LHS expression ("x" in the examples) is a column value, or ** the SELECT statement returns a column value, then the affinity of that ** column is used to build the index keys. If both 'x' and the @@ -99306,8 +99557,7 @@ SQLITE_PRIVATE void sqlite3VectorErrorMsg(Parse *pParse, Expr *pExpr){ SQLITE_PRIVATE void sqlite3CodeRhsOfIN( Parse *pParse, /* Parsing context */ Expr *pExpr, /* The IN operator */ - int iTab, /* Use this cursor number */ - int isRowid /* If true, LHS is a rowid */ + int iTab /* Use this cursor number */ ){ int addrOnce = 0; /* Address of the OP_Once instruction at top */ int addr; /* Address of OP_OpenEphemeral instruction */ @@ -99360,14 +99610,12 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN( /* Check to see if this is a vector IN operator */ pLeft = pExpr->pLeft; nVal = sqlite3ExprVectorSize(pLeft); - assert( !isRowid || nVal==1 ); /* Construct the ephemeral table that will contain the content of ** RHS of the IN operator. */ pExpr->iTable = iTab; - addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, - pExpr->iTable, (isRowid?0:nVal)); + addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, nVal); #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS if( ExprHasProperty(pExpr, EP_xIsSelect) ){ VdbeComment((v, "Result of SELECT %u", pExpr->x.pSelect->selId)); @@ -99375,7 +99623,7 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN( VdbeComment((v, "RHS of IN operator")); } #endif - pKeyInfo = isRowid ? 0 : sqlite3KeyInfoAlloc(pParse->db, nVal, 1); + pKeyInfo = sqlite3KeyInfoAlloc(pParse->db, nVal, 1); if( ExprHasProperty(pExpr, EP_xIsSelect) ){ /* Case 1: expr IN (SELECT ...) @@ -99389,7 +99637,6 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN( ExplainQueryPlan((pParse, 1, "%sLIST SUBQUERY %d", addrOnce?"":"CORRELATED ", pSelect->selId )); - assert( !isRowid ); /* If the LHS and RHS of the IN operator do not match, that ** error will have been caught long before we reach this point. */ if( ALWAYS(pEList->nExpr==nVal) ){ @@ -99442,10 +99689,8 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN( /* Loop through each expression in . */ r1 = sqlite3GetTempReg(pParse); r2 = sqlite3GetTempReg(pParse); - if( isRowid ) sqlite3VdbeAddOp4(v, OP_Blob, 0, r2, 0, "", P4_STATIC); for(i=pList->nExpr, pItem=pList->a; i>0; i--, pItem++){ Expr *pE2 = pItem->pExpr; - int iValToIns; /* If the expression is not constant then we will need to ** disable the test that was generated above that makes sure @@ -99458,20 +99703,9 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN( } /* Evaluate the expression and insert it into the temp table */ - if( isRowid && sqlite3ExprIsInteger(pE2, &iValToIns) ){ - sqlite3VdbeAddOp3(v, OP_InsertInt, iTab, r2, iValToIns); - }else{ - r3 = sqlite3ExprCodeTarget(pParse, pE2, r1); - if( isRowid ){ - sqlite3VdbeAddOp2(v, OP_MustBeInt, r3, - sqlite3VdbeCurrentAddr(v)+2); - VdbeCoverage(v); - sqlite3VdbeAddOp3(v, OP_Insert, iTab, r2, r3); - }else{ - sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1); - sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iTab, r2, r3, 1); - } - } + r3 = sqlite3ExprCodeTarget(pParse, pE2, r1); + sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1); + sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iTab, r2, r3, 1); } sqlite3ReleaseTempReg(pParse, r1); sqlite3ReleaseTempReg(pParse, r2); @@ -101687,6 +101921,17 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ */ SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr *p, int iTab){ Walker w; + p = sqlite3ExprSkipCollate(p); + while( p ){ + if( p->op==TK_NOTNULL ){ + p = p->pLeft; + }else if( p->op==TK_AND ){ + if( sqlite3ExprImpliesNonNullRow(p->pLeft, iTab) ) return 1; + p = p->pRight; + }else{ + break; + } + } w.xExprCallback = impliesNotNullRow; w.xSelectCallback = 0; w.xSelectCallback2 = 0; @@ -102268,15 +102513,15 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable( } #endif - /* Begin a transaction for database iDb. - ** Then modify the schema cookie (since the ALTER TABLE modifies the - ** schema). Open a statement transaction if the table is a virtual - ** table. - */ + /* Begin a transaction for database iDb. Then modify the schema cookie + ** (since the ALTER TABLE modifies the schema). Call sqlite3MayAbort(), + ** as the scalar functions (e.g. sqlite_rename_table()) invoked by the + ** nested SQL may raise an exception. */ v = sqlite3GetVdbe(pParse); if( v==0 ){ goto exit_rename_table; } + sqlite3MayAbort(pParse); /* figure out how many UTF-8 characters are in zName */ zTabName = pTab->zName; @@ -102345,7 +102590,6 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable( int i = ++pParse->nMem; sqlite3VdbeLoadString(v, i, zName); sqlite3VdbeAddOp4(v, OP_VRename, i, 0, 0,(const char*)pVTab, P4_VTAB); - sqlite3MayAbort(pParse); } #endif @@ -102666,6 +102910,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameColumn( ** uses the sqlite_rename_column() SQL function to compute the new ** CREATE statement text for the sqlite_master table. */ + sqlite3MayAbort(pParse); zNew = sqlite3NameFromToken(db, pNew); if( !zNew ) goto exit_rename_column; assert( pNew->n>0 ); @@ -105920,12 +106165,14 @@ static void attachFunc( sqlite3BtreeEnterAll(db); db->init.iDb = 0; db->mDbFlags &= ~(DBFLAG_SchemaKnownOk); - rc = sqlite3Init(db, &zErrDyn); + if( !REOPEN_AS_MEMDB(db) ){ + rc = sqlite3Init(db, &zErrDyn); + } sqlite3BtreeLeaveAll(db); assert( zErrDyn==0 || rc!=SQLITE_OK ); } #ifdef SQLITE_USER_AUTHENTICATION - if( rc==SQLITE_OK ){ + if( rc==SQLITE_OK && !REOPEN_AS_MEMDB(db) ){ u8 newAuth = 0; rc = sqlite3UserAuthCheckLogin(db, zName, &newAuth); if( newAuthauth.authLevel ){ @@ -106854,7 +107101,12 @@ SQLITE_PRIVATE void sqlite3NestedParse(Parse *pParse, const char *zFormat, ...){ zSql = sqlite3VMPrintf(db, zFormat, ap); va_end(ap); if( zSql==0 ){ - return; /* A malloc must have failed */ + /* This can result either from an OOM or because the formatted string + ** exceeds SQLITE_LIMIT_LENGTH. In the latter case, we need to set + ** an error */ + if( !db->mallocFailed ) pParse->rc = SQLITE_TOOBIG; + pParse->nErr++; + return; } pParse->nested++; memcpy(saveBuf, PARSE_TAIL(pParse), PARSE_TAIL_SZ); @@ -107994,7 +108246,8 @@ SQLITE_PRIVATE void sqlite3AddPrimaryKey( && sortOrder!=SQLITE_SO_DESC ){ if( IN_RENAME_OBJECT && pList ){ - sqlite3RenameTokenRemap(pParse, &pTab->iPKey, pList->a[0].pExpr); + Expr *pCExpr = sqlite3ExprSkipCollate(pList->a[0].pExpr); + sqlite3RenameTokenRemap(pParse, &pTab->iPKey, pCExpr); } pTab->iPKey = iCol; pTab->keyConf = (u8)onError; @@ -108415,6 +108668,7 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ pTab->iPKey = -1; }else{ pPk = sqlite3PrimaryKeyIndex(pTab); + assert( pPk!=0 ); /* ** Remove all redundant columns from the PRIMARY KEY. For example, change @@ -108584,6 +108838,11 @@ SQLITE_PRIVATE void sqlite3EndTable( if( p->tnum==1 ) p->tabFlags |= TF_Readonly; } + assert( (p->tabFlags & TF_HasPrimaryKey)==0 + || p->iPKey>=0 || sqlite3PrimaryKeyIndex(p)!=0 ); + assert( (p->tabFlags & TF_HasPrimaryKey)!=0 + || (p->iPKey<0 && sqlite3PrimaryKeyIndex(p)==0) ); + /* Special processing for WITHOUT ROWID Tables */ if( tabOpts & TF_WithoutRowid ){ if( (p->tabFlags & TF_Autoincrement) ){ @@ -109737,13 +109996,13 @@ SQLITE_PRIVATE void sqlite3CreateIndex( assert( pParse->nErr==0 ); if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 && db->init.busy==0 + && pTblName!=0 #if SQLITE_USER_AUTHENTICATION && sqlite3UserAuthTable(pTab->zName)==0 #endif #ifdef SQLITE_ALLOW_SQLITE_MASTER_INDEX && sqlite3StrICmp(&pTab->zName[7],"master")!=0 #endif - && sqlite3StrNICmp(&pTab->zName[7],"altertab_",9)!=0 ){ sqlite3ErrorMsg(pParse, "table %s may not be indexed", pTab->zName); goto exit_create_index; @@ -109847,6 +110106,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex( sqlite3ExprListSetSortOrder(pList, sortOrder); }else{ sqlite3ExprListCheckLength(pParse, pList, "index"); + if( pParse->nErr ) goto exit_create_index; } /* Figure out how many bytes of space are required to store explicitly @@ -109865,6 +110125,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex( */ nName = sqlite3Strlen30(zName); nExtraCol = pPk ? pPk->nKeyCol : 1; + assert( pList->nExpr + nExtraCol <= 32767 /* Fits in i16 */ ); pIndex = sqlite3AllocateIndexObject(db, pList->nExpr + nExtraCol, nName + nExtra + 1, &zExtra); if( db->mallocFailed ){ @@ -110348,9 +110609,9 @@ SQLITE_PRIVATE void *sqlite3ArrayAllocate( int *pIdx /* Write the index of a new slot here */ ){ char *z; - int n = *pnEntry; + sqlite3_int64 n = *pIdx = *pnEntry; if( (n & (n-1))==0 ){ - int sz = (n==0) ? 1 : 2*n; + sqlite3_int64 sz = (n==0) ? 1 : 2*n; void *pNew = sqlite3DbRealloc(db, pArray, sz*szEntry); if( pNew==0 ){ *pIdx = -1; @@ -110360,7 +110621,6 @@ SQLITE_PRIVATE void *sqlite3ArrayAllocate( } z = (char*)pArray; memset(&z[n * szEntry], 0, szEntry); - *pIdx = n; ++*pnEntry; return pArray; } @@ -110471,7 +110731,7 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge( /* Allocate additional space if needed */ if( (u32)pSrc->nSrc+nExtra>pSrc->nAlloc ){ SrcList *pNew; - int nAlloc = pSrc->nSrc*2+nExtra; + sqlite3_int64 nAlloc = 2*(sqlite3_int64)pSrc->nSrc+nExtra; sqlite3 *db = pParse->db; if( pSrc->nSrc+nExtra>=SQLITE_MAX_SRCLIST ){ @@ -110978,7 +111238,8 @@ SQLITE_PRIVATE void sqlite3UniqueConstraint( StrAccum errMsg; Table *pTab = pIdx->pTable; - sqlite3StrAccumInit(&errMsg, pParse->db, 0, 0, 200); + sqlite3StrAccumInit(&errMsg, pParse->db, 0, 0, + pParse->db->aLimit[SQLITE_LIMIT_LENGTH]); if( pIdx->aColExpr ){ sqlite3_str_appendf(&errMsg, "index '%q'", pIdx->zName); }else{ @@ -111227,7 +111488,7 @@ SQLITE_PRIVATE With *sqlite3WithAdd( } if( pWith ){ - int nByte = sizeof(*pWith) + (sizeof(pWith->a[1]) * pWith->nCte); + sqlite3_int64 nByte = sizeof(*pWith) + (sizeof(pWith->a[1]) * pWith->nCte); pNew = sqlite3DbRealloc(db, pWith, nByte); }else{ pNew = sqlite3DbMallocZero(db, sizeof(*pWith)); @@ -114535,6 +114796,10 @@ static void setLikeOptFlag(sqlite3 *db, const char *zName, u8 flagVal){ if( ALWAYS(pDef) ){ pDef->funcFlags |= flagVal; } + pDef = sqlite3FindFunction(db, zName, 3, SQLITE_UTF8, 0); + if( pDef ){ + pDef->funcFlags |= flagVal; + } } /* @@ -117857,7 +118122,9 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn, aRegIdx[ix]); VdbeComment((v, "for %s", pIdx->zName)); #ifdef SQLITE_ENABLE_NULL_TRIM - if( pIdx->idxType==2 ) sqlite3SetMakeRecordP5(v, pIdx->pTable); + if( pIdx->idxType==SQLITE_IDXTYPE_PRIMARYKEY ){ + sqlite3SetMakeRecordP5(v, pIdx->pTable); + } #endif /* In an UPDATE operation, if this index is the PRIMARY KEY index @@ -118107,10 +118374,13 @@ SQLITE_PRIVATE void sqlite3CompleteInsertion( pik_flags |= (update_flags & OPFLAG_SAVEPOSITION); #ifdef SQLITE_ENABLE_PREUPDATE_HOOK if( update_flags==0 ){ - sqlite3VdbeAddOp4(v, OP_InsertInt, - iIdxCur+i, aRegIdx[i], 0, (char*)pTab, P4_TABLE + int r = sqlite3GetTempReg(pParse); + sqlite3VdbeAddOp2(v, OP_Integer, 0, r); + sqlite3VdbeAddOp4(v, OP_Insert, + iIdxCur+i, aRegIdx[i], r, (char*)pTab, P4_TABLE ); sqlite3VdbeChangeP5(v, OPFLAG_ISNOOP); + sqlite3ReleaseTempReg(pParse, r); } #endif } @@ -118458,6 +118728,13 @@ static int xferOptimization( if( pSrcIdx==0 ){ return 0; /* pDestIdx has no corresponding index in pSrc */ } + if( pSrcIdx->tnum==pDestIdx->tnum && pSrc->pSchema==pDest->pSchema + && sqlite3FaultSim(411)==SQLITE_OK ){ + /* The sqlite3FaultSim() call allows this corruption test to be + ** bypassed during testing, in order to exercise other corruption tests + ** further downstream. */ + return 0; /* Corrupt schema - two indexes on the same btree */ + } } #ifndef SQLITE_OMIT_CHECK if( pDest->pCheck && sqlite3ExprListCompare(pSrc->pCheck,pDest->pCheck,-1) ){ @@ -118535,7 +118812,7 @@ static int xferOptimization( sqlite3RowidConstraint(pParse, onError, pDest); sqlite3VdbeJumpHere(v, addr2); autoIncStep(pParse, regAutoinc, regRowid); - }else if( pDest->pIndex==0 ){ + }else if( pDest->pIndex==0 && !(db->mDbFlags & DBFLAG_VacuumInto) ){ addr1 = sqlite3VdbeAddOp2(v, OP_NewRowid, iDest, regRowid); }else{ addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid); @@ -118598,7 +118875,7 @@ static int xferOptimization( sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest); } } - if( !HasRowid(pSrc) && pDestIdx->idxType==2 ){ + if( !HasRowid(pSrc) && pDestIdx->idxType==SQLITE_IDXTYPE_PRIMARYKEY ){ idxInsFlags |= OPFLAG_NCHANGE; } sqlite3VdbeAddOp2(v, OP_IdxInsert, iDest, regData); @@ -119110,6 +119387,9 @@ struct sqlite3_api_routines { void(*xDestroy)(void*)); /* Version 3.26.0 and later */ const char *(*normalized_sql)(sqlite3_stmt*); + /* Version 3.28.0 and later */ + int (*stmt_isexplain)(sqlite3_stmt*); + int (*value_frombind)(sqlite3_value*); }; /* @@ -119399,6 +119679,9 @@ typedef int (*sqlite3_loadext_entry)( #define sqlite3_create_window_function sqlite3_api->create_window_function /* Version 3.26.0 and later */ #define sqlite3_normalized_sql sqlite3_api->normalized_sql +/* Version 3.28.0 and later */ +#define sqlite3_stmt_isexplain sqlite3_api->isexplain +#define sqlite3_value_frombind sqlite3_api->frombind #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) @@ -119858,10 +120141,13 @@ static const sqlite3_api_routines sqlite3Apis = { sqlite3_create_window_function, /* Version 3.26.0 and later */ #ifdef SQLITE_ENABLE_NORMALIZE - sqlite3_normalized_sql + sqlite3_normalized_sql, #else - 0 + 0, #endif + /* Version 3.28.0 and later */ + sqlite3_stmt_isexplain, + sqlite3_value_frombind }; /* @@ -129641,7 +129927,7 @@ static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){ ** ** If regAcc is non-zero and there are no min() or max() aggregates ** in pAggInfo, then only populate the pAggInfo->nAccumulator accumulator -** registers i register regAcc contains 0. The caller will take care +** registers if register regAcc contains 0. The caller will take care ** of setting and clearing regAcc. */ static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){ @@ -133733,11 +134019,11 @@ build_vacuum_end: /* ** This routine implements the OP_Vacuum opcode of the VDBE. */ -SQLITE_PRIVATE int sqlite3RunVacuum( +SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum( char **pzErrMsg, /* Write error message here */ sqlite3 *db, /* Database connection */ int iDb, /* Which attached DB to vacuum */ - sqlite3_value *pOut /* Write results here, if not NULL */ + sqlite3_value *pOut /* Write results here, if not NULL. VACUUM INTO */ ){ int rc = SQLITE_OK; /* Return code from service routines */ Btree *pMain; /* The database being vacuumed */ @@ -133746,6 +134032,7 @@ SQLITE_PRIVATE int sqlite3RunVacuum( u64 saved_flags; /* Saved value of db->flags */ int saved_nChange; /* Saved value of db->nChange */ int saved_nTotalChange; /* Saved value of db->nTotalChange */ + u32 saved_openFlags; /* Saved value of db->openFlags */ u8 saved_mTrace; /* Saved trace settings */ Db *pDb = 0; /* Database to detach at end of vacuum */ int isMemDb; /* True if vacuuming a :memory: database */ @@ -133756,18 +134043,21 @@ SQLITE_PRIVATE int sqlite3RunVacuum( if( !db->autoCommit ){ sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction"); - return SQLITE_ERROR; + return SQLITE_ERROR; /* IMP: R-12218-18073 */ } if( db->nVdbeActive>1 ){ sqlite3SetString(pzErrMsg, db,"cannot VACUUM - SQL statements in progress"); - return SQLITE_ERROR; + return SQLITE_ERROR; /* IMP: R-15610-35227 */ } + saved_openFlags = db->openFlags; if( pOut ){ if( sqlite3_value_type(pOut)!=SQLITE_TEXT ){ sqlite3SetString(pzErrMsg, db, "non-text filename"); return SQLITE_ERROR; } zOut = (const char*)sqlite3_value_text(pOut); + db->openFlags &= ~SQLITE_OPEN_READONLY; + db->openFlags |= SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE; }else{ zOut = ""; } @@ -133806,6 +134096,7 @@ SQLITE_PRIVATE int sqlite3RunVacuum( */ nDb = db->nDb; rc = execSqlF(db, pzErrMsg, "ATTACH %Q AS vacuum_db", zOut); + db->openFlags = saved_openFlags; if( rc!=SQLITE_OK ) goto end_of_vacuum; assert( (db->nDb-1)==nDb ); pDb = &db->aDb[nDb]; @@ -133819,6 +134110,7 @@ SQLITE_PRIVATE int sqlite3RunVacuum( sqlite3SetString(pzErrMsg, db, "output file already exists"); goto end_of_vacuum; } + db->mDbFlags |= DBFLAG_VacuumInto; } nRes = sqlite3BtreeGetOptimalReserve(pMain); @@ -134307,9 +134599,13 @@ SQLITE_PRIVATE void sqlite3VtabClear(sqlite3 *db, Table *p){ ** string will be freed automatically when the table is ** deleted. */ -static void addModuleArgument(sqlite3 *db, Table *pTable, char *zArg){ - int nBytes = sizeof(char *)*(2+pTable->nModuleArg); +static void addModuleArgument(Parse *pParse, Table *pTable, char *zArg){ + sqlite3_int64 nBytes = sizeof(char *)*(2+pTable->nModuleArg); char **azModuleArg; + sqlite3 *db = pParse->db; + if( pTable->nModuleArg+3>=db->aLimit[SQLITE_LIMIT_COLUMN] ){ + sqlite3ErrorMsg(pParse, "too many columns on %s", pTable->zName); + } azModuleArg = sqlite3DbRealloc(db, pTable->azModuleArg, nBytes); if( azModuleArg==0 ){ sqlite3DbFree(db, zArg); @@ -134344,9 +134640,9 @@ SQLITE_PRIVATE void sqlite3VtabBeginParse( db = pParse->db; assert( pTable->nModuleArg==0 ); - addModuleArgument(db, pTable, sqlite3NameFromToken(db, pModuleName)); - addModuleArgument(db, pTable, 0); - addModuleArgument(db, pTable, sqlite3DbStrDup(db, pTable->zName)); + addModuleArgument(pParse, pTable, sqlite3NameFromToken(db, pModuleName)); + addModuleArgument(pParse, pTable, 0); + addModuleArgument(pParse, pTable, sqlite3DbStrDup(db, pTable->zName)); assert( (pParse->sNameToken.z==pName2->z && pName2->z!=0) || (pParse->sNameToken.z==pName1->z && pName2->z==0) ); @@ -134379,7 +134675,7 @@ static void addArgumentToVtab(Parse *pParse){ const char *z = (const char*)pParse->sArg.z; int n = pParse->sArg.n; sqlite3 *db = pParse->db; - addModuleArgument(db, pParse->pNewTable, sqlite3DbStrNDup(db, z, n)); + addModuleArgument(pParse, pParse->pNewTable, sqlite3DbStrNDup(db, z, n)); } } @@ -134668,7 +134964,8 @@ static int growVTrans(sqlite3 *db){ /* Grow the sqlite3.aVTrans array if required */ if( (db->nVTrans%ARRAY_INCR)==0 ){ VTable **aVTrans; - int nBytes = sizeof(sqlite3_vtab *) * (db->nVTrans + ARRAY_INCR); + sqlite3_int64 nBytes = sizeof(sqlite3_vtab*)* + ((sqlite3_int64)db->nVTrans + ARRAY_INCR); aVTrans = sqlite3DbRealloc(db, (void *)db->aVTrans, nBytes); if( !aVTrans ){ return SQLITE_NOMEM_BKPT; @@ -135164,9 +135461,9 @@ SQLITE_PRIVATE int sqlite3VtabEponymousTableInit(Parse *pParse, Module *pMod){ pTab->pSchema = db->aDb[0].pSchema; assert( pTab->nModuleArg==0 ); pTab->iPKey = -1; - addModuleArgument(db, pTab, sqlite3DbStrDup(db, pTab->zName)); - addModuleArgument(db, pTab, 0); - addModuleArgument(db, pTab, sqlite3DbStrDup(db, pTab->zName)); + addModuleArgument(pParse, pTab, sqlite3DbStrDup(db, pTab->zName)); + addModuleArgument(pParse, pTab, 0); + addModuleArgument(pParse, pTab, sqlite3DbStrDup(db, pTab->zName)); rc = vtabCallConstructor(db, pTab, pMod, pModule->xConnect, &zErr); if( rc ){ sqlite3ErrorMsg(pParse, "%s", zErr); @@ -136427,7 +136724,6 @@ static int codeEqualityTerm( if( pLoop->aLTerm[i]->pExpr==pX ){ int iOut = iReg + i - iEq; if( eType==IN_INDEX_ROWID ){ - testcase( nEq>1 ); /* Happens with a UNIQUE index on ROWID */ pIn->addrInTop = sqlite3VdbeAddOp2(v, OP_Rowid, iTab, iOut); }else{ int iCol = aiMap ? aiMap[iMap++] : 0; @@ -137005,6 +137301,34 @@ static void whereIndexExprTrans( } } +/* +** The pTruth expression is always true because it is the WHERE clause +** a partial index that is driving a query loop. Look through all of the +** WHERE clause terms on the query, and if any of those terms must be +** true because pTruth is true, then mark those WHERE clause terms as +** coded. +*/ +static void whereApplyPartialIndexConstraints( + Expr *pTruth, + int iTabCur, + WhereClause *pWC +){ + int i; + WhereTerm *pTerm; + while( pTruth->op==TK_AND ){ + whereApplyPartialIndexConstraints(pTruth->pLeft, iTabCur, pWC); + pTruth = pTruth->pRight; + } + for(i=0, pTerm=pWC->a; inTerm; i++, pTerm++){ + Expr *pExpr; + if( pTerm->wtFlags & TERM_CODED ) continue; + pExpr = pTerm->pExpr; + if( sqlite3ExprCompare(0, pExpr, pTruth, iTabCur)==0 ){ + pTerm->wtFlags |= TERM_CODED; + } + } +} + /* ** Generate code for the start of the iLevel-th loop in the WHERE clause ** implementation described by pWInfo. @@ -137189,6 +137513,9 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( sqlite3VdbeAddOp3(v, OP_SeekRowid, iCur, addrNxt, iRowidReg); VdbeCoverage(v); pLevel->op = OP_Noop; + if( (pTerm->prereqAll & pLevel->notReady)==0 ){ + pTerm->wtFlags |= TERM_CODED; + } }else if( (pLoop->wsFlags & WHERE_IPK)!=0 && (pLoop->wsFlags & WHERE_COLUMN_RANGE)!=0 ){ @@ -137611,6 +137938,14 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( whereIndexExprTrans(pIdx, iCur, iIdxCur, pWInfo); } + /* If a partial index is driving the loop, try to eliminate WHERE clause + ** terms from the query that must be true due to the WHERE clause of + ** the partial index + */ + if( pIdx->pPartIdxWhere ){ + whereApplyPartialIndexConstraints(pIdx->pPartIdxWhere, iCur, pWC); + } + /* Record the instruction used to terminate the loop. */ if( pLoop->wsFlags & WHERE_ONEROW ){ pLevel->op = OP_Noop; @@ -137774,7 +138109,12 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( pAndExpr = sqlite3ExprAnd(db, pAndExpr, pExpr); } if( pAndExpr ){ - pAndExpr = sqlite3PExpr(pParse, TK_AND|TKFLG_DONTFOLD, 0, pAndExpr); + /* The extra 0x10000 bit on the opcode is masked off and does not + ** become part of the new Expr.op. However, it does make the + ** op==TK_AND comparison inside of sqlite3PExpr() false, and this + ** prevents sqlite3PExpr() from implementing AND short-circuit + ** optimization, which we do not want here. */ + pAndExpr = sqlite3PExpr(pParse, TK_AND|0x10000, 0, pAndExpr); } } @@ -138004,8 +138344,9 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( u32 x = pLevel->iLikeRepCntr; if( x>0 ){ skipLikeAddr = sqlite3VdbeAddOp1(v, (x&1)?OP_IfNot:OP_If,(int)(x>>1)); + VdbeCoverageIf(v, (x&1)==1); + VdbeCoverageIf(v, (x&1)==0); } - VdbeCoverage(v); #endif } #ifdef WHERETRACE_ENABLED /* 0xffff */ @@ -139607,6 +139948,12 @@ SQLITE_PRIVATE Bitmask sqlite3WhereExprUsageNN(WhereMaskSet *pMaskSet, Expr *p){ }else if( p->x.pList ){ mask |= sqlite3WhereExprListUsage(pMaskSet, p->x.pList); } +#ifndef SQLITE_OMIT_WINDOWFUNC + if( p->op==TK_FUNCTION && p->y.pWin ){ + mask |= sqlite3WhereExprListUsage(pMaskSet, p->y.pWin->pPartition); + mask |= sqlite3WhereExprListUsage(pMaskSet, p->y.pWin->pOrderBy); + } +#endif return mask; } SQLITE_PRIVATE Bitmask sqlite3WhereExprUsage(WhereMaskSet *pMaskSet, Expr *p){ @@ -143023,11 +143370,11 @@ static int whereLoopAddVirtual( rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn); /* If the call to xBestIndex() with all terms enabled produced a plan - ** that does not require any source tables (IOW: a plan with mBest==0), - ** then there is no point in making any further calls to xBestIndex() - ** since they will all return the same result (if the xBestIndex() - ** implementation is sane). */ - if( rc==SQLITE_OK && (mBest = (pNew->prereq & ~mPrereq))!=0 ){ + ** that does not require any source tables (IOW: a plan with mBest==0) + ** and does not use an IN(...) operator, then there is no point in making + ** any further calls to xBestIndex() since they will all return the same + ** result (if the xBestIndex() implementation is sane). */ + if( rc==SQLITE_OK && ((mBest = (pNew->prereq & ~mPrereq))!=0 || bIn) ){ int seenZero = 0; /* True if a plan with no prereqs seen */ int seenZeroNoIN = 0; /* Plan with no prereqs and no IN(...) seen */ Bitmask mPrev = 0; @@ -145260,6 +145607,96 @@ static void dense_rankValueFunc(sqlite3_context *pCtx){ } } +/* +** Implementation of built-in window function nth_value(). This +** implementation is used in "slow mode" only - when the EXCLUDE clause +** is not set to the default value "NO OTHERS". +*/ +struct NthValueCtx { + i64 nStep; + sqlite3_value *pValue; +}; +static void nth_valueStepFunc( + sqlite3_context *pCtx, + int nArg, + sqlite3_value **apArg +){ + struct NthValueCtx *p; + p = (struct NthValueCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p)); + if( p ){ + i64 iVal; + switch( sqlite3_value_numeric_type(apArg[1]) ){ + case SQLITE_INTEGER: + iVal = sqlite3_value_int64(apArg[1]); + break; + case SQLITE_FLOAT: { + double fVal = sqlite3_value_double(apArg[1]); + if( ((i64)fVal)!=fVal ) goto error_out; + iVal = (i64)fVal; + break; + } + default: + goto error_out; + } + if( iVal<=0 ) goto error_out; + + p->nStep++; + if( iVal==p->nStep ){ + p->pValue = sqlite3_value_dup(apArg[0]); + if( !p->pValue ){ + sqlite3_result_error_nomem(pCtx); + } + } + } + UNUSED_PARAMETER(nArg); + UNUSED_PARAMETER(apArg); + return; + + error_out: + sqlite3_result_error( + pCtx, "second argument to nth_value must be a positive integer", -1 + ); +} +static void nth_valueFinalizeFunc(sqlite3_context *pCtx){ + struct NthValueCtx *p; + p = (struct NthValueCtx*)sqlite3_aggregate_context(pCtx, 0); + if( p && p->pValue ){ + sqlite3_result_value(pCtx, p->pValue); + sqlite3_value_free(p->pValue); + p->pValue = 0; + } +} +#define nth_valueInvFunc noopStepFunc +#define nth_valueValueFunc noopValueFunc + +static void first_valueStepFunc( + sqlite3_context *pCtx, + int nArg, + sqlite3_value **apArg +){ + struct NthValueCtx *p; + p = (struct NthValueCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p)); + if( p && p->pValue==0 ){ + p->pValue = sqlite3_value_dup(apArg[0]); + if( !p->pValue ){ + sqlite3_result_error_nomem(pCtx); + } + } + UNUSED_PARAMETER(nArg); + UNUSED_PARAMETER(apArg); +} +static void first_valueFinalizeFunc(sqlite3_context *pCtx){ + struct NthValueCtx *p; + p = (struct NthValueCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p)); + if( p && p->pValue ){ + sqlite3_result_value(pCtx, p->pValue); + sqlite3_value_free(p->pValue); + p->pValue = 0; + } +} +#define first_valueInvFunc noopStepFunc +#define first_valueValueFunc noopValueFunc + /* ** Implementation of built-in window function rank(). Assumes that ** the window frame has been set to: @@ -145295,7 +145732,7 @@ static void rankValueFunc(sqlite3_context *pCtx){ ** Implementation of built-in window function percent_rank(). Assumes that ** the window frame has been set to: ** -** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW +** GROUPS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING */ static void percent_rankStepFunc( sqlite3_context *pCtx, @@ -145303,38 +145740,44 @@ static void percent_rankStepFunc( sqlite3_value **apArg ){ struct CallCount *p; - UNUSED_PARAMETER(nArg); assert( nArg==1 ); - + UNUSED_PARAMETER(nArg); assert( nArg==0 ); + UNUSED_PARAMETER(apArg); p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p)); if( p ){ - if( p->nTotal==0 ){ - p->nTotal = sqlite3_value_int64(apArg[0]); - } - p->nStep++; - if( p->nValue==0 ){ - p->nValue = p->nStep; - } + p->nTotal++; } } +static void percent_rankInvFunc( + sqlite3_context *pCtx, + int nArg, + sqlite3_value **apArg +){ + struct CallCount *p; + UNUSED_PARAMETER(nArg); assert( nArg==0 ); + UNUSED_PARAMETER(apArg); + p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p)); + p->nStep++; +} static void percent_rankValueFunc(sqlite3_context *pCtx){ struct CallCount *p; p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p)); if( p ){ + p->nValue = p->nStep; if( p->nTotal>1 ){ - double r = (double)(p->nValue-1) / (double)(p->nTotal-1); + double r = (double)p->nValue / (double)(p->nTotal-1); sqlite3_result_double(pCtx, r); }else{ sqlite3_result_double(pCtx, 0.0); } - p->nValue = 0; } } +#define percent_rankFinalizeFunc percent_rankValueFunc /* ** Implementation of built-in window function cume_dist(). Assumes that ** the window frame has been set to: ** -** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW +** GROUPS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING */ static void cume_distStepFunc( sqlite3_context *pCtx, @@ -145342,24 +145785,33 @@ static void cume_distStepFunc( sqlite3_value **apArg ){ struct CallCount *p; - assert( nArg==1 ); UNUSED_PARAMETER(nArg); - + UNUSED_PARAMETER(nArg); assert( nArg==0 ); + UNUSED_PARAMETER(apArg); p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p)); if( p ){ - if( p->nTotal==0 ){ - p->nTotal = sqlite3_value_int64(apArg[0]); - } - p->nStep++; + p->nTotal++; } } -static void cume_distValueFunc(sqlite3_context *pCtx){ +static void cume_distInvFunc( + sqlite3_context *pCtx, + int nArg, + sqlite3_value **apArg +){ struct CallCount *p; + UNUSED_PARAMETER(nArg); assert( nArg==0 ); + UNUSED_PARAMETER(apArg); p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p)); - if( p && p->nTotal ){ + p->nStep++; +} +static void cume_distValueFunc(sqlite3_context *pCtx){ + struct CallCount *p; + p = (struct CallCount*)sqlite3_aggregate_context(pCtx, 0); + if( p ){ double r = (double)(p->nStep) / (double)(p->nTotal); sqlite3_result_double(pCtx, r); } } +#define cume_distFinalizeFunc cume_distValueFunc /* ** Context object for ntile() window function. @@ -145374,7 +145826,7 @@ struct NtileCtx { ** Implementation of ntile(). This assumes that the window frame has ** been coerced to: ** -** ROWS UNBOUNDED PRECEDING AND CURRENT ROW +** ROWS CURRENT ROW AND UNBOUNDED FOLLOWING */ static void ntileStepFunc( sqlite3_context *pCtx, @@ -145382,32 +145834,42 @@ static void ntileStepFunc( sqlite3_value **apArg ){ struct NtileCtx *p; - assert( nArg==2 ); UNUSED_PARAMETER(nArg); + assert( nArg==1 ); UNUSED_PARAMETER(nArg); p = (struct NtileCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p)); if( p ){ if( p->nTotal==0 ){ p->nParam = sqlite3_value_int64(apArg[0]); - p->nTotal = sqlite3_value_int64(apArg[1]); if( p->nParam<=0 ){ sqlite3_result_error( pCtx, "argument of ntile must be a positive integer", -1 ); } } - p->iRow++; + p->nTotal++; } } +static void ntileInvFunc( + sqlite3_context *pCtx, + int nArg, + sqlite3_value **apArg +){ + struct NtileCtx *p; + assert( nArg==1 ); UNUSED_PARAMETER(nArg); + UNUSED_PARAMETER(apArg); + p = (struct NtileCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p)); + p->iRow++; +} static void ntileValueFunc(sqlite3_context *pCtx){ struct NtileCtx *p; p = (struct NtileCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p)); if( p && p->nParam>0 ){ int nSize = (p->nTotal / p->nParam); if( nSize==0 ){ - sqlite3_result_int64(pCtx, p->iRow); + sqlite3_result_int64(pCtx, p->iRow+1); }else{ i64 nLarge = p->nTotal - p->nParam*nSize; i64 iSmall = nLarge*(nSize+1); - i64 iRow = p->iRow-1; + i64 iRow = p->iRow; assert( (nLarge*(nSize+1) + (p->nParam-nLarge)*nSize)==p->nTotal ); @@ -145419,6 +145881,7 @@ static void ntileValueFunc(sqlite3_context *pCtx){ } } } +#define ntileFinalizeFunc ntileValueFunc /* ** Context object for last_value() window function. @@ -145468,7 +145931,7 @@ static void last_valueInvFunc( } static void last_valueValueFunc(sqlite3_context *pCtx){ struct LastValueCtx *p; - p = (struct LastValueCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p)); + p = (struct LastValueCtx*)sqlite3_aggregate_context(pCtx, 0); if( p && p->pVal ){ sqlite3_result_value(pCtx, p->pVal); } @@ -145558,12 +146021,12 @@ SQLITE_PRIVATE void sqlite3WindowFunctions(void){ WINDOWFUNCX(row_number, 0, 0), WINDOWFUNCX(dense_rank, 0, 0), WINDOWFUNCX(rank, 0, 0), - WINDOWFUNCX(percent_rank, 0, SQLITE_FUNC_WINDOW_SIZE), - WINDOWFUNCX(cume_dist, 0, SQLITE_FUNC_WINDOW_SIZE), - WINDOWFUNCX(ntile, 1, SQLITE_FUNC_WINDOW_SIZE), + WINDOWFUNCALL(percent_rank, 0, 0), + WINDOWFUNCALL(cume_dist, 0, 0), + WINDOWFUNCALL(ntile, 1, 0), WINDOWFUNCALL(last_value, 1, 0), - WINDOWFUNCNOOP(nth_value, 2, 0), - WINDOWFUNCNOOP(first_value, 1, 0), + WINDOWFUNCALL(nth_value, 2, 0), + WINDOWFUNCALL(first_value, 1, 0), WINDOWFUNCNOOP(lead, 1, 0), WINDOWFUNCNOOP(lead, 2, 0), WINDOWFUNCNOOP(lead, 3, 0), @@ -145574,6 +146037,17 @@ SQLITE_PRIVATE void sqlite3WindowFunctions(void){ sqlite3InsertBuiltinFuncs(aWindowFuncs, ArraySize(aWindowFuncs)); } +static Window *windowFind(Parse *pParse, Window *pList, const char *zName){ + Window *p; + for(p=pList; p; p=p->pNextWin){ + if( sqlite3StrICmp(p->zName, zName)==0 ) break; + } + if( p==0 ){ + sqlite3ErrorMsg(pParse, "no such window: %s", zName); + } + return p; +} + /* ** This function is called immediately after resolving the function name ** for a window function within a SELECT statement. Argument pList is a @@ -145597,48 +146071,66 @@ SQLITE_PRIVATE void sqlite3WindowUpdate( Window *pWin, /* Window frame to update */ FuncDef *pFunc /* Window function definition */ ){ - if( pWin->zName && pWin->eType==0 ){ - Window *p; - for(p=pList; p; p=p->pNextWin){ - if( sqlite3StrICmp(p->zName, pWin->zName)==0 ) break; - } - if( p==0 ){ - sqlite3ErrorMsg(pParse, "no such window: %s", pWin->zName); - return; - } + if( pWin->zName && pWin->eFrmType==0 ){ + Window *p = windowFind(pParse, pList, pWin->zName); + if( p==0 ) return; pWin->pPartition = sqlite3ExprListDup(pParse->db, p->pPartition, 0); pWin->pOrderBy = sqlite3ExprListDup(pParse->db, p->pOrderBy, 0); pWin->pStart = sqlite3ExprDup(pParse->db, p->pStart, 0); pWin->pEnd = sqlite3ExprDup(pParse->db, p->pEnd, 0); pWin->eStart = p->eStart; pWin->eEnd = p->eEnd; - pWin->eType = p->eType; + pWin->eFrmType = p->eFrmType; + pWin->eExclude = p->eExclude; + }else{ + sqlite3WindowChain(pParse, pWin, pList); } + if( (pWin->eFrmType==TK_RANGE) + && (pWin->pStart || pWin->pEnd) + && (pWin->pOrderBy==0 || pWin->pOrderBy->nExpr!=1) + ){ + sqlite3ErrorMsg(pParse, + "RANGE with offset PRECEDING/FOLLOWING requires one ORDER BY expression" + ); + }else if( pFunc->funcFlags & SQLITE_FUNC_WINDOW ){ sqlite3 *db = pParse->db; if( pWin->pFilter ){ sqlite3ErrorMsg(pParse, "FILTER clause may only be used with aggregate window functions" ); - }else - if( pFunc->zName==row_numberName || pFunc->zName==ntileName ){ - sqlite3ExprDelete(db, pWin->pStart); - sqlite3ExprDelete(db, pWin->pEnd); - pWin->pStart = pWin->pEnd = 0; - pWin->eType = TK_ROWS; - pWin->eStart = TK_UNBOUNDED; - pWin->eEnd = TK_CURRENT; - }else - - if( pFunc->zName==dense_rankName || pFunc->zName==rankName - || pFunc->zName==percent_rankName || pFunc->zName==cume_distName - ){ - sqlite3ExprDelete(db, pWin->pStart); - sqlite3ExprDelete(db, pWin->pEnd); - pWin->pStart = pWin->pEnd = 0; - pWin->eType = TK_RANGE; - pWin->eStart = TK_UNBOUNDED; - pWin->eEnd = TK_CURRENT; + }else{ + struct WindowUpdate { + const char *zFunc; + int eFrmType; + int eStart; + int eEnd; + } aUp[] = { + { row_numberName, TK_ROWS, TK_UNBOUNDED, TK_CURRENT }, + { dense_rankName, TK_RANGE, TK_UNBOUNDED, TK_CURRENT }, + { rankName, TK_RANGE, TK_UNBOUNDED, TK_CURRENT }, + { percent_rankName, TK_GROUPS, TK_CURRENT, TK_UNBOUNDED }, + { cume_distName, TK_GROUPS, TK_FOLLOWING, TK_UNBOUNDED }, + { ntileName, TK_ROWS, TK_CURRENT, TK_UNBOUNDED }, + { leadName, TK_ROWS, TK_UNBOUNDED, TK_UNBOUNDED }, + { lagName, TK_ROWS, TK_UNBOUNDED, TK_CURRENT }, + }; + int i; + for(i=0; izName==aUp[i].zFunc ){ + sqlite3ExprDelete(db, pWin->pStart); + sqlite3ExprDelete(db, pWin->pEnd); + pWin->pEnd = pWin->pStart = 0; + pWin->eFrmType = aUp[i].eFrmType; + pWin->eStart = aUp[i].eStart; + pWin->eEnd = aUp[i].eEnd; + pWin->eExclude = 0; + if( pWin->eStart==TK_FOLLOWING ){ + pWin->pStart = sqlite3Expr(db, TK_INTEGER, "1"); + } + break; + } + } } } pWin->pFunc = pFunc; @@ -145843,6 +146335,7 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){ ** The OpenEphemeral instruction is coded later, after it is known how ** many columns the table will have. */ pMWin->iEphCsr = pParse->nTab++; + pParse->nTab += 3; selectWindowRewriteEList(pParse, pMWin, pSrc, p->pEList, &pSublist); selectWindowRewriteEList(pParse, pMWin, pSrc, p->pOrderBy, &pSublist); @@ -145898,6 +146391,9 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){ } sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pMWin->iEphCsr, pSublist->nExpr); + sqlite3VdbeAddOp2(v, OP_OpenDup, pMWin->iEphCsr+1, pMWin->iEphCsr); + sqlite3VdbeAddOp2(v, OP_OpenDup, pMWin->iEphCsr+2, pMWin->iEphCsr); + sqlite3VdbeAddOp2(v, OP_OpenDup, pMWin->iEphCsr+3, pMWin->iEphCsr); }else{ sqlite3SelectDelete(db, pSub); } @@ -145918,6 +146414,7 @@ SQLITE_PRIVATE void sqlite3WindowDelete(sqlite3 *db, Window *p){ sqlite3ExprDelete(db, p->pEnd); sqlite3ExprDelete(db, p->pStart); sqlite3DbFree(db, p->zName); + sqlite3DbFree(db, p->zBase); sqlite3DbFree(db, p); } } @@ -145954,16 +146451,18 @@ static Expr *sqlite3WindowOffsetExpr(Parse *pParse, Expr *pExpr){ */ SQLITE_PRIVATE Window *sqlite3WindowAlloc( Parse *pParse, /* Parsing context */ - int eType, /* Frame type. TK_RANGE or TK_ROWS */ + int eType, /* Frame type. TK_RANGE, TK_ROWS, TK_GROUPS, or 0 */ int eStart, /* Start type: CURRENT, PRECEDING, FOLLOWING, UNBOUNDED */ Expr *pStart, /* Start window size if TK_PRECEDING or FOLLOWING */ int eEnd, /* End type: CURRENT, FOLLOWING, TK_UNBOUNDED, PRECEDING */ - Expr *pEnd /* End window size if TK_FOLLOWING or PRECEDING */ + Expr *pEnd, /* End window size if TK_FOLLOWING or PRECEDING */ + u8 eExclude /* EXCLUDE clause */ ){ Window *pWin = 0; + int bImplicitFrame = 0; /* Parser assures the following: */ - assert( eType==TK_RANGE || eType==TK_ROWS ); + assert( eType==0 || eType==TK_RANGE || eType==TK_ROWS || eType==TK_GROUPS ); assert( eStart==TK_CURRENT || eStart==TK_PRECEDING || eStart==TK_UNBOUNDED || eStart==TK_FOLLOWING ); assert( eEnd==TK_CURRENT || eEnd==TK_FOLLOWING @@ -145971,13 +146470,9 @@ SQLITE_PRIVATE Window *sqlite3WindowAlloc( assert( (eStart==TK_PRECEDING || eStart==TK_FOLLOWING)==(pStart!=0) ); assert( (eEnd==TK_FOLLOWING || eEnd==TK_PRECEDING)==(pEnd!=0) ); - - /* If a frame is declared "RANGE" (not "ROWS"), then it may not use - ** either " PRECEDING" or " FOLLOWING". - */ - if( eType==TK_RANGE && (pStart!=0 || pEnd!=0) ){ - sqlite3ErrorMsg(pParse, "RANGE must use only UNBOUNDED or CURRENT ROW"); - goto windowAllocErr; + if( eType==0 ){ + bImplicitFrame = 1; + eType = TK_RANGE; } /* Additionally, the @@ -145997,15 +146492,20 @@ SQLITE_PRIVATE Window *sqlite3WindowAlloc( if( (eStart==TK_CURRENT && eEnd==TK_PRECEDING) || (eStart==TK_FOLLOWING && (eEnd==TK_PRECEDING || eEnd==TK_CURRENT)) ){ - sqlite3ErrorMsg(pParse, "unsupported frame delimiter for ROWS"); + sqlite3ErrorMsg(pParse, "unsupported frame specification"); goto windowAllocErr; } pWin = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); if( pWin==0 ) goto windowAllocErr; - pWin->eType = eType; + pWin->eFrmType = eType; pWin->eStart = eStart; pWin->eEnd = eEnd; + if( eExclude==0 && OptimizationDisabled(pParse->db, SQLITE_WindowFunc) ){ + eExclude = TK_NO; + } + pWin->eExclude = eExclude; + pWin->bImplicitFrame = bImplicitFrame; pWin->pEnd = sqlite3WindowOffsetExpr(pParse, pEnd); pWin->pStart = sqlite3WindowOffsetExpr(pParse, pStart); return pWin; @@ -146016,6 +146516,69 @@ windowAllocErr: return 0; } +/* +** Attach PARTITION and ORDER BY clauses pPartition and pOrderBy to window +** pWin. Also, if parameter pBase is not NULL, set pWin->zBase to the +** equivalent nul-terminated string. +*/ +SQLITE_PRIVATE Window *sqlite3WindowAssemble( + Parse *pParse, + Window *pWin, + ExprList *pPartition, + ExprList *pOrderBy, + Token *pBase +){ + if( pWin ){ + pWin->pPartition = pPartition; + pWin->pOrderBy = pOrderBy; + if( pBase ){ + pWin->zBase = sqlite3DbStrNDup(pParse->db, pBase->z, pBase->n); + } + }else{ + sqlite3ExprListDelete(pParse->db, pPartition); + sqlite3ExprListDelete(pParse->db, pOrderBy); + } + return pWin; +} + +/* +** Window *pWin has just been created from a WINDOW clause. Tokne pBase +** is the base window. Earlier windows from the same WINDOW clause are +** stored in the linked list starting at pWin->pNextWin. This function +** either updates *pWin according to the base specification, or else +** leaves an error in pParse. +*/ +SQLITE_PRIVATE void sqlite3WindowChain(Parse *pParse, Window *pWin, Window *pList){ + if( pWin->zBase ){ + sqlite3 *db = pParse->db; + Window *pExist = windowFind(pParse, pList, pWin->zBase); + if( pExist ){ + const char *zErr = 0; + /* Check for errors */ + if( pWin->pPartition ){ + zErr = "PARTITION clause"; + }else if( pExist->pOrderBy && pWin->pOrderBy ){ + zErr = "ORDER BY clause"; + }else if( pExist->bImplicitFrame==0 ){ + zErr = "frame specification"; + } + if( zErr ){ + sqlite3ErrorMsg(pParse, + "cannot override %s of window: %s", zErr, pWin->zBase + ); + }else{ + pWin->pPartition = sqlite3ExprListDup(db, pExist->pPartition, 0); + if( pExist->pOrderBy ){ + assert( pWin->pOrderBy==0 ); + pWin->pOrderBy = sqlite3ExprListDup(db, pExist->pOrderBy, 0); + } + sqlite3DbFree(db, pWin->zBase); + pWin->zBase = 0; + } + } + } +} + /* ** Attach window object pWin to expression p. */ @@ -146044,9 +146607,10 @@ SQLITE_PRIVATE void sqlite3WindowAttach(Parse *pParse, Expr *p, Window *pWin){ ** Identical window objects can be processed in a single scan. */ SQLITE_PRIVATE int sqlite3WindowCompare(Parse *pParse, Window *p1, Window *p2){ - if( p1->eType!=p2->eType ) return 1; + if( p1->eFrmType!=p2->eFrmType ) return 1; if( p1->eStart!=p2->eStart ) return 1; if( p1->eEnd!=p2->eEnd ) return 1; + if( p1->eExclude!=p2->eExclude ) return 1; if( sqlite3ExprCompare(pParse, p1->pStart, p2->pStart, -1) ) return 1; if( sqlite3ExprCompare(pParse, p1->pEnd, p2->pEnd, -1) ) return 1; if( sqlite3ExprListCompare(p1->pPartition, p2->pPartition, -1) ) return 1; @@ -146063,12 +146627,27 @@ SQLITE_PRIVATE int sqlite3WindowCompare(Parse *pParse, Window *p1, Window *p2){ SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse *pParse, Window *pMWin){ Window *pWin; Vdbe *v = sqlite3GetVdbe(pParse); - int nPart = (pMWin->pPartition ? pMWin->pPartition->nExpr : 0); - nPart += (pMWin->pOrderBy ? pMWin->pOrderBy->nExpr : 0); - if( nPart ){ + + /* Allocate registers to use for PARTITION BY values, if any. Initialize + ** said registers to NULL. */ + if( pMWin->pPartition ){ + int nExpr = pMWin->pPartition->nExpr; pMWin->regPart = pParse->nMem+1; - pParse->nMem += nPart; - sqlite3VdbeAddOp3(v, OP_Null, 0, pMWin->regPart, pMWin->regPart+nPart-1); + pParse->nMem += nExpr; + sqlite3VdbeAddOp3(v, OP_Null, 0, pMWin->regPart, pMWin->regPart+nExpr-1); + } + + pMWin->regOne = ++pParse->nMem; + sqlite3VdbeAddOp2(v, OP_Integer, 1, pMWin->regOne); + + if( pMWin->eExclude ){ + pMWin->regStartRowid = ++pParse->nMem; + pMWin->regEndRowid = ++pParse->nMem; + pMWin->csrApp = pParse->nTab++; + sqlite3VdbeAddOp2(v, OP_Integer, 1, pMWin->regStartRowid); + sqlite3VdbeAddOp2(v, OP_Integer, 0, pMWin->regEndRowid); + sqlite3VdbeAddOp2(v, OP_OpenDup, pMWin->csrApp, pMWin->iEphCsr); + return; } for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ @@ -146097,20 +146676,24 @@ SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse *pParse, Window *pMWin){ else if( p->zName==nth_valueName || p->zName==first_valueName ){ /* Allocate two registers at pWin->regApp. These will be used to ** store the start and end index of the current frame. */ - assert( pMWin->iEphCsr ); pWin->regApp = pParse->nMem+1; pWin->csrApp = pParse->nTab++; pParse->nMem += 2; sqlite3VdbeAddOp2(v, OP_OpenDup, pWin->csrApp, pMWin->iEphCsr); } else if( p->zName==leadName || p->zName==lagName ){ - assert( pMWin->iEphCsr ); pWin->csrApp = pParse->nTab++; sqlite3VdbeAddOp2(v, OP_OpenDup, pWin->csrApp, pMWin->iEphCsr); } } } +#define WINDOW_STARTING_INT 0 +#define WINDOW_ENDING_INT 1 +#define WINDOW_NTH_VALUE_INT 2 +#define WINDOW_STARTING_NUM 3 +#define WINDOW_ENDING_NUM 4 + /* ** A "PRECEDING " (eCond==0) or "FOLLOWING " (eCond==1) or the ** value of the second argument to nth_value() (eCond==2) has just been @@ -146118,25 +146701,42 @@ SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse *pParse, Window *pMWin){ ** code to check that the value is a non-negative integer and throws an ** exception if it is not. */ -static void windowCheckIntValue(Parse *pParse, int reg, int eCond){ +static void windowCheckValue(Parse *pParse, int reg, int eCond){ static const char *azErr[] = { "frame starting offset must be a non-negative integer", "frame ending offset must be a non-negative integer", - "second argument to nth_value must be a positive integer" + "second argument to nth_value must be a positive integer", + "frame starting offset must be a non-negative number", + "frame ending offset must be a non-negative number", }; - static int aOp[] = { OP_Ge, OP_Ge, OP_Gt }; + static int aOp[] = { OP_Ge, OP_Ge, OP_Gt, OP_Ge, OP_Ge }; Vdbe *v = sqlite3GetVdbe(pParse); int regZero = sqlite3GetTempReg(pParse); - assert( eCond==0 || eCond==1 || eCond==2 ); + assert( eCond>=0 && eCond=WINDOW_STARTING_NUM ){ + int regString = sqlite3GetTempReg(pParse); + sqlite3VdbeAddOp4(v, OP_String8, 0, regString, 0, "", P4_STATIC); + sqlite3VdbeAddOp3(v, OP_Ge, regString, sqlite3VdbeCurrentAddr(v)+2, reg); + sqlite3VdbeChangeP5(v, SQLITE_AFF_NUMERIC|SQLITE_JUMPIFNULL); + VdbeCoverage(v); + assert( eCond==3 || eCond==4 ); + VdbeCoverageIf(v, eCond==3); + VdbeCoverageIf(v, eCond==4); + }else{ + sqlite3VdbeAddOp2(v, OP_MustBeInt, reg, sqlite3VdbeCurrentAddr(v)+2); + VdbeCoverage(v); + assert( eCond==0 || eCond==1 || eCond==2 ); + VdbeCoverageIf(v, eCond==0); + VdbeCoverageIf(v, eCond==1); + VdbeCoverageIf(v, eCond==2); + } sqlite3VdbeAddOp3(v, aOp[eCond], regZero, sqlite3VdbeCurrentAddr(v)+2, reg); - VdbeCoverageNeverNullIf(v, eCond==0); - VdbeCoverageNeverNullIf(v, eCond==1); + VdbeCoverageNeverNullIf(v, eCond==0); /* NULL case captured by */ + VdbeCoverageNeverNullIf(v, eCond==1); /* the OP_MustBeInt */ VdbeCoverageNeverNullIf(v, eCond==2); + VdbeCoverageNeverNullIf(v, eCond==3); /* NULL case caught by */ + VdbeCoverageNeverNullIf(v, eCond==4); /* the OP_Ge */ sqlite3MayAbort(pParse); sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_ERROR, OE_Abort); sqlite3VdbeAppendP4(v, (void*)azErr[eCond], P4_STATIC); @@ -146176,37 +146776,28 @@ static void windowAggStep( Window *pMWin, /* Linked list of window functions */ int csr, /* Read arguments from this cursor */ int bInverse, /* True to invoke xInverse instead of xStep */ - int reg, /* Array of registers */ - int regPartSize /* Register containing size of partition */ + int reg /* Array of registers */ ){ Vdbe *v = sqlite3GetVdbe(pParse); Window *pWin; for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ - int flags = pWin->pFunc->funcFlags; + FuncDef *pFunc = pWin->pFunc; int regArg; int nArg = windowArgCount(pWin); + int i; - if( csr>=0 ){ - int i; - for(i=0; izName!=nth_valueName ){ sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+i, reg+i); + }else{ + sqlite3VdbeAddOp3(v, OP_Column, pMWin->iEphCsr, pWin->iArgCol+i, reg+i); } - regArg = reg; - if( flags & SQLITE_FUNC_WINDOW_SIZE ){ - if( nArg==0 ){ - regArg = regPartSize; - }else{ - sqlite3VdbeAddOp2(v, OP_SCopy, regPartSize, reg+nArg); - } - nArg++; - } - }else{ - assert( !(flags & SQLITE_FUNC_WINDOW_SIZE) ); - regArg = reg + pWin->iArgCol; } + regArg = reg; - if( (pWin->pFunc->funcFlags & SQLITE_FUNC_MINMAX) - && pWin->eStart!=TK_UNBOUNDED + if( pMWin->regStartRowid==0 + && (pFunc->funcFlags & SQLITE_FUNC_MINMAX) + && (pWin->eStart!=TK_UNBOUNDED) ){ int addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, regArg); VdbeCoverage(v); @@ -146223,34 +146814,24 @@ static void windowAggStep( } sqlite3VdbeJumpHere(v, addrIsNull); }else if( pWin->regApp ){ - assert( pWin->pFunc->zName==nth_valueName - || pWin->pFunc->zName==first_valueName + assert( pFunc->zName==nth_valueName + || pFunc->zName==first_valueName ); assert( bInverse==0 || bInverse==1 ); sqlite3VdbeAddOp2(v, OP_AddImm, pWin->regApp+1-bInverse, 1); - }else if( pWin->pFunc->zName==leadName - || pWin->pFunc->zName==lagName - ){ - /* no-op */ - }else{ + }else if( pFunc->xSFunc!=noopStepFunc ){ int addrIf = 0; if( pWin->pFilter ){ int regTmp; assert( nArg==0 || nArg==pWin->pOwner->x.pList->nExpr ); assert( nArg || pWin->pOwner->x.pList==0 ); - if( csr>0 ){ - regTmp = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+nArg,regTmp); - }else{ - regTmp = regArg + nArg; - } + regTmp = sqlite3GetTempReg(pParse); + sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+nArg,regTmp); addrIf = sqlite3VdbeAddOp3(v, OP_IfNot, regTmp, 0, 1); VdbeCoverage(v); - if( csr>0 ){ - sqlite3ReleaseTempReg(pParse, regTmp); - } + sqlite3ReleaseTempReg(pParse, regTmp); } - if( pWin->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){ + if( pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){ CollSeq *pColl; assert( nArg>0 ); pColl = sqlite3ExprNNCollSeq(pParse, pWin->pOwner->x.pList->a[0].pExpr); @@ -146258,45 +146839,96 @@ static void windowAggStep( } sqlite3VdbeAddOp3(v, bInverse? OP_AggInverse : OP_AggStep, bInverse, regArg, pWin->regAccum); - sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF); + sqlite3VdbeAppendP4(v, pFunc, P4_FUNCDEF); sqlite3VdbeChangeP5(v, (u8)nArg); if( addrIf ) sqlite3VdbeJumpHere(v, addrIf); } } } +typedef struct WindowCodeArg WindowCodeArg; +typedef struct WindowCsrAndReg WindowCsrAndReg; +struct WindowCsrAndReg { + int csr; + int reg; +}; + +struct WindowCodeArg { + Parse *pParse; + Window *pMWin; + Vdbe *pVdbe; + int regGosub; + int addrGosub; + int regArg; + int eDelete; + + WindowCsrAndReg start; + WindowCsrAndReg current; + WindowCsrAndReg end; +}; + +/* +** Values that may be passed as the second argument to windowCodeOp(). +*/ +#define WINDOW_RETURN_ROW 1 +#define WINDOW_AGGINVERSE 2 +#define WINDOW_AGGSTEP 3 + +/* +** Generate VM code to read the window frames peer values from cursor csr into +** an array of registers starting at reg. +*/ +static void windowReadPeerValues( + WindowCodeArg *p, + int csr, + int reg +){ + Window *pMWin = p->pMWin; + ExprList *pOrderBy = pMWin->pOrderBy; + if( pOrderBy ){ + Vdbe *v = sqlite3GetVdbe(p->pParse); + ExprList *pPart = pMWin->pPartition; + int iColOff = pMWin->nBufferCol + (pPart ? pPart->nExpr : 0); + int i; + for(i=0; inExpr; i++){ + sqlite3VdbeAddOp3(v, OP_Column, csr, iColOff+i, reg+i); + } + } +} + /* -** Generate VM code to invoke either xValue() (bFinal==0) or xFinalize() -** (bFinal==1) for each window function in the linked list starting at +** Generate VM code to invoke either xValue() (bFin==0) or xFinalize() +** (bFin==1) for each window function in the linked list starting at ** pMWin. Or, for built-in window-functions that do not use the standard ** API, generate the equivalent VM code. */ -static void windowAggFinal(Parse *pParse, Window *pMWin, int bFinal){ +static void windowAggFinal(WindowCodeArg *p, int bFin){ + Parse *pParse = p->pParse; + Window *pMWin = p->pMWin; Vdbe *v = sqlite3GetVdbe(pParse); Window *pWin; for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ - if( (pWin->pFunc->funcFlags & SQLITE_FUNC_MINMAX) - && pWin->eStart!=TK_UNBOUNDED + if( pMWin->regStartRowid==0 + && (pWin->pFunc->funcFlags & SQLITE_FUNC_MINMAX) + && (pWin->eStart!=TK_UNBOUNDED) ){ sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult); sqlite3VdbeAddOp1(v, OP_Last, pWin->csrApp); VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_Column, pWin->csrApp, 0, pWin->regResult); sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2); - if( bFinal ){ - sqlite3VdbeAddOp1(v, OP_ResetSorter, pWin->csrApp); - } }else if( pWin->regApp ){ + assert( pMWin->regStartRowid==0 ); }else{ - if( bFinal ){ - sqlite3VdbeAddOp2(v, OP_AggFinal, pWin->regAccum, windowArgCount(pWin)); + int nArg = windowArgCount(pWin); + if( bFin ){ + sqlite3VdbeAddOp2(v, OP_AggFinal, pWin->regAccum, nArg); sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF); sqlite3VdbeAddOp2(v, OP_Copy, pWin->regAccum, pWin->regResult); sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum); }else{ - sqlite3VdbeAddOp3(v, OP_AggValue, pWin->regAccum, windowArgCount(pWin), - pWin->regResult); + sqlite3VdbeAddOp3(v, OP_AggValue,pWin->regAccum,nArg,pWin->regResult); sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF); } } @@ -146304,66 +146936,97 @@ static void windowAggFinal(Parse *pParse, Window *pMWin, int bFinal){ } /* -** This function generates VM code to invoke the sub-routine at address -** lblFlushPart once for each partition with the entire partition cached in -** the Window.iEphCsr temp table. +** Generate code to calculate the current values of all window functions in the +** p->pMWin list by doing a full scan of the current window frame. Store the +** results in the Window.regResult registers, ready to return the upper +** layer. */ -static void windowPartitionCache( - Parse *pParse, - Select *p, /* The rewritten SELECT statement */ - WhereInfo *pWInfo, /* WhereInfo to call WhereEnd() on */ - int regFlushPart, /* Register to use with Gosub lblFlushPart */ - int lblFlushPart, /* Subroutine to Gosub to */ - int *pRegSize /* OUT: Register containing partition size */ -){ - Window *pMWin = p->pWin; - Vdbe *v = sqlite3GetVdbe(pParse); - int iSubCsr = p->pSrc->a[0].iCursor; - int nSub = p->pSrc->a[0].pTab->nCol; - int k; +static void windowFullScan(WindowCodeArg *p){ + Window *pWin; + Parse *pParse = p->pParse; + Window *pMWin = p->pMWin; + Vdbe *v = p->pVdbe; - int reg = pParse->nMem+1; - int regRecord = reg+nSub; - int regRowid = regRecord+1; + int regCRowid = 0; /* Current rowid value */ + int regCPeer = 0; /* Current peer values */ + int regRowid = 0; /* AggStep rowid value */ + int regPeer = 0; /* AggStep peer values */ - *pRegSize = regRowid; - pParse->nMem += nSub + 2; + int nPeer; + int lblNext; + int lblBrk; + int addrNext; + int csr = pMWin->csrApp; - /* Load the column values for the row returned by the sub-select - ** into an array of registers starting at reg. */ - for(k=0; kpOrderBy ? pMWin->pOrderBy->nExpr : 0); + + lblNext = sqlite3VdbeMakeLabel(pParse); + lblBrk = sqlite3VdbeMakeLabel(pParse); + + regCRowid = sqlite3GetTempReg(pParse); + regRowid = sqlite3GetTempReg(pParse); + if( nPeer ){ + regCPeer = sqlite3GetTempRange(pParse, nPeer); + regPeer = sqlite3GetTempRange(pParse, nPeer); } - sqlite3VdbeAddOp3(v, OP_MakeRecord, reg, nSub, regRecord); - /* Check if this is the start of a new partition. If so, call the - ** flush_partition sub-routine. */ - if( pMWin->pPartition ){ + sqlite3VdbeAddOp2(v, OP_Rowid, pMWin->iEphCsr, regCRowid); + windowReadPeerValues(p, pMWin->iEphCsr, regCPeer); + + for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ + sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum); + } + + sqlite3VdbeAddOp3(v, OP_SeekGE, csr, lblBrk, pMWin->regStartRowid); + VdbeCoverage(v); + addrNext = sqlite3VdbeCurrentAddr(v); + sqlite3VdbeAddOp2(v, OP_Rowid, csr, regRowid); + sqlite3VdbeAddOp3(v, OP_Gt, pMWin->regEndRowid, lblBrk, regRowid); + VdbeCoverageNeverNull(v); + + if( pMWin->eExclude==TK_CURRENT ){ + sqlite3VdbeAddOp3(v, OP_Eq, regCRowid, lblNext, regRowid); + VdbeCoverageNeverNull(v); + }else if( pMWin->eExclude!=TK_NO ){ int addr; - ExprList *pPart = pMWin->pPartition; - int nPart = pPart->nExpr; - int regNewPart = reg + pMWin->nBufferCol; - KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pPart, 0, 0); + int addrEq = 0; + KeyInfo *pKeyInfo = 0; - addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPart, pMWin->regPart,nPart); - sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO); - sqlite3VdbeAddOp3(v, OP_Jump, addr+2, addr+4, addr+2); - VdbeCoverageEqNe(v); - sqlite3VdbeAddOp3(v, OP_Copy, regNewPart, pMWin->regPart, nPart-1); - sqlite3VdbeAddOp2(v, OP_Gosub, regFlushPart, lblFlushPart); - VdbeComment((v, "call flush_partition")); + if( pMWin->pOrderBy ){ + pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pMWin->pOrderBy, 0, 0); + } + if( pMWin->eExclude==TK_TIES ){ + addrEq = sqlite3VdbeAddOp3(v, OP_Eq, regCRowid, 0, regRowid); + VdbeCoverageNeverNull(v); + } + if( pKeyInfo ){ + windowReadPeerValues(p, csr, regPeer); + sqlite3VdbeAddOp3(v, OP_Compare, regPeer, regCPeer, nPeer); + sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO); + addr = sqlite3VdbeCurrentAddr(v)+1; + sqlite3VdbeAddOp3(v, OP_Jump, addr, lblNext, addr); + VdbeCoverageEqNe(v); + }else{ + sqlite3VdbeAddOp2(v, OP_Goto, 0, lblNext); + } + if( addrEq ) sqlite3VdbeJumpHere(v, addrEq); } - /* Buffer the current row in the ephemeral table. */ - sqlite3VdbeAddOp2(v, OP_NewRowid, pMWin->iEphCsr, regRowid); - sqlite3VdbeAddOp3(v, OP_Insert, pMWin->iEphCsr, regRecord, regRowid); + windowAggStep(pParse, pMWin, csr, 0, p->regArg); - /* End of the input loop */ - sqlite3WhereEnd(pWInfo); + sqlite3VdbeResolveLabel(v, lblNext); + sqlite3VdbeAddOp2(v, OP_Next, csr, addrNext); + VdbeCoverage(v); + sqlite3VdbeJumpHere(v, addrNext-1); + sqlite3VdbeJumpHere(v, addrNext+1); + sqlite3ReleaseTempReg(pParse, regRowid); + sqlite3ReleaseTempReg(pParse, regCRowid); + if( nPeer ){ + sqlite3ReleaseTempRange(pParse, regPeer, nPeer); + sqlite3ReleaseTempRange(pParse, regCPeer, nPeer); + } - /* Invoke "flush_partition" to deal with the final (or only) partition */ - sqlite3VdbeAddOp2(v, OP_Gosub, regFlushPart, lblFlushPart); - VdbeComment((v, "call flush_partition")); + windowAggFinal(p, 1); } /* @@ -146379,110 +147042,74 @@ static void windowPartitionCache( ** lag() ** lead() */ -static void windowReturnOneRow( - Parse *pParse, - Window *pMWin, - int regGosub, - int addrGosub -){ - Vdbe *v = sqlite3GetVdbe(pParse); - Window *pWin; - for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ - FuncDef *pFunc = pWin->pFunc; - if( pFunc->zName==nth_valueName - || pFunc->zName==first_valueName - ){ - int csr = pWin->csrApp; - int lbl = sqlite3VdbeMakeLabel(pParse); - int tmpReg = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult); +static void windowReturnOneRow(WindowCodeArg *p){ + Window *pMWin = p->pMWin; + Vdbe *v = p->pVdbe; - if( pFunc->zName==nth_valueName ){ - sqlite3VdbeAddOp3(v, OP_Column, pMWin->iEphCsr, pWin->iArgCol+1,tmpReg); - windowCheckIntValue(pParse, tmpReg, 2); - }else{ - sqlite3VdbeAddOp2(v, OP_Integer, 1, tmpReg); - } - sqlite3VdbeAddOp3(v, OP_Add, tmpReg, pWin->regApp, tmpReg); - sqlite3VdbeAddOp3(v, OP_Gt, pWin->regApp+1, lbl, tmpReg); - VdbeCoverageNeverNull(v); - sqlite3VdbeAddOp3(v, OP_SeekRowid, csr, 0, tmpReg); - VdbeCoverageNeverTaken(v); - sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol, pWin->regResult); - sqlite3VdbeResolveLabel(v, lbl); - sqlite3ReleaseTempReg(pParse, tmpReg); - } - else if( pFunc->zName==leadName || pFunc->zName==lagName ){ - int nArg = pWin->pOwner->x.pList->nExpr; - int iEph = pMWin->iEphCsr; - int csr = pWin->csrApp; - int lbl = sqlite3VdbeMakeLabel(pParse); - int tmpReg = sqlite3GetTempReg(pParse); - - if( nArg<3 ){ + if( pMWin->regStartRowid ){ + windowFullScan(p); + }else{ + Parse *pParse = p->pParse; + Window *pWin; + + for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ + FuncDef *pFunc = pWin->pFunc; + if( pFunc->zName==nth_valueName + || pFunc->zName==first_valueName + ){ + int csr = pWin->csrApp; + int lbl = sqlite3VdbeMakeLabel(pParse); + int tmpReg = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult); - }else{ - sqlite3VdbeAddOp3(v, OP_Column, iEph, pWin->iArgCol+2, pWin->regResult); - } - sqlite3VdbeAddOp2(v, OP_Rowid, iEph, tmpReg); - if( nArg<2 ){ - int val = (pFunc->zName==leadName ? 1 : -1); - sqlite3VdbeAddOp2(v, OP_AddImm, tmpReg, val); - }else{ - int op = (pFunc->zName==leadName ? OP_Add : OP_Subtract); - int tmpReg2 = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp3(v, OP_Column, iEph, pWin->iArgCol+1, tmpReg2); - sqlite3VdbeAddOp3(v, op, tmpReg2, tmpReg, tmpReg); - sqlite3ReleaseTempReg(pParse, tmpReg2); + + if( pFunc->zName==nth_valueName ){ + sqlite3VdbeAddOp3(v, OP_Column,pMWin->iEphCsr,pWin->iArgCol+1,tmpReg); + windowCheckValue(pParse, tmpReg, 2); + }else{ + sqlite3VdbeAddOp2(v, OP_Integer, 1, tmpReg); + } + sqlite3VdbeAddOp3(v, OP_Add, tmpReg, pWin->regApp, tmpReg); + sqlite3VdbeAddOp3(v, OP_Gt, pWin->regApp+1, lbl, tmpReg); + VdbeCoverageNeverNull(v); + sqlite3VdbeAddOp3(v, OP_SeekRowid, csr, 0, tmpReg); + VdbeCoverageNeverTaken(v); + sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol, pWin->regResult); + sqlite3VdbeResolveLabel(v, lbl); + sqlite3ReleaseTempReg(pParse, tmpReg); + } + else if( pFunc->zName==leadName || pFunc->zName==lagName ){ + int nArg = pWin->pOwner->x.pList->nExpr; + int csr = pWin->csrApp; + int lbl = sqlite3VdbeMakeLabel(pParse); + int tmpReg = sqlite3GetTempReg(pParse); + int iEph = pMWin->iEphCsr; + + if( nArg<3 ){ + sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult); + }else{ + sqlite3VdbeAddOp3(v, OP_Column, iEph,pWin->iArgCol+2,pWin->regResult); + } + sqlite3VdbeAddOp2(v, OP_Rowid, iEph, tmpReg); + if( nArg<2 ){ + int val = (pFunc->zName==leadName ? 1 : -1); + sqlite3VdbeAddOp2(v, OP_AddImm, tmpReg, val); + }else{ + int op = (pFunc->zName==leadName ? OP_Add : OP_Subtract); + int tmpReg2 = sqlite3GetTempReg(pParse); + sqlite3VdbeAddOp3(v, OP_Column, iEph, pWin->iArgCol+1, tmpReg2); + sqlite3VdbeAddOp3(v, op, tmpReg2, tmpReg, tmpReg); + sqlite3ReleaseTempReg(pParse, tmpReg2); + } + + sqlite3VdbeAddOp3(v, OP_SeekRowid, csr, lbl, tmpReg); + VdbeCoverage(v); + sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol, pWin->regResult); + sqlite3VdbeResolveLabel(v, lbl); + sqlite3ReleaseTempReg(pParse, tmpReg); } - - sqlite3VdbeAddOp3(v, OP_SeekRowid, csr, lbl, tmpReg); - VdbeCoverage(v); - sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol, pWin->regResult); - sqlite3VdbeResolveLabel(v, lbl); - sqlite3ReleaseTempReg(pParse, tmpReg); } } - sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub); -} - -/* -** Invoke the code generated by windowReturnOneRow() and, optionally, the -** xInverse() function for each window function, for one or more rows -** from the Window.iEphCsr temp table. This routine generates VM code -** similar to: -** -** while( regCtr>0 ){ -** regCtr--; -** windowReturnOneRow() -** if( bInverse ){ -** AggInverse -** } -** Next (Window.iEphCsr) -** } -*/ -static void windowReturnRows( - Parse *pParse, - Window *pMWin, /* List of window functions */ - int regCtr, /* Register containing number of rows */ - int regGosub, /* Register for Gosub addrGosub */ - int addrGosub, /* Address of sub-routine for ReturnOneRow */ - int regInvArg, /* Array of registers for xInverse args */ - int regInvSize /* Register containing size of partition */ -){ - int addr; - Vdbe *v = sqlite3GetVdbe(pParse); - windowAggFinal(pParse, pMWin, 0); - addr = sqlite3VdbeAddOp3(v, OP_IfPos, regCtr, sqlite3VdbeCurrentAddr(v)+2 ,1); - VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_Goto, 0, 0); - windowReturnOneRow(pParse, pMWin, regGosub, addrGosub); - if( regInvArg ){ - windowAggStep(pParse, pMWin, pMWin->iEphCsr, 1, regInvArg, regInvSize); - } - sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, addr); - VdbeCoverage(v); - sqlite3VdbeJumpHere(v, addr+1); /* The OP_Goto */ + sqlite3VdbeAddOp2(v, OP_Gosub, p->regGosub, p->addrGosub); } /* @@ -146500,17 +147127,17 @@ static int windowInitAccum(Parse *pParse, Window *pMWin){ FuncDef *pFunc = pWin->pFunc; sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum); nArg = MAX(nArg, windowArgCount(pWin)); - if( pFunc->zName==nth_valueName - || pFunc->zName==first_valueName - ){ - sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp); - sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp+1); - } + if( pMWin->regStartRowid==0 ){ + if( pFunc->zName==nth_valueName || pFunc->zName==first_valueName ){ + sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp); + sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp+1); + } - if( (pFunc->funcFlags & SQLITE_FUNC_MINMAX) && pWin->csrApp ){ - assert( pWin->eStart!=TK_UNBOUNDED ); - sqlite3VdbeAddOp1(v, OP_ResetSorter, pWin->csrApp); - sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp+1); + if( (pFunc->funcFlags & SQLITE_FUNC_MINMAX) && pWin->csrApp ){ + assert( pWin->eStart!=TK_UNBOUNDED ); + sqlite3VdbeAddOp1(v, OP_ResetSorter, pWin->csrApp); + sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp+1); + } } } regArg = pParse->nMem+1; @@ -146518,672 +147145,248 @@ static int windowInitAccum(Parse *pParse, Window *pMWin){ return regArg; } +/* +** Return true if the current frame should be cached in the ephemeral table, +** even if there are no xInverse() calls required. +*/ +static int windowCacheFrame(Window *pMWin){ + Window *pWin; + if( pMWin->regStartRowid ) return 1; + for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ + FuncDef *pFunc = pWin->pFunc; + if( (pFunc->zName==nth_valueName) + || (pFunc->zName==first_valueName) + || (pFunc->zName==leadName) + || (pFunc->zName==lagName) + ){ + return 1; + } + } + return 0; +} /* -** This function does the work of sqlite3WindowCodeStep() for all "ROWS" -** window frame types except for "BETWEEN UNBOUNDED PRECEDING AND CURRENT -** ROW". Pseudo-code for each follows. -** -** ROWS BETWEEN PRECEDING AND FOLLOWING -** -** ... -** if( new partition ){ -** Gosub flush_partition -** } -** Insert (record in eph-table) -** sqlite3WhereEnd() -** Gosub flush_partition -** -** flush_partition: -** Once { -** OpenDup (iEphCsr -> csrStart) -** OpenDup (iEphCsr -> csrEnd) -** } -** regStart = // PRECEDING expression -** regEnd = // FOLLOWING expression -** if( regStart<0 || regEnd<0 ){ error! } -** Rewind (csr,csrStart,csrEnd) // if EOF goto flush_partition_done -** Next(csrEnd) // if EOF skip Aggstep -** Aggstep (csrEnd) -** if( (regEnd--)<=0 ){ -** AggFinal (xValue) -** Gosub addrGosub -** Next(csr) // if EOF goto flush_partition_done -** if( (regStart--)<=0 ){ -** AggInverse (csrStart) -** Next(csrStart) -** } -** } -** flush_partition_done: -** ResetSorter (csr) -** Return -** -** ROWS BETWEEN PRECEDING AND CURRENT ROW -** ROWS BETWEEN CURRENT ROW AND FOLLOWING -** ROWS BETWEEN UNBOUNDED PRECEDING AND FOLLOWING -** -** These are similar to the above. For "CURRENT ROW", intialize the -** register to 0. For "UNBOUNDED PRECEDING" to infinity. -** -** ROWS BETWEEN PRECEDING AND UNBOUNDED FOLLOWING -** ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING -** -** Rewind (csr,csrStart,csrEnd) // if EOF goto flush_partition_done -** while( 1 ){ -** Next(csrEnd) // Exit while(1) at EOF -** Aggstep (csrEnd) -** } -** while( 1 ){ -** AggFinal (xValue) -** Gosub addrGosub -** Next(csr) // if EOF goto flush_partition_done -** if( (regStart--)<=0 ){ -** AggInverse (csrStart) -** Next(csrStart) -** } -** } -** -** For the "CURRENT ROW AND UNBOUNDED FOLLOWING" case, the final if() -** condition is always true (as if regStart were initialized to 0). -** -** RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING -** -** This is the only RANGE case handled by this routine. It modifies the -** second while( 1 ) loop in "ROWS BETWEEN CURRENT ... UNBOUNDED..." to -** be: -** -** while( 1 ){ -** AggFinal (xValue) -** while( 1 ){ -** regPeer++ -** Gosub addrGosub -** Next(csr) // if EOF goto flush_partition_done -** if( new peer ) break; -** } -** while( (regPeer--)>0 ){ -** AggInverse (csrStart) -** Next(csrStart) -** } -** } -** -** ROWS BETWEEN FOLLOWING AND FOLLOWING -** -** regEnd = regEnd - regStart -** Rewind (csr,csrStart,csrEnd) // if EOF goto flush_partition_done -** Aggstep (csrEnd) -** Next(csrEnd) // if EOF fall-through -** if( (regEnd--)<=0 ){ -** if( (regStart--)<=0 ){ -** AggFinal (xValue) -** Gosub addrGosub -** Next(csr) // if EOF goto flush_partition_done -** } -** AggInverse (csrStart) -** Next (csrStart) -** } -** -** ROWS BETWEEN PRECEDING AND PRECEDING -** -** Replace the bit after "Rewind" in the above with: -** -** if( (regEnd--)<=0 ){ -** AggStep (csrEnd) -** Next (csrEnd) -** } -** AggFinal (xValue) -** Gosub addrGosub -** Next(csr) // if EOF goto flush_partition_done -** if( (regStart--)<=0 ){ -** AggInverse (csr2) -** Next (csr2) -** } +** regOld and regNew are each the first register in an array of size +** pOrderBy->nExpr. This function generates code to compare the two +** arrays of registers using the collation sequences and other comparison +** parameters specified by pOrderBy. ** +** If the two arrays are not equal, the contents of regNew is copied to +** regOld and control falls through. Otherwise, if the contents of the arrays +** are equal, an OP_Goto is executed. The address of the OP_Goto is returned. */ -static void windowCodeRowExprStep( - Parse *pParse, - Select *p, - WhereInfo *pWInfo, - int regGosub, - int addrGosub +static void windowIfNewPeer( + Parse *pParse, + ExprList *pOrderBy, + int regNew, /* First in array of new values */ + int regOld, /* First in array of old values */ + int addr /* Jump here */ ){ - Window *pMWin = p->pWin; Vdbe *v = sqlite3GetVdbe(pParse); - int regFlushPart; /* Register for "Gosub flush_partition" */ - int lblFlushPart; /* Label for "Gosub flush_partition" */ - int lblFlushDone; /* Label for "Gosub flush_partition_done" */ - - int regArg; - int addr; - int csrStart = pParse->nTab++; - int csrEnd = pParse->nTab++; - int regStart; /* Value of PRECEDING */ - int regEnd; /* Value of FOLLOWING */ - int addrGoto; - int addrTop; - int addrIfPos1 = 0; - int addrIfPos2 = 0; - int regSize = 0; - - assert( pMWin->eStart==TK_PRECEDING - || pMWin->eStart==TK_CURRENT - || pMWin->eStart==TK_FOLLOWING - || pMWin->eStart==TK_UNBOUNDED - ); - assert( pMWin->eEnd==TK_FOLLOWING - || pMWin->eEnd==TK_CURRENT - || pMWin->eEnd==TK_UNBOUNDED - || pMWin->eEnd==TK_PRECEDING - ); - - /* Allocate register and label for the "flush_partition" sub-routine. */ - regFlushPart = ++pParse->nMem; - lblFlushPart = sqlite3VdbeMakeLabel(pParse); - lblFlushDone = sqlite3VdbeMakeLabel(pParse); - - regStart = ++pParse->nMem; - regEnd = ++pParse->nMem; - - windowPartitionCache(pParse, p, pWInfo, regFlushPart, lblFlushPart, ®Size); - - addrGoto = sqlite3VdbeAddOp0(v, OP_Goto); - - /* Start of "flush_partition" */ - sqlite3VdbeResolveLabel(v, lblFlushPart); - sqlite3VdbeAddOp2(v, OP_Once, 0, sqlite3VdbeCurrentAddr(v)+3); - VdbeCoverage(v); - VdbeComment((v, "Flush_partition subroutine")); - sqlite3VdbeAddOp2(v, OP_OpenDup, csrStart, pMWin->iEphCsr); - sqlite3VdbeAddOp2(v, OP_OpenDup, csrEnd, pMWin->iEphCsr); - - /* If either regStart or regEnd are not non-negative integers, throw - ** an exception. */ - if( pMWin->pStart ){ - sqlite3ExprCode(pParse, pMWin->pStart, regStart); - windowCheckIntValue(pParse, regStart, 0); - } - if( pMWin->pEnd ){ - sqlite3ExprCode(pParse, pMWin->pEnd, regEnd); - windowCheckIntValue(pParse, regEnd, 1); - } - - /* If this is "ROWS FOLLOWING AND ROWS FOLLOWING", do: - ** - ** if( regEndpEnd && pMWin->eStart==TK_FOLLOWING ){ - assert( pMWin->pStart!=0 ); - assert( pMWin->eEnd==TK_FOLLOWING ); - sqlite3VdbeAddOp3(v, OP_Ge, regStart, sqlite3VdbeCurrentAddr(v)+2, regEnd); - VdbeCoverageNeverNull(v); - sqlite3VdbeAddOp2(v, OP_Copy, regSize, regStart); - sqlite3VdbeAddOp3(v, OP_Subtract, regStart, regEnd, regEnd); - } - - if( pMWin->pStart && pMWin->eEnd==TK_PRECEDING ){ - assert( pMWin->pEnd!=0 ); - assert( pMWin->eStart==TK_PRECEDING ); - sqlite3VdbeAddOp3(v, OP_Le, regStart, sqlite3VdbeCurrentAddr(v)+3, regEnd); - VdbeCoverageNeverNull(v); - sqlite3VdbeAddOp2(v, OP_Copy, regSize, regStart); - sqlite3VdbeAddOp2(v, OP_Copy, regSize, regEnd); - } - - /* Initialize the accumulator register for each window function to NULL */ - regArg = windowInitAccum(pParse, pMWin); - - sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr, lblFlushDone); - VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_Rewind, csrStart, lblFlushDone); - VdbeCoverageNeverTaken(v); - sqlite3VdbeChangeP5(v, 1); - sqlite3VdbeAddOp2(v, OP_Rewind, csrEnd, lblFlushDone); - VdbeCoverageNeverTaken(v); - sqlite3VdbeChangeP5(v, 1); - - /* Invoke AggStep function for each window function using the row that - ** csrEnd currently points to. Or, if csrEnd is already at EOF, - ** do nothing. */ - addrTop = sqlite3VdbeCurrentAddr(v); - if( pMWin->eEnd==TK_PRECEDING ){ - addrIfPos1 = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0 , 1); - VdbeCoverage(v); - } - sqlite3VdbeAddOp2(v, OP_Next, csrEnd, sqlite3VdbeCurrentAddr(v)+2); - VdbeCoverage(v); - addr = sqlite3VdbeAddOp0(v, OP_Goto); - windowAggStep(pParse, pMWin, csrEnd, 0, regArg, regSize); - if( pMWin->eEnd==TK_UNBOUNDED ){ - sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop); - sqlite3VdbeJumpHere(v, addr); - addrTop = sqlite3VdbeCurrentAddr(v); + if( pOrderBy ){ + int nVal = pOrderBy->nExpr; + KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOrderBy, 0, 0); + sqlite3VdbeAddOp3(v, OP_Compare, regOld, regNew, nVal); + sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO); + sqlite3VdbeAddOp3(v, OP_Jump, + sqlite3VdbeCurrentAddr(v)+1, addr, sqlite3VdbeCurrentAddr(v)+1 + ); + VdbeCoverageEqNe(v); + sqlite3VdbeAddOp3(v, OP_Copy, regNew, regOld, nVal-1); }else{ - sqlite3VdbeJumpHere(v, addr); - if( pMWin->eEnd==TK_PRECEDING ){ - sqlite3VdbeJumpHere(v, addrIfPos1); - } - } - - if( pMWin->eEnd==TK_FOLLOWING ){ - addrIfPos1 = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0 , 1); - VdbeCoverage(v); - } - if( pMWin->eStart==TK_FOLLOWING ){ - addrIfPos2 = sqlite3VdbeAddOp3(v, OP_IfPos, regStart, 0 , 1); - VdbeCoverage(v); + sqlite3VdbeAddOp2(v, OP_Goto, 0, addr); } - windowAggFinal(pParse, pMWin, 0); - windowReturnOneRow(pParse, pMWin, regGosub, addrGosub); - sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)+2); - VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_Goto, 0, lblFlushDone); - if( pMWin->eStart==TK_FOLLOWING ){ - sqlite3VdbeJumpHere(v, addrIfPos2); - } - - if( pMWin->eStart==TK_CURRENT - || pMWin->eStart==TK_PRECEDING - || pMWin->eStart==TK_FOLLOWING - ){ - int lblSkipInverse = sqlite3VdbeMakeLabel(pParse);; - if( pMWin->eStart==TK_PRECEDING ){ - sqlite3VdbeAddOp3(v, OP_IfPos, regStart, lblSkipInverse, 1); - VdbeCoverage(v); - } - if( pMWin->eStart==TK_FOLLOWING ){ - sqlite3VdbeAddOp2(v, OP_Next, csrStart, sqlite3VdbeCurrentAddr(v)+2); - VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_Goto, 0, lblSkipInverse); - }else{ - sqlite3VdbeAddOp2(v, OP_Next, csrStart, sqlite3VdbeCurrentAddr(v)+1); - VdbeCoverageAlwaysTaken(v); - } - windowAggStep(pParse, pMWin, csrStart, 1, regArg, regSize); - sqlite3VdbeResolveLabel(v, lblSkipInverse); - } - if( pMWin->eEnd==TK_FOLLOWING ){ - sqlite3VdbeJumpHere(v, addrIfPos1); - } - sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop); - - /* flush_partition_done: */ - sqlite3VdbeResolveLabel(v, lblFlushDone); - sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr); - sqlite3VdbeAddOp1(v, OP_Return, regFlushPart); - VdbeComment((v, "end flush_partition subroutine")); - - /* Jump to here to skip over flush_partition */ - sqlite3VdbeJumpHere(v, addrGoto); } /* -** This function does the work of sqlite3WindowCodeStep() for cases that -** would normally be handled by windowCodeDefaultStep() when there are -** one or more built-in window-functions that require the entire partition -** to be cached in a temp table before any rows can be returned. Additionally. -** "RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING" is always handled by -** this function. -** -** Pseudo-code corresponding to the VM code generated by this function -** for each type of window follows. -** -** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW -** -** flush_partition: -** Once { -** OpenDup (iEphCsr -> csrLead) -** } -** Integer ctr 0 -** foreach row (csrLead){ -** if( new peer ){ -** AggFinal (xValue) -** for(i=0; i= csr2.peerVal ) goto lbl; ** -** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW -** -** As above, except that the "if( new peer )" branch is always taken. -** -** RANGE BETWEEN CURRENT ROW AND CURRENT ROW -** -** As above, except that each of the for() loops becomes: -** -** for(i=0; i csrLead) -** } -** foreach row (csrLead) { -** AggStep (csrLead) -** } -** foreach row (iEphCsr) { -** Gosub addrGosub -** } -** -** RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING -** -** flush_partition: -** Once { -** OpenDup (iEphCsr -> csrLead) -** } -** foreach row (csrLead){ -** AggStep (csrLead) -** } -** Rewind (csrLead) -** Integer ctr 0 -** foreach row (csrLead){ -** if( new peer ){ -** AggFinal (xValue) -** for(i=0; ipWin; + Parse *pParse = p->pParse; Vdbe *v = sqlite3GetVdbe(pParse); - int k; - int addr; - ExprList *pPart = pMWin->pPartition; - ExprList *pOrderBy = pMWin->pOrderBy; - int nPeer = pOrderBy ? pOrderBy->nExpr : 0; - int regNewPeer; - - int addrGoto; /* Address of Goto used to jump flush_par.. */ - int addrNext; /* Jump here for next iteration of loop */ - int regFlushPart; - int lblFlushPart; - int csrLead; - int regCtr; - int regArg; /* Register array to martial function args */ - int regSize; - int lblEmpty; - int bReverse = pMWin->pOrderBy && pMWin->eStart==TK_CURRENT - && pMWin->eEnd==TK_UNBOUNDED; - - assert( (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_CURRENT) - || (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_UNBOUNDED) - || (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_CURRENT) - || (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_UNBOUNDED) - ); - - lblEmpty = sqlite3VdbeMakeLabel(pParse); - regNewPeer = pParse->nMem+1; - pParse->nMem += nPeer; - - /* Allocate register and label for the "flush_partition" sub-routine. */ - regFlushPart = ++pParse->nMem; - lblFlushPart = sqlite3VdbeMakeLabel(pParse); - - csrLead = pParse->nTab++; - regCtr = ++pParse->nMem; - - windowPartitionCache(pParse, p, pWInfo, regFlushPart, lblFlushPart, ®Size); - addrGoto = sqlite3VdbeAddOp0(v, OP_Goto); - - /* Start of "flush_partition" */ - sqlite3VdbeResolveLabel(v, lblFlushPart); - sqlite3VdbeAddOp2(v, OP_Once, 0, sqlite3VdbeCurrentAddr(v)+2); - VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_OpenDup, csrLead, pMWin->iEphCsr); - - /* Initialize the accumulator register for each window function to NULL */ - regArg = windowInitAccum(pParse, pMWin); - - sqlite3VdbeAddOp2(v, OP_Integer, 0, regCtr); - sqlite3VdbeAddOp2(v, OP_Rewind, csrLead, lblEmpty); - VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr, lblEmpty); - VdbeCoverageNeverTaken(v); + int reg1 = sqlite3GetTempReg(pParse); + int reg2 = sqlite3GetTempReg(pParse); + int arith = OP_Add; + int addrGe; - if( bReverse ){ - int addr2 = sqlite3VdbeCurrentAddr(v); - windowAggStep(pParse, pMWin, csrLead, 0, regArg, regSize); - sqlite3VdbeAddOp2(v, OP_Next, csrLead, addr2); - VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_Rewind, csrLead, lblEmpty); - VdbeCoverageNeverTaken(v); - } - addrNext = sqlite3VdbeCurrentAddr(v); + int regString = ++pParse->nMem; - if( pOrderBy && (pMWin->eEnd==TK_CURRENT || pMWin->eStart==TK_CURRENT) ){ - int bCurrent = (pMWin->eStart==TK_CURRENT); - int addrJump = 0; /* Address of OP_Jump below */ - if( pMWin->eType==TK_RANGE ){ - int iOff = pMWin->nBufferCol + (pPart ? pPart->nExpr : 0); - int regPeer = pMWin->regPart + (pPart ? pPart->nExpr : 0); - KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOrderBy, 0, 0); - for(k=0; kpMWin->pOrderBy && p->pMWin->pOrderBy->nExpr==1 ); + if( p->pMWin->pOrderBy->a[0].sortOrder ){ + switch( op ){ + case OP_Ge: op = OP_Le; break; + case OP_Gt: op = OP_Lt; break; + default: assert( op==OP_Le ); op = OP_Ge; break; } - - windowReturnRows(pParse, pMWin, regCtr, regGosub, addrGosub, - (bCurrent ? regArg : 0), (bCurrent ? regSize : 0) - ); - if( addrJump ) sqlite3VdbeJumpHere(v, addrJump); - } - - if( bReverse==0 ){ - windowAggStep(pParse, pMWin, csrLead, 0, regArg, regSize); + arith = OP_Subtract; } - sqlite3VdbeAddOp2(v, OP_AddImm, regCtr, 1); - sqlite3VdbeAddOp2(v, OP_Next, csrLead, addrNext); - VdbeCoverage(v); - windowReturnRows(pParse, pMWin, regCtr, regGosub, addrGosub, 0, 0); + windowReadPeerValues(p, csr1, reg1); + windowReadPeerValues(p, csr2, reg2); - sqlite3VdbeResolveLabel(v, lblEmpty); - sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr); - sqlite3VdbeAddOp1(v, OP_Return, regFlushPart); + /* Check if the peer value for csr1 value is a text or blob by comparing + ** it to the smallest possible string - ''. If it is, jump over the + ** OP_Add or OP_Subtract operation and proceed directly to the comparison. */ + sqlite3VdbeAddOp4(v, OP_String8, 0, regString, 0, "", P4_STATIC); + addrGe = sqlite3VdbeAddOp3(v, OP_Ge, regString, 0, reg1); + VdbeCoverage(v); + sqlite3VdbeAddOp3(v, arith, regVal, reg1, reg1); + sqlite3VdbeJumpHere(v, addrGe); + sqlite3VdbeAddOp3(v, op, reg2, lbl, reg1); VdbeCoverage(v); + sqlite3VdbeChangeP5(v, SQLITE_NULLEQ); + assert( op==OP_Ge || op==OP_Gt || op==OP_Lt || op==OP_Le ); + testcase(op==OP_Ge); VdbeCoverageIf(v, op==OP_Ge); + testcase(op==OP_Lt); VdbeCoverageIf(v, op==OP_Lt); + testcase(op==OP_Le); VdbeCoverageIf(v, op==OP_Le); + testcase(op==OP_Gt); VdbeCoverageIf(v, op==OP_Gt); - /* Jump to here to skip over flush_partition */ - sqlite3VdbeJumpHere(v, addrGoto); + sqlite3ReleaseTempReg(pParse, reg1); + sqlite3ReleaseTempReg(pParse, reg2); } - /* -** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW -** -** ... -** if( new partition ){ -** AggFinal (xFinalize) -** Gosub addrGosub -** ResetSorter eph-table -** } -** else if( new peer ){ -** AggFinal (xValue) -** Gosub addrGosub -** ResetSorter eph-table -** } -** AggStep -** Insert (record into eph-table) -** sqlite3WhereEnd() -** AggFinal (xFinalize) -** Gosub addrGosub -** -** RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING -** -** As above, except take no action for a "new peer". Invoke -** the sub-routine once only for each partition. -** -** RANGE BETWEEN CURRENT ROW AND CURRENT ROW -** -** As above, except that the "new peer" condition is handled in the -** same way as "new partition" (so there is no "else if" block). -** -** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW -** -** As above, except assume every row is a "new peer". +** Helper function for sqlite3WindowCodeStep(). Each call to this function +** generates VM code for a single RETURN_ROW, AGGSTEP or AGGINVERSE +** operation. Refer to the header comment for sqlite3WindowCodeStep() for +** details. */ -static void windowCodeDefaultStep( - Parse *pParse, - Select *p, - WhereInfo *pWInfo, - int regGosub, - int addrGosub +static int windowCodeOp( + WindowCodeArg *p, /* Context object */ + int op, /* WINDOW_RETURN_ROW, AGGSTEP or AGGINVERSE */ + int regCountdown, /* Register for OP_IfPos countdown */ + int jumpOnEof /* Jump here if stepped cursor reaches EOF */ ){ - Window *pMWin = p->pWin; - Vdbe *v = sqlite3GetVdbe(pParse); - int k; - int iSubCsr = p->pSrc->a[0].iCursor; - int nSub = p->pSrc->a[0].pTab->nCol; - int reg = pParse->nMem+1; - int regRecord = reg+nSub; - int regRowid = regRecord+1; - int addr; - ExprList *pPart = pMWin->pPartition; - ExprList *pOrderBy = pMWin->pOrderBy; - - assert( pMWin->eType==TK_RANGE - || (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_CURRENT) - ); - - assert( (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_CURRENT) - || (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_UNBOUNDED) - || (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_CURRENT) - || (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_UNBOUNDED && !pOrderBy) - ); - - if( pMWin->eEnd==TK_UNBOUNDED ){ - pOrderBy = 0; - } - - pParse->nMem += nSub + 2; - - /* Load the individual column values of the row returned by - ** the sub-select into an array of registers. */ - for(k=0; kpParse; + Window *pMWin = p->pMWin; + int ret = 0; + Vdbe *v = p->pVdbe; + int addrIf = 0; + int addrContinue = 0; + int addrGoto = 0; + int bPeer = (pMWin->eFrmType!=TK_ROWS); + + int lblDone = sqlite3VdbeMakeLabel(pParse); + int addrNextRange = 0; + + /* Special case - WINDOW_AGGINVERSE is always a no-op if the frame + ** starts with UNBOUNDED PRECEDING. */ + if( op==WINDOW_AGGINVERSE && pMWin->eStart==TK_UNBOUNDED ){ + assert( regCountdown==0 && jumpOnEof==0 ); + return 0; } - /* Check if this is the start of a new partition or peer group. */ - if( pPart || pOrderBy ){ - int nPart = (pPart ? pPart->nExpr : 0); - int addrGoto = 0; - int addrJump = 0; - int nPeer = (pOrderBy ? pOrderBy->nExpr : 0); - - if( pPart ){ - int regNewPart = reg + pMWin->nBufferCol; - KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pPart, 0, 0); - addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPart, pMWin->regPart,nPart); - sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO); - addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2); - VdbeCoverageEqNe(v); - windowAggFinal(pParse, pMWin, 1); - if( pOrderBy ){ - addrGoto = sqlite3VdbeAddOp0(v, OP_Goto); + if( regCountdown>0 ){ + if( pMWin->eFrmType==TK_RANGE ){ + addrNextRange = sqlite3VdbeCurrentAddr(v); + assert( op==WINDOW_AGGINVERSE || op==WINDOW_AGGSTEP ); + if( op==WINDOW_AGGINVERSE ){ + if( pMWin->eStart==TK_FOLLOWING ){ + windowCodeRangeTest( + p, OP_Le, p->current.csr, regCountdown, p->start.csr, lblDone + ); + }else{ + windowCodeRangeTest( + p, OP_Ge, p->start.csr, regCountdown, p->current.csr, lblDone + ); + } + }else{ + windowCodeRangeTest( + p, OP_Gt, p->end.csr, regCountdown, p->current.csr, lblDone + ); } + }else{ + addrIf = sqlite3VdbeAddOp3(v, OP_IfPos, regCountdown, 0, 1); + VdbeCoverage(v); } + } - if( pOrderBy ){ - int regNewPeer = reg + pMWin->nBufferCol + nPart; - int regPeer = pMWin->regPart + nPart; + if( op==WINDOW_RETURN_ROW && pMWin->regStartRowid==0 ){ + windowAggFinal(p, 0); + } + addrContinue = sqlite3VdbeCurrentAddr(v); + switch( op ){ + case WINDOW_RETURN_ROW: + csr = p->current.csr; + reg = p->current.reg; + windowReturnOneRow(p); + break; - if( addrJump ) sqlite3VdbeJumpHere(v, addrJump); - if( pMWin->eType==TK_RANGE ){ - KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOrderBy, 0, 0); - addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPeer, regPeer, nPeer); - sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO); - addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2); - VdbeCoverage(v); + case WINDOW_AGGINVERSE: + csr = p->start.csr; + reg = p->start.reg; + if( pMWin->regStartRowid ){ + assert( pMWin->regEndRowid ); + sqlite3VdbeAddOp2(v, OP_AddImm, pMWin->regStartRowid, 1); }else{ - addrJump = 0; + windowAggStep(pParse, pMWin, csr, 1, p->regArg); } - windowAggFinal(pParse, pMWin, pMWin->eStart==TK_CURRENT); - if( addrGoto ) sqlite3VdbeJumpHere(v, addrGoto); - } - - sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr,sqlite3VdbeCurrentAddr(v)+3); - VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub); - sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)-1); - VdbeCoverage(v); - - sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr); - sqlite3VdbeAddOp3( - v, OP_Copy, reg+pMWin->nBufferCol, pMWin->regPart, nPart+nPeer-1 - ); + break; - if( addrJump ) sqlite3VdbeJumpHere(v, addrJump); + default: + assert( op==WINDOW_AGGSTEP ); + csr = p->end.csr; + reg = p->end.reg; + if( pMWin->regStartRowid ){ + assert( pMWin->regEndRowid ); + sqlite3VdbeAddOp2(v, OP_AddImm, pMWin->regEndRowid, 1); + }else{ + windowAggStep(pParse, pMWin, csr, 0, p->regArg); + } + break; } - /* Invoke step function for window functions */ - windowAggStep(pParse, pMWin, -1, 0, reg, 0); + if( op==p->eDelete ){ + sqlite3VdbeAddOp1(v, OP_Delete, csr); + sqlite3VdbeChangeP5(v, OPFLAG_SAVEPOSITION); + } - /* Buffer the current row in the ephemeral table. */ - if( pMWin->nBufferCol>0 ){ - sqlite3VdbeAddOp3(v, OP_MakeRecord, reg, pMWin->nBufferCol, regRecord); + if( jumpOnEof ){ + sqlite3VdbeAddOp2(v, OP_Next, csr, sqlite3VdbeCurrentAddr(v)+2); + VdbeCoverage(v); + ret = sqlite3VdbeAddOp0(v, OP_Goto); }else{ - sqlite3VdbeAddOp2(v, OP_Blob, 0, regRecord); - sqlite3VdbeAppendP4(v, (void*)"", 0); + sqlite3VdbeAddOp2(v, OP_Next, csr, sqlite3VdbeCurrentAddr(v)+1+bPeer); + VdbeCoverage(v); + if( bPeer ){ + addrGoto = sqlite3VdbeAddOp0(v, OP_Goto); + } } - sqlite3VdbeAddOp2(v, OP_NewRowid, pMWin->iEphCsr, regRowid); - sqlite3VdbeAddOp3(v, OP_Insert, pMWin->iEphCsr, regRecord, regRowid); - /* End the database scan loop. */ - sqlite3WhereEnd(pWInfo); + if( bPeer ){ + int nReg = (pMWin->pOrderBy ? pMWin->pOrderBy->nExpr : 0); + int regTmp = (nReg ? sqlite3GetTempRange(pParse, nReg) : 0); + windowReadPeerValues(p, csr, regTmp); + windowIfNewPeer(pParse, pMWin->pOrderBy, regTmp, reg, addrContinue); + sqlite3ReleaseTempRange(pParse, regTmp, nReg); + } - windowAggFinal(pParse, pMWin, 1); - sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr,sqlite3VdbeCurrentAddr(v)+3); - VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub); - sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)-1); - VdbeCoverage(v); + if( addrNextRange ){ + sqlite3VdbeAddOp2(v, OP_Goto, 0, addrNextRange); + } + sqlite3VdbeResolveLabel(v, lblDone); + if( addrGoto ) sqlite3VdbeJumpHere(v, addrGoto); + if( addrIf ) sqlite3VdbeJumpHere(v, addrIf); + return ret; } + /* ** Allocate and return a duplicate of the Window object indicated by the ** third argument. Set the Window.pOwner field of the new object to @@ -147199,9 +147402,10 @@ SQLITE_PRIVATE Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p){ pNew->pFunc = p->pFunc; pNew->pPartition = sqlite3ExprListDup(db, p->pPartition, 0); pNew->pOrderBy = sqlite3ExprListDup(db, p->pOrderBy, 0); - pNew->eType = p->eType; + pNew->eFrmType = p->eFrmType; pNew->eEnd = p->eEnd; pNew->eStart = p->eStart; + pNew->eExclude = p->eExclude; pNew->pStart = sqlite3ExprDup(db, p->pStart, 0); pNew->pEnd = sqlite3ExprDup(db, p->pEnd, 0); pNew->pOwner = pOwner; @@ -147228,12 +147432,360 @@ SQLITE_PRIVATE Window *sqlite3WindowListDup(sqlite3 *db, Window *p){ return pRet; } +/* +** Return true if it can be determined at compile time that expression +** pExpr evaluates to a value that, when cast to an integer, is greater +** than zero. False otherwise. +** +** If an OOM error occurs, this function sets the Parse.db.mallocFailed +** flag and returns zero. +*/ +static int windowExprGtZero(Parse *pParse, Expr *pExpr){ + int ret = 0; + sqlite3 *db = pParse->db; + sqlite3_value *pVal = 0; + sqlite3ValueFromExpr(db, pExpr, db->enc, SQLITE_AFF_NUMERIC, &pVal); + if( pVal && sqlite3_value_int(pVal)>0 ){ + ret = 1; + } + sqlite3ValueFree(pVal); + return ret; +} + /* ** sqlite3WhereBegin() has already been called for the SELECT statement ** passed as the second argument when this function is invoked. It generates -** code to populate the Window.regResult register for each window function and -** invoke the sub-routine at instruction addrGosub once for each row. -** This function calls sqlite3WhereEnd() before returning. +** code to populate the Window.regResult register for each window function +** and invoke the sub-routine at instruction addrGosub once for each row. +** sqlite3WhereEnd() is always called before returning. +** +** This function handles several different types of window frames, which +** require slightly different processing. The following pseudo code is +** used to implement window frames of the form: +** +** ROWS BETWEEN PRECEDING AND FOLLOWING +** +** Other window frame types use variants of the following: +** +** ... loop started by sqlite3WhereBegin() ... +** if( new partition ){ +** Gosub flush +** } +** Insert new row into eph table. +** +** if( first row of partition ){ +** // Rewind three cursors, all open on the eph table. +** Rewind(csrEnd); +** Rewind(csrStart); +** Rewind(csrCurrent); +** +** regEnd = // FOLLOWING expression +** regStart = // PRECEDING expression +** }else{ +** // First time this branch is taken, the eph table contains two +** // rows. The first row in the partition, which all three cursors +** // currently point to, and the following row. +** AGGSTEP +** if( (regEnd--)<=0 ){ +** RETURN_ROW +** if( (regStart--)<=0 ){ +** AGGINVERSE +** } +** } +** } +** } +** flush: +** AGGSTEP +** while( 1 ){ +** RETURN ROW +** if( csrCurrent is EOF ) break; +** if( (regStart--)<=0 ){ +** AggInverse(csrStart) +** Next(csrStart) +** } +** } +** +** The pseudo-code above uses the following shorthand: +** +** AGGSTEP: invoke the aggregate xStep() function for each window function +** with arguments read from the current row of cursor csrEnd, then +** step cursor csrEnd forward one row (i.e. sqlite3BtreeNext()). +** +** RETURN_ROW: return a row to the caller based on the contents of the +** current row of csrCurrent and the current state of all +** aggregates. Then step cursor csrCurrent forward one row. +** +** AGGINVERSE: invoke the aggregate xInverse() function for each window +** functions with arguments read from the current row of cursor +** csrStart. Then step csrStart forward one row. +** +** There are two other ROWS window frames that are handled significantly +** differently from the above - "BETWEEN PRECEDING AND PRECEDING" +** and "BETWEEN FOLLOWING AND FOLLOWING". These are special +** cases because they change the order in which the three cursors (csrStart, +** csrCurrent and csrEnd) iterate through the ephemeral table. Cases that +** use UNBOUNDED or CURRENT ROW are much simpler variations on one of these +** three. +** +** ROWS BETWEEN PRECEDING AND PRECEDING +** +** ... loop started by sqlite3WhereBegin() ... +** if( new partition ){ +** Gosub flush +** } +** Insert new row into eph table. +** if( first row of partition ){ +** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent) +** regEnd = +** regStart = +** }else{ +** if( (regEnd--)<=0 ){ +** AGGSTEP +** } +** RETURN_ROW +** if( (regStart--)<=0 ){ +** AGGINVERSE +** } +** } +** } +** flush: +** if( (regEnd--)<=0 ){ +** AGGSTEP +** } +** RETURN_ROW +** +** +** ROWS BETWEEN FOLLOWING AND FOLLOWING +** +** ... loop started by sqlite3WhereBegin() ... +** if( new partition ){ +** Gosub flush +** } +** Insert new row into eph table. +** if( first row of partition ){ +** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent) +** regEnd = +** regStart = regEnd - +** }else{ +** AGGSTEP +** if( (regEnd--)<=0 ){ +** RETURN_ROW +** } +** if( (regStart--)<=0 ){ +** AGGINVERSE +** } +** } +** } +** flush: +** AGGSTEP +** while( 1 ){ +** if( (regEnd--)<=0 ){ +** RETURN_ROW +** if( eof ) break; +** } +** if( (regStart--)<=0 ){ +** AGGINVERSE +** if( eof ) break +** } +** } +** while( !eof csrCurrent ){ +** RETURN_ROW +** } +** +** For the most part, the patterns above are adapted to support UNBOUNDED by +** assuming that it is equivalent to "infinity PRECEDING/FOLLOWING" and +** CURRENT ROW by assuming that it is equivilent to "0 PRECEDING/FOLLOWING". +** This is optimized of course - branches that will never be taken and +** conditions that are always true are omitted from the VM code. The only +** exceptional case is: +** +** ROWS BETWEEN FOLLOWING AND UNBOUNDED FOLLOWING +** +** ... loop started by sqlite3WhereBegin() ... +** if( new partition ){ +** Gosub flush +** } +** Insert new row into eph table. +** if( first row of partition ){ +** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent) +** regStart = +** }else{ +** AGGSTEP +** } +** } +** flush: +** AGGSTEP +** while( 1 ){ +** if( (regStart--)<=0 ){ +** AGGINVERSE +** if( eof ) break +** } +** RETURN_ROW +** } +** while( !eof csrCurrent ){ +** RETURN_ROW +** } +** +** Also requiring special handling are the cases: +** +** ROWS BETWEEN PRECEDING AND PRECEDING +** ROWS BETWEEN FOLLOWING AND FOLLOWING +** +** when (expr1 < expr2). This is detected at runtime, not by this function. +** To handle this case, the pseudo-code programs depicted above are modified +** slightly to be: +** +** ... loop started by sqlite3WhereBegin() ... +** if( new partition ){ +** Gosub flush +** } +** Insert new row into eph table. +** if( first row of partition ){ +** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent) +** regEnd = +** regStart = +** if( regEnd < regStart ){ +** RETURN_ROW +** delete eph table contents +** continue +** } +** ... +** +** The new "continue" statement in the above jumps to the next iteration +** of the outer loop - the one started by sqlite3WhereBegin(). +** +** The various GROUPS cases are implemented using the same patterns as +** ROWS. The VM code is modified slightly so that: +** +** 1. The else branch in the main loop is only taken if the row just +** added to the ephemeral table is the start of a new group. In +** other words, it becomes: +** +** ... loop started by sqlite3WhereBegin() ... +** if( new partition ){ +** Gosub flush +** } +** Insert new row into eph table. +** if( first row of partition ){ +** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent) +** regEnd = +** regStart = +** }else if( new group ){ +** ... +** } +** } +** +** 2. Instead of processing a single row, each RETURN_ROW, AGGSTEP or +** AGGINVERSE step processes the current row of the relevant cursor and +** all subsequent rows belonging to the same group. +** +** RANGE window frames are a little different again. As for GROUPS, the +** main loop runs once per group only. And RETURN_ROW, AGGSTEP and AGGINVERSE +** deal in groups instead of rows. As for ROWS and GROUPS, there are three +** basic cases: +** +** RANGE BETWEEN PRECEDING AND FOLLOWING +** +** ... loop started by sqlite3WhereBegin() ... +** if( new partition ){ +** Gosub flush +** } +** Insert new row into eph table. +** if( first row of partition ){ +** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent) +** regEnd = +** regStart = +** }else{ +** AGGSTEP +** while( (csrCurrent.key + regEnd) < csrEnd.key ){ +** RETURN_ROW +** while( csrStart.key + regStart) < csrCurrent.key ){ +** AGGINVERSE +** } +** } +** } +** } +** flush: +** AGGSTEP +** while( 1 ){ +** RETURN ROW +** if( csrCurrent is EOF ) break; +** while( csrStart.key + regStart) < csrCurrent.key ){ +** AGGINVERSE +** } +** } +** } +** +** In the above notation, "csr.key" means the current value of the ORDER BY +** expression (there is only ever 1 for a RANGE that uses an FOLLOWING +** or PRECEDING AND PRECEDING +** +** ... loop started by sqlite3WhereBegin() ... +** if( new partition ){ +** Gosub flush +** } +** Insert new row into eph table. +** if( first row of partition ){ +** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent) +** regEnd = +** regStart = +** }else{ +** if( (csrEnd.key + regEnd) <= csrCurrent.key ){ +** AGGSTEP +** } +** while( (csrStart.key + regStart) < csrCurrent.key ){ +** AGGINVERSE +** } +** RETURN_ROW +** } +** } +** flush: +** while( (csrEnd.key + regEnd) <= csrCurrent.key ){ +** AGGSTEP +** } +** while( (csrStart.key + regStart) < csrCurrent.key ){ +** AGGINVERSE +** } +** RETURN_ROW +** +** RANGE BETWEEN FOLLOWING AND FOLLOWING +** +** ... loop started by sqlite3WhereBegin() ... +** if( new partition ){ +** Gosub flush +** } +** Insert new row into eph table. +** if( first row of partition ){ +** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent) +** regEnd = +** regStart = +** }else{ +** AGGSTEP +** while( (csrCurrent.key + regEnd) < csrEnd.key ){ +** while( (csrCurrent.key + regStart) > csrStart.key ){ +** AGGINVERSE +** } +** RETURN_ROW +** } +** } +** } +** flush: +** AGGSTEP +** while( 1 ){ +** while( (csrCurrent.key + regStart) > csrStart.key ){ +** AGGINVERSE +** if( eof ) break "while( 1 )" loop. +** } +** RETURN_ROW +** } +** while( !eof csrCurrent ){ +** RETURN_ROW +** } +** +** The text above leaves out many details. Refer to the code and comments +** below for a more complete picture. */ SQLITE_PRIVATE void sqlite3WindowCodeStep( Parse *pParse, /* Parse context */ @@ -147243,75 +147795,321 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep( int addrGosub /* OP_Gosub here to return each row */ ){ Window *pMWin = p->pWin; + ExprList *pOrderBy = pMWin->pOrderBy; + Vdbe *v = sqlite3GetVdbe(pParse); + int csrWrite; /* Cursor used to write to eph. table */ + int csrInput = p->pSrc->a[0].iCursor; /* Cursor of sub-select */ + int nInput = p->pSrc->a[0].pTab->nCol; /* Number of cols returned by sub */ + int iInput; /* To iterate through sub cols */ + int addrNe; /* Address of OP_Ne */ + int addrGosubFlush = 0; /* Address of OP_Gosub to flush: */ + int addrInteger = 0; /* Address of OP_Integer */ + int addrEmpty; /* Address of OP_Rewind in flush: */ + int regStart = 0; /* Value of PRECEDING */ + int regEnd = 0; /* Value of FOLLOWING */ + int regNew; /* Array of registers holding new input row */ + int regRecord; /* regNew array in record form */ + int regRowid; /* Rowid for regRecord in eph table */ + int regNewPeer = 0; /* Peer values for new row (part of regNew) */ + int regPeer = 0; /* Peer values for current row */ + int regFlushPart = 0; /* Register for "Gosub flush_partition" */ + WindowCodeArg s; /* Context object for sub-routines */ + int lblWhereEnd; /* Label just before sqlite3WhereEnd() code */ + + assert( pMWin->eStart==TK_PRECEDING || pMWin->eStart==TK_CURRENT + || pMWin->eStart==TK_FOLLOWING || pMWin->eStart==TK_UNBOUNDED + ); + assert( pMWin->eEnd==TK_FOLLOWING || pMWin->eEnd==TK_CURRENT + || pMWin->eEnd==TK_UNBOUNDED || pMWin->eEnd==TK_PRECEDING + ); + assert( pMWin->eExclude==0 || pMWin->eExclude==TK_CURRENT + || pMWin->eExclude==TK_GROUP || pMWin->eExclude==TK_TIES + || pMWin->eExclude==TK_NO + ); - /* There are three different functions that may be used to do the work - ** of this one, depending on the window frame and the specific built-in - ** window functions used (if any). - ** - ** windowCodeRowExprStep() handles all "ROWS" window frames, except for: - ** - ** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW - ** - ** The exception is because windowCodeRowExprStep() implements all window - ** frame types by caching the entire partition in a temp table, and - ** "ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW" is easy enough to - ** implement without such a cache. - ** - ** windowCodeCacheStep() is used for: - ** - ** RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING - ** - ** It is also used for anything not handled by windowCodeRowExprStep() - ** that invokes a built-in window function that requires the entire - ** partition to be cached in a temp table before any rows are returned - ** (e.g. nth_value() or percent_rank()). - ** - ** Finally, assuming there is no built-in window function that requires - ** the partition to be cached, windowCodeDefaultStep() is used for: - ** - ** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW - ** RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING - ** RANGE BETWEEN CURRENT ROW AND CURRENT ROW - ** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW - ** - ** windowCodeDefaultStep() is the only one of the three functions that - ** does not cache each partition in a temp table before beginning to - ** return rows. - */ - if( pMWin->eType==TK_ROWS - && (pMWin->eStart!=TK_UNBOUNDED||pMWin->eEnd!=TK_CURRENT||!pMWin->pOrderBy) - ){ - VdbeModuleComment((pParse->pVdbe, "Begin RowExprStep()")); - windowCodeRowExprStep(pParse, p, pWInfo, regGosub, addrGosub); - }else{ - Window *pWin; - int bCache = 0; /* True to use CacheStep() */ - - if( pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_UNBOUNDED ){ - bCache = 1; - }else{ - for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ - FuncDef *pFunc = pWin->pFunc; - if( (pFunc->funcFlags & SQLITE_FUNC_WINDOW_SIZE) - || (pFunc->zName==nth_valueName) - || (pFunc->zName==first_valueName) - || (pFunc->zName==leadName) - || (pFunc->zName==lagName) - ){ - bCache = 1; - break; + lblWhereEnd = sqlite3VdbeMakeLabel(pParse); + + /* Fill in the context object */ + memset(&s, 0, sizeof(WindowCodeArg)); + s.pParse = pParse; + s.pMWin = pMWin; + s.pVdbe = v; + s.regGosub = regGosub; + s.addrGosub = addrGosub; + s.current.csr = pMWin->iEphCsr; + csrWrite = s.current.csr+1; + s.start.csr = s.current.csr+2; + s.end.csr = s.current.csr+3; + + /* Figure out when rows may be deleted from the ephemeral table. There + ** are four options - they may never be deleted (eDelete==0), they may + ** be deleted as soon as they are no longer part of the window frame + ** (eDelete==WINDOW_AGGINVERSE), they may be deleted as after the row + ** has been returned to the caller (WINDOW_RETURN_ROW), or they may + ** be deleted after they enter the frame (WINDOW_AGGSTEP). */ + switch( pMWin->eStart ){ + case TK_FOLLOWING: + if( pMWin->eFrmType!=TK_RANGE + && windowExprGtZero(pParse, pMWin->pStart) + ){ + s.eDelete = WINDOW_RETURN_ROW; + } + break; + case TK_UNBOUNDED: + if( windowCacheFrame(pMWin)==0 ){ + if( pMWin->eEnd==TK_PRECEDING ){ + if( pMWin->eFrmType!=TK_RANGE + && windowExprGtZero(pParse, pMWin->pEnd) + ){ + s.eDelete = WINDOW_AGGSTEP; + } + }else{ + s.eDelete = WINDOW_RETURN_ROW; + } + } + break; + default: + s.eDelete = WINDOW_AGGINVERSE; + break; + } + + /* Allocate registers for the array of values from the sub-query, the + ** samve values in record form, and the rowid used to insert said record + ** into the ephemeral table. */ + regNew = pParse->nMem+1; + pParse->nMem += nInput; + regRecord = ++pParse->nMem; + regRowid = ++pParse->nMem; + + /* If the window frame contains an " PRECEDING" or " FOLLOWING" + ** clause, allocate registers to store the results of evaluating each + ** . */ + if( pMWin->eStart==TK_PRECEDING || pMWin->eStart==TK_FOLLOWING ){ + regStart = ++pParse->nMem; + } + if( pMWin->eEnd==TK_PRECEDING || pMWin->eEnd==TK_FOLLOWING ){ + regEnd = ++pParse->nMem; + } + + /* If this is not a "ROWS BETWEEN ..." frame, then allocate arrays of + ** registers to store copies of the ORDER BY expressions (peer values) + ** for the main loop, and for each cursor (start, current and end). */ + if( pMWin->eFrmType!=TK_ROWS ){ + int nPeer = (pOrderBy ? pOrderBy->nExpr : 0); + regNewPeer = regNew + pMWin->nBufferCol; + if( pMWin->pPartition ) regNewPeer += pMWin->pPartition->nExpr; + regPeer = pParse->nMem+1; pParse->nMem += nPeer; + s.start.reg = pParse->nMem+1; pParse->nMem += nPeer; + s.current.reg = pParse->nMem+1; pParse->nMem += nPeer; + s.end.reg = pParse->nMem+1; pParse->nMem += nPeer; + } + + /* Load the column values for the row returned by the sub-select + ** into an array of registers starting at regNew. Assemble them into + ** a record in register regRecord. */ + for(iInput=0; iInputpPartition ){ + int addr; + ExprList *pPart = pMWin->pPartition; + int nPart = pPart->nExpr; + int regNewPart = regNew + pMWin->nBufferCol; + KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pPart, 0, 0); + + regFlushPart = ++pParse->nMem; + addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPart, pMWin->regPart, nPart); + sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO); + sqlite3VdbeAddOp3(v, OP_Jump, addr+2, addr+4, addr+2); + VdbeCoverageEqNe(v); + addrGosubFlush = sqlite3VdbeAddOp1(v, OP_Gosub, regFlushPart); + VdbeComment((v, "call flush_partition")); + sqlite3VdbeAddOp3(v, OP_Copy, regNewPart, pMWin->regPart, nPart-1); + } + + /* Insert the new row into the ephemeral table */ + sqlite3VdbeAddOp2(v, OP_NewRowid, csrWrite, regRowid); + sqlite3VdbeAddOp3(v, OP_Insert, csrWrite, regRecord, regRowid); + addrNe = sqlite3VdbeAddOp3(v, OP_Ne, pMWin->regOne, 0, regRowid); + VdbeCoverageNeverNull(v); + + /* This block is run for the first row of each partition */ + s.regArg = windowInitAccum(pParse, pMWin); + + if( regStart ){ + sqlite3ExprCode(pParse, pMWin->pStart, regStart); + windowCheckValue(pParse, regStart, 0 + (pMWin->eFrmType==TK_RANGE ? 3 : 0)); + } + if( regEnd ){ + sqlite3ExprCode(pParse, pMWin->pEnd, regEnd); + windowCheckValue(pParse, regEnd, 1 + (pMWin->eFrmType==TK_RANGE ? 3 : 0)); + } + + if( pMWin->eStart==pMWin->eEnd && regStart ){ + int op = ((pMWin->eStart==TK_FOLLOWING) ? OP_Ge : OP_Le); + int addrGe = sqlite3VdbeAddOp3(v, op, regStart, 0, regEnd); + VdbeCoverageNeverNullIf(v, op==OP_Ge); /* NeverNull because bound */ + VdbeCoverageNeverNullIf(v, op==OP_Le); /* values previously checked */ + windowAggFinal(&s, 0); + sqlite3VdbeAddOp2(v, OP_Rewind, s.current.csr, 1); + VdbeCoverageNeverTaken(v); + windowReturnOneRow(&s); + sqlite3VdbeAddOp1(v, OP_ResetSorter, s.current.csr); + sqlite3VdbeAddOp2(v, OP_Goto, 0, lblWhereEnd); + sqlite3VdbeJumpHere(v, addrGe); + } + if( pMWin->eStart==TK_FOLLOWING && pMWin->eFrmType!=TK_RANGE && regEnd ){ + assert( pMWin->eEnd==TK_FOLLOWING ); + sqlite3VdbeAddOp3(v, OP_Subtract, regStart, regEnd, regStart); + } + + if( pMWin->eStart!=TK_UNBOUNDED ){ + sqlite3VdbeAddOp2(v, OP_Rewind, s.start.csr, 1); + VdbeCoverageNeverTaken(v); + } + sqlite3VdbeAddOp2(v, OP_Rewind, s.current.csr, 1); + VdbeCoverageNeverTaken(v); + sqlite3VdbeAddOp2(v, OP_Rewind, s.end.csr, 1); + VdbeCoverageNeverTaken(v); + if( regPeer && pOrderBy ){ + sqlite3VdbeAddOp3(v, OP_Copy, regNewPeer, regPeer, pOrderBy->nExpr-1); + sqlite3VdbeAddOp3(v, OP_Copy, regPeer, s.start.reg, pOrderBy->nExpr-1); + sqlite3VdbeAddOp3(v, OP_Copy, regPeer, s.current.reg, pOrderBy->nExpr-1); + sqlite3VdbeAddOp3(v, OP_Copy, regPeer, s.end.reg, pOrderBy->nExpr-1); + } + + sqlite3VdbeAddOp2(v, OP_Goto, 0, lblWhereEnd); + + sqlite3VdbeJumpHere(v, addrNe); + + /* Beginning of the block executed for the second and subsequent rows. */ + if( regPeer ){ + windowIfNewPeer(pParse, pOrderBy, regNewPeer, regPeer, lblWhereEnd); + } + if( pMWin->eStart==TK_FOLLOWING ){ + windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0); + if( pMWin->eEnd!=TK_UNBOUNDED ){ + if( pMWin->eFrmType==TK_RANGE ){ + int lbl = sqlite3VdbeMakeLabel(pParse); + int addrNext = sqlite3VdbeCurrentAddr(v); + windowCodeRangeTest(&s, OP_Ge, s.current.csr, regEnd, s.end.csr, lbl); + windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0); + windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0); + sqlite3VdbeAddOp2(v, OP_Goto, 0, addrNext); + sqlite3VdbeResolveLabel(v, lbl); + }else{ + windowCodeOp(&s, WINDOW_RETURN_ROW, regEnd, 0); + windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0); + } + } + }else + if( pMWin->eEnd==TK_PRECEDING ){ + int bRPS = (pMWin->eStart==TK_PRECEDING && pMWin->eFrmType==TK_RANGE); + windowCodeOp(&s, WINDOW_AGGSTEP, regEnd, 0); + if( bRPS ) windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0); + windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0); + if( !bRPS ) windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0); + }else{ + int addr = 0; + windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0); + if( pMWin->eEnd!=TK_UNBOUNDED ){ + if( pMWin->eFrmType==TK_RANGE ){ + int lbl = 0; + addr = sqlite3VdbeCurrentAddr(v); + if( regEnd ){ + lbl = sqlite3VdbeMakeLabel(pParse); + windowCodeRangeTest(&s, OP_Ge, s.current.csr, regEnd, s.end.csr, lbl); + } + windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0); + windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0); + if( regEnd ){ + sqlite3VdbeAddOp2(v, OP_Goto, 0, addr); + sqlite3VdbeResolveLabel(v, lbl); + } + }else{ + if( regEnd ){ + addr = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0, 1); + VdbeCoverage(v); } + windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0); + windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0); + if( regEnd ) sqlite3VdbeJumpHere(v, addr); } } + } - /* Otherwise, call windowCodeDefaultStep(). */ - if( bCache ){ - VdbeModuleComment((pParse->pVdbe, "Begin CacheStep()")); - windowCodeCacheStep(pParse, p, pWInfo, regGosub, addrGosub); - }else{ - VdbeModuleComment((pParse->pVdbe, "Begin DefaultStep()")); - windowCodeDefaultStep(pParse, p, pWInfo, regGosub, addrGosub); + /* End of the main input loop */ + sqlite3VdbeResolveLabel(v, lblWhereEnd); + sqlite3WhereEnd(pWInfo); + + /* Fall through */ + if( pMWin->pPartition ){ + addrInteger = sqlite3VdbeAddOp2(v, OP_Integer, 0, regFlushPart); + sqlite3VdbeJumpHere(v, addrGosubFlush); + } + + addrEmpty = sqlite3VdbeAddOp1(v, OP_Rewind, csrWrite); + VdbeCoverage(v); + if( pMWin->eEnd==TK_PRECEDING ){ + int bRPS = (pMWin->eStart==TK_PRECEDING && pMWin->eFrmType==TK_RANGE); + windowCodeOp(&s, WINDOW_AGGSTEP, regEnd, 0); + if( bRPS ) windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0); + windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0); + }else if( pMWin->eStart==TK_FOLLOWING ){ + int addrStart; + int addrBreak1; + int addrBreak2; + int addrBreak3; + windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0); + if( pMWin->eFrmType==TK_RANGE ){ + addrStart = sqlite3VdbeCurrentAddr(v); + addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 1); + addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 1); + }else + if( pMWin->eEnd==TK_UNBOUNDED ){ + addrStart = sqlite3VdbeCurrentAddr(v); + addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, regStart, 1); + addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, 0, 1); + }else{ + assert( pMWin->eEnd==TK_FOLLOWING ); + addrStart = sqlite3VdbeCurrentAddr(v); + addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, regEnd, 1); + addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 1); + } + sqlite3VdbeAddOp2(v, OP_Goto, 0, addrStart); + sqlite3VdbeJumpHere(v, addrBreak2); + addrStart = sqlite3VdbeCurrentAddr(v); + addrBreak3 = windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 1); + sqlite3VdbeAddOp2(v, OP_Goto, 0, addrStart); + sqlite3VdbeJumpHere(v, addrBreak1); + sqlite3VdbeJumpHere(v, addrBreak3); + }else{ + int addrBreak; + int addrStart; + windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0); + addrStart = sqlite3VdbeCurrentAddr(v); + addrBreak = windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 1); + windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0); + sqlite3VdbeAddOp2(v, OP_Goto, 0, addrStart); + sqlite3VdbeJumpHere(v, addrBreak); + } + sqlite3VdbeJumpHere(v, addrEmpty); + + sqlite3VdbeAddOp1(v, OP_ResetSorter, s.current.csr); + if( pMWin->pPartition ){ + if( pMWin->regStartRowid ){ + sqlite3VdbeAddOp2(v, OP_Integer, 1, pMWin->regStartRowid); + sqlite3VdbeAddOp2(v, OP_Integer, 0, pMWin->regEndRowid); } + sqlite3VdbeChangeP1(v, addrInteger, sqlite3VdbeCurrentAddr(v)); + sqlite3VdbeAddOp1(v, OP_Return, regFlushPart); } } @@ -147500,6 +148298,10 @@ static void disableLookaside(Parse *pParse){ sqlite3ExprListSetName(pParse, p, pIdToken, 1); return p; } + +#if TK_SPAN>255 +# error too many tokens in the grammar +#endif /**************** End of %include directives **********************************/ /* These constants specify the various numeric values for terminal symbols ** in a format understandable to "makeheaders". This section is blank unless @@ -147563,27 +148365,28 @@ static void disableLookaside(Parse *pParse){ #endif /************* Begin control #defines *****************************************/ #define YYCODETYPE unsigned short int -#define YYNOCODE 278 +#define YYNOCODE 301 #define YYACTIONTYPE unsigned short int -#define YYWILDCARD 91 +#define YYWILDCARD 95 #define sqlite3ParserTOKENTYPE Token typedef union { int yyinit; sqlite3ParserTOKENTYPE yy0; - ExprList* yy42; - int yy96; - TriggerStep* yy119; - Window* yy147; - SrcList* yy167; - Upsert* yy266; - struct FrameBound yy317; - IdList* yy336; - struct TrigEvent yy350; - struct {int value; int mask;} yy367; - Select* yy423; - const char* yy464; - Expr* yy490; - With* yy499; + With* yy59; + IdList* yy62; + struct TrigEvent yy90; + Upsert* yy136; + struct FrameBound yy201; + u8 yy238; + const char* yy294; + Window* yy295; + struct {int value; int mask;} yy355; + ExprList* yy434; + TriggerStep* yy455; + Select* yy457; + SrcList* yy483; + int yy494; + Expr* yy524; } YYMINORTYPE; #ifndef YYSTACKDEPTH #define YYSTACKDEPTH 100 @@ -147599,17 +148402,17 @@ typedef union { #define sqlite3ParserCTX_FETCH Parse *pParse=yypParser->pParse; #define sqlite3ParserCTX_STORE yypParser->pParse=pParse; #define YYFALLBACK 1 -#define YYNSTATE 524 -#define YYNRULE 369 -#define YYNTOKEN 155 -#define YY_MAX_SHIFT 523 -#define YY_MIN_SHIFTREDUCE 760 -#define YY_MAX_SHIFTREDUCE 1128 -#define YY_ERROR_ACTION 1129 -#define YY_ACCEPT_ACTION 1130 -#define YY_NO_ACTION 1131 -#define YY_MIN_REDUCE 1132 -#define YY_MAX_REDUCE 1500 +#define YYNSTATE 541 +#define YYNRULE 375 +#define YYNTOKEN 176 +#define YY_MAX_SHIFT 540 +#define YY_MIN_SHIFTREDUCE 784 +#define YY_MAX_SHIFTREDUCE 1158 +#define YY_ERROR_ACTION 1159 +#define YY_ACCEPT_ACTION 1160 +#define YY_NO_ACTION 1161 +#define YY_MIN_REDUCE 1162 +#define YY_MAX_REDUCE 1536 /************* End control #defines *******************************************/ #define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0]))) @@ -147676,569 +148479,603 @@ typedef union { ** yy_default[] Default action for each state. ** *********** Begin parsing tables **********************************************/ -#define YY_ACTTAB_COUNT (2009) +#define YY_ACTTAB_COUNT (2142) static const YYACTIONTYPE yy_action[] = { - /* 0 */ 377, 518, 371, 107, 104, 200, 1293, 518, 1130, 1, - /* 10 */ 1, 523, 2, 1134, 518, 1203, 1203, 1262, 277, 373, - /* 20 */ 129, 495, 37, 37, 1397, 1201, 1201, 1211, 65, 65, - /* 30 */ 480, 891, 107, 104, 200, 37, 37, 1043, 1494, 892, - /* 40 */ 346, 1494, 342, 114, 115, 105, 1106, 1106, 957, 960, - /* 50 */ 950, 950, 112, 112, 113, 113, 113, 113, 285, 254, - /* 60 */ 254, 518, 254, 254, 500, 518, 495, 518, 107, 104, - /* 70 */ 200, 1085, 515, 481, 386, 515, 1464, 442, 501, 230, - /* 80 */ 197, 439, 37, 37, 1172, 210, 65, 65, 65, 65, - /* 90 */ 254, 254, 111, 111, 111, 111, 110, 110, 109, 109, - /* 100 */ 109, 108, 404, 515, 404, 155, 1041, 431, 401, 400, - /* 110 */ 254, 254, 373, 1431, 1427, 408, 1110, 1085, 1086, 1087, - /* 120 */ 284, 1112, 500, 515, 500, 368, 1433, 1421, 1428, 1111, - /* 130 */ 1261, 499, 373, 502, 108, 404, 114, 115, 105, 1106, - /* 140 */ 1106, 957, 960, 950, 950, 112, 112, 113, 113, 113, - /* 150 */ 113, 276, 509, 1113, 369, 1113, 114, 115, 105, 1106, - /* 160 */ 1106, 957, 960, 950, 950, 112, 112, 113, 113, 113, - /* 170 */ 113, 496, 1420, 1431, 493, 1468, 1065, 260, 1063, 433, - /* 180 */ 74, 107, 104, 200, 498, 111, 111, 111, 111, 110, - /* 190 */ 110, 109, 109, 109, 108, 404, 373, 113, 113, 113, - /* 200 */ 113, 106, 131, 91, 1361, 111, 111, 111, 111, 110, - /* 210 */ 110, 109, 109, 109, 108, 404, 113, 113, 113, 113, - /* 220 */ 114, 115, 105, 1106, 1106, 957, 960, 950, 950, 112, - /* 230 */ 112, 113, 113, 113, 113, 111, 111, 111, 111, 110, - /* 240 */ 110, 109, 109, 109, 108, 404, 116, 110, 110, 109, - /* 250 */ 109, 109, 108, 404, 111, 111, 111, 111, 110, 110, - /* 260 */ 109, 109, 109, 108, 404, 917, 512, 512, 512, 111, - /* 270 */ 111, 111, 111, 110, 110, 109, 109, 109, 108, 404, - /* 280 */ 517, 1198, 1177, 181, 109, 109, 109, 108, 404, 373, - /* 290 */ 1198, 402, 402, 402, 75, 360, 111, 111, 111, 111, - /* 300 */ 110, 110, 109, 109, 109, 108, 404, 382, 299, 419, - /* 310 */ 287, 170, 518, 114, 115, 105, 1106, 1106, 957, 960, - /* 320 */ 950, 950, 112, 112, 113, 113, 113, 113, 1444, 523, - /* 330 */ 2, 1134, 518, 13, 13, 337, 277, 1085, 129, 226, - /* 340 */ 937, 1058, 1000, 471, 917, 1211, 453, 384, 1085, 395, - /* 350 */ 162, 1057, 155, 45, 45, 416, 928, 401, 400, 479, - /* 360 */ 927, 12, 111, 111, 111, 111, 110, 110, 109, 109, - /* 370 */ 109, 108, 404, 226, 286, 254, 254, 254, 254, 518, - /* 380 */ 16, 16, 373, 1085, 1086, 1087, 314, 299, 515, 472, - /* 390 */ 515, 927, 927, 929, 1085, 1086, 1087, 378, 276, 509, - /* 400 */ 65, 65, 1113, 210, 1113, 1085, 114, 115, 105, 1106, - /* 410 */ 1106, 957, 960, 950, 950, 112, 112, 113, 113, 113, - /* 420 */ 113, 1448, 222, 1134, 1089, 461, 458, 457, 277, 180, - /* 430 */ 129, 378, 392, 408, 423, 456, 500, 1211, 240, 257, - /* 440 */ 324, 464, 319, 463, 227, 470, 12, 317, 424, 300, - /* 450 */ 317, 1085, 1086, 1087, 485, 111, 111, 111, 111, 110, - /* 460 */ 110, 109, 109, 109, 108, 404, 181, 118, 1085, 254, - /* 470 */ 254, 1089, 518, 90, 351, 373, 518, 1181, 365, 798, - /* 480 */ 1440, 339, 515, 248, 248, 77, 325, 133, 1085, 249, - /* 490 */ 424, 300, 794, 49, 49, 210, 515, 65, 65, 114, - /* 500 */ 115, 105, 1106, 1106, 957, 960, 950, 950, 112, 112, - /* 510 */ 113, 113, 113, 113, 1085, 1086, 1087, 222, 1085, 438, - /* 520 */ 461, 458, 457, 937, 787, 408, 171, 857, 362, 1021, - /* 530 */ 456, 136, 198, 486, 1085, 1086, 1087, 448, 794, 928, - /* 540 */ 5, 193, 192, 927, 1022, 107, 104, 200, 111, 111, - /* 550 */ 111, 111, 110, 110, 109, 109, 109, 108, 404, 1023, - /* 560 */ 254, 254, 803, 1085, 1085, 1086, 1087, 437, 373, 1085, - /* 570 */ 344, 787, 791, 515, 927, 927, 929, 1085, 1408, 1396, - /* 580 */ 832, 1085, 176, 3, 852, 1085, 518, 1439, 429, 851, - /* 590 */ 833, 518, 114, 115, 105, 1106, 1106, 957, 960, 950, - /* 600 */ 950, 112, 112, 113, 113, 113, 113, 13, 13, 1085, - /* 610 */ 1086, 1087, 13, 13, 518, 1085, 1086, 1087, 1496, 358, - /* 620 */ 1085, 389, 1234, 1085, 1086, 1087, 391, 1085, 1086, 1087, - /* 630 */ 448, 1085, 1086, 1087, 518, 65, 65, 947, 947, 958, - /* 640 */ 961, 111, 111, 111, 111, 110, 110, 109, 109, 109, - /* 650 */ 108, 404, 518, 382, 878, 13, 13, 518, 877, 518, - /* 660 */ 263, 373, 518, 431, 448, 1070, 1085, 1086, 1087, 267, - /* 670 */ 448, 488, 1360, 64, 64, 431, 812, 155, 50, 50, - /* 680 */ 65, 65, 518, 65, 65, 114, 115, 105, 1106, 1106, - /* 690 */ 957, 960, 950, 950, 112, 112, 113, 113, 113, 113, - /* 700 */ 518, 951, 382, 13, 13, 415, 411, 462, 414, 1085, - /* 710 */ 1366, 777, 1210, 292, 297, 813, 399, 497, 181, 403, - /* 720 */ 261, 15, 15, 276, 509, 414, 413, 1366, 1368, 410, - /* 730 */ 372, 345, 1209, 264, 111, 111, 111, 111, 110, 110, - /* 740 */ 109, 109, 109, 108, 404, 265, 254, 254, 229, 1405, - /* 750 */ 268, 1215, 268, 1103, 373, 1085, 1086, 1087, 938, 515, - /* 760 */ 393, 409, 876, 515, 254, 254, 1152, 482, 473, 262, - /* 770 */ 422, 476, 325, 503, 289, 518, 291, 515, 114, 115, - /* 780 */ 105, 1106, 1106, 957, 960, 950, 950, 112, 112, 113, - /* 790 */ 113, 113, 113, 414, 1021, 1366, 39, 39, 254, 254, - /* 800 */ 254, 254, 980, 254, 254, 254, 254, 255, 255, 1022, - /* 810 */ 279, 515, 516, 515, 846, 846, 515, 138, 515, 518, - /* 820 */ 515, 1043, 1495, 251, 1023, 1495, 876, 111, 111, 111, - /* 830 */ 111, 110, 110, 109, 109, 109, 108, 404, 518, 1353, - /* 840 */ 51, 51, 518, 199, 518, 506, 290, 373, 518, 276, - /* 850 */ 509, 922, 9, 483, 233, 1005, 1005, 445, 189, 52, - /* 860 */ 52, 325, 280, 53, 53, 54, 54, 373, 876, 55, - /* 870 */ 55, 114, 115, 105, 1106, 1106, 957, 960, 950, 950, - /* 880 */ 112, 112, 113, 113, 113, 113, 97, 518, 95, 1104, - /* 890 */ 1041, 114, 115, 105, 1106, 1106, 957, 960, 950, 950, - /* 900 */ 112, 112, 113, 113, 113, 113, 135, 199, 56, 56, - /* 910 */ 765, 766, 767, 225, 224, 223, 518, 283, 437, 233, - /* 920 */ 111, 111, 111, 111, 110, 110, 109, 109, 109, 108, - /* 930 */ 404, 1002, 876, 326, 518, 1002, 1104, 40, 40, 518, - /* 940 */ 111, 111, 111, 111, 110, 110, 109, 109, 109, 108, - /* 950 */ 404, 518, 448, 518, 1104, 41, 41, 518, 17, 518, - /* 960 */ 43, 43, 1155, 379, 518, 448, 518, 443, 518, 390, - /* 970 */ 518, 194, 44, 44, 57, 57, 1247, 518, 58, 58, - /* 980 */ 59, 59, 518, 466, 326, 14, 14, 60, 60, 120, - /* 990 */ 120, 61, 61, 449, 1206, 93, 518, 425, 46, 46, - /* 1000 */ 518, 1104, 518, 62, 62, 518, 437, 305, 518, 852, - /* 1010 */ 518, 298, 518, 1246, 851, 373, 518, 63, 63, 1293, - /* 1020 */ 397, 47, 47, 142, 142, 1467, 143, 143, 821, 70, - /* 1030 */ 70, 48, 48, 66, 66, 373, 518, 121, 121, 114, - /* 1040 */ 115, 105, 1106, 1106, 957, 960, 950, 950, 112, 112, - /* 1050 */ 113, 113, 113, 113, 518, 418, 518, 67, 67, 114, - /* 1060 */ 115, 105, 1106, 1106, 957, 960, 950, 950, 112, 112, - /* 1070 */ 113, 113, 113, 113, 312, 122, 122, 123, 123, 1293, - /* 1080 */ 518, 357, 1126, 88, 518, 435, 325, 387, 111, 111, - /* 1090 */ 111, 111, 110, 110, 109, 109, 109, 108, 404, 266, - /* 1100 */ 518, 119, 119, 518, 1293, 141, 141, 518, 111, 111, - /* 1110 */ 111, 111, 110, 110, 109, 109, 109, 108, 404, 518, - /* 1120 */ 801, 140, 140, 518, 127, 127, 511, 379, 126, 126, - /* 1130 */ 518, 137, 518, 1308, 518, 307, 518, 310, 518, 203, - /* 1140 */ 124, 124, 1307, 96, 125, 125, 207, 388, 1441, 468, - /* 1150 */ 1127, 69, 69, 71, 71, 68, 68, 38, 38, 42, - /* 1160 */ 42, 357, 1042, 373, 1293, 276, 509, 801, 185, 469, - /* 1170 */ 494, 436, 444, 6, 380, 156, 253, 197, 469, 134, - /* 1180 */ 426, 33, 1038, 373, 1121, 359, 1411, 114, 115, 105, - /* 1190 */ 1106, 1106, 957, 960, 950, 950, 112, 112, 113, 113, - /* 1200 */ 113, 113, 914, 296, 27, 293, 90, 114, 103, 105, - /* 1210 */ 1106, 1106, 957, 960, 950, 950, 112, 112, 113, 113, - /* 1220 */ 113, 113, 919, 275, 430, 232, 891, 232, 432, 256, - /* 1230 */ 1127, 232, 398, 370, 892, 28, 111, 111, 111, 111, - /* 1240 */ 110, 110, 109, 109, 109, 108, 404, 301, 454, 1385, - /* 1250 */ 90, 228, 209, 987, 811, 810, 111, 111, 111, 111, - /* 1260 */ 110, 110, 109, 109, 109, 108, 404, 315, 818, 819, - /* 1270 */ 90, 323, 983, 931, 885, 228, 373, 232, 999, 849, - /* 1280 */ 999, 322, 102, 998, 1384, 998, 785, 850, 440, 132, - /* 1290 */ 102, 302, 1243, 306, 309, 311, 373, 313, 1194, 1180, - /* 1300 */ 987, 115, 105, 1106, 1106, 957, 960, 950, 950, 112, - /* 1310 */ 112, 113, 113, 113, 113, 1178, 1179, 318, 327, 328, - /* 1320 */ 931, 1255, 105, 1106, 1106, 957, 960, 950, 950, 112, - /* 1330 */ 112, 113, 113, 113, 113, 1292, 1230, 1457, 273, 1241, - /* 1340 */ 504, 505, 1298, 100, 510, 246, 4, 1161, 1154, 111, - /* 1350 */ 111, 111, 111, 110, 110, 109, 109, 109, 108, 404, - /* 1360 */ 513, 1143, 187, 1142, 202, 1144, 1451, 356, 1227, 111, - /* 1370 */ 111, 111, 111, 110, 110, 109, 109, 109, 108, 404, - /* 1380 */ 11, 1277, 330, 405, 332, 334, 191, 1285, 364, 195, - /* 1390 */ 295, 417, 288, 100, 510, 507, 4, 434, 459, 321, - /* 1400 */ 1177, 349, 1357, 1356, 336, 155, 190, 1454, 1121, 158, - /* 1410 */ 513, 508, 235, 1404, 937, 1402, 1118, 381, 77, 428, - /* 1420 */ 98, 98, 8, 1282, 168, 30, 152, 99, 160, 405, - /* 1430 */ 520, 519, 88, 405, 927, 1362, 1274, 420, 163, 73, - /* 1440 */ 164, 76, 165, 166, 421, 507, 452, 212, 361, 363, - /* 1450 */ 427, 276, 509, 31, 1288, 172, 491, 441, 216, 1351, - /* 1460 */ 82, 490, 447, 1373, 937, 927, 927, 929, 930, 24, - /* 1470 */ 98, 98, 304, 247, 218, 177, 308, 99, 219, 405, - /* 1480 */ 520, 519, 450, 1145, 927, 220, 366, 1197, 100, 510, - /* 1490 */ 465, 4, 1188, 1196, 1195, 394, 803, 1169, 1187, 367, - /* 1500 */ 1168, 396, 484, 320, 1167, 513, 1466, 87, 475, 100, - /* 1510 */ 510, 271, 4, 272, 478, 927, 927, 929, 930, 24, - /* 1520 */ 1443, 1074, 407, 1238, 1239, 258, 513, 329, 405, 331, - /* 1530 */ 355, 355, 354, 243, 352, 234, 489, 774, 498, 184, - /* 1540 */ 507, 338, 1422, 339, 117, 1220, 10, 341, 333, 405, - /* 1550 */ 204, 491, 282, 1219, 1237, 1236, 492, 335, 343, 937, - /* 1560 */ 281, 507, 94, 1337, 186, 98, 98, 347, 89, 487, - /* 1570 */ 348, 241, 99, 29, 405, 520, 519, 274, 1151, 927, - /* 1580 */ 937, 521, 1080, 245, 242, 244, 98, 98, 856, 522, - /* 1590 */ 206, 1140, 1135, 99, 144, 405, 520, 519, 147, 375, - /* 1600 */ 927, 149, 376, 157, 1389, 1390, 1388, 1387, 205, 145, - /* 1610 */ 927, 927, 929, 930, 24, 146, 130, 761, 1165, 1164, - /* 1620 */ 72, 100, 510, 1162, 4, 269, 406, 188, 278, 201, - /* 1630 */ 259, 927, 927, 929, 930, 24, 128, 911, 513, 997, - /* 1640 */ 995, 159, 374, 208, 148, 161, 835, 276, 509, 211, - /* 1650 */ 294, 1011, 915, 167, 150, 383, 169, 78, 385, 79, - /* 1660 */ 80, 405, 81, 151, 1014, 213, 214, 1010, 139, 18, - /* 1670 */ 412, 215, 303, 507, 232, 1115, 1003, 446, 173, 217, - /* 1680 */ 174, 32, 776, 451, 491, 322, 221, 175, 814, 490, - /* 1690 */ 83, 455, 937, 19, 460, 316, 20, 84, 98, 98, - /* 1700 */ 270, 182, 85, 467, 153, 99, 154, 405, 520, 519, - /* 1710 */ 1074, 407, 927, 183, 258, 963, 1046, 86, 34, 355, - /* 1720 */ 355, 354, 243, 352, 474, 1047, 774, 35, 477, 196, - /* 1730 */ 250, 100, 510, 252, 4, 884, 178, 231, 1060, 204, - /* 1740 */ 21, 282, 102, 927, 927, 929, 930, 24, 513, 281, - /* 1750 */ 879, 22, 1064, 1062, 1051, 7, 340, 23, 978, 179, - /* 1760 */ 90, 92, 510, 964, 4, 236, 962, 966, 1020, 1019, - /* 1770 */ 237, 405, 967, 25, 36, 514, 932, 786, 513, 206, - /* 1780 */ 101, 26, 845, 507, 238, 239, 1459, 147, 350, 1458, - /* 1790 */ 149, 353, 1075, 1131, 1131, 1131, 1131, 205, 1131, 1131, - /* 1800 */ 1131, 405, 937, 1131, 1131, 1131, 1131, 1131, 98, 98, - /* 1810 */ 1131, 1131, 1131, 507, 1131, 99, 1131, 405, 520, 519, - /* 1820 */ 1131, 1131, 927, 1131, 1131, 1131, 1131, 1131, 1131, 1131, - /* 1830 */ 1131, 374, 937, 1131, 1131, 1131, 276, 509, 98, 98, - /* 1840 */ 1131, 1131, 1131, 1131, 1131, 99, 1131, 405, 520, 519, - /* 1850 */ 1131, 1131, 927, 927, 927, 929, 930, 24, 1131, 412, - /* 1860 */ 1131, 1131, 1131, 258, 1131, 1131, 1131, 1131, 355, 355, - /* 1870 */ 354, 243, 352, 1131, 1131, 774, 1131, 1131, 1131, 1131, - /* 1880 */ 1131, 1131, 1131, 927, 927, 929, 930, 24, 204, 1131, - /* 1890 */ 282, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 281, 1131, - /* 1900 */ 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, - /* 1910 */ 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, - /* 1920 */ 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 206, 1131, - /* 1930 */ 1131, 1131, 1131, 1131, 1131, 1131, 147, 1131, 1131, 149, - /* 1940 */ 1131, 1131, 1131, 1131, 1131, 1131, 205, 1131, 1131, 1131, - /* 1950 */ 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, - /* 1960 */ 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, - /* 1970 */ 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, - /* 1980 */ 374, 1131, 1131, 1131, 1131, 276, 509, 1131, 1131, 1131, - /* 1990 */ 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, - /* 2000 */ 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 412, + /* 0 */ 535, 1323, 112, 109, 209, 112, 109, 209, 1160, 1, + /* 10 */ 1, 540, 2, 1164, 535, 1292, 1228, 1207, 289, 384, + /* 20 */ 134, 42, 42, 1427, 382, 1228, 9, 1241, 242, 492, + /* 30 */ 1291, 915, 373, 379, 1026, 70, 70, 427, 1026, 916, + /* 40 */ 529, 529, 529, 119, 120, 110, 1136, 1136, 981, 984, + /* 50 */ 974, 974, 117, 117, 118, 118, 118, 118, 380, 264, + /* 60 */ 264, 264, 264, 1134, 264, 264, 112, 109, 209, 397, + /* 70 */ 454, 517, 532, 491, 532, 1233, 1233, 532, 239, 206, + /* 80 */ 493, 112, 109, 209, 464, 219, 118, 118, 118, 118, + /* 90 */ 111, 393, 440, 444, 16, 16, 116, 116, 116, 116, + /* 100 */ 115, 115, 114, 114, 114, 113, 415, 971, 971, 982, + /* 110 */ 985, 235, 1463, 351, 1134, 419, 384, 116, 116, 116, + /* 120 */ 116, 115, 115, 114, 114, 114, 113, 415, 116, 116, + /* 130 */ 116, 116, 115, 115, 114, 114, 114, 113, 415, 961, + /* 140 */ 119, 120, 110, 1136, 1136, 981, 984, 974, 974, 117, + /* 150 */ 117, 118, 118, 118, 118, 952, 415, 941, 298, 951, + /* 160 */ 941, 1480, 540, 2, 1164, 1115, 535, 1458, 160, 289, + /* 170 */ 6, 134, 1504, 389, 406, 975, 338, 1024, 1241, 337, + /* 180 */ 1089, 1476, 1089, 118, 118, 118, 118, 42, 42, 329, + /* 190 */ 951, 951, 953, 116, 116, 116, 116, 115, 115, 114, + /* 200 */ 114, 114, 113, 415, 311, 430, 299, 311, 881, 160, + /* 210 */ 264, 264, 401, 384, 324, 1115, 1116, 1117, 288, 526, + /* 220 */ 96, 159, 1441, 532, 141, 116, 116, 116, 116, 115, + /* 230 */ 115, 114, 114, 114, 113, 415, 219, 119, 120, 110, + /* 240 */ 1136, 1136, 981, 984, 974, 974, 117, 117, 118, 118, + /* 250 */ 118, 118, 115, 115, 114, 114, 114, 113, 415, 288, + /* 260 */ 526, 403, 533, 121, 870, 870, 419, 250, 267, 336, + /* 270 */ 475, 331, 474, 236, 160, 319, 1084, 322, 1465, 329, + /* 280 */ 350, 12, 535, 384, 502, 1115, 1084, 435, 312, 1084, + /* 290 */ 116, 116, 116, 116, 115, 115, 114, 114, 114, 113, + /* 300 */ 415, 535, 836, 42, 42, 138, 426, 119, 120, 110, + /* 310 */ 1136, 1136, 981, 984, 974, 974, 117, 117, 118, 118, + /* 320 */ 118, 118, 70, 70, 288, 526, 412, 411, 480, 1457, + /* 330 */ 335, 79, 6, 473, 1140, 1115, 1116, 1117, 501, 1142, + /* 340 */ 334, 837, 811, 1484, 512, 1164, 534, 1141, 123, 187, + /* 350 */ 289, 384, 134, 448, 434, 1115, 80, 349, 498, 1241, + /* 360 */ 116, 116, 116, 116, 115, 115, 114, 114, 114, 113, + /* 370 */ 415, 1143, 1115, 1143, 459, 119, 120, 110, 1136, 1136, + /* 380 */ 981, 984, 974, 974, 117, 117, 118, 118, 118, 118, + /* 390 */ 404, 264, 264, 811, 1463, 506, 368, 1156, 535, 114, + /* 400 */ 114, 114, 113, 415, 532, 1115, 1116, 1117, 231, 518, + /* 410 */ 1500, 472, 469, 468, 175, 497, 422, 219, 1202, 70, + /* 420 */ 70, 467, 1115, 1116, 1117, 176, 201, 200, 116, 116, + /* 430 */ 116, 116, 115, 115, 114, 114, 114, 113, 415, 535, + /* 440 */ 1115, 264, 264, 435, 312, 1115, 273, 419, 384, 513, + /* 450 */ 1450, 1115, 326, 1084, 532, 517, 82, 1084, 167, 388, + /* 460 */ 69, 69, 1115, 1084, 519, 509, 1084, 1084, 12, 1157, + /* 470 */ 1084, 420, 119, 120, 110, 1136, 1136, 981, 984, 974, + /* 480 */ 974, 117, 117, 118, 118, 118, 118, 258, 258, 535, + /* 490 */ 1115, 1116, 1117, 1045, 535, 1115, 1116, 1117, 1323, 535, + /* 500 */ 532, 1115, 1116, 1117, 296, 483, 1211, 818, 1046, 448, + /* 510 */ 70, 70, 1115, 1116, 1117, 50, 50, 448, 356, 500, + /* 520 */ 70, 70, 207, 1047, 32, 116, 116, 116, 116, 115, + /* 530 */ 115, 114, 114, 114, 113, 415, 453, 264, 264, 1115, + /* 540 */ 450, 449, 961, 508, 856, 384, 517, 5, 900, 822, + /* 550 */ 532, 484, 181, 1115, 857, 516, 517, 818, 952, 507, + /* 560 */ 3, 1115, 951, 1231, 1231, 482, 398, 1115, 1095, 119, + /* 570 */ 120, 110, 1136, 1136, 981, 984, 974, 974, 117, 117, + /* 580 */ 118, 118, 118, 118, 1115, 535, 238, 1115, 1391, 1115, + /* 590 */ 1116, 1117, 159, 951, 951, 953, 231, 1115, 259, 472, + /* 600 */ 469, 468, 310, 1115, 1116, 1117, 13, 13, 297, 467, + /* 610 */ 276, 1115, 1116, 1117, 412, 411, 1095, 1115, 1116, 1117, + /* 620 */ 395, 355, 116, 116, 116, 116, 115, 115, 114, 114, + /* 630 */ 114, 113, 415, 208, 1115, 1116, 1117, 1115, 1116, 1117, + /* 640 */ 264, 264, 384, 337, 902, 393, 815, 1115, 1116, 1117, + /* 650 */ 413, 413, 413, 532, 112, 109, 209, 309, 900, 1143, + /* 660 */ 535, 1143, 535, 393, 901, 1210, 119, 120, 110, 1136, + /* 670 */ 1136, 981, 984, 974, 974, 117, 117, 118, 118, 118, + /* 680 */ 118, 13, 13, 13, 13, 265, 265, 535, 143, 264, + /* 690 */ 264, 288, 526, 535, 1119, 400, 535, 402, 532, 510, + /* 700 */ 1457, 512, 532, 6, 113, 415, 1067, 1530, 70, 70, + /* 710 */ 1530, 535, 271, 535, 70, 70, 535, 13, 13, 116, + /* 720 */ 116, 116, 116, 115, 115, 114, 114, 114, 113, 415, + /* 730 */ 272, 277, 13, 13, 13, 13, 535, 13, 13, 384, + /* 740 */ 535, 304, 425, 1100, 284, 1119, 184, 801, 185, 338, + /* 750 */ 285, 514, 1532, 369, 1239, 1438, 1182, 70, 70, 425, + /* 760 */ 424, 70, 70, 119, 120, 110, 1136, 1136, 981, 984, + /* 770 */ 974, 974, 117, 117, 118, 118, 118, 118, 190, 1065, + /* 780 */ 1067, 1531, 442, 107, 1531, 408, 264, 264, 264, 264, + /* 790 */ 383, 1396, 261, 410, 95, 900, 485, 414, 421, 532, + /* 800 */ 1045, 532, 301, 1133, 303, 488, 433, 1451, 1396, 1398, + /* 810 */ 278, 535, 278, 520, 1435, 1046, 116, 116, 116, 116, + /* 820 */ 115, 115, 114, 114, 114, 113, 415, 425, 264, 264, + /* 830 */ 1047, 190, 54, 54, 535, 291, 384, 264, 264, 362, + /* 840 */ 962, 532, 1004, 376, 1084, 264, 264, 1029, 1029, 456, + /* 850 */ 532, 523, 270, 1065, 1084, 55, 55, 1084, 532, 442, + /* 860 */ 119, 120, 110, 1136, 1136, 981, 984, 974, 974, 117, + /* 870 */ 117, 118, 118, 118, 118, 535, 1396, 190, 302, 1383, + /* 880 */ 208, 535, 789, 790, 791, 535, 515, 535, 1323, 371, + /* 890 */ 337, 234, 233, 232, 459, 515, 15, 15, 459, 477, + /* 900 */ 459, 459, 44, 44, 136, 900, 56, 56, 57, 57, + /* 910 */ 1185, 390, 197, 116, 116, 116, 116, 115, 115, 114, + /* 920 */ 114, 114, 113, 415, 535, 876, 535, 442, 535, 274, + /* 930 */ 875, 1323, 357, 384, 353, 140, 1426, 946, 1455, 1323, + /* 940 */ 1390, 6, 1240, 1236, 292, 58, 58, 59, 59, 60, + /* 950 */ 60, 535, 1456, 384, 535, 6, 399, 119, 120, 110, + /* 960 */ 1136, 1136, 981, 984, 974, 974, 117, 117, 118, 118, + /* 970 */ 118, 118, 61, 61, 535, 45, 45, 119, 120, 110, + /* 980 */ 1136, 1136, 981, 984, 974, 974, 117, 117, 118, 118, + /* 990 */ 118, 118, 1477, 479, 202, 46, 46, 275, 95, 455, + /* 1000 */ 535, 212, 535, 337, 535, 1454, 535, 409, 6, 242, + /* 1010 */ 116, 116, 116, 116, 115, 115, 114, 114, 114, 113, + /* 1020 */ 415, 48, 48, 49, 49, 62, 62, 63, 63, 535, + /* 1030 */ 116, 116, 116, 116, 115, 115, 114, 114, 114, 113, + /* 1040 */ 415, 535, 459, 535, 1134, 535, 1151, 535, 142, 535, + /* 1050 */ 64, 64, 535, 1338, 535, 494, 535, 446, 535, 1264, + /* 1060 */ 535, 1337, 14, 14, 65, 65, 125, 125, 66, 66, + /* 1070 */ 51, 51, 535, 67, 67, 68, 68, 52, 52, 147, + /* 1080 */ 147, 148, 148, 1453, 317, 98, 6, 535, 1245, 481, + /* 1090 */ 535, 827, 535, 75, 75, 1134, 102, 481, 100, 535, + /* 1100 */ 532, 535, 368, 1066, 1503, 384, 535, 845, 53, 53, + /* 1110 */ 93, 71, 71, 126, 126, 295, 528, 390, 288, 526, + /* 1120 */ 72, 72, 127, 127, 139, 384, 38, 128, 128, 119, + /* 1130 */ 120, 110, 1136, 1136, 981, 984, 974, 974, 117, 117, + /* 1140 */ 118, 118, 118, 118, 535, 495, 535, 447, 535, 119, + /* 1150 */ 120, 110, 1136, 1136, 981, 984, 974, 974, 117, 117, + /* 1160 */ 118, 118, 118, 118, 235, 124, 124, 146, 146, 145, + /* 1170 */ 145, 287, 535, 1277, 535, 1157, 535, 391, 161, 263, + /* 1180 */ 206, 381, 116, 116, 116, 116, 115, 115, 114, 114, + /* 1190 */ 114, 113, 415, 132, 132, 131, 131, 129, 129, 535, + /* 1200 */ 30, 535, 116, 116, 116, 116, 115, 115, 114, 114, + /* 1210 */ 114, 113, 415, 535, 216, 1062, 1276, 535, 370, 535, + /* 1220 */ 130, 130, 74, 74, 535, 915, 389, 876, 17, 437, + /* 1230 */ 429, 31, 875, 916, 76, 76, 266, 101, 73, 73, + /* 1240 */ 43, 43, 835, 834, 308, 47, 47, 95, 825, 943, + /* 1250 */ 441, 938, 241, 241, 305, 443, 313, 384, 241, 95, + /* 1260 */ 842, 843, 193, 465, 1209, 327, 237, 436, 95, 1011, + /* 1270 */ 1007, 909, 873, 237, 241, 107, 1023, 384, 1023, 955, + /* 1280 */ 1415, 119, 120, 110, 1136, 1136, 981, 984, 974, 974, + /* 1290 */ 117, 117, 118, 118, 118, 118, 1022, 809, 1022, 825, + /* 1300 */ 137, 119, 108, 110, 1136, 1136, 981, 984, 974, 974, + /* 1310 */ 117, 117, 118, 118, 118, 118, 874, 1414, 451, 107, + /* 1320 */ 1011, 314, 1273, 318, 218, 321, 323, 325, 1224, 1208, + /* 1330 */ 955, 330, 339, 340, 116, 116, 116, 116, 115, 115, + /* 1340 */ 114, 114, 114, 113, 415, 1285, 1322, 1260, 1493, 1470, + /* 1350 */ 1271, 283, 521, 1328, 116, 116, 116, 116, 115, 115, + /* 1360 */ 114, 114, 114, 113, 415, 1191, 1184, 1173, 1172, 1174, + /* 1370 */ 522, 1487, 211, 460, 384, 256, 199, 367, 1257, 342, + /* 1380 */ 195, 470, 307, 344, 11, 333, 525, 445, 1307, 1315, + /* 1390 */ 375, 203, 1207, 1151, 384, 346, 1387, 188, 360, 120, + /* 1400 */ 110, 1136, 1136, 981, 984, 974, 974, 117, 117, 118, + /* 1410 */ 118, 118, 118, 1386, 428, 1490, 245, 300, 348, 1148, + /* 1420 */ 110, 1136, 1136, 981, 984, 974, 974, 117, 117, 118, + /* 1430 */ 118, 118, 118, 189, 198, 1434, 1432, 78, 81, 163, + /* 1440 */ 82, 392, 439, 1392, 173, 105, 527, 35, 4, 157, + /* 1450 */ 1312, 116, 116, 116, 116, 115, 115, 114, 114, 114, + /* 1460 */ 113, 415, 530, 165, 93, 1304, 431, 432, 168, 463, + /* 1470 */ 221, 116, 116, 116, 116, 115, 115, 114, 114, 114, + /* 1480 */ 113, 415, 169, 452, 170, 416, 171, 374, 372, 438, + /* 1490 */ 36, 1318, 177, 225, 1381, 87, 458, 524, 1403, 316, + /* 1500 */ 257, 105, 527, 227, 4, 182, 461, 160, 320, 228, + /* 1510 */ 377, 1175, 476, 229, 1227, 1226, 405, 1225, 530, 1218, + /* 1520 */ 961, 378, 1199, 1198, 827, 332, 103, 103, 1197, 407, + /* 1530 */ 8, 1217, 1502, 104, 487, 416, 537, 536, 281, 282, + /* 1540 */ 951, 416, 490, 1268, 496, 92, 341, 243, 1269, 343, + /* 1550 */ 244, 1267, 122, 524, 345, 1461, 515, 288, 526, 10, + /* 1560 */ 354, 1266, 1460, 352, 504, 1250, 99, 1367, 94, 503, + /* 1570 */ 499, 951, 951, 953, 954, 27, 961, 347, 1249, 194, + /* 1580 */ 251, 358, 103, 103, 359, 1181, 34, 538, 1110, 104, + /* 1590 */ 255, 416, 537, 536, 286, 252, 951, 254, 539, 149, + /* 1600 */ 1170, 1419, 1165, 1420, 1418, 150, 1417, 135, 279, 785, + /* 1610 */ 151, 417, 1195, 196, 290, 210, 386, 1194, 269, 387, + /* 1620 */ 162, 1021, 133, 77, 1192, 1019, 935, 951, 951, 953, + /* 1630 */ 954, 27, 1479, 1104, 418, 164, 153, 268, 217, 166, + /* 1640 */ 859, 306, 366, 366, 365, 253, 363, 220, 1035, 798, + /* 1650 */ 172, 939, 105, 527, 155, 4, 394, 174, 396, 156, + /* 1660 */ 83, 1038, 213, 84, 294, 85, 86, 223, 222, 530, + /* 1670 */ 1034, 144, 293, 18, 224, 315, 241, 1027, 1145, 178, + /* 1680 */ 457, 226, 179, 37, 800, 334, 462, 230, 328, 466, + /* 1690 */ 180, 471, 416, 88, 19, 20, 89, 280, 838, 158, + /* 1700 */ 191, 90, 215, 478, 524, 1097, 204, 192, 987, 91, + /* 1710 */ 152, 1070, 39, 154, 1071, 504, 486, 40, 489, 205, + /* 1720 */ 505, 260, 105, 527, 214, 4, 908, 961, 262, 183, + /* 1730 */ 240, 21, 903, 103, 103, 107, 22, 1086, 23, 530, + /* 1740 */ 104, 1088, 416, 537, 536, 24, 1093, 951, 25, 1074, + /* 1750 */ 1090, 1094, 7, 33, 511, 186, 26, 1002, 385, 95, + /* 1760 */ 988, 986, 416, 288, 526, 990, 1044, 246, 1043, 247, + /* 1770 */ 991, 28, 41, 106, 524, 956, 810, 29, 951, 951, + /* 1780 */ 953, 954, 27, 531, 361, 504, 423, 248, 869, 249, + /* 1790 */ 503, 1495, 364, 1105, 1161, 1494, 1161, 961, 1161, 1161, + /* 1800 */ 1161, 1161, 1161, 103, 103, 1161, 1161, 1161, 1161, 1161, + /* 1810 */ 104, 1161, 416, 537, 536, 1104, 418, 951, 1161, 268, + /* 1820 */ 1161, 1161, 1161, 1161, 366, 366, 365, 253, 363, 1161, + /* 1830 */ 1161, 798, 1161, 1161, 1161, 1161, 105, 527, 1161, 4, + /* 1840 */ 1161, 1161, 1161, 1161, 213, 1161, 294, 1161, 951, 951, + /* 1850 */ 953, 954, 27, 530, 293, 1161, 1161, 1161, 1161, 1161, + /* 1860 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, + /* 1870 */ 1161, 1161, 1161, 1161, 1161, 1161, 416, 1161, 1161, 1161, + /* 1880 */ 1161, 1161, 1161, 1161, 215, 1161, 1161, 1161, 524, 1161, + /* 1890 */ 1161, 1161, 152, 1161, 1161, 154, 105, 527, 1161, 4, + /* 1900 */ 1161, 1161, 1161, 1161, 1161, 1161, 214, 1161, 1161, 1161, + /* 1910 */ 1161, 961, 1161, 530, 1161, 1161, 1161, 103, 103, 880, + /* 1920 */ 1161, 1161, 1161, 1161, 104, 1161, 416, 537, 536, 1161, + /* 1930 */ 1161, 951, 1161, 1161, 1161, 1161, 416, 1161, 1161, 1161, + /* 1940 */ 385, 1161, 1161, 1161, 1161, 288, 526, 1161, 524, 1161, + /* 1950 */ 1161, 1161, 1161, 1161, 1161, 1161, 97, 527, 1161, 4, + /* 1960 */ 1161, 1161, 951, 951, 953, 954, 27, 1161, 423, 1161, + /* 1970 */ 1161, 961, 1161, 530, 1161, 1161, 1161, 103, 103, 1161, + /* 1980 */ 1161, 1161, 1161, 1161, 104, 1161, 416, 537, 536, 1161, + /* 1990 */ 1161, 951, 268, 1161, 1161, 1161, 416, 366, 366, 365, + /* 2000 */ 253, 363, 1161, 1161, 798, 1161, 1161, 1161, 524, 1161, + /* 2010 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 213, 1161, 294, + /* 2020 */ 1161, 1161, 951, 951, 953, 954, 27, 293, 1161, 1161, + /* 2030 */ 1161, 961, 1161, 1161, 1161, 1161, 1161, 103, 103, 1161, + /* 2040 */ 1161, 1161, 1161, 1161, 104, 1161, 416, 537, 536, 1161, + /* 2050 */ 1161, 951, 1161, 1161, 1161, 1161, 1161, 215, 1161, 1161, + /* 2060 */ 1161, 1161, 1161, 1161, 1161, 152, 1161, 1161, 154, 1161, + /* 2070 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 214, + /* 2080 */ 1161, 1161, 951, 951, 953, 954, 27, 1161, 1161, 1161, + /* 2090 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, + /* 2100 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, + /* 2110 */ 1161, 1161, 1161, 385, 1161, 1161, 1161, 1161, 288, 526, + /* 2120 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, + /* 2130 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, + /* 2140 */ 1161, 423, }; static const YYCODETYPE yy_lookahead[] = { - /* 0 */ 168, 163, 184, 238, 239, 240, 163, 163, 155, 156, - /* 10 */ 157, 158, 159, 160, 163, 202, 203, 187, 165, 19, - /* 20 */ 167, 163, 184, 185, 259, 202, 203, 174, 184, 185, - /* 30 */ 174, 31, 238, 239, 240, 184, 185, 22, 23, 39, - /* 40 */ 216, 26, 218, 43, 44, 45, 46, 47, 48, 49, - /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 174, 206, - /* 60 */ 207, 163, 206, 207, 220, 163, 163, 163, 238, 239, - /* 70 */ 240, 59, 219, 229, 231, 219, 183, 245, 174, 223, - /* 80 */ 224, 249, 184, 185, 191, 232, 184, 185, 184, 185, - /* 90 */ 206, 207, 92, 93, 94, 95, 96, 97, 98, 99, - /* 100 */ 100, 101, 102, 219, 102, 81, 91, 163, 96, 97, - /* 110 */ 206, 207, 19, 275, 276, 262, 104, 105, 106, 107, - /* 120 */ 163, 109, 220, 219, 220, 184, 275, 269, 277, 117, - /* 130 */ 187, 229, 19, 229, 101, 102, 43, 44, 45, 46, - /* 140 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 150 */ 57, 127, 128, 141, 184, 143, 43, 44, 45, 46, - /* 160 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 170 */ 57, 268, 269, 275, 276, 197, 83, 233, 85, 163, - /* 180 */ 67, 238, 239, 240, 134, 92, 93, 94, 95, 96, - /* 190 */ 97, 98, 99, 100, 101, 102, 19, 54, 55, 56, - /* 200 */ 57, 58, 152, 26, 247, 92, 93, 94, 95, 96, - /* 210 */ 97, 98, 99, 100, 101, 102, 54, 55, 56, 57, - /* 220 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, - /* 230 */ 53, 54, 55, 56, 57, 92, 93, 94, 95, 96, - /* 240 */ 97, 98, 99, 100, 101, 102, 69, 96, 97, 98, - /* 250 */ 99, 100, 101, 102, 92, 93, 94, 95, 96, 97, - /* 260 */ 98, 99, 100, 101, 102, 73, 179, 180, 181, 92, - /* 270 */ 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, - /* 280 */ 163, 191, 192, 163, 98, 99, 100, 101, 102, 19, - /* 290 */ 200, 179, 180, 181, 24, 175, 92, 93, 94, 95, - /* 300 */ 96, 97, 98, 99, 100, 101, 102, 163, 116, 117, - /* 310 */ 118, 22, 163, 43, 44, 45, 46, 47, 48, 49, - /* 320 */ 50, 51, 52, 53, 54, 55, 56, 57, 157, 158, - /* 330 */ 159, 160, 163, 184, 185, 163, 165, 59, 167, 46, - /* 340 */ 90, 76, 11, 174, 73, 174, 19, 198, 59, 19, - /* 350 */ 72, 86, 81, 184, 185, 234, 106, 96, 97, 163, - /* 360 */ 110, 182, 92, 93, 94, 95, 96, 97, 98, 99, - /* 370 */ 100, 101, 102, 46, 230, 206, 207, 206, 207, 163, - /* 380 */ 184, 185, 19, 105, 106, 107, 23, 116, 219, 220, - /* 390 */ 219, 141, 142, 143, 105, 106, 107, 104, 127, 128, - /* 400 */ 184, 185, 141, 232, 143, 59, 43, 44, 45, 46, - /* 410 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 420 */ 57, 158, 108, 160, 59, 111, 112, 113, 165, 250, - /* 430 */ 167, 104, 102, 262, 255, 121, 220, 174, 108, 109, - /* 440 */ 110, 111, 112, 113, 114, 229, 182, 120, 117, 118, - /* 450 */ 120, 105, 106, 107, 163, 92, 93, 94, 95, 96, - /* 460 */ 97, 98, 99, 100, 101, 102, 163, 22, 59, 206, - /* 470 */ 207, 106, 163, 26, 171, 19, 163, 193, 175, 23, - /* 480 */ 163, 22, 219, 206, 207, 139, 163, 22, 59, 182, - /* 490 */ 117, 118, 59, 184, 185, 232, 219, 184, 185, 43, - /* 500 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, - /* 510 */ 54, 55, 56, 57, 105, 106, 107, 108, 59, 255, - /* 520 */ 111, 112, 113, 90, 59, 262, 22, 98, 174, 12, - /* 530 */ 121, 208, 163, 220, 105, 106, 107, 163, 105, 106, - /* 540 */ 22, 96, 97, 110, 27, 238, 239, 240, 92, 93, - /* 550 */ 94, 95, 96, 97, 98, 99, 100, 101, 102, 42, - /* 560 */ 206, 207, 115, 59, 105, 106, 107, 163, 19, 59, - /* 570 */ 163, 106, 23, 219, 141, 142, 143, 59, 163, 205, - /* 580 */ 63, 59, 72, 22, 124, 59, 163, 270, 234, 129, - /* 590 */ 73, 163, 43, 44, 45, 46, 47, 48, 49, 50, - /* 600 */ 51, 52, 53, 54, 55, 56, 57, 184, 185, 105, - /* 610 */ 106, 107, 184, 185, 163, 105, 106, 107, 265, 266, - /* 620 */ 59, 198, 225, 105, 106, 107, 198, 105, 106, 107, - /* 630 */ 163, 105, 106, 107, 163, 184, 185, 46, 47, 48, - /* 640 */ 49, 92, 93, 94, 95, 96, 97, 98, 99, 100, - /* 650 */ 101, 102, 163, 163, 132, 184, 185, 163, 132, 163, - /* 660 */ 256, 19, 163, 163, 163, 23, 105, 106, 107, 198, - /* 670 */ 163, 220, 205, 184, 185, 163, 35, 81, 184, 185, - /* 680 */ 184, 185, 163, 184, 185, 43, 44, 45, 46, 47, - /* 690 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, - /* 700 */ 163, 110, 163, 184, 185, 109, 205, 66, 163, 59, - /* 710 */ 163, 21, 205, 16, 174, 74, 220, 198, 163, 220, - /* 720 */ 230, 184, 185, 127, 128, 180, 181, 180, 181, 163, - /* 730 */ 175, 242, 174, 233, 92, 93, 94, 95, 96, 97, - /* 740 */ 98, 99, 100, 101, 102, 233, 206, 207, 26, 163, - /* 750 */ 195, 207, 197, 26, 19, 105, 106, 107, 23, 219, - /* 760 */ 119, 260, 26, 219, 206, 207, 174, 19, 174, 230, - /* 770 */ 80, 174, 163, 174, 77, 163, 79, 219, 43, 44, - /* 780 */ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, - /* 790 */ 55, 56, 57, 248, 12, 248, 184, 185, 206, 207, - /* 800 */ 206, 207, 112, 206, 207, 206, 207, 206, 207, 27, - /* 810 */ 163, 219, 123, 219, 125, 126, 219, 208, 219, 163, - /* 820 */ 219, 22, 23, 23, 42, 26, 26, 92, 93, 94, - /* 830 */ 95, 96, 97, 98, 99, 100, 101, 102, 163, 149, - /* 840 */ 184, 185, 163, 107, 163, 63, 149, 19, 163, 127, - /* 850 */ 128, 23, 22, 105, 24, 116, 117, 118, 131, 184, - /* 860 */ 185, 163, 163, 184, 185, 184, 185, 19, 132, 184, - /* 870 */ 185, 43, 44, 45, 46, 47, 48, 49, 50, 51, - /* 880 */ 52, 53, 54, 55, 56, 57, 146, 163, 148, 59, - /* 890 */ 91, 43, 44, 45, 46, 47, 48, 49, 50, 51, - /* 900 */ 52, 53, 54, 55, 56, 57, 208, 107, 184, 185, - /* 910 */ 7, 8, 9, 116, 117, 118, 163, 163, 163, 24, - /* 920 */ 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, - /* 930 */ 102, 29, 132, 163, 163, 33, 106, 184, 185, 163, - /* 940 */ 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, - /* 950 */ 102, 163, 163, 163, 59, 184, 185, 163, 22, 163, - /* 960 */ 184, 185, 177, 178, 163, 163, 163, 65, 163, 199, - /* 970 */ 163, 26, 184, 185, 184, 185, 163, 163, 184, 185, - /* 980 */ 184, 185, 163, 98, 163, 184, 185, 184, 185, 184, - /* 990 */ 185, 184, 185, 252, 205, 147, 163, 61, 184, 185, - /* 1000 */ 163, 106, 163, 184, 185, 163, 163, 205, 163, 124, - /* 1010 */ 163, 256, 163, 163, 129, 19, 163, 184, 185, 163, - /* 1020 */ 199, 184, 185, 184, 185, 23, 184, 185, 26, 184, - /* 1030 */ 185, 184, 185, 184, 185, 19, 163, 184, 185, 43, - /* 1040 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, - /* 1050 */ 54, 55, 56, 57, 163, 163, 163, 184, 185, 43, - /* 1060 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, - /* 1070 */ 54, 55, 56, 57, 16, 184, 185, 184, 185, 163, - /* 1080 */ 163, 22, 23, 138, 163, 19, 163, 231, 92, 93, - /* 1090 */ 94, 95, 96, 97, 98, 99, 100, 101, 102, 256, - /* 1100 */ 163, 184, 185, 163, 163, 184, 185, 163, 92, 93, - /* 1110 */ 94, 95, 96, 97, 98, 99, 100, 101, 102, 163, - /* 1120 */ 59, 184, 185, 163, 184, 185, 177, 178, 184, 185, - /* 1130 */ 163, 208, 163, 237, 163, 77, 163, 79, 163, 15, - /* 1140 */ 184, 185, 237, 147, 184, 185, 24, 231, 153, 154, - /* 1150 */ 91, 184, 185, 184, 185, 184, 185, 184, 185, 184, - /* 1160 */ 185, 22, 23, 19, 163, 127, 128, 106, 24, 273, - /* 1170 */ 271, 105, 231, 274, 263, 264, 223, 224, 273, 22, - /* 1180 */ 118, 24, 23, 19, 60, 26, 163, 43, 44, 45, - /* 1190 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, - /* 1200 */ 56, 57, 140, 23, 22, 163, 26, 43, 44, 45, - /* 1210 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, - /* 1220 */ 56, 57, 23, 211, 23, 26, 31, 26, 23, 22, - /* 1230 */ 91, 26, 231, 221, 39, 53, 92, 93, 94, 95, - /* 1240 */ 96, 97, 98, 99, 100, 101, 102, 23, 23, 163, - /* 1250 */ 26, 26, 130, 59, 109, 110, 92, 93, 94, 95, - /* 1260 */ 96, 97, 98, 99, 100, 101, 102, 23, 7, 8, - /* 1270 */ 26, 110, 23, 59, 23, 26, 19, 26, 141, 23, - /* 1280 */ 143, 120, 26, 141, 163, 143, 23, 23, 163, 26, - /* 1290 */ 26, 163, 163, 163, 163, 163, 19, 163, 163, 193, - /* 1300 */ 106, 44, 45, 46, 47, 48, 49, 50, 51, 52, - /* 1310 */ 53, 54, 55, 56, 57, 163, 193, 163, 163, 163, - /* 1320 */ 106, 163, 45, 46, 47, 48, 49, 50, 51, 52, - /* 1330 */ 53, 54, 55, 56, 57, 163, 163, 130, 222, 163, - /* 1340 */ 163, 203, 163, 19, 20, 251, 22, 163, 163, 92, - /* 1350 */ 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, - /* 1360 */ 36, 163, 209, 163, 261, 163, 163, 161, 222, 92, - /* 1370 */ 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, - /* 1380 */ 210, 213, 222, 59, 222, 222, 182, 213, 213, 196, - /* 1390 */ 257, 226, 226, 19, 20, 71, 22, 257, 188, 187, - /* 1400 */ 192, 212, 187, 187, 226, 81, 210, 166, 60, 261, - /* 1410 */ 36, 244, 130, 170, 90, 170, 38, 170, 139, 104, - /* 1420 */ 96, 97, 48, 236, 22, 235, 43, 103, 201, 105, - /* 1430 */ 106, 107, 138, 59, 110, 247, 213, 18, 204, 258, - /* 1440 */ 204, 258, 204, 204, 170, 71, 18, 169, 213, 236, - /* 1450 */ 213, 127, 128, 235, 201, 201, 82, 170, 169, 213, - /* 1460 */ 146, 87, 62, 254, 90, 141, 142, 143, 144, 145, - /* 1470 */ 96, 97, 253, 170, 169, 22, 170, 103, 169, 105, - /* 1480 */ 106, 107, 189, 170, 110, 169, 189, 186, 19, 20, - /* 1490 */ 104, 22, 194, 186, 186, 64, 115, 186, 194, 189, - /* 1500 */ 188, 102, 133, 186, 186, 36, 186, 104, 189, 19, - /* 1510 */ 20, 246, 22, 246, 189, 141, 142, 143, 144, 145, - /* 1520 */ 0, 1, 2, 228, 228, 5, 36, 227, 59, 227, - /* 1530 */ 10, 11, 12, 13, 14, 170, 84, 17, 134, 216, - /* 1540 */ 71, 272, 270, 22, 137, 217, 22, 216, 227, 59, - /* 1550 */ 30, 82, 32, 217, 228, 228, 87, 227, 170, 90, - /* 1560 */ 40, 71, 146, 241, 215, 96, 97, 214, 136, 135, - /* 1570 */ 213, 25, 103, 26, 105, 106, 107, 243, 173, 110, - /* 1580 */ 90, 172, 13, 6, 164, 164, 96, 97, 98, 162, - /* 1590 */ 70, 162, 162, 103, 176, 105, 106, 107, 78, 267, - /* 1600 */ 110, 81, 267, 264, 182, 182, 182, 182, 88, 176, - /* 1610 */ 141, 142, 143, 144, 145, 176, 190, 4, 182, 182, - /* 1620 */ 182, 19, 20, 182, 22, 190, 3, 22, 151, 15, - /* 1630 */ 89, 141, 142, 143, 144, 145, 16, 128, 36, 23, - /* 1640 */ 23, 139, 122, 24, 119, 131, 20, 127, 128, 133, - /* 1650 */ 16, 1, 140, 131, 119, 61, 139, 53, 37, 53, - /* 1660 */ 53, 59, 53, 119, 105, 34, 130, 1, 5, 22, - /* 1670 */ 150, 104, 149, 71, 26, 75, 68, 41, 68, 130, - /* 1680 */ 104, 24, 20, 19, 82, 120, 114, 22, 28, 87, - /* 1690 */ 22, 67, 90, 22, 67, 23, 22, 22, 96, 97, - /* 1700 */ 67, 23, 138, 22, 37, 103, 153, 105, 106, 107, - /* 1710 */ 1, 2, 110, 23, 5, 23, 23, 26, 22, 10, - /* 1720 */ 11, 12, 13, 14, 24, 23, 17, 22, 24, 130, - /* 1730 */ 23, 19, 20, 23, 22, 105, 22, 34, 85, 30, - /* 1740 */ 34, 32, 26, 141, 142, 143, 144, 145, 36, 40, - /* 1750 */ 132, 34, 75, 83, 23, 44, 24, 34, 23, 26, - /* 1760 */ 26, 19, 20, 23, 22, 26, 23, 23, 23, 23, - /* 1770 */ 22, 59, 11, 22, 22, 26, 23, 23, 36, 70, - /* 1780 */ 22, 22, 124, 71, 130, 130, 130, 78, 23, 130, - /* 1790 */ 81, 15, 1, 278, 278, 278, 278, 88, 278, 278, - /* 1800 */ 278, 59, 90, 278, 278, 278, 278, 278, 96, 97, - /* 1810 */ 278, 278, 278, 71, 278, 103, 278, 105, 106, 107, - /* 1820 */ 278, 278, 110, 278, 278, 278, 278, 278, 278, 278, - /* 1830 */ 278, 122, 90, 278, 278, 278, 127, 128, 96, 97, - /* 1840 */ 278, 278, 278, 278, 278, 103, 278, 105, 106, 107, - /* 1850 */ 278, 278, 110, 141, 142, 143, 144, 145, 278, 150, - /* 1860 */ 278, 278, 278, 5, 278, 278, 278, 278, 10, 11, - /* 1870 */ 12, 13, 14, 278, 278, 17, 278, 278, 278, 278, - /* 1880 */ 278, 278, 278, 141, 142, 143, 144, 145, 30, 278, - /* 1890 */ 32, 278, 278, 278, 278, 278, 278, 278, 40, 278, - /* 1900 */ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, - /* 1910 */ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, - /* 1920 */ 278, 278, 278, 278, 278, 278, 278, 278, 70, 278, - /* 1930 */ 278, 278, 278, 278, 278, 278, 78, 278, 278, 81, - /* 1940 */ 278, 278, 278, 278, 278, 278, 88, 278, 278, 278, - /* 1950 */ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, - /* 1960 */ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, - /* 1970 */ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, - /* 1980 */ 122, 278, 278, 278, 278, 127, 128, 278, 278, 278, - /* 1990 */ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, - /* 2000 */ 278, 278, 278, 278, 278, 278, 278, 278, 150, 278, - /* 2010 */ 278, 278, 278, 278, 278, 278, 278, 278, 278, + /* 0 */ 184, 184, 259, 260, 261, 259, 260, 261, 176, 177, + /* 10 */ 178, 179, 180, 181, 184, 208, 212, 213, 186, 19, + /* 20 */ 188, 205, 206, 280, 205, 221, 22, 195, 24, 195, + /* 30 */ 208, 31, 195, 205, 29, 205, 206, 255, 33, 39, + /* 40 */ 200, 201, 202, 43, 44, 45, 46, 47, 48, 49, + /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 205, 227, + /* 60 */ 228, 227, 228, 59, 227, 228, 259, 260, 261, 252, + /* 70 */ 65, 241, 240, 184, 240, 223, 224, 240, 244, 245, + /* 80 */ 250, 259, 260, 261, 19, 253, 54, 55, 56, 57, + /* 90 */ 58, 184, 255, 184, 205, 206, 96, 97, 98, 99, + /* 100 */ 100, 101, 102, 103, 104, 105, 106, 46, 47, 48, + /* 110 */ 49, 46, 296, 297, 110, 283, 19, 96, 97, 98, + /* 120 */ 99, 100, 101, 102, 103, 104, 105, 106, 96, 97, + /* 130 */ 98, 99, 100, 101, 102, 103, 104, 105, 106, 94, + /* 140 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, + /* 150 */ 53, 54, 55, 56, 57, 110, 106, 73, 251, 114, + /* 160 */ 73, 178, 179, 180, 181, 59, 184, 292, 81, 186, + /* 170 */ 295, 188, 218, 108, 19, 114, 184, 11, 195, 184, + /* 180 */ 83, 184, 85, 54, 55, 56, 57, 205, 206, 124, + /* 190 */ 145, 146, 147, 96, 97, 98, 99, 100, 101, 102, + /* 200 */ 103, 104, 105, 106, 120, 121, 122, 120, 102, 81, + /* 210 */ 227, 228, 220, 19, 16, 109, 110, 111, 131, 132, + /* 220 */ 26, 184, 184, 240, 229, 96, 97, 98, 99, 100, + /* 230 */ 101, 102, 103, 104, 105, 106, 253, 43, 44, 45, + /* 240 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + /* 250 */ 56, 57, 100, 101, 102, 103, 104, 105, 106, 131, + /* 260 */ 132, 106, 127, 69, 129, 130, 283, 112, 113, 114, + /* 270 */ 115, 116, 117, 118, 81, 77, 76, 79, 296, 124, + /* 280 */ 298, 203, 184, 19, 84, 59, 86, 121, 122, 89, + /* 290 */ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + /* 300 */ 106, 184, 35, 205, 206, 22, 113, 43, 44, 45, + /* 310 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + /* 320 */ 56, 57, 205, 206, 131, 132, 100, 101, 291, 292, + /* 330 */ 114, 67, 295, 66, 108, 109, 110, 111, 138, 113, + /* 340 */ 124, 74, 59, 179, 184, 181, 184, 121, 22, 271, + /* 350 */ 186, 19, 188, 184, 276, 59, 24, 184, 241, 195, + /* 360 */ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + /* 370 */ 106, 145, 59, 147, 184, 43, 44, 45, 46, 47, + /* 380 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + /* 390 */ 123, 227, 228, 110, 296, 297, 22, 23, 184, 102, + /* 400 */ 103, 104, 105, 106, 240, 109, 110, 111, 112, 195, + /* 410 */ 204, 115, 116, 117, 22, 184, 226, 253, 212, 205, + /* 420 */ 206, 125, 109, 110, 111, 22, 100, 101, 96, 97, + /* 430 */ 98, 99, 100, 101, 102, 103, 104, 105, 106, 184, + /* 440 */ 59, 227, 228, 121, 122, 59, 277, 283, 19, 289, + /* 450 */ 290, 59, 23, 76, 240, 241, 143, 76, 72, 189, + /* 460 */ 205, 206, 59, 86, 250, 84, 89, 86, 203, 95, + /* 470 */ 89, 281, 43, 44, 45, 46, 47, 48, 49, 50, + /* 480 */ 51, 52, 53, 54, 55, 56, 57, 227, 228, 184, + /* 490 */ 109, 110, 111, 12, 184, 109, 110, 111, 184, 184, + /* 500 */ 240, 109, 110, 111, 184, 195, 214, 59, 27, 184, + /* 510 */ 205, 206, 109, 110, 111, 205, 206, 184, 263, 138, + /* 520 */ 205, 206, 184, 42, 22, 96, 97, 98, 99, 100, + /* 530 */ 101, 102, 103, 104, 105, 106, 266, 227, 228, 59, + /* 540 */ 270, 276, 94, 66, 63, 19, 241, 22, 26, 23, + /* 550 */ 240, 241, 72, 59, 73, 250, 241, 109, 110, 82, + /* 560 */ 22, 59, 114, 223, 224, 250, 252, 59, 91, 43, + /* 570 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + /* 580 */ 54, 55, 56, 57, 59, 184, 26, 59, 268, 109, + /* 590 */ 110, 111, 184, 145, 146, 147, 112, 59, 203, 115, + /* 600 */ 116, 117, 277, 109, 110, 111, 205, 206, 195, 125, + /* 610 */ 277, 109, 110, 111, 100, 101, 139, 109, 110, 111, + /* 620 */ 219, 184, 96, 97, 98, 99, 100, 101, 102, 103, + /* 630 */ 104, 105, 106, 111, 109, 110, 111, 109, 110, 111, + /* 640 */ 227, 228, 19, 184, 136, 184, 23, 109, 110, 111, + /* 650 */ 200, 201, 202, 240, 259, 260, 261, 195, 136, 145, + /* 660 */ 184, 147, 184, 184, 136, 214, 43, 44, 45, 46, + /* 670 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + /* 680 */ 57, 205, 206, 205, 206, 227, 228, 184, 229, 227, + /* 690 */ 228, 131, 132, 184, 59, 219, 184, 219, 240, 291, + /* 700 */ 292, 184, 240, 295, 105, 106, 22, 23, 205, 206, + /* 710 */ 26, 184, 251, 184, 205, 206, 184, 205, 206, 96, + /* 720 */ 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + /* 730 */ 251, 219, 205, 206, 205, 206, 184, 205, 206, 19, + /* 740 */ 184, 16, 184, 23, 241, 110, 219, 21, 219, 184, + /* 750 */ 241, 219, 286, 287, 195, 184, 195, 205, 206, 201, + /* 760 */ 202, 205, 206, 43, 44, 45, 46, 47, 48, 49, + /* 770 */ 50, 51, 52, 53, 54, 55, 56, 57, 184, 95, + /* 780 */ 22, 23, 184, 26, 26, 220, 227, 228, 227, 228, + /* 790 */ 196, 184, 23, 241, 26, 26, 195, 241, 184, 240, + /* 800 */ 12, 240, 77, 26, 79, 195, 80, 290, 201, 202, + /* 810 */ 216, 184, 218, 195, 184, 27, 96, 97, 98, 99, + /* 820 */ 100, 101, 102, 103, 104, 105, 106, 269, 227, 228, + /* 830 */ 42, 184, 205, 206, 184, 184, 19, 227, 228, 192, + /* 840 */ 23, 240, 116, 196, 76, 227, 228, 120, 121, 122, + /* 850 */ 240, 63, 254, 95, 86, 205, 206, 89, 240, 184, + /* 860 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, + /* 870 */ 53, 54, 55, 56, 57, 184, 269, 184, 153, 153, + /* 880 */ 111, 184, 7, 8, 9, 184, 138, 184, 184, 196, + /* 890 */ 184, 120, 121, 122, 184, 138, 205, 206, 184, 102, + /* 900 */ 184, 184, 205, 206, 156, 136, 205, 206, 205, 206, + /* 910 */ 198, 199, 135, 96, 97, 98, 99, 100, 101, 102, + /* 920 */ 103, 104, 105, 106, 184, 128, 184, 184, 184, 254, + /* 930 */ 133, 184, 237, 19, 239, 229, 226, 23, 292, 184, + /* 940 */ 226, 295, 226, 226, 184, 205, 206, 205, 206, 205, + /* 950 */ 206, 184, 292, 19, 184, 295, 252, 43, 44, 45, + /* 960 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + /* 970 */ 56, 57, 205, 206, 184, 205, 206, 43, 44, 45, + /* 980 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + /* 990 */ 56, 57, 157, 158, 26, 205, 206, 254, 26, 252, + /* 1000 */ 184, 15, 184, 184, 184, 292, 184, 252, 295, 24, + /* 1010 */ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + /* 1020 */ 106, 205, 206, 205, 206, 205, 206, 205, 206, 184, + /* 1030 */ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + /* 1040 */ 106, 184, 184, 184, 59, 184, 60, 184, 229, 184, + /* 1050 */ 205, 206, 184, 258, 184, 19, 184, 19, 184, 246, + /* 1060 */ 184, 258, 205, 206, 205, 206, 205, 206, 205, 206, + /* 1070 */ 205, 206, 184, 205, 206, 205, 206, 205, 206, 205, + /* 1080 */ 206, 205, 206, 292, 226, 151, 295, 184, 228, 294, + /* 1090 */ 184, 119, 184, 205, 206, 110, 150, 294, 152, 184, + /* 1100 */ 240, 184, 22, 23, 23, 19, 184, 26, 205, 206, + /* 1110 */ 142, 205, 206, 205, 206, 184, 198, 199, 131, 132, + /* 1120 */ 205, 206, 205, 206, 22, 19, 24, 205, 206, 43, + /* 1130 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + /* 1140 */ 54, 55, 56, 57, 184, 109, 184, 109, 184, 43, + /* 1150 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + /* 1160 */ 54, 55, 56, 57, 46, 205, 206, 205, 206, 205, + /* 1170 */ 206, 232, 184, 184, 184, 95, 184, 284, 285, 244, + /* 1180 */ 245, 242, 96, 97, 98, 99, 100, 101, 102, 103, + /* 1190 */ 104, 105, 106, 205, 206, 205, 206, 205, 206, 184, + /* 1200 */ 22, 184, 96, 97, 98, 99, 100, 101, 102, 103, + /* 1210 */ 104, 105, 106, 184, 24, 23, 184, 184, 26, 184, + /* 1220 */ 205, 206, 205, 206, 184, 31, 108, 128, 22, 122, + /* 1230 */ 184, 53, 133, 39, 205, 206, 22, 151, 205, 206, + /* 1240 */ 205, 206, 113, 114, 23, 205, 206, 26, 59, 23, + /* 1250 */ 23, 144, 26, 26, 184, 23, 23, 19, 26, 26, + /* 1260 */ 7, 8, 24, 23, 214, 23, 26, 61, 26, 59, + /* 1270 */ 23, 23, 23, 26, 26, 26, 145, 19, 147, 59, + /* 1280 */ 184, 43, 44, 45, 46, 47, 48, 49, 50, 51, + /* 1290 */ 52, 53, 54, 55, 56, 57, 145, 23, 147, 110, + /* 1300 */ 26, 43, 44, 45, 46, 47, 48, 49, 50, 51, + /* 1310 */ 52, 53, 54, 55, 56, 57, 23, 184, 184, 26, + /* 1320 */ 110, 184, 184, 184, 134, 184, 184, 184, 184, 184, + /* 1330 */ 110, 184, 184, 184, 96, 97, 98, 99, 100, 101, + /* 1340 */ 102, 103, 104, 105, 106, 184, 184, 184, 134, 300, + /* 1350 */ 184, 243, 184, 184, 96, 97, 98, 99, 100, 101, + /* 1360 */ 102, 103, 104, 105, 106, 184, 184, 184, 184, 184, + /* 1370 */ 224, 184, 282, 273, 19, 272, 203, 182, 243, 243, + /* 1380 */ 230, 209, 278, 243, 231, 208, 265, 278, 234, 234, + /* 1390 */ 234, 217, 213, 60, 19, 243, 208, 237, 233, 44, + /* 1400 */ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + /* 1410 */ 55, 56, 57, 208, 247, 187, 134, 247, 247, 38, + /* 1420 */ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + /* 1430 */ 55, 56, 57, 237, 231, 191, 191, 279, 279, 282, + /* 1440 */ 143, 191, 108, 268, 22, 19, 20, 256, 22, 43, + /* 1450 */ 257, 96, 97, 98, 99, 100, 101, 102, 103, 104, + /* 1460 */ 105, 106, 36, 222, 142, 234, 18, 191, 225, 18, + /* 1470 */ 190, 96, 97, 98, 99, 100, 101, 102, 103, 104, + /* 1480 */ 105, 106, 225, 191, 225, 59, 225, 257, 234, 234, + /* 1490 */ 256, 222, 222, 190, 234, 150, 62, 71, 275, 274, + /* 1500 */ 191, 19, 20, 190, 22, 22, 210, 81, 191, 190, + /* 1510 */ 210, 191, 108, 190, 207, 207, 64, 207, 36, 215, + /* 1520 */ 94, 210, 207, 209, 119, 207, 100, 101, 207, 106, + /* 1530 */ 48, 215, 207, 107, 210, 109, 110, 111, 267, 267, + /* 1540 */ 114, 59, 210, 249, 137, 108, 248, 191, 249, 248, + /* 1550 */ 88, 249, 141, 71, 248, 299, 138, 131, 132, 22, + /* 1560 */ 191, 249, 299, 237, 82, 238, 150, 262, 140, 87, + /* 1570 */ 139, 145, 146, 147, 148, 149, 94, 248, 238, 236, + /* 1580 */ 25, 235, 100, 101, 234, 194, 26, 193, 13, 107, + /* 1590 */ 6, 109, 110, 111, 264, 185, 114, 185, 183, 197, + /* 1600 */ 183, 203, 183, 203, 203, 197, 203, 211, 211, 4, + /* 1610 */ 197, 3, 203, 22, 155, 15, 288, 203, 93, 288, + /* 1620 */ 285, 23, 16, 203, 203, 23, 132, 145, 146, 147, + /* 1630 */ 148, 149, 0, 1, 2, 143, 123, 5, 24, 135, + /* 1640 */ 20, 16, 10, 11, 12, 13, 14, 137, 1, 17, + /* 1650 */ 135, 144, 19, 20, 123, 22, 61, 143, 37, 123, + /* 1660 */ 53, 109, 30, 53, 32, 53, 53, 134, 34, 36, + /* 1670 */ 1, 5, 40, 22, 108, 153, 26, 68, 75, 68, + /* 1680 */ 41, 134, 108, 24, 20, 124, 19, 118, 23, 67, + /* 1690 */ 22, 67, 59, 22, 22, 22, 22, 67, 28, 37, + /* 1700 */ 23, 142, 70, 22, 71, 23, 157, 23, 23, 26, + /* 1710 */ 78, 23, 22, 81, 23, 82, 24, 22, 24, 134, + /* 1720 */ 87, 23, 19, 20, 92, 22, 109, 94, 23, 22, + /* 1730 */ 34, 34, 136, 100, 101, 26, 34, 85, 34, 36, + /* 1740 */ 107, 83, 109, 110, 111, 34, 90, 114, 34, 23, + /* 1750 */ 75, 75, 44, 22, 24, 26, 34, 23, 126, 26, + /* 1760 */ 23, 23, 59, 131, 132, 23, 23, 26, 23, 22, + /* 1770 */ 11, 22, 22, 22, 71, 23, 23, 22, 145, 146, + /* 1780 */ 147, 148, 149, 26, 23, 82, 154, 134, 128, 134, + /* 1790 */ 87, 134, 15, 1, 301, 134, 301, 94, 301, 301, + /* 1800 */ 301, 301, 301, 100, 101, 301, 301, 301, 301, 301, + /* 1810 */ 107, 301, 109, 110, 111, 1, 2, 114, 301, 5, + /* 1820 */ 301, 301, 301, 301, 10, 11, 12, 13, 14, 301, + /* 1830 */ 301, 17, 301, 301, 301, 301, 19, 20, 301, 22, + /* 1840 */ 301, 301, 301, 301, 30, 301, 32, 301, 145, 146, + /* 1850 */ 147, 148, 149, 36, 40, 301, 301, 301, 301, 301, + /* 1860 */ 301, 301, 301, 301, 301, 301, 301, 301, 301, 301, + /* 1870 */ 301, 301, 301, 301, 301, 301, 59, 301, 301, 301, + /* 1880 */ 301, 301, 301, 301, 70, 301, 301, 301, 71, 301, + /* 1890 */ 301, 301, 78, 301, 301, 81, 19, 20, 301, 22, + /* 1900 */ 301, 301, 301, 301, 301, 301, 92, 301, 301, 301, + /* 1910 */ 301, 94, 301, 36, 301, 301, 301, 100, 101, 102, + /* 1920 */ 301, 301, 301, 301, 107, 301, 109, 110, 111, 301, + /* 1930 */ 301, 114, 301, 301, 301, 301, 59, 301, 301, 301, + /* 1940 */ 126, 301, 301, 301, 301, 131, 132, 301, 71, 301, + /* 1950 */ 301, 301, 301, 301, 301, 301, 19, 20, 301, 22, + /* 1960 */ 301, 301, 145, 146, 147, 148, 149, 301, 154, 301, + /* 1970 */ 301, 94, 301, 36, 301, 301, 301, 100, 101, 301, + /* 1980 */ 301, 301, 301, 301, 107, 301, 109, 110, 111, 301, + /* 1990 */ 301, 114, 5, 301, 301, 301, 59, 10, 11, 12, + /* 2000 */ 13, 14, 301, 301, 17, 301, 301, 301, 71, 301, + /* 2010 */ 301, 301, 301, 301, 301, 301, 301, 30, 301, 32, + /* 2020 */ 301, 301, 145, 146, 147, 148, 149, 40, 301, 301, + /* 2030 */ 301, 94, 301, 301, 301, 301, 301, 100, 101, 301, + /* 2040 */ 301, 301, 301, 301, 107, 301, 109, 110, 111, 301, + /* 2050 */ 301, 114, 301, 301, 301, 301, 301, 70, 301, 301, + /* 2060 */ 301, 301, 301, 301, 301, 78, 301, 301, 81, 301, + /* 2070 */ 301, 301, 301, 301, 301, 301, 301, 301, 301, 92, + /* 2080 */ 301, 301, 145, 146, 147, 148, 149, 301, 301, 301, + /* 2090 */ 301, 301, 301, 301, 301, 301, 301, 301, 301, 301, + /* 2100 */ 301, 301, 301, 301, 301, 301, 301, 301, 301, 301, + /* 2110 */ 301, 301, 301, 126, 301, 301, 301, 301, 131, 132, + /* 2120 */ 301, 301, 301, 301, 301, 301, 301, 301, 301, 301, + /* 2130 */ 301, 301, 301, 301, 301, 301, 301, 301, 301, 301, + /* 2140 */ 301, 154, 301, 301, 301, 301, 301, 301, 301, 301, + /* 2150 */ 301, 301, 301, 301, 301, 301, 301, 301, 301, 301, + /* 2160 */ 301, 301, 301, 301, 301, 301, 301, 301, 301, }; -#define YY_SHIFT_COUNT (523) +#define YY_SHIFT_COUNT (540) #define YY_SHIFT_MIN (0) -#define YY_SHIFT_MAX (1858) +#define YY_SHIFT_MAX (1987) static const unsigned short int yy_shift_ofst[] = { - /* 0 */ 1709, 1520, 1858, 1324, 1324, 24, 1374, 1469, 1602, 1712, - /* 10 */ 1712, 1712, 271, 0, 0, 113, 1016, 1712, 1712, 1712, - /* 20 */ 1712, 1712, 1712, 1712, 1712, 1712, 1712, 12, 12, 409, - /* 30 */ 596, 24, 24, 24, 24, 24, 24, 93, 177, 270, - /* 40 */ 363, 456, 549, 642, 735, 828, 848, 996, 1144, 1016, - /* 50 */ 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, - /* 60 */ 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1164, 1016, 1257, - /* 70 */ 1277, 1277, 1490, 1712, 1712, 1712, 1712, 1712, 1712, 1712, - /* 80 */ 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, - /* 90 */ 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, - /* 100 */ 1712, 1712, 1712, 1712, 1712, 1742, 1712, 1712, 1712, 1712, - /* 110 */ 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 143, - /* 120 */ 162, 162, 162, 162, 162, 204, 151, 186, 650, 690, - /* 130 */ 327, 650, 261, 261, 650, 722, 722, 722, 722, 373, - /* 140 */ 33, 2, 2009, 2009, 330, 330, 330, 346, 289, 278, - /* 150 */ 289, 289, 517, 517, 459, 510, 15, 799, 650, 650, - /* 160 */ 650, 650, 650, 650, 650, 650, 650, 650, 650, 650, - /* 170 */ 650, 650, 650, 650, 650, 650, 650, 650, 650, 650, - /* 180 */ 331, 365, 995, 995, 265, 365, 50, 1038, 2009, 2009, - /* 190 */ 2009, 433, 250, 250, 504, 314, 429, 518, 522, 526, - /* 200 */ 561, 650, 650, 650, 650, 650, 650, 650, 650, 650, - /* 210 */ 192, 650, 650, 650, 650, 650, 650, 650, 650, 650, - /* 220 */ 650, 650, 650, 641, 641, 641, 650, 650, 650, 650, - /* 230 */ 800, 650, 650, 650, 830, 650, 650, 782, 650, 650, - /* 240 */ 650, 650, 650, 650, 650, 650, 739, 902, 689, 895, - /* 250 */ 895, 895, 895, 736, 689, 689, 885, 445, 903, 1124, - /* 260 */ 945, 748, 748, 1066, 945, 945, 1066, 447, 1002, 293, - /* 270 */ 1195, 1195, 1195, 748, 740, 727, 460, 1157, 1348, 1282, - /* 280 */ 1282, 1378, 1378, 1282, 1279, 1315, 1402, 1383, 1294, 1419, - /* 290 */ 1419, 1419, 1419, 1282, 1428, 1294, 1294, 1315, 1402, 1383, - /* 300 */ 1383, 1294, 1282, 1428, 1314, 1400, 1282, 1428, 1453, 1282, - /* 310 */ 1428, 1282, 1428, 1453, 1386, 1386, 1386, 1431, 1453, 1386, - /* 320 */ 1381, 1386, 1431, 1386, 1386, 1453, 1399, 1399, 1453, 1369, - /* 330 */ 1403, 1369, 1403, 1369, 1403, 1369, 1403, 1282, 1404, 1452, - /* 340 */ 1521, 1407, 1404, 1524, 1282, 1416, 1407, 1432, 1434, 1294, - /* 350 */ 1546, 1547, 1569, 1569, 1577, 1577, 1577, 2009, 2009, 2009, - /* 360 */ 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, - /* 370 */ 2009, 2009, 2009, 591, 697, 1059, 1139, 1058, 797, 465, - /* 380 */ 1159, 1182, 1122, 1062, 1180, 936, 1199, 1201, 1205, 1224, - /* 390 */ 1225, 1244, 1061, 1145, 1261, 1161, 1194, 1249, 1251, 1256, - /* 400 */ 1137, 1142, 1263, 1264, 1214, 1207, 1613, 1623, 1605, 1477, - /* 410 */ 1614, 1541, 1620, 1616, 1617, 1509, 1502, 1525, 1619, 1514, - /* 420 */ 1626, 1516, 1634, 1650, 1522, 1512, 1535, 1594, 1621, 1517, - /* 430 */ 1604, 1606, 1607, 1609, 1544, 1559, 1631, 1536, 1666, 1663, - /* 440 */ 1647, 1567, 1523, 1608, 1648, 1610, 1600, 1636, 1549, 1576, - /* 450 */ 1657, 1662, 1664, 1565, 1572, 1665, 1624, 1668, 1671, 1672, - /* 460 */ 1674, 1627, 1660, 1675, 1633, 1667, 1678, 1564, 1681, 1553, - /* 470 */ 1690, 1692, 1691, 1693, 1696, 1700, 1702, 1705, 1704, 1599, - /* 480 */ 1707, 1710, 1630, 1703, 1714, 1618, 1716, 1706, 1716, 1717, - /* 490 */ 1653, 1677, 1670, 1711, 1731, 1732, 1733, 1734, 1723, 1735, - /* 500 */ 1716, 1740, 1743, 1744, 1745, 1739, 1746, 1748, 1761, 1751, - /* 510 */ 1752, 1753, 1754, 1758, 1759, 1749, 1658, 1654, 1655, 1656, - /* 520 */ 1659, 1765, 1776, 1791, + /* 0 */ 1814, 1632, 1987, 1426, 1426, 128, 1482, 1633, 1703, 1877, + /* 10 */ 1877, 1877, 87, 0, 0, 264, 1106, 1877, 1877, 1877, + /* 20 */ 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, + /* 30 */ 226, 226, 381, 381, 296, 193, 128, 128, 128, 128, + /* 40 */ 128, 128, 97, 194, 332, 429, 526, 623, 720, 817, + /* 50 */ 914, 934, 1086, 1238, 1106, 1106, 1106, 1106, 1106, 1106, + /* 60 */ 1106, 1106, 1106, 1106, 1106, 1106, 1106, 1106, 1106, 1106, + /* 70 */ 1106, 1106, 1258, 1106, 1355, 1375, 1375, 1817, 1877, 1877, + /* 80 */ 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, + /* 90 */ 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, + /* 100 */ 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, + /* 110 */ 1937, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, + /* 120 */ 1877, 1877, 1877, 1877, 32, 129, 129, 129, 129, 129, + /* 130 */ 21, 152, 297, 494, 726, 65, 494, 514, 514, 494, + /* 140 */ 560, 560, 560, 560, 322, 599, 50, 2142, 2142, 155, + /* 150 */ 155, 155, 313, 392, 386, 392, 392, 481, 481, 200, + /* 160 */ 480, 684, 758, 494, 494, 494, 494, 494, 494, 494, + /* 170 */ 494, 494, 494, 494, 494, 494, 494, 494, 494, 494, + /* 180 */ 494, 494, 494, 494, 768, 768, 494, 166, 377, 377, + /* 190 */ 635, 835, 835, 635, 748, 987, 2142, 2142, 2142, 448, + /* 200 */ 45, 45, 403, 484, 502, 106, 525, 508, 528, 538, + /* 210 */ 494, 494, 494, 494, 494, 494, 494, 494, 494, 84, + /* 220 */ 494, 494, 494, 494, 494, 494, 494, 494, 494, 494, + /* 230 */ 494, 494, 267, 267, 267, 494, 494, 494, 494, 769, + /* 240 */ 494, 494, 494, 4, 477, 494, 494, 788, 494, 494, + /* 250 */ 494, 494, 494, 494, 494, 494, 727, 5, 135, 985, + /* 260 */ 985, 985, 985, 522, 135, 135, 797, 326, 875, 986, + /* 270 */ 968, 1036, 1036, 1038, 968, 968, 1038, 972, 1081, 1118, + /* 280 */ 1194, 1194, 1194, 1036, 757, 757, 946, 777, 1099, 1102, + /* 290 */ 1333, 1282, 1282, 1381, 1381, 1282, 1297, 1334, 1422, 1406, + /* 300 */ 1322, 1448, 1448, 1448, 1448, 1282, 1451, 1322, 1322, 1334, + /* 310 */ 1422, 1406, 1406, 1322, 1282, 1451, 1345, 1434, 1282, 1451, + /* 320 */ 1483, 1282, 1451, 1282, 1451, 1483, 1404, 1404, 1404, 1452, + /* 330 */ 1483, 1404, 1405, 1404, 1452, 1404, 1404, 1483, 1423, 1423, + /* 340 */ 1483, 1407, 1437, 1407, 1437, 1407, 1437, 1407, 1437, 1282, + /* 350 */ 1462, 1462, 1411, 1418, 1537, 1282, 1416, 1411, 1428, 1431, + /* 360 */ 1322, 1555, 1560, 1575, 1575, 1584, 1584, 1584, 2142, 2142, + /* 370 */ 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, + /* 380 */ 2142, 2142, 2142, 2142, 61, 725, 374, 1080, 198, 771, + /* 390 */ 283, 1192, 1178, 1190, 1107, 1221, 1206, 1226, 1227, 1232, + /* 400 */ 1233, 1240, 1242, 1189, 1129, 1253, 216, 1210, 1247, 1248, + /* 410 */ 1249, 1131, 1151, 1274, 1293, 1220, 1214, 1605, 1608, 1591, + /* 420 */ 1459, 1600, 1525, 1606, 1598, 1602, 1494, 1492, 1513, 1614, + /* 430 */ 1504, 1620, 1510, 1625, 1647, 1515, 1507, 1531, 1595, 1621, + /* 440 */ 1514, 1607, 1610, 1612, 1613, 1536, 1552, 1634, 1533, 1669, + /* 450 */ 1666, 1651, 1566, 1522, 1609, 1650, 1611, 1603, 1639, 1547, + /* 460 */ 1574, 1659, 1664, 1667, 1561, 1569, 1668, 1622, 1671, 1672, + /* 470 */ 1665, 1673, 1624, 1670, 1674, 1630, 1662, 1677, 1559, 1681, + /* 480 */ 1682, 1549, 1684, 1685, 1683, 1688, 1690, 1692, 1691, 1695, + /* 490 */ 1694, 1585, 1698, 1705, 1617, 1696, 1707, 1596, 1709, 1697, + /* 500 */ 1702, 1704, 1711, 1652, 1675, 1658, 1708, 1676, 1656, 1714, + /* 510 */ 1726, 1731, 1730, 1729, 1733, 1722, 1734, 1709, 1737, 1738, + /* 520 */ 1742, 1743, 1741, 1745, 1747, 1759, 1749, 1750, 1752, 1753, + /* 530 */ 1751, 1755, 1757, 1660, 1653, 1655, 1657, 1661, 1761, 1777, + /* 540 */ 1792, }; -#define YY_REDUCE_COUNT (372) -#define YY_REDUCE_MIN (-235) -#define YY_REDUCE_MAX (1441) +#define YY_REDUCE_COUNT (383) +#define YY_REDUCE_MIN (-257) +#define YY_REDUCE_MAX (1421) static const short yy_reduce_ofst[] = { - /* 0 */ -147, 171, 263, -96, 169, -144, -162, -149, -102, -156, - /* 10 */ -98, 216, 354, -170, -57, -235, 307, 149, 423, 428, - /* 20 */ 471, 313, 451, 519, 489, 496, 499, 545, 547, 555, - /* 30 */ -116, 540, 558, 592, 594, 597, 599, -206, -206, -206, - /* 40 */ -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, - /* 50 */ -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, - /* 60 */ -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, - /* 70 */ -206, -206, 196, 309, 494, 537, 612, 656, 675, 679, - /* 80 */ 681, 685, 724, 753, 771, 776, 788, 790, 794, 796, - /* 90 */ 801, 803, 805, 807, 814, 819, 833, 837, 839, 842, - /* 100 */ 845, 847, 849, 853, 873, 891, 893, 917, 921, 937, - /* 110 */ 940, 944, 956, 960, 967, 969, 971, 973, 975, -206, - /* 120 */ -206, -206, -206, -206, -206, -206, -206, -206, 501, -168, - /* 130 */ 90, -97, 87, 112, 303, 277, 601, 277, 601, 179, - /* 140 */ -206, -206, -206, -206, -107, -107, -107, -43, -56, 323, - /* 150 */ 500, 512, -187, -177, 317, 609, 353, 353, 120, 144, - /* 160 */ 490, 539, 698, 374, 467, 507, 789, 404, -157, 755, - /* 170 */ 856, 916, 843, 941, 802, 770, 923, 821, 1001, -142, - /* 180 */ 264, 785, 896, 905, 899, 949, -176, 544, 911, 953, - /* 190 */ 1012, -182, -59, -30, 16, -22, 117, 172, 291, 369, - /* 200 */ 407, 415, 566, 586, 647, 699, 754, 813, 850, 892, - /* 210 */ 121, 1023, 1042, 1086, 1121, 1125, 1128, 1129, 1130, 1131, - /* 220 */ 1132, 1134, 1135, 284, 1106, 1123, 1152, 1154, 1155, 1156, - /* 230 */ 397, 1158, 1172, 1173, 1116, 1176, 1177, 1138, 1179, 117, - /* 240 */ 1184, 1185, 1198, 1200, 1202, 1203, 741, 1094, 1153, 1146, - /* 250 */ 1160, 1162, 1163, 397, 1153, 1153, 1170, 1204, 1206, 1103, - /* 260 */ 1168, 1165, 1166, 1133, 1174, 1175, 1140, 1210, 1193, 1208, - /* 270 */ 1212, 1215, 1216, 1178, 1167, 1189, 1196, 1241, 1148, 1243, - /* 280 */ 1245, 1181, 1183, 1247, 1188, 1187, 1190, 1227, 1223, 1234, - /* 290 */ 1236, 1238, 1239, 1274, 1278, 1235, 1237, 1213, 1218, 1253, - /* 300 */ 1254, 1246, 1287, 1289, 1209, 1219, 1303, 1305, 1293, 1306, - /* 310 */ 1309, 1313, 1316, 1297, 1301, 1307, 1308, 1298, 1310, 1311, - /* 320 */ 1312, 1317, 1304, 1318, 1320, 1319, 1265, 1267, 1325, 1295, - /* 330 */ 1300, 1296, 1302, 1326, 1321, 1327, 1330, 1365, 1323, 1269, - /* 340 */ 1272, 1328, 1331, 1322, 1388, 1334, 1336, 1349, 1353, 1357, - /* 350 */ 1405, 1409, 1420, 1421, 1427, 1429, 1430, 1332, 1335, 1339, - /* 360 */ 1418, 1422, 1423, 1424, 1425, 1433, 1426, 1435, 1436, 1437, - /* 370 */ 1438, 1441, 1439, + /* 0 */ -168, -17, 164, 214, 310, -166, -184, -18, 98, -170, + /* 10 */ 305, 315, -163, -193, -178, -257, 395, 401, 476, 478, + /* 20 */ 512, 117, 527, 529, 503, 509, 532, 255, 552, 556, + /* 30 */ 558, 607, 37, 408, 594, 413, 462, 559, 561, 601, + /* 40 */ 610, 618, -254, -254, -254, -254, -254, -254, -254, -254, + /* 50 */ -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, + /* 60 */ -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, + /* 70 */ -254, -254, -254, -254, -254, -254, -254, -111, 627, 650, + /* 80 */ 691, 697, 701, 703, 740, 742, 744, 767, 770, 790, + /* 90 */ 816, 818, 820, 822, 845, 857, 859, 861, 863, 865, + /* 100 */ 868, 870, 872, 874, 876, 888, 903, 906, 908, 915, + /* 110 */ 917, 922, 960, 962, 964, 988, 990, 992, 1015, 1017, + /* 120 */ 1029, 1033, 1035, 1040, -254, -254, -254, -254, -254, -254, + /* 130 */ -254, -254, -254, 190, 270, -196, 160, -160, 450, 647, + /* 140 */ 260, 458, 260, 458, 78, -254, -254, -254, -254, 206, + /* 150 */ 206, 206, 320, 598, -5, 675, 743, -148, 340, -125, + /* 160 */ 459, 466, 466, 693, -93, 461, 479, 706, 710, 714, + /* 170 */ 716, 717, 169, -183, 325, 314, 704, 333, 747, 858, + /* 180 */ -8, 819, 565, 755, 646, 660, 517, 265, 713, 791, + /* 190 */ 712, 795, 803, 918, 695, 860, 893, 935, 939, -181, + /* 200 */ -172, -147, -91, -46, -3, 162, 173, 231, 338, 437, + /* 210 */ 571, 614, 630, 651, 760, 931, 989, 1032, 1046, -218, + /* 220 */ 38, 1070, 1096, 1133, 1134, 1137, 1138, 1139, 1141, 1142, + /* 230 */ 1143, 1144, 292, 451, 1050, 1145, 1147, 1148, 1149, 813, + /* 240 */ 1161, 1162, 1163, 1108, 1049, 1166, 1168, 1146, 1169, 162, + /* 250 */ 1181, 1182, 1183, 1184, 1185, 1187, 1100, 1103, 1150, 1135, + /* 260 */ 1136, 1140, 1152, 813, 1150, 1150, 1153, 1173, 1195, 1090, + /* 270 */ 1154, 1167, 1170, 1104, 1155, 1156, 1109, 1172, 1174, 1179, + /* 280 */ 1177, 1188, 1205, 1171, 1160, 1196, 1121, 1165, 1203, 1228, + /* 290 */ 1157, 1244, 1245, 1158, 1159, 1250, 1175, 1193, 1191, 1241, + /* 300 */ 1231, 1243, 1257, 1259, 1261, 1276, 1280, 1254, 1255, 1230, + /* 310 */ 1234, 1269, 1270, 1260, 1292, 1303, 1223, 1225, 1309, 1313, + /* 320 */ 1296, 1317, 1319, 1320, 1323, 1300, 1307, 1308, 1310, 1304, + /* 330 */ 1311, 1315, 1314, 1318, 1316, 1321, 1325, 1324, 1271, 1272, + /* 340 */ 1332, 1294, 1298, 1299, 1301, 1302, 1306, 1312, 1329, 1356, + /* 350 */ 1256, 1263, 1327, 1326, 1305, 1369, 1330, 1340, 1343, 1346, + /* 360 */ 1350, 1391, 1394, 1410, 1412, 1415, 1417, 1419, 1328, 1331, + /* 370 */ 1335, 1402, 1398, 1400, 1401, 1403, 1408, 1396, 1397, 1409, + /* 380 */ 1414, 1420, 1421, 1413, }; static const YYACTIONTYPE yy_default[] = { - /* 0 */ 1500, 1500, 1500, 1346, 1129, 1235, 1129, 1129, 1129, 1346, - /* 10 */ 1346, 1346, 1129, 1265, 1265, 1399, 1160, 1129, 1129, 1129, - /* 20 */ 1129, 1129, 1129, 1129, 1345, 1129, 1129, 1129, 1129, 1129, - /* 30 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1271, 1129, - /* 40 */ 1129, 1129, 1129, 1129, 1347, 1348, 1129, 1129, 1129, 1398, - /* 50 */ 1400, 1363, 1281, 1280, 1279, 1278, 1381, 1252, 1276, 1269, - /* 60 */ 1273, 1341, 1342, 1340, 1344, 1348, 1347, 1129, 1272, 1312, - /* 70 */ 1326, 1311, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, - /* 80 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, - /* 90 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, - /* 100 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, - /* 110 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1320, - /* 120 */ 1325, 1331, 1324, 1321, 1314, 1313, 1315, 1316, 1129, 1150, - /* 130 */ 1199, 1129, 1129, 1129, 1129, 1417, 1416, 1129, 1129, 1160, - /* 140 */ 1317, 1318, 1328, 1327, 1406, 1456, 1455, 1364, 1129, 1129, - /* 150 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, - /* 160 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, - /* 170 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, - /* 180 */ 1160, 1156, 1306, 1305, 1426, 1156, 1259, 1129, 1412, 1235, - /* 190 */ 1226, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, - /* 200 */ 1129, 1129, 1129, 1129, 1403, 1401, 1129, 1129, 1129, 1129, - /* 210 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, - /* 220 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, - /* 230 */ 1129, 1129, 1129, 1129, 1231, 1129, 1129, 1129, 1129, 1129, - /* 240 */ 1129, 1129, 1129, 1129, 1129, 1450, 1129, 1376, 1213, 1231, - /* 250 */ 1231, 1231, 1231, 1233, 1214, 1212, 1225, 1160, 1136, 1492, - /* 260 */ 1275, 1254, 1254, 1489, 1275, 1275, 1489, 1174, 1470, 1171, - /* 270 */ 1265, 1265, 1265, 1254, 1343, 1232, 1225, 1129, 1492, 1240, - /* 280 */ 1240, 1491, 1491, 1240, 1364, 1284, 1290, 1202, 1275, 1208, - /* 290 */ 1208, 1208, 1208, 1240, 1147, 1275, 1275, 1284, 1290, 1202, - /* 300 */ 1202, 1275, 1240, 1147, 1380, 1486, 1240, 1147, 1354, 1240, - /* 310 */ 1147, 1240, 1147, 1354, 1200, 1200, 1200, 1189, 1354, 1200, - /* 320 */ 1174, 1200, 1189, 1200, 1200, 1354, 1358, 1358, 1354, 1258, - /* 330 */ 1253, 1258, 1253, 1258, 1253, 1258, 1253, 1240, 1259, 1425, - /* 340 */ 1129, 1270, 1259, 1349, 1240, 1129, 1270, 1268, 1266, 1275, - /* 350 */ 1153, 1192, 1453, 1453, 1449, 1449, 1449, 1497, 1497, 1412, - /* 360 */ 1465, 1160, 1160, 1160, 1160, 1465, 1176, 1176, 1160, 1160, - /* 370 */ 1160, 1160, 1465, 1129, 1129, 1129, 1129, 1129, 1129, 1460, - /* 380 */ 1129, 1365, 1244, 1129, 1129, 1129, 1129, 1129, 1129, 1129, - /* 390 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, - /* 400 */ 1129, 1129, 1129, 1129, 1129, 1295, 1129, 1132, 1409, 1129, - /* 410 */ 1129, 1407, 1129, 1129, 1129, 1129, 1129, 1129, 1245, 1129, - /* 420 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, - /* 430 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1488, 1129, 1129, - /* 440 */ 1129, 1129, 1129, 1129, 1379, 1378, 1129, 1129, 1242, 1129, - /* 450 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, - /* 460 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, - /* 470 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, - /* 480 */ 1129, 1129, 1129, 1129, 1129, 1129, 1267, 1129, 1424, 1129, - /* 490 */ 1129, 1129, 1129, 1129, 1129, 1129, 1438, 1260, 1129, 1129, - /* 500 */ 1479, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, - /* 510 */ 1129, 1129, 1129, 1129, 1129, 1474, 1216, 1297, 1129, 1296, - /* 520 */ 1300, 1129, 1141, 1129, + /* 0 */ 1536, 1536, 1536, 1376, 1159, 1265, 1159, 1159, 1159, 1376, + /* 10 */ 1376, 1376, 1159, 1295, 1295, 1429, 1190, 1159, 1159, 1159, + /* 20 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1375, 1159, 1159, + /* 30 */ 1159, 1159, 1459, 1459, 1159, 1159, 1159, 1159, 1159, 1159, + /* 40 */ 1159, 1159, 1159, 1301, 1159, 1159, 1159, 1159, 1159, 1377, + /* 50 */ 1378, 1159, 1159, 1159, 1428, 1430, 1393, 1311, 1310, 1309, + /* 60 */ 1308, 1411, 1282, 1306, 1299, 1303, 1371, 1372, 1370, 1374, + /* 70 */ 1378, 1377, 1159, 1302, 1342, 1356, 1341, 1159, 1159, 1159, + /* 80 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, + /* 90 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, + /* 100 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, + /* 110 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, + /* 120 */ 1159, 1159, 1159, 1159, 1350, 1355, 1361, 1354, 1351, 1344, + /* 130 */ 1343, 1345, 1346, 1159, 1180, 1229, 1159, 1159, 1159, 1159, + /* 140 */ 1447, 1446, 1159, 1159, 1190, 1347, 1348, 1358, 1357, 1436, + /* 150 */ 1492, 1491, 1394, 1159, 1159, 1159, 1159, 1159, 1159, 1459, + /* 160 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, + /* 170 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, + /* 180 */ 1159, 1159, 1159, 1159, 1459, 1459, 1159, 1190, 1459, 1459, + /* 190 */ 1186, 1336, 1335, 1186, 1289, 1159, 1442, 1265, 1256, 1159, + /* 200 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, + /* 210 */ 1159, 1159, 1159, 1433, 1431, 1159, 1159, 1159, 1159, 1159, + /* 220 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, + /* 230 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, + /* 240 */ 1159, 1159, 1159, 1261, 1159, 1159, 1159, 1159, 1159, 1159, + /* 250 */ 1159, 1159, 1159, 1159, 1159, 1486, 1159, 1406, 1243, 1261, + /* 260 */ 1261, 1261, 1261, 1263, 1244, 1242, 1255, 1190, 1166, 1528, + /* 270 */ 1305, 1284, 1284, 1525, 1305, 1305, 1525, 1204, 1506, 1201, + /* 280 */ 1295, 1295, 1295, 1284, 1289, 1289, 1373, 1262, 1255, 1159, + /* 290 */ 1528, 1270, 1270, 1527, 1527, 1270, 1394, 1314, 1320, 1232, + /* 300 */ 1305, 1238, 1238, 1238, 1238, 1270, 1177, 1305, 1305, 1314, + /* 310 */ 1320, 1232, 1232, 1305, 1270, 1177, 1410, 1522, 1270, 1177, + /* 320 */ 1384, 1270, 1177, 1270, 1177, 1384, 1230, 1230, 1230, 1219, + /* 330 */ 1384, 1230, 1204, 1230, 1219, 1230, 1230, 1384, 1388, 1388, + /* 340 */ 1384, 1288, 1283, 1288, 1283, 1288, 1283, 1288, 1283, 1270, + /* 350 */ 1469, 1469, 1300, 1289, 1379, 1270, 1159, 1300, 1298, 1296, + /* 360 */ 1305, 1183, 1222, 1489, 1489, 1485, 1485, 1485, 1533, 1533, + /* 370 */ 1442, 1501, 1190, 1190, 1190, 1190, 1501, 1206, 1206, 1190, + /* 380 */ 1190, 1190, 1190, 1501, 1159, 1159, 1159, 1159, 1159, 1159, + /* 390 */ 1496, 1159, 1395, 1274, 1159, 1159, 1159, 1159, 1159, 1159, + /* 400 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, + /* 410 */ 1159, 1159, 1159, 1159, 1159, 1159, 1325, 1159, 1162, 1439, + /* 420 */ 1159, 1159, 1437, 1159, 1159, 1159, 1159, 1159, 1159, 1275, + /* 430 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, + /* 440 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1524, 1159, + /* 450 */ 1159, 1159, 1159, 1159, 1159, 1409, 1408, 1159, 1159, 1272, + /* 460 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, + /* 470 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, + /* 480 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, + /* 490 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1297, 1159, + /* 500 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, + /* 510 */ 1159, 1159, 1159, 1474, 1290, 1159, 1159, 1515, 1159, 1159, + /* 520 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, + /* 530 */ 1159, 1159, 1510, 1246, 1327, 1159, 1326, 1330, 1159, 1171, + /* 540 */ 1159, }; /********** End of lemon-generated parsing tables *****************************/ @@ -148346,6 +149183,10 @@ static const YYCODETYPE yyFallback[] = { 59, /* PRECEDING => ID */ 59, /* RANGE => ID */ 59, /* UNBOUNDED => ID */ + 59, /* EXCLUDE => ID */ + 59, /* GROUPS => ID */ + 59, /* OTHERS => ID */ + 59, /* TIES => ID */ 59, /* REINDEX => ID */ 59, /* RENAME => ID */ 59, /* CTIME_KW => ID */ @@ -148524,196 +149365,219 @@ static const char *const yyTokenName[] = { /* 85 */ "PRECEDING", /* 86 */ "RANGE", /* 87 */ "UNBOUNDED", - /* 88 */ "REINDEX", - /* 89 */ "RENAME", - /* 90 */ "CTIME_KW", - /* 91 */ "ANY", - /* 92 */ "BITAND", - /* 93 */ "BITOR", - /* 94 */ "LSHIFT", - /* 95 */ "RSHIFT", - /* 96 */ "PLUS", - /* 97 */ "MINUS", - /* 98 */ "STAR", - /* 99 */ "SLASH", - /* 100 */ "REM", - /* 101 */ "CONCAT", - /* 102 */ "COLLATE", - /* 103 */ "BITNOT", - /* 104 */ "ON", - /* 105 */ "INDEXED", - /* 106 */ "STRING", - /* 107 */ "JOIN_KW", - /* 108 */ "CONSTRAINT", - /* 109 */ "DEFAULT", - /* 110 */ "NULL", - /* 111 */ "PRIMARY", - /* 112 */ "UNIQUE", - /* 113 */ "CHECK", - /* 114 */ "REFERENCES", - /* 115 */ "AUTOINCR", - /* 116 */ "INSERT", - /* 117 */ "DELETE", - /* 118 */ "UPDATE", - /* 119 */ "SET", - /* 120 */ "DEFERRABLE", - /* 121 */ "FOREIGN", - /* 122 */ "DROP", - /* 123 */ "UNION", - /* 124 */ "ALL", - /* 125 */ "EXCEPT", - /* 126 */ "INTERSECT", - /* 127 */ "SELECT", - /* 128 */ "VALUES", - /* 129 */ "DISTINCT", - /* 130 */ "DOT", - /* 131 */ "FROM", - /* 132 */ "JOIN", - /* 133 */ "USING", - /* 134 */ "ORDER", - /* 135 */ "GROUP", - /* 136 */ "HAVING", - /* 137 */ "LIMIT", - /* 138 */ "WHERE", - /* 139 */ "INTO", - /* 140 */ "NOTHING", - /* 141 */ "FLOAT", - /* 142 */ "BLOB", - /* 143 */ "INTEGER", - /* 144 */ "VARIABLE", - /* 145 */ "CASE", - /* 146 */ "WHEN", - /* 147 */ "THEN", - /* 148 */ "ELSE", - /* 149 */ "INDEX", - /* 150 */ "ALTER", - /* 151 */ "ADD", - /* 152 */ "WINDOW", - /* 153 */ "OVER", - /* 154 */ "FILTER", - /* 155 */ "input", - /* 156 */ "cmdlist", - /* 157 */ "ecmd", - /* 158 */ "cmdx", - /* 159 */ "explain", - /* 160 */ "cmd", - /* 161 */ "transtype", - /* 162 */ "trans_opt", - /* 163 */ "nm", - /* 164 */ "savepoint_opt", - /* 165 */ "create_table", - /* 166 */ "create_table_args", - /* 167 */ "createkw", - /* 168 */ "temp", - /* 169 */ "ifnotexists", - /* 170 */ "dbnm", - /* 171 */ "columnlist", - /* 172 */ "conslist_opt", - /* 173 */ "table_options", - /* 174 */ "select", - /* 175 */ "columnname", - /* 176 */ "carglist", - /* 177 */ "typetoken", - /* 178 */ "typename", - /* 179 */ "signed", - /* 180 */ "plus_num", - /* 181 */ "minus_num", - /* 182 */ "scanpt", - /* 183 */ "ccons", - /* 184 */ "term", - /* 185 */ "expr", - /* 186 */ "onconf", - /* 187 */ "sortorder", - /* 188 */ "autoinc", - /* 189 */ "eidlist_opt", - /* 190 */ "refargs", - /* 191 */ "defer_subclause", - /* 192 */ "refarg", - /* 193 */ "refact", - /* 194 */ "init_deferred_pred_opt", - /* 195 */ "conslist", - /* 196 */ "tconscomma", - /* 197 */ "tcons", - /* 198 */ "sortlist", - /* 199 */ "eidlist", - /* 200 */ "defer_subclause_opt", - /* 201 */ "orconf", - /* 202 */ "resolvetype", - /* 203 */ "raisetype", - /* 204 */ "ifexists", - /* 205 */ "fullname", - /* 206 */ "selectnowith", - /* 207 */ "oneselect", - /* 208 */ "wqlist", - /* 209 */ "multiselect_op", - /* 210 */ "distinct", - /* 211 */ "selcollist", - /* 212 */ "from", - /* 213 */ "where_opt", - /* 214 */ "groupby_opt", - /* 215 */ "having_opt", - /* 216 */ "orderby_opt", - /* 217 */ "limit_opt", - /* 218 */ "window_clause", - /* 219 */ "values", - /* 220 */ "nexprlist", - /* 221 */ "sclp", - /* 222 */ "as", - /* 223 */ "seltablist", - /* 224 */ "stl_prefix", - /* 225 */ "joinop", - /* 226 */ "indexed_opt", - /* 227 */ "on_opt", - /* 228 */ "using_opt", - /* 229 */ "exprlist", - /* 230 */ "xfullname", - /* 231 */ "idlist", - /* 232 */ "with", - /* 233 */ "setlist", - /* 234 */ "insert_cmd", - /* 235 */ "idlist_opt", - /* 236 */ "upsert", - /* 237 */ "over_clause", - /* 238 */ "likeop", - /* 239 */ "between_op", - /* 240 */ "in_op", - /* 241 */ "paren_exprlist", - /* 242 */ "case_operand", - /* 243 */ "case_exprlist", - /* 244 */ "case_else", - /* 245 */ "uniqueflag", - /* 246 */ "collate", - /* 247 */ "vinto", - /* 248 */ "nmnum", - /* 249 */ "trigger_decl", - /* 250 */ "trigger_cmd_list", - /* 251 */ "trigger_time", - /* 252 */ "trigger_event", - /* 253 */ "foreach_clause", - /* 254 */ "when_clause", - /* 255 */ "trigger_cmd", - /* 256 */ "trnm", - /* 257 */ "tridxby", - /* 258 */ "database_kw_opt", - /* 259 */ "key_opt", - /* 260 */ "add_column_fullname", - /* 261 */ "kwcolumn_opt", - /* 262 */ "create_vtab", - /* 263 */ "vtabarglist", - /* 264 */ "vtabarg", - /* 265 */ "vtabargtoken", - /* 266 */ "lp", - /* 267 */ "anylist", - /* 268 */ "windowdefn_list", - /* 269 */ "windowdefn", - /* 270 */ "window", - /* 271 */ "frame_opt", - /* 272 */ "part_opt", - /* 273 */ "filter_opt", - /* 274 */ "range_or_rows", - /* 275 */ "frame_bound", - /* 276 */ "frame_bound_s", - /* 277 */ "frame_bound_e", + /* 88 */ "EXCLUDE", + /* 89 */ "GROUPS", + /* 90 */ "OTHERS", + /* 91 */ "TIES", + /* 92 */ "REINDEX", + /* 93 */ "RENAME", + /* 94 */ "CTIME_KW", + /* 95 */ "ANY", + /* 96 */ "BITAND", + /* 97 */ "BITOR", + /* 98 */ "LSHIFT", + /* 99 */ "RSHIFT", + /* 100 */ "PLUS", + /* 101 */ "MINUS", + /* 102 */ "STAR", + /* 103 */ "SLASH", + /* 104 */ "REM", + /* 105 */ "CONCAT", + /* 106 */ "COLLATE", + /* 107 */ "BITNOT", + /* 108 */ "ON", + /* 109 */ "INDEXED", + /* 110 */ "STRING", + /* 111 */ "JOIN_KW", + /* 112 */ "CONSTRAINT", + /* 113 */ "DEFAULT", + /* 114 */ "NULL", + /* 115 */ "PRIMARY", + /* 116 */ "UNIQUE", + /* 117 */ "CHECK", + /* 118 */ "REFERENCES", + /* 119 */ "AUTOINCR", + /* 120 */ "INSERT", + /* 121 */ "DELETE", + /* 122 */ "UPDATE", + /* 123 */ "SET", + /* 124 */ "DEFERRABLE", + /* 125 */ "FOREIGN", + /* 126 */ "DROP", + /* 127 */ "UNION", + /* 128 */ "ALL", + /* 129 */ "EXCEPT", + /* 130 */ "INTERSECT", + /* 131 */ "SELECT", + /* 132 */ "VALUES", + /* 133 */ "DISTINCT", + /* 134 */ "DOT", + /* 135 */ "FROM", + /* 136 */ "JOIN", + /* 137 */ "USING", + /* 138 */ "ORDER", + /* 139 */ "GROUP", + /* 140 */ "HAVING", + /* 141 */ "LIMIT", + /* 142 */ "WHERE", + /* 143 */ "INTO", + /* 144 */ "NOTHING", + /* 145 */ "FLOAT", + /* 146 */ "BLOB", + /* 147 */ "INTEGER", + /* 148 */ "VARIABLE", + /* 149 */ "CASE", + /* 150 */ "WHEN", + /* 151 */ "THEN", + /* 152 */ "ELSE", + /* 153 */ "INDEX", + /* 154 */ "ALTER", + /* 155 */ "ADD", + /* 156 */ "WINDOW", + /* 157 */ "OVER", + /* 158 */ "FILTER", + /* 159 */ "TRUEFALSE", + /* 160 */ "ISNOT", + /* 161 */ "FUNCTION", + /* 162 */ "COLUMN", + /* 163 */ "AGG_FUNCTION", + /* 164 */ "AGG_COLUMN", + /* 165 */ "UMINUS", + /* 166 */ "UPLUS", + /* 167 */ "TRUTH", + /* 168 */ "REGISTER", + /* 169 */ "VECTOR", + /* 170 */ "SELECT_COLUMN", + /* 171 */ "IF_NULL_ROW", + /* 172 */ "ASTERISK", + /* 173 */ "SPAN", + /* 174 */ "SPACE", + /* 175 */ "ILLEGAL", + /* 176 */ "input", + /* 177 */ "cmdlist", + /* 178 */ "ecmd", + /* 179 */ "cmdx", + /* 180 */ "explain", + /* 181 */ "cmd", + /* 182 */ "transtype", + /* 183 */ "trans_opt", + /* 184 */ "nm", + /* 185 */ "savepoint_opt", + /* 186 */ "create_table", + /* 187 */ "create_table_args", + /* 188 */ "createkw", + /* 189 */ "temp", + /* 190 */ "ifnotexists", + /* 191 */ "dbnm", + /* 192 */ "columnlist", + /* 193 */ "conslist_opt", + /* 194 */ "table_options", + /* 195 */ "select", + /* 196 */ "columnname", + /* 197 */ "carglist", + /* 198 */ "typetoken", + /* 199 */ "typename", + /* 200 */ "signed", + /* 201 */ "plus_num", + /* 202 */ "minus_num", + /* 203 */ "scanpt", + /* 204 */ "ccons", + /* 205 */ "term", + /* 206 */ "expr", + /* 207 */ "onconf", + /* 208 */ "sortorder", + /* 209 */ "autoinc", + /* 210 */ "eidlist_opt", + /* 211 */ "refargs", + /* 212 */ "defer_subclause", + /* 213 */ "refarg", + /* 214 */ "refact", + /* 215 */ "init_deferred_pred_opt", + /* 216 */ "conslist", + /* 217 */ "tconscomma", + /* 218 */ "tcons", + /* 219 */ "sortlist", + /* 220 */ "eidlist", + /* 221 */ "defer_subclause_opt", + /* 222 */ "orconf", + /* 223 */ "resolvetype", + /* 224 */ "raisetype", + /* 225 */ "ifexists", + /* 226 */ "fullname", + /* 227 */ "selectnowith", + /* 228 */ "oneselect", + /* 229 */ "wqlist", + /* 230 */ "multiselect_op", + /* 231 */ "distinct", + /* 232 */ "selcollist", + /* 233 */ "from", + /* 234 */ "where_opt", + /* 235 */ "groupby_opt", + /* 236 */ "having_opt", + /* 237 */ "orderby_opt", + /* 238 */ "limit_opt", + /* 239 */ "window_clause", + /* 240 */ "values", + /* 241 */ "nexprlist", + /* 242 */ "sclp", + /* 243 */ "as", + /* 244 */ "seltablist", + /* 245 */ "stl_prefix", + /* 246 */ "joinop", + /* 247 */ "indexed_opt", + /* 248 */ "on_opt", + /* 249 */ "using_opt", + /* 250 */ "exprlist", + /* 251 */ "xfullname", + /* 252 */ "idlist", + /* 253 */ "with", + /* 254 */ "setlist", + /* 255 */ "insert_cmd", + /* 256 */ "idlist_opt", + /* 257 */ "upsert", + /* 258 */ "over_clause", + /* 259 */ "likeop", + /* 260 */ "between_op", + /* 261 */ "in_op", + /* 262 */ "paren_exprlist", + /* 263 */ "case_operand", + /* 264 */ "case_exprlist", + /* 265 */ "case_else", + /* 266 */ "uniqueflag", + /* 267 */ "collate", + /* 268 */ "vinto", + /* 269 */ "nmnum", + /* 270 */ "trigger_decl", + /* 271 */ "trigger_cmd_list", + /* 272 */ "trigger_time", + /* 273 */ "trigger_event", + /* 274 */ "foreach_clause", + /* 275 */ "when_clause", + /* 276 */ "trigger_cmd", + /* 277 */ "trnm", + /* 278 */ "tridxby", + /* 279 */ "database_kw_opt", + /* 280 */ "key_opt", + /* 281 */ "add_column_fullname", + /* 282 */ "kwcolumn_opt", + /* 283 */ "create_vtab", + /* 284 */ "vtabarglist", + /* 285 */ "vtabarg", + /* 286 */ "vtabargtoken", + /* 287 */ "lp", + /* 288 */ "anylist", + /* 289 */ "windowdefn_list", + /* 290 */ "windowdefn", + /* 291 */ "window", + /* 292 */ "frame_opt", + /* 293 */ "part_opt", + /* 294 */ "filter_opt", + /* 295 */ "range_or_rows", + /* 296 */ "frame_bound", + /* 297 */ "frame_bound_s", + /* 298 */ "frame_bound_e", + /* 299 */ "frame_exclude_opt", + /* 300 */ "frame_exclude", }; #endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */ @@ -149011,85 +149875,91 @@ static const char *const yyRuleName[] = { /* 287 */ "wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP", /* 288 */ "windowdefn_list ::= windowdefn", /* 289 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn", - /* 290 */ "windowdefn ::= nm AS window", - /* 291 */ "window ::= LP part_opt orderby_opt frame_opt RP", - /* 292 */ "part_opt ::= PARTITION BY nexprlist", - /* 293 */ "part_opt ::=", - /* 294 */ "frame_opt ::=", - /* 295 */ "frame_opt ::= range_or_rows frame_bound_s", - /* 296 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e", - /* 297 */ "range_or_rows ::= RANGE", - /* 298 */ "range_or_rows ::= ROWS", - /* 299 */ "frame_bound_s ::= frame_bound", - /* 300 */ "frame_bound_s ::= UNBOUNDED PRECEDING", - /* 301 */ "frame_bound_e ::= frame_bound", - /* 302 */ "frame_bound_e ::= UNBOUNDED FOLLOWING", - /* 303 */ "frame_bound ::= expr PRECEDING", - /* 304 */ "frame_bound ::= CURRENT ROW", - /* 305 */ "frame_bound ::= expr FOLLOWING", - /* 306 */ "window_clause ::= WINDOW windowdefn_list", - /* 307 */ "over_clause ::= filter_opt OVER window", - /* 308 */ "over_clause ::= filter_opt OVER nm", - /* 309 */ "filter_opt ::=", - /* 310 */ "filter_opt ::= FILTER LP WHERE expr RP", - /* 311 */ "input ::= cmdlist", - /* 312 */ "cmdlist ::= cmdlist ecmd", - /* 313 */ "cmdlist ::= ecmd", - /* 314 */ "ecmd ::= SEMI", - /* 315 */ "ecmd ::= cmdx SEMI", - /* 316 */ "ecmd ::= explain cmdx", - /* 317 */ "trans_opt ::=", - /* 318 */ "trans_opt ::= TRANSACTION", - /* 319 */ "trans_opt ::= TRANSACTION nm", - /* 320 */ "savepoint_opt ::= SAVEPOINT", - /* 321 */ "savepoint_opt ::=", - /* 322 */ "cmd ::= create_table create_table_args", - /* 323 */ "columnlist ::= columnlist COMMA columnname carglist", - /* 324 */ "columnlist ::= columnname carglist", - /* 325 */ "nm ::= ID|INDEXED", - /* 326 */ "nm ::= STRING", - /* 327 */ "nm ::= JOIN_KW", - /* 328 */ "typetoken ::= typename", - /* 329 */ "typename ::= ID|STRING", - /* 330 */ "signed ::= plus_num", - /* 331 */ "signed ::= minus_num", - /* 332 */ "carglist ::= carglist ccons", - /* 333 */ "carglist ::=", - /* 334 */ "ccons ::= NULL onconf", - /* 335 */ "conslist_opt ::= COMMA conslist", - /* 336 */ "conslist ::= conslist tconscomma tcons", - /* 337 */ "conslist ::= tcons", - /* 338 */ "tconscomma ::=", - /* 339 */ "defer_subclause_opt ::= defer_subclause", - /* 340 */ "resolvetype ::= raisetype", - /* 341 */ "selectnowith ::= oneselect", - /* 342 */ "oneselect ::= values", - /* 343 */ "sclp ::= selcollist COMMA", - /* 344 */ "as ::= ID|STRING", - /* 345 */ "expr ::= term", - /* 346 */ "likeop ::= LIKE_KW|MATCH", - /* 347 */ "exprlist ::= nexprlist", - /* 348 */ "nmnum ::= plus_num", - /* 349 */ "nmnum ::= nm", - /* 350 */ "nmnum ::= ON", - /* 351 */ "nmnum ::= DELETE", - /* 352 */ "nmnum ::= DEFAULT", - /* 353 */ "plus_num ::= INTEGER|FLOAT", - /* 354 */ "foreach_clause ::=", - /* 355 */ "foreach_clause ::= FOR EACH ROW", - /* 356 */ "trnm ::= nm", - /* 357 */ "tridxby ::=", - /* 358 */ "database_kw_opt ::= DATABASE", - /* 359 */ "database_kw_opt ::=", - /* 360 */ "kwcolumn_opt ::=", - /* 361 */ "kwcolumn_opt ::= COLUMNKW", - /* 362 */ "vtabarglist ::= vtabarg", - /* 363 */ "vtabarglist ::= vtabarglist COMMA vtabarg", - /* 364 */ "vtabarg ::= vtabarg vtabargtoken", - /* 365 */ "anylist ::=", - /* 366 */ "anylist ::= anylist LP anylist RP", - /* 367 */ "anylist ::= anylist ANY", - /* 368 */ "with ::=", + /* 290 */ "windowdefn ::= nm AS LP window RP", + /* 291 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt", + /* 292 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt", + /* 293 */ "window ::= ORDER BY sortlist frame_opt", + /* 294 */ "window ::= nm ORDER BY sortlist frame_opt", + /* 295 */ "window ::= frame_opt", + /* 296 */ "window ::= nm frame_opt", + /* 297 */ "frame_opt ::=", + /* 298 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt", + /* 299 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt", + /* 300 */ "range_or_rows ::= RANGE|ROWS|GROUPS", + /* 301 */ "frame_bound_s ::= frame_bound", + /* 302 */ "frame_bound_s ::= UNBOUNDED PRECEDING", + /* 303 */ "frame_bound_e ::= frame_bound", + /* 304 */ "frame_bound_e ::= UNBOUNDED FOLLOWING", + /* 305 */ "frame_bound ::= expr PRECEDING|FOLLOWING", + /* 306 */ "frame_bound ::= CURRENT ROW", + /* 307 */ "frame_exclude_opt ::=", + /* 308 */ "frame_exclude_opt ::= EXCLUDE frame_exclude", + /* 309 */ "frame_exclude ::= NO OTHERS", + /* 310 */ "frame_exclude ::= CURRENT ROW", + /* 311 */ "frame_exclude ::= GROUP|TIES", + /* 312 */ "window_clause ::= WINDOW windowdefn_list", + /* 313 */ "over_clause ::= filter_opt OVER LP window RP", + /* 314 */ "over_clause ::= filter_opt OVER nm", + /* 315 */ "filter_opt ::=", + /* 316 */ "filter_opt ::= FILTER LP WHERE expr RP", + /* 317 */ "input ::= cmdlist", + /* 318 */ "cmdlist ::= cmdlist ecmd", + /* 319 */ "cmdlist ::= ecmd", + /* 320 */ "ecmd ::= SEMI", + /* 321 */ "ecmd ::= cmdx SEMI", + /* 322 */ "ecmd ::= explain cmdx", + /* 323 */ "trans_opt ::=", + /* 324 */ "trans_opt ::= TRANSACTION", + /* 325 */ "trans_opt ::= TRANSACTION nm", + /* 326 */ "savepoint_opt ::= SAVEPOINT", + /* 327 */ "savepoint_opt ::=", + /* 328 */ "cmd ::= create_table create_table_args", + /* 329 */ "columnlist ::= columnlist COMMA columnname carglist", + /* 330 */ "columnlist ::= columnname carglist", + /* 331 */ "nm ::= ID|INDEXED", + /* 332 */ "nm ::= STRING", + /* 333 */ "nm ::= JOIN_KW", + /* 334 */ "typetoken ::= typename", + /* 335 */ "typename ::= ID|STRING", + /* 336 */ "signed ::= plus_num", + /* 337 */ "signed ::= minus_num", + /* 338 */ "carglist ::= carglist ccons", + /* 339 */ "carglist ::=", + /* 340 */ "ccons ::= NULL onconf", + /* 341 */ "conslist_opt ::= COMMA conslist", + /* 342 */ "conslist ::= conslist tconscomma tcons", + /* 343 */ "conslist ::= tcons", + /* 344 */ "tconscomma ::=", + /* 345 */ "defer_subclause_opt ::= defer_subclause", + /* 346 */ "resolvetype ::= raisetype", + /* 347 */ "selectnowith ::= oneselect", + /* 348 */ "oneselect ::= values", + /* 349 */ "sclp ::= selcollist COMMA", + /* 350 */ "as ::= ID|STRING", + /* 351 */ "expr ::= term", + /* 352 */ "likeop ::= LIKE_KW|MATCH", + /* 353 */ "exprlist ::= nexprlist", + /* 354 */ "nmnum ::= plus_num", + /* 355 */ "nmnum ::= nm", + /* 356 */ "nmnum ::= ON", + /* 357 */ "nmnum ::= DELETE", + /* 358 */ "nmnum ::= DEFAULT", + /* 359 */ "plus_num ::= INTEGER|FLOAT", + /* 360 */ "foreach_clause ::=", + /* 361 */ "foreach_clause ::= FOR EACH ROW", + /* 362 */ "trnm ::= nm", + /* 363 */ "tridxby ::=", + /* 364 */ "database_kw_opt ::= DATABASE", + /* 365 */ "database_kw_opt ::=", + /* 366 */ "kwcolumn_opt ::=", + /* 367 */ "kwcolumn_opt ::= COLUMNKW", + /* 368 */ "vtabarglist ::= vtabarg", + /* 369 */ "vtabarglist ::= vtabarglist COMMA vtabarg", + /* 370 */ "vtabarg ::= vtabarg vtabargtoken", + /* 371 */ "anylist ::=", + /* 372 */ "anylist ::= anylist LP anylist RP", + /* 373 */ "anylist ::= anylist ANY", + /* 374 */ "with ::=", }; #endif /* NDEBUG */ @@ -149215,97 +150085,97 @@ static void yy_destructor( ** inside the C code. */ /********* Begin destructor definitions ***************************************/ - case 174: /* select */ - case 206: /* selectnowith */ - case 207: /* oneselect */ - case 219: /* values */ + case 195: /* select */ + case 227: /* selectnowith */ + case 228: /* oneselect */ + case 240: /* values */ { -sqlite3SelectDelete(pParse->db, (yypminor->yy423)); +sqlite3SelectDelete(pParse->db, (yypminor->yy457)); } break; - case 184: /* term */ - case 185: /* expr */ - case 213: /* where_opt */ - case 215: /* having_opt */ - case 227: /* on_opt */ - case 242: /* case_operand */ - case 244: /* case_else */ - case 247: /* vinto */ - case 254: /* when_clause */ - case 259: /* key_opt */ - case 273: /* filter_opt */ + case 205: /* term */ + case 206: /* expr */ + case 234: /* where_opt */ + case 236: /* having_opt */ + case 248: /* on_opt */ + case 263: /* case_operand */ + case 265: /* case_else */ + case 268: /* vinto */ + case 275: /* when_clause */ + case 280: /* key_opt */ + case 294: /* filter_opt */ { -sqlite3ExprDelete(pParse->db, (yypminor->yy490)); +sqlite3ExprDelete(pParse->db, (yypminor->yy524)); } break; - case 189: /* eidlist_opt */ - case 198: /* sortlist */ - case 199: /* eidlist */ - case 211: /* selcollist */ - case 214: /* groupby_opt */ - case 216: /* orderby_opt */ - case 220: /* nexprlist */ - case 221: /* sclp */ - case 229: /* exprlist */ - case 233: /* setlist */ - case 241: /* paren_exprlist */ - case 243: /* case_exprlist */ - case 272: /* part_opt */ + case 210: /* eidlist_opt */ + case 219: /* sortlist */ + case 220: /* eidlist */ + case 232: /* selcollist */ + case 235: /* groupby_opt */ + case 237: /* orderby_opt */ + case 241: /* nexprlist */ + case 242: /* sclp */ + case 250: /* exprlist */ + case 254: /* setlist */ + case 262: /* paren_exprlist */ + case 264: /* case_exprlist */ + case 293: /* part_opt */ { -sqlite3ExprListDelete(pParse->db, (yypminor->yy42)); +sqlite3ExprListDelete(pParse->db, (yypminor->yy434)); } break; - case 205: /* fullname */ - case 212: /* from */ - case 223: /* seltablist */ - case 224: /* stl_prefix */ - case 230: /* xfullname */ + case 226: /* fullname */ + case 233: /* from */ + case 244: /* seltablist */ + case 245: /* stl_prefix */ + case 251: /* xfullname */ { -sqlite3SrcListDelete(pParse->db, (yypminor->yy167)); +sqlite3SrcListDelete(pParse->db, (yypminor->yy483)); } break; - case 208: /* wqlist */ + case 229: /* wqlist */ { -sqlite3WithDelete(pParse->db, (yypminor->yy499)); +sqlite3WithDelete(pParse->db, (yypminor->yy59)); } break; - case 218: /* window_clause */ - case 268: /* windowdefn_list */ + case 239: /* window_clause */ + case 289: /* windowdefn_list */ { -sqlite3WindowListDelete(pParse->db, (yypminor->yy147)); +sqlite3WindowListDelete(pParse->db, (yypminor->yy295)); } break; - case 228: /* using_opt */ - case 231: /* idlist */ - case 235: /* idlist_opt */ + case 249: /* using_opt */ + case 252: /* idlist */ + case 256: /* idlist_opt */ { -sqlite3IdListDelete(pParse->db, (yypminor->yy336)); +sqlite3IdListDelete(pParse->db, (yypminor->yy62)); } break; - case 237: /* over_clause */ - case 269: /* windowdefn */ - case 270: /* window */ - case 271: /* frame_opt */ + case 258: /* over_clause */ + case 290: /* windowdefn */ + case 291: /* window */ + case 292: /* frame_opt */ { -sqlite3WindowDelete(pParse->db, (yypminor->yy147)); +sqlite3WindowDelete(pParse->db, (yypminor->yy295)); } break; - case 250: /* trigger_cmd_list */ - case 255: /* trigger_cmd */ + case 271: /* trigger_cmd_list */ + case 276: /* trigger_cmd */ { -sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy119)); +sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy455)); } break; - case 252: /* trigger_event */ + case 273: /* trigger_event */ { -sqlite3IdListDelete(pParse->db, (yypminor->yy350).b); +sqlite3IdListDelete(pParse->db, (yypminor->yy90).b); } break; - case 275: /* frame_bound */ - case 276: /* frame_bound_s */ - case 277: /* frame_bound_e */ + case 296: /* frame_bound */ + case 297: /* frame_bound_s */ + case 298: /* frame_bound_e */ { -sqlite3ExprDelete(pParse->db, (yypminor->yy317).pExpr); +sqlite3ExprDelete(pParse->db, (yypminor->yy201).pExpr); } break; /********* End destructor definitions *****************************************/ @@ -149600,375 +150470,381 @@ static void yy_shift( /* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side ** of that rule */ static const YYCODETYPE yyRuleInfoLhs[] = { - 159, /* (0) explain ::= EXPLAIN */ - 159, /* (1) explain ::= EXPLAIN QUERY PLAN */ - 158, /* (2) cmdx ::= cmd */ - 160, /* (3) cmd ::= BEGIN transtype trans_opt */ - 161, /* (4) transtype ::= */ - 161, /* (5) transtype ::= DEFERRED */ - 161, /* (6) transtype ::= IMMEDIATE */ - 161, /* (7) transtype ::= EXCLUSIVE */ - 160, /* (8) cmd ::= COMMIT|END trans_opt */ - 160, /* (9) cmd ::= ROLLBACK trans_opt */ - 160, /* (10) cmd ::= SAVEPOINT nm */ - 160, /* (11) cmd ::= RELEASE savepoint_opt nm */ - 160, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */ - 165, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */ - 167, /* (14) createkw ::= CREATE */ - 169, /* (15) ifnotexists ::= */ - 169, /* (16) ifnotexists ::= IF NOT EXISTS */ - 168, /* (17) temp ::= TEMP */ - 168, /* (18) temp ::= */ - 166, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_options */ - 166, /* (20) create_table_args ::= AS select */ - 173, /* (21) table_options ::= */ - 173, /* (22) table_options ::= WITHOUT nm */ - 175, /* (23) columnname ::= nm typetoken */ - 177, /* (24) typetoken ::= */ - 177, /* (25) typetoken ::= typename LP signed RP */ - 177, /* (26) typetoken ::= typename LP signed COMMA signed RP */ - 178, /* (27) typename ::= typename ID|STRING */ - 182, /* (28) scanpt ::= */ - 183, /* (29) ccons ::= CONSTRAINT nm */ - 183, /* (30) ccons ::= DEFAULT scanpt term scanpt */ - 183, /* (31) ccons ::= DEFAULT LP expr RP */ - 183, /* (32) ccons ::= DEFAULT PLUS term scanpt */ - 183, /* (33) ccons ::= DEFAULT MINUS term scanpt */ - 183, /* (34) ccons ::= DEFAULT scanpt ID|INDEXED */ - 183, /* (35) ccons ::= NOT NULL onconf */ - 183, /* (36) ccons ::= PRIMARY KEY sortorder onconf autoinc */ - 183, /* (37) ccons ::= UNIQUE onconf */ - 183, /* (38) ccons ::= CHECK LP expr RP */ - 183, /* (39) ccons ::= REFERENCES nm eidlist_opt refargs */ - 183, /* (40) ccons ::= defer_subclause */ - 183, /* (41) ccons ::= COLLATE ID|STRING */ - 188, /* (42) autoinc ::= */ - 188, /* (43) autoinc ::= AUTOINCR */ - 190, /* (44) refargs ::= */ - 190, /* (45) refargs ::= refargs refarg */ - 192, /* (46) refarg ::= MATCH nm */ - 192, /* (47) refarg ::= ON INSERT refact */ - 192, /* (48) refarg ::= ON DELETE refact */ - 192, /* (49) refarg ::= ON UPDATE refact */ - 193, /* (50) refact ::= SET NULL */ - 193, /* (51) refact ::= SET DEFAULT */ - 193, /* (52) refact ::= CASCADE */ - 193, /* (53) refact ::= RESTRICT */ - 193, /* (54) refact ::= NO ACTION */ - 191, /* (55) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ - 191, /* (56) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ - 194, /* (57) init_deferred_pred_opt ::= */ - 194, /* (58) init_deferred_pred_opt ::= INITIALLY DEFERRED */ - 194, /* (59) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ - 172, /* (60) conslist_opt ::= */ - 196, /* (61) tconscomma ::= COMMA */ - 197, /* (62) tcons ::= CONSTRAINT nm */ - 197, /* (63) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ - 197, /* (64) tcons ::= UNIQUE LP sortlist RP onconf */ - 197, /* (65) tcons ::= CHECK LP expr RP onconf */ - 197, /* (66) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ - 200, /* (67) defer_subclause_opt ::= */ - 186, /* (68) onconf ::= */ - 186, /* (69) onconf ::= ON CONFLICT resolvetype */ - 201, /* (70) orconf ::= */ - 201, /* (71) orconf ::= OR resolvetype */ - 202, /* (72) resolvetype ::= IGNORE */ - 202, /* (73) resolvetype ::= REPLACE */ - 160, /* (74) cmd ::= DROP TABLE ifexists fullname */ - 204, /* (75) ifexists ::= IF EXISTS */ - 204, /* (76) ifexists ::= */ - 160, /* (77) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ - 160, /* (78) cmd ::= DROP VIEW ifexists fullname */ - 160, /* (79) cmd ::= select */ - 174, /* (80) select ::= WITH wqlist selectnowith */ - 174, /* (81) select ::= WITH RECURSIVE wqlist selectnowith */ - 174, /* (82) select ::= selectnowith */ - 206, /* (83) selectnowith ::= selectnowith multiselect_op oneselect */ - 209, /* (84) multiselect_op ::= UNION */ - 209, /* (85) multiselect_op ::= UNION ALL */ - 209, /* (86) multiselect_op ::= EXCEPT|INTERSECT */ - 207, /* (87) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ - 207, /* (88) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ - 219, /* (89) values ::= VALUES LP nexprlist RP */ - 219, /* (90) values ::= values COMMA LP nexprlist RP */ - 210, /* (91) distinct ::= DISTINCT */ - 210, /* (92) distinct ::= ALL */ - 210, /* (93) distinct ::= */ - 221, /* (94) sclp ::= */ - 211, /* (95) selcollist ::= sclp scanpt expr scanpt as */ - 211, /* (96) selcollist ::= sclp scanpt STAR */ - 211, /* (97) selcollist ::= sclp scanpt nm DOT STAR */ - 222, /* (98) as ::= AS nm */ - 222, /* (99) as ::= */ - 212, /* (100) from ::= */ - 212, /* (101) from ::= FROM seltablist */ - 224, /* (102) stl_prefix ::= seltablist joinop */ - 224, /* (103) stl_prefix ::= */ - 223, /* (104) seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ - 223, /* (105) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */ - 223, /* (106) seltablist ::= stl_prefix LP select RP as on_opt using_opt */ - 223, /* (107) seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ - 170, /* (108) dbnm ::= */ - 170, /* (109) dbnm ::= DOT nm */ - 205, /* (110) fullname ::= nm */ - 205, /* (111) fullname ::= nm DOT nm */ - 230, /* (112) xfullname ::= nm */ - 230, /* (113) xfullname ::= nm DOT nm */ - 230, /* (114) xfullname ::= nm DOT nm AS nm */ - 230, /* (115) xfullname ::= nm AS nm */ - 225, /* (116) joinop ::= COMMA|JOIN */ - 225, /* (117) joinop ::= JOIN_KW JOIN */ - 225, /* (118) joinop ::= JOIN_KW nm JOIN */ - 225, /* (119) joinop ::= JOIN_KW nm nm JOIN */ - 227, /* (120) on_opt ::= ON expr */ - 227, /* (121) on_opt ::= */ - 226, /* (122) indexed_opt ::= */ - 226, /* (123) indexed_opt ::= INDEXED BY nm */ - 226, /* (124) indexed_opt ::= NOT INDEXED */ - 228, /* (125) using_opt ::= USING LP idlist RP */ - 228, /* (126) using_opt ::= */ - 216, /* (127) orderby_opt ::= */ - 216, /* (128) orderby_opt ::= ORDER BY sortlist */ - 198, /* (129) sortlist ::= sortlist COMMA expr sortorder */ - 198, /* (130) sortlist ::= expr sortorder */ - 187, /* (131) sortorder ::= ASC */ - 187, /* (132) sortorder ::= DESC */ - 187, /* (133) sortorder ::= */ - 214, /* (134) groupby_opt ::= */ - 214, /* (135) groupby_opt ::= GROUP BY nexprlist */ - 215, /* (136) having_opt ::= */ - 215, /* (137) having_opt ::= HAVING expr */ - 217, /* (138) limit_opt ::= */ - 217, /* (139) limit_opt ::= LIMIT expr */ - 217, /* (140) limit_opt ::= LIMIT expr OFFSET expr */ - 217, /* (141) limit_opt ::= LIMIT expr COMMA expr */ - 160, /* (142) cmd ::= with DELETE FROM xfullname indexed_opt where_opt */ - 213, /* (143) where_opt ::= */ - 213, /* (144) where_opt ::= WHERE expr */ - 160, /* (145) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt */ - 233, /* (146) setlist ::= setlist COMMA nm EQ expr */ - 233, /* (147) setlist ::= setlist COMMA LP idlist RP EQ expr */ - 233, /* (148) setlist ::= nm EQ expr */ - 233, /* (149) setlist ::= LP idlist RP EQ expr */ - 160, /* (150) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ - 160, /* (151) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */ - 236, /* (152) upsert ::= */ - 236, /* (153) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */ - 236, /* (154) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */ - 236, /* (155) upsert ::= ON CONFLICT DO NOTHING */ - 234, /* (156) insert_cmd ::= INSERT orconf */ - 234, /* (157) insert_cmd ::= REPLACE */ - 235, /* (158) idlist_opt ::= */ - 235, /* (159) idlist_opt ::= LP idlist RP */ - 231, /* (160) idlist ::= idlist COMMA nm */ - 231, /* (161) idlist ::= nm */ - 185, /* (162) expr ::= LP expr RP */ - 185, /* (163) expr ::= ID|INDEXED */ - 185, /* (164) expr ::= JOIN_KW */ - 185, /* (165) expr ::= nm DOT nm */ - 185, /* (166) expr ::= nm DOT nm DOT nm */ - 184, /* (167) term ::= NULL|FLOAT|BLOB */ - 184, /* (168) term ::= STRING */ - 184, /* (169) term ::= INTEGER */ - 185, /* (170) expr ::= VARIABLE */ - 185, /* (171) expr ::= expr COLLATE ID|STRING */ - 185, /* (172) expr ::= CAST LP expr AS typetoken RP */ - 185, /* (173) expr ::= ID|INDEXED LP distinct exprlist RP */ - 185, /* (174) expr ::= ID|INDEXED LP STAR RP */ - 185, /* (175) expr ::= ID|INDEXED LP distinct exprlist RP over_clause */ - 185, /* (176) expr ::= ID|INDEXED LP STAR RP over_clause */ - 184, /* (177) term ::= CTIME_KW */ - 185, /* (178) expr ::= LP nexprlist COMMA expr RP */ - 185, /* (179) expr ::= expr AND expr */ - 185, /* (180) expr ::= expr OR expr */ - 185, /* (181) expr ::= expr LT|GT|GE|LE expr */ - 185, /* (182) expr ::= expr EQ|NE expr */ - 185, /* (183) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ - 185, /* (184) expr ::= expr PLUS|MINUS expr */ - 185, /* (185) expr ::= expr STAR|SLASH|REM expr */ - 185, /* (186) expr ::= expr CONCAT expr */ - 238, /* (187) likeop ::= NOT LIKE_KW|MATCH */ - 185, /* (188) expr ::= expr likeop expr */ - 185, /* (189) expr ::= expr likeop expr ESCAPE expr */ - 185, /* (190) expr ::= expr ISNULL|NOTNULL */ - 185, /* (191) expr ::= expr NOT NULL */ - 185, /* (192) expr ::= expr IS expr */ - 185, /* (193) expr ::= expr IS NOT expr */ - 185, /* (194) expr ::= NOT expr */ - 185, /* (195) expr ::= BITNOT expr */ - 185, /* (196) expr ::= PLUS|MINUS expr */ - 239, /* (197) between_op ::= BETWEEN */ - 239, /* (198) between_op ::= NOT BETWEEN */ - 185, /* (199) expr ::= expr between_op expr AND expr */ - 240, /* (200) in_op ::= IN */ - 240, /* (201) in_op ::= NOT IN */ - 185, /* (202) expr ::= expr in_op LP exprlist RP */ - 185, /* (203) expr ::= LP select RP */ - 185, /* (204) expr ::= expr in_op LP select RP */ - 185, /* (205) expr ::= expr in_op nm dbnm paren_exprlist */ - 185, /* (206) expr ::= EXISTS LP select RP */ - 185, /* (207) expr ::= CASE case_operand case_exprlist case_else END */ - 243, /* (208) case_exprlist ::= case_exprlist WHEN expr THEN expr */ - 243, /* (209) case_exprlist ::= WHEN expr THEN expr */ - 244, /* (210) case_else ::= ELSE expr */ - 244, /* (211) case_else ::= */ - 242, /* (212) case_operand ::= expr */ - 242, /* (213) case_operand ::= */ - 229, /* (214) exprlist ::= */ - 220, /* (215) nexprlist ::= nexprlist COMMA expr */ - 220, /* (216) nexprlist ::= expr */ - 241, /* (217) paren_exprlist ::= */ - 241, /* (218) paren_exprlist ::= LP exprlist RP */ - 160, /* (219) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ - 245, /* (220) uniqueflag ::= UNIQUE */ - 245, /* (221) uniqueflag ::= */ - 189, /* (222) eidlist_opt ::= */ - 189, /* (223) eidlist_opt ::= LP eidlist RP */ - 199, /* (224) eidlist ::= eidlist COMMA nm collate sortorder */ - 199, /* (225) eidlist ::= nm collate sortorder */ - 246, /* (226) collate ::= */ - 246, /* (227) collate ::= COLLATE ID|STRING */ - 160, /* (228) cmd ::= DROP INDEX ifexists fullname */ - 160, /* (229) cmd ::= VACUUM vinto */ - 160, /* (230) cmd ::= VACUUM nm vinto */ - 247, /* (231) vinto ::= INTO expr */ - 247, /* (232) vinto ::= */ - 160, /* (233) cmd ::= PRAGMA nm dbnm */ - 160, /* (234) cmd ::= PRAGMA nm dbnm EQ nmnum */ - 160, /* (235) cmd ::= PRAGMA nm dbnm LP nmnum RP */ - 160, /* (236) cmd ::= PRAGMA nm dbnm EQ minus_num */ - 160, /* (237) cmd ::= PRAGMA nm dbnm LP minus_num RP */ - 180, /* (238) plus_num ::= PLUS INTEGER|FLOAT */ - 181, /* (239) minus_num ::= MINUS INTEGER|FLOAT */ - 160, /* (240) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ - 249, /* (241) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ - 251, /* (242) trigger_time ::= BEFORE|AFTER */ - 251, /* (243) trigger_time ::= INSTEAD OF */ - 251, /* (244) trigger_time ::= */ - 252, /* (245) trigger_event ::= DELETE|INSERT */ - 252, /* (246) trigger_event ::= UPDATE */ - 252, /* (247) trigger_event ::= UPDATE OF idlist */ - 254, /* (248) when_clause ::= */ - 254, /* (249) when_clause ::= WHEN expr */ - 250, /* (250) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ - 250, /* (251) trigger_cmd_list ::= trigger_cmd SEMI */ - 256, /* (252) trnm ::= nm DOT nm */ - 257, /* (253) tridxby ::= INDEXED BY nm */ - 257, /* (254) tridxby ::= NOT INDEXED */ - 255, /* (255) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */ - 255, /* (256) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ - 255, /* (257) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ - 255, /* (258) trigger_cmd ::= scanpt select scanpt */ - 185, /* (259) expr ::= RAISE LP IGNORE RP */ - 185, /* (260) expr ::= RAISE LP raisetype COMMA nm RP */ - 203, /* (261) raisetype ::= ROLLBACK */ - 203, /* (262) raisetype ::= ABORT */ - 203, /* (263) raisetype ::= FAIL */ - 160, /* (264) cmd ::= DROP TRIGGER ifexists fullname */ - 160, /* (265) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ - 160, /* (266) cmd ::= DETACH database_kw_opt expr */ - 259, /* (267) key_opt ::= */ - 259, /* (268) key_opt ::= KEY expr */ - 160, /* (269) cmd ::= REINDEX */ - 160, /* (270) cmd ::= REINDEX nm dbnm */ - 160, /* (271) cmd ::= ANALYZE */ - 160, /* (272) cmd ::= ANALYZE nm dbnm */ - 160, /* (273) cmd ::= ALTER TABLE fullname RENAME TO nm */ - 160, /* (274) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ - 260, /* (275) add_column_fullname ::= fullname */ - 160, /* (276) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ - 160, /* (277) cmd ::= create_vtab */ - 160, /* (278) cmd ::= create_vtab LP vtabarglist RP */ - 262, /* (279) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ - 264, /* (280) vtabarg ::= */ - 265, /* (281) vtabargtoken ::= ANY */ - 265, /* (282) vtabargtoken ::= lp anylist RP */ - 266, /* (283) lp ::= LP */ - 232, /* (284) with ::= WITH wqlist */ - 232, /* (285) with ::= WITH RECURSIVE wqlist */ - 208, /* (286) wqlist ::= nm eidlist_opt AS LP select RP */ - 208, /* (287) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */ - 268, /* (288) windowdefn_list ::= windowdefn */ - 268, /* (289) windowdefn_list ::= windowdefn_list COMMA windowdefn */ - 269, /* (290) windowdefn ::= nm AS window */ - 270, /* (291) window ::= LP part_opt orderby_opt frame_opt RP */ - 272, /* (292) part_opt ::= PARTITION BY nexprlist */ - 272, /* (293) part_opt ::= */ - 271, /* (294) frame_opt ::= */ - 271, /* (295) frame_opt ::= range_or_rows frame_bound_s */ - 271, /* (296) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e */ - 274, /* (297) range_or_rows ::= RANGE */ - 274, /* (298) range_or_rows ::= ROWS */ - 276, /* (299) frame_bound_s ::= frame_bound */ - 276, /* (300) frame_bound_s ::= UNBOUNDED PRECEDING */ - 277, /* (301) frame_bound_e ::= frame_bound */ - 277, /* (302) frame_bound_e ::= UNBOUNDED FOLLOWING */ - 275, /* (303) frame_bound ::= expr PRECEDING */ - 275, /* (304) frame_bound ::= CURRENT ROW */ - 275, /* (305) frame_bound ::= expr FOLLOWING */ - 218, /* (306) window_clause ::= WINDOW windowdefn_list */ - 237, /* (307) over_clause ::= filter_opt OVER window */ - 237, /* (308) over_clause ::= filter_opt OVER nm */ - 273, /* (309) filter_opt ::= */ - 273, /* (310) filter_opt ::= FILTER LP WHERE expr RP */ - 155, /* (311) input ::= cmdlist */ - 156, /* (312) cmdlist ::= cmdlist ecmd */ - 156, /* (313) cmdlist ::= ecmd */ - 157, /* (314) ecmd ::= SEMI */ - 157, /* (315) ecmd ::= cmdx SEMI */ - 157, /* (316) ecmd ::= explain cmdx */ - 162, /* (317) trans_opt ::= */ - 162, /* (318) trans_opt ::= TRANSACTION */ - 162, /* (319) trans_opt ::= TRANSACTION nm */ - 164, /* (320) savepoint_opt ::= SAVEPOINT */ - 164, /* (321) savepoint_opt ::= */ - 160, /* (322) cmd ::= create_table create_table_args */ - 171, /* (323) columnlist ::= columnlist COMMA columnname carglist */ - 171, /* (324) columnlist ::= columnname carglist */ - 163, /* (325) nm ::= ID|INDEXED */ - 163, /* (326) nm ::= STRING */ - 163, /* (327) nm ::= JOIN_KW */ - 177, /* (328) typetoken ::= typename */ - 178, /* (329) typename ::= ID|STRING */ - 179, /* (330) signed ::= plus_num */ - 179, /* (331) signed ::= minus_num */ - 176, /* (332) carglist ::= carglist ccons */ - 176, /* (333) carglist ::= */ - 183, /* (334) ccons ::= NULL onconf */ - 172, /* (335) conslist_opt ::= COMMA conslist */ - 195, /* (336) conslist ::= conslist tconscomma tcons */ - 195, /* (337) conslist ::= tcons */ - 196, /* (338) tconscomma ::= */ - 200, /* (339) defer_subclause_opt ::= defer_subclause */ - 202, /* (340) resolvetype ::= raisetype */ - 206, /* (341) selectnowith ::= oneselect */ - 207, /* (342) oneselect ::= values */ - 221, /* (343) sclp ::= selcollist COMMA */ - 222, /* (344) as ::= ID|STRING */ - 185, /* (345) expr ::= term */ - 238, /* (346) likeop ::= LIKE_KW|MATCH */ - 229, /* (347) exprlist ::= nexprlist */ - 248, /* (348) nmnum ::= plus_num */ - 248, /* (349) nmnum ::= nm */ - 248, /* (350) nmnum ::= ON */ - 248, /* (351) nmnum ::= DELETE */ - 248, /* (352) nmnum ::= DEFAULT */ - 180, /* (353) plus_num ::= INTEGER|FLOAT */ - 253, /* (354) foreach_clause ::= */ - 253, /* (355) foreach_clause ::= FOR EACH ROW */ - 256, /* (356) trnm ::= nm */ - 257, /* (357) tridxby ::= */ - 258, /* (358) database_kw_opt ::= DATABASE */ - 258, /* (359) database_kw_opt ::= */ - 261, /* (360) kwcolumn_opt ::= */ - 261, /* (361) kwcolumn_opt ::= COLUMNKW */ - 263, /* (362) vtabarglist ::= vtabarg */ - 263, /* (363) vtabarglist ::= vtabarglist COMMA vtabarg */ - 264, /* (364) vtabarg ::= vtabarg vtabargtoken */ - 267, /* (365) anylist ::= */ - 267, /* (366) anylist ::= anylist LP anylist RP */ - 267, /* (367) anylist ::= anylist ANY */ - 232, /* (368) with ::= */ + 180, /* (0) explain ::= EXPLAIN */ + 180, /* (1) explain ::= EXPLAIN QUERY PLAN */ + 179, /* (2) cmdx ::= cmd */ + 181, /* (3) cmd ::= BEGIN transtype trans_opt */ + 182, /* (4) transtype ::= */ + 182, /* (5) transtype ::= DEFERRED */ + 182, /* (6) transtype ::= IMMEDIATE */ + 182, /* (7) transtype ::= EXCLUSIVE */ + 181, /* (8) cmd ::= COMMIT|END trans_opt */ + 181, /* (9) cmd ::= ROLLBACK trans_opt */ + 181, /* (10) cmd ::= SAVEPOINT nm */ + 181, /* (11) cmd ::= RELEASE savepoint_opt nm */ + 181, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */ + 186, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */ + 188, /* (14) createkw ::= CREATE */ + 190, /* (15) ifnotexists ::= */ + 190, /* (16) ifnotexists ::= IF NOT EXISTS */ + 189, /* (17) temp ::= TEMP */ + 189, /* (18) temp ::= */ + 187, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_options */ + 187, /* (20) create_table_args ::= AS select */ + 194, /* (21) table_options ::= */ + 194, /* (22) table_options ::= WITHOUT nm */ + 196, /* (23) columnname ::= nm typetoken */ + 198, /* (24) typetoken ::= */ + 198, /* (25) typetoken ::= typename LP signed RP */ + 198, /* (26) typetoken ::= typename LP signed COMMA signed RP */ + 199, /* (27) typename ::= typename ID|STRING */ + 203, /* (28) scanpt ::= */ + 204, /* (29) ccons ::= CONSTRAINT nm */ + 204, /* (30) ccons ::= DEFAULT scanpt term scanpt */ + 204, /* (31) ccons ::= DEFAULT LP expr RP */ + 204, /* (32) ccons ::= DEFAULT PLUS term scanpt */ + 204, /* (33) ccons ::= DEFAULT MINUS term scanpt */ + 204, /* (34) ccons ::= DEFAULT scanpt ID|INDEXED */ + 204, /* (35) ccons ::= NOT NULL onconf */ + 204, /* (36) ccons ::= PRIMARY KEY sortorder onconf autoinc */ + 204, /* (37) ccons ::= UNIQUE onconf */ + 204, /* (38) ccons ::= CHECK LP expr RP */ + 204, /* (39) ccons ::= REFERENCES nm eidlist_opt refargs */ + 204, /* (40) ccons ::= defer_subclause */ + 204, /* (41) ccons ::= COLLATE ID|STRING */ + 209, /* (42) autoinc ::= */ + 209, /* (43) autoinc ::= AUTOINCR */ + 211, /* (44) refargs ::= */ + 211, /* (45) refargs ::= refargs refarg */ + 213, /* (46) refarg ::= MATCH nm */ + 213, /* (47) refarg ::= ON INSERT refact */ + 213, /* (48) refarg ::= ON DELETE refact */ + 213, /* (49) refarg ::= ON UPDATE refact */ + 214, /* (50) refact ::= SET NULL */ + 214, /* (51) refact ::= SET DEFAULT */ + 214, /* (52) refact ::= CASCADE */ + 214, /* (53) refact ::= RESTRICT */ + 214, /* (54) refact ::= NO ACTION */ + 212, /* (55) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ + 212, /* (56) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ + 215, /* (57) init_deferred_pred_opt ::= */ + 215, /* (58) init_deferred_pred_opt ::= INITIALLY DEFERRED */ + 215, /* (59) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ + 193, /* (60) conslist_opt ::= */ + 217, /* (61) tconscomma ::= COMMA */ + 218, /* (62) tcons ::= CONSTRAINT nm */ + 218, /* (63) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ + 218, /* (64) tcons ::= UNIQUE LP sortlist RP onconf */ + 218, /* (65) tcons ::= CHECK LP expr RP onconf */ + 218, /* (66) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ + 221, /* (67) defer_subclause_opt ::= */ + 207, /* (68) onconf ::= */ + 207, /* (69) onconf ::= ON CONFLICT resolvetype */ + 222, /* (70) orconf ::= */ + 222, /* (71) orconf ::= OR resolvetype */ + 223, /* (72) resolvetype ::= IGNORE */ + 223, /* (73) resolvetype ::= REPLACE */ + 181, /* (74) cmd ::= DROP TABLE ifexists fullname */ + 225, /* (75) ifexists ::= IF EXISTS */ + 225, /* (76) ifexists ::= */ + 181, /* (77) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ + 181, /* (78) cmd ::= DROP VIEW ifexists fullname */ + 181, /* (79) cmd ::= select */ + 195, /* (80) select ::= WITH wqlist selectnowith */ + 195, /* (81) select ::= WITH RECURSIVE wqlist selectnowith */ + 195, /* (82) select ::= selectnowith */ + 227, /* (83) selectnowith ::= selectnowith multiselect_op oneselect */ + 230, /* (84) multiselect_op ::= UNION */ + 230, /* (85) multiselect_op ::= UNION ALL */ + 230, /* (86) multiselect_op ::= EXCEPT|INTERSECT */ + 228, /* (87) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ + 228, /* (88) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ + 240, /* (89) values ::= VALUES LP nexprlist RP */ + 240, /* (90) values ::= values COMMA LP nexprlist RP */ + 231, /* (91) distinct ::= DISTINCT */ + 231, /* (92) distinct ::= ALL */ + 231, /* (93) distinct ::= */ + 242, /* (94) sclp ::= */ + 232, /* (95) selcollist ::= sclp scanpt expr scanpt as */ + 232, /* (96) selcollist ::= sclp scanpt STAR */ + 232, /* (97) selcollist ::= sclp scanpt nm DOT STAR */ + 243, /* (98) as ::= AS nm */ + 243, /* (99) as ::= */ + 233, /* (100) from ::= */ + 233, /* (101) from ::= FROM seltablist */ + 245, /* (102) stl_prefix ::= seltablist joinop */ + 245, /* (103) stl_prefix ::= */ + 244, /* (104) seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ + 244, /* (105) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */ + 244, /* (106) seltablist ::= stl_prefix LP select RP as on_opt using_opt */ + 244, /* (107) seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ + 191, /* (108) dbnm ::= */ + 191, /* (109) dbnm ::= DOT nm */ + 226, /* (110) fullname ::= nm */ + 226, /* (111) fullname ::= nm DOT nm */ + 251, /* (112) xfullname ::= nm */ + 251, /* (113) xfullname ::= nm DOT nm */ + 251, /* (114) xfullname ::= nm DOT nm AS nm */ + 251, /* (115) xfullname ::= nm AS nm */ + 246, /* (116) joinop ::= COMMA|JOIN */ + 246, /* (117) joinop ::= JOIN_KW JOIN */ + 246, /* (118) joinop ::= JOIN_KW nm JOIN */ + 246, /* (119) joinop ::= JOIN_KW nm nm JOIN */ + 248, /* (120) on_opt ::= ON expr */ + 248, /* (121) on_opt ::= */ + 247, /* (122) indexed_opt ::= */ + 247, /* (123) indexed_opt ::= INDEXED BY nm */ + 247, /* (124) indexed_opt ::= NOT INDEXED */ + 249, /* (125) using_opt ::= USING LP idlist RP */ + 249, /* (126) using_opt ::= */ + 237, /* (127) orderby_opt ::= */ + 237, /* (128) orderby_opt ::= ORDER BY sortlist */ + 219, /* (129) sortlist ::= sortlist COMMA expr sortorder */ + 219, /* (130) sortlist ::= expr sortorder */ + 208, /* (131) sortorder ::= ASC */ + 208, /* (132) sortorder ::= DESC */ + 208, /* (133) sortorder ::= */ + 235, /* (134) groupby_opt ::= */ + 235, /* (135) groupby_opt ::= GROUP BY nexprlist */ + 236, /* (136) having_opt ::= */ + 236, /* (137) having_opt ::= HAVING expr */ + 238, /* (138) limit_opt ::= */ + 238, /* (139) limit_opt ::= LIMIT expr */ + 238, /* (140) limit_opt ::= LIMIT expr OFFSET expr */ + 238, /* (141) limit_opt ::= LIMIT expr COMMA expr */ + 181, /* (142) cmd ::= with DELETE FROM xfullname indexed_opt where_opt */ + 234, /* (143) where_opt ::= */ + 234, /* (144) where_opt ::= WHERE expr */ + 181, /* (145) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt */ + 254, /* (146) setlist ::= setlist COMMA nm EQ expr */ + 254, /* (147) setlist ::= setlist COMMA LP idlist RP EQ expr */ + 254, /* (148) setlist ::= nm EQ expr */ + 254, /* (149) setlist ::= LP idlist RP EQ expr */ + 181, /* (150) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ + 181, /* (151) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */ + 257, /* (152) upsert ::= */ + 257, /* (153) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */ + 257, /* (154) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */ + 257, /* (155) upsert ::= ON CONFLICT DO NOTHING */ + 255, /* (156) insert_cmd ::= INSERT orconf */ + 255, /* (157) insert_cmd ::= REPLACE */ + 256, /* (158) idlist_opt ::= */ + 256, /* (159) idlist_opt ::= LP idlist RP */ + 252, /* (160) idlist ::= idlist COMMA nm */ + 252, /* (161) idlist ::= nm */ + 206, /* (162) expr ::= LP expr RP */ + 206, /* (163) expr ::= ID|INDEXED */ + 206, /* (164) expr ::= JOIN_KW */ + 206, /* (165) expr ::= nm DOT nm */ + 206, /* (166) expr ::= nm DOT nm DOT nm */ + 205, /* (167) term ::= NULL|FLOAT|BLOB */ + 205, /* (168) term ::= STRING */ + 205, /* (169) term ::= INTEGER */ + 206, /* (170) expr ::= VARIABLE */ + 206, /* (171) expr ::= expr COLLATE ID|STRING */ + 206, /* (172) expr ::= CAST LP expr AS typetoken RP */ + 206, /* (173) expr ::= ID|INDEXED LP distinct exprlist RP */ + 206, /* (174) expr ::= ID|INDEXED LP STAR RP */ + 206, /* (175) expr ::= ID|INDEXED LP distinct exprlist RP over_clause */ + 206, /* (176) expr ::= ID|INDEXED LP STAR RP over_clause */ + 205, /* (177) term ::= CTIME_KW */ + 206, /* (178) expr ::= LP nexprlist COMMA expr RP */ + 206, /* (179) expr ::= expr AND expr */ + 206, /* (180) expr ::= expr OR expr */ + 206, /* (181) expr ::= expr LT|GT|GE|LE expr */ + 206, /* (182) expr ::= expr EQ|NE expr */ + 206, /* (183) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ + 206, /* (184) expr ::= expr PLUS|MINUS expr */ + 206, /* (185) expr ::= expr STAR|SLASH|REM expr */ + 206, /* (186) expr ::= expr CONCAT expr */ + 259, /* (187) likeop ::= NOT LIKE_KW|MATCH */ + 206, /* (188) expr ::= expr likeop expr */ + 206, /* (189) expr ::= expr likeop expr ESCAPE expr */ + 206, /* (190) expr ::= expr ISNULL|NOTNULL */ + 206, /* (191) expr ::= expr NOT NULL */ + 206, /* (192) expr ::= expr IS expr */ + 206, /* (193) expr ::= expr IS NOT expr */ + 206, /* (194) expr ::= NOT expr */ + 206, /* (195) expr ::= BITNOT expr */ + 206, /* (196) expr ::= PLUS|MINUS expr */ + 260, /* (197) between_op ::= BETWEEN */ + 260, /* (198) between_op ::= NOT BETWEEN */ + 206, /* (199) expr ::= expr between_op expr AND expr */ + 261, /* (200) in_op ::= IN */ + 261, /* (201) in_op ::= NOT IN */ + 206, /* (202) expr ::= expr in_op LP exprlist RP */ + 206, /* (203) expr ::= LP select RP */ + 206, /* (204) expr ::= expr in_op LP select RP */ + 206, /* (205) expr ::= expr in_op nm dbnm paren_exprlist */ + 206, /* (206) expr ::= EXISTS LP select RP */ + 206, /* (207) expr ::= CASE case_operand case_exprlist case_else END */ + 264, /* (208) case_exprlist ::= case_exprlist WHEN expr THEN expr */ + 264, /* (209) case_exprlist ::= WHEN expr THEN expr */ + 265, /* (210) case_else ::= ELSE expr */ + 265, /* (211) case_else ::= */ + 263, /* (212) case_operand ::= expr */ + 263, /* (213) case_operand ::= */ + 250, /* (214) exprlist ::= */ + 241, /* (215) nexprlist ::= nexprlist COMMA expr */ + 241, /* (216) nexprlist ::= expr */ + 262, /* (217) paren_exprlist ::= */ + 262, /* (218) paren_exprlist ::= LP exprlist RP */ + 181, /* (219) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ + 266, /* (220) uniqueflag ::= UNIQUE */ + 266, /* (221) uniqueflag ::= */ + 210, /* (222) eidlist_opt ::= */ + 210, /* (223) eidlist_opt ::= LP eidlist RP */ + 220, /* (224) eidlist ::= eidlist COMMA nm collate sortorder */ + 220, /* (225) eidlist ::= nm collate sortorder */ + 267, /* (226) collate ::= */ + 267, /* (227) collate ::= COLLATE ID|STRING */ + 181, /* (228) cmd ::= DROP INDEX ifexists fullname */ + 181, /* (229) cmd ::= VACUUM vinto */ + 181, /* (230) cmd ::= VACUUM nm vinto */ + 268, /* (231) vinto ::= INTO expr */ + 268, /* (232) vinto ::= */ + 181, /* (233) cmd ::= PRAGMA nm dbnm */ + 181, /* (234) cmd ::= PRAGMA nm dbnm EQ nmnum */ + 181, /* (235) cmd ::= PRAGMA nm dbnm LP nmnum RP */ + 181, /* (236) cmd ::= PRAGMA nm dbnm EQ minus_num */ + 181, /* (237) cmd ::= PRAGMA nm dbnm LP minus_num RP */ + 201, /* (238) plus_num ::= PLUS INTEGER|FLOAT */ + 202, /* (239) minus_num ::= MINUS INTEGER|FLOAT */ + 181, /* (240) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ + 270, /* (241) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + 272, /* (242) trigger_time ::= BEFORE|AFTER */ + 272, /* (243) trigger_time ::= INSTEAD OF */ + 272, /* (244) trigger_time ::= */ + 273, /* (245) trigger_event ::= DELETE|INSERT */ + 273, /* (246) trigger_event ::= UPDATE */ + 273, /* (247) trigger_event ::= UPDATE OF idlist */ + 275, /* (248) when_clause ::= */ + 275, /* (249) when_clause ::= WHEN expr */ + 271, /* (250) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + 271, /* (251) trigger_cmd_list ::= trigger_cmd SEMI */ + 277, /* (252) trnm ::= nm DOT nm */ + 278, /* (253) tridxby ::= INDEXED BY nm */ + 278, /* (254) tridxby ::= NOT INDEXED */ + 276, /* (255) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */ + 276, /* (256) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ + 276, /* (257) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ + 276, /* (258) trigger_cmd ::= scanpt select scanpt */ + 206, /* (259) expr ::= RAISE LP IGNORE RP */ + 206, /* (260) expr ::= RAISE LP raisetype COMMA nm RP */ + 224, /* (261) raisetype ::= ROLLBACK */ + 224, /* (262) raisetype ::= ABORT */ + 224, /* (263) raisetype ::= FAIL */ + 181, /* (264) cmd ::= DROP TRIGGER ifexists fullname */ + 181, /* (265) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + 181, /* (266) cmd ::= DETACH database_kw_opt expr */ + 280, /* (267) key_opt ::= */ + 280, /* (268) key_opt ::= KEY expr */ + 181, /* (269) cmd ::= REINDEX */ + 181, /* (270) cmd ::= REINDEX nm dbnm */ + 181, /* (271) cmd ::= ANALYZE */ + 181, /* (272) cmd ::= ANALYZE nm dbnm */ + 181, /* (273) cmd ::= ALTER TABLE fullname RENAME TO nm */ + 181, /* (274) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + 281, /* (275) add_column_fullname ::= fullname */ + 181, /* (276) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ + 181, /* (277) cmd ::= create_vtab */ + 181, /* (278) cmd ::= create_vtab LP vtabarglist RP */ + 283, /* (279) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + 285, /* (280) vtabarg ::= */ + 286, /* (281) vtabargtoken ::= ANY */ + 286, /* (282) vtabargtoken ::= lp anylist RP */ + 287, /* (283) lp ::= LP */ + 253, /* (284) with ::= WITH wqlist */ + 253, /* (285) with ::= WITH RECURSIVE wqlist */ + 229, /* (286) wqlist ::= nm eidlist_opt AS LP select RP */ + 229, /* (287) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */ + 289, /* (288) windowdefn_list ::= windowdefn */ + 289, /* (289) windowdefn_list ::= windowdefn_list COMMA windowdefn */ + 290, /* (290) windowdefn ::= nm AS LP window RP */ + 291, /* (291) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + 291, /* (292) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + 291, /* (293) window ::= ORDER BY sortlist frame_opt */ + 291, /* (294) window ::= nm ORDER BY sortlist frame_opt */ + 291, /* (295) window ::= frame_opt */ + 291, /* (296) window ::= nm frame_opt */ + 292, /* (297) frame_opt ::= */ + 292, /* (298) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ + 292, /* (299) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ + 295, /* (300) range_or_rows ::= RANGE|ROWS|GROUPS */ + 297, /* (301) frame_bound_s ::= frame_bound */ + 297, /* (302) frame_bound_s ::= UNBOUNDED PRECEDING */ + 298, /* (303) frame_bound_e ::= frame_bound */ + 298, /* (304) frame_bound_e ::= UNBOUNDED FOLLOWING */ + 296, /* (305) frame_bound ::= expr PRECEDING|FOLLOWING */ + 296, /* (306) frame_bound ::= CURRENT ROW */ + 299, /* (307) frame_exclude_opt ::= */ + 299, /* (308) frame_exclude_opt ::= EXCLUDE frame_exclude */ + 300, /* (309) frame_exclude ::= NO OTHERS */ + 300, /* (310) frame_exclude ::= CURRENT ROW */ + 300, /* (311) frame_exclude ::= GROUP|TIES */ + 239, /* (312) window_clause ::= WINDOW windowdefn_list */ + 258, /* (313) over_clause ::= filter_opt OVER LP window RP */ + 258, /* (314) over_clause ::= filter_opt OVER nm */ + 294, /* (315) filter_opt ::= */ + 294, /* (316) filter_opt ::= FILTER LP WHERE expr RP */ + 176, /* (317) input ::= cmdlist */ + 177, /* (318) cmdlist ::= cmdlist ecmd */ + 177, /* (319) cmdlist ::= ecmd */ + 178, /* (320) ecmd ::= SEMI */ + 178, /* (321) ecmd ::= cmdx SEMI */ + 178, /* (322) ecmd ::= explain cmdx */ + 183, /* (323) trans_opt ::= */ + 183, /* (324) trans_opt ::= TRANSACTION */ + 183, /* (325) trans_opt ::= TRANSACTION nm */ + 185, /* (326) savepoint_opt ::= SAVEPOINT */ + 185, /* (327) savepoint_opt ::= */ + 181, /* (328) cmd ::= create_table create_table_args */ + 192, /* (329) columnlist ::= columnlist COMMA columnname carglist */ + 192, /* (330) columnlist ::= columnname carglist */ + 184, /* (331) nm ::= ID|INDEXED */ + 184, /* (332) nm ::= STRING */ + 184, /* (333) nm ::= JOIN_KW */ + 198, /* (334) typetoken ::= typename */ + 199, /* (335) typename ::= ID|STRING */ + 200, /* (336) signed ::= plus_num */ + 200, /* (337) signed ::= minus_num */ + 197, /* (338) carglist ::= carglist ccons */ + 197, /* (339) carglist ::= */ + 204, /* (340) ccons ::= NULL onconf */ + 193, /* (341) conslist_opt ::= COMMA conslist */ + 216, /* (342) conslist ::= conslist tconscomma tcons */ + 216, /* (343) conslist ::= tcons */ + 217, /* (344) tconscomma ::= */ + 221, /* (345) defer_subclause_opt ::= defer_subclause */ + 223, /* (346) resolvetype ::= raisetype */ + 227, /* (347) selectnowith ::= oneselect */ + 228, /* (348) oneselect ::= values */ + 242, /* (349) sclp ::= selcollist COMMA */ + 243, /* (350) as ::= ID|STRING */ + 206, /* (351) expr ::= term */ + 259, /* (352) likeop ::= LIKE_KW|MATCH */ + 250, /* (353) exprlist ::= nexprlist */ + 269, /* (354) nmnum ::= plus_num */ + 269, /* (355) nmnum ::= nm */ + 269, /* (356) nmnum ::= ON */ + 269, /* (357) nmnum ::= DELETE */ + 269, /* (358) nmnum ::= DEFAULT */ + 201, /* (359) plus_num ::= INTEGER|FLOAT */ + 274, /* (360) foreach_clause ::= */ + 274, /* (361) foreach_clause ::= FOR EACH ROW */ + 277, /* (362) trnm ::= nm */ + 278, /* (363) tridxby ::= */ + 279, /* (364) database_kw_opt ::= DATABASE */ + 279, /* (365) database_kw_opt ::= */ + 282, /* (366) kwcolumn_opt ::= */ + 282, /* (367) kwcolumn_opt ::= COLUMNKW */ + 284, /* (368) vtabarglist ::= vtabarg */ + 284, /* (369) vtabarglist ::= vtabarglist COMMA vtabarg */ + 285, /* (370) vtabarg ::= vtabarg vtabargtoken */ + 288, /* (371) anylist ::= */ + 288, /* (372) anylist ::= anylist LP anylist RP */ + 288, /* (373) anylist ::= anylist ANY */ + 253, /* (374) with ::= */ }; /* For rule J, yyRuleInfoNRhs[J] contains the negative of the number @@ -150264,85 +151140,91 @@ static const signed char yyRuleInfoNRhs[] = { -8, /* (287) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */ -1, /* (288) windowdefn_list ::= windowdefn */ -3, /* (289) windowdefn_list ::= windowdefn_list COMMA windowdefn */ - -3, /* (290) windowdefn ::= nm AS window */ - -5, /* (291) window ::= LP part_opt orderby_opt frame_opt RP */ - -3, /* (292) part_opt ::= PARTITION BY nexprlist */ - 0, /* (293) part_opt ::= */ - 0, /* (294) frame_opt ::= */ - -2, /* (295) frame_opt ::= range_or_rows frame_bound_s */ - -5, /* (296) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e */ - -1, /* (297) range_or_rows ::= RANGE */ - -1, /* (298) range_or_rows ::= ROWS */ - -1, /* (299) frame_bound_s ::= frame_bound */ - -2, /* (300) frame_bound_s ::= UNBOUNDED PRECEDING */ - -1, /* (301) frame_bound_e ::= frame_bound */ - -2, /* (302) frame_bound_e ::= UNBOUNDED FOLLOWING */ - -2, /* (303) frame_bound ::= expr PRECEDING */ - -2, /* (304) frame_bound ::= CURRENT ROW */ - -2, /* (305) frame_bound ::= expr FOLLOWING */ - -2, /* (306) window_clause ::= WINDOW windowdefn_list */ - -3, /* (307) over_clause ::= filter_opt OVER window */ - -3, /* (308) over_clause ::= filter_opt OVER nm */ - 0, /* (309) filter_opt ::= */ - -5, /* (310) filter_opt ::= FILTER LP WHERE expr RP */ - -1, /* (311) input ::= cmdlist */ - -2, /* (312) cmdlist ::= cmdlist ecmd */ - -1, /* (313) cmdlist ::= ecmd */ - -1, /* (314) ecmd ::= SEMI */ - -2, /* (315) ecmd ::= cmdx SEMI */ - -2, /* (316) ecmd ::= explain cmdx */ - 0, /* (317) trans_opt ::= */ - -1, /* (318) trans_opt ::= TRANSACTION */ - -2, /* (319) trans_opt ::= TRANSACTION nm */ - -1, /* (320) savepoint_opt ::= SAVEPOINT */ - 0, /* (321) savepoint_opt ::= */ - -2, /* (322) cmd ::= create_table create_table_args */ - -4, /* (323) columnlist ::= columnlist COMMA columnname carglist */ - -2, /* (324) columnlist ::= columnname carglist */ - -1, /* (325) nm ::= ID|INDEXED */ - -1, /* (326) nm ::= STRING */ - -1, /* (327) nm ::= JOIN_KW */ - -1, /* (328) typetoken ::= typename */ - -1, /* (329) typename ::= ID|STRING */ - -1, /* (330) signed ::= plus_num */ - -1, /* (331) signed ::= minus_num */ - -2, /* (332) carglist ::= carglist ccons */ - 0, /* (333) carglist ::= */ - -2, /* (334) ccons ::= NULL onconf */ - -2, /* (335) conslist_opt ::= COMMA conslist */ - -3, /* (336) conslist ::= conslist tconscomma tcons */ - -1, /* (337) conslist ::= tcons */ - 0, /* (338) tconscomma ::= */ - -1, /* (339) defer_subclause_opt ::= defer_subclause */ - -1, /* (340) resolvetype ::= raisetype */ - -1, /* (341) selectnowith ::= oneselect */ - -1, /* (342) oneselect ::= values */ - -2, /* (343) sclp ::= selcollist COMMA */ - -1, /* (344) as ::= ID|STRING */ - -1, /* (345) expr ::= term */ - -1, /* (346) likeop ::= LIKE_KW|MATCH */ - -1, /* (347) exprlist ::= nexprlist */ - -1, /* (348) nmnum ::= plus_num */ - -1, /* (349) nmnum ::= nm */ - -1, /* (350) nmnum ::= ON */ - -1, /* (351) nmnum ::= DELETE */ - -1, /* (352) nmnum ::= DEFAULT */ - -1, /* (353) plus_num ::= INTEGER|FLOAT */ - 0, /* (354) foreach_clause ::= */ - -3, /* (355) foreach_clause ::= FOR EACH ROW */ - -1, /* (356) trnm ::= nm */ - 0, /* (357) tridxby ::= */ - -1, /* (358) database_kw_opt ::= DATABASE */ - 0, /* (359) database_kw_opt ::= */ - 0, /* (360) kwcolumn_opt ::= */ - -1, /* (361) kwcolumn_opt ::= COLUMNKW */ - -1, /* (362) vtabarglist ::= vtabarg */ - -3, /* (363) vtabarglist ::= vtabarglist COMMA vtabarg */ - -2, /* (364) vtabarg ::= vtabarg vtabargtoken */ - 0, /* (365) anylist ::= */ - -4, /* (366) anylist ::= anylist LP anylist RP */ - -2, /* (367) anylist ::= anylist ANY */ - 0, /* (368) with ::= */ + -5, /* (290) windowdefn ::= nm AS LP window RP */ + -5, /* (291) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + -6, /* (292) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + -4, /* (293) window ::= ORDER BY sortlist frame_opt */ + -5, /* (294) window ::= nm ORDER BY sortlist frame_opt */ + -1, /* (295) window ::= frame_opt */ + -2, /* (296) window ::= nm frame_opt */ + 0, /* (297) frame_opt ::= */ + -3, /* (298) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ + -6, /* (299) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ + -1, /* (300) range_or_rows ::= RANGE|ROWS|GROUPS */ + -1, /* (301) frame_bound_s ::= frame_bound */ + -2, /* (302) frame_bound_s ::= UNBOUNDED PRECEDING */ + -1, /* (303) frame_bound_e ::= frame_bound */ + -2, /* (304) frame_bound_e ::= UNBOUNDED FOLLOWING */ + -2, /* (305) frame_bound ::= expr PRECEDING|FOLLOWING */ + -2, /* (306) frame_bound ::= CURRENT ROW */ + 0, /* (307) frame_exclude_opt ::= */ + -2, /* (308) frame_exclude_opt ::= EXCLUDE frame_exclude */ + -2, /* (309) frame_exclude ::= NO OTHERS */ + -2, /* (310) frame_exclude ::= CURRENT ROW */ + -1, /* (311) frame_exclude ::= GROUP|TIES */ + -2, /* (312) window_clause ::= WINDOW windowdefn_list */ + -5, /* (313) over_clause ::= filter_opt OVER LP window RP */ + -3, /* (314) over_clause ::= filter_opt OVER nm */ + 0, /* (315) filter_opt ::= */ + -5, /* (316) filter_opt ::= FILTER LP WHERE expr RP */ + -1, /* (317) input ::= cmdlist */ + -2, /* (318) cmdlist ::= cmdlist ecmd */ + -1, /* (319) cmdlist ::= ecmd */ + -1, /* (320) ecmd ::= SEMI */ + -2, /* (321) ecmd ::= cmdx SEMI */ + -2, /* (322) ecmd ::= explain cmdx */ + 0, /* (323) trans_opt ::= */ + -1, /* (324) trans_opt ::= TRANSACTION */ + -2, /* (325) trans_opt ::= TRANSACTION nm */ + -1, /* (326) savepoint_opt ::= SAVEPOINT */ + 0, /* (327) savepoint_opt ::= */ + -2, /* (328) cmd ::= create_table create_table_args */ + -4, /* (329) columnlist ::= columnlist COMMA columnname carglist */ + -2, /* (330) columnlist ::= columnname carglist */ + -1, /* (331) nm ::= ID|INDEXED */ + -1, /* (332) nm ::= STRING */ + -1, /* (333) nm ::= JOIN_KW */ + -1, /* (334) typetoken ::= typename */ + -1, /* (335) typename ::= ID|STRING */ + -1, /* (336) signed ::= plus_num */ + -1, /* (337) signed ::= minus_num */ + -2, /* (338) carglist ::= carglist ccons */ + 0, /* (339) carglist ::= */ + -2, /* (340) ccons ::= NULL onconf */ + -2, /* (341) conslist_opt ::= COMMA conslist */ + -3, /* (342) conslist ::= conslist tconscomma tcons */ + -1, /* (343) conslist ::= tcons */ + 0, /* (344) tconscomma ::= */ + -1, /* (345) defer_subclause_opt ::= defer_subclause */ + -1, /* (346) resolvetype ::= raisetype */ + -1, /* (347) selectnowith ::= oneselect */ + -1, /* (348) oneselect ::= values */ + -2, /* (349) sclp ::= selcollist COMMA */ + -1, /* (350) as ::= ID|STRING */ + -1, /* (351) expr ::= term */ + -1, /* (352) likeop ::= LIKE_KW|MATCH */ + -1, /* (353) exprlist ::= nexprlist */ + -1, /* (354) nmnum ::= plus_num */ + -1, /* (355) nmnum ::= nm */ + -1, /* (356) nmnum ::= ON */ + -1, /* (357) nmnum ::= DELETE */ + -1, /* (358) nmnum ::= DEFAULT */ + -1, /* (359) plus_num ::= INTEGER|FLOAT */ + 0, /* (360) foreach_clause ::= */ + -3, /* (361) foreach_clause ::= FOR EACH ROW */ + -1, /* (362) trnm ::= nm */ + 0, /* (363) tridxby ::= */ + -1, /* (364) database_kw_opt ::= DATABASE */ + 0, /* (365) database_kw_opt ::= */ + 0, /* (366) kwcolumn_opt ::= */ + -1, /* (367) kwcolumn_opt ::= COLUMNKW */ + -1, /* (368) vtabarglist ::= vtabarg */ + -3, /* (369) vtabarglist ::= vtabarglist COMMA vtabarg */ + -2, /* (370) vtabarg ::= vtabarg vtabargtoken */ + 0, /* (371) anylist ::= */ + -4, /* (372) anylist ::= anylist LP anylist RP */ + -2, /* (373) anylist ::= anylist ANY */ + 0, /* (374) with ::= */ }; static void yy_accept(yyParser*); /* Forward Declaration */ @@ -150439,15 +151321,16 @@ static YYACTIONTYPE yy_reduce( { sqlite3FinishCoding(pParse); } break; case 3: /* cmd ::= BEGIN transtype trans_opt */ -{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy96);} +{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy494);} break; case 4: /* transtype ::= */ -{yymsp[1].minor.yy96 = TK_DEFERRED;} +{yymsp[1].minor.yy494 = TK_DEFERRED;} break; case 5: /* transtype ::= DEFERRED */ case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6); case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7); -{yymsp[0].minor.yy96 = yymsp[0].major; /*A-overwrites-X*/} + case 300: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==300); +{yymsp[0].minor.yy494 = yymsp[0].major; /*A-overwrites-X*/} break; case 8: /* cmd ::= COMMIT|END trans_opt */ case 9: /* cmd ::= ROLLBACK trans_opt */ yytestcase(yyruleno==9); @@ -150470,7 +151353,7 @@ static YYACTIONTYPE yy_reduce( break; case 13: /* create_table ::= createkw temp TABLE ifnotexists nm dbnm */ { - sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy96,0,0,yymsp[-2].minor.yy96); + sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy494,0,0,yymsp[-2].minor.yy494); } break; case 14: /* createkw ::= CREATE */ @@ -150485,32 +151368,32 @@ static YYACTIONTYPE yy_reduce( case 76: /* ifexists ::= */ yytestcase(yyruleno==76); case 93: /* distinct ::= */ yytestcase(yyruleno==93); case 226: /* collate ::= */ yytestcase(yyruleno==226); -{yymsp[1].minor.yy96 = 0;} +{yymsp[1].minor.yy494 = 0;} break; case 16: /* ifnotexists ::= IF NOT EXISTS */ -{yymsp[-2].minor.yy96 = 1;} +{yymsp[-2].minor.yy494 = 1;} break; case 17: /* temp ::= TEMP */ case 43: /* autoinc ::= AUTOINCR */ yytestcase(yyruleno==43); -{yymsp[0].minor.yy96 = 1;} +{yymsp[0].minor.yy494 = 1;} break; case 19: /* create_table_args ::= LP columnlist conslist_opt RP table_options */ { - sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy96,0); + sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy494,0); } break; case 20: /* create_table_args ::= AS select */ { - sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy423); - sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy423); + sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy457); + sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy457); } break; case 22: /* table_options ::= WITHOUT nm */ { if( yymsp[0].minor.yy0.n==5 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"rowid",5)==0 ){ - yymsp[-1].minor.yy96 = TF_WithoutRowid | TF_NoVisibleRowid; + yymsp[-1].minor.yy494 = TF_WithoutRowid | TF_NoVisibleRowid; }else{ - yymsp[-1].minor.yy96 = 0; + yymsp[-1].minor.yy494 = 0; sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z); } } @@ -150539,7 +151422,7 @@ static YYACTIONTYPE yy_reduce( case 28: /* scanpt ::= */ { assert( yyLookahead!=YYNOCODE ); - yymsp[1].minor.yy464 = yyLookaheadToken.z; + yymsp[1].minor.yy294 = yyLookaheadToken.z; } break; case 29: /* ccons ::= CONSTRAINT nm */ @@ -150547,18 +151430,18 @@ static YYACTIONTYPE yy_reduce( {pParse->constraintName = yymsp[0].minor.yy0;} break; case 30: /* ccons ::= DEFAULT scanpt term scanpt */ -{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy490,yymsp[-2].minor.yy464,yymsp[0].minor.yy464);} +{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy524,yymsp[-2].minor.yy294,yymsp[0].minor.yy294);} break; case 31: /* ccons ::= DEFAULT LP expr RP */ -{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy490,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);} +{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy524,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);} break; case 32: /* ccons ::= DEFAULT PLUS term scanpt */ -{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy490,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy464);} +{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy524,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy294);} break; case 33: /* ccons ::= DEFAULT MINUS term scanpt */ { - Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[-1].minor.yy490, 0); - sqlite3AddDefaultValue(pParse,p,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy464); + Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[-1].minor.yy524, 0); + sqlite3AddDefaultValue(pParse,p,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy294); } break; case 34: /* ccons ::= DEFAULT scanpt ID|INDEXED */ @@ -150572,170 +151455,170 @@ static YYACTIONTYPE yy_reduce( } break; case 35: /* ccons ::= NOT NULL onconf */ -{sqlite3AddNotNull(pParse, yymsp[0].minor.yy96);} +{sqlite3AddNotNull(pParse, yymsp[0].minor.yy494);} break; case 36: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */ -{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy96,yymsp[0].minor.yy96,yymsp[-2].minor.yy96);} +{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy494,yymsp[0].minor.yy494,yymsp[-2].minor.yy494);} break; case 37: /* ccons ::= UNIQUE onconf */ -{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy96,0,0,0,0, +{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy494,0,0,0,0, SQLITE_IDXTYPE_UNIQUE);} break; case 38: /* ccons ::= CHECK LP expr RP */ -{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy490);} +{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy524);} break; case 39: /* ccons ::= REFERENCES nm eidlist_opt refargs */ -{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy42,yymsp[0].minor.yy96);} +{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy434,yymsp[0].minor.yy494);} break; case 40: /* ccons ::= defer_subclause */ -{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy96);} +{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy494);} break; case 41: /* ccons ::= COLLATE ID|STRING */ {sqlite3AddCollateType(pParse, &yymsp[0].minor.yy0);} break; case 44: /* refargs ::= */ -{ yymsp[1].minor.yy96 = OE_None*0x0101; /* EV: R-19803-45884 */} +{ yymsp[1].minor.yy494 = OE_None*0x0101; /* EV: R-19803-45884 */} break; case 45: /* refargs ::= refargs refarg */ -{ yymsp[-1].minor.yy96 = (yymsp[-1].minor.yy96 & ~yymsp[0].minor.yy367.mask) | yymsp[0].minor.yy367.value; } +{ yymsp[-1].minor.yy494 = (yymsp[-1].minor.yy494 & ~yymsp[0].minor.yy355.mask) | yymsp[0].minor.yy355.value; } break; case 46: /* refarg ::= MATCH nm */ -{ yymsp[-1].minor.yy367.value = 0; yymsp[-1].minor.yy367.mask = 0x000000; } +{ yymsp[-1].minor.yy355.value = 0; yymsp[-1].minor.yy355.mask = 0x000000; } break; case 47: /* refarg ::= ON INSERT refact */ -{ yymsp[-2].minor.yy367.value = 0; yymsp[-2].minor.yy367.mask = 0x000000; } +{ yymsp[-2].minor.yy355.value = 0; yymsp[-2].minor.yy355.mask = 0x000000; } break; case 48: /* refarg ::= ON DELETE refact */ -{ yymsp[-2].minor.yy367.value = yymsp[0].minor.yy96; yymsp[-2].minor.yy367.mask = 0x0000ff; } +{ yymsp[-2].minor.yy355.value = yymsp[0].minor.yy494; yymsp[-2].minor.yy355.mask = 0x0000ff; } break; case 49: /* refarg ::= ON UPDATE refact */ -{ yymsp[-2].minor.yy367.value = yymsp[0].minor.yy96<<8; yymsp[-2].minor.yy367.mask = 0x00ff00; } +{ yymsp[-2].minor.yy355.value = yymsp[0].minor.yy494<<8; yymsp[-2].minor.yy355.mask = 0x00ff00; } break; case 50: /* refact ::= SET NULL */ -{ yymsp[-1].minor.yy96 = OE_SetNull; /* EV: R-33326-45252 */} +{ yymsp[-1].minor.yy494 = OE_SetNull; /* EV: R-33326-45252 */} break; case 51: /* refact ::= SET DEFAULT */ -{ yymsp[-1].minor.yy96 = OE_SetDflt; /* EV: R-33326-45252 */} +{ yymsp[-1].minor.yy494 = OE_SetDflt; /* EV: R-33326-45252 */} break; case 52: /* refact ::= CASCADE */ -{ yymsp[0].minor.yy96 = OE_Cascade; /* EV: R-33326-45252 */} +{ yymsp[0].minor.yy494 = OE_Cascade; /* EV: R-33326-45252 */} break; case 53: /* refact ::= RESTRICT */ -{ yymsp[0].minor.yy96 = OE_Restrict; /* EV: R-33326-45252 */} +{ yymsp[0].minor.yy494 = OE_Restrict; /* EV: R-33326-45252 */} break; case 54: /* refact ::= NO ACTION */ -{ yymsp[-1].minor.yy96 = OE_None; /* EV: R-33326-45252 */} +{ yymsp[-1].minor.yy494 = OE_None; /* EV: R-33326-45252 */} break; case 55: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ -{yymsp[-2].minor.yy96 = 0;} +{yymsp[-2].minor.yy494 = 0;} break; case 56: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ case 71: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==71); case 156: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==156); -{yymsp[-1].minor.yy96 = yymsp[0].minor.yy96;} +{yymsp[-1].minor.yy494 = yymsp[0].minor.yy494;} break; case 58: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */ case 75: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==75); case 198: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==198); case 201: /* in_op ::= NOT IN */ yytestcase(yyruleno==201); case 227: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==227); -{yymsp[-1].minor.yy96 = 1;} +{yymsp[-1].minor.yy494 = 1;} break; case 59: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ -{yymsp[-1].minor.yy96 = 0;} +{yymsp[-1].minor.yy494 = 0;} break; case 61: /* tconscomma ::= COMMA */ {pParse->constraintName.n = 0;} break; case 63: /* tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ -{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy42,yymsp[0].minor.yy96,yymsp[-2].minor.yy96,0);} +{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy434,yymsp[0].minor.yy494,yymsp[-2].minor.yy494,0);} break; case 64: /* tcons ::= UNIQUE LP sortlist RP onconf */ -{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy42,yymsp[0].minor.yy96,0,0,0,0, +{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy434,yymsp[0].minor.yy494,0,0,0,0, SQLITE_IDXTYPE_UNIQUE);} break; case 65: /* tcons ::= CHECK LP expr RP onconf */ -{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy490);} +{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy524);} break; case 66: /* tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ { - sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy42, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy42, yymsp[-1].minor.yy96); - sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy96); + sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy434, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy434, yymsp[-1].minor.yy494); + sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy494); } break; case 68: /* onconf ::= */ case 70: /* orconf ::= */ yytestcase(yyruleno==70); -{yymsp[1].minor.yy96 = OE_Default;} +{yymsp[1].minor.yy494 = OE_Default;} break; case 69: /* onconf ::= ON CONFLICT resolvetype */ -{yymsp[-2].minor.yy96 = yymsp[0].minor.yy96;} +{yymsp[-2].minor.yy494 = yymsp[0].minor.yy494;} break; case 72: /* resolvetype ::= IGNORE */ -{yymsp[0].minor.yy96 = OE_Ignore;} +{yymsp[0].minor.yy494 = OE_Ignore;} break; case 73: /* resolvetype ::= REPLACE */ case 157: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==157); -{yymsp[0].minor.yy96 = OE_Replace;} +{yymsp[0].minor.yy494 = OE_Replace;} break; case 74: /* cmd ::= DROP TABLE ifexists fullname */ { - sqlite3DropTable(pParse, yymsp[0].minor.yy167, 0, yymsp[-1].minor.yy96); + sqlite3DropTable(pParse, yymsp[0].minor.yy483, 0, yymsp[-1].minor.yy494); } break; case 77: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ { - sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy42, yymsp[0].minor.yy423, yymsp[-7].minor.yy96, yymsp[-5].minor.yy96); + sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy434, yymsp[0].minor.yy457, yymsp[-7].minor.yy494, yymsp[-5].minor.yy494); } break; case 78: /* cmd ::= DROP VIEW ifexists fullname */ { - sqlite3DropTable(pParse, yymsp[0].minor.yy167, 1, yymsp[-1].minor.yy96); + sqlite3DropTable(pParse, yymsp[0].minor.yy483, 1, yymsp[-1].minor.yy494); } break; case 79: /* cmd ::= select */ { SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0}; - sqlite3Select(pParse, yymsp[0].minor.yy423, &dest); - sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy423); + sqlite3Select(pParse, yymsp[0].minor.yy457, &dest); + sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy457); } break; case 80: /* select ::= WITH wqlist selectnowith */ { - Select *p = yymsp[0].minor.yy423; + Select *p = yymsp[0].minor.yy457; if( p ){ - p->pWith = yymsp[-1].minor.yy499; + p->pWith = yymsp[-1].minor.yy59; parserDoubleLinkSelect(pParse, p); }else{ - sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy499); + sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy59); } - yymsp[-2].minor.yy423 = p; + yymsp[-2].minor.yy457 = p; } break; case 81: /* select ::= WITH RECURSIVE wqlist selectnowith */ { - Select *p = yymsp[0].minor.yy423; + Select *p = yymsp[0].minor.yy457; if( p ){ - p->pWith = yymsp[-1].minor.yy499; + p->pWith = yymsp[-1].minor.yy59; parserDoubleLinkSelect(pParse, p); }else{ - sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy499); + sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy59); } - yymsp[-3].minor.yy423 = p; + yymsp[-3].minor.yy457 = p; } break; case 82: /* select ::= selectnowith */ { - Select *p = yymsp[0].minor.yy423; + Select *p = yymsp[0].minor.yy457; if( p ){ parserDoubleLinkSelect(pParse, p); } - yymsp[0].minor.yy423 = p; /*A-overwrites-X*/ + yymsp[0].minor.yy457 = p; /*A-overwrites-X*/ } break; case 83: /* selectnowith ::= selectnowith multiselect_op oneselect */ { - Select *pRhs = yymsp[0].minor.yy423; - Select *pLhs = yymsp[-2].minor.yy423; + Select *pRhs = yymsp[0].minor.yy457; + Select *pLhs = yymsp[-2].minor.yy457; if( pRhs && pRhs->pPrior ){ SrcList *pFrom; Token x; @@ -150745,63 +151628,63 @@ static YYACTIONTYPE yy_reduce( pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0); } if( pRhs ){ - pRhs->op = (u8)yymsp[-1].minor.yy96; + pRhs->op = (u8)yymsp[-1].minor.yy494; pRhs->pPrior = pLhs; if( ALWAYS(pLhs) ) pLhs->selFlags &= ~SF_MultiValue; pRhs->selFlags &= ~SF_MultiValue; - if( yymsp[-1].minor.yy96!=TK_ALL ) pParse->hasCompound = 1; + if( yymsp[-1].minor.yy494!=TK_ALL ) pParse->hasCompound = 1; }else{ sqlite3SelectDelete(pParse->db, pLhs); } - yymsp[-2].minor.yy423 = pRhs; + yymsp[-2].minor.yy457 = pRhs; } break; case 84: /* multiselect_op ::= UNION */ case 86: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==86); -{yymsp[0].minor.yy96 = yymsp[0].major; /*A-overwrites-OP*/} +{yymsp[0].minor.yy494 = yymsp[0].major; /*A-overwrites-OP*/} break; case 85: /* multiselect_op ::= UNION ALL */ -{yymsp[-1].minor.yy96 = TK_ALL;} +{yymsp[-1].minor.yy494 = TK_ALL;} break; case 87: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ { - yymsp[-8].minor.yy423 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy42,yymsp[-5].minor.yy167,yymsp[-4].minor.yy490,yymsp[-3].minor.yy42,yymsp[-2].minor.yy490,yymsp[-1].minor.yy42,yymsp[-7].minor.yy96,yymsp[0].minor.yy490); + yymsp[-8].minor.yy457 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy434,yymsp[-5].minor.yy483,yymsp[-4].minor.yy524,yymsp[-3].minor.yy434,yymsp[-2].minor.yy524,yymsp[-1].minor.yy434,yymsp[-7].minor.yy494,yymsp[0].minor.yy524); } break; case 88: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ { - yymsp[-9].minor.yy423 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy42,yymsp[-6].minor.yy167,yymsp[-5].minor.yy490,yymsp[-4].minor.yy42,yymsp[-3].minor.yy490,yymsp[-1].minor.yy42,yymsp[-8].minor.yy96,yymsp[0].minor.yy490); - if( yymsp[-9].minor.yy423 ){ - yymsp[-9].minor.yy423->pWinDefn = yymsp[-2].minor.yy147; + yymsp[-9].minor.yy457 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy434,yymsp[-6].minor.yy483,yymsp[-5].minor.yy524,yymsp[-4].minor.yy434,yymsp[-3].minor.yy524,yymsp[-1].minor.yy434,yymsp[-8].minor.yy494,yymsp[0].minor.yy524); + if( yymsp[-9].minor.yy457 ){ + yymsp[-9].minor.yy457->pWinDefn = yymsp[-2].minor.yy295; }else{ - sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy147); + sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy295); } } break; case 89: /* values ::= VALUES LP nexprlist RP */ { - yymsp[-3].minor.yy423 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy42,0,0,0,0,0,SF_Values,0); + yymsp[-3].minor.yy457 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy434,0,0,0,0,0,SF_Values,0); } break; case 90: /* values ::= values COMMA LP nexprlist RP */ { - Select *pRight, *pLeft = yymsp[-4].minor.yy423; - pRight = sqlite3SelectNew(pParse,yymsp[-1].minor.yy42,0,0,0,0,0,SF_Values|SF_MultiValue,0); + Select *pRight, *pLeft = yymsp[-4].minor.yy457; + pRight = sqlite3SelectNew(pParse,yymsp[-1].minor.yy434,0,0,0,0,0,SF_Values|SF_MultiValue,0); if( ALWAYS(pLeft) ) pLeft->selFlags &= ~SF_MultiValue; if( pRight ){ pRight->op = TK_ALL; pRight->pPrior = pLeft; - yymsp[-4].minor.yy423 = pRight; + yymsp[-4].minor.yy457 = pRight; }else{ - yymsp[-4].minor.yy423 = pLeft; + yymsp[-4].minor.yy457 = pLeft; } } break; case 91: /* distinct ::= DISTINCT */ -{yymsp[0].minor.yy96 = SF_Distinct;} +{yymsp[0].minor.yy494 = SF_Distinct;} break; case 92: /* distinct ::= ALL */ -{yymsp[0].minor.yy96 = SF_All;} +{yymsp[0].minor.yy494 = SF_All;} break; case 94: /* sclp ::= */ case 127: /* orderby_opt ::= */ yytestcase(yyruleno==127); @@ -150809,19 +151692,19 @@ static YYACTIONTYPE yy_reduce( case 214: /* exprlist ::= */ yytestcase(yyruleno==214); case 217: /* paren_exprlist ::= */ yytestcase(yyruleno==217); case 222: /* eidlist_opt ::= */ yytestcase(yyruleno==222); -{yymsp[1].minor.yy42 = 0;} +{yymsp[1].minor.yy434 = 0;} break; case 95: /* selcollist ::= sclp scanpt expr scanpt as */ { - yymsp[-4].minor.yy42 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy42, yymsp[-2].minor.yy490); - if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy42, &yymsp[0].minor.yy0, 1); - sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy42,yymsp[-3].minor.yy464,yymsp[-1].minor.yy464); + yymsp[-4].minor.yy434 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy434, yymsp[-2].minor.yy524); + if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy434, &yymsp[0].minor.yy0, 1); + sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy434,yymsp[-3].minor.yy294,yymsp[-1].minor.yy294); } break; case 96: /* selcollist ::= sclp scanpt STAR */ { Expr *p = sqlite3Expr(pParse->db, TK_ASTERISK, 0); - yymsp[-2].minor.yy42 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy42, p); + yymsp[-2].minor.yy434 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy434, p); } break; case 97: /* selcollist ::= sclp scanpt nm DOT STAR */ @@ -150829,7 +151712,7 @@ static YYACTIONTYPE yy_reduce( Expr *pRight = sqlite3PExpr(pParse, TK_ASTERISK, 0, 0); Expr *pLeft = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1); Expr *pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight); - yymsp[-4].minor.yy42 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy42, pDot); + yymsp[-4].minor.yy434 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy434, pDot); } break; case 98: /* as ::= AS nm */ @@ -150839,48 +151722,48 @@ static YYACTIONTYPE yy_reduce( {yymsp[-1].minor.yy0 = yymsp[0].minor.yy0;} break; case 100: /* from ::= */ -{yymsp[1].minor.yy167 = sqlite3DbMallocZero(pParse->db, sizeof(*yymsp[1].minor.yy167));} +{yymsp[1].minor.yy483 = sqlite3DbMallocZero(pParse->db, sizeof(*yymsp[1].minor.yy483));} break; case 101: /* from ::= FROM seltablist */ { - yymsp[-1].minor.yy167 = yymsp[0].minor.yy167; - sqlite3SrcListShiftJoinType(yymsp[-1].minor.yy167); + yymsp[-1].minor.yy483 = yymsp[0].minor.yy483; + sqlite3SrcListShiftJoinType(yymsp[-1].minor.yy483); } break; case 102: /* stl_prefix ::= seltablist joinop */ { - if( ALWAYS(yymsp[-1].minor.yy167 && yymsp[-1].minor.yy167->nSrc>0) ) yymsp[-1].minor.yy167->a[yymsp[-1].minor.yy167->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy96; + if( ALWAYS(yymsp[-1].minor.yy483 && yymsp[-1].minor.yy483->nSrc>0) ) yymsp[-1].minor.yy483->a[yymsp[-1].minor.yy483->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy494; } break; case 103: /* stl_prefix ::= */ -{yymsp[1].minor.yy167 = 0;} +{yymsp[1].minor.yy483 = 0;} break; case 104: /* seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ { - yymsp[-6].minor.yy167 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy167,&yymsp[-5].minor.yy0,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,0,yymsp[-1].minor.yy490,yymsp[0].minor.yy336); - sqlite3SrcListIndexedBy(pParse, yymsp[-6].minor.yy167, &yymsp[-2].minor.yy0); + yymsp[-6].minor.yy483 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy483,&yymsp[-5].minor.yy0,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,0,yymsp[-1].minor.yy524,yymsp[0].minor.yy62); + sqlite3SrcListIndexedBy(pParse, yymsp[-6].minor.yy483, &yymsp[-2].minor.yy0); } break; case 105: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */ { - yymsp[-8].minor.yy167 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-8].minor.yy167,&yymsp[-7].minor.yy0,&yymsp[-6].minor.yy0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy490,yymsp[0].minor.yy336); - sqlite3SrcListFuncArgs(pParse, yymsp[-8].minor.yy167, yymsp[-4].minor.yy42); + yymsp[-8].minor.yy483 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-8].minor.yy483,&yymsp[-7].minor.yy0,&yymsp[-6].minor.yy0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy524,yymsp[0].minor.yy62); + sqlite3SrcListFuncArgs(pParse, yymsp[-8].minor.yy483, yymsp[-4].minor.yy434); } break; case 106: /* seltablist ::= stl_prefix LP select RP as on_opt using_opt */ { - yymsp[-6].minor.yy167 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy167,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy423,yymsp[-1].minor.yy490,yymsp[0].minor.yy336); + yymsp[-6].minor.yy483 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy483,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy457,yymsp[-1].minor.yy524,yymsp[0].minor.yy62); } break; case 107: /* seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ { - if( yymsp[-6].minor.yy167==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy490==0 && yymsp[0].minor.yy336==0 ){ - yymsp[-6].minor.yy167 = yymsp[-4].minor.yy167; - }else if( yymsp[-4].minor.yy167->nSrc==1 ){ - yymsp[-6].minor.yy167 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy167,0,0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy490,yymsp[0].minor.yy336); - if( yymsp[-6].minor.yy167 ){ - struct SrcList_item *pNew = &yymsp[-6].minor.yy167->a[yymsp[-6].minor.yy167->nSrc-1]; - struct SrcList_item *pOld = yymsp[-4].minor.yy167->a; + if( yymsp[-6].minor.yy483==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy524==0 && yymsp[0].minor.yy62==0 ){ + yymsp[-6].minor.yy483 = yymsp[-4].minor.yy483; + }else if( yymsp[-4].minor.yy483->nSrc==1 ){ + yymsp[-6].minor.yy483 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy483,0,0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy524,yymsp[0].minor.yy62); + if( yymsp[-6].minor.yy483 ){ + struct SrcList_item *pNew = &yymsp[-6].minor.yy483->a[yymsp[-6].minor.yy483->nSrc-1]; + struct SrcList_item *pOld = yymsp[-4].minor.yy483->a; pNew->zName = pOld->zName; pNew->zDatabase = pOld->zDatabase; pNew->pSelect = pOld->pSelect; @@ -150893,12 +151776,12 @@ static YYACTIONTYPE yy_reduce( pOld->zName = pOld->zDatabase = 0; pOld->pSelect = 0; } - sqlite3SrcListDelete(pParse->db, yymsp[-4].minor.yy167); + sqlite3SrcListDelete(pParse->db, yymsp[-4].minor.yy483); }else{ Select *pSubquery; - sqlite3SrcListShiftJoinType(yymsp[-4].minor.yy167); - pSubquery = sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy167,0,0,0,0,SF_NestedFrom,0); - yymsp[-6].minor.yy167 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy167,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy490,yymsp[0].minor.yy336); + sqlite3SrcListShiftJoinType(yymsp[-4].minor.yy483); + pSubquery = sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy483,0,0,0,0,SF_NestedFrom,0); + yymsp[-6].minor.yy483 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy483,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy524,yymsp[0].minor.yy62); } } break; @@ -150908,54 +151791,54 @@ static YYACTIONTYPE yy_reduce( break; case 110: /* fullname ::= nm */ { - yylhsminor.yy167 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); - if( IN_RENAME_OBJECT && yylhsminor.yy167 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy167->a[0].zName, &yymsp[0].minor.yy0); + yylhsminor.yy483 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); + if( IN_RENAME_OBJECT && yylhsminor.yy483 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy483->a[0].zName, &yymsp[0].minor.yy0); } - yymsp[0].minor.yy167 = yylhsminor.yy167; + yymsp[0].minor.yy483 = yylhsminor.yy483; break; case 111: /* fullname ::= nm DOT nm */ { - yylhsminor.yy167 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); - if( IN_RENAME_OBJECT && yylhsminor.yy167 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy167->a[0].zName, &yymsp[0].minor.yy0); + yylhsminor.yy483 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); + if( IN_RENAME_OBJECT && yylhsminor.yy483 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy483->a[0].zName, &yymsp[0].minor.yy0); } - yymsp[-2].minor.yy167 = yylhsminor.yy167; + yymsp[-2].minor.yy483 = yylhsminor.yy483; break; case 112: /* xfullname ::= nm */ -{yymsp[0].minor.yy167 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/} +{yymsp[0].minor.yy483 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/} break; case 113: /* xfullname ::= nm DOT nm */ -{yymsp[-2].minor.yy167 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/} +{yymsp[-2].minor.yy483 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/} break; case 114: /* xfullname ::= nm DOT nm AS nm */ { - yymsp[-4].minor.yy167 = sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/ - if( yymsp[-4].minor.yy167 ) yymsp[-4].minor.yy167->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); + yymsp[-4].minor.yy483 = sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/ + if( yymsp[-4].minor.yy483 ) yymsp[-4].minor.yy483->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); } break; case 115: /* xfullname ::= nm AS nm */ { - yymsp[-2].minor.yy167 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/ - if( yymsp[-2].minor.yy167 ) yymsp[-2].minor.yy167->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); + yymsp[-2].minor.yy483 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/ + if( yymsp[-2].minor.yy483 ) yymsp[-2].minor.yy483->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); } break; case 116: /* joinop ::= COMMA|JOIN */ -{ yymsp[0].minor.yy96 = JT_INNER; } +{ yymsp[0].minor.yy494 = JT_INNER; } break; case 117: /* joinop ::= JOIN_KW JOIN */ -{yymsp[-1].minor.yy96 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/} +{yymsp[-1].minor.yy494 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/} break; case 118: /* joinop ::= JOIN_KW nm JOIN */ -{yymsp[-2].minor.yy96 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/} +{yymsp[-2].minor.yy494 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/} break; case 119: /* joinop ::= JOIN_KW nm nm JOIN */ -{yymsp[-3].minor.yy96 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/} +{yymsp[-3].minor.yy494 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/} break; case 120: /* on_opt ::= ON expr */ case 137: /* having_opt ::= HAVING expr */ yytestcase(yyruleno==137); case 144: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==144); case 210: /* case_else ::= ELSE expr */ yytestcase(yyruleno==210); case 231: /* vinto ::= INTO expr */ yytestcase(yyruleno==231); -{yymsp[-1].minor.yy490 = yymsp[0].minor.yy490;} +{yymsp[-1].minor.yy524 = yymsp[0].minor.yy524;} break; case 121: /* on_opt ::= */ case 136: /* having_opt ::= */ yytestcase(yyruleno==136); @@ -150964,7 +151847,7 @@ static YYACTIONTYPE yy_reduce( case 211: /* case_else ::= */ yytestcase(yyruleno==211); case 213: /* case_operand ::= */ yytestcase(yyruleno==213); case 232: /* vinto ::= */ yytestcase(yyruleno==232); -{yymsp[1].minor.yy490 = 0;} +{yymsp[1].minor.yy524 = 0;} break; case 123: /* indexed_opt ::= INDEXED BY nm */ {yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;} @@ -150973,119 +151856,119 @@ static YYACTIONTYPE yy_reduce( {yymsp[-1].minor.yy0.z=0; yymsp[-1].minor.yy0.n=1;} break; case 125: /* using_opt ::= USING LP idlist RP */ -{yymsp[-3].minor.yy336 = yymsp[-1].minor.yy336;} +{yymsp[-3].minor.yy62 = yymsp[-1].minor.yy62;} break; case 126: /* using_opt ::= */ case 158: /* idlist_opt ::= */ yytestcase(yyruleno==158); -{yymsp[1].minor.yy336 = 0;} +{yymsp[1].minor.yy62 = 0;} break; case 128: /* orderby_opt ::= ORDER BY sortlist */ case 135: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==135); -{yymsp[-2].minor.yy42 = yymsp[0].minor.yy42;} +{yymsp[-2].minor.yy434 = yymsp[0].minor.yy434;} break; case 129: /* sortlist ::= sortlist COMMA expr sortorder */ { - yymsp[-3].minor.yy42 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy42,yymsp[-1].minor.yy490); - sqlite3ExprListSetSortOrder(yymsp[-3].minor.yy42,yymsp[0].minor.yy96); + yymsp[-3].minor.yy434 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy434,yymsp[-1].minor.yy524); + sqlite3ExprListSetSortOrder(yymsp[-3].minor.yy434,yymsp[0].minor.yy494); } break; case 130: /* sortlist ::= expr sortorder */ { - yymsp[-1].minor.yy42 = sqlite3ExprListAppend(pParse,0,yymsp[-1].minor.yy490); /*A-overwrites-Y*/ - sqlite3ExprListSetSortOrder(yymsp[-1].minor.yy42,yymsp[0].minor.yy96); + yymsp[-1].minor.yy434 = sqlite3ExprListAppend(pParse,0,yymsp[-1].minor.yy524); /*A-overwrites-Y*/ + sqlite3ExprListSetSortOrder(yymsp[-1].minor.yy434,yymsp[0].minor.yy494); } break; case 131: /* sortorder ::= ASC */ -{yymsp[0].minor.yy96 = SQLITE_SO_ASC;} +{yymsp[0].minor.yy494 = SQLITE_SO_ASC;} break; case 132: /* sortorder ::= DESC */ -{yymsp[0].minor.yy96 = SQLITE_SO_DESC;} +{yymsp[0].minor.yy494 = SQLITE_SO_DESC;} break; case 133: /* sortorder ::= */ -{yymsp[1].minor.yy96 = SQLITE_SO_UNDEFINED;} +{yymsp[1].minor.yy494 = SQLITE_SO_UNDEFINED;} break; case 139: /* limit_opt ::= LIMIT expr */ -{yymsp[-1].minor.yy490 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy490,0);} +{yymsp[-1].minor.yy524 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy524,0);} break; case 140: /* limit_opt ::= LIMIT expr OFFSET expr */ -{yymsp[-3].minor.yy490 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy490,yymsp[0].minor.yy490);} +{yymsp[-3].minor.yy524 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy524,yymsp[0].minor.yy524);} break; case 141: /* limit_opt ::= LIMIT expr COMMA expr */ -{yymsp[-3].minor.yy490 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy490,yymsp[-2].minor.yy490);} +{yymsp[-3].minor.yy524 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy524,yymsp[-2].minor.yy524);} break; case 142: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt */ { - sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy167, &yymsp[-1].minor.yy0); - sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy167,yymsp[0].minor.yy490,0,0); + sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy483, &yymsp[-1].minor.yy0); + sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy483,yymsp[0].minor.yy524,0,0); } break; case 145: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt */ { - sqlite3SrcListIndexedBy(pParse, yymsp[-4].minor.yy167, &yymsp[-3].minor.yy0); - sqlite3ExprListCheckLength(pParse,yymsp[-1].minor.yy42,"set list"); - sqlite3Update(pParse,yymsp[-4].minor.yy167,yymsp[-1].minor.yy42,yymsp[0].minor.yy490,yymsp[-5].minor.yy96,0,0,0); + sqlite3SrcListIndexedBy(pParse, yymsp[-4].minor.yy483, &yymsp[-3].minor.yy0); + sqlite3ExprListCheckLength(pParse,yymsp[-1].minor.yy434,"set list"); + sqlite3Update(pParse,yymsp[-4].minor.yy483,yymsp[-1].minor.yy434,yymsp[0].minor.yy524,yymsp[-5].minor.yy494,0,0,0); } break; case 146: /* setlist ::= setlist COMMA nm EQ expr */ { - yymsp[-4].minor.yy42 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy42, yymsp[0].minor.yy490); - sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy42, &yymsp[-2].minor.yy0, 1); + yymsp[-4].minor.yy434 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy434, yymsp[0].minor.yy524); + sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy434, &yymsp[-2].minor.yy0, 1); } break; case 147: /* setlist ::= setlist COMMA LP idlist RP EQ expr */ { - yymsp[-6].minor.yy42 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy42, yymsp[-3].minor.yy336, yymsp[0].minor.yy490); + yymsp[-6].minor.yy434 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy434, yymsp[-3].minor.yy62, yymsp[0].minor.yy524); } break; case 148: /* setlist ::= nm EQ expr */ { - yylhsminor.yy42 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy490); - sqlite3ExprListSetName(pParse, yylhsminor.yy42, &yymsp[-2].minor.yy0, 1); + yylhsminor.yy434 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy524); + sqlite3ExprListSetName(pParse, yylhsminor.yy434, &yymsp[-2].minor.yy0, 1); } - yymsp[-2].minor.yy42 = yylhsminor.yy42; + yymsp[-2].minor.yy434 = yylhsminor.yy434; break; case 149: /* setlist ::= LP idlist RP EQ expr */ { - yymsp[-4].minor.yy42 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy336, yymsp[0].minor.yy490); + yymsp[-4].minor.yy434 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy62, yymsp[0].minor.yy524); } break; case 150: /* cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ { - sqlite3Insert(pParse, yymsp[-3].minor.yy167, yymsp[-1].minor.yy423, yymsp[-2].minor.yy336, yymsp[-5].minor.yy96, yymsp[0].minor.yy266); + sqlite3Insert(pParse, yymsp[-3].minor.yy483, yymsp[-1].minor.yy457, yymsp[-2].minor.yy62, yymsp[-5].minor.yy494, yymsp[0].minor.yy136); } break; case 151: /* cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */ { - sqlite3Insert(pParse, yymsp[-3].minor.yy167, 0, yymsp[-2].minor.yy336, yymsp[-5].minor.yy96, 0); + sqlite3Insert(pParse, yymsp[-3].minor.yy483, 0, yymsp[-2].minor.yy62, yymsp[-5].minor.yy494, 0); } break; case 152: /* upsert ::= */ -{ yymsp[1].minor.yy266 = 0; } +{ yymsp[1].minor.yy136 = 0; } break; case 153: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */ -{ yymsp[-10].minor.yy266 = sqlite3UpsertNew(pParse->db,yymsp[-7].minor.yy42,yymsp[-5].minor.yy490,yymsp[-1].minor.yy42,yymsp[0].minor.yy490);} +{ yymsp[-10].minor.yy136 = sqlite3UpsertNew(pParse->db,yymsp[-7].minor.yy434,yymsp[-5].minor.yy524,yymsp[-1].minor.yy434,yymsp[0].minor.yy524);} break; case 154: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */ -{ yymsp[-7].minor.yy266 = sqlite3UpsertNew(pParse->db,yymsp[-4].minor.yy42,yymsp[-2].minor.yy490,0,0); } +{ yymsp[-7].minor.yy136 = sqlite3UpsertNew(pParse->db,yymsp[-4].minor.yy434,yymsp[-2].minor.yy524,0,0); } break; case 155: /* upsert ::= ON CONFLICT DO NOTHING */ -{ yymsp[-3].minor.yy266 = sqlite3UpsertNew(pParse->db,0,0,0,0); } +{ yymsp[-3].minor.yy136 = sqlite3UpsertNew(pParse->db,0,0,0,0); } break; case 159: /* idlist_opt ::= LP idlist RP */ -{yymsp[-2].minor.yy336 = yymsp[-1].minor.yy336;} +{yymsp[-2].minor.yy62 = yymsp[-1].minor.yy62;} break; case 160: /* idlist ::= idlist COMMA nm */ -{yymsp[-2].minor.yy336 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy336,&yymsp[0].minor.yy0);} +{yymsp[-2].minor.yy62 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy62,&yymsp[0].minor.yy0);} break; case 161: /* idlist ::= nm */ -{yymsp[0].minor.yy336 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/} +{yymsp[0].minor.yy62 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/} break; case 162: /* expr ::= LP expr RP */ -{yymsp[-2].minor.yy490 = yymsp[-1].minor.yy490;} +{yymsp[-2].minor.yy524 = yymsp[-1].minor.yy524;} break; case 163: /* expr ::= ID|INDEXED */ case 164: /* expr ::= JOIN_KW */ yytestcase(yyruleno==164); -{yymsp[0].minor.yy490=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/} +{yymsp[0].minor.yy524=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/} break; case 165: /* expr ::= nm DOT nm */ { @@ -151095,9 +151978,9 @@ static YYACTIONTYPE yy_reduce( sqlite3RenameTokenMap(pParse, (void*)temp2, &yymsp[0].minor.yy0); sqlite3RenameTokenMap(pParse, (void*)temp1, &yymsp[-2].minor.yy0); } - yylhsminor.yy490 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2); + yylhsminor.yy524 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2); } - yymsp[-2].minor.yy490 = yylhsminor.yy490; + yymsp[-2].minor.yy524 = yylhsminor.yy524; break; case 166: /* expr ::= nm DOT nm DOT nm */ { @@ -151109,26 +151992,26 @@ static YYACTIONTYPE yy_reduce( sqlite3RenameTokenMap(pParse, (void*)temp3, &yymsp[0].minor.yy0); sqlite3RenameTokenMap(pParse, (void*)temp2, &yymsp[-2].minor.yy0); } - yylhsminor.yy490 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4); + yylhsminor.yy524 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4); } - yymsp[-4].minor.yy490 = yylhsminor.yy490; + yymsp[-4].minor.yy524 = yylhsminor.yy524; break; case 167: /* term ::= NULL|FLOAT|BLOB */ case 168: /* term ::= STRING */ yytestcase(yyruleno==168); -{yymsp[0].minor.yy490=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/} +{yymsp[0].minor.yy524=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/} break; case 169: /* term ::= INTEGER */ { - yylhsminor.yy490 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1); + yylhsminor.yy524 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1); } - yymsp[0].minor.yy490 = yylhsminor.yy490; + yymsp[0].minor.yy524 = yylhsminor.yy524; break; case 170: /* expr ::= VARIABLE */ { if( !(yymsp[0].minor.yy0.z[0]=='#' && sqlite3Isdigit(yymsp[0].minor.yy0.z[1])) ){ u32 n = yymsp[0].minor.yy0.n; - yymsp[0].minor.yy490 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0); - sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy490, n); + yymsp[0].minor.yy524 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0); + sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy524, n); }else{ /* When doing a nested parse, one can include terms in an expression ** that look like this: #1 #2 ... These terms refer to registers @@ -151137,63 +152020,63 @@ static YYACTIONTYPE yy_reduce( assert( t.n>=2 ); if( pParse->nested==0 ){ sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t); - yymsp[0].minor.yy490 = 0; + yymsp[0].minor.yy524 = 0; }else{ - yymsp[0].minor.yy490 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0); - if( yymsp[0].minor.yy490 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy490->iTable); + yymsp[0].minor.yy524 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0); + if( yymsp[0].minor.yy524 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy524->iTable); } } } break; case 171: /* expr ::= expr COLLATE ID|STRING */ { - yymsp[-2].minor.yy490 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy490, &yymsp[0].minor.yy0, 1); + yymsp[-2].minor.yy524 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy524, &yymsp[0].minor.yy0, 1); } break; case 172: /* expr ::= CAST LP expr AS typetoken RP */ { - yymsp[-5].minor.yy490 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1); - sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy490, yymsp[-3].minor.yy490, 0); + yymsp[-5].minor.yy524 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1); + sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy524, yymsp[-3].minor.yy524, 0); } break; case 173: /* expr ::= ID|INDEXED LP distinct exprlist RP */ { - yylhsminor.yy490 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy42, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy96); + yylhsminor.yy524 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy434, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy494); } - yymsp[-4].minor.yy490 = yylhsminor.yy490; + yymsp[-4].minor.yy524 = yylhsminor.yy524; break; case 174: /* expr ::= ID|INDEXED LP STAR RP */ { - yylhsminor.yy490 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0); + yylhsminor.yy524 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0); } - yymsp[-3].minor.yy490 = yylhsminor.yy490; + yymsp[-3].minor.yy524 = yylhsminor.yy524; break; case 175: /* expr ::= ID|INDEXED LP distinct exprlist RP over_clause */ { - yylhsminor.yy490 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy42, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy96); - sqlite3WindowAttach(pParse, yylhsminor.yy490, yymsp[0].minor.yy147); + yylhsminor.yy524 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy434, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy494); + sqlite3WindowAttach(pParse, yylhsminor.yy524, yymsp[0].minor.yy295); } - yymsp[-5].minor.yy490 = yylhsminor.yy490; + yymsp[-5].minor.yy524 = yylhsminor.yy524; break; case 176: /* expr ::= ID|INDEXED LP STAR RP over_clause */ { - yylhsminor.yy490 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0); - sqlite3WindowAttach(pParse, yylhsminor.yy490, yymsp[0].minor.yy147); + yylhsminor.yy524 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0); + sqlite3WindowAttach(pParse, yylhsminor.yy524, yymsp[0].minor.yy295); } - yymsp[-4].minor.yy490 = yylhsminor.yy490; + yymsp[-4].minor.yy524 = yylhsminor.yy524; break; case 177: /* term ::= CTIME_KW */ { - yylhsminor.yy490 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0); + yylhsminor.yy524 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0); } - yymsp[0].minor.yy490 = yylhsminor.yy490; + yymsp[0].minor.yy524 = yylhsminor.yy524; break; case 178: /* expr ::= LP nexprlist COMMA expr RP */ { - ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy42, yymsp[-1].minor.yy490); - yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0); - if( yymsp[-4].minor.yy490 ){ - yymsp[-4].minor.yy490->x.pList = pList; + ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy434, yymsp[-1].minor.yy524); + yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0); + if( yymsp[-4].minor.yy524 ){ + yymsp[-4].minor.yy524->x.pList = pList; }else{ sqlite3ExprListDelete(pParse->db, pList); } @@ -151207,7 +152090,7 @@ static YYACTIONTYPE yy_reduce( case 184: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==184); case 185: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==185); case 186: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==186); -{yymsp[-2].minor.yy490=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy490,yymsp[0].minor.yy490);} +{yymsp[-2].minor.yy524=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy524,yymsp[0].minor.yy524);} break; case 187: /* likeop ::= NOT LIKE_KW|MATCH */ {yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.n|=0x80000000; /*yymsp[-1].minor.yy0-overwrite-yymsp[0].minor.yy0*/} @@ -151217,11 +152100,11 @@ static YYACTIONTYPE yy_reduce( ExprList *pList; int bNot = yymsp[-1].minor.yy0.n & 0x80000000; yymsp[-1].minor.yy0.n &= 0x7fffffff; - pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy490); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy490); - yymsp[-2].minor.yy490 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); - if( bNot ) yymsp[-2].minor.yy490 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy490, 0); - if( yymsp[-2].minor.yy490 ) yymsp[-2].minor.yy490->flags |= EP_InfixFunc; + pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy524); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy524); + yymsp[-2].minor.yy524 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); + if( bNot ) yymsp[-2].minor.yy524 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy524, 0); + if( yymsp[-2].minor.yy524 ) yymsp[-2].minor.yy524->flags |= EP_InfixFunc; } break; case 189: /* expr ::= expr likeop expr ESCAPE expr */ @@ -151229,62 +152112,62 @@ static YYACTIONTYPE yy_reduce( ExprList *pList; int bNot = yymsp[-3].minor.yy0.n & 0x80000000; yymsp[-3].minor.yy0.n &= 0x7fffffff; - pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy490); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy490); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy490); - yymsp[-4].minor.yy490 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0); - if( bNot ) yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy490, 0); - if( yymsp[-4].minor.yy490 ) yymsp[-4].minor.yy490->flags |= EP_InfixFunc; + pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy524); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy524); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy524); + yymsp[-4].minor.yy524 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0); + if( bNot ) yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy524, 0); + if( yymsp[-4].minor.yy524 ) yymsp[-4].minor.yy524->flags |= EP_InfixFunc; } break; case 190: /* expr ::= expr ISNULL|NOTNULL */ -{yymsp[-1].minor.yy490 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy490,0);} +{yymsp[-1].minor.yy524 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy524,0);} break; case 191: /* expr ::= expr NOT NULL */ -{yymsp[-2].minor.yy490 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy490,0);} +{yymsp[-2].minor.yy524 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy524,0);} break; case 192: /* expr ::= expr IS expr */ { - yymsp[-2].minor.yy490 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy490,yymsp[0].minor.yy490); - binaryToUnaryIfNull(pParse, yymsp[0].minor.yy490, yymsp[-2].minor.yy490, TK_ISNULL); + yymsp[-2].minor.yy524 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy524,yymsp[0].minor.yy524); + binaryToUnaryIfNull(pParse, yymsp[0].minor.yy524, yymsp[-2].minor.yy524, TK_ISNULL); } break; case 193: /* expr ::= expr IS NOT expr */ { - yymsp[-3].minor.yy490 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy490,yymsp[0].minor.yy490); - binaryToUnaryIfNull(pParse, yymsp[0].minor.yy490, yymsp[-3].minor.yy490, TK_NOTNULL); + yymsp[-3].minor.yy524 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy524,yymsp[0].minor.yy524); + binaryToUnaryIfNull(pParse, yymsp[0].minor.yy524, yymsp[-3].minor.yy524, TK_NOTNULL); } break; case 194: /* expr ::= NOT expr */ case 195: /* expr ::= BITNOT expr */ yytestcase(yyruleno==195); -{yymsp[-1].minor.yy490 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy490, 0);/*A-overwrites-B*/} +{yymsp[-1].minor.yy524 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy524, 0);/*A-overwrites-B*/} break; case 196: /* expr ::= PLUS|MINUS expr */ { - yymsp[-1].minor.yy490 = sqlite3PExpr(pParse, yymsp[-1].major==TK_PLUS ? TK_UPLUS : TK_UMINUS, yymsp[0].minor.yy490, 0); + yymsp[-1].minor.yy524 = sqlite3PExpr(pParse, yymsp[-1].major==TK_PLUS ? TK_UPLUS : TK_UMINUS, yymsp[0].minor.yy524, 0); /*A-overwrites-B*/ } break; case 197: /* between_op ::= BETWEEN */ case 200: /* in_op ::= IN */ yytestcase(yyruleno==200); -{yymsp[0].minor.yy96 = 0;} +{yymsp[0].minor.yy494 = 0;} break; case 199: /* expr ::= expr between_op expr AND expr */ { - ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy490); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy490); - yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy490, 0); - if( yymsp[-4].minor.yy490 ){ - yymsp[-4].minor.yy490->x.pList = pList; + ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy524); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy524); + yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy524, 0); + if( yymsp[-4].minor.yy524 ){ + yymsp[-4].minor.yy524->x.pList = pList; }else{ sqlite3ExprListDelete(pParse->db, pList); } - if( yymsp[-3].minor.yy96 ) yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy490, 0); + if( yymsp[-3].minor.yy494 ) yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy524, 0); } break; case 202: /* expr ::= expr in_op LP exprlist RP */ { - if( yymsp[-1].minor.yy42==0 ){ + if( yymsp[-1].minor.yy434==0 ){ /* Expressions of the form ** ** expr1 IN () @@ -151294,10 +152177,10 @@ static YYACTIONTYPE yy_reduce( ** regardless of the value of expr1. */ if( IN_RENAME_OBJECT==0 ){ - sqlite3ExprDelete(pParse->db, yymsp[-4].minor.yy490); - yymsp[-4].minor.yy490 = sqlite3ExprAlloc(pParse->db, TK_INTEGER,&sqlite3IntTokens[yymsp[-3].minor.yy96],1); + sqlite3ExprDelete(pParse->db, yymsp[-4].minor.yy524); + yymsp[-4].minor.yy524 = sqlite3ExprAlloc(pParse->db, TK_INTEGER,&sqlite3IntTokens[yymsp[-3].minor.yy494],1); } - }else if( yymsp[-1].minor.yy42->nExpr==1 ){ + }else if( yymsp[-1].minor.yy434->nExpr==1 ){ /* Expressions of the form: ** ** expr1 IN (?1) @@ -151314,100 +152197,100 @@ static YYACTIONTYPE yy_reduce( ** affinity or the collating sequence to use for comparison. Otherwise, ** the semantics would be subtly different from IN or NOT IN. */ - Expr *pRHS = yymsp[-1].minor.yy42->a[0].pExpr; - yymsp[-1].minor.yy42->a[0].pExpr = 0; - sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy42); + Expr *pRHS = yymsp[-1].minor.yy434->a[0].pExpr; + yymsp[-1].minor.yy434->a[0].pExpr = 0; + sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy434); /* pRHS cannot be NULL because a malloc error would have been detected ** before now and control would have never reached this point */ if( ALWAYS(pRHS) ){ pRHS->flags &= ~EP_Collate; pRHS->flags |= EP_Generic; } - yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, yymsp[-3].minor.yy96 ? TK_NE : TK_EQ, yymsp[-4].minor.yy490, pRHS); + yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, yymsp[-3].minor.yy494 ? TK_NE : TK_EQ, yymsp[-4].minor.yy524, pRHS); }else{ - yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy490, 0); - if( yymsp[-4].minor.yy490 ){ - yymsp[-4].minor.yy490->x.pList = yymsp[-1].minor.yy42; - sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy490); + yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy524, 0); + if( yymsp[-4].minor.yy524 ){ + yymsp[-4].minor.yy524->x.pList = yymsp[-1].minor.yy434; + sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy524); }else{ - sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy42); + sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy434); } - if( yymsp[-3].minor.yy96 ) yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy490, 0); + if( yymsp[-3].minor.yy494 ) yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy524, 0); } } break; case 203: /* expr ::= LP select RP */ { - yymsp[-2].minor.yy490 = sqlite3PExpr(pParse, TK_SELECT, 0, 0); - sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy490, yymsp[-1].minor.yy423); + yymsp[-2].minor.yy524 = sqlite3PExpr(pParse, TK_SELECT, 0, 0); + sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy524, yymsp[-1].minor.yy457); } break; case 204: /* expr ::= expr in_op LP select RP */ { - yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy490, 0); - sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy490, yymsp[-1].minor.yy423); - if( yymsp[-3].minor.yy96 ) yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy490, 0); + yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy524, 0); + sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy524, yymsp[-1].minor.yy457); + if( yymsp[-3].minor.yy494 ) yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy524, 0); } break; case 205: /* expr ::= expr in_op nm dbnm paren_exprlist */ { SrcList *pSrc = sqlite3SrcListAppend(pParse, 0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0); Select *pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0); - if( yymsp[0].minor.yy42 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy42); - yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy490, 0); - sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy490, pSelect); - if( yymsp[-3].minor.yy96 ) yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy490, 0); + if( yymsp[0].minor.yy434 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy434); + yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy524, 0); + sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy524, pSelect); + if( yymsp[-3].minor.yy494 ) yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy524, 0); } break; case 206: /* expr ::= EXISTS LP select RP */ { Expr *p; - p = yymsp[-3].minor.yy490 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0); - sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy423); + p = yymsp[-3].minor.yy524 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0); + sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy457); } break; case 207: /* expr ::= CASE case_operand case_exprlist case_else END */ { - yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy490, 0); - if( yymsp[-4].minor.yy490 ){ - yymsp[-4].minor.yy490->x.pList = yymsp[-1].minor.yy490 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy42,yymsp[-1].minor.yy490) : yymsp[-2].minor.yy42; - sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy490); + yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy524, 0); + if( yymsp[-4].minor.yy524 ){ + yymsp[-4].minor.yy524->x.pList = yymsp[-1].minor.yy524 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy434,yymsp[-1].minor.yy524) : yymsp[-2].minor.yy434; + sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy524); }else{ - sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy42); - sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy490); + sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy434); + sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy524); } } break; case 208: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */ { - yymsp[-4].minor.yy42 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy42, yymsp[-2].minor.yy490); - yymsp[-4].minor.yy42 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy42, yymsp[0].minor.yy490); + yymsp[-4].minor.yy434 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy434, yymsp[-2].minor.yy524); + yymsp[-4].minor.yy434 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy434, yymsp[0].minor.yy524); } break; case 209: /* case_exprlist ::= WHEN expr THEN expr */ { - yymsp[-3].minor.yy42 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy490); - yymsp[-3].minor.yy42 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy42, yymsp[0].minor.yy490); + yymsp[-3].minor.yy434 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy524); + yymsp[-3].minor.yy434 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy434, yymsp[0].minor.yy524); } break; case 212: /* case_operand ::= expr */ -{yymsp[0].minor.yy490 = yymsp[0].minor.yy490; /*A-overwrites-X*/} +{yymsp[0].minor.yy524 = yymsp[0].minor.yy524; /*A-overwrites-X*/} break; case 215: /* nexprlist ::= nexprlist COMMA expr */ -{yymsp[-2].minor.yy42 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy42,yymsp[0].minor.yy490);} +{yymsp[-2].minor.yy434 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy434,yymsp[0].minor.yy524);} break; case 216: /* nexprlist ::= expr */ -{yymsp[0].minor.yy42 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy490); /*A-overwrites-Y*/} +{yymsp[0].minor.yy434 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy524); /*A-overwrites-Y*/} break; case 218: /* paren_exprlist ::= LP exprlist RP */ case 223: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==223); -{yymsp[-2].minor.yy42 = yymsp[-1].minor.yy42;} +{yymsp[-2].minor.yy434 = yymsp[-1].minor.yy434;} break; case 219: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ { sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, - sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy42, yymsp[-10].minor.yy96, - &yymsp[-11].minor.yy0, yymsp[0].minor.yy490, SQLITE_SO_ASC, yymsp[-8].minor.yy96, SQLITE_IDXTYPE_APPDEF); + sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy434, yymsp[-10].minor.yy494, + &yymsp[-11].minor.yy0, yymsp[0].minor.yy524, SQLITE_SO_ASC, yymsp[-8].minor.yy494, SQLITE_IDXTYPE_APPDEF); if( IN_RENAME_OBJECT && pParse->pNewIndex ){ sqlite3RenameTokenMap(pParse, pParse->pNewIndex->zName, &yymsp[-4].minor.yy0); } @@ -151415,29 +152298,29 @@ static YYACTIONTYPE yy_reduce( break; case 220: /* uniqueflag ::= UNIQUE */ case 262: /* raisetype ::= ABORT */ yytestcase(yyruleno==262); -{yymsp[0].minor.yy96 = OE_Abort;} +{yymsp[0].minor.yy494 = OE_Abort;} break; case 221: /* uniqueflag ::= */ -{yymsp[1].minor.yy96 = OE_None;} +{yymsp[1].minor.yy494 = OE_None;} break; case 224: /* eidlist ::= eidlist COMMA nm collate sortorder */ { - yymsp[-4].minor.yy42 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy42, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy96, yymsp[0].minor.yy96); + yymsp[-4].minor.yy434 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy434, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy494, yymsp[0].minor.yy494); } break; case 225: /* eidlist ::= nm collate sortorder */ { - yymsp[-2].minor.yy42 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy96, yymsp[0].minor.yy96); /*A-overwrites-Y*/ + yymsp[-2].minor.yy434 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy494, yymsp[0].minor.yy494); /*A-overwrites-Y*/ } break; case 228: /* cmd ::= DROP INDEX ifexists fullname */ -{sqlite3DropIndex(pParse, yymsp[0].minor.yy167, yymsp[-1].minor.yy96);} +{sqlite3DropIndex(pParse, yymsp[0].minor.yy483, yymsp[-1].minor.yy494);} break; case 229: /* cmd ::= VACUUM vinto */ -{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy490);} +{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy524);} break; case 230: /* cmd ::= VACUUM nm vinto */ -{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy490);} +{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy524);} break; case 233: /* cmd ::= PRAGMA nm dbnm */ {sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);} @@ -151459,51 +152342,51 @@ static YYACTIONTYPE yy_reduce( Token all; all.z = yymsp[-3].minor.yy0.z; all.n = (int)(yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n; - sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy119, &all); + sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy455, &all); } break; case 241: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ { - sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy96, yymsp[-4].minor.yy350.a, yymsp[-4].minor.yy350.b, yymsp[-2].minor.yy167, yymsp[0].minor.yy490, yymsp[-10].minor.yy96, yymsp[-8].minor.yy96); + sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy494, yymsp[-4].minor.yy90.a, yymsp[-4].minor.yy90.b, yymsp[-2].minor.yy483, yymsp[0].minor.yy524, yymsp[-10].minor.yy494, yymsp[-8].minor.yy494); yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/ } break; case 242: /* trigger_time ::= BEFORE|AFTER */ -{ yymsp[0].minor.yy96 = yymsp[0].major; /*A-overwrites-X*/ } +{ yymsp[0].minor.yy494 = yymsp[0].major; /*A-overwrites-X*/ } break; case 243: /* trigger_time ::= INSTEAD OF */ -{ yymsp[-1].minor.yy96 = TK_INSTEAD;} +{ yymsp[-1].minor.yy494 = TK_INSTEAD;} break; case 244: /* trigger_time ::= */ -{ yymsp[1].minor.yy96 = TK_BEFORE; } +{ yymsp[1].minor.yy494 = TK_BEFORE; } break; case 245: /* trigger_event ::= DELETE|INSERT */ case 246: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==246); -{yymsp[0].minor.yy350.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy350.b = 0;} +{yymsp[0].minor.yy90.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy90.b = 0;} break; case 247: /* trigger_event ::= UPDATE OF idlist */ -{yymsp[-2].minor.yy350.a = TK_UPDATE; yymsp[-2].minor.yy350.b = yymsp[0].minor.yy336;} +{yymsp[-2].minor.yy90.a = TK_UPDATE; yymsp[-2].minor.yy90.b = yymsp[0].minor.yy62;} break; case 248: /* when_clause ::= */ case 267: /* key_opt ::= */ yytestcase(yyruleno==267); - case 309: /* filter_opt ::= */ yytestcase(yyruleno==309); -{ yymsp[1].minor.yy490 = 0; } + case 315: /* filter_opt ::= */ yytestcase(yyruleno==315); +{ yymsp[1].minor.yy524 = 0; } break; case 249: /* when_clause ::= WHEN expr */ case 268: /* key_opt ::= KEY expr */ yytestcase(yyruleno==268); -{ yymsp[-1].minor.yy490 = yymsp[0].minor.yy490; } +{ yymsp[-1].minor.yy524 = yymsp[0].minor.yy524; } break; case 250: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ { - assert( yymsp[-2].minor.yy119!=0 ); - yymsp[-2].minor.yy119->pLast->pNext = yymsp[-1].minor.yy119; - yymsp[-2].minor.yy119->pLast = yymsp[-1].minor.yy119; + assert( yymsp[-2].minor.yy455!=0 ); + yymsp[-2].minor.yy455->pLast->pNext = yymsp[-1].minor.yy455; + yymsp[-2].minor.yy455->pLast = yymsp[-1].minor.yy455; } break; case 251: /* trigger_cmd_list ::= trigger_cmd SEMI */ { - assert( yymsp[-1].minor.yy119!=0 ); - yymsp[-1].minor.yy119->pLast = yymsp[-1].minor.yy119; + assert( yymsp[-1].minor.yy455!=0 ); + yymsp[-1].minor.yy455->pLast = yymsp[-1].minor.yy455; } break; case 252: /* trnm ::= nm DOT nm */ @@ -151529,58 +152412,58 @@ static YYACTIONTYPE yy_reduce( } break; case 255: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */ -{yylhsminor.yy119 = sqlite3TriggerUpdateStep(pParse, &yymsp[-5].minor.yy0, yymsp[-2].minor.yy42, yymsp[-1].minor.yy490, yymsp[-6].minor.yy96, yymsp[-7].minor.yy0.z, yymsp[0].minor.yy464);} - yymsp[-7].minor.yy119 = yylhsminor.yy119; +{yylhsminor.yy455 = sqlite3TriggerUpdateStep(pParse, &yymsp[-5].minor.yy0, yymsp[-2].minor.yy434, yymsp[-1].minor.yy524, yymsp[-6].minor.yy494, yymsp[-7].minor.yy0.z, yymsp[0].minor.yy294);} + yymsp[-7].minor.yy455 = yylhsminor.yy455; break; case 256: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ { - yylhsminor.yy119 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy336,yymsp[-2].minor.yy423,yymsp[-6].minor.yy96,yymsp[-1].minor.yy266,yymsp[-7].minor.yy464,yymsp[0].minor.yy464);/*yylhsminor.yy119-overwrites-yymsp[-6].minor.yy96*/ + yylhsminor.yy455 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy62,yymsp[-2].minor.yy457,yymsp[-6].minor.yy494,yymsp[-1].minor.yy136,yymsp[-7].minor.yy294,yymsp[0].minor.yy294);/*yylhsminor.yy455-overwrites-yymsp[-6].minor.yy494*/ } - yymsp[-7].minor.yy119 = yylhsminor.yy119; + yymsp[-7].minor.yy455 = yylhsminor.yy455; break; case 257: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ -{yylhsminor.yy119 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy490, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy464);} - yymsp[-5].minor.yy119 = yylhsminor.yy119; +{yylhsminor.yy455 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy524, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy294);} + yymsp[-5].minor.yy455 = yylhsminor.yy455; break; case 258: /* trigger_cmd ::= scanpt select scanpt */ -{yylhsminor.yy119 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy423, yymsp[-2].minor.yy464, yymsp[0].minor.yy464); /*yylhsminor.yy119-overwrites-yymsp[-1].minor.yy423*/} - yymsp[-2].minor.yy119 = yylhsminor.yy119; +{yylhsminor.yy455 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy457, yymsp[-2].minor.yy294, yymsp[0].minor.yy294); /*yylhsminor.yy455-overwrites-yymsp[-1].minor.yy457*/} + yymsp[-2].minor.yy455 = yylhsminor.yy455; break; case 259: /* expr ::= RAISE LP IGNORE RP */ { - yymsp[-3].minor.yy490 = sqlite3PExpr(pParse, TK_RAISE, 0, 0); - if( yymsp[-3].minor.yy490 ){ - yymsp[-3].minor.yy490->affinity = OE_Ignore; + yymsp[-3].minor.yy524 = sqlite3PExpr(pParse, TK_RAISE, 0, 0); + if( yymsp[-3].minor.yy524 ){ + yymsp[-3].minor.yy524->affinity = OE_Ignore; } } break; case 260: /* expr ::= RAISE LP raisetype COMMA nm RP */ { - yymsp[-5].minor.yy490 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1); - if( yymsp[-5].minor.yy490 ) { - yymsp[-5].minor.yy490->affinity = (char)yymsp[-3].minor.yy96; + yymsp[-5].minor.yy524 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1); + if( yymsp[-5].minor.yy524 ) { + yymsp[-5].minor.yy524->affinity = (char)yymsp[-3].minor.yy494; } } break; case 261: /* raisetype ::= ROLLBACK */ -{yymsp[0].minor.yy96 = OE_Rollback;} +{yymsp[0].minor.yy494 = OE_Rollback;} break; case 263: /* raisetype ::= FAIL */ -{yymsp[0].minor.yy96 = OE_Fail;} +{yymsp[0].minor.yy494 = OE_Fail;} break; case 264: /* cmd ::= DROP TRIGGER ifexists fullname */ { - sqlite3DropTrigger(pParse,yymsp[0].minor.yy167,yymsp[-1].minor.yy96); + sqlite3DropTrigger(pParse,yymsp[0].minor.yy483,yymsp[-1].minor.yy494); } break; case 265: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ { - sqlite3Attach(pParse, yymsp[-3].minor.yy490, yymsp[-1].minor.yy490, yymsp[0].minor.yy490); + sqlite3Attach(pParse, yymsp[-3].minor.yy524, yymsp[-1].minor.yy524, yymsp[0].minor.yy524); } break; case 266: /* cmd ::= DETACH database_kw_opt expr */ { - sqlite3Detach(pParse, yymsp[0].minor.yy490); + sqlite3Detach(pParse, yymsp[0].minor.yy524); } break; case 269: /* cmd ::= REINDEX */ @@ -151597,7 +152480,7 @@ static YYACTIONTYPE yy_reduce( break; case 273: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ { - sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy167,&yymsp[0].minor.yy0); + sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy483,&yymsp[0].minor.yy0); } break; case 274: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ @@ -151609,12 +152492,12 @@ static YYACTIONTYPE yy_reduce( case 275: /* add_column_fullname ::= fullname */ { disableLookaside(pParse); - sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy167); + sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy483); } break; case 276: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ { - sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy167, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0); + sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy483, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0); } break; case 277: /* cmd ::= create_vtab */ @@ -151625,7 +152508,7 @@ static YYACTIONTYPE yy_reduce( break; case 279: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ { - sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy96); + sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy494); } break; case 280: /* vtabarg ::= */ @@ -151638,182 +152521,204 @@ static YYACTIONTYPE yy_reduce( break; case 284: /* with ::= WITH wqlist */ case 285: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==285); -{ sqlite3WithPush(pParse, yymsp[0].minor.yy499, 1); } +{ sqlite3WithPush(pParse, yymsp[0].minor.yy59, 1); } break; case 286: /* wqlist ::= nm eidlist_opt AS LP select RP */ { - yymsp[-5].minor.yy499 = sqlite3WithAdd(pParse, 0, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy42, yymsp[-1].minor.yy423); /*A-overwrites-X*/ + yymsp[-5].minor.yy59 = sqlite3WithAdd(pParse, 0, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy434, yymsp[-1].minor.yy457); /*A-overwrites-X*/ } break; case 287: /* wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */ { - yymsp[-7].minor.yy499 = sqlite3WithAdd(pParse, yymsp[-7].minor.yy499, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy42, yymsp[-1].minor.yy423); + yymsp[-7].minor.yy59 = sqlite3WithAdd(pParse, yymsp[-7].minor.yy59, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy434, yymsp[-1].minor.yy457); } break; case 288: /* windowdefn_list ::= windowdefn */ -{ yylhsminor.yy147 = yymsp[0].minor.yy147; } - yymsp[0].minor.yy147 = yylhsminor.yy147; +{ yylhsminor.yy295 = yymsp[0].minor.yy295; } + yymsp[0].minor.yy295 = yylhsminor.yy295; break; case 289: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ { - assert( yymsp[0].minor.yy147!=0 ); - yymsp[0].minor.yy147->pNextWin = yymsp[-2].minor.yy147; - yylhsminor.yy147 = yymsp[0].minor.yy147; + assert( yymsp[0].minor.yy295!=0 ); + sqlite3WindowChain(pParse, yymsp[0].minor.yy295, yymsp[-2].minor.yy295); + yymsp[0].minor.yy295->pNextWin = yymsp[-2].minor.yy295; + yylhsminor.yy295 = yymsp[0].minor.yy295; } - yymsp[-2].minor.yy147 = yylhsminor.yy147; + yymsp[-2].minor.yy295 = yylhsminor.yy295; break; - case 290: /* windowdefn ::= nm AS window */ + case 290: /* windowdefn ::= nm AS LP window RP */ { - if( ALWAYS(yymsp[0].minor.yy147) ){ - yymsp[0].minor.yy147->zName = sqlite3DbStrNDup(pParse->db, yymsp[-2].minor.yy0.z, yymsp[-2].minor.yy0.n); + if( ALWAYS(yymsp[-1].minor.yy295) ){ + yymsp[-1].minor.yy295->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n); } - yylhsminor.yy147 = yymsp[0].minor.yy147; + yylhsminor.yy295 = yymsp[-1].minor.yy295; } - yymsp[-2].minor.yy147 = yylhsminor.yy147; + yymsp[-4].minor.yy295 = yylhsminor.yy295; break; - case 291: /* window ::= LP part_opt orderby_opt frame_opt RP */ + case 291: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */ { - yymsp[-4].minor.yy147 = yymsp[-1].minor.yy147; - if( ALWAYS(yymsp[-4].minor.yy147) ){ - yymsp[-4].minor.yy147->pPartition = yymsp[-3].minor.yy42; - yymsp[-4].minor.yy147->pOrderBy = yymsp[-2].minor.yy42; - } + yymsp[-4].minor.yy295 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy295, yymsp[-2].minor.yy434, yymsp[-1].minor.yy434, 0); +} + break; + case 292: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ +{ + yylhsminor.yy295 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy295, yymsp[-2].minor.yy434, yymsp[-1].minor.yy434, &yymsp[-5].minor.yy0); +} + yymsp[-5].minor.yy295 = yylhsminor.yy295; + break; + case 293: /* window ::= ORDER BY sortlist frame_opt */ +{ + yymsp[-3].minor.yy295 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy295, 0, yymsp[-1].minor.yy434, 0); +} + break; + case 294: /* window ::= nm ORDER BY sortlist frame_opt */ +{ + yylhsminor.yy295 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy295, 0, yymsp[-1].minor.yy434, &yymsp[-4].minor.yy0); } + yymsp[-4].minor.yy295 = yylhsminor.yy295; break; - case 292: /* part_opt ::= PARTITION BY nexprlist */ -{ yymsp[-2].minor.yy42 = yymsp[0].minor.yy42; } + case 295: /* window ::= frame_opt */ +{ + yylhsminor.yy295 = yymsp[0].minor.yy295; +} + yymsp[0].minor.yy295 = yylhsminor.yy295; break; - case 293: /* part_opt ::= */ -{ yymsp[1].minor.yy42 = 0; } + case 296: /* window ::= nm frame_opt */ +{ + yylhsminor.yy295 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy295, 0, 0, &yymsp[-1].minor.yy0); +} + yymsp[-1].minor.yy295 = yylhsminor.yy295; break; - case 294: /* frame_opt ::= */ + case 297: /* frame_opt ::= */ { - yymsp[1].minor.yy147 = sqlite3WindowAlloc(pParse, TK_RANGE, TK_UNBOUNDED, 0, TK_CURRENT, 0); + yymsp[1].minor.yy295 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0); } break; - case 295: /* frame_opt ::= range_or_rows frame_bound_s */ + case 298: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ { - yylhsminor.yy147 = sqlite3WindowAlloc(pParse, yymsp[-1].minor.yy96, yymsp[0].minor.yy317.eType, yymsp[0].minor.yy317.pExpr, TK_CURRENT, 0); + yylhsminor.yy295 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy494, yymsp[-1].minor.yy201.eType, yymsp[-1].minor.yy201.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy238); } - yymsp[-1].minor.yy147 = yylhsminor.yy147; + yymsp[-2].minor.yy295 = yylhsminor.yy295; break; - case 296: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e */ + case 299: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ { - yylhsminor.yy147 = sqlite3WindowAlloc(pParse, yymsp[-4].minor.yy96, yymsp[-2].minor.yy317.eType, yymsp[-2].minor.yy317.pExpr, yymsp[0].minor.yy317.eType, yymsp[0].minor.yy317.pExpr); + yylhsminor.yy295 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy494, yymsp[-3].minor.yy201.eType, yymsp[-3].minor.yy201.pExpr, yymsp[-1].minor.yy201.eType, yymsp[-1].minor.yy201.pExpr, yymsp[0].minor.yy238); } - yymsp[-4].minor.yy147 = yylhsminor.yy147; + yymsp[-5].minor.yy295 = yylhsminor.yy295; break; - case 297: /* range_or_rows ::= RANGE */ -{ yymsp[0].minor.yy96 = TK_RANGE; } + case 301: /* frame_bound_s ::= frame_bound */ + case 303: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==303); +{yylhsminor.yy201 = yymsp[0].minor.yy201;} + yymsp[0].minor.yy201 = yylhsminor.yy201; break; - case 298: /* range_or_rows ::= ROWS */ -{ yymsp[0].minor.yy96 = TK_ROWS; } + case 302: /* frame_bound_s ::= UNBOUNDED PRECEDING */ + case 304: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==304); + case 306: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==306); +{yylhsminor.yy201.eType = yymsp[-1].major; yylhsminor.yy201.pExpr = 0;} + yymsp[-1].minor.yy201 = yylhsminor.yy201; break; - case 299: /* frame_bound_s ::= frame_bound */ - case 301: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==301); -{ yylhsminor.yy317 = yymsp[0].minor.yy317; } - yymsp[0].minor.yy317 = yylhsminor.yy317; + case 305: /* frame_bound ::= expr PRECEDING|FOLLOWING */ +{yylhsminor.yy201.eType = yymsp[0].major; yylhsminor.yy201.pExpr = yymsp[-1].minor.yy524;} + yymsp[-1].minor.yy201 = yylhsminor.yy201; break; - case 300: /* frame_bound_s ::= UNBOUNDED PRECEDING */ - case 302: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==302); -{yymsp[-1].minor.yy317.eType = TK_UNBOUNDED; yymsp[-1].minor.yy317.pExpr = 0;} + case 307: /* frame_exclude_opt ::= */ +{yymsp[1].minor.yy238 = 0;} break; - case 303: /* frame_bound ::= expr PRECEDING */ -{ yylhsminor.yy317.eType = TK_PRECEDING; yylhsminor.yy317.pExpr = yymsp[-1].minor.yy490; } - yymsp[-1].minor.yy317 = yylhsminor.yy317; + case 308: /* frame_exclude_opt ::= EXCLUDE frame_exclude */ +{yymsp[-1].minor.yy238 = yymsp[0].minor.yy238;} break; - case 304: /* frame_bound ::= CURRENT ROW */ -{ yymsp[-1].minor.yy317.eType = TK_CURRENT ; yymsp[-1].minor.yy317.pExpr = 0; } + case 309: /* frame_exclude ::= NO OTHERS */ + case 310: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==310); +{yymsp[-1].minor.yy238 = yymsp[-1].major; /*A-overwrites-X*/} break; - case 305: /* frame_bound ::= expr FOLLOWING */ -{ yylhsminor.yy317.eType = TK_FOLLOWING; yylhsminor.yy317.pExpr = yymsp[-1].minor.yy490; } - yymsp[-1].minor.yy317 = yylhsminor.yy317; + case 311: /* frame_exclude ::= GROUP|TIES */ +{yymsp[0].minor.yy238 = yymsp[0].major; /*A-overwrites-X*/} break; - case 306: /* window_clause ::= WINDOW windowdefn_list */ -{ yymsp[-1].minor.yy147 = yymsp[0].minor.yy147; } + case 312: /* window_clause ::= WINDOW windowdefn_list */ +{ yymsp[-1].minor.yy295 = yymsp[0].minor.yy295; } break; - case 307: /* over_clause ::= filter_opt OVER window */ + case 313: /* over_clause ::= filter_opt OVER LP window RP */ { - yylhsminor.yy147 = yymsp[0].minor.yy147; - assert( yylhsminor.yy147!=0 ); - yylhsminor.yy147->pFilter = yymsp[-2].minor.yy490; + yylhsminor.yy295 = yymsp[-1].minor.yy295; + assert( yylhsminor.yy295!=0 ); + yylhsminor.yy295->pFilter = yymsp[-4].minor.yy524; } - yymsp[-2].minor.yy147 = yylhsminor.yy147; + yymsp[-4].minor.yy295 = yylhsminor.yy295; break; - case 308: /* over_clause ::= filter_opt OVER nm */ + case 314: /* over_clause ::= filter_opt OVER nm */ { - yylhsminor.yy147 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); - if( yylhsminor.yy147 ){ - yylhsminor.yy147->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n); - yylhsminor.yy147->pFilter = yymsp[-2].minor.yy490; + yylhsminor.yy295 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); + if( yylhsminor.yy295 ){ + yylhsminor.yy295->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n); + yylhsminor.yy295->pFilter = yymsp[-2].minor.yy524; }else{ - sqlite3ExprDelete(pParse->db, yymsp[-2].minor.yy490); + sqlite3ExprDelete(pParse->db, yymsp[-2].minor.yy524); } } - yymsp[-2].minor.yy147 = yylhsminor.yy147; + yymsp[-2].minor.yy295 = yylhsminor.yy295; break; - case 310: /* filter_opt ::= FILTER LP WHERE expr RP */ -{ yymsp[-4].minor.yy490 = yymsp[-1].minor.yy490; } + case 316: /* filter_opt ::= FILTER LP WHERE expr RP */ +{ yymsp[-4].minor.yy524 = yymsp[-1].minor.yy524; } break; default: - /* (311) input ::= cmdlist */ yytestcase(yyruleno==311); - /* (312) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==312); - /* (313) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=313); - /* (314) ecmd ::= SEMI */ yytestcase(yyruleno==314); - /* (315) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==315); - /* (316) ecmd ::= explain cmdx */ yytestcase(yyruleno==316); - /* (317) trans_opt ::= */ yytestcase(yyruleno==317); - /* (318) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==318); - /* (319) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==319); - /* (320) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==320); - /* (321) savepoint_opt ::= */ yytestcase(yyruleno==321); - /* (322) cmd ::= create_table create_table_args */ yytestcase(yyruleno==322); - /* (323) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==323); - /* (324) columnlist ::= columnname carglist */ yytestcase(yyruleno==324); - /* (325) nm ::= ID|INDEXED */ yytestcase(yyruleno==325); - /* (326) nm ::= STRING */ yytestcase(yyruleno==326); - /* (327) nm ::= JOIN_KW */ yytestcase(yyruleno==327); - /* (328) typetoken ::= typename */ yytestcase(yyruleno==328); - /* (329) typename ::= ID|STRING */ yytestcase(yyruleno==329); - /* (330) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=330); - /* (331) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=331); - /* (332) carglist ::= carglist ccons */ yytestcase(yyruleno==332); - /* (333) carglist ::= */ yytestcase(yyruleno==333); - /* (334) ccons ::= NULL onconf */ yytestcase(yyruleno==334); - /* (335) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==335); - /* (336) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==336); - /* (337) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=337); - /* (338) tconscomma ::= */ yytestcase(yyruleno==338); - /* (339) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=339); - /* (340) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=340); - /* (341) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=341); - /* (342) oneselect ::= values */ yytestcase(yyruleno==342); - /* (343) sclp ::= selcollist COMMA */ yytestcase(yyruleno==343); - /* (344) as ::= ID|STRING */ yytestcase(yyruleno==344); - /* (345) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=345); - /* (346) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==346); - /* (347) exprlist ::= nexprlist */ yytestcase(yyruleno==347); - /* (348) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=348); - /* (349) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=349); - /* (350) nmnum ::= ON */ yytestcase(yyruleno==350); - /* (351) nmnum ::= DELETE */ yytestcase(yyruleno==351); - /* (352) nmnum ::= DEFAULT */ yytestcase(yyruleno==352); - /* (353) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==353); - /* (354) foreach_clause ::= */ yytestcase(yyruleno==354); - /* (355) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==355); - /* (356) trnm ::= nm */ yytestcase(yyruleno==356); - /* (357) tridxby ::= */ yytestcase(yyruleno==357); - /* (358) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==358); - /* (359) database_kw_opt ::= */ yytestcase(yyruleno==359); - /* (360) kwcolumn_opt ::= */ yytestcase(yyruleno==360); - /* (361) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==361); - /* (362) vtabarglist ::= vtabarg */ yytestcase(yyruleno==362); - /* (363) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==363); - /* (364) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==364); - /* (365) anylist ::= */ yytestcase(yyruleno==365); - /* (366) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==366); - /* (367) anylist ::= anylist ANY */ yytestcase(yyruleno==367); - /* (368) with ::= */ yytestcase(yyruleno==368); + /* (317) input ::= cmdlist */ yytestcase(yyruleno==317); + /* (318) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==318); + /* (319) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=319); + /* (320) ecmd ::= SEMI */ yytestcase(yyruleno==320); + /* (321) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==321); + /* (322) ecmd ::= explain cmdx */ yytestcase(yyruleno==322); + /* (323) trans_opt ::= */ yytestcase(yyruleno==323); + /* (324) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==324); + /* (325) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==325); + /* (326) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==326); + /* (327) savepoint_opt ::= */ yytestcase(yyruleno==327); + /* (328) cmd ::= create_table create_table_args */ yytestcase(yyruleno==328); + /* (329) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==329); + /* (330) columnlist ::= columnname carglist */ yytestcase(yyruleno==330); + /* (331) nm ::= ID|INDEXED */ yytestcase(yyruleno==331); + /* (332) nm ::= STRING */ yytestcase(yyruleno==332); + /* (333) nm ::= JOIN_KW */ yytestcase(yyruleno==333); + /* (334) typetoken ::= typename */ yytestcase(yyruleno==334); + /* (335) typename ::= ID|STRING */ yytestcase(yyruleno==335); + /* (336) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=336); + /* (337) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=337); + /* (338) carglist ::= carglist ccons */ yytestcase(yyruleno==338); + /* (339) carglist ::= */ yytestcase(yyruleno==339); + /* (340) ccons ::= NULL onconf */ yytestcase(yyruleno==340); + /* (341) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==341); + /* (342) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==342); + /* (343) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=343); + /* (344) tconscomma ::= */ yytestcase(yyruleno==344); + /* (345) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=345); + /* (346) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=346); + /* (347) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=347); + /* (348) oneselect ::= values */ yytestcase(yyruleno==348); + /* (349) sclp ::= selcollist COMMA */ yytestcase(yyruleno==349); + /* (350) as ::= ID|STRING */ yytestcase(yyruleno==350); + /* (351) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=351); + /* (352) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==352); + /* (353) exprlist ::= nexprlist */ yytestcase(yyruleno==353); + /* (354) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=354); + /* (355) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=355); + /* (356) nmnum ::= ON */ yytestcase(yyruleno==356); + /* (357) nmnum ::= DELETE */ yytestcase(yyruleno==357); + /* (358) nmnum ::= DEFAULT */ yytestcase(yyruleno==358); + /* (359) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==359); + /* (360) foreach_clause ::= */ yytestcase(yyruleno==360); + /* (361) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==361); + /* (362) trnm ::= nm */ yytestcase(yyruleno==362); + /* (363) tridxby ::= */ yytestcase(yyruleno==363); + /* (364) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==364); + /* (365) database_kw_opt ::= */ yytestcase(yyruleno==365); + /* (366) kwcolumn_opt ::= */ yytestcase(yyruleno==366); + /* (367) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==367); + /* (368) vtabarglist ::= vtabarg */ yytestcase(yyruleno==368); + /* (369) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==369); + /* (370) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==370); + /* (371) anylist ::= */ yytestcase(yyruleno==371); + /* (372) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==372); + /* (373) anylist ::= anylist ANY */ yytestcase(yyruleno==373); + /* (374) with ::= */ yytestcase(yyruleno==374); break; /********** End reduce actions ************************************************/ }; @@ -152276,144 +153181,144 @@ const unsigned char ebcdicToAscii[] = { ** is substantially reduced. This is important for embedded applications ** on platforms with limited memory. */ -/* Hash score: 208 */ -/* zKWText[] encodes 923 bytes of keyword text in 614 bytes */ +/* Hash score: 214 */ +/* zKWText[] encodes 950 bytes of keyword text in 629 bytes */ /* REINDEXEDESCAPEACHECKEYBEFOREIGNOREGEXPLAINSTEADDATABASELECT */ -/* ABLEFTHENDEFERRABLELSEXCEPTRANSACTIONATURALTERAISEXCLUSIVE */ -/* XISTSAVEPOINTERSECTRIGGEREFERENCESCONSTRAINTOFFSETEMPORARY */ -/* UNIQUERYWITHOUTERELEASEATTACHAVINGROUPDATEBEGINNERANGEBETWEEN */ -/* OTHINGLOBYCASCADELETECASECOLLATECREATECURRENT_DATEDETACH */ -/* IMMEDIATEJOINSERTLIKEMATCHPLANALYZEPRAGMABORTVALUESVIRTUALIMIT */ -/* WHENOTNULLWHERECURSIVEAFTERENAMEANDEFAULTAUTOINCREMENTCAST */ -/* COLUMNCOMMITCONFLICTCROSSCURRENT_TIMESTAMPARTITIONDEFERRED */ -/* ISTINCTDROPRECEDINGFAILFILTEREPLACEFOLLOWINGFROMFULLIFISNULL */ -/* ORDERESTRICTOVERIGHTROLLBACKROWSUNBOUNDEDUNIONUSINGVACUUMVIEW */ -/* INDOWINITIALLYPRIMARY */ -static const char zKWText[613] = { +/* ABLEFTHENDEFERRABLELSEXCLUDELETEMPORARYCONSTRAINTERSECTIES */ +/* AVEPOINTOFFSETRANSACTIONATURALTERAISEXCEPTRIGGEREFERENCES */ +/* UNIQUERYWITHOUTERELEASEXCLUSIVEXISTSATTACHAVINGLOBEGINNERANGE */ +/* BETWEENOTHINGROUPSCASCADETACHCASECOLLATECREATECURRENT_DATE */ +/* IMMEDIATEJOINSERTLIKEMATCHPLANALYZEPRAGMABORTUPDATEVALUES */ +/* VIRTUALIMITWHENOTNULLWHERECURSIVEAFTERENAMEANDEFAULT */ +/* AUTOINCREMENTCASTCOLUMNCOMMITCONFLICTCROSSCURRENT_TIMESTAMP */ +/* ARTITIONDEFERREDISTINCTDROPRECEDINGFAILFILTEREPLACEFOLLOWING */ +/* FROMFULLIFISNULLORDERESTRICTOTHERSOVERIGHTROLLBACKROWS */ +/* UNBOUNDEDUNIONUSINGVACUUMVIEWINDOWBYINITIALLYPRIMARY */ +static const char zKWText[628] = { 'R','E','I','N','D','E','X','E','D','E','S','C','A','P','E','A','C','H', 'E','C','K','E','Y','B','E','F','O','R','E','I','G','N','O','R','E','G', 'E','X','P','L','A','I','N','S','T','E','A','D','D','A','T','A','B','A', 'S','E','L','E','C','T','A','B','L','E','F','T','H','E','N','D','E','F', - 'E','R','R','A','B','L','E','L','S','E','X','C','E','P','T','R','A','N', - 'S','A','C','T','I','O','N','A','T','U','R','A','L','T','E','R','A','I', - 'S','E','X','C','L','U','S','I','V','E','X','I','S','T','S','A','V','E', - 'P','O','I','N','T','E','R','S','E','C','T','R','I','G','G','E','R','E', - 'F','E','R','E','N','C','E','S','C','O','N','S','T','R','A','I','N','T', - 'O','F','F','S','E','T','E','M','P','O','R','A','R','Y','U','N','I','Q', - 'U','E','R','Y','W','I','T','H','O','U','T','E','R','E','L','E','A','S', - 'E','A','T','T','A','C','H','A','V','I','N','G','R','O','U','P','D','A', - 'T','E','B','E','G','I','N','N','E','R','A','N','G','E','B','E','T','W', - 'E','E','N','O','T','H','I','N','G','L','O','B','Y','C','A','S','C','A', - 'D','E','L','E','T','E','C','A','S','E','C','O','L','L','A','T','E','C', - 'R','E','A','T','E','C','U','R','R','E','N','T','_','D','A','T','E','D', - 'E','T','A','C','H','I','M','M','E','D','I','A','T','E','J','O','I','N', - 'S','E','R','T','L','I','K','E','M','A','T','C','H','P','L','A','N','A', - 'L','Y','Z','E','P','R','A','G','M','A','B','O','R','T','V','A','L','U', - 'E','S','V','I','R','T','U','A','L','I','M','I','T','W','H','E','N','O', - 'T','N','U','L','L','W','H','E','R','E','C','U','R','S','I','V','E','A', - 'F','T','E','R','E','N','A','M','E','A','N','D','E','F','A','U','L','T', - 'A','U','T','O','I','N','C','R','E','M','E','N','T','C','A','S','T','C', - 'O','L','U','M','N','C','O','M','M','I','T','C','O','N','F','L','I','C', - 'T','C','R','O','S','S','C','U','R','R','E','N','T','_','T','I','M','E', - 'S','T','A','M','P','A','R','T','I','T','I','O','N','D','E','F','E','R', - 'R','E','D','I','S','T','I','N','C','T','D','R','O','P','R','E','C','E', - 'D','I','N','G','F','A','I','L','F','I','L','T','E','R','E','P','L','A', - 'C','E','F','O','L','L','O','W','I','N','G','F','R','O','M','F','U','L', - 'L','I','F','I','S','N','U','L','L','O','R','D','E','R','E','S','T','R', - 'I','C','T','O','V','E','R','I','G','H','T','R','O','L','L','B','A','C', - 'K','R','O','W','S','U','N','B','O','U','N','D','E','D','U','N','I','O', - 'N','U','S','I','N','G','V','A','C','U','U','M','V','I','E','W','I','N', - 'D','O','W','I','N','I','T','I','A','L','L','Y','P','R','I','M','A','R', - 'Y', + 'E','R','R','A','B','L','E','L','S','E','X','C','L','U','D','E','L','E', + 'T','E','M','P','O','R','A','R','Y','C','O','N','S','T','R','A','I','N', + 'T','E','R','S','E','C','T','I','E','S','A','V','E','P','O','I','N','T', + 'O','F','F','S','E','T','R','A','N','S','A','C','T','I','O','N','A','T', + 'U','R','A','L','T','E','R','A','I','S','E','X','C','E','P','T','R','I', + 'G','G','E','R','E','F','E','R','E','N','C','E','S','U','N','I','Q','U', + 'E','R','Y','W','I','T','H','O','U','T','E','R','E','L','E','A','S','E', + 'X','C','L','U','S','I','V','E','X','I','S','T','S','A','T','T','A','C', + 'H','A','V','I','N','G','L','O','B','E','G','I','N','N','E','R','A','N', + 'G','E','B','E','T','W','E','E','N','O','T','H','I','N','G','R','O','U', + 'P','S','C','A','S','C','A','D','E','T','A','C','H','C','A','S','E','C', + 'O','L','L','A','T','E','C','R','E','A','T','E','C','U','R','R','E','N', + 'T','_','D','A','T','E','I','M','M','E','D','I','A','T','E','J','O','I', + 'N','S','E','R','T','L','I','K','E','M','A','T','C','H','P','L','A','N', + 'A','L','Y','Z','E','P','R','A','G','M','A','B','O','R','T','U','P','D', + 'A','T','E','V','A','L','U','E','S','V','I','R','T','U','A','L','I','M', + 'I','T','W','H','E','N','O','T','N','U','L','L','W','H','E','R','E','C', + 'U','R','S','I','V','E','A','F','T','E','R','E','N','A','M','E','A','N', + 'D','E','F','A','U','L','T','A','U','T','O','I','N','C','R','E','M','E', + 'N','T','C','A','S','T','C','O','L','U','M','N','C','O','M','M','I','T', + 'C','O','N','F','L','I','C','T','C','R','O','S','S','C','U','R','R','E', + 'N','T','_','T','I','M','E','S','T','A','M','P','A','R','T','I','T','I', + 'O','N','D','E','F','E','R','R','E','D','I','S','T','I','N','C','T','D', + 'R','O','P','R','E','C','E','D','I','N','G','F','A','I','L','F','I','L', + 'T','E','R','E','P','L','A','C','E','F','O','L','L','O','W','I','N','G', + 'F','R','O','M','F','U','L','L','I','F','I','S','N','U','L','L','O','R', + 'D','E','R','E','S','T','R','I','C','T','O','T','H','E','R','S','O','V', + 'E','R','I','G','H','T','R','O','L','L','B','A','C','K','R','O','W','S', + 'U','N','B','O','U','N','D','E','D','U','N','I','O','N','U','S','I','N', + 'G','V','A','C','U','U','M','V','I','E','W','I','N','D','O','W','B','Y', + 'I','N','I','T','I','A','L','L','Y','P','R','I','M','A','R','Y', }; /* aKWHash[i] is the hash value for the i-th keyword */ static const unsigned char aKWHash[127] = { - 74, 109, 124, 72, 106, 45, 0, 0, 81, 0, 76, 61, 0, - 42, 12, 77, 15, 0, 123, 84, 54, 118, 125, 19, 0, 0, - 130, 0, 128, 121, 0, 22, 96, 0, 9, 0, 0, 115, 69, - 0, 67, 6, 0, 48, 93, 136, 0, 126, 104, 0, 0, 44, - 0, 107, 24, 0, 17, 0, 131, 53, 23, 0, 5, 62, 132, - 99, 0, 0, 135, 110, 60, 134, 57, 113, 55, 0, 94, 0, - 103, 26, 0, 102, 0, 0, 0, 98, 95, 100, 105, 117, 14, - 39, 116, 0, 80, 0, 133, 114, 92, 59, 0, 129, 79, 119, - 86, 46, 83, 0, 0, 97, 40, 122, 120, 0, 127, 0, 0, - 29, 0, 89, 87, 88, 0, 20, 85, 111, 56, + 75, 111, 127, 73, 108, 29, 0, 0, 83, 0, 77, 63, 0, + 37, 33, 78, 15, 0, 126, 86, 57, 120, 128, 19, 0, 0, + 133, 0, 131, 123, 0, 22, 98, 0, 9, 0, 0, 117, 71, + 0, 69, 6, 0, 49, 95, 140, 0, 129, 106, 0, 0, 54, + 0, 109, 24, 0, 17, 0, 134, 56, 23, 26, 5, 58, 135, + 101, 0, 0, 139, 112, 62, 138, 59, 115, 65, 0, 96, 0, + 105, 45, 0, 104, 0, 0, 0, 100, 97, 102, 107, 119, 14, + 31, 118, 0, 81, 0, 136, 116, 137, 61, 124, 132, 80, 121, + 88, 30, 85, 0, 0, 99, 35, 125, 122, 0, 130, 0, 0, + 41, 0, 91, 89, 90, 0, 20, 87, 113, 82, }; /* aKWNext[] forms the hash collision chain. If aKWHash[i]==0 ** then the i-th keyword has no more hash collisions. Otherwise, ** the next keyword with the same hash is aKWHash[i]-1. */ -static const unsigned char aKWNext[136] = { +static const unsigned char aKWNext[140] = { 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, - 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 33, 0, 21, 0, 0, 0, 0, 0, 50, - 0, 43, 3, 47, 0, 0, 32, 0, 0, 0, 0, 0, 0, - 0, 1, 64, 0, 0, 65, 0, 41, 0, 38, 0, 0, 0, - 0, 0, 49, 75, 0, 0, 30, 0, 58, 0, 0, 0, 31, - 63, 16, 34, 10, 0, 0, 0, 0, 0, 0, 0, 11, 70, - 91, 0, 0, 8, 0, 108, 0, 101, 28, 52, 68, 0, 112, - 0, 73, 51, 0, 90, 27, 37, 0, 71, 36, 82, 0, 35, - 66, 25, 18, 0, 0, 78, + 0, 0, 0, 21, 0, 0, 12, 0, 0, 0, 0, 0, 0, + 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 51, 28, 0, 0, 38, 0, 0, 0, 44, 0, 0, 0, 3, + 0, 0, 67, 1, 66, 0, 0, 0, 36, 0, 47, 0, 0, + 0, 0, 0, 48, 50, 76, 0, 0, 42, 0, 60, 0, 0, + 0, 43, 0, 16, 55, 10, 0, 0, 0, 0, 0, 0, 0, + 11, 72, 93, 0, 0, 8, 0, 110, 0, 103, 40, 53, 70, + 0, 114, 0, 74, 52, 0, 0, 92, 39, 46, 0, 68, 32, + 84, 0, 34, 27, 25, 18, 94, 0, 64, 79, }; /* aKWLen[i] is the length (in bytes) of the i-th keyword */ -static const unsigned char aKWLen[136] = { +static const unsigned char aKWLen[140] = { 7, 7, 5, 4, 6, 4, 5, 3, 6, 7, 3, 6, 6, - 7, 7, 3, 8, 2, 6, 5, 4, 4, 3, 10, 4, 6, - 11, 6, 2, 7, 5, 5, 9, 6, 9, 9, 7, 10, 10, - 4, 6, 2, 3, 9, 4, 2, 6, 5, 7, 4, 5, 7, - 6, 6, 5, 6, 5, 5, 5, 7, 7, 4, 2, 7, 3, - 6, 4, 7, 6, 12, 6, 9, 4, 6, 4, 5, 4, 7, - 6, 5, 6, 7, 5, 4, 7, 3, 2, 4, 5, 9, 5, - 6, 3, 7, 13, 2, 2, 4, 6, 6, 8, 5, 17, 12, - 7, 9, 8, 8, 2, 4, 9, 4, 6, 7, 9, 4, 4, - 2, 6, 5, 8, 4, 5, 8, 4, 3, 9, 5, 5, 6, - 4, 6, 2, 9, 3, 7, + 7, 7, 3, 8, 2, 6, 5, 4, 4, 3, 10, 4, 7, + 6, 9, 4, 2, 10, 9, 4, 9, 4, 6, 2, 3, 11, + 6, 2, 7, 5, 5, 6, 7, 10, 6, 5, 7, 4, 5, + 7, 9, 6, 6, 6, 4, 5, 5, 5, 7, 7, 6, 5, + 7, 3, 6, 4, 7, 6, 12, 9, 4, 6, 4, 5, 4, + 7, 6, 5, 6, 6, 7, 5, 4, 7, 3, 2, 4, 5, + 9, 5, 6, 3, 7, 13, 2, 2, 4, 6, 6, 8, 5, + 17, 12, 7, 9, 8, 8, 2, 4, 9, 4, 6, 7, 9, + 4, 4, 2, 6, 5, 8, 6, 4, 5, 8, 4, 3, 9, + 5, 5, 6, 4, 6, 2, 2, 9, 3, 7, }; /* aKWOffset[i] is the index into zKWText[] of the start of ** the text for the i-th keyword. */ -static const unsigned short int aKWOffset[136] = { +static const unsigned short int aKWOffset[140] = { 0, 2, 2, 8, 9, 14, 16, 20, 23, 25, 25, 29, 33, 36, 41, 46, 48, 53, 54, 59, 62, 65, 67, 69, 78, 81, - 86, 91, 95, 96, 101, 105, 109, 117, 122, 128, 136, 142, 152, - 159, 162, 162, 165, 167, 167, 171, 176, 179, 184, 184, 188, 192, - 199, 204, 209, 212, 218, 221, 225, 230, 236, 242, 245, 247, 248, - 252, 258, 262, 269, 275, 287, 293, 302, 304, 310, 314, 319, 321, - 328, 333, 338, 344, 350, 355, 358, 358, 358, 361, 365, 368, 377, - 381, 387, 389, 396, 398, 400, 409, 413, 419, 425, 433, 438, 438, - 438, 454, 463, 470, 471, 478, 481, 490, 494, 499, 506, 515, 519, - 523, 525, 531, 535, 543, 546, 551, 559, 559, 563, 572, 577, 582, - 588, 591, 594, 597, 602, 606, + 86, 90, 90, 94, 99, 106, 114, 117, 123, 126, 126, 129, 131, + 136, 140, 141, 146, 150, 154, 159, 165, 175, 178, 183, 183, 187, + 191, 197, 205, 211, 216, 221, 224, 227, 231, 236, 242, 248, 248, + 254, 255, 259, 265, 269, 276, 282, 294, 303, 305, 311, 315, 320, + 322, 329, 334, 339, 345, 351, 357, 362, 365, 365, 365, 368, 372, + 375, 384, 388, 394, 396, 403, 405, 407, 416, 420, 426, 432, 440, + 445, 445, 445, 461, 470, 477, 478, 485, 488, 497, 501, 506, 513, + 522, 526, 530, 532, 538, 542, 550, 556, 559, 564, 572, 572, 576, + 585, 590, 595, 601, 604, 607, 610, 612, 617, 621, }; /* aKWCode[i] is the parser symbol code for the i-th keyword */ -static const unsigned char aKWCode[136] = { +static const unsigned char aKWCode[140] = { TK_REINDEX, TK_INDEXED, TK_INDEX, TK_DESC, TK_ESCAPE, TK_EACH, TK_CHECK, TK_KEY, TK_BEFORE, TK_FOREIGN, TK_FOR, TK_IGNORE, TK_LIKE_KW, TK_EXPLAIN, TK_INSTEAD, TK_ADD, TK_DATABASE, TK_AS, TK_SELECT, TK_TABLE, TK_JOIN_KW, TK_THEN, TK_END, TK_DEFERRABLE, TK_ELSE, - TK_EXCEPT, TK_TRANSACTION,TK_ACTION, TK_ON, TK_JOIN_KW, - TK_ALTER, TK_RAISE, TK_EXCLUSIVE, TK_EXISTS, TK_SAVEPOINT, - TK_INTERSECT, TK_TRIGGER, TK_REFERENCES, TK_CONSTRAINT, TK_INTO, - TK_OFFSET, TK_OF, TK_SET, TK_TEMP, TK_TEMP, - TK_OR, TK_UNIQUE, TK_QUERY, TK_WITHOUT, TK_WITH, - TK_JOIN_KW, TK_RELEASE, TK_ATTACH, TK_HAVING, TK_GROUP, - TK_UPDATE, TK_BEGIN, TK_JOIN_KW, TK_RANGE, TK_BETWEEN, - TK_NOTHING, TK_LIKE_KW, TK_BY, TK_CASCADE, TK_ASC, - TK_DELETE, TK_CASE, TK_COLLATE, TK_CREATE, TK_CTIME_KW, - TK_DETACH, TK_IMMEDIATE, TK_JOIN, TK_INSERT, TK_LIKE_KW, - TK_MATCH, TK_PLAN, TK_ANALYZE, TK_PRAGMA, TK_ABORT, - TK_VALUES, TK_VIRTUAL, TK_LIMIT, TK_WHEN, TK_NOTNULL, - TK_NOT, TK_NO, TK_NULL, TK_WHERE, TK_RECURSIVE, - TK_AFTER, TK_RENAME, TK_AND, TK_DEFAULT, TK_AUTOINCR, - TK_TO, TK_IN, TK_CAST, TK_COLUMNKW, TK_COMMIT, - TK_CONFLICT, TK_JOIN_KW, TK_CTIME_KW, TK_CTIME_KW, TK_CURRENT, - TK_PARTITION, TK_DEFERRED, TK_DISTINCT, TK_IS, TK_DROP, - TK_PRECEDING, TK_FAIL, TK_FILTER, TK_REPLACE, TK_FOLLOWING, - TK_FROM, TK_JOIN_KW, TK_IF, TK_ISNULL, TK_ORDER, - TK_RESTRICT, TK_OVER, TK_JOIN_KW, TK_ROLLBACK, TK_ROWS, - TK_ROW, TK_UNBOUNDED, TK_UNION, TK_USING, TK_VACUUM, - TK_VIEW, TK_WINDOW, TK_DO, TK_INITIALLY, TK_ALL, - TK_PRIMARY, + TK_EXCLUDE, TK_DELETE, TK_TEMP, TK_TEMP, TK_OR, + TK_CONSTRAINT, TK_INTERSECT, TK_TIES, TK_SAVEPOINT, TK_INTO, + TK_OFFSET, TK_OF, TK_SET, TK_TRANSACTION,TK_ACTION, + TK_ON, TK_JOIN_KW, TK_ALTER, TK_RAISE, TK_EXCEPT, + TK_TRIGGER, TK_REFERENCES, TK_UNIQUE, TK_QUERY, TK_WITHOUT, + TK_WITH, TK_JOIN_KW, TK_RELEASE, TK_EXCLUSIVE, TK_EXISTS, + TK_ATTACH, TK_HAVING, TK_LIKE_KW, TK_BEGIN, TK_JOIN_KW, + TK_RANGE, TK_BETWEEN, TK_NOTHING, TK_GROUPS, TK_GROUP, + TK_CASCADE, TK_ASC, TK_DETACH, TK_CASE, TK_COLLATE, + TK_CREATE, TK_CTIME_KW, TK_IMMEDIATE, TK_JOIN, TK_INSERT, + TK_LIKE_KW, TK_MATCH, TK_PLAN, TK_ANALYZE, TK_PRAGMA, + TK_ABORT, TK_UPDATE, TK_VALUES, TK_VIRTUAL, TK_LIMIT, + TK_WHEN, TK_NOTNULL, TK_NOT, TK_NO, TK_NULL, + TK_WHERE, TK_RECURSIVE, TK_AFTER, TK_RENAME, TK_AND, + TK_DEFAULT, TK_AUTOINCR, TK_TO, TK_IN, TK_CAST, + TK_COLUMNKW, TK_COMMIT, TK_CONFLICT, TK_JOIN_KW, TK_CTIME_KW, + TK_CTIME_KW, TK_CURRENT, TK_PARTITION, TK_DEFERRED, TK_DISTINCT, + TK_IS, TK_DROP, TK_PRECEDING, TK_FAIL, TK_FILTER, + TK_REPLACE, TK_FOLLOWING, TK_FROM, TK_JOIN_KW, TK_IF, + TK_ISNULL, TK_ORDER, TK_RESTRICT, TK_OTHERS, TK_OVER, + TK_JOIN_KW, TK_ROLLBACK, TK_ROWS, TK_ROW, TK_UNBOUNDED, + TK_UNION, TK_USING, TK_VACUUM, TK_VIEW, TK_WINDOW, + TK_DO, TK_BY, TK_INITIALLY, TK_ALL, TK_PRIMARY, }; /* Check to see if z[0..n-1] is a keyword. If it is, write the ** parser symbol code for that keyword into *pType. Always @@ -152459,117 +153364,121 @@ static int keywordCode(const char *z, int n, int *pType){ testcase( i==22 ); /* END */ testcase( i==23 ); /* DEFERRABLE */ testcase( i==24 ); /* ELSE */ - testcase( i==25 ); /* EXCEPT */ - testcase( i==26 ); /* TRANSACTION */ - testcase( i==27 ); /* ACTION */ - testcase( i==28 ); /* ON */ - testcase( i==29 ); /* NATURAL */ - testcase( i==30 ); /* ALTER */ - testcase( i==31 ); /* RAISE */ - testcase( i==32 ); /* EXCLUSIVE */ - testcase( i==33 ); /* EXISTS */ - testcase( i==34 ); /* SAVEPOINT */ - testcase( i==35 ); /* INTERSECT */ - testcase( i==36 ); /* TRIGGER */ - testcase( i==37 ); /* REFERENCES */ - testcase( i==38 ); /* CONSTRAINT */ - testcase( i==39 ); /* INTO */ - testcase( i==40 ); /* OFFSET */ - testcase( i==41 ); /* OF */ - testcase( i==42 ); /* SET */ - testcase( i==43 ); /* TEMPORARY */ - testcase( i==44 ); /* TEMP */ - testcase( i==45 ); /* OR */ - testcase( i==46 ); /* UNIQUE */ - testcase( i==47 ); /* QUERY */ - testcase( i==48 ); /* WITHOUT */ - testcase( i==49 ); /* WITH */ - testcase( i==50 ); /* OUTER */ - testcase( i==51 ); /* RELEASE */ - testcase( i==52 ); /* ATTACH */ - testcase( i==53 ); /* HAVING */ - testcase( i==54 ); /* GROUP */ - testcase( i==55 ); /* UPDATE */ - testcase( i==56 ); /* BEGIN */ - testcase( i==57 ); /* INNER */ - testcase( i==58 ); /* RANGE */ - testcase( i==59 ); /* BETWEEN */ - testcase( i==60 ); /* NOTHING */ - testcase( i==61 ); /* GLOB */ - testcase( i==62 ); /* BY */ - testcase( i==63 ); /* CASCADE */ - testcase( i==64 ); /* ASC */ - testcase( i==65 ); /* DELETE */ - testcase( i==66 ); /* CASE */ - testcase( i==67 ); /* COLLATE */ - testcase( i==68 ); /* CREATE */ - testcase( i==69 ); /* CURRENT_DATE */ - testcase( i==70 ); /* DETACH */ - testcase( i==71 ); /* IMMEDIATE */ - testcase( i==72 ); /* JOIN */ - testcase( i==73 ); /* INSERT */ - testcase( i==74 ); /* LIKE */ - testcase( i==75 ); /* MATCH */ - testcase( i==76 ); /* PLAN */ - testcase( i==77 ); /* ANALYZE */ - testcase( i==78 ); /* PRAGMA */ - testcase( i==79 ); /* ABORT */ - testcase( i==80 ); /* VALUES */ - testcase( i==81 ); /* VIRTUAL */ - testcase( i==82 ); /* LIMIT */ - testcase( i==83 ); /* WHEN */ - testcase( i==84 ); /* NOTNULL */ - testcase( i==85 ); /* NOT */ - testcase( i==86 ); /* NO */ - testcase( i==87 ); /* NULL */ - testcase( i==88 ); /* WHERE */ - testcase( i==89 ); /* RECURSIVE */ - testcase( i==90 ); /* AFTER */ - testcase( i==91 ); /* RENAME */ - testcase( i==92 ); /* AND */ - testcase( i==93 ); /* DEFAULT */ - testcase( i==94 ); /* AUTOINCREMENT */ - testcase( i==95 ); /* TO */ - testcase( i==96 ); /* IN */ - testcase( i==97 ); /* CAST */ - testcase( i==98 ); /* COLUMN */ - testcase( i==99 ); /* COMMIT */ - testcase( i==100 ); /* CONFLICT */ - testcase( i==101 ); /* CROSS */ - testcase( i==102 ); /* CURRENT_TIMESTAMP */ - testcase( i==103 ); /* CURRENT_TIME */ - testcase( i==104 ); /* CURRENT */ - testcase( i==105 ); /* PARTITION */ - testcase( i==106 ); /* DEFERRED */ - testcase( i==107 ); /* DISTINCT */ - testcase( i==108 ); /* IS */ - testcase( i==109 ); /* DROP */ - testcase( i==110 ); /* PRECEDING */ - testcase( i==111 ); /* FAIL */ - testcase( i==112 ); /* FILTER */ - testcase( i==113 ); /* REPLACE */ - testcase( i==114 ); /* FOLLOWING */ - testcase( i==115 ); /* FROM */ - testcase( i==116 ); /* FULL */ - testcase( i==117 ); /* IF */ - testcase( i==118 ); /* ISNULL */ - testcase( i==119 ); /* ORDER */ - testcase( i==120 ); /* RESTRICT */ - testcase( i==121 ); /* OVER */ - testcase( i==122 ); /* RIGHT */ - testcase( i==123 ); /* ROLLBACK */ - testcase( i==124 ); /* ROWS */ - testcase( i==125 ); /* ROW */ - testcase( i==126 ); /* UNBOUNDED */ - testcase( i==127 ); /* UNION */ - testcase( i==128 ); /* USING */ - testcase( i==129 ); /* VACUUM */ - testcase( i==130 ); /* VIEW */ - testcase( i==131 ); /* WINDOW */ - testcase( i==132 ); /* DO */ - testcase( i==133 ); /* INITIALLY */ - testcase( i==134 ); /* ALL */ - testcase( i==135 ); /* PRIMARY */ + testcase( i==25 ); /* EXCLUDE */ + testcase( i==26 ); /* DELETE */ + testcase( i==27 ); /* TEMPORARY */ + testcase( i==28 ); /* TEMP */ + testcase( i==29 ); /* OR */ + testcase( i==30 ); /* CONSTRAINT */ + testcase( i==31 ); /* INTERSECT */ + testcase( i==32 ); /* TIES */ + testcase( i==33 ); /* SAVEPOINT */ + testcase( i==34 ); /* INTO */ + testcase( i==35 ); /* OFFSET */ + testcase( i==36 ); /* OF */ + testcase( i==37 ); /* SET */ + testcase( i==38 ); /* TRANSACTION */ + testcase( i==39 ); /* ACTION */ + testcase( i==40 ); /* ON */ + testcase( i==41 ); /* NATURAL */ + testcase( i==42 ); /* ALTER */ + testcase( i==43 ); /* RAISE */ + testcase( i==44 ); /* EXCEPT */ + testcase( i==45 ); /* TRIGGER */ + testcase( i==46 ); /* REFERENCES */ + testcase( i==47 ); /* UNIQUE */ + testcase( i==48 ); /* QUERY */ + testcase( i==49 ); /* WITHOUT */ + testcase( i==50 ); /* WITH */ + testcase( i==51 ); /* OUTER */ + testcase( i==52 ); /* RELEASE */ + testcase( i==53 ); /* EXCLUSIVE */ + testcase( i==54 ); /* EXISTS */ + testcase( i==55 ); /* ATTACH */ + testcase( i==56 ); /* HAVING */ + testcase( i==57 ); /* GLOB */ + testcase( i==58 ); /* BEGIN */ + testcase( i==59 ); /* INNER */ + testcase( i==60 ); /* RANGE */ + testcase( i==61 ); /* BETWEEN */ + testcase( i==62 ); /* NOTHING */ + testcase( i==63 ); /* GROUPS */ + testcase( i==64 ); /* GROUP */ + testcase( i==65 ); /* CASCADE */ + testcase( i==66 ); /* ASC */ + testcase( i==67 ); /* DETACH */ + testcase( i==68 ); /* CASE */ + testcase( i==69 ); /* COLLATE */ + testcase( i==70 ); /* CREATE */ + testcase( i==71 ); /* CURRENT_DATE */ + testcase( i==72 ); /* IMMEDIATE */ + testcase( i==73 ); /* JOIN */ + testcase( i==74 ); /* INSERT */ + testcase( i==75 ); /* LIKE */ + testcase( i==76 ); /* MATCH */ + testcase( i==77 ); /* PLAN */ + testcase( i==78 ); /* ANALYZE */ + testcase( i==79 ); /* PRAGMA */ + testcase( i==80 ); /* ABORT */ + testcase( i==81 ); /* UPDATE */ + testcase( i==82 ); /* VALUES */ + testcase( i==83 ); /* VIRTUAL */ + testcase( i==84 ); /* LIMIT */ + testcase( i==85 ); /* WHEN */ + testcase( i==86 ); /* NOTNULL */ + testcase( i==87 ); /* NOT */ + testcase( i==88 ); /* NO */ + testcase( i==89 ); /* NULL */ + testcase( i==90 ); /* WHERE */ + testcase( i==91 ); /* RECURSIVE */ + testcase( i==92 ); /* AFTER */ + testcase( i==93 ); /* RENAME */ + testcase( i==94 ); /* AND */ + testcase( i==95 ); /* DEFAULT */ + testcase( i==96 ); /* AUTOINCREMENT */ + testcase( i==97 ); /* TO */ + testcase( i==98 ); /* IN */ + testcase( i==99 ); /* CAST */ + testcase( i==100 ); /* COLUMN */ + testcase( i==101 ); /* COMMIT */ + testcase( i==102 ); /* CONFLICT */ + testcase( i==103 ); /* CROSS */ + testcase( i==104 ); /* CURRENT_TIMESTAMP */ + testcase( i==105 ); /* CURRENT_TIME */ + testcase( i==106 ); /* CURRENT */ + testcase( i==107 ); /* PARTITION */ + testcase( i==108 ); /* DEFERRED */ + testcase( i==109 ); /* DISTINCT */ + testcase( i==110 ); /* IS */ + testcase( i==111 ); /* DROP */ + testcase( i==112 ); /* PRECEDING */ + testcase( i==113 ); /* FAIL */ + testcase( i==114 ); /* FILTER */ + testcase( i==115 ); /* REPLACE */ + testcase( i==116 ); /* FOLLOWING */ + testcase( i==117 ); /* FROM */ + testcase( i==118 ); /* FULL */ + testcase( i==119 ); /* IF */ + testcase( i==120 ); /* ISNULL */ + testcase( i==121 ); /* ORDER */ + testcase( i==122 ); /* RESTRICT */ + testcase( i==123 ); /* OTHERS */ + testcase( i==124 ); /* OVER */ + testcase( i==125 ); /* RIGHT */ + testcase( i==126 ); /* ROLLBACK */ + testcase( i==127 ); /* ROWS */ + testcase( i==128 ); /* ROW */ + testcase( i==129 ); /* UNBOUNDED */ + testcase( i==130 ); /* UNION */ + testcase( i==131 ); /* USING */ + testcase( i==132 ); /* VACUUM */ + testcase( i==133 ); /* VIEW */ + testcase( i==134 ); /* WINDOW */ + testcase( i==135 ); /* DO */ + testcase( i==136 ); /* BY */ + testcase( i==137 ); /* INITIALLY */ + testcase( i==138 ); /* ALL */ + testcase( i==139 ); /* PRIMARY */ *pType = aKWCode[i]; break; } @@ -152581,7 +153490,7 @@ SQLITE_PRIVATE int sqlite3KeywordCode(const unsigned char *z, int n){ keywordCode((char*)z, n, &id); return id; } -#define SQLITE_N_KEYWORD 136 +#define SQLITE_N_KEYWORD 140 SQLITE_API int sqlite3_keyword_name(int i,const char **pzName,int *pnName){ if( i<0 || i>=SQLITE_N_KEYWORD ) return SQLITE_ERROR; *pzName = zKWText + aKWOffset[i]; @@ -153014,6 +153923,7 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr #ifdef sqlite3Parser_ENGINEALWAYSONSTACK yyParser sEngine; /* Space to hold the Lemon-generated Parser object */ #endif + VVA_ONLY( u8 startedWithOom = db->mallocFailed ); assert( zSql!=0 ); mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH]; @@ -153045,6 +153955,8 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr assert( pParse->pNewTrigger==0 ); assert( pParse->nVar==0 ); assert( pParse->pVList==0 ); + pParse->pParentParse = db->pParse; + db->pParse = pParse; while( 1 ){ n = sqlite3GetToken((u8*)zSql, &tokenType); mxSqlLen -= n; @@ -153101,7 +154013,8 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr sqlite3Parser(pEngine, tokenType, pParse->sLastToken); lastTokenParsed = tokenType; zSql += n; - if( pParse->rc!=SQLITE_OK || db->mallocFailed ) break; + assert( db->mallocFailed==0 || pParse->rc!=SQLITE_OK || startedWithOom ); + if( pParse->rc!=SQLITE_OK ) break; } assert( nErr==0 ); #ifdef YYTRACKMAXSTACKDEPTH @@ -153169,6 +154082,8 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr pParse->pZombieTab = p->pNextZombie; sqlite3DeleteTable(db, p); } + db->pParse = pParse->pParentParse; + pParse->pParentParse = 0; assert( nErr==0 || pParse->rc!=SQLITE_OK ); return nErr; } @@ -154405,7 +155320,7 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){ pStart = 0; }else if( pBuf==0 ){ sqlite3BeginBenignMalloc(); - pStart = sqlite3Malloc( sz*cnt ); /* IMP: R-61949-35727 */ + pStart = sqlite3Malloc( sz*(sqlite3_int64)cnt ); /* IMP: R-61949-35727 */ sqlite3EndBenignMalloc(); if( pStart ) cnt = sqlite3MallocSize(pStart)/sz; }else{ @@ -154543,6 +155458,8 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){ { SQLITE_DBCONFIG_TRIGGER_EQP, SQLITE_TriggerEQP }, { SQLITE_DBCONFIG_RESET_DATABASE, SQLITE_ResetDatabase }, { SQLITE_DBCONFIG_DEFENSIVE, SQLITE_Defensive }, + { SQLITE_DBCONFIG_WRITABLE_SCHEMA, SQLITE_WriteSchema| + SQLITE_NoSchemaError }, }; unsigned int i; rc = SQLITE_ERROR; /* IMP: R-42790-23372 */ @@ -161211,7 +162128,7 @@ static int fts3ScanInteriorNode( zCsr += fts3GetVarint32(zCsr, &nSuffix); assert( nPrefix>=0 && nSuffix>=0 ); - if( nPrefix>zCsr-zNode || nSuffix>zEnd-zCsr ){ + if( nPrefix>zCsr-zNode || nSuffix>zEnd-zCsr || nSuffix==0 ){ rc = FTS_CORRUPT_VTAB; goto finish_scan; } @@ -168324,7 +169241,7 @@ static void fts3TokenizerFunc( nName = sqlite3_value_bytes(argv[0])+1; if( argc==2 ){ - if( fts3TokenizerEnabled(context) ){ + if( fts3TokenizerEnabled(context) || sqlite3_value_frombind(argv[1]) ){ void *pOld; int n = sqlite3_value_bytes(argv[1]); if( zName==0 || n!=sizeof(pPtr) ){ @@ -168351,7 +169268,9 @@ static void fts3TokenizerFunc( return; } } - sqlite3_result_blob(context, (void *)&pPtr, sizeof(pPtr), SQLITE_TRANSIENT); + if( fts3TokenizerEnabled(context) || sqlite3_value_frombind(argv[0]) ){ + sqlite3_result_blob(context, (void *)&pPtr, sizeof(pPtr), SQLITE_TRANSIENT); + } } SQLITE_PRIVATE int sqlite3Fts3IsIdChar(char c){ @@ -168439,8 +169358,8 @@ SQLITE_PRIVATE int sqlite3Fts3InitTokenizer( int iArg = 0; z = &z[n+1]; while( zzInput = sqlite3_malloc(nByte+1); + pCsr->zInput = sqlite3_malloc64(nByte+1); if( pCsr->zInput==0 ){ rc = SQLITE_NOMEM; }else{ @@ -170807,7 +171726,9 @@ static int fts3SegReaderNext( /* If iCurrentBlock>=iLeafEndBlock, this is an EOF condition. All leaf ** blocks have already been traversed. */ - assert( pReader->iCurrentBlock<=pReader->iLeafEndBlock ); +#ifdef CORRUPT_DB + assert( pReader->iCurrentBlock<=pReader->iLeafEndBlock || CORRUPT_DB ); +#endif if( pReader->iCurrentBlock>=pReader->iLeafEndBlock ){ return SQLITE_OK; } @@ -171209,8 +172130,9 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderPending( } if( nElem>0 ){ - int nByte = sizeof(Fts3SegReader) + (nElem+1)*sizeof(Fts3HashElem *); - pReader = (Fts3SegReader *)sqlite3_malloc(nByte); + sqlite3_int64 nByte; + nByte = sizeof(Fts3SegReader) + (nElem+1)*sizeof(Fts3HashElem *); + pReader = (Fts3SegReader *)sqlite3_malloc64(nByte); if( !pReader ){ rc = SQLITE_NOMEM; }else{ @@ -172694,8 +173616,10 @@ static int fts3SegmentMerge( if( rc!=SQLITE_OK ) goto finished; assert( csr.nSegment>0 ); - assert( iNewLevel>=getAbsoluteLevel(p, iLangid, iIndex, 0) ); - assert( iNewLevel=getAbsoluteLevel(p, iLangid, iIndex, 0) ); + assert_fts3_nc( + iNewLevelnColumn ); + pBlob = sqlite3_malloc64( 10*(sqlite3_int64)p->nColumn ); if( pBlob==0 ){ *pRC = SQLITE_NOMEM; return; @@ -172872,7 +173796,7 @@ static void fts3UpdateDocTotals( const int nStat = p->nColumn+2; if( *pRC ) return; - a = sqlite3_malloc( (sizeof(u32)+10)*nStat ); + a = sqlite3_malloc64( (sizeof(u32)+10)*(sqlite3_int64)nStat ); if( a==0 ){ *pRC = SQLITE_NOMEM; return; @@ -172993,8 +173917,8 @@ static int fts3DoRebuild(Fts3Table *p){ } if( rc==SQLITE_OK ){ - int nByte = sizeof(u32) * (p->nColumn+1)*3; - aSz = (u32 *)sqlite3_malloc(nByte); + sqlite3_int64 nByte = sizeof(u32) * ((sqlite3_int64)p->nColumn+1)*3; + aSz = (u32 *)sqlite3_malloc64(nByte); if( aSz==0 ){ rc = SQLITE_NOMEM; }else{ @@ -173060,12 +173984,12 @@ static int fts3IncrmergeCsr( ){ int rc; /* Return Code */ sqlite3_stmt *pStmt = 0; /* Statement used to read %_segdir entry */ - int nByte; /* Bytes allocated at pCsr->apSegment[] */ + sqlite3_int64 nByte; /* Bytes allocated at pCsr->apSegment[] */ /* Allocate space for the Fts3MultiSegReader.aCsr[] array */ memset(pCsr, 0, sizeof(*pCsr)); nByte = sizeof(Fts3SegReader *) * nSeg; - pCsr->apSegment = (Fts3SegReader **)sqlite3_malloc(nByte); + pCsr->apSegment = (Fts3SegReader **)sqlite3_malloc64(nByte); if( pCsr->apSegment==0 ){ rc = SQLITE_NOMEM; @@ -175045,7 +175969,7 @@ SQLITE_PRIVATE int sqlite3Fts3UpdateMethod( } /* Allocate space to hold the change in document sizes */ - aSzDel = sqlite3_malloc( sizeof(aSzDel[0])*(p->nColumn+1)*2 ); + aSzDel = sqlite3_malloc64(sizeof(aSzDel[0])*((sqlite3_int64)p->nColumn+1)*2); if( aSzDel==0 ){ rc = SQLITE_NOMEM; goto update_out; @@ -175299,17 +176223,19 @@ struct StrBuffer { /* ** Allocate a two-slot MatchinfoBuffer object. */ -static MatchinfoBuffer *fts3MIBufferNew(int nElem, const char *zMatchinfo){ +static MatchinfoBuffer *fts3MIBufferNew(size_t nElem, const char *zMatchinfo){ MatchinfoBuffer *pRet; - int nByte = sizeof(u32) * (2*nElem + 1) + sizeof(MatchinfoBuffer); - int nStr = (int)strlen(zMatchinfo); + sqlite3_int64 nByte = sizeof(u32) * (2*(sqlite3_int64)nElem + 1) + + sizeof(MatchinfoBuffer); + sqlite3_int64 nStr = strlen(zMatchinfo); - pRet = sqlite3_malloc(nByte + nStr+1); + pRet = sqlite3_malloc64(nByte + nStr+1); if( pRet ){ memset(pRet, 0, nByte); pRet->aMatchinfo[0] = (u8*)(&pRet->aMatchinfo[1]) - (u8*)pRet; - pRet->aMatchinfo[1+nElem] = pRet->aMatchinfo[0] + sizeof(u32)*(nElem+1); - pRet->nElem = nElem; + pRet->aMatchinfo[1+nElem] = pRet->aMatchinfo[0] + + sizeof(u32)*((int)nElem+1); + pRet->nElem = (int)nElem; pRet->zMatchinfo = ((char*)pRet) + nByte; memcpy(pRet->zMatchinfo, zMatchinfo, nStr+1); pRet->aRef[0] = 1; @@ -175600,7 +176526,7 @@ static void fts3SnippetDetails( char *pCsr = pPhrase->pTail; int iCsr = pPhrase->iTail; - while( iCsr<(iStart+pIter->nSnippet) ){ + while( iCsr<(iStart+pIter->nSnippet) && iCsr>=iStart ){ int j; u64 mPhrase = (u64)1 << i; u64 mPos = (u64)1 << (iCsr - iStart); @@ -176170,8 +177096,8 @@ static int fts3MatchinfoCheck( return SQLITE_ERROR; } -static int fts3MatchinfoSize(MatchInfo *pInfo, char cArg){ - int nVal; /* Number of integers output by cArg */ +static size_t fts3MatchinfoSize(MatchInfo *pInfo, char cArg){ + size_t nVal; /* Number of integers output by cArg */ switch( cArg ){ case FTS3_MATCHINFO_NDOC: @@ -176455,7 +177381,7 @@ static int fts3MatchinfoValues( case FTS3_MATCHINFO_LHITS_BM: case FTS3_MATCHINFO_LHITS: { - int nZero = fts3MatchinfoSize(pInfo, zArg[i]) * sizeof(u32); + size_t nZero = fts3MatchinfoSize(pInfo, zArg[i]) * sizeof(u32); memset(pInfo->aMatchinfo, 0, nZero); rc = fts3ExprLHitGather(pCsr->pExpr, pInfo); break; @@ -176524,7 +177450,7 @@ static void fts3GetMatchinfo( ** initialize those elements that are constant for every row. */ if( pCsr->pMIBuffer==0 ){ - int nMatchinfo = 0; /* Number of u32 elements in match-info */ + size_t nMatchinfo = 0; /* Number of u32 elements in match-info */ int i; /* Used to iterate through zArg */ /* Determine the number of phrases in the query */ @@ -176714,7 +177640,7 @@ static int fts3ExprTermOffsetInit(Fts3Expr *pExpr, int iPhrase, void *ctx){ nTerm = pExpr->pPhrase->nToken; if( pList ){ fts3GetDeltaPosition(&pList, &iPos); - assert( iPos>=0 ); + assert_fts3_nc( iPos>=0 ); } for(iTerm=0; iTermpList) ){ pTerm->pList = 0; }else{ @@ -183982,49 +184908,45 @@ rtreeInit_fail: ** *2 coordinates. */ static void rtreenode(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){ - char *zText = 0; RtreeNode node; Rtree tree; int ii; + int nData; + int errCode; + sqlite3_str *pOut; UNUSED_PARAMETER(nArg); memset(&node, 0, sizeof(RtreeNode)); memset(&tree, 0, sizeof(Rtree)); tree.nDim = (u8)sqlite3_value_int(apArg[0]); + if( tree.nDim<1 || tree.nDim>5 ) return; tree.nDim2 = tree.nDim*2; tree.nBytesPerCell = 8 + 8 * tree.nDim; node.zData = (u8 *)sqlite3_value_blob(apArg[1]); + nData = sqlite3_value_bytes(apArg[1]); + if( nData<4 ) return; + if( nData0 ) sqlite3_str_append(pOut, " ", 1); + sqlite3_str_appendf(pOut, "{%lld", cell.iRowid); for(jj=0; jjnVertex = s.nVertex; @@ -185175,7 +186097,7 @@ static GeoPoly *geopolyBBox( if( pRc ) *pRc = SQLITE_OK; if( aCoord==0 ){ geopolyBboxFill: - pOut = sqlite3_realloc(p, GEOPOLY_SZ(4)); + pOut = sqlite3_realloc64(p, GEOPOLY_SZ(4)); if( pOut==0 ){ sqlite3_free(p); if( context ) sqlite3_result_error_nomem(context); @@ -185571,9 +186493,9 @@ static GeoSegment *geopolySortSegmentsByYAndC(GeoSegment *pList){ ** Determine the overlap between two polygons */ static int geopolyOverlap(GeoPoly *p1, GeoPoly *p2){ - int nVertex = p1->nVertex + p2->nVertex + 2; + sqlite3_int64 nVertex = p1->nVertex + p2->nVertex + 2; GeoOverlap *p; - int nByte; + sqlite3_int64 nByte; GeoEvent *pThisEvent; double rX; int rc = 0; @@ -185585,7 +186507,7 @@ static int geopolyOverlap(GeoPoly *p1, GeoPoly *p2){ nByte = sizeof(GeoEvent)*nVertex*2 + sizeof(GeoSegment)*nVertex + sizeof(GeoOverlap); - p = sqlite3_malloc( nByte ); + p = sqlite3_malloc64( nByte ); if( p==0 ) return -1; p->aEvent = (GeoEvent*)&p[1]; p->aSegment = (GeoSegment*)&p->aEvent[nVertex*2]; @@ -185744,8 +186666,8 @@ static int geopolyInit( ){ int rc = SQLITE_OK; Rtree *pRtree; - int nDb; /* Length of string argv[1] */ - int nName; /* Length of string argv[2] */ + sqlite3_int64 nDb; /* Length of string argv[1] */ + sqlite3_int64 nName; /* Length of string argv[2] */ sqlite3_str *pSql; char *zSql; int ii; @@ -185753,9 +186675,9 @@ static int geopolyInit( sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1); /* Allocate the sqlite3_vtab structure */ - nDb = (int)strlen(argv[1]); - nName = (int)strlen(argv[2]); - pRtree = (Rtree *)sqlite3_malloc(sizeof(Rtree)+nDb+nName+2); + nDb = strlen(argv[1]); + nName = strlen(argv[2]); + pRtree = (Rtree *)sqlite3_malloc64(sizeof(Rtree)+nDb+nName+2); if( !pRtree ){ return SQLITE_NOMEM; } @@ -188180,6 +189102,11 @@ struct RbuUpdateStmt { ** it points to an array of flags nTblCol elements in size. The flag is ** set for each column that is either a part of the PK or a part of an ** index. Or clear otherwise. +** +** If there are one or more partial indexes on the table, all fields of +** this array set set to 1. This is because in that case, the module has +** no way to tell which fields will be required to add and remove entries +** from the partial indexes. ** */ struct RbuObjIter { @@ -188624,6 +189551,7 @@ static void rbuFossilDeltaFunc( }else{ nOut2 = rbuDeltaApply(aOrig, nOrig, aDelta, nDelta, aOut); if( nOut2!=nOut ){ + sqlite3_free(aOut); sqlite3_result_error(context, "corrupt fossil delta", -1); }else{ sqlite3_result_blob(context, aOut, nOut, sqlite3_free); @@ -188974,7 +189902,7 @@ static int rbuMPrintfExec(sqlite3rbu *p, sqlite3 *db, const char *zFmt, ...){ ** immediately without attempting the allocation or modifying the stored ** error code. */ -static void *rbuMalloc(sqlite3rbu *p, int nByte){ +static void *rbuMalloc(sqlite3rbu *p, sqlite3_int64 nByte){ void *pRet = 0; if( p->rc==SQLITE_OK ){ assert( nByte>0 ); @@ -188995,7 +189923,7 @@ static void *rbuMalloc(sqlite3rbu *p, int nByte){ ** error code in the RBU handle passed as the first argument. */ static void rbuAllocateIterArrays(sqlite3rbu *p, RbuObjIter *pIter, int nCol){ - int nByte = (2*sizeof(char*) + sizeof(int) + 3*sizeof(u8)) * nCol; + sqlite3_int64 nByte = (2*sizeof(char*) + sizeof(int) + 3*sizeof(u8)) * nCol; char **azNew; azNew = (char**)rbuMalloc(p, nByte); @@ -189189,8 +190117,12 @@ static void rbuObjIterCacheIndexedCols(sqlite3rbu *p, RbuObjIter *pIter){ pIter->nIndex = 0; while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pList) ){ const char *zIdx = (const char*)sqlite3_column_text(pList, 1); + int bPartial = sqlite3_column_int(pList, 4); sqlite3_stmt *pXInfo = 0; if( zIdx==0 ) break; + if( bPartial ){ + memset(pIter->abIndexed, 0x01, sizeof(u8)*pIter->nTblCol); + } p->rc = prepareFreeAndCollectError(p->dbMain, &pXInfo, &p->zErrmsg, sqlite3_mprintf("PRAGMA main.index_xinfo = %Q", zIdx) ); @@ -189635,7 +190567,7 @@ static char *rbuObjIterGetSetlist( */ static char *rbuObjIterGetBindlist(sqlite3rbu *p, int nBind){ char *zRet = 0; - int nByte = nBind*2 + 1; + sqlite3_int64 nByte = 2*(sqlite3_int64)nBind + 1; zRet = (char*)rbuMalloc(p, nByte); if( zRet ){ @@ -189897,6 +190829,62 @@ static void rbuTmpInsertFunc( } } +static char *rbuObjIterGetIndexWhere(sqlite3rbu *p, RbuObjIter *pIter){ + sqlite3_stmt *pStmt = 0; + int rc = p->rc; + char *zRet = 0; + + if( rc==SQLITE_OK ){ + rc = prepareAndCollectError(p->dbMain, &pStmt, &p->zErrmsg, + "SELECT trim(sql) FROM sqlite_master WHERE type='index' AND name=?" + ); + } + if( rc==SQLITE_OK ){ + int rc2; + rc = sqlite3_bind_text(pStmt, 1, pIter->zIdx, -1, SQLITE_STATIC); + if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ + const char *zSql = (const char*)sqlite3_column_text(pStmt, 0); + if( zSql ){ + int nParen = 0; /* Number of open parenthesis */ + int i; + for(i=0; zSql[i]; i++){ + char c = zSql[i]; + if( c=='(' ){ + nParen++; + } + else if( c==')' ){ + nParen--; + if( nParen==0 ){ + i++; + break; + } + }else if( c=='"' || c=='\'' || c=='`' ){ + for(i++; 1; i++){ + if( zSql[i]==c ){ + if( zSql[i+1]!=c ) break; + i++; + } + } + }else if( c=='[' ){ + for(i++; 1; i++){ + if( zSql[i]==']' ) break; + } + } + } + if( zSql[i] ){ + zRet = rbuStrndup(&zSql[i], &rc); + } + } + } + + rc2 = sqlite3_finalize(pStmt); + if( rc==SQLITE_OK ) rc = rc2; + } + + p->rc = rc; + return zRet; +} + /* ** Ensure that the SQLite statement handles required to update the ** target database object currently indicated by the iterator passed @@ -189926,6 +190914,7 @@ static int rbuObjIterPrepareAll( char *zImposterPK = 0; /* Primary key declaration for imposter */ char *zWhere = 0; /* WHERE clause on PK columns */ char *zBind = 0; + char *zPart = 0; int nBind = 0; assert( pIter->eType!=RBU_PK_VTAB ); @@ -189933,6 +190922,7 @@ static int rbuObjIterPrepareAll( p, pIter, &zImposterCols, &zImposterPK, &zWhere, &nBind ); zBind = rbuObjIterGetBindlist(p, nBind); + zPart = rbuObjIterGetIndexWhere(p, pIter); /* Create the imposter table used to write to this index. */ sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 0, 1); @@ -189965,28 +190955,30 @@ static int rbuObjIterPrepareAll( char *zSql; if( rbuIsVacuum(p) ){ zSql = sqlite3_mprintf( - "SELECT %s, 0 AS rbu_control FROM '%q' ORDER BY %s%s", + "SELECT %s, 0 AS rbu_control FROM '%q' %s ORDER BY %s%s", zCollist, pIter->zDataTbl, - zCollist, zLimit + zPart, zCollist, zLimit ); }else if( pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE ){ zSql = sqlite3_mprintf( - "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' ORDER BY %s%s", + "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' %s ORDER BY %s%s", zCollist, p->zStateDb, pIter->zDataTbl, - zCollist, zLimit + zPart, zCollist, zLimit ); }else{ zSql = sqlite3_mprintf( - "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' " + "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' %s " "UNION ALL " "SELECT %s, rbu_control FROM '%q' " - "WHERE typeof(rbu_control)='integer' AND rbu_control!=1 " + "%s %s typeof(rbu_control)='integer' AND rbu_control!=1 " "ORDER BY %s%s", - zCollist, p->zStateDb, pIter->zDataTbl, + zCollist, p->zStateDb, pIter->zDataTbl, zPart, zCollist, pIter->zDataTbl, + zPart, + (zPart ? "AND" : "WHERE"), zCollist, zLimit ); } @@ -189997,6 +190989,7 @@ static int rbuObjIterPrepareAll( sqlite3_free(zImposterPK); sqlite3_free(zWhere); sqlite3_free(zBind); + sqlite3_free(zPart); }else{ int bRbuRowid = (pIter->eType==RBU_PK_VTAB) ||(pIter->eType==RBU_PK_NONE) @@ -192430,7 +193423,7 @@ static int rbuVfsShmMap( assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB) ); if( eStage==RBU_STAGE_OAL || eStage==RBU_STAGE_MOVE ){ if( iRegion<=p->nShm ){ - int nByte = (iRegion+1) * sizeof(char*); + sqlite3_int64 nByte = (iRegion+1) * sizeof(char*); char **apNew = (char**)sqlite3_realloc64(p->apShm, nByte); if( apNew==0 ){ rc = SQLITE_NOMEM; @@ -194941,7 +195934,7 @@ static int sessionGrowHash(int bPatchset, SessionTable *pTab){ if( pTab->nChange==0 || pTab->nEntry>=(pTab->nChange/2) ){ int i; SessionChange **apNew; - int nNew = (pTab->nChange ? pTab->nChange : 128) * 2; + sqlite3_int64 nNew = 2*(sqlite3_int64)(pTab->nChange ? pTab->nChange : 128); apNew = (SessionChange **)sqlite3_malloc64(sizeof(SessionChange *) * nNew); if( apNew==0 ){ @@ -195868,7 +196861,7 @@ SQLITE_API int sqlite3session_attach( ** If successful, return zero. Otherwise, if an OOM condition is encountered, ** set *pRc to SQLITE_NOMEM and return non-zero. */ -static int sessionBufferGrow(SessionBuffer *p, int nByte, int *pRc){ +static int sessionBufferGrow(SessionBuffer *p, size_t nByte, int *pRc){ if( *pRc==SQLITE_OK && p->nAlloc-p->nBufnAlloc ? p->nAlloc : 128; @@ -196986,7 +197979,7 @@ static int sessionChangesetReadTblhdr(sqlite3_changeset_iter *p){ } if( rc==SQLITE_OK ){ - int iPK = sizeof(sqlite3_value*)*p->nCol*2; + size_t iPK = sizeof(sqlite3_value*)*p->nCol*2; memset(p->tblhdr.aBuf, 0, iPK); memcpy(&p->tblhdr.aBuf[iPK], &p->in.aData[p->in.iNext], nCopy); p->in.iNext += nCopy; @@ -197901,7 +198894,7 @@ static int sessionSeekToRow( } /* -** This function is called from within sqlite3changset_apply_v2() when +** This function is called from within sqlite3changeset_apply_v2() when ** a conflict is encountered and resolved using conflict resolution ** mode eType (either SQLITE_CHANGESET_OMIT or SQLITE_CHANGESET_REPLACE).. ** It adds a conflict resolution record to the buffer in @@ -198290,7 +199283,7 @@ static int sessionRetryConstraints( rc = sessionChangesetStart(&pIter2, 0, 0, cons.nBuf, cons.aBuf, 0); if( rc==SQLITE_OK ){ - int nByte = 2*pApply->nCol*sizeof(sqlite3_value*); + size_t nByte = 2*pApply->nCol*sizeof(sqlite3_value*); int rc2; pIter2->bPatchset = bPatchset; pIter2->zTab = (char*)zTab; @@ -199683,7 +200676,7 @@ struct Fts5PhraseIter { ** Save the pointer passed as the second argument as the extension functions ** "auxiliary data". The pointer may then be retrieved by the current or any ** future invocation of the same fts5 extension function made as part of -** of the same MATCH query using the xGetAuxdata() API. +** the same MATCH query using the xGetAuxdata() API. ** ** Each extension function is allocated a single auxiliary data slot for ** each FTS query (MATCH expression). If the extension function is invoked @@ -199698,7 +200691,7 @@ struct Fts5PhraseIter { ** The xDelete callback, if one is specified, is also invoked on the ** auxiliary data pointer after the FTS5 query has finished. ** -** If an error (e.g. an OOM condition) occurs within this function, an +** If an error (e.g. an OOM condition) occurs within this function, ** the auxiliary data is set to NULL and an error code returned. If the ** xDelete parameter was not NULL, it is invoked on the auxiliary data ** pointer before returning. @@ -200680,8 +201673,9 @@ static void sqlite3Fts5HashClear(Fts5Hash*); static int sqlite3Fts5HashQuery( Fts5Hash*, /* Hash table to query */ + int nPre, const char *pTerm, int nTerm, /* Query term */ - const u8 **ppDoclist, /* OUT: Pointer to doclist for pTerm */ + void **ppObj, /* OUT: Pointer to doclist for pTerm */ int *pnDoclist /* OUT: Size of doclist in bytes */ ); @@ -202751,7 +203745,7 @@ static int fts5SnippetScore( sqlite3_int64 iAdj = iFirst - (nToken - (iLast-iFirst)) / 2; if( (iAdj+nToken)>nDocsize ) iAdj = nDocsize - nToken; if( iAdj<0 ) iAdj = 0; - *piPos = iAdj; + *piPos = (int)iAdj; } return rc; @@ -202979,7 +203973,7 @@ static int fts5Bm25GetData( if( p==0 ){ rc = SQLITE_NOMEM; }else{ - memset(p, 0, nByte); + memset(p, 0, (size_t)nByte); p->nPhrase = nPhrase; p->aIDF = (double*)&p[1]; p->aFreq = &p->aIDF[nPhrase]; @@ -203142,7 +204136,7 @@ static int sqlite3Fts5BufferSize(int *pRc, Fts5Buffer *pBuf, u32 nByte){ *pRc = SQLITE_NOMEM; return 1; }else{ - pBuf->nSpace = nNew; + pBuf->nSpace = (int)nNew; pBuf->p = pNew; } } @@ -203366,7 +204360,7 @@ static void *sqlite3Fts5MallocZero(int *pRc, sqlite3_int64 nByte){ if( pRet==0 ){ if( nByte>0 ) *pRc = SQLITE_NOMEM; }else{ - memset(pRet, 0, nByte); + memset(pRet, 0, (size_t)nByte); } } return pRet; @@ -203835,7 +204829,7 @@ static int fts5ConfigParseSpecial( rc = SQLITE_ERROR; }else{ rc = sqlite3Fts5GetTokenizer(pGlobal, - (const char**)azArg, nArg, &pConfig->pTok, &pConfig->pTokApi, + (const char**)azArg, (int)nArg, &pConfig->pTok, &pConfig->pTokApi, pzErr ); } @@ -203945,7 +204939,7 @@ static const char *fts5ConfigGobbleWord( if( zOut==0 ){ *pRc = SQLITE_NOMEM; }else{ - memcpy(zOut, zIn, nIn+1); + memcpy(zOut, zIn, (size_t)(nIn+1)); if( fts5_isopenquote(zOut[0]) ){ int ii = fts5Dequote(zOut); zRet = &zIn[ii]; @@ -205959,7 +206953,7 @@ static Fts5ExprNearset *sqlite3Fts5ParseNearset( if( pRet==0 ){ pParse->rc = SQLITE_NOMEM; }else{ - memset(pRet, 0, nByte); + memset(pRet, 0, (size_t)nByte); } }else if( (pNear->nPhrase % SZALLOC)==0 ){ int nNew = pNear->nPhrase + SZALLOC; @@ -206035,7 +207029,7 @@ static int fts5ParseTokenize( if( pSyn==0 ){ rc = SQLITE_NOMEM; }else{ - memset(pSyn, 0, nByte); + memset(pSyn, 0, (size_t)nByte); pSyn->zTerm = ((char*)pSyn) + sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer); memcpy(pSyn->zTerm, pToken, nToken); pSyn->pSynonym = pPhrase->aTerm[pPhrase->nTerm-1].pSynonym; @@ -206195,7 +207189,7 @@ static int sqlite3Fts5ExprClonePhrase( nByte = sizeof(Fts5Colset) + (pColsetOrig->nCol-1) * sizeof(int); pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte); if( pColset ){ - memcpy(pColset, pColsetOrig, nByte); + memcpy(pColset, pColsetOrig, (size_t)nByte); } pNew->pRoot->pNear->pColset = pColset; } @@ -206412,7 +207406,7 @@ static Fts5Colset *fts5CloneColset(int *pRc, Fts5Colset *pOrig){ sqlite3_int64 nByte = sizeof(Fts5Colset) + (pOrig->nCol-1) * sizeof(int); pRet = (Fts5Colset*)sqlite3Fts5MallocZero(pRc, nByte); if( pRet ){ - memcpy(pRet, pOrig, nByte); + memcpy(pRet, pOrig, (size_t)nByte); } }else{ pRet = 0; @@ -207429,7 +208423,7 @@ static int sqlite3Fts5HashNew(Fts5Config *pConfig, Fts5Hash **ppNew, int *pnByte *ppNew = 0; rc = SQLITE_NOMEM; }else{ - memset(pNew->aSlot, 0, nByte); + memset(pNew->aSlot, 0, (size_t)nByte); } } return rc; @@ -207513,19 +208507,25 @@ static int fts5HashResize(Fts5Hash *pHash){ return SQLITE_OK; } -static void fts5HashAddPoslistSize(Fts5Hash *pHash, Fts5HashEntry *p){ +static int fts5HashAddPoslistSize( + Fts5Hash *pHash, + Fts5HashEntry *p, + Fts5HashEntry *p2 +){ + int nRet = 0; if( p->iSzPoslist ){ - u8 *pPtr = (u8*)p; + u8 *pPtr = p2 ? (u8*)p2 : (u8*)p; + int nData = p->nData; if( pHash->eDetail==FTS5_DETAIL_NONE ){ - assert( p->nData==p->iSzPoslist ); + assert( nData==p->iSzPoslist ); if( p->bDel ){ - pPtr[p->nData++] = 0x00; + pPtr[nData++] = 0x00; if( p->bContent ){ - pPtr[p->nData++] = 0x00; + pPtr[nData++] = 0x00; } } }else{ - int nSz = (p->nData - p->iSzPoslist - 1); /* Size in bytes */ + int nSz = (nData - p->iSzPoslist - 1); /* Size in bytes */ int nPos = nSz*2 + p->bDel; /* Value of nPos field */ assert( p->bDel==0 || p->bDel==1 ); @@ -207535,14 +208535,19 @@ static void fts5HashAddPoslistSize(Fts5Hash *pHash, Fts5HashEntry *p){ int nByte = sqlite3Fts5GetVarintLen((u32)nPos); memmove(&pPtr[p->iSzPoslist + nByte], &pPtr[p->iSzPoslist + 1], nSz); sqlite3Fts5PutVarint(&pPtr[p->iSzPoslist], nPos); - p->nData += (nByte-1); + nData += (nByte-1); } } - p->iSzPoslist = 0; - p->bDel = 0; - p->bContent = 0; + nRet = nData - p->nData; + if( p2==0 ){ + p->iSzPoslist = 0; + p->bDel = 0; + p->bContent = 0; + p->nData = nData; + } } + return nRet; } /* @@ -207599,7 +208604,7 @@ static int sqlite3Fts5HashWrite( p = (Fts5HashEntry*)sqlite3_malloc64(nByte); if( !p ) return SQLITE_NOMEM; memset(p, 0, sizeof(Fts5HashEntry)); - p->nAlloc = nByte; + p->nAlloc = (int)nByte; zKey = fts5EntryKey(p); zKey[0] = bByte; memcpy(&zKey[1], pToken, nToken); @@ -207654,7 +208659,7 @@ static int sqlite3Fts5HashWrite( /* If this is a new rowid, append the 4-byte size field for the previous ** entry, and the new rowid for this entry. */ if( iRowid!=p->iRowid ){ - fts5HashAddPoslistSize(pHash, p); + fts5HashAddPoslistSize(pHash, p, 0); p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iRowid - p->iRowid); p->iRowid = iRowid; bNew = 1; @@ -207771,7 +208776,9 @@ static int fts5HashEntrySort( for(iSlot=0; iSlotnSlot; iSlot++){ Fts5HashEntry *pIter; for(pIter=pHash->aSlot[iSlot]; pIter; pIter=pIter->pHashNext){ - if( pTerm==0 || 0==memcmp(fts5EntryKey(pIter), pTerm, nTerm) ){ + if( pTerm==0 + || (pIter->nKey+1>=nTerm && 0==memcmp(fts5EntryKey(pIter), pTerm, nTerm)) + ){ Fts5HashEntry *pEntry = pIter; pEntry->pScanNext = 0; for(i=0; ap[i]; i++){ @@ -207799,8 +208806,9 @@ static int fts5HashEntrySort( */ static int sqlite3Fts5HashQuery( Fts5Hash *pHash, /* Hash table to query */ + int nPre, const char *pTerm, int nTerm, /* Query term */ - const u8 **ppDoclist, /* OUT: Pointer to doclist for pTerm */ + void **ppOut, /* OUT: Pointer to new object */ int *pnDoclist /* OUT: Size of doclist in bytes */ ){ unsigned int iHash = fts5HashKey(pHash->nSlot, (const u8*)pTerm, nTerm); @@ -207814,11 +208822,20 @@ static int sqlite3Fts5HashQuery( } if( p ){ - fts5HashAddPoslistSize(pHash, p); - *ppDoclist = (const u8*)&zKey[nTerm+1]; - *pnDoclist = p->nData - (sizeof(Fts5HashEntry) + nTerm + 1); + int nHashPre = sizeof(Fts5HashEntry) + nTerm + 1; + int nList = p->nData - nHashPre; + u8 *pRet = (u8*)(*ppOut = sqlite3_malloc64(nPre + nList + 10)); + if( pRet ){ + Fts5HashEntry *pFaux = (Fts5HashEntry*)&pRet[nPre-nHashPre]; + memcpy(&pRet[nPre], &((u8*)p)[nHashPre], nList); + nList += fts5HashAddPoslistSize(pHash, p, pFaux); + *pnDoclist = nList; + }else{ + *pnDoclist = 0; + return SQLITE_NOMEM; + } }else{ - *ppDoclist = 0; + *ppOut = 0; *pnDoclist = 0; } @@ -207851,7 +208868,7 @@ static void sqlite3Fts5HashScanEntry( if( (p = pHash->pScan) ){ char *zKey = fts5EntryKey(p); int nTerm = (int)strlen(zKey); - fts5HashAddPoslistSize(pHash, p); + fts5HashAddPoslistSize(pHash, p, 0); *pzTerm = zKey; *ppDoclist = (const u8*)&zKey[nTerm+1]; *pnDoclist = p->nData - (sizeof(Fts5HashEntry) + nTerm + 1); @@ -210321,31 +211338,40 @@ static void fts5SegIterHashInit( int flags, /* Mask of FTS5INDEX_XXX flags */ Fts5SegIter *pIter /* Object to populate */ ){ - const u8 *pList = 0; int nList = 0; const u8 *z = 0; int n = 0; + Fts5Data *pLeaf = 0; assert( p->pHash ); assert( p->rc==SQLITE_OK ); if( pTerm==0 || (flags & FTS5INDEX_QUERY_SCAN) ){ + const u8 *pList = 0; + p->rc = sqlite3Fts5HashScanInit(p->pHash, (const char*)pTerm, nTerm); sqlite3Fts5HashScanEntry(p->pHash, (const char**)&z, &pList, &nList); n = (z ? (int)strlen((const char*)z) : 0); + if( pList ){ + pLeaf = fts5IdxMalloc(p, sizeof(Fts5Data)); + if( pLeaf ){ + pLeaf->p = (u8*)pList; + } + } }else{ - pIter->flags |= FTS5_SEGITER_ONETERM; - sqlite3Fts5HashQuery(p->pHash, (const char*)pTerm, nTerm, &pList, &nList); + p->rc = sqlite3Fts5HashQuery(p->pHash, sizeof(Fts5Data), + (const char*)pTerm, nTerm, (void**)&pLeaf, &nList + ); + if( pLeaf ){ + pLeaf->p = (u8*)&pLeaf[1]; + } z = pTerm; n = nTerm; + pIter->flags |= FTS5_SEGITER_ONETERM; } - if( pList ){ - Fts5Data *pLeaf; + if( pLeaf ){ sqlite3Fts5BufferSet(&p->rc, &pIter->term, n, z); - pLeaf = fts5IdxMalloc(p, sizeof(Fts5Data)); - if( pLeaf==0 ) return; - pLeaf->p = (u8*)pList; pLeaf->nn = pLeaf->szLeaf = nList; pIter->pLeaf = pLeaf; pIter->iLeafOffset = fts5GetVarint(pLeaf->p, (u64*)&pIter->iRowid); @@ -210498,8 +211524,8 @@ static int fts5MultiIterDoCompare(Fts5Iter *pIter, int iOut){ }else{ int res = fts5BufferCompare(&p1->term, &p2->term); if( res==0 ){ - assert( i2>i1 ); - assert( i2!=0 ); + assert_nc( i2>i1 ); + assert_nc( i2!=0 ); pRes->bTermEq = 1; if( p1->iRowid==p2->iRowid ){ p1->bDel = p2->bDel; @@ -211546,7 +212572,7 @@ static int fts5WriteDlidxGrow( if( aDlidx==0 ){ p->rc = SQLITE_NOMEM; }else{ - int nByte = sizeof(Fts5DlidxWriter) * (nLvl - pWriter->nDlidx); + size_t nByte = sizeof(Fts5DlidxWriter) * (nLvl - pWriter->nDlidx); memset(&aDlidx[pWriter->nDlidx], 0, nByte); pWriter->aDlidx = aDlidx; pWriter->nDlidx = nLvl; @@ -212033,13 +213059,14 @@ static void fts5TrimSegments(Fts5Index *p, Fts5Iter *pIter){ /* Set up the new page-index array */ fts5BufferAppendVarint(&p->rc, &buf, 4); if( pSeg->iLeafPgno==pSeg->iTermLeafPgno - && pSeg->iEndofDoclistszLeaf - ){ + && pSeg->iEndofDoclistszLeaf + && pSeg->iPgidxOff<=pData->nn + ){ int nDiff = pData->szLeaf - pSeg->iEndofDoclist; fts5BufferAppendVarint(&p->rc, &buf, buf.n - 1 - nDiff - 4); fts5BufferAppendBlob(&p->rc, &buf, pData->nn - pSeg->iPgidxOff, &pData->p[pSeg->iPgidxOff] - ); + ); } pSeg->pSeg->pgnoFirst = pSeg->iTermLeafPgno; @@ -215061,7 +216088,7 @@ static int fts5OpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){ pCsr = (Fts5Cursor*)sqlite3_malloc64(nByte); if( pCsr ){ Fts5Global *pGlobal = pTab->pGlobal; - memset(pCsr, 0, nByte); + memset(pCsr, 0, (size_t)nByte); pCsr->aColumnSize = (int*)&pCsr[1]; pCsr->pNext = pGlobal->pCsr; pGlobal->pCsr = pCsr; @@ -215342,7 +216369,7 @@ static int fts5CursorFirstSorted( nByte = sizeof(Fts5Sorter) + sizeof(int) * (nPhrase-1); pSorter = (Fts5Sorter*)sqlite3_malloc64(nByte); if( pSorter==0 ) return SQLITE_NOMEM; - memset(pSorter, 0, nByte); + memset(pSorter, 0, (size_t)nByte); pSorter->nIdx = nPhrase; /* TODO: It would be better to have some system for reusing statement @@ -216896,14 +217923,14 @@ static int fts5CreateAux( int rc = sqlite3_overload_function(pGlobal->db, zName, -1); if( rc==SQLITE_OK ){ Fts5Auxiliary *pAux; - int nName; /* Size of zName in bytes, including \0 */ - int nByte; /* Bytes of space to allocate */ + sqlite3_int64 nName; /* Size of zName in bytes, including \0 */ + sqlite3_int64 nByte; /* Bytes of space to allocate */ - nName = (int)strlen(zName) + 1; + nName = strlen(zName) + 1; nByte = sizeof(Fts5Auxiliary) + nName; - pAux = (Fts5Auxiliary*)sqlite3_malloc(nByte); + pAux = (Fts5Auxiliary*)sqlite3_malloc64(nByte); if( pAux ){ - memset(pAux, 0, nByte); + memset(pAux, 0, (size_t)nByte); pAux->zFunc = (char*)&pAux[1]; memcpy(pAux->zFunc, zName, nName); pAux->pGlobal = pGlobal; @@ -216933,15 +217960,15 @@ static int fts5CreateTokenizer( ){ Fts5Global *pGlobal = (Fts5Global*)pApi; Fts5TokenizerModule *pNew; - int nName; /* Size of zName and its \0 terminator */ - int nByte; /* Bytes of space to allocate */ + sqlite3_int64 nName; /* Size of zName and its \0 terminator */ + sqlite3_int64 nByte; /* Bytes of space to allocate */ int rc = SQLITE_OK; - nName = (int)strlen(zName) + 1; + nName = strlen(zName) + 1; nByte = sizeof(Fts5TokenizerModule) + nName; - pNew = (Fts5TokenizerModule*)sqlite3_malloc(nByte); + pNew = (Fts5TokenizerModule*)sqlite3_malloc64(nByte); if( pNew ){ - memset(pNew, 0, nByte); + memset(pNew, 0, (size_t)nByte); pNew->zName = (char*)&pNew[1]; memcpy(pNew->zName, zName, nName); pNew->pUserData = pUserData; @@ -217076,7 +218103,7 @@ static void fts5SourceIdFunc( ){ assert( nArg==0 ); UNUSED_PARAM2(nArg, apUnused); - sqlite3_result_text(pCtx, "fts5: 2019-02-08 13:17:39 0eca3dd3d38b31c92b49ca2d311128b74584714d9e7de895b1a6286ef959a1dd", -1, SQLITE_TRANSIENT); + sqlite3_result_text(pCtx, "fts5: 2019-04-16 19:49:53 884b4b7e502b4e991677b53971277adfaf0a04a284f8e483e2553d0f83156b50", -1, SQLITE_TRANSIENT); } /* @@ -217499,7 +218526,7 @@ static int sqlite3Fts5StorageOpen( *pp = p = (Fts5Storage*)sqlite3_malloc64(nByte); if( !p ) return SQLITE_NOMEM; - memset(p, 0, nByte); + memset(p, 0, (size_t)nByte); p->aTotalSize = (i64*)&p[1]; p->pConfig = pConfig; p->pIndex = pIndex; @@ -218721,7 +219748,7 @@ static int fts5UnicodeCreate( p->eRemoveDiacritic = FTS5_REMOVE_DIACRITICS_SIMPLE; p->nFold = 64; - p->aFold = sqlite3_malloc(p->nFold * sizeof(char)); + p->aFold = sqlite3_malloc64(p->nFold * sizeof(char)); if( p->aFold==0 ){ rc = SQLITE_NOMEM; } @@ -220409,7 +221436,7 @@ static void sqlite3Fts5UnicodeAscii(u8 *aArray, u8 *aAscii){ int bToken = aArray[ aFts5UnicodeData[iTbl] & 0x1F ]; int n = (aFts5UnicodeData[iTbl] >> 5) + i; for(; i<128 && iSQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER -**
    ^This option is used to enable or disable the two-argument -** version of the [fts3_tokenizer()] function which is part of the +**
    ^This option is used to enable or disable the +** [fts3_tokenizer()] function which is part of the ** [FTS3] full-text search engine extension. ** There should be two additional arguments. ** The first argument is an integer which is 0 to disable fts3_tokenizer() or @@ -2199,6 +2202,17 @@ struct sqlite3_mem_methods { **
  • Direct writes to [shadow tables]. ** **
  • +** +** [[SQLITE_DBCONFIG_WRITABLE_SCHEMA]]
    SQLITE_DBCONFIG_WRITABLE_SCHEMA
    +**
    The SQLITE_DBCONFIG_WRITABLE_SCHEMA option activates or deactivates the +** "writable_schema" flag. This has the same effect and is logically equivalent +** to setting [PRAGMA writable_schema=ON] or [PRAGMA writable_schema=OFF]. +** The first argument to this setting is an integer which is 0 to disable +** the writable_schema, positive to enable writable_schema, or negative to +** leave the setting unchanged. The second parameter is a pointer to an +** integer into which is written 0 or 1 to indicate whether the writable_schema +** is enabled or disabled following this call. +**
    ** */ #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ @@ -2212,7 +2226,8 @@ struct sqlite3_mem_methods { #define SQLITE_DBCONFIG_TRIGGER_EQP 1008 /* int int* */ #define SQLITE_DBCONFIG_RESET_DATABASE 1009 /* int int* */ #define SQLITE_DBCONFIG_DEFENSIVE 1010 /* int int* */ -#define SQLITE_DBCONFIG_MAX 1010 /* Largest DBCONFIG */ +#define SQLITE_DBCONFIG_WRITABLE_SCHEMA 1011 /* int int* */ +#define SQLITE_DBCONFIG_MAX 1011 /* Largest DBCONFIG */ /* ** CAPI3REF: Enable Or Disable Extended Result Codes @@ -2369,7 +2384,7 @@ SQLITE_API int sqlite3_changes(sqlite3*); ** not. ^Changes to a view that are intercepted by INSTEAD OF triggers ** are not counted. ** -** This the [sqlite3_total_changes(D)] interface only reports the number +** The [sqlite3_total_changes(D)] interface only reports the number ** of rows that changed due to SQL statement run against database ** connection D. Any changes by other database connections are ignored. ** To detect changes against a database file from other database @@ -3894,6 +3909,18 @@ SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt); */ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); +/* +** CAPI3REF: Query The EXPLAIN Setting For A Prepared Statement +** METHOD: sqlite3_stmt +** +** ^The sqlite3_stmt_isexplain(S) interface returns 1 if the +** prepared statement S is an EXPLAIN statement, or 2 if the +** statement S is an EXPLAIN QUERY PLAN. +** ^The sqlite3_stmt_isexplain(S) interface returns 0 if S is +** an ordinary statement or a NULL pointer. +*/ +SQLITE_API int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt); + /* ** CAPI3REF: Determine If A Prepared Statement Has Been Reset ** METHOD: sqlite3_stmt @@ -4033,7 +4060,9 @@ typedef struct sqlite3_context sqlite3_context; ** ^The fifth argument to the BLOB and string binding interfaces ** is a destructor used to dispose of the BLOB or ** string after SQLite has finished with it. ^The destructor is called -** to dispose of the BLOB or string even if the call to bind API fails. +** to dispose of the BLOB or string even if the call to the bind API fails, +** except the destructor is not called if the third parameter is a NULL +** pointer or the fourth parameter is negative. ** ^If the fifth argument is ** the special value [SQLITE_STATIC], then SQLite assumes that the ** information is in static, unmanaged space and does not need to be freed. @@ -4950,6 +4979,8 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6 ** sqlite3_value_nochange   ** →  True if the column is unchanged in an UPDATE ** against a virtual table. +** sqlite3_value_frombind   +** →  True if value originated from a [bound parameter] ** ** ** Details: @@ -5011,6 +5042,11 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6 ** than within an [xUpdate] method call for an UPDATE statement, then ** the return value is arbitrary and meaningless. ** +** ^The sqlite3_value_frombind(X) interface returns non-zero if the +** value X originated from one of the [sqlite3_bind_int|sqlite3_bind()] +** interfaces. ^If X comes from an SQL literal value, or a table column, +** and expression, then sqlite3_value_frombind(X) returns zero. +** ** Please pay particular attention to the fact that the pointer returned ** from [sqlite3_value_blob()], [sqlite3_value_text()], or ** [sqlite3_value_text16()] can be invalidated by a subsequent call to @@ -5056,6 +5092,7 @@ SQLITE_API int sqlite3_value_bytes16(sqlite3_value*); SQLITE_API int sqlite3_value_type(sqlite3_value*); SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); SQLITE_API int sqlite3_value_nochange(sqlite3_value*); +SQLITE_API int sqlite3_value_frombind(sqlite3_value*); /* ** CAPI3REF: Finding The Subtype Of SQL Values @@ -5791,7 +5828,7 @@ SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); ** associated with database N of connection D. ^The main database file ** has the name "main". If there is no attached database N on the database ** connection D, or if database N is a temporary or in-memory database, then -** a NULL pointer is returned. +** this function will return either a NULL pointer or an empty string. ** ** ^The filename returned by this function is the output of the ** xFullPathname method of the [VFS]. ^In other words, the filename @@ -10892,7 +10929,7 @@ SQLITE_API int sqlite3rebaser_configure( ** in size. This function allocates and populates a buffer with a copy ** of the changeset rebased rebased according to the configuration of the ** rebaser object passed as the first argument. If successful, (*ppOut) -** is set to point to the new buffer containing the rebased changset and +** is set to point to the new buffer containing the rebased changeset and ** (*pnOut) to its size in bytes and SQLITE_OK returned. It is the ** responsibility of the caller to eventually free the new buffer using ** sqlite3_free(). Otherwise, if an error occurs, (*ppOut) and (*pnOut) @@ -11301,7 +11338,7 @@ struct Fts5PhraseIter { ** Save the pointer passed as the second argument as the extension functions ** "auxiliary data". The pointer may then be retrieved by the current or any ** future invocation of the same fts5 extension function made as part of -** of the same MATCH query using the xGetAuxdata() API. +** the same MATCH query using the xGetAuxdata() API. ** ** Each extension function is allocated a single auxiliary data slot for ** each FTS query (MATCH expression). If the extension function is invoked @@ -11316,7 +11353,7 @@ struct Fts5PhraseIter { ** The xDelete callback, if one is specified, is also invoked on the ** auxiliary data pointer after the FTS5 query has finished. ** -** If an error (e.g. an OOM condition) occurs within this function, an +** If an error (e.g. an OOM condition) occurs within this function, ** the auxiliary data is set to NULL and an error code returned. If the ** xDelete parameter was not NULL, it is invoked on the auxiliary data ** pointer before returning. -- cgit v1.2.3 From 2ea3e133c369deefeae0605ad2f32aaea52c9e97 Mon Sep 17 00:00:00 2001 From: Paul Wicking Date: Fri, 7 Jun 2019 14:47:31 +0200 Subject: Doc: Add Q_OS_WASM documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: QTBUG-76264 Change-Id: Ie167a64e0c84375012e8b1056eaae2243b09bf5d Reviewed-by: Topi Reiniö --- src/corelib/global/qglobal.cpp | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src') diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp index 7879109930..f7f04c9684 100644 --- a/src/corelib/global/qglobal.cpp +++ b/src/corelib/global/qglobal.cpp @@ -1545,6 +1545,13 @@ bool qSharedBuild() Q_DECL_NOTHROW Defined on Any UNIX BSD/SYSV system. */ +/*! + \macro Q_OS_WASM + \relates + + Defined on Web Assembly. +*/ + /*! \macro Q_CC_SYM \relates -- cgit v1.2.3 From 2e7b7e4c90882cef50b293fe8048205a9ca7e5a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Nordheim?= Date: Thu, 13 Dec 2018 15:39:26 +0100 Subject: QSslSocket: add and set the TLSv1.3-specific PSK callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If this callback is not set then OpenSSL will call the callback used for <= TLS 1.2 unconditionally when connecting. If using PSK it will call it again later once the preshared key is needed. We don't currently handle the TLSv1.3 PSK, but we definitely should. But for now we can work around it - when psk_use_session_callback is called we simply change the PSK callback to a dummy function whose only purpose is to restore the old callback. This is mostly done to keep behavior the same as it is now for users (and to keep our tests running). Later we can add a new signal and handle this new feature properly. Reviewed-by: Simo Fält (cherry picked from commit d8efc8d718e3b3a0464f321e740541f5b221a5d6) Task-number: QTBUG-67463 Change-Id: I4aca4ae73ec4be7c4f82a85e8864de103f35a834 Reviewed-by: Timur Pocheptsov --- src/network/ssl/qsslsocket_openssl.cpp | 56 ++++++++++++++++++++++++ src/network/ssl/qsslsocket_openssl11_symbols_p.h | 6 +++ src/network/ssl/qsslsocket_openssl_symbols.cpp | 2 + 3 files changed, 64 insertions(+) (limited to 'src') diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index c48cd42360..977d8a6742 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -137,6 +137,55 @@ static unsigned int q_ssl_psk_server_callback(SSL *ssl, Q_ASSERT(d); return d->tlsPskServerCallback(identity, psk, max_psk_len); } + +#ifdef TLS1_3_VERSION +#ifndef OPENSSL_NO_PSK +static unsigned int q_ssl_psk_restore_client(SSL *ssl, + const char *hint, + char *identity, unsigned int max_identity_len, + unsigned char *psk, unsigned int max_psk_len) +{ + Q_UNUSED(hint); + Q_UNUSED(identity); + Q_UNUSED(max_identity_len); + Q_UNUSED(psk); + Q_UNUSED(max_psk_len); + +#ifdef QT_DEBUG + QSslSocketBackendPrivate *d = reinterpret_cast(q_SSL_get_ex_data(ssl, QSslSocketBackendPrivate::s_indexForSSLExtraData)); + Q_ASSERT(d); + Q_ASSERT(d->mode == QSslSocket::SslClientMode); +#endif + q_SSL_set_psk_client_callback(ssl, &q_ssl_psk_client_callback); + + return 0; +} +#endif // !OPENSSL_NO_PSK + +static int q_ssl_psk_use_session_callback(SSL *ssl, const EVP_MD *md, const unsigned char **id, + size_t *idlen, SSL_SESSION **sess) +{ + Q_UNUSED(ssl); + Q_UNUSED(md); + Q_UNUSED(id); + Q_UNUSED(idlen); + Q_UNUSED(sess); + +#ifndef OPENSSL_NO_PSK +#ifdef QT_DEBUG + QSslSocketBackendPrivate *d = reinterpret_cast(q_SSL_get_ex_data(ssl, QSslSocketBackendPrivate::s_indexForSSLExtraData)); + Q_ASSERT(d); + Q_ASSERT(d->mode == QSslSocket::SslClientMode); +#endif + + // Temporarily rebind the psk because it will be called next. The function will restore it. + q_SSL_set_psk_client_callback(ssl, &q_ssl_psk_restore_client); +#endif + + return 1; // need to return 1 or else "the connection setup fails." +} +#endif // TLS1_3_VERSION + #endif #if QT_CONFIG(ocsp) @@ -555,6 +604,13 @@ bool QSslSocketBackendPrivate::initSslContext() q_SSL_set_psk_server_callback(ssl, &q_ssl_psk_server_callback); } #endif +#if OPENSSL_VERSION_NUMBER >= 0x10101006L + // Set the client callback for TLSv1.3 PSK + if (mode == QSslSocket::SslClientMode + && QSslSocket::sslLibraryBuildVersionNumber() >= 0x10101006L) { + q_SSL_set_psk_use_session_callback(ssl, &q_ssl_psk_use_session_callback); + } +#endif // openssl version >= 0x10101006L #if QT_CONFIG(ocsp) if (configuration.ocspStaplingEnabled) { diff --git a/src/network/ssl/qsslsocket_openssl11_symbols_p.h b/src/network/ssl/qsslsocket_openssl11_symbols_p.h index a44d00a830..d523a95750 100644 --- a/src/network/ssl/qsslsocket_openssl11_symbols_p.h +++ b/src/network/ssl/qsslsocket_openssl11_symbols_p.h @@ -183,4 +183,10 @@ const OCSP_CERTID *q_OCSP_SINGLERESP_get0_id(const OCSP_SINGLERESP *x); #define q_SSL_CTX_set_max_proto_version(ctx, version) \ q_SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MAX_PROTO_VERSION, version, nullptr) +extern "C" { +typedef int (*q_SSL_psk_use_session_cb_func_t)(SSL *, const EVP_MD *, const unsigned char **, size_t *, + SSL_SESSION **); +} +void q_SSL_set_psk_use_session_callback(SSL *s, q_SSL_psk_use_session_cb_func_t); + #endif diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp index aa1dc681e0..93b54aaa67 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols.cpp +++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp @@ -163,6 +163,7 @@ DEFINEFUNC(int, SSL_session_reused, SSL *a, a, return 0, return) DEFINEFUNC2(unsigned long, SSL_CTX_set_options, SSL_CTX *ctx, ctx, unsigned long op, op, return 0, return) #ifdef TLS1_3_VERSION DEFINEFUNC2(int, SSL_CTX_set_ciphersuites, SSL_CTX *ctx, ctx, const char *str, str, return 0, return) +DEFINEFUNC2(void, SSL_set_psk_use_session_callback, SSL *ssl, ssl, q_SSL_psk_use_session_cb_func_t callback, callback, return, DUMMYARG) #endif DEFINEFUNC3(size_t, SSL_get_client_random, SSL *a, a, unsigned char *out, out, size_t outlen, outlen, return 0, return) DEFINEFUNC3(size_t, SSL_SESSION_get_master_key, const SSL_SESSION *ses, ses, unsigned char *out, out, size_t outlen, outlen, return 0, return) @@ -967,6 +968,7 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(SSL_CTX_set_options) #ifdef TLS1_3_VERSION RESOLVEFUNC(SSL_CTX_set_ciphersuites) + RESOLVEFUNC(SSL_set_psk_use_session_callback) #endif // TLS 1.3 or OpenSSL > 1.1.1 RESOLVEFUNC(SSL_get_client_random) RESOLVEFUNC(SSL_SESSION_get_master_key) -- cgit v1.2.3 From 5aaade8c935caa1add92f61187c0065e34cf7e0e Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Wed, 15 May 2019 12:35:08 +0200 Subject: Fix editing of QDateTimeEdit in 12-hour locales that don't use AM/PM The code made two incorrect assumptions: that the strings used are "AM" or "PM", or would be translated. Instead, the locale provides the correct strings, and there is no need to translate. However, in order not to break existing translations, we give those preference. And that the AM/PM string is not longer than 4 characters, while in e.g Spanish/Columbia locale the strings are "A. M." and "P. M.", ie 5 characters long. Also, the use of qMin in a function that is asked to provide the maximum section length is wrong. [ChangeLog][QWidgets][QDateTimeEdit] Use the information provided by the locale to determine the AM/PM strings, unless they are already translated. Change-Id: I6d1b05376e5ac62fc58da2cdea2e6cb732ec6747 Fixes: QTBUG-72833 Reviewed-by: Andy Shaw Reviewed-by: Edward Welbourne --- src/corelib/tools/qdatetimeparser.cpp | 27 +++++++++++++++------------ src/widgets/widgets/qdatetimeedit.cpp | 22 ++++++++++++++++++++-- 2 files changed, 35 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/corelib/tools/qdatetimeparser.cpp b/src/corelib/tools/qdatetimeparser.cpp index e8470f6cde..adc5451dec 100644 --- a/src/corelib/tools/qdatetimeparser.cpp +++ b/src/corelib/tools/qdatetimeparser.cpp @@ -622,11 +622,11 @@ int QDateTimeParser::sectionMaxSize(Section s, int count) const case LastSection: return 0; case AmPmSection: { - const int lowerMax = qMin(getAmPmText(AmText, LowerCase).size(), + const int lowerMax = qMax(getAmPmText(AmText, LowerCase).size(), getAmPmText(PmText, LowerCase).size()); - const int upperMax = qMin(getAmPmText(AmText, UpperCase).size(), + const int upperMax = qMax(getAmPmText(AmText, UpperCase).size(), getAmPmText(PmText, UpperCase).size()); - return qMin(4, qMin(lowerMax, upperMax)); + return qMax(lowerMax, upperMax); } case Hour24Section: @@ -1665,13 +1665,16 @@ QDateTimeParser::findTimeZone(QStringRef str, const QDateTime &when, /*! \internal - Returns - AM if str == tr("AM") - PM if str == tr("PM") - PossibleAM if str can become tr("AM") - PossiblePM if str can become tr("PM") - PossibleBoth if str can become tr("PM") and can become tr("AM") - Neither if str can't become anything sensible + Compares str to the am/pm texts returned by getAmPmText(). + Returns AM or PM if str is one of those texts. Failing that, it looks to see + whether, ignoring spaces and case, each character of str appears in one of + the am/pm texts. + If neither text can be the result of the user typing more into str, returns + Neither. If both texts are possible results of further typing, returns + PossibleBoth. Otherwise, only one of them is a possible completion, so this + returns PossibleAM or PossiblePM to indicate which. + + \sa getAmPmText() */ QDateTimeParser::AmPmFinder QDateTimeParser::findAmPm(QString &str, int sectionIndex, int *used) const { @@ -1700,10 +1703,10 @@ QDateTimeParser::AmPmFinder QDateTimeParser::findAmPm(QString &str, int sectionI QDTPDEBUG << "findAmPm" << str << ampm[0] << ampm[1]; - if (str.indexOf(ampm[amindex], 0, Qt::CaseInsensitive) == 0) { + if (str.startsWith(ampm[amindex], Qt::CaseInsensitive)) { str = ampm[amindex]; return AM; - } else if (str.indexOf(ampm[pmindex], 0, Qt::CaseInsensitive) == 0) { + } else if (str.startsWith(ampm[pmindex], Qt::CaseInsensitive)) { str = ampm[pmindex]; return PM; } else if (context == FromString || (str.count(space) == 0 && str.size() >= size)) { diff --git a/src/widgets/widgets/qdatetimeedit.cpp b/src/widgets/widgets/qdatetimeedit.cpp index 3cebea77d6..3e6afdb586 100644 --- a/src/widgets/widgets/qdatetimeedit.cpp +++ b/src/widgets/widgets/qdatetimeedit.cpp @@ -2307,13 +2307,31 @@ void QDateTimeEdit::paintEvent(QPaintEvent *event) style()->drawComplexControl(QStyle::CC_ComboBox, &optCombo, &p, this); } +/* + Returns the string for AM and PM markers. + + If a translation for "AM" and "PM" is installed, then use that. + Otherwise, use the default implementation, which uses the locale. +*/ QString QDateTimeEditPrivate::getAmPmText(AmPm ap, Case cs) const { + QString original; + QString translated; if (ap == AmText) { - return (cs == UpperCase ? QDateTimeParser::tr("AM") : QDateTimeParser::tr("am")); + original = QLatin1String(cs == UpperCase ? "AM" : "am"); + translated = (cs == UpperCase ? QDateTimeParser::tr("AM") : QDateTimeParser::tr("am")); } else { - return (cs == UpperCase ? QDateTimeParser::tr("PM") : QDateTimeParser::tr("pm")); + original = QLatin1String(cs == UpperCase ? "PM" : "pm"); + translated = (cs == UpperCase ? QDateTimeParser::tr("PM") : QDateTimeParser::tr("pm")); } + + // This logic fails if a translation exists but doesn't change the string, + // which we can accept as a corner-case for which a locale-derived answer + // will be acceptable. + if (original != translated) + return translated; + + return QDateTimeParser::getAmPmText(ap, cs); } int QDateTimeEditPrivate::absoluteIndex(QDateTimeEdit::Section s, int index) const -- cgit v1.2.3 From 4ad915425d804df798e12031c9b2fc13534dcb97 Mon Sep 17 00:00:00 2001 From: Topi Reinio Date: Fri, 3 May 2019 14:17:19 +0200 Subject: Doc: Replace example file lists with links to code.qt.io Instead of generating .html page for each file in an example project, generate links to code.qt.io, under the correct path and branch, where the user can browse the example source. Store all URLs under QT_INSTALL_DOCS/config where other qt5 submodules can access them. The repository name appears in the URL, so we cannot define a single URL for all modules. Task-number: QTBUG-74391 Change-Id: I63d4d6d2c352877797b1ee8e057d48c0cd789bff Reviewed-by: Paul Wicking --- src/concurrent/doc/qtconcurrent.qdocconf | 1 + src/corelib/doc/qtcore.qdocconf | 1 + src/dbus/doc/qtdbus.qdocconf | 1 + src/gui/doc/qtgui.qdocconf | 1 + src/network/doc/qtnetwork.qdocconf | 1 + src/opengl/doc/qtopengl.qdocconf | 1 + src/platformheaders/doc/qtplatformheaders.qdocconf | 1 + src/printsupport/doc/qtprintsupport.qdocconf | 1 + src/sql/doc/qtsql.qdocconf | 1 + src/testlib/doc/qttestlib.qdocconf | 1 + src/widgets/doc/qtwidgets.qdocconf | 1 + src/xml/doc/qtxml.qdocconf | 1 + 12 files changed, 12 insertions(+) (limited to 'src') diff --git a/src/concurrent/doc/qtconcurrent.qdocconf b/src/concurrent/doc/qtconcurrent.qdocconf index 356d602a7c..610dca2a7e 100644 --- a/src/concurrent/doc/qtconcurrent.qdocconf +++ b/src/concurrent/doc/qtconcurrent.qdocconf @@ -1,4 +1,5 @@ include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) +include($QT_INSTALL_DOCS/config/exampleurl-qtbase.qdocconf) project = QtConcurrent description = Qt Concurrent Reference Documentation diff --git a/src/corelib/doc/qtcore.qdocconf b/src/corelib/doc/qtcore.qdocconf index 85dcde4607..15b1925e51 100644 --- a/src/corelib/doc/qtcore.qdocconf +++ b/src/corelib/doc/qtcore.qdocconf @@ -1,4 +1,5 @@ include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) +include($QT_INSTALL_DOCS/config/exampleurl-qtbase.qdocconf) project = QtCore description = Qt Core Reference Documentation diff --git a/src/dbus/doc/qtdbus.qdocconf b/src/dbus/doc/qtdbus.qdocconf index 4ff7242b25..e08bbfd94e 100644 --- a/src/dbus/doc/qtdbus.qdocconf +++ b/src/dbus/doc/qtdbus.qdocconf @@ -1,4 +1,5 @@ include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) +include($QT_INSTALL_DOCS/config/exampleurl-qtbase.qdocconf) # Name of the project which must match the outputdir. Determines the .index file project = QtDBus diff --git a/src/gui/doc/qtgui.qdocconf b/src/gui/doc/qtgui.qdocconf index b8b8a00cd6..049b9ef179 100644 --- a/src/gui/doc/qtgui.qdocconf +++ b/src/gui/doc/qtgui.qdocconf @@ -1,4 +1,5 @@ include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) +include($QT_INSTALL_DOCS/config/exampleurl-qtbase.qdocconf) project = QtGui description = Qt GUI Reference Documentation diff --git a/src/network/doc/qtnetwork.qdocconf b/src/network/doc/qtnetwork.qdocconf index 4f667eed9d..5465b1c0af 100644 --- a/src/network/doc/qtnetwork.qdocconf +++ b/src/network/doc/qtnetwork.qdocconf @@ -1,4 +1,5 @@ include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) +include($QT_INSTALL_DOCS/config/exampleurl-qtbase.qdocconf) project = QtNetwork description = Qt Network Reference Documentation diff --git a/src/opengl/doc/qtopengl.qdocconf b/src/opengl/doc/qtopengl.qdocconf index 2d38a5d2af..22194bda59 100644 --- a/src/opengl/doc/qtopengl.qdocconf +++ b/src/opengl/doc/qtopengl.qdocconf @@ -1,4 +1,5 @@ include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) +include($QT_INSTALL_DOCS/config/exampleurl-qtbase.qdocconf) # Name of the project which must match the outputdir. Determines the .index file project = QtOpenGL diff --git a/src/platformheaders/doc/qtplatformheaders.qdocconf b/src/platformheaders/doc/qtplatformheaders.qdocconf index 9a034e7671..c8868d678b 100644 --- a/src/platformheaders/doc/qtplatformheaders.qdocconf +++ b/src/platformheaders/doc/qtplatformheaders.qdocconf @@ -1,4 +1,5 @@ include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) +include($QT_INSTALL_DOCS/config/exampleurl-qtbase.qdocconf) project = QtPlatformHeaders description = Qt Platform Headers Reference Documentation diff --git a/src/printsupport/doc/qtprintsupport.qdocconf b/src/printsupport/doc/qtprintsupport.qdocconf index fbb6f8d1a9..b46d9f37d9 100644 --- a/src/printsupport/doc/qtprintsupport.qdocconf +++ b/src/printsupport/doc/qtprintsupport.qdocconf @@ -1,4 +1,5 @@ include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) +include($QT_INSTALL_DOCS/config/exampleurl-qtbase.qdocconf) project = QtPrintSupport description = Qt Print Support Reference Documentation diff --git a/src/sql/doc/qtsql.qdocconf b/src/sql/doc/qtsql.qdocconf index 5a224adeb9..f353a11e8b 100644 --- a/src/sql/doc/qtsql.qdocconf +++ b/src/sql/doc/qtsql.qdocconf @@ -1,4 +1,5 @@ include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) +include($QT_INSTALL_DOCS/config/exampleurl-qtbase.qdocconf) project = QtSql description = Qt SQL Reference Documentation diff --git a/src/testlib/doc/qttestlib.qdocconf b/src/testlib/doc/qttestlib.qdocconf index 5fdf6d9415..73310221cf 100644 --- a/src/testlib/doc/qttestlib.qdocconf +++ b/src/testlib/doc/qttestlib.qdocconf @@ -1,4 +1,5 @@ include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) +include($QT_INSTALL_DOCS/config/exampleurl-qtbase.qdocconf) project = QtTestLib moduleheader = QtTest diff --git a/src/widgets/doc/qtwidgets.qdocconf b/src/widgets/doc/qtwidgets.qdocconf index 5d7262fca1..6e04372a8b 100644 --- a/src/widgets/doc/qtwidgets.qdocconf +++ b/src/widgets/doc/qtwidgets.qdocconf @@ -1,4 +1,5 @@ include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) +include($QT_INSTALL_DOCS/config/exampleurl-qtbase.qdocconf) project = QtWidgets description = Qt Widgets Reference Documentation diff --git a/src/xml/doc/qtxml.qdocconf b/src/xml/doc/qtxml.qdocconf index a23915487f..25a463fecd 100644 --- a/src/xml/doc/qtxml.qdocconf +++ b/src/xml/doc/qtxml.qdocconf @@ -1,4 +1,5 @@ include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) +include($QT_INSTALL_DOCS/config/exampleurl-qtbase.qdocconf) project = QtXml description = Qt XML Reference Documentation -- cgit v1.2.3 From 7ae2f47214408fb37ccb52d1fa659cc47a63443b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Tue, 5 Mar 2019 00:17:43 +0100 Subject: QHighDpi: Replace fromNative()/toNative() with scale() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need one scale() function for each type: the scale factor can be inverted by the caller for the fromNative case. Add a generic templated implementation which will handle simple types. Add add specialization functions for compound types that don't have operator*(), as well as for position types (e.g. QPoint) which account for the origin. There's no need for fromNativePixels() and toNativePixels() overloads for each type; replace with generic implementations which call scale(). Do the same thing for fromNativeLocalPosition(). Some user code is calling fromNative()/toNative() directly, so leave a definition of those functions around for now. Also leave a couple of one-off scaling functions which do not fit the pattern. Also fix “narrowing conversion scaleFactor to int” warning for the QMargins scale function. Change-Id: Ia67accbb670a80dc1747c2e264b97aab75b1251b Reviewed-by: Friedemann Kleint Reviewed-by: Tor Arne Vestbø --- src/gui/kernel/qhighdpiscaling_p.h | 264 ++++++++----------------------------- 1 file changed, 55 insertions(+), 209 deletions(-) (limited to 'src') diff --git a/src/gui/kernel/qhighdpiscaling_p.h b/src/gui/kernel/qhighdpiscaling_p.h index dfc6abf5ba..525e3fe78e 100644 --- a/src/gui/kernel/qhighdpiscaling_p.h +++ b/src/gui/kernel/qhighdpiscaling_p.h @@ -108,208 +108,114 @@ private: namespace QHighDpi { -inline QPointF fromNative(const QPointF &pos, qreal scaleFactor, const QPointF &origin) +template +inline T scale(const T &value, qreal scaleFactor, QPoint origin = QPoint(0, 0)) { - return (pos - origin) / scaleFactor + origin; + Q_UNUSED(origin) + return value * scaleFactor; } -inline QPointF toNative(const QPointF &pos, qreal scaleFactor, const QPointF &origin) +inline QPointF scale(const QPointF &pos, qreal scaleFactor, QPointF origin = QPointF(0, 0)) { return (pos - origin) * scaleFactor + origin; } -inline QPoint fromNative(const QPoint &pos, qreal scaleFactor, const QPoint &origin) -{ - return (pos - origin) / scaleFactor + origin; -} - -inline QPoint toNative(const QPoint &pos, qreal scaleFactor, const QPoint &origin) +inline QPoint scale(const QPoint &pos, qreal scaleFactor, QPoint origin = QPoint(0, 0)) { return (pos - origin) * scaleFactor + origin; } -inline QPoint fromNative(const QPoint &pos, qreal scaleFactor) -{ - return pos / scaleFactor; -} - -inline QPoint toNative(const QPoint &pos, qreal scaleFactor) -{ - return pos * scaleFactor; -} - -inline QSize fromNative(const QSize &size, qreal scaleFactor) -{ - return size / scaleFactor; // TODO: should we round up? -} - -inline QSize toNative(const QSize &size, qreal scaleFactor) +inline QRect scale(const QRect &rect, qreal scaleFactor, QPoint origin = QPoint(0, 0)) { - return size * scaleFactor; -} - -inline QSizeF fromNative(const QSizeF &size, qreal scaleFactor) -{ - return size / scaleFactor; -} - -inline QSizeF toNative(const QSizeF &size, qreal scaleFactor) -{ - return size * scaleFactor; -} - -inline QRect fromNative(const QRect &rect, qreal scaleFactor, const QPoint &origin) -{ - return QRect(fromNative(rect.topLeft(), scaleFactor, origin), fromNative(rect.size(), scaleFactor)); -} - -inline QRect toNative(const QRect &rect, qreal scaleFactor, const QPoint &origin) -{ - return QRect(toNative(rect.topLeft(), scaleFactor, origin), toNative(rect.size(), scaleFactor)); - -} - -inline QRect fromNative(const QRect &rect, const QScreen *screen, const QPoint &screenOrigin) -{ - return fromNative(rect, QHighDpiScaling::factor(screen), screenOrigin); -} - -inline QRect fromNativeScreenGeometry(const QRect &nativeScreenGeometry, const QScreen *screen) -{ - return QRect(nativeScreenGeometry.topLeft(), - fromNative(nativeScreenGeometry.size(), QHighDpiScaling::factor(screen))); -} - -inline QPoint fromNativeLocalPosition(const QPoint &pos, const QWindow *window) -{ - const qreal scaleFactor = QHighDpiScaling::factor(window); - return pos / scaleFactor; -} - -inline QPoint toNativeLocalPosition(const QPoint &pos, const QWindow *window) -{ - const qreal scaleFactor = QHighDpiScaling::factor(window); - return pos * scaleFactor; -} - -inline QPointF fromNativeLocalPosition(const QPointF &pos, const QWindow *window) -{ - const qreal scaleFactor = QHighDpiScaling::factor(window); - return pos / scaleFactor; -} - -inline QPointF toNativeLocalPosition(const QPointF &pos, const QWindow *window) -{ - const qreal scaleFactor = QHighDpiScaling::factor(window); - return pos * scaleFactor; + return QRect(scale(rect.topLeft(), scaleFactor, origin), scale(rect.size(), scaleFactor)); } -template -inline QRect fromNativePixels(const QRect &pixelRect, const C *context) +inline QRectF scale(const QRectF &rect, qreal scaleFactor, QPoint origin = QPoint(0, 0)) { - const qreal scaleFactor = QHighDpiScaling::factor(context); - const QPoint origin = QHighDpiScaling::origin(context); - return QRect(fromNative(pixelRect.topLeft(), scaleFactor, origin), - fromNative(pixelRect.size(), scaleFactor)); + return QRectF(scale(rect.topLeft(), scaleFactor, origin), scale(rect.size(), scaleFactor)); } -template -inline QRect toNativePixels(const QRect &pointRect, const C *context) +inline QMargins scale(const QMargins &margins, qreal scaleFactor, QPoint origin = QPoint(0, 0)) { - const qreal scaleFactor = QHighDpiScaling::factor(context); - const QPoint origin = QHighDpiScaling::origin(context); - return QRect(toNative(pointRect.topLeft(), scaleFactor, origin), - toNative(pointRect.size(), scaleFactor)); + Q_UNUSED(origin) + return QMargins(qRound(qreal(margins.left()) * scaleFactor), qRound(qreal(margins.top()) * scaleFactor), + qRound(qreal(margins.right()) * scaleFactor), qRound(qreal(margins.bottom()) * scaleFactor)); } -template -inline QRectF toNativePixels(const QRectF &pointRect, const C *context) +template +QVector scale(const QVector &vector, qreal scaleFactor, QPoint origin = QPoint(0, 0)) { - const qreal scaleFactor = QHighDpiScaling::factor(context); - const QPoint origin = QHighDpiScaling::origin(context); - return QRectF(toNative(pointRect.topLeft(), scaleFactor, origin), - toNative(pointRect.size(), scaleFactor)); -} + if (!QHighDpiScaling::isActive()) + return vector; -template -inline QRectF fromNativePixels(const QRectF &pixelRect, const C *context) -{ - const qreal scaleFactor = QHighDpiScaling::factor(context); - const QPoint origin = QHighDpiScaling::origin(context); - return QRectF(fromNative(pixelRect.topLeft(), scaleFactor, origin), - fromNative(pixelRect.size(), scaleFactor)); + QVector scaled; + scaled.reserve(vector.size()); + for (const T &item : vector) + scaled.append(scale(item, scaleFactor, origin)); + return scaled; } -inline QSize fromNativePixels(const QSize &pixelSize, const QWindow *window) +inline QRegion scale(const QRegion ®ion, qreal scaleFactor, QPoint origin = QPoint(0, 0)) { - return pixelSize / QHighDpiScaling::factor(window); -} + if (!QHighDpiScaling::isActive()) + return region; -inline QSize toNativePixels(const QSize &pointSize, const QWindow *window) -{ - return pointSize * QHighDpiScaling::factor(window); + QRegion scaled; + for (const QRect &rect : region) + scaled += scale(rect, scaleFactor, origin); + return scaled; } -inline QSizeF fromNativePixels(const QSizeF &pixelSize, const QWindow *window) +template +T fromNativePixels(const T &value, const C *context) { - return pixelSize / QHighDpiScaling::factor(window); + return scale(value, qreal(1) / QHighDpiScaling::factor(context), QHighDpiScaling::origin(context)); } -inline QSizeF toNativePixels(const QSizeF &pointSize, const QWindow *window) +template +T toNativePixels(const T &value, const C *context) { - return pointSize * QHighDpiScaling::factor(window); + return scale(value, QHighDpiScaling::factor(context), QHighDpiScaling::origin(context)); } -template -inline QPoint fromNativePixels(const QPoint &pixelPoint, const C *context) +template +T fromNativeLocalPosition(const T &value, const C *context) { - return fromNative(pixelPoint, QHighDpiScaling::factor(context), QHighDpiScaling::origin(context)); + return scale(value, qreal(1) / QHighDpiScaling::factor(context)); } -template -inline QPoint toNativePixels(const QPoint &pointPoint, const C *context) +template +T toNativeLocalPosition(const T &value, const C *context) { - return toNative(pointPoint, QHighDpiScaling::factor(context), QHighDpiScaling::origin(context)); + return scale(value, QHighDpiScaling::factor(context)); } -template -inline QPointF fromNativePixels(const QPointF &pixelPoint, const C *context) +template +inline T fromNative(const T &value, qreal scaleFactor, QPoint origin = QPoint(0, 0)) { - return fromNative(pixelPoint, QHighDpiScaling::factor(context), QHighDpiScaling::origin(context)); + return scale(value, qreal(1) / scaleFactor, origin); } -template -inline QPointF toNativePixels(const QPointF &pointPoint, const C *context) +template +inline T toNative(const T &value, qreal scaleFactor, QPoint origin = QPoint(0, 0)) { - return toNative(pointPoint, QHighDpiScaling::factor(context), QHighDpiScaling::origin(context)); + return scale(value, scaleFactor, origin); } -inline QMargins fromNativePixels(const QMargins &pixelMargins, const QWindow *window) +inline QRect fromNative(const QRect &rect, const QScreen *screen, const QPoint &screenOrigin) { - const qreal scaleFactor = QHighDpiScaling::factor(window); - return QMargins(pixelMargins.left() / scaleFactor, pixelMargins.top() / scaleFactor, - pixelMargins.right() / scaleFactor, pixelMargins.bottom() / scaleFactor); + return scale(rect, qreal(1) / QHighDpiScaling::factor(screen), screenOrigin); } -inline QMargins toNativePixels(const QMargins &pointMargins, const QWindow *window) +inline QRect fromNativeScreenGeometry(const QRect &nativeScreenGeometry, const QScreen *screen) { - const qreal scaleFactor = QHighDpiScaling::factor(window); - return QMargins(pointMargins.left() * scaleFactor, pointMargins.top() * scaleFactor, - pointMargins.right() * scaleFactor, pointMargins.bottom() * scaleFactor); + return QRect(nativeScreenGeometry.topLeft(), + scale(nativeScreenGeometry.size(), qreal(1) / QHighDpiScaling::factor(screen))); } inline QRegion fromNativeLocalRegion(const QRegion &pixelRegion, const QWindow *window) { - if (!QHighDpiScaling::isActive()) - return pixelRegion; - - qreal scaleFactor = QHighDpiScaling::factor(window); - QRegion pointRegion; - for (const QRect &rect : pixelRegion) { - pointRegion += QRect(fromNative(rect.topLeft(), scaleFactor), - fromNative(rect.size(), scaleFactor)); - } - return pointRegion; + return scale(pixelRegion, qreal(1) / QHighDpiScaling::factor(window)); } // When mapping expose events to Qt rects: round top/left towards the origin and @@ -333,67 +239,7 @@ inline QRegion fromNativeLocalExposedRegion(const QRegion &pixelRegion, const QW inline QRegion toNativeLocalRegion(const QRegion &pointRegion, const QWindow *window) { - if (!QHighDpiScaling::isActive()) - return pointRegion; - - qreal scaleFactor = QHighDpiScaling::factor(window); - QRegion pixelRegon; - for (const QRect &rect : pointRegion) { - pixelRegon += QRect(toNative(rect.topLeft(), scaleFactor), - toNative(rect.size(), scaleFactor)); - } - return pixelRegon; -} - -// Any T that has operator/() -template -T fromNativePixels(const T &pixelValue, const C *context) -{ - if (!QHighDpiScaling::isActive()) - return pixelValue; - - return pixelValue / QHighDpiScaling::factor(context); - -} - -// Any T that has operator*() -template -T toNativePixels(const T &pointValue, const C *context) -{ - if (!QHighDpiScaling::isActive()) - return pointValue; - - return pointValue * QHighDpiScaling::factor(context); -} - -// Any QVector where T has operator/() -template -QVector fromNativePixels(const QVector &pixelValues, const QWindow *window) -{ - if (!QHighDpiScaling::isActive()) - return pixelValues; - - QVector pointValues; - pointValues.reserve(pixelValues.size()); - const auto factor = QHighDpiScaling::factor(window); - for (const T &pixelValue : pixelValues) - pointValues.append(pixelValue / factor); - return pointValues; -} - -// Any QVector where T has operator*() -template -QVector toNativePixels(const QVector &pointValues, const QWindow *window) -{ - if (!QHighDpiScaling::isActive()) - return pointValues; - - QVector pixelValues; - pixelValues.reserve(pointValues.size()); - const auto factor = QHighDpiScaling::factor(window); - for (const T &pointValue : pointValues) - pixelValues.append(pointValue * factor); - return pixelValues; + return scale(pointRegion, QHighDpiScaling::factor(window)); } } // namespace QHighDpi -- cgit v1.2.3 From 8e528d8bd08406e9cc86abcfe153f02d585d3654 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 21 May 2019 14:16:16 +0200 Subject: iOS Accessibility: implement accessibilityElements and check indexing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: QTBUG-70683 Change-Id: I122c67a5cee22363de5c8e45dc1c83e7760162fb Reviewed-by: Tor Arne Vestbø --- src/plugins/platforms/ios/quiview_accessibility.mm | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src') diff --git a/src/plugins/platforms/ios/quiview_accessibility.mm b/src/plugins/platforms/ios/quiview_accessibility.mm index a3f4156a59..458ddcc9b8 100644 --- a/src/plugins/platforms/ios/quiview_accessibility.mm +++ b/src/plugins/platforms/ios/quiview_accessibility.mm @@ -101,6 +101,8 @@ - (id)accessibilityElementAtIndex:(NSInteger)index { [self initAccessibility]; + if (index >= [m_accessibleElements count]) + return nil; return m_accessibleElements[index]; } @@ -110,4 +112,10 @@ return [m_accessibleElements indexOfObject:element]; } +- (NSArray *)accessibilityElements +{ + [self initAccessibility]; + return m_accessibleElements; +} + @end -- cgit v1.2.3 From 60932f69cc2e7d8f00a05ff84153d82bfc533e23 Mon Sep 17 00:00:00 2001 From: Rolf Eike Beer Date: Wed, 19 Jun 2019 09:30:12 +0200 Subject: QTimeZone: avoid needless QString instances Change-Id: I250bfb58c04b2c013285c8ebd6db1f964f0b7b29 Reviewed-by: Marc Mutz Reviewed-by: Thiago Macieira --- src/corelib/time/qtimezoneprivate_tz.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/corelib/time/qtimezoneprivate_tz.cpp b/src/corelib/time/qtimezoneprivate_tz.cpp index 1c74305e3d..f62d7998c8 100644 --- a/src/corelib/time/qtimezoneprivate_tz.cpp +++ b/src/corelib/time/qtimezoneprivate_tz.cpp @@ -1112,7 +1112,7 @@ QByteArray QTzTimeZonePrivate::systemTimeZoneId() const path = QFile::symLinkTarget(path); if (index >= 0) { // /etc/localtime is a symlink to the current TZ file, so extract from path - ianaId = path.mid(index + zoneinfo.size()).toUtf8(); + ianaId = path.midRef(index + zoneinfo.size()).toUtf8(); } } @@ -1138,9 +1138,9 @@ QByteArray QTzTimeZonePrivate::systemTimeZoneId() const while (ianaId.isEmpty() && !ts.atEnd() && ts.status() == QTextStream::Ok) { line = ts.readLine(); if (line.startsWith(QLatin1String("ZONE="))) { - ianaId = line.mid(6, line.size() - 7).toUtf8(); + ianaId = line.midRef(6, line.size() - 7).toUtf8(); } else if (line.startsWith(QLatin1String("TIMEZONE="))) { - ianaId = line.mid(10, line.size() - 11).toUtf8(); + ianaId = line.midRef(10, line.size() - 11).toUtf8(); } } } -- cgit v1.2.3 From 743bc1254cee51e7c5578706d77cea9f0e06d945 Mon Sep 17 00:00:00 2001 From: BogDan Vatra Date: Wed, 29 May 2019 09:42:53 +0300 Subject: Android: Fix resources folder Resources are usually needed to add extra stuff to apk lib/ which normally are stripped by gradle. Change-Id: Id96ce246fdc2a4606e39ecfb75d5b3294aad3c08 Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/android/templates/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/android/templates/build.gradle b/src/android/templates/build.gradle index 989d0792cf..5754c82708 100644 --- a/src/android/templates/build.gradle +++ b/src/android/templates/build.gradle @@ -44,7 +44,7 @@ android { java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java'] aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl'] res.srcDirs = [qt5AndroidDir + '/res', 'res'] - resources.srcDirs = ['src'] + resources.srcDirs = ['resources'] renderscript.srcDirs = ['src'] assets.srcDirs = ['assets'] jniLibs.srcDirs = ['libs'] -- cgit v1.2.3 From 84e89c1e9e00d4fab576b876cfa80e92b5602982 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Wed, 12 Jun 2019 18:06:23 +0200 Subject: Convert uses of QTime as a timer to QElapsedTimer Change-Id: I2297f61efa5adf9ea5194c7f3ff68574cbcf452c Reviewed-by: Friedemann Kleint --- src/corelib/kernel/qelapsedtimer.cpp | 2 +- src/gui/image/qmovie.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/corelib/kernel/qelapsedtimer.cpp b/src/corelib/kernel/qelapsedtimer.cpp index adb554b624..57825583dd 100644 --- a/src/corelib/kernel/qelapsedtimer.cpp +++ b/src/corelib/kernel/qelapsedtimer.cpp @@ -65,7 +65,7 @@ QT_BEGIN_NAMESPACE \snippet qelapsedtimer/main.cpp 0 In this example, the timer is started by a call to start() and the - elapsed timer is calculated by the elapsed() function. + elapsed time is calculated by the elapsed() function. The time elapsed can also be used to recalculate the time available for another operation, after the first one is complete. This is useful when diff --git a/src/gui/image/qmovie.cpp b/src/gui/image/qmovie.cpp index c9344fe2a1..4b588527ae 100644 --- a/src/gui/image/qmovie.cpp +++ b/src/gui/image/qmovie.cpp @@ -180,7 +180,7 @@ #include "qimagereader.h" #include "qpixmap.h" #include "qrect.h" -#include "qdatetime.h" +#include "qelapsedtimer.h" #include "qtimer.h" #include "qpair.h" #include "qmap.h" -- cgit v1.2.3 From 34fe9232dbf6a9fe58ebc4c7680bb67d2f642c40 Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Mon, 10 Jun 2019 11:08:29 +0200 Subject: Port from QAtomic::load() to loadRelaxed() Semi-automated, just needed ~20 manual fixes: $ find \( -iname \*.cpp -or -iname \*.h \) -exec perl -pe 's/(\.|->)load\(\)/$1loadRelaxed\(\)/g' -i \{\} + $ find \( -iname \*.cpp -or -iname \*.h \) -exec perl -pe 's/(\.|->)store\(/$1storeRelaxed\(/g' -i \{\} + It can be easily improved (e.g. for store check that there are no commas after the opening parens). The most common offender is QLibrary::load, and some code using std::atomic directly. Change-Id: I07c38a3c8ed32c924ef4999e85c7e45cf48f0f6c Reviewed-by: Marc Mutz --- src/concurrent/qtconcurrentiteratekernel.h | 8 +- src/concurrent/qtconcurrentthreadengine.cpp | 10 +- src/corelib/animation/qvariantanimation.cpp | 4 +- src/corelib/codecs/qsimplecodec.cpp | 6 +- src/corelib/global/qglobalstatic.h | 14 +- src/corelib/global/qlogging.cpp | 12 +- src/corelib/global/qrandom.cpp | 4 +- src/corelib/io/qloggingcategory.cpp | 10 +- src/corelib/io/qloggingcategory.h | 16 +- src/corelib/io/qprocess_p.h | 2 +- src/corelib/io/qurl.cpp | 2 +- src/corelib/io/qurlquery.cpp | 4 +- src/corelib/itemmodels/qabstractitemmodel.cpp | 2 +- src/corelib/kernel/qabstracteventdispatcher.cpp | 2 +- src/corelib/kernel/qcore_unix_p.h | 4 +- src/corelib/kernel/qcoreapplication.cpp | 16 +- src/corelib/kernel/qcoreevent.cpp | 6 +- src/corelib/kernel/qeventdispatcher_glib.cpp | 6 +- src/corelib/kernel/qeventdispatcher_unix.cpp | 8 +- src/corelib/kernel/qeventdispatcher_win.cpp | 14 +- src/corelib/kernel/qeventloop.cpp | 10 +- src/corelib/kernel/qeventloop_p.h | 4 +- src/corelib/kernel/qmetatype.h | 2 +- src/corelib/kernel/qobject.cpp | 256 ++++++++++----------- src/corelib/kernel/qobject_p.h | 28 +-- src/corelib/kernel/qsocketnotifier.cpp | 6 +- src/corelib/kernel/qvariant.cpp | 2 +- src/corelib/kernel/qvariant.h | 2 +- src/corelib/kernel/qwineventnotifier.cpp | 6 +- src/corelib/plugin/qlibrary.cpp | 16 +- src/corelib/plugin/qlibrary_p.h | 2 +- src/corelib/serialization/qcborvalue.cpp | 16 +- src/corelib/serialization/qjson_p.h | 2 +- src/corelib/serialization/qjsonarray.cpp | 2 +- src/corelib/serialization/qjsonobject.cpp | 4 +- src/corelib/thread/qatomic.h | 2 +- src/corelib/thread/qfutureinterface.cpp | 42 ++-- src/corelib/thread/qfutureinterface_p.h | 4 +- src/corelib/thread/qfuturewatcher.cpp | 6 +- src/corelib/thread/qmutex.cpp | 44 ++-- src/corelib/thread/qmutex.h | 2 +- src/corelib/thread/qmutex_linux.cpp | 4 +- src/corelib/thread/qmutex_p.h | 10 +- src/corelib/thread/qmutexpool.cpp | 6 +- src/corelib/thread/qmutexpool_p.h | 2 +- src/corelib/thread/qreadwritelock.cpp | 12 +- src/corelib/thread/qsemaphore.cpp | 6 +- src/corelib/thread/qthread.cpp | 10 +- src/corelib/thread/qthread_p.h | 4 +- src/corelib/thread/qthread_unix.cpp | 26 +-- src/corelib/thread/qthread_win.cpp | 12 +- src/corelib/thread/qthreadstorage.cpp | 6 +- src/corelib/time/qdatetime.cpp | 6 +- src/corelib/tools/qarraydata.cpp | 4 +- src/corelib/tools/qarraydataops.h | 4 +- src/corelib/tools/qcollator.cpp | 2 +- src/corelib/tools/qcontiguouscache.h | 14 +- src/corelib/tools/qfreelist_p.h | 14 +- src/corelib/tools/qhash.cpp | 10 +- src/corelib/tools/qlinkedlist.h | 2 +- src/corelib/tools/qlocale_p.h | 2 +- src/corelib/tools/qrefcount.h | 14 +- src/corelib/tools/qregexp.cpp | 2 +- src/corelib/tools/qshareddata.h | 4 +- src/corelib/tools/qsharedpointer.cpp | 10 +- src/corelib/tools/qsharedpointer_impl.h | 16 +- src/corelib/tools/qsimd.cpp | 4 +- src/corelib/tools/qsimd_p.h | 4 +- src/dbus/qdbusargument.cpp | 4 +- src/dbus/qdbusintegrator.cpp | 8 +- src/dbus/qdbusmetatype.cpp | 4 +- src/dbus/qdbuspendingcall.cpp | 2 +- src/dbus/qdbusserver.cpp | 2 +- src/dbus/qdbusunixfiledescriptor.cpp | 10 +- src/gui/image/qicon.cpp | 4 +- src/gui/image/qimage.cpp | 8 +- src/gui/image/qpicture.cpp | 2 +- src/gui/image/qpixmap.cpp | 10 +- src/gui/kernel/qevent.cpp | 48 ++-- src/gui/kernel/qevent_p.h | 2 +- src/gui/kernel/qguiapplication_p.h | 2 +- src/gui/kernel/qkeysequence.cpp | 2 +- src/gui/kernel/qopenglcontext.cpp | 2 +- src/gui/kernel/qpalette.cpp | 2 +- src/gui/kernel/qsurfaceformat.cpp | 2 +- src/gui/kernel/qwindowsysteminterface.cpp | 4 +- src/gui/opengl/qopenglframebufferobject.cpp | 2 +- src/gui/opengl/qopenglfunctions_1_0.cpp | 4 +- src/gui/opengl/qopenglfunctions_1_1.cpp | 8 +- src/gui/opengl/qopenglfunctions_1_2.cpp | 12 +- src/gui/opengl/qopenglfunctions_1_3.cpp | 16 +- src/gui/opengl/qopenglfunctions_1_4.cpp | 20 +- src/gui/opengl/qopenglfunctions_1_5.cpp | 22 +- src/gui/opengl/qopenglfunctions_2_0.cpp | 24 +- src/gui/opengl/qopenglfunctions_2_1.cpp | 26 +-- src/gui/opengl/qopenglfunctions_3_0.cpp | 28 +-- src/gui/opengl/qopenglfunctions_3_1.cpp | 20 +- .../opengl/qopenglfunctions_3_2_compatibility.cpp | 32 +-- src/gui/opengl/qopenglfunctions_3_2_core.cpp | 22 +- .../opengl/qopenglfunctions_3_3_compatibility.cpp | 36 +-- src/gui/opengl/qopenglfunctions_3_3_core.cpp | 24 +- .../opengl/qopenglfunctions_4_0_compatibility.cpp | 38 +-- src/gui/opengl/qopenglfunctions_4_0_core.cpp | 26 +-- .../opengl/qopenglfunctions_4_1_compatibility.cpp | 40 ++-- src/gui/opengl/qopenglfunctions_4_1_core.cpp | 28 +-- .../opengl/qopenglfunctions_4_2_compatibility.cpp | 42 ++-- src/gui/opengl/qopenglfunctions_4_2_core.cpp | 30 +-- .../opengl/qopenglfunctions_4_3_compatibility.cpp | 44 ++-- src/gui/opengl/qopenglfunctions_4_3_core.cpp | 32 +-- .../opengl/qopenglfunctions_4_4_compatibility.cpp | 46 ++-- src/gui/opengl/qopenglfunctions_4_4_core.cpp | 34 +-- .../opengl/qopenglfunctions_4_5_compatibility.cpp | 50 ++-- src/gui/opengl/qopenglfunctions_4_5_core.cpp | 36 +-- src/gui/painting/qbrush.cpp | 8 +- src/gui/painting/qbrush.h | 2 +- src/gui/painting/qcolorspace_p.h | 2 +- src/gui/painting/qcolortransform.cpp | 4 +- src/gui/painting/qpainterpath.cpp | 2 +- src/gui/painting/qpainterpath_p.h | 4 +- src/gui/painting/qpen.cpp | 6 +- src/gui/text/qdistancefield.cpp | 2 +- src/gui/text/qfont.cpp | 30 +-- src/gui/text/qfontdatabase.cpp | 4 +- src/gui/text/qglyphrun.cpp | 2 +- src/gui/text/qplatformfontdatabase.cpp | 2 +- src/gui/text/qrawfont_p.h | 2 +- src/gui/text/qstatictext.cpp | 4 +- src/network/access/qnetworkaccessbackend.cpp | 6 +- src/network/bearer/qbearerengine.cpp | 2 +- src/network/kernel/qnetworkproxy.cpp | 4 +- src/network/ssl/qdtls.cpp | 2 +- src/network/ssl/qdtls_openssl.cpp | 2 +- src/network/ssl/qsslsocket.cpp | 4 +- src/network/ssl/qsslsocket_openssl.cpp | 2 +- src/opengl/qgl.cpp | 4 +- src/opengl/qglcolormap.cpp | 2 +- src/opengl/qglcolormap.h | 2 +- src/opengl/qglframebufferobject.cpp | 2 +- .../fontdatabases/freetype/qfontengine_ft.cpp | 2 +- .../fontdatabases/windows/qwindowsfontdatabase.cpp | 4 +- .../platforms/android/qandroideventdispatcher.cpp | 4 +- .../platforms/android/qandroideventdispatcher.h | 2 +- .../platforms/android/qandroidinputcontext.cpp | 2 +- .../eglfs_x11/qeglfsx11integration.cpp | 2 +- src/plugins/platforms/windows/qwindowstheme.cpp | 8 +- src/plugins/platforms/winrt/qwinrtscreen.cpp | 8 +- src/sql/kernel/qsqldatabase.cpp | 2 +- src/sql/kernel/qsqlquery.cpp | 4 +- src/testlib/qtestcase.cpp | 10 +- src/testlib/qtestlog.cpp | 2 +- src/widgets/dialogs/qfileinfogatherer.cpp | 16 +- src/xml/dom/qdom.cpp | 2 +- 152 files changed, 926 insertions(+), 926 deletions(-) (limited to 'src') diff --git a/src/concurrent/qtconcurrentiteratekernel.h b/src/concurrent/qtconcurrentiteratekernel.h index 89fd3d2592..3095c9ff52 100644 --- a/src/concurrent/qtconcurrentiteratekernel.h +++ b/src/concurrent/qtconcurrentiteratekernel.h @@ -206,9 +206,9 @@ public: bool shouldStartThread() override { if (forIteration) - return (currentIndex.load() < iterationCount) && !this->shouldThrottleThread(); + return (currentIndex.loadRelaxed() < iterationCount) && !this->shouldThrottleThread(); else // whileIteration - return (iteratorThreads.load() == 0); + return (iteratorThreads.loadRelaxed() == 0); } ThreadFunctionResult threadFunction() override @@ -230,7 +230,7 @@ public: const int currentBlockSize = blockSizeManager.blockSize(); - if (currentIndex.load() >= iterationCount) + if (currentIndex.loadRelaxed() >= iterationCount) break; // Atomically reserve a block of iterationCount for this thread. @@ -261,7 +261,7 @@ public: // Report progress if progress reporting enabled. if (progressReportingEnabled) { completed.fetchAndAddAcquire(finalBlockSize); - this->setProgressValue(this->completed.load()); + this->setProgressValue(this->completed.loadRelaxed()); } if (this->shouldThrottleThread()) diff --git a/src/concurrent/qtconcurrentthreadengine.cpp b/src/concurrent/qtconcurrentthreadengine.cpp index 968720cbbe..7f91a2ba68 100644 --- a/src/concurrent/qtconcurrentthreadengine.cpp +++ b/src/concurrent/qtconcurrentthreadengine.cpp @@ -91,7 +91,7 @@ ThreadEngineBarrier::ThreadEngineBarrier() void ThreadEngineBarrier::acquire() { forever { - int localCount = count.load(); + int localCount = count.loadRelaxed(); if (localCount < 0) { if (count.testAndSetOrdered(localCount, localCount -1)) return; @@ -105,7 +105,7 @@ void ThreadEngineBarrier::acquire() int ThreadEngineBarrier::release() { forever { - int localCount = count.load(); + int localCount = count.loadRelaxed(); if (localCount == -1) { if (count.testAndSetOrdered(-1, 0)) { semaphore.release(); @@ -125,7 +125,7 @@ int ThreadEngineBarrier::release() void ThreadEngineBarrier::wait() { forever { - int localCount = count.load(); + int localCount = count.loadRelaxed(); if (localCount == 0) return; @@ -139,7 +139,7 @@ void ThreadEngineBarrier::wait() int ThreadEngineBarrier::currentCount() { - return count.load(); + return count.loadRelaxed(); } // releases a thread, unless this is the last thread. @@ -147,7 +147,7 @@ int ThreadEngineBarrier::currentCount() bool ThreadEngineBarrier::releaseUnlessLast() { forever { - int localCount = count.load(); + int localCount = count.loadRelaxed(); if (qAbs(localCount) == 1) { return false; } else if (localCount < 0) { diff --git a/src/corelib/animation/qvariantanimation.cpp b/src/corelib/animation/qvariantanimation.cpp index ac81f89ed4..01a699c5dc 100644 --- a/src/corelib/animation/qvariantanimation.cpp +++ b/src/corelib/animation/qvariantanimation.cpp @@ -283,11 +283,11 @@ void QVariantAnimationPrivate::setCurrentValueForProgress(const qreal progress) qSwap(currentValue, ret); q->updateCurrentValue(currentValue); static QBasicAtomicInt changedSignalIndex = Q_BASIC_ATOMIC_INITIALIZER(0); - if (!changedSignalIndex.load()) { + if (!changedSignalIndex.loadRelaxed()) { //we keep the mask so that we emit valueChanged only when needed (for performance reasons) changedSignalIndex.testAndSetRelaxed(0, signalIndex("valueChanged(QVariant)")); } - if (isSignalConnected(changedSignalIndex.load()) && currentValue != ret) { + if (isSignalConnected(changedSignalIndex.loadRelaxed()) && currentValue != ret) { //the value has changed emit q->valueChanged(currentValue); } diff --git a/src/corelib/codecs/qsimplecodec.cpp b/src/corelib/codecs/qsimplecodec.cpp index 9ab545d783..580461321a 100644 --- a/src/corelib/codecs/qsimplecodec.cpp +++ b/src/corelib/codecs/qsimplecodec.cpp @@ -610,7 +610,7 @@ QSimpleTextCodec::QSimpleTextCodec(int i) : forwardIndex(i), reverseMap(0) QSimpleTextCodec::~QSimpleTextCodec() { - delete reverseMap.load(); + delete reverseMap.loadRelaxed(); } static QByteArray *buildReverseMap(int forwardIndex) @@ -662,12 +662,12 @@ QByteArray QSimpleTextCodec::convertFromUnicode(const QChar *in, int length, Con const char replacement = (state && state->flags & ConvertInvalidToNull) ? 0 : '?'; int invalid = 0; - QByteArray *rmap = reverseMap.load(); + QByteArray *rmap = reverseMap.loadRelaxed(); if (!rmap){ rmap = buildReverseMap(this->forwardIndex); if (!reverseMap.testAndSetRelease(0, rmap)) { delete rmap; - rmap = reverseMap.load(); + rmap = reverseMap.loadRelaxed(); } } diff --git a/src/corelib/global/qglobalstatic.h b/src/corelib/global/qglobalstatic.h index 4f89876793..7a7d65ed76 100644 --- a/src/corelib/global/qglobalstatic.h +++ b/src/corelib/global/qglobalstatic.h @@ -80,15 +80,15 @@ enum GuardValues { { \ struct HolderBase { \ ~HolderBase() noexcept \ - { if (guard.load() == QtGlobalStatic::Initialized) \ - guard.store(QtGlobalStatic::Destroyed); } \ + { if (guard.loadRelaxed() == QtGlobalStatic::Initialized) \ + guard.storeRelaxed(QtGlobalStatic::Destroyed); } \ }; \ static struct Holder : public HolderBase { \ Type value; \ Holder() \ noexcept(noexcept(Type ARGS)) \ : value ARGS \ - { guard.store(QtGlobalStatic::Initialized); } \ + { guard.storeRelaxed(QtGlobalStatic::Initialized); } \ } holder; \ return &holder.value; \ } @@ -108,12 +108,12 @@ QT_BEGIN_NAMESPACE int x = guard.loadAcquire(); \ if (Q_UNLIKELY(x >= QtGlobalStatic::Uninitialized)) { \ QMutexLocker locker(&mutex); \ - if (guard.load() == QtGlobalStatic::Uninitialized) { \ + if (guard.loadRelaxed() == QtGlobalStatic::Uninitialized) { \ d = new Type ARGS; \ static struct Cleanup { \ ~Cleanup() { \ delete d; \ - guard.store(QtGlobalStatic::Destroyed); \ + guard.storeRelaxed(QtGlobalStatic::Destroyed); \ } \ } cleanup; \ guard.storeRelease(QtGlobalStatic::Initialized); \ @@ -129,8 +129,8 @@ struct QGlobalStatic { typedef T Type; - bool isDestroyed() const { return guard.load() <= QtGlobalStatic::Destroyed; } - bool exists() const { return guard.load() == QtGlobalStatic::Initialized; } + bool isDestroyed() const { return guard.loadRelaxed() <= QtGlobalStatic::Destroyed; } + bool exists() const { return guard.loadRelaxed() == QtGlobalStatic::Initialized; } operator Type *() { if (isDestroyed()) return nullptr; return innerFunction(); } Type *operator()() { if (isDestroyed()) return nullptr; return innerFunction(); } Type *operator->() diff --git a/src/corelib/global/qlogging.cpp b/src/corelib/global/qlogging.cpp index 8db6ab630a..60ed619aae 100644 --- a/src/corelib/global/qlogging.cpp +++ b/src/corelib/global/qlogging.cpp @@ -197,7 +197,7 @@ static bool isFatal(QtMsgType msgType) // it's fatal if the current value is exactly 1, // otherwise decrement if it's non-zero - return fatalCriticals.load() && fatalCriticals.fetchAndAddRelaxed(-1) == 1; + return fatalCriticals.loadRelaxed() && fatalCriticals.fetchAndAddRelaxed(-1) == 1; } if (msgType == QtWarningMsg || msgType == QtCriticalMsg) { @@ -205,7 +205,7 @@ static bool isFatal(QtMsgType msgType) // it's fatal if the current value is exactly 1, // otherwise decrement if it's non-zero - return fatalWarnings.load() && fatalWarnings.fetchAndAddRelaxed(-1) == 1; + return fatalWarnings.loadRelaxed() && fatalWarnings.fetchAndAddRelaxed(-1) == 1; } return false; @@ -1814,11 +1814,11 @@ static void qt_message_print(QtMsgType msgType, const QMessageLogContext &contex // itself, e.g. by using Qt API if (grabMessageHandler()) { // prefer new message handler over the old one - if (msgHandler.load() == qDefaultMsgHandler - || messageHandler.load() != qDefaultMessageHandler) { - (*messageHandler.load())(msgType, context, message); + if (msgHandler.loadRelaxed() == qDefaultMsgHandler + || messageHandler.loadRelaxed() != qDefaultMessageHandler) { + (*messageHandler.loadRelaxed())(msgType, context, message); } else { - (*msgHandler.load())(msgType, message.toLocal8Bit().constData()); + (*msgHandler.loadRelaxed())(msgType, message.toLocal8Bit().constData()); } ungrabMessageHandler(); } else { diff --git a/src/corelib/global/qrandom.cpp b/src/corelib/global/qrandom.cpp index 2ee8f0dd01..84cf960f2d 100644 --- a/src/corelib/global/qrandom.cpp +++ b/src/corelib/global/qrandom.cpp @@ -186,7 +186,7 @@ struct QRandomGenerator::SystemGenerator #endif static void closeDevice() { - int fd = self().fdp1.load() - 1; + int fd = self().fdp1.loadRelaxed() - 1; if (fd >= 0) qt_safe_close(fd); } @@ -310,7 +310,7 @@ static void fallback_fill(quint32 *ptr, qsizetype left) noexcept *end++ = quint32(nsecs); // 5 #endif - if (quint32 v = seed.load()) + if (quint32 v = seed.loadRelaxed()) *end++ = v; // 6 #if QT_CONFIG(getauxval) diff --git a/src/corelib/io/qloggingcategory.cpp b/src/corelib/io/qloggingcategory.cpp index 91b3396217..f6ff56c83c 100644 --- a/src/corelib/io/qloggingcategory.cpp +++ b/src/corelib/io/qloggingcategory.cpp @@ -239,7 +239,7 @@ QLoggingCategory::QLoggingCategory(const char *category, QtMsgType enableForLeve void QLoggingCategory::init(const char *category, QtMsgType severityLevel) { - enabled.store(0x01010101); // enabledDebug = enabledWarning = enabledCritical = true; + enabled.storeRelaxed(0x01010101); // enabledDebug = enabledWarning = enabledCritical = true; if (category) name = category; @@ -342,10 +342,10 @@ void QLoggingCategory::setEnabled(QtMsgType type, bool enable) { switch (type) { #ifdef Q_ATOMIC_INT8_IS_SUPPORTED - case QtDebugMsg: bools.enabledDebug.store(enable); break; - case QtInfoMsg: bools.enabledInfo.store(enable); break; - case QtWarningMsg: bools.enabledWarning.store(enable); break; - case QtCriticalMsg: bools.enabledCritical.store(enable); break; + case QtDebugMsg: bools.enabledDebug.storeRelaxed(enable); break; + case QtInfoMsg: bools.enabledInfo.storeRelaxed(enable); break; + case QtWarningMsg: bools.enabledWarning.storeRelaxed(enable); break; + case QtCriticalMsg: bools.enabledCritical.storeRelaxed(enable); break; #else case QtDebugMsg: setBoolLane(&enabled, enable, DebugShift); break; case QtInfoMsg: setBoolLane(&enabled, enable, InfoShift); break; diff --git a/src/corelib/io/qloggingcategory.h b/src/corelib/io/qloggingcategory.h index 5825095729..1c3e10b493 100644 --- a/src/corelib/io/qloggingcategory.h +++ b/src/corelib/io/qloggingcategory.h @@ -58,15 +58,15 @@ public: void setEnabled(QtMsgType type, bool enable); #ifdef Q_ATOMIC_INT8_IS_SUPPORTED - bool isDebugEnabled() const { return bools.enabledDebug.load(); } - bool isInfoEnabled() const { return bools.enabledInfo.load(); } - bool isWarningEnabled() const { return bools.enabledWarning.load(); } - bool isCriticalEnabled() const { return bools.enabledCritical.load(); } + bool isDebugEnabled() const { return bools.enabledDebug.loadRelaxed(); } + bool isInfoEnabled() const { return bools.enabledInfo.loadRelaxed(); } + bool isWarningEnabled() const { return bools.enabledWarning.loadRelaxed(); } + bool isCriticalEnabled() const { return bools.enabledCritical.loadRelaxed(); } #else - bool isDebugEnabled() const { return enabled.load() >> DebugShift & 1; } - bool isInfoEnabled() const { return enabled.load() >> InfoShift & 1; } - bool isWarningEnabled() const { return enabled.load() >> WarningShift & 1; } - bool isCriticalEnabled() const { return enabled.load() >> CriticalShift & 1; } + bool isDebugEnabled() const { return enabled.loadRelaxed() >> DebugShift & 1; } + bool isInfoEnabled() const { return enabled.loadRelaxed() >> InfoShift & 1; } + bool isWarningEnabled() const { return enabled.loadRelaxed() >> WarningShift & 1; } + bool isCriticalEnabled() const { return enabled.loadRelaxed() >> CriticalShift & 1; } #endif const char *categoryName() const { return name; } diff --git a/src/corelib/io/qprocess_p.h b/src/corelib/io/qprocess_p.h index 00acb81158..d02e87837c 100644 --- a/src/corelib/io/qprocess_p.h +++ b/src/corelib/io/qprocess_p.h @@ -220,7 +220,7 @@ public: template<> Q_INLINE_TEMPLATE void QSharedDataPointer::detach() { - if (d && d->ref.load() == 1) + if (d && d->ref.loadRelaxed() == 1) return; QProcessEnvironmentPrivate *x = (d ? new QProcessEnvironmentPrivate(*d) : new QProcessEnvironmentPrivate); diff --git a/src/corelib/io/qurl.cpp b/src/corelib/io/qurl.cpp index 9bebfff53f..f82ad48b2c 100644 --- a/src/corelib/io/qurl.cpp +++ b/src/corelib/io/qurl.cpp @@ -3802,7 +3802,7 @@ void QUrl::detach() */ bool QUrl::isDetached() const { - return !d || d->ref.load() == 1; + return !d || d->ref.loadRelaxed() == 1; } diff --git a/src/corelib/io/qurlquery.cpp b/src/corelib/io/qurlquery.cpp index a8f40a3a12..36a2880bf1 100644 --- a/src/corelib/io/qurlquery.cpp +++ b/src/corelib/io/qurlquery.cpp @@ -189,7 +189,7 @@ public: template<> void QSharedDataPointer::detach() { - if (d && d->ref.load() == 1) + if (d && d->ref.loadRelaxed() == 1) return; QUrlQueryPrivate *x = (d ? new QUrlQueryPrivate(*d) : new QUrlQueryPrivate); @@ -462,7 +462,7 @@ bool QUrlQuery::isEmpty() const */ bool QUrlQuery::isDetached() const { - return d && d->ref.load() == 1; + return d && d->ref.loadRelaxed() == 1; } /*! diff --git a/src/corelib/itemmodels/qabstractitemmodel.cpp b/src/corelib/itemmodels/qabstractitemmodel.cpp index d171b313e6..3e74e5a0c8 100644 --- a/src/corelib/itemmodels/qabstractitemmodel.cpp +++ b/src/corelib/itemmodels/qabstractitemmodel.cpp @@ -77,7 +77,7 @@ QPersistentModelIndexData *QPersistentModelIndexData::create(const QModelIndex & void QPersistentModelIndexData::destroy(QPersistentModelIndexData *data) { Q_ASSERT(data); - Q_ASSERT(data->ref.load() == 0); + Q_ASSERT(data->ref.loadRelaxed() == 0); QAbstractItemModel *model = const_cast(data->index.model()); // a valid persistent model index with a null model pointer can only happen if the model was destroyed if (model) { diff --git a/src/corelib/kernel/qabstracteventdispatcher.cpp b/src/corelib/kernel/qabstracteventdispatcher.cpp index 186c2e743b..7215b3f2bd 100644 --- a/src/corelib/kernel/qabstracteventdispatcher.cpp +++ b/src/corelib/kernel/qabstracteventdispatcher.cpp @@ -170,7 +170,7 @@ QAbstractEventDispatcher::~QAbstractEventDispatcher() QAbstractEventDispatcher *QAbstractEventDispatcher::instance(QThread *thread) { QThreadData *data = thread ? QThreadData::get2(thread) : QThreadData::current(); - return data->eventDispatcher.load(); + return data->eventDispatcher.loadRelaxed(); } /*! diff --git a/src/corelib/kernel/qcore_unix_p.h b/src/corelib/kernel/qcore_unix_p.h index 7f58813535..b56c2b9732 100644 --- a/src/corelib/kernel/qcore_unix_p.h +++ b/src/corelib/kernel/qcore_unix_p.h @@ -164,7 +164,7 @@ inline void qt_ignore_sigpipe() { // Set to ignore SIGPIPE once only. static QBasicAtomicInt atom = Q_BASIC_ATOMIC_INITIALIZER(0); - if (!atom.load()) { + if (!atom.loadRelaxed()) { // More than one thread could turn off SIGPIPE at the same time // But that's acceptable because they all would be doing the same // action @@ -172,7 +172,7 @@ inline void qt_ignore_sigpipe() memset(&noaction, 0, sizeof(noaction)); noaction.sa_handler = SIG_IGN; ::sigaction(SIGPIPE, &noaction, nullptr); - atom.store(1); + atom.storeRelaxed(1); } } diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index d62188a49c..2a17642ba7 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -552,8 +552,8 @@ void QCoreApplicationPrivate::eventDispatcherReady() QBasicAtomicPointer QCoreApplicationPrivate::theMainThread = Q_BASIC_ATOMIC_INITIALIZER(0); QThread *QCoreApplicationPrivate::mainThread() { - Q_ASSERT(theMainThread.load() != 0); - return theMainThread.load(); + Q_ASSERT(theMainThread.loadRelaxed() != 0); + return theMainThread.loadRelaxed(); } bool QCoreApplicationPrivate::threadRequiresCoreApplication() @@ -854,7 +854,7 @@ void QCoreApplicationPrivate::init() #ifndef QT_NO_QOBJECT // use the event dispatcher created by the app programmer (if any) Q_ASSERT(!eventDispatcher); - eventDispatcher = threadData->eventDispatcher.load(); + eventDispatcher = threadData->eventDispatcher.loadRelaxed(); // otherwise we create one if (!eventDispatcher) @@ -1302,7 +1302,7 @@ void QCoreApplication::processEvents(QEventLoop::ProcessEventsFlags flags) QThreadData *data = QThreadData::current(); if (!data->hasEventDispatcher()) return; - data->eventDispatcher.load()->processEvents(flags); + data->eventDispatcher.loadRelaxed()->processEvents(flags); } /*! @@ -1334,7 +1334,7 @@ void QCoreApplication::processEvents(QEventLoop::ProcessEventsFlags flags, int m return; QElapsedTimer start; start.start(); - while (data->eventDispatcher.load()->processEvents(flags & ~QEventLoop::WaitForMoreEvents)) { + while (data->eventDispatcher.loadRelaxed()->processEvents(flags & ~QEventLoop::WaitForMoreEvents)) { if (start.elapsed() > ms) break; } @@ -1738,7 +1738,7 @@ void QCoreApplicationPrivate::sendPostedEvents(QObject *receiver, int event_type --data->postEventList.recursion; if (!data->postEventList.recursion && !data->canWait && data->hasEventDispatcher()) - data->eventDispatcher.load()->wakeUp(); + data->eventDispatcher.loadRelaxed()->wakeUp(); // clear the global list, i.e. remove everything that was // delivered. @@ -1989,7 +1989,7 @@ void QCoreApplicationPrivate::deref() void QCoreApplicationPrivate::maybeQuit() { - if (quitLockRef.load() == 0 && in_exec && quitLockRefEnabled && shouldQuit()) + if (quitLockRef.loadRelaxed() == 0 && in_exec && quitLockRefEnabled && shouldQuit()) QCoreApplication::postEvent(QCoreApplication::instance(), new QEvent(QEvent::Quit)); } @@ -2958,7 +2958,7 @@ bool QCoreApplication::hasPendingEvents() QAbstractEventDispatcher *QCoreApplication::eventDispatcher() { if (QCoreApplicationPrivate::theMainThread) - return QCoreApplicationPrivate::theMainThread.load()->eventDispatcher(); + return QCoreApplicationPrivate::theMainThread.loadRelaxed()->eventDispatcher(); return 0; } diff --git a/src/corelib/kernel/qcoreevent.cpp b/src/corelib/kernel/qcoreevent.cpp index aabd32b4a8..4cfc749386 100644 --- a/src/corelib/kernel/qcoreevent.cpp +++ b/src/corelib/kernel/qcoreevent.cpp @@ -424,7 +424,7 @@ struct QBasicAtomicBitField { bool allocateSpecific(int which) noexcept { QBasicAtomicInteger &entry = data[which / BitsPerInt]; - const uint old = entry.load(); + const uint old = entry.loadRelaxed(); const uint bit = 1U << (which % BitsPerInt); return !(old & bit) // wasn't taken && entry.testAndSetRelaxed(old, old | bit); // still wasn't taken @@ -445,10 +445,10 @@ struct QBasicAtomicBitField { // Then again, this should never execute many iterations, so // leave like this for now: - for (uint i = next.load(); i < NumBits; ++i) { + for (uint i = next.loadRelaxed(); i < NumBits; ++i) { if (allocateSpecific(i)) { // remember next (possibly) free id: - const uint oldNext = next.load(); + const uint oldNext = next.loadRelaxed(); next.testAndSetRelaxed(oldNext, qMax(i + 1, oldNext)); return i; } diff --git a/src/corelib/kernel/qeventdispatcher_glib.cpp b/src/corelib/kernel/qeventdispatcher_glib.cpp index 34c2dde6a8..d9746ef6e2 100644 --- a/src/corelib/kernel/qeventdispatcher_glib.cpp +++ b/src/corelib/kernel/qeventdispatcher_glib.cpp @@ -261,7 +261,7 @@ static gboolean postEventSourcePrepare(GSource *s, gint *timeout) *timeout = canWait ? -1 : 0; GPostEventSource *source = reinterpret_cast(s); - source->d->wakeUpCalled = source->serialNumber.load() != source->lastSerialNumber; + source->d->wakeUpCalled = source->serialNumber.loadRelaxed() != source->lastSerialNumber; return !canWait || source->d->wakeUpCalled; } @@ -273,7 +273,7 @@ static gboolean postEventSourceCheck(GSource *source) static gboolean postEventSourceDispatch(GSource *s, GSourceFunc, gpointer) { GPostEventSource *source = reinterpret_cast(s); - source->lastSerialNumber = source->serialNumber.load(); + source->lastSerialNumber = source->serialNumber.loadRelaxed(); QCoreApplication::sendPostedEvents(); source->d->runTimersOnceWithNormalPriority(); return true; // i dunno, george... @@ -320,7 +320,7 @@ QEventDispatcherGlibPrivate::QEventDispatcherGlibPrivate(GMainContext *context) // setup post event source postEventSource = reinterpret_cast(g_source_new(&postEventSourceFuncs, sizeof(GPostEventSource))); - postEventSource->serialNumber.store(1); + postEventSource->serialNumber.storeRelaxed(1); postEventSource->d = this; g_source_set_can_recurse(&postEventSource->source, true); g_source_attach(&postEventSource->source, mainContext); diff --git a/src/corelib/kernel/qeventdispatcher_unix.cpp b/src/corelib/kernel/qeventdispatcher_unix.cpp index df0cac0239..5bc65b7110 100644 --- a/src/corelib/kernel/qeventdispatcher_unix.cpp +++ b/src/corelib/kernel/qeventdispatcher_unix.cpp @@ -459,7 +459,7 @@ void QEventDispatcherUNIX::unregisterSocketNotifier(QSocketNotifier *notifier) bool QEventDispatcherUNIX::processEvents(QEventLoop::ProcessEventsFlags flags) { Q_D(QEventDispatcherUNIX); - d->interrupt.store(0); + d->interrupt.storeRelaxed(0); // we are awake, broadcast it emit awake(); @@ -470,13 +470,13 @@ bool QEventDispatcherUNIX::processEvents(QEventLoop::ProcessEventsFlags flags) const bool wait_for_events = flags & QEventLoop::WaitForMoreEvents; const bool canWait = (d->threadData->canWaitLocked() - && !d->interrupt.load() + && !d->interrupt.loadRelaxed() && wait_for_events); if (canWait) emit aboutToBlock(); - if (d->interrupt.load()) + if (d->interrupt.loadRelaxed()) return false; timespec *tm = nullptr; @@ -545,7 +545,7 @@ void QEventDispatcherUNIX::wakeUp() void QEventDispatcherUNIX::interrupt() { Q_D(QEventDispatcherUNIX); - d->interrupt.store(1); + d->interrupt.storeRelaxed(1); wakeUp(); } diff --git a/src/corelib/kernel/qeventdispatcher_win.cpp b/src/corelib/kernel/qeventdispatcher_win.cpp index e0641a0282..c2e57a7924 100644 --- a/src/corelib/kernel/qeventdispatcher_win.cpp +++ b/src/corelib/kernel/qeventdispatcher_win.cpp @@ -247,7 +247,7 @@ LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPA Q_ASSERT(d != 0); // Allow posting WM_QT_SENDPOSTEDEVENTS message. - d->wakeUps.store(0); + d->wakeUps.storeRelaxed(0); // We send posted events manually, if the window procedure was invoked // by the foreign event loop (e.g. from the native modal dialog). @@ -526,7 +526,7 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags) wakeUp(); // trigger a call to sendPostedEvents() } - d->interrupt.store(false); + d->interrupt.storeRelaxed(false); emit awake(); // To prevent livelocks, send posted events once per iteration. @@ -545,7 +545,7 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags) pHandles = &d->winEventNotifierActivatedEvent; } QVarLengthArray processedTimers; - while (!d->interrupt.load()) { + while (!d->interrupt.loadRelaxed()) { MSG msg; bool haveMessage; @@ -590,7 +590,7 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags) if (d->internalHwnd == msg.hwnd && msg.message == WM_QT_SENDPOSTEDEVENTS) { // Set result to 'true', if the message was sent by wakeUp(). if (msg.wParam == WMWP_QT_FROMWAKEUP) { - d->wakeUps.store(0); + d->wakeUps.storeRelaxed(0); retVal = true; } needWM_QT_SENDPOSTEDEVENTS = true; @@ -639,7 +639,7 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags) // still nothing - wait for message or signalled objects canWait = (!retVal - && !d->interrupt.load() + && !d->interrupt.loadRelaxed() && (flags & QEventLoop::WaitForMoreEvents)); if (canWait) { emit aboutToBlock(); @@ -949,7 +949,7 @@ void QEventDispatcherWin32::activateEventNotifiers() for (int i = 0; i < d->winEventNotifierList.count(); ++i) { QWinEventNotifier *notifier = d->winEventNotifierList.at(i); QWinEventNotifierPrivate *nd = QWinEventNotifierPrivate::get(notifier); - if (nd->signaledCount.load() != 0) { + if (nd->signaledCount.loadRelaxed() != 0) { --nd->signaledCount; nd->unregisterWaitObject(); d->activateEventNotifier(notifier); @@ -1014,7 +1014,7 @@ void QEventDispatcherWin32::wakeUp() void QEventDispatcherWin32::interrupt() { Q_D(QEventDispatcherWin32); - d->interrupt.store(true); + d->interrupt.storeRelaxed(true); wakeUp(); } diff --git a/src/corelib/kernel/qeventloop.cpp b/src/corelib/kernel/qeventloop.cpp index a6cc51621a..2104b22095 100644 --- a/src/corelib/kernel/qeventloop.cpp +++ b/src/corelib/kernel/qeventloop.cpp @@ -135,7 +135,7 @@ bool QEventLoop::processEvents(ProcessEventsFlags flags) Q_D(QEventLoop); if (!d->threadData->hasEventDispatcher()) return false; - return d->threadData->eventDispatcher.load()->processEvents(flags); + return d->threadData->eventDispatcher.loadRelaxed()->processEvents(flags); } /*! @@ -225,7 +225,7 @@ int QEventLoop::exec(ProcessEventsFlags flags) processEvents(flags | WaitForMoreEvents | EventLoopExec); ref.exceptionCaught = false; - return d->returnCode.load(); + return d->returnCode.loadRelaxed(); } /*! @@ -279,9 +279,9 @@ void QEventLoop::exit(int returnCode) if (!d->threadData->hasEventDispatcher()) return; - d->returnCode.store(returnCode); + d->returnCode.storeRelaxed(returnCode); d->exit.storeRelease(true); - d->threadData->eventDispatcher.load()->interrupt(); + d->threadData->eventDispatcher.loadRelaxed()->interrupt(); #ifdef Q_OS_WASM // QEventLoop::exec() never returns in emscripten. We implement approximate behavior here. @@ -318,7 +318,7 @@ void QEventLoop::wakeUp() Q_D(QEventLoop); if (!d->threadData->hasEventDispatcher()) return; - d->threadData->eventDispatcher.load()->wakeUp(); + d->threadData->eventDispatcher.loadRelaxed()->wakeUp(); } diff --git a/src/corelib/kernel/qeventloop_p.h b/src/corelib/kernel/qeventloop_p.h index dcbb5c63c6..4ad6d92007 100644 --- a/src/corelib/kernel/qeventloop_p.h +++ b/src/corelib/kernel/qeventloop_p.h @@ -63,8 +63,8 @@ public: inline QEventLoopPrivate() : inExec(false) { - returnCode.store(-1); - exit.store(true); + returnCode.storeRelaxed(-1); + exit.storeRelaxed(true); } QAtomicInt quitLockRef; diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h index fef25a32c4..9fe2b9733b 100644 --- a/src/corelib/kernel/qmetatype.h +++ b/src/corelib/kernel/qmetatype.h @@ -1999,7 +1999,7 @@ struct QMetaTypeId< SINGLE_ARG_TEMPLATE > \ static int qt_metatype_id() \ { \ static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0); \ - if (const int id = metatype_id.load()) \ + if (const int id = metatype_id.loadRelaxed()) \ return id; \ const char *tName = QMetaType::typeName(qMetaTypeId()); \ Q_ASSERT(tName); \ diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index b4e7568a23..7eba9b05ff 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -222,7 +222,7 @@ QObjectPrivate::~QObjectPrivate() if (Q_LIKELY(threadData->thread == QThread::currentThread())) { // unregister pending timers if (threadData->hasEventDispatcher()) - threadData->eventDispatcher.load()->unregisterTimers(q_ptr); + threadData->eventDispatcher.loadRelaxed()->unregisterTimers(q_ptr); // release the timer ids back to the pool for (int i = 0; i < extraData->runningTimers.size(); ++i) @@ -268,17 +268,17 @@ bool QObjectPrivate::isSender(const QObject *receiver, const char *signal) const { Q_Q(const QObject); int signal_index = signalIndex(signal); - ConnectionData *cd = connections.load(); + ConnectionData *cd = connections.loadRelaxed(); if (signal_index < 0 || !cd) return false; QBasicMutexLocker locker(signalSlotLock(q)); if (signal_index < cd->signalVectorCount()) { - const QObjectPrivate::Connection *c = cd->signalVector.load()->at(signal_index).first.load(); + const QObjectPrivate::Connection *c = cd->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed(); while (c) { - if (c->receiver.load() == receiver) + if (c->receiver.loadRelaxed() == receiver) return true; - c = c->nextConnectionList.load(); + c = c->nextConnectionList.loadRelaxed(); } } return false; @@ -289,17 +289,17 @@ QObjectList QObjectPrivate::receiverList(const char *signal) const { QObjectList returnValue; int signal_index = signalIndex(signal); - ConnectionData *cd = connections.load(); + ConnectionData *cd = connections.loadRelaxed(); if (signal_index < 0 || !cd) return returnValue; if (signal_index < cd->signalVectorCount()) { - const QObjectPrivate::Connection *c = cd->signalVector.load()->at(signal_index).first.load(); + const QObjectPrivate::Connection *c = cd->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed(); while (c) { - QObject *r = c->receiver.load(); + QObject *r = c->receiver.loadRelaxed(); if (r) returnValue << r; - c = c->nextConnectionList.load(); + c = c->nextConnectionList.loadRelaxed(); } } return returnValue; @@ -309,7 +309,7 @@ QObjectList QObjectPrivate::receiverList(const char *signal) const QObjectList QObjectPrivate::senderList() const { QObjectList returnValue; - ConnectionData *cd = connections.load(); + ConnectionData *cd = connections.loadRelaxed(); if (cd) { QBasicMutexLocker locker(signalSlotLock(q_func())); for (Connection *c = cd->senders; c; c = c->next) @@ -332,24 +332,24 @@ void QObjectPrivate::addConnection(int signal, Connection *c) { Q_ASSERT(c->sender == q_ptr); ensureConnectionData(); - ConnectionData *cd = connections.load(); + ConnectionData *cd = connections.loadRelaxed(); cd->resizeSignalVector(signal + 1); ConnectionList &connectionList = cd->connectionsForSignal(signal); - if (connectionList.last.load()) { - Q_ASSERT(connectionList.last.load()->receiver.load()); - connectionList.last.load()->nextConnectionList.store(c); + if (connectionList.last.loadRelaxed()) { + Q_ASSERT(connectionList.last.loadRelaxed()->receiver.loadRelaxed()); + connectionList.last.loadRelaxed()->nextConnectionList.storeRelaxed(c); } else { - connectionList.first.store(c); + connectionList.first.storeRelaxed(c); } c->id = ++cd->currentConnectionId; - c->prevConnectionList = connectionList.last.load(); - connectionList.last.store(c); + c->prevConnectionList = connectionList.last.loadRelaxed(); + connectionList.last.storeRelaxed(c); - QObjectPrivate *rd = QObjectPrivate::get(c->receiver.load()); + QObjectPrivate *rd = QObjectPrivate::get(c->receiver.loadRelaxed()); rd->ensureConnectionData(); - c->prev = &(rd->connections.load()->senders); + c->prev = &(rd->connections.loadRelaxed()->senders); c->next = *c->prev; *c->prev = c; if (c->next) @@ -358,17 +358,17 @@ void QObjectPrivate::addConnection(int signal, Connection *c) void QObjectPrivate::ConnectionData::removeConnection(QObjectPrivate::Connection *c) { - Q_ASSERT(c->receiver.load()); - ConnectionList &connections = signalVector.load()->at(c->signal_index); - c->receiver.store(nullptr); - QThreadData *td = c->receiverThreadData.load(); + Q_ASSERT(c->receiver.loadRelaxed()); + ConnectionList &connections = signalVector.loadRelaxed()->at(c->signal_index); + c->receiver.storeRelaxed(nullptr); + QThreadData *td = c->receiverThreadData.loadRelaxed(); if (td) td->deref(); - c->receiverThreadData.store(nullptr); + c->receiverThreadData.storeRelaxed(nullptr); #ifndef QT_NO_DEBUG bool found = false; - for (Connection *cc = connections.first.load(); cc; cc = cc->nextConnectionList.load()) { + for (Connection *cc = connections.first.loadRelaxed(); cc; cc = cc->nextConnectionList.loadRelaxed()) { if (cc == c) { found = true; break; @@ -383,29 +383,29 @@ void QObjectPrivate::ConnectionData::removeConnection(QObjectPrivate::Connection c->next->prev = c->prev; c->prev = nullptr; - if (connections.first.load() == c) - connections.first.store(c->nextConnectionList.load()); - if (connections.last.load() == c) - connections.last.store(c->prevConnectionList); - Q_ASSERT(signalVector.load()->at(c->signal_index).first.load() != c); - Q_ASSERT(signalVector.load()->at(c->signal_index).last.load() != c); + if (connections.first.loadRelaxed() == c) + connections.first.storeRelaxed(c->nextConnectionList.loadRelaxed()); + if (connections.last.loadRelaxed() == c) + connections.last.storeRelaxed(c->prevConnectionList); + Q_ASSERT(signalVector.loadRelaxed()->at(c->signal_index).first.loadRelaxed() != c); + Q_ASSERT(signalVector.loadRelaxed()->at(c->signal_index).last.loadRelaxed() != c); // keep c->nextConnectionList intact, as it might still get accessed by activate - Connection *n = c->nextConnectionList.load(); + Connection *n = c->nextConnectionList.loadRelaxed(); if (n) n->prevConnectionList = c->prevConnectionList; if (c->prevConnectionList) - c->prevConnectionList->nextConnectionList.store(n); + c->prevConnectionList->nextConnectionList.storeRelaxed(n); c->prevConnectionList = nullptr; - Q_ASSERT(c != orphaned.load()); + Q_ASSERT(c != orphaned.loadRelaxed()); // add c to orphanedConnections - c->nextInOrphanList = orphaned.load(); - orphaned.store(c); + c->nextInOrphanList = orphaned.loadRelaxed(); + orphaned.storeRelaxed(c); #ifndef QT_NO_DEBUG found = false; - for (Connection *cc = connections.first.load(); cc; cc = cc->nextConnectionList.load()) { + for (Connection *cc = connections.first.loadRelaxed(); cc; cc = cc->nextConnectionList.loadRelaxed()) { if (cc == c) { found = true; break; @@ -427,8 +427,8 @@ void QObjectPrivate::ConnectionData::cleanOrphanedConnectionsImpl(QObject *sende // Since ref == 1, no activate() is in process since we locked the mutex. That implies, // that nothing can reference the orphaned connection objects anymore and they can // be safely deleted - c = orphaned.load(); - orphaned.store(nullptr); + c = orphaned.loadRelaxed(); + orphaned.storeRelaxed(nullptr); } deleteOrphaned(c); } @@ -443,7 +443,7 @@ void QObjectPrivate::ConnectionData::deleteOrphaned(QObjectPrivate::ConnectionOr } else { QObjectPrivate::Connection *c = static_cast(o); next = c->nextInOrphanList; - Q_ASSERT(!c->receiver.load()); + Q_ASSERT(!c->receiver.loadRelaxed()); Q_ASSERT(!c->prev); c->freeSlotObject(); c->deref(); @@ -463,22 +463,22 @@ bool QObjectPrivate::isSignalConnected(uint signalIndex, bool checkDeclarative) if (checkDeclarative && isDeclarativeSignalConnected(signalIndex)) return true; - ConnectionData *cd = connections.load(); + ConnectionData *cd = connections.loadRelaxed(); if (!cd) return false; - SignalVector *signalVector = cd->signalVector.load(); + SignalVector *signalVector = cd->signalVector.loadRelaxed(); if (!signalVector) return false; - if (signalVector->at(-1).first.load()) + if (signalVector->at(-1).first.loadRelaxed()) return true; if (signalIndex < uint(cd->signalVectorCount())) { - const QObjectPrivate::Connection *c = signalVector->at(signalIndex).first.load(); + const QObjectPrivate::Connection *c = signalVector->at(signalIndex).first.loadRelaxed(); while (c) { - if (c->receiver.load()) + if (c->receiver.loadRelaxed()) return true; - c = c->nextConnectionList.load(); + c = c->nextConnectionList.loadRelaxed(); } } return false; @@ -486,10 +486,10 @@ bool QObjectPrivate::isSignalConnected(uint signalIndex, bool checkDeclarative) bool QObjectPrivate::maybeSignalConnected(uint signalIndex) const { - ConnectionData *cd = connections.load(); + ConnectionData *cd = connections.loadRelaxed(); if (!cd) return false; - SignalVector *signalVector = cd->signalVector.load(); + SignalVector *signalVector = cd->signalVector.loadRelaxed(); if (!signalVector) return false; @@ -944,15 +944,15 @@ QObject::~QObject() d->wasDeleted = true; d->blockSig = 0; // unblock signals so we always emit destroyed() - QtSharedPointer::ExternalRefCountData *sharedRefcount = d->sharedRefcount.load(); + QtSharedPointer::ExternalRefCountData *sharedRefcount = d->sharedRefcount.loadRelaxed(); if (sharedRefcount) { - if (sharedRefcount->strongref.load() > 0) { + if (sharedRefcount->strongref.loadRelaxed() > 0) { qWarning("QObject: shared QObject was deleted directly. The program is malformed and may crash."); // but continue deleting, it's too late to stop anyway } // indicate to all QWeakPointers that this QObject has now been deleted - sharedRefcount->strongref.store(0); + sharedRefcount->strongref.storeRelaxed(0); if (!sharedRefcount->weakref.deref()) delete sharedRefcount; } @@ -971,7 +971,7 @@ QObject::~QObject() } } - QObjectPrivate::ConnectionData *cd = d->connections.load(); + QObjectPrivate::ConnectionData *cd = d->connections.loadRelaxed(); if (cd) { if (cd->currentSender) { cd->currentSender->receiverDeleted(); @@ -986,14 +986,14 @@ QObject::~QObject() for (int signal = -1; signal < receiverCount; ++signal) { QObjectPrivate::ConnectionList &connectionList = cd->connectionsForSignal(signal); - while (QObjectPrivate::Connection *c = connectionList.first.load()) { + while (QObjectPrivate::Connection *c = connectionList.first.loadRelaxed()) { Q_ASSERT(c->receiver); - QBasicMutex *m = signalSlotLock(c->receiver.load()); + QBasicMutex *m = signalSlotLock(c->receiver.loadRelaxed()); bool needToUnlock = QOrderedMutexLocker::relock(signalSlotMutex, m); if (c->receiver) { cd->removeConnection(c); - Q_ASSERT(connectionList.first.load() != c); + Q_ASSERT(connectionList.first.loadRelaxed() != c); } if (needToUnlock) m->unlock(); @@ -1019,7 +1019,7 @@ QObject::~QObject() continue; } - QObjectPrivate::ConnectionData *senderData = sender->d_func()->connections.load(); + QObjectPrivate::ConnectionData *senderData = sender->d_func()->connections.loadRelaxed(); Q_ASSERT(senderData); QtPrivate::QSlotObjectBase *slotObj = nullptr; @@ -1041,11 +1041,11 @@ QObject::~QObject() // invalidate all connections on the object and make sure // activate() will skip them - cd->currentConnectionId.store(0); + cd->currentConnectionId.storeRelaxed(0); } if (cd && !cd->ref.deref()) delete cd; - d->connections.store(nullptr); + d->connections.storeRelaxed(nullptr); if (!d->children.isEmpty()) d->deleteChildren(); @@ -1065,7 +1065,7 @@ QObject::~QObject() QObjectPrivate::Connection::~Connection() { if (ownArgumentTypes) { - const int *v = argumentTypes.load(); + const int *v = argumentTypes.loadRelaxed(); if (v != &DIRECT_CONNECTION_ONLY) delete [] v; } @@ -1274,7 +1274,7 @@ bool QObject::event(QEvent *e) { QAbstractMetaCallEvent *mce = static_cast(e); - if (!d_func()->connections.load()) { + if (!d_func()->connections.loadRelaxed()) { QBasicMutexLocker locker(signalSlotLock(this)); d_func()->ensureConnectionData(); } @@ -1287,7 +1287,7 @@ bool QObject::event(QEvent *e) case QEvent::ThreadChange: { Q_D(QObject); QThreadData *threadData = d->threadData; - QAbstractEventDispatcher *eventDispatcher = threadData->eventDispatcher.load(); + QAbstractEventDispatcher *eventDispatcher = threadData->eventDispatcher.loadRelaxed(); if (eventDispatcher) { QList timers = eventDispatcher->registeredTimers(this); if (!timers.isEmpty()) { @@ -1522,7 +1522,7 @@ void QObject::moveToThread(QThread *targetThread) } else if (d->threadData != currentData) { qWarning("QObject::moveToThread: Current thread (%p) is not the object's thread (%p).\n" "Cannot move to target thread (%p)\n", - currentData->thread.load(), d->threadData->thread.load(), targetData ? targetData->thread.load() : nullptr); + currentData->thread.loadRelaxed(), d->threadData->thread.loadRelaxed(), targetData ? targetData->thread.loadRelaxed() : nullptr); #ifdef Q_OS_MAC qWarning("You might be loading two sets of Qt binaries into the same process. " @@ -1587,11 +1587,11 @@ void QObjectPrivate::setThreadData_helper(QThreadData *currentData, QThreadData } if (eventsMoved > 0 && targetData->hasEventDispatcher()) { targetData->canWait = false; - targetData->eventDispatcher.load()->wakeUp(); + targetData->eventDispatcher.loadRelaxed()->wakeUp(); } // the current emitting thread shouldn't restore currentSender after calling moveToThread() - ConnectionData *cd = connections.load(); + ConnectionData *cd = connections.loadRelaxed(); if (cd) { if (cd->currentSender) { cd->currentSender->receiverDeleted(); @@ -1602,14 +1602,14 @@ void QObjectPrivate::setThreadData_helper(QThreadData *currentData, QThreadData if (cd) { auto *c = cd->senders; while (c) { - QObject *r = c->receiver.load(); + QObject *r = c->receiver.loadRelaxed(); if (r) { Q_ASSERT(r == q); targetData->ref(); - QThreadData *old = c->receiverThreadData.load(); + QThreadData *old = c->receiverThreadData.loadRelaxed(); if (old) old->deref(); - c->receiverThreadData.store(targetData); + c->receiverThreadData.storeRelaxed(targetData); } c = c->next; } @@ -1632,7 +1632,7 @@ void QObjectPrivate::_q_reregisterTimers(void *pointer) { Q_Q(QObject); QList *timerList = reinterpret_cast *>(pointer); - QAbstractEventDispatcher *eventDispatcher = threadData->eventDispatcher.load(); + QAbstractEventDispatcher *eventDispatcher = threadData->eventDispatcher.loadRelaxed(); for (int i = 0; i < timerList->size(); ++i) { const QAbstractEventDispatcher::TimerInfo &ti = timerList->at(i); eventDispatcher->registerTimer(ti.timerId, ti.interval, ti.timerType, q); @@ -1698,7 +1698,7 @@ int QObject::startTimer(int interval, Qt::TimerType timerType) qWarning("QObject::startTimer: Timers cannot be started from another thread"); return 0; } - int timerId = d->threadData->eventDispatcher.load()->registerTimer(interval, timerType, this); + int timerId = d->threadData->eventDispatcher.loadRelaxed()->registerTimer(interval, timerType, this); if (!d->extraData) d->extraData = new QObjectPrivate::ExtraData; d->extraData->runningTimers.append(timerId); @@ -1773,7 +1773,7 @@ void QObject::killTimer(int id) } if (d->threadData->hasEventDispatcher()) - d->threadData->eventDispatcher.load()->unregisterTimer(id); + d->threadData->eventDispatcher.loadRelaxed()->unregisterTimer(id); d->extraData->runningTimers.remove(at); QAbstractEventDispatcherPrivate::releaseTimerId(id); @@ -2442,7 +2442,7 @@ QObject *QObject::sender() const Q_D(const QObject); QBasicMutexLocker locker(signalSlotLock(this)); - QObjectPrivate::ConnectionData *cd = d->connections.load(); + QObjectPrivate::ConnectionData *cd = d->connections.loadRelaxed(); if (!cd || !cd->currentSender) return nullptr; @@ -2484,7 +2484,7 @@ int QObject::senderSignalIndex() const Q_D(const QObject); QBasicMutexLocker locker(signalSlotLock(this)); - QObjectPrivate::ConnectionData *cd = d->connections.load(); + QObjectPrivate::ConnectionData *cd = d->connections.loadRelaxed(); if (!cd || !cd->currentSender) return -1; @@ -2547,13 +2547,13 @@ int QObject::receivers(const char *signal) const signal_index); } - QObjectPrivate::ConnectionData *cd = d->connections.load(); + QObjectPrivate::ConnectionData *cd = d->connections.loadRelaxed(); QBasicMutexLocker locker(signalSlotLock(this)); if (cd && signal_index < cd->signalVectorCount()) { - const QObjectPrivate::Connection *c = cd->signalVector.load()->at(signal_index).first.load(); + const QObjectPrivate::Connection *c = cd->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed(); while (c) { - receivers += c->receiver.load() ? 1 : 0; - c = c->nextConnectionList.load(); + receivers += c->receiver.loadRelaxed() ? 1 : 0; + c = c->nextConnectionList.loadRelaxed(); } } } @@ -3362,17 +3362,17 @@ QObjectPrivate::Connection *QMetaObjectPrivate::connect(const QObject *sender, QOrderedMutexLocker locker(signalSlotLock(sender), signalSlotLock(receiver)); - QObjectPrivate::ConnectionData *scd = QObjectPrivate::get(s)->connections.load(); + QObjectPrivate::ConnectionData *scd = QObjectPrivate::get(s)->connections.loadRelaxed(); if (type & Qt::UniqueConnection && scd) { if (scd->signalVectorCount() > signal_index) { - const QObjectPrivate::Connection *c2 = scd->signalVector.load()->at(signal_index).first.load(); + const QObjectPrivate::Connection *c2 = scd->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed(); int method_index_absolute = method_index + method_offset; while (c2) { - if (!c2->isSlotObject && c2->receiver.load() == receiver && c2->method() == method_index_absolute) + if (!c2->isSlotObject && c2->receiver.loadRelaxed() == receiver && c2->method() == method_index_absolute) return nullptr; - c2 = c2->nextConnectionList.load(); + c2 = c2->nextConnectionList.loadRelaxed(); } } type &= Qt::UniqueConnection - 1; @@ -3381,15 +3381,15 @@ QObjectPrivate::Connection *QMetaObjectPrivate::connect(const QObject *sender, QScopedPointer c(new QObjectPrivate::Connection); c->sender = s; c->signal_index = signal_index; - c->receiver.store(r); + c->receiver.storeRelaxed(r); QThreadData *td = r->d_func()->threadData; td->ref(); - c->receiverThreadData.store(td); + c->receiverThreadData.storeRelaxed(td); c->method_relative = method_index; c->method_offset = method_offset; c->connectionType = type; c->isSlotObject = false; - c->argumentTypes.store(types); + c->argumentTypes.storeRelaxed(types); c->callFunction = callFunction; QObjectPrivate::get(s)->addConnection(signal_index, c.data()); @@ -3442,9 +3442,9 @@ bool QMetaObjectPrivate::disconnectHelper(QObjectPrivate::ConnectionData *connec bool success = false; auto &connectionList = connections->connectionsForSignal(signalIndex); - auto *c = connectionList.first.load(); + auto *c = connectionList.first.loadRelaxed(); while (c) { - QObject *r = c->receiver.load(); + QObject *r = c->receiver.loadRelaxed(); if (r && (receiver == nullptr || (r == receiver && (method_index < 0 || (!c->isSlotObject && c->method() == method_index)) && (slot == nullptr || (c->isSlotObject && c->slotObj->compare(slot)))))) { @@ -3455,7 +3455,7 @@ bool QMetaObjectPrivate::disconnectHelper(QObjectPrivate::ConnectionData *connec // need to relock this receiver and sender in the correct order needToUnlock = QOrderedMutexLocker::relock(senderMutex, receiverMutex); } - if (c->receiver.load()) + if (c->receiver.loadRelaxed()) connections->removeConnection(c); if (needToUnlock) @@ -3466,7 +3466,7 @@ bool QMetaObjectPrivate::disconnectHelper(QObjectPrivate::ConnectionData *connec if (disconnectType == DisconnectOne) return success; } - c = c->nextConnectionList.load(); + c = c->nextConnectionList.loadRelaxed(); } return success; } @@ -3488,7 +3488,7 @@ bool QMetaObjectPrivate::disconnect(const QObject *sender, QBasicMutex *senderMutex = signalSlotLock(sender); QBasicMutexLocker locker(senderMutex); - QObjectPrivate::ConnectionData *scd = QObjectPrivate::get(s)->connections.load(); + QObjectPrivate::ConnectionData *scd = QObjectPrivate::get(s)->connections.loadRelaxed(); if (!scd) return false; @@ -3630,7 +3630,7 @@ void QMetaObject::connectSlotsByName(QObject *o) */ static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connection *c, void **argv) { - const int *argumentTypes = c->argumentTypes.load(); + const int *argumentTypes = c->argumentTypes.loadRelaxed(); if (!argumentTypes) { QMetaMethod m = QMetaObjectPrivate::signal(sender->metaObject(), signal); argumentTypes = queuedConnectionTypes(m.parameterTypes()); @@ -3639,7 +3639,7 @@ static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connect if (!c->argumentTypes.testAndSetOrdered(0, argumentTypes)) { if (argumentTypes != &DIRECT_CONNECTION_ONLY) delete [] argumentTypes; - argumentTypes = c->argumentTypes.load(); + argumentTypes = c->argumentTypes.loadRelaxed(); } } if (argumentTypes == &DIRECT_CONNECTION_ONLY) // cannot activate @@ -3662,8 +3662,8 @@ static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connect args[n] = QMetaType::create(types[n], argv[n]); } - QBasicMutexLocker locker(signalSlotLock(c->receiver.load())); - if (!c->receiver.load()) { + QBasicMutexLocker locker(signalSlotLock(c->receiver.loadRelaxed())); + if (!c->receiver.loadRelaxed()) { // the connection has been disconnected before we got the lock locker.unlock(); for (int n = 1; n < nargs; ++n) @@ -3676,7 +3676,7 @@ static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connect QMetaCallEvent *ev = c->isSlotObject ? new QMetaCallEvent(c->slotObj, sender, signal, nargs, types, args) : new QMetaCallEvent(c->method_offset, c->method_relative, c->callFunction, sender, signal, nargs, types, args); - QCoreApplication::postEvent(c->receiver.load(), ev); + QCoreApplication::postEvent(c->receiver.loadRelaxed(), ev); } template @@ -3717,8 +3717,8 @@ void doActivate(QObject *sender, int signal_index, void **argv) bool senderDeleted = false; { Q_ASSERT(sp->connections); - QObjectPrivate::ConnectionDataPointer connections(sp->connections.load()); - QObjectPrivate::SignalVector *signalVector = connections->signalVector.load(); + QObjectPrivate::ConnectionDataPointer connections(sp->connections.loadRelaxed()); + QObjectPrivate::SignalVector *signalVector = connections->signalVector.loadRelaxed(); const QObjectPrivate::ConnectionList *list; if (signal_index < signalVector->count()) @@ -3727,32 +3727,32 @@ void doActivate(QObject *sender, int signal_index, void **argv) list = &signalVector->at(-1); Qt::HANDLE currentThreadId = QThread::currentThreadId(); - bool inSenderThread = currentThreadId == QObjectPrivate::get(sender)->threadData->threadId.load(); + bool inSenderThread = currentThreadId == QObjectPrivate::get(sender)->threadData->threadId.loadRelaxed(); // We need to check against the highest connection id to ensure that signals added // during the signal emission are not emitted in this emission. - uint highestConnectionId = connections->currentConnectionId.load(); + uint highestConnectionId = connections->currentConnectionId.loadRelaxed(); do { - QObjectPrivate::Connection *c = list->first.load(); + QObjectPrivate::Connection *c = list->first.loadRelaxed(); if (!c) continue; do { - QObject * const receiver = c->receiver.load(); + QObject * const receiver = c->receiver.loadRelaxed(); if (!receiver) continue; - QThreadData *td = c->receiverThreadData.load(); + QThreadData *td = c->receiverThreadData.loadRelaxed(); if (!td) continue; bool receiverInSameThread; if (inSenderThread) { - receiverInSameThread = currentThreadId == td->threadId.load(); + receiverInSameThread = currentThreadId == td->threadId.loadRelaxed(); } else { // need to lock before reading the threadId, because moveToThread() could interfere QMutexLocker lock(signalSlotLock(receiver)); - receiverInSameThread = currentThreadId == td->threadId.load(); + receiverInSameThread = currentThreadId == td->threadId.loadRelaxed(); } @@ -3825,17 +3825,17 @@ void doActivate(QObject *sender, int signal_index, void **argv) if (callbacks_enabled && signal_spy_set->slot_end_callback != nullptr) signal_spy_set->slot_end_callback(receiver, method); } - } while ((c = c->nextConnectionList.load()) != nullptr && c->id <= highestConnectionId); + } while ((c = c->nextConnectionList.loadRelaxed()) != nullptr && c->id <= highestConnectionId); } while (list != &signalVector->at(-1) && //start over for all signals; ((list = &signalVector->at(-1)), true)); - if (connections->currentConnectionId.load() == 0) + if (connections->currentConnectionId.loadRelaxed() == 0) senderDeleted = true; } if (!senderDeleted) - sp->connections.load()->cleanOrphanedConnections(sender); + sp->connections.loadRelaxed()->cleanOrphanedConnections(sender); if (callbacks_enabled && signal_spy_set->signal_end_callback != nullptr) signal_spy_set->signal_end_callback(sender, signal_index); @@ -3849,7 +3849,7 @@ void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_sign { int signal_index = local_signal_index + QMetaObjectPrivate::signalOffset(m); - if (Q_UNLIKELY(qt_signal_spy_callback_set.load())) + if (Q_UNLIKELY(qt_signal_spy_callback_set.loadRelaxed())) doActivate(sender, signal_index, argv); else doActivate(sender, signal_index, argv); @@ -3862,7 +3862,7 @@ void QMetaObject::activate(QObject *sender, int signalOffset, int local_signal_i { int signal_index = signalOffset + local_signal_index; - if (Q_UNLIKELY(qt_signal_spy_callback_set.load())) + if (Q_UNLIKELY(qt_signal_spy_callback_set.loadRelaxed())) doActivate(sender, signal_index, argv); else doActivate(sender, signal_index, argv); @@ -4131,11 +4131,11 @@ void QObject::dumpObjectInfo() const // first, look for connections where this object is the sender qDebug(" SIGNALS OUT"); - QObjectPrivate::ConnectionData *cd = d->connections.load(); + QObjectPrivate::ConnectionData *cd = d->connections.loadRelaxed(); if (cd && cd->signalVectorCount()) { - QObjectPrivate::SignalVector *signalVector = cd->signalVector.load(); + QObjectPrivate::SignalVector *signalVector = cd->signalVector.loadRelaxed(); for (int signal_index = 0; signal_index < signalVector->count(); ++signal_index) { - const QObjectPrivate::Connection *c = signalVector->at(signal_index).first.load(); + const QObjectPrivate::Connection *c = signalVector->at(signal_index).first.loadRelaxed(); if (!c) continue; const QMetaMethod signal = QMetaObjectPrivate::signal(metaObject(), signal_index); @@ -4143,23 +4143,23 @@ void QObject::dumpObjectInfo() const // receivers while (c) { - if (!c->receiver.load()) { + if (!c->receiver.loadRelaxed()) { qDebug(" "); - c = c->nextConnectionList.load(); + c = c->nextConnectionList.loadRelaxed(); continue; } if (c->isSlotObject) { qDebug(" "); - c = c->nextConnectionList.load(); + c = c->nextConnectionList.loadRelaxed(); continue; } - const QMetaObject *receiverMetaObject = c->receiver.load()->metaObject(); + const QMetaObject *receiverMetaObject = c->receiver.loadRelaxed()->metaObject(); const QMetaMethod method = receiverMetaObject->method(c->method()); qDebug(" --> %s::%s %s", receiverMetaObject->className(), - c->receiver.load()->objectName().isEmpty() ? "unnamed" : qPrintable(c->receiver.load()->objectName()), + c->receiver.loadRelaxed()->objectName().isEmpty() ? "unnamed" : qPrintable(c->receiver.loadRelaxed()->objectName()), method.methodSignature().constData()); - c = c->nextConnectionList.load(); + c = c->nextConnectionList.loadRelaxed(); } } } else { @@ -4910,17 +4910,17 @@ QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int s QOrderedMutexLocker locker(signalSlotLock(sender), signalSlotLock(receiver)); - if (type & Qt::UniqueConnection && slot && QObjectPrivate::get(s)->connections.load()) { - QObjectPrivate::ConnectionData *connections = QObjectPrivate::get(s)->connections.load(); + if (type & Qt::UniqueConnection && slot && QObjectPrivate::get(s)->connections.loadRelaxed()) { + QObjectPrivate::ConnectionData *connections = QObjectPrivate::get(s)->connections.loadRelaxed(); if (connections->signalVectorCount() > signal_index) { - const QObjectPrivate::Connection *c2 = connections->signalVector.load()->at(signal_index).first.load(); + const QObjectPrivate::Connection *c2 = connections->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed(); while (c2) { - if (c2->receiver.load() == receiver && c2->isSlotObject && c2->slotObj->compare(slot)) { + if (c2->receiver.loadRelaxed() == receiver && c2->isSlotObject && c2->slotObj->compare(slot)) { slotObj->destroyIfLastRef(); return QMetaObject::Connection(); } - c2 = c2->nextConnectionList.load(); + c2 = c2->nextConnectionList.loadRelaxed(); } } type = static_cast(type ^ Qt::UniqueConnection); @@ -4931,13 +4931,13 @@ QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int s c->signal_index = signal_index; QThreadData *td = r->d_func()->threadData; td->ref(); - c->receiverThreadData.store(td); - c->receiver.store(r); + c->receiverThreadData.storeRelaxed(td); + c->receiver.storeRelaxed(r); c->slotObj = slotObj; c->connectionType = type; c->isSlotObject = true; if (types) { - c->argumentTypes.store(types); + c->argumentTypes.storeRelaxed(types); c->ownArgumentTypes = false; } @@ -4966,7 +4966,7 @@ bool QObject::disconnect(const QMetaObject::Connection &connection) if (!c) return false; - QObject *receiver = c->receiver.load(); + QObject *receiver = c->receiver.loadRelaxed(); if (!receiver) return false; @@ -4978,11 +4978,11 @@ bool QObject::disconnect(const QMetaObject::Connection &connection) QOrderedMutexLocker locker(senderMutex, receiverMutex); // load receiver once again and recheck to ensure nobody else has removed the connection in the meantime - receiver = c->receiver.load(); + receiver = c->receiver.loadRelaxed(); if (!receiver) return false; - connections = QObjectPrivate::get(c->sender)->connections.load(); + connections = QObjectPrivate::get(c->sender)->connections.loadRelaxed(); Q_ASSERT(connections); connections->removeConnection(c); } @@ -5174,7 +5174,7 @@ bool QMetaObject::Connection::isConnected_helper() const Q_ASSERT(d_ptr); // we're only called from operator RestrictedBool() const QObjectPrivate::Connection *c = static_cast(d_ptr); - return c->receiver.load(); + return c->receiver.loadRelaxed(); } diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h index e6e57b29b9..1953aea21e 100644 --- a/src/corelib/kernel/qobject_p.h +++ b/src/corelib/kernel/qobject_p.h @@ -184,7 +184,7 @@ public: } void deref() { if (!ref_.deref()) { - Q_ASSERT(!receiver.load()); + Q_ASSERT(!receiver.loadRelaxed()); Q_ASSERT(!isSlotObject); delete this; } @@ -202,7 +202,7 @@ public: : receiver(receiver), sender(sender), signal(signal) { if (receiver) { - ConnectionData *cd = receiver->d_func()->connections.load(); + ConnectionData *cd = receiver->d_func()->connections.loadRelaxed(); previous = cd->currentSender; cd->currentSender = this; } @@ -210,7 +210,7 @@ public: ~Sender() { if (receiver) - receiver->d_func()->connections.load()->currentSender = previous; + receiver->d_func()->connections.loadRelaxed()->currentSender = previous; } void receiverDeleted() { @@ -268,8 +268,8 @@ public: ~ConnectionData() { - deleteOrphaned(orphaned.load()); - SignalVector *v = signalVector.load(); + deleteOrphaned(orphaned.loadRelaxed()); + SignalVector *v = signalVector.loadRelaxed(); if (v) free(v); } @@ -279,18 +279,18 @@ public: void removeConnection(Connection *c); void cleanOrphanedConnections(QObject *sender) { - if (orphaned.load() && ref == 1) + if (orphaned.loadRelaxed() && ref == 1) cleanOrphanedConnectionsImpl(sender); } void cleanOrphanedConnectionsImpl(QObject *sender); ConnectionList &connectionsForSignal(int signal) { - return signalVector.load()->at(signal); + return signalVector.loadRelaxed()->at(signal); } void resizeSignalVector(uint size) { - SignalVector *vector = this->signalVector.load(); + SignalVector *vector = this->signalVector.loadRelaxed(); if (vector && vector->allocated > size) return; size = (size + 7) & ~7; @@ -305,14 +305,14 @@ public: newVector->next = nullptr; newVector->allocated = size; - signalVector.store(newVector); + signalVector.storeRelaxed(newVector); if (vector) { - vector->nextInOrphanList = orphaned.load(); - orphaned.store(ConnectionOrSignalVector::fromSignalVector(vector)); + vector->nextInOrphanList = orphaned.loadRelaxed(); + orphaned.storeRelaxed(ConnectionOrSignalVector::fromSignalVector(vector)); } } int signalVectorCount() const { - return signalVector ? signalVector.load()->count() : -1; + return signalVector ? signalVector.loadRelaxed()->count() : -1; } static void deleteOrphaned(ConnectionOrSignalVector *c); @@ -366,11 +366,11 @@ public: void ensureConnectionData() { - if (connections.load()) + if (connections.loadRelaxed()) return; ConnectionData *cd = new ConnectionData; cd->ref.ref(); - connections.store(cd); + connections.storeRelaxed(cd); } public: ExtraData *extraData; // extra data set by the user diff --git a/src/corelib/kernel/qsocketnotifier.cpp b/src/corelib/kernel/qsocketnotifier.cpp index 6ff8268978..2a246b1204 100644 --- a/src/corelib/kernel/qsocketnotifier.cpp +++ b/src/corelib/kernel/qsocketnotifier.cpp @@ -152,7 +152,7 @@ QSocketNotifier::QSocketNotifier(qintptr socket, Type type, QObject *parent) else if (!d->threadData->hasEventDispatcher()) qWarning("QSocketNotifier: Can only be used with threads started with QThread"); else - d->threadData->eventDispatcher.load()->registerSocketNotifier(this); + d->threadData->eventDispatcher.loadRelaxed()->registerSocketNotifier(this); } /*! @@ -241,9 +241,9 @@ void QSocketNotifier::setEnabled(bool enable) return; } if (d->snenabled) - d->threadData->eventDispatcher.load()->registerSocketNotifier(this); + d->threadData->eventDispatcher.loadRelaxed()->registerSocketNotifier(this); else - d->threadData->eventDispatcher.load()->unregisterSocketNotifier(this); + d->threadData->eventDispatcher.loadRelaxed()->unregisterSocketNotifier(this); } diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index b9563b8395..511dc3c81c 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -2366,7 +2366,7 @@ QVariant& QVariant::operator=(const QVariant &variant) void QVariant::detach() { - if (!d.is_shared || d.data.shared->ref.load() == 1) + if (!d.is_shared || d.data.shared->ref.loadRelaxed() == 1) return; Private dd; diff --git a/src/corelib/kernel/qvariant.h b/src/corelib/kernel/qvariant.h index e094ebff52..c8cb551863 100644 --- a/src/corelib/kernel/qvariant.h +++ b/src/corelib/kernel/qvariant.h @@ -577,7 +577,7 @@ Q_CORE_EXPORT QDataStream& operator<< (QDataStream& s, const QVariant::Type p); #endif inline bool QVariant::isDetached() const -{ return !d.is_shared || d.data.shared->ref.load() == 1; } +{ return !d.is_shared || d.data.shared->ref.loadRelaxed() == 1; } #ifdef Q_QDOC diff --git a/src/corelib/kernel/qwineventnotifier.cpp b/src/corelib/kernel/qwineventnotifier.cpp index 3c73c0b851..d2ae9668fe 100644 --- a/src/corelib/kernel/qwineventnotifier.cpp +++ b/src/corelib/kernel/qwineventnotifier.cpp @@ -124,7 +124,7 @@ QWinEventNotifier::QWinEventNotifier(HANDLE hEvent, QObject *parent) : QObject(*new QWinEventNotifierPrivate(hEvent, false), parent) { Q_D(QWinEventNotifier); - QAbstractEventDispatcher *eventDispatcher = d->threadData->eventDispatcher.load(); + QAbstractEventDispatcher *eventDispatcher = d->threadData->eventDispatcher.loadRelaxed(); if (Q_UNLIKELY(!eventDispatcher)) { qWarning("QWinEventNotifier: Can only be used with threads started with QThread"); return; @@ -197,7 +197,7 @@ void QWinEventNotifier::setEnabled(bool enable) return; d->enabled = enable; - QAbstractEventDispatcher *eventDispatcher = d->threadData->eventDispatcher.load(); + QAbstractEventDispatcher *eventDispatcher = d->threadData->eventDispatcher.loadRelaxed(); if (!eventDispatcher) { // perhaps application is shutting down if (!enable && d->waitHandle != nullptr) d->unregisterWaitObject(); @@ -256,7 +256,7 @@ void QWinEventNotifierPrivate::unregisterWaitObject() static void CALLBACK wfsoCallback(void *context, BOOLEAN /*ignore*/) { QWinEventNotifierPrivate *nd = reinterpret_cast(context); - QAbstractEventDispatcher *eventDispatcher = nd->threadData->eventDispatcher.load(); + QAbstractEventDispatcher *eventDispatcher = nd->threadData->eventDispatcher.loadRelaxed(); // Happens when Q(Core)Application is destroyed before QWinEventNotifier. // https://bugreports.qt.io/browse/QTBUG-70214 diff --git a/src/corelib/plugin/qlibrary.cpp b/src/corelib/plugin/qlibrary.cpp index 533a2790b9..1ace28c93f 100644 --- a/src/corelib/plugin/qlibrary.cpp +++ b/src/corelib/plugin/qlibrary.cpp @@ -405,10 +405,10 @@ inline void QLibraryStore::cleanup() LibraryMap::Iterator it = data->libraryMap.begin(); for (; it != data->libraryMap.end(); ++it) { QLibraryPrivate *lib = it.value(); - if (lib->libraryRefCount.load() == 1) { - if (lib->libraryUnloadCount.load() > 0) { + if (lib->libraryRefCount.loadRelaxed() == 1) { + if (lib->libraryUnloadCount.loadRelaxed() > 0) { Q_ASSERT(lib->pHnd); - lib->libraryUnloadCount.store(1); + lib->libraryUnloadCount.storeRelaxed(1); #ifdef __GLIBC__ // glibc has a bug in unloading from global destructors // see https://bugzilla.novell.com/show_bug.cgi?id=622977 @@ -428,7 +428,7 @@ inline void QLibraryStore::cleanup() for (QLibraryPrivate *lib : qAsConst(data->libraryMap)) { if (lib) qDebug() << "On QtCore unload," << lib->fileName << "was leaked, with" - << lib->libraryRefCount.load() << "users"; + << lib->libraryRefCount.loadRelaxed() << "users"; } } @@ -487,7 +487,7 @@ inline void QLibraryStore::releaseLibrary(QLibraryPrivate *lib) } // no one else is using - Q_ASSERT(lib->libraryUnloadCount.load() == 0); + Q_ASSERT(lib->libraryUnloadCount.loadRelaxed() == 0); if (Q_LIKELY(data) && !lib->fileName.isEmpty()) { QLibraryPrivate *that = data->libraryMap.take(lib->fileName); @@ -501,7 +501,7 @@ QLibraryPrivate::QLibraryPrivate(const QString &canonicalFileName, const QString : pHnd(0), fileName(canonicalFileName), fullVersion(version), instance(0), libraryRefCount(0), libraryUnloadCount(0), pluginState(MightBeAPlugin) { - loadHintsInt.store(loadHints); + loadHintsInt.storeRelaxed(loadHints); if (canonicalFileName.isEmpty()) errorString = QLibrary::tr("The shared library was not found."); } @@ -522,7 +522,7 @@ void QLibraryPrivate::mergeLoadHints(QLibrary::LoadHints lh) if (pHnd) return; - loadHintsInt.store(lh); + loadHintsInt.storeRelaxed(lh); } QFunctionPointer QLibraryPrivate::resolve(const char *symbol) @@ -575,7 +575,7 @@ bool QLibraryPrivate::unload(UnloadFlag flag) { if (!pHnd) return false; - if (libraryUnloadCount.load() > 0 && !libraryUnloadCount.deref()) { // only unload if ALL QLibrary instance wanted to + if (libraryUnloadCount.loadRelaxed() > 0 && !libraryUnloadCount.deref()) { // only unload if ALL QLibrary instance wanted to delete inst.data(); if (flag == NoUnloadSys || unload_sys()) { if (qt_debug_component()) diff --git a/src/corelib/plugin/qlibrary_p.h b/src/corelib/plugin/qlibrary_p.h index 1a216c98b5..db5afac98e 100644 --- a/src/corelib/plugin/qlibrary_p.h +++ b/src/corelib/plugin/qlibrary_p.h @@ -92,7 +92,7 @@ public: QFunctionPointer resolve(const char *); QLibrary::LoadHints loadHints() const - { return QLibrary::LoadHints(loadHintsInt.load()); } + { return QLibrary::LoadHints(loadHintsInt.loadRelaxed()); } void setLoadHints(QLibrary::LoadHints lh); static QLibraryPrivate *findOrCreate(const QString &fileName, const QString &version = QString(), diff --git a/src/corelib/serialization/qcborvalue.cpp b/src/corelib/serialization/qcborvalue.cpp index b2e0ba6d53..ba616c0a7d 100644 --- a/src/corelib/serialization/qcborvalue.cpp +++ b/src/corelib/serialization/qcborvalue.cpp @@ -849,7 +849,7 @@ QCborContainerPrivate *QCborContainerPrivate::clone(QCborContainerPrivate *d, qs QCborContainerPrivate *QCborContainerPrivate::detach(QCborContainerPrivate *d, qsizetype reserved) { - if (!d || d->ref.load() != 1) + if (!d || d->ref.loadRelaxed() != 1) return clone(d, reserved); return d; } @@ -884,12 +884,12 @@ void QCborContainerPrivate::replaceAt_complex(Element &e, const QCborValue &valu // detect self-assignment if (Q_UNLIKELY(this == value.container)) { - Q_ASSERT(ref.load() >= 2); + Q_ASSERT(ref.loadRelaxed() >= 2); if (disp == MoveContainer) ref.deref(); // not deref() because it can't drop to 0 QCborContainerPrivate *d = QCborContainerPrivate::clone(this); d->elements.detach(); - d->ref.store(1); + d->ref.storeRelaxed(1); e.container = d; } else { e.container = value.container; @@ -1368,7 +1368,7 @@ static Element decodeBasicValueFromCbor(QCborStreamReader &reader) static inline QCborContainerPrivate *createContainerFromCbor(QCborStreamReader &reader) { auto d = new QCborContainerPrivate; - d->ref.store(1); + d->ref.storeRelaxed(1); d->decodeFromCbor(reader); return d; } @@ -1643,7 +1643,7 @@ QCborValue::QCborValue(const QByteArray &ba) : n(0), container(new QCborContainerPrivate), t(ByteArray) { container->appendByteData(ba.constData(), ba.size(), t); - container->ref.store(1); + container->ref.storeRelaxed(1); } /*! @@ -1656,7 +1656,7 @@ QCborValue::QCborValue(const QString &s) : n(0), container(new QCborContainerPrivate), t(String) { container->append(s); - container->ref.store(1); + container->ref.storeRelaxed(1); } /*! @@ -1671,7 +1671,7 @@ QCborValue::QCborValue(QLatin1String s) : n(0), container(new QCborContainerPrivate), t(String) { container->append(s); - container->ref.store(1); + container->ref.storeRelaxed(1); } /*! @@ -1719,7 +1719,7 @@ QCborValue::QCborValue(const QCborMap &m) QCborValue::QCborValue(QCborTag t, const QCborValue &tv) : n(-1), container(new QCborContainerPrivate), t(Tag) { - container->ref.store(1); + container->ref.storeRelaxed(1); container->append(t); container->append(tv); } diff --git a/src/corelib/serialization/qjson_p.h b/src/corelib/serialization/qjson_p.h index 85fb2a1254..9de53d5b74 100644 --- a/src/corelib/serialization/qjson_p.h +++ b/src/corelib/serialization/qjson_p.h @@ -719,7 +719,7 @@ public: Data *clone(Base *b, int reserve = 0) { int size = sizeof(Header) + b->size; - if (b == header->root() && ref.load() == 1 && alloc >= size + reserve) + if (b == header->root() && ref.loadRelaxed() == 1 && alloc >= size + reserve) return this; if (reserve) { diff --git a/src/corelib/serialization/qjsonarray.cpp b/src/corelib/serialization/qjsonarray.cpp index 3a6244f5ac..6b327619ad 100644 --- a/src/corelib/serialization/qjsonarray.cpp +++ b/src/corelib/serialization/qjsonarray.cpp @@ -1226,7 +1226,7 @@ bool QJsonArray::detach2(uint reserve) d->ref.ref(); return true; } - if (reserve == 0 && d->ref.load() == 1) + if (reserve == 0 && d->ref.loadRelaxed() == 1) return true; QJsonPrivate::Data *x = d->clone(a, reserve); diff --git a/src/corelib/serialization/qjsonobject.cpp b/src/corelib/serialization/qjsonobject.cpp index f92bdd0e80..c1a6328b2f 100644 --- a/src/corelib/serialization/qjsonobject.cpp +++ b/src/corelib/serialization/qjsonobject.cpp @@ -646,7 +646,7 @@ bool QJsonObject::operator!=(const QJsonObject &other) const */ QJsonObject::iterator QJsonObject::erase(QJsonObject::iterator it) { - Q_ASSERT(d && d->ref.load() == 1); + Q_ASSERT(d && d->ref.loadRelaxed() == 1); if (it.o != this || it.i < 0 || it.i >= (int)o->length) return iterator(this, o->length); @@ -1231,7 +1231,7 @@ bool QJsonObject::detach2(uint reserve) d->ref.ref(); return true; } - if (reserve == 0 && d->ref.load() == 1) + if (reserve == 0 && d->ref.loadRelaxed() == 1) return true; QJsonPrivate::Data *x = d->clone(o, reserve); diff --git a/src/corelib/thread/qatomic.h b/src/corelib/thread/qatomic.h index 7990db2fd9..a3b9be0729 100644 --- a/src/corelib/thread/qatomic.h +++ b/src/corelib/thread/qatomic.h @@ -257,7 +257,7 @@ inline void qAtomicAssign(T *&d, T *x) template inline void qAtomicDetach(T *&d) { - if (d->ref.load() == 1) + if (d->ref.loadRelaxed() == 1) return; T *x = d; d = new T(*d); diff --git a/src/corelib/thread/qfutureinterface.cpp b/src/corelib/thread/qfutureinterface.cpp index dfbed5fb0f..8f4cae8816 100644 --- a/src/corelib/thread/qfutureinterface.cpp +++ b/src/corelib/thread/qfutureinterface.cpp @@ -97,7 +97,7 @@ static inline int switch_off(QAtomicInt &a, int which) static inline int switch_from_to(QAtomicInt &a, int from, int to) { int newValue; - int expected = a.load(); + int expected = a.loadRelaxed(); do { newValue = (expected & ~from) | to; } while (!a.testAndSetRelaxed(expected, newValue, expected)); @@ -107,7 +107,7 @@ static inline int switch_from_to(QAtomicInt &a, int from, int to) void QFutureInterfaceBase::cancel() { QMutexLocker locker(&d->m_mutex); - if (d->state.load() & Canceled) + if (d->state.loadRelaxed() & Canceled) return; switch_from_to(d->state, Paused, Canceled); @@ -132,7 +132,7 @@ void QFutureInterfaceBase::setPaused(bool paused) void QFutureInterfaceBase::togglePaused() { QMutexLocker locker(&d->m_mutex); - if (d->state.load() & Paused) { + if (d->state.loadRelaxed() & Paused) { switch_off(d->state, Paused); d->pausedWaitCondition.wakeAll(); d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::Resumed)); @@ -149,7 +149,7 @@ void QFutureInterfaceBase::setThrottled(bool enable) switch_on(d->state, Throttled); } else { switch_off(d->state, Throttled); - if (!(d->state.load() & Paused)) + if (!(d->state.loadRelaxed() & Paused)) d->pausedWaitCondition.wakeAll(); } } @@ -201,13 +201,13 @@ void QFutureInterfaceBase::waitForResume() { // return early if possible to avoid taking the mutex lock. { - const int state = d->state.load(); + const int state = d->state.loadRelaxed(); if (!(state & Paused) || (state & Canceled)) return; } QMutexLocker lock(&d->m_mutex); - const int state = d->state.load(); + const int state = d->state.loadRelaxed(); if (!(state & Paused) || (state & Canceled)) return; @@ -256,7 +256,7 @@ bool QFutureInterfaceBase::isProgressUpdateNeeded() const void QFutureInterfaceBase::reportStarted() { QMutexLocker locker(&d->m_mutex); - if (d->state.load() & (Started|Canceled|Finished)) + if (d->state.loadRelaxed() & (Started|Canceled|Finished)) return; d->setState(State(Started | Running)); @@ -272,7 +272,7 @@ void QFutureInterfaceBase::reportCanceled() void QFutureInterfaceBase::reportException(const QException &exception) { QMutexLocker locker(&d->m_mutex); - if (d->state.load() & (Canceled|Finished)) + if (d->state.loadRelaxed() & (Canceled|Finished)) return; d->m_exceptionStore.setException(exception); @@ -307,7 +307,7 @@ int QFutureInterfaceBase::expectedResultCount() bool QFutureInterfaceBase::queryState(State state) const { - return d->state.load() & state; + return d->state.loadRelaxed() & state; } void QFutureInterfaceBase::waitForResult(int resultIndex) @@ -352,7 +352,7 @@ void QFutureInterfaceBase::waitForFinished() void QFutureInterfaceBase::reportResultsReady(int beginIndex, int endIndex) { - if (beginIndex == endIndex || (d->state.load() & (Canceled|Finished))) + if (beginIndex == endIndex || (d->state.loadRelaxed() & (Canceled|Finished))) return; d->waitCondition.wakeAll(); @@ -414,7 +414,7 @@ void QFutureInterfaceBase::setProgressValueAndText(int progressValue, if (d->m_progressValue >= progressValue) return; - if (d->state.load() & (Canceled|Finished)) + if (d->state.loadRelaxed() & (Canceled|Finished)) return; if (d->internal_updateProgress(progressValue, progressText)) { @@ -486,10 +486,10 @@ bool QFutureInterfaceBasePrivate::internal_waitForNextResult() if (m_results.hasNextResult()) return true; - while ((state.load() & QFutureInterfaceBase::Running) && m_results.hasNextResult() == false) + while ((state.loadRelaxed() & QFutureInterfaceBase::Running) && m_results.hasNextResult() == false) waitCondition.wait(&m_mutex); - return !(state.load() & QFutureInterfaceBase::Canceled) && m_results.hasNextResult(); + return !(state.loadRelaxed() & QFutureInterfaceBase::Canceled) && m_results.hasNextResult(); } bool QFutureInterfaceBasePrivate::internal_updateProgress(int progress, @@ -512,8 +512,8 @@ bool QFutureInterfaceBasePrivate::internal_updateProgress(int progress, void QFutureInterfaceBasePrivate::internal_setThrottled(bool enable) { // bail out if we are not changing the state - if ((enable && (state.load() & QFutureInterfaceBase::Throttled)) - || (!enable && !(state.load() & QFutureInterfaceBase::Throttled))) + if ((enable && (state.loadRelaxed() & QFutureInterfaceBase::Throttled)) + || (!enable && !(state.loadRelaxed() & QFutureInterfaceBase::Throttled))) return; // change the state @@ -521,7 +521,7 @@ void QFutureInterfaceBasePrivate::internal_setThrottled(bool enable) switch_on(state, QFutureInterfaceBase::Throttled); } else { switch_off(state, QFutureInterfaceBase::Throttled); - if (!(state.load() & QFutureInterfaceBase::Paused)) + if (!(state.loadRelaxed() & QFutureInterfaceBase::Paused)) pausedWaitCondition.wakeAll(); } } @@ -556,7 +556,7 @@ void QFutureInterfaceBasePrivate::connectOutputInterface(QFutureCallOutInterface { QMutexLocker locker(&m_mutex); - if (state.load() & QFutureInterfaceBase::Started) { + if (state.loadRelaxed() & QFutureInterfaceBase::Started) { interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Started)); interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::ProgressRange, m_progressMinimum, @@ -576,13 +576,13 @@ void QFutureInterfaceBasePrivate::connectOutputInterface(QFutureCallOutInterface it.batchedAdvance(); } - if (state.load() & QFutureInterfaceBase::Paused) + if (state.loadRelaxed() & QFutureInterfaceBase::Paused) interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Paused)); - if (state.load() & QFutureInterfaceBase::Canceled) + if (state.loadRelaxed() & QFutureInterfaceBase::Canceled) interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Canceled)); - if (state.load() & QFutureInterfaceBase::Finished) + if (state.loadRelaxed() & QFutureInterfaceBase::Finished) interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Finished)); outputConnections.append(interface); @@ -601,7 +601,7 @@ void QFutureInterfaceBasePrivate::disconnectOutputInterface(QFutureCallOutInterf void QFutureInterfaceBasePrivate::setState(QFutureInterfaceBase::State newState) { - state.store(newState); + state.storeRelaxed(newState); } QT_END_NAMESPACE diff --git a/src/corelib/thread/qfutureinterface_p.h b/src/corelib/thread/qfutureinterface_p.h index 63e534464f..b297dff633 100644 --- a/src/corelib/thread/qfutureinterface_p.h +++ b/src/corelib/thread/qfutureinterface_p.h @@ -144,11 +144,11 @@ public: // Default ref counter for QFIBP inline bool ref() { return m_refCount.ref(); } inline bool deref() { return m_refCount.deref(); } - inline int load() const { return m_refCount.load(); } + inline int load() const { return m_refCount.loadRelaxed(); } // Ref counter for type T inline bool refT() { return m_refCountT.ref(); } inline bool derefT() { return m_refCountT.deref(); } - inline int loadT() const { return m_refCountT.load(); } + inline int loadT() const { return m_refCountT.loadRelaxed(); } private: QAtomicInt m_refCount; diff --git a/src/corelib/thread/qfuturewatcher.cpp b/src/corelib/thread/qfuturewatcher.cpp index 4ee7693ace..ed369ce829 100644 --- a/src/corelib/thread/qfuturewatcher.cpp +++ b/src/corelib/thread/qfuturewatcher.cpp @@ -402,7 +402,7 @@ void QFutureWatcherBase::disconnectOutputInterface(bool pendingAssignment) { if (pendingAssignment) { Q_D(QFutureWatcherBase); - d->pendingResultsReady.store(0); + d->pendingResultsReady.storeRelaxed(0); qDeleteAll(d->pendingCallOutEvents); d->pendingCallOutEvents.clear(); d->finished = false; /* May soon be amended, during connectOutputInterface() */ @@ -441,7 +441,7 @@ void QFutureWatcherBasePrivate::sendCallOutEvent(QFutureCallOutEvent *event) emit q->finished(); break; case QFutureCallOutEvent::Canceled: - pendingResultsReady.store(0); + pendingResultsReady.storeRelaxed(0); emit q->canceled(); break; case QFutureCallOutEvent::Paused: @@ -466,7 +466,7 @@ void QFutureWatcherBasePrivate::sendCallOutEvent(QFutureCallOutEvent *event) emit q->resultsReadyAt(beginIndex, endIndex); - if (resultAtConnected.load() <= 0) + if (resultAtConnected.loadRelaxed() <= 0) break; for (int i = beginIndex; i < endIndex; ++i) diff --git a/src/corelib/thread/qmutex.cpp b/src/corelib/thread/qmutex.cpp index 4f55e50fe5..bd3a0fa7ba 100644 --- a/src/corelib/thread/qmutex.cpp +++ b/src/corelib/thread/qmutex.cpp @@ -179,7 +179,7 @@ public: */ QMutex::QMutex(RecursionMode mode) { - d_ptr.store(mode == Recursive ? new QRecursiveMutexPrivate : 0); + d_ptr.storeRelaxed(mode == Recursive ? new QRecursiveMutexPrivate : 0); } /*! @@ -189,12 +189,12 @@ QMutex::QMutex(RecursionMode mode) */ QMutex::~QMutex() { - QMutexData *d = d_ptr.load(); + QMutexData *d = d_ptr.loadRelaxed(); if (isRecursive()) { delete static_cast(d); } else if (d) { #ifndef QT_LINUX_FUTEX - if (d != dummyLocked() && static_cast(d)->possiblyUnlocked.load() + if (d != dummyLocked() && static_cast(d)->possiblyUnlocked.loadRelaxed() && tryLock()) { unlock(); return; @@ -517,7 +517,7 @@ bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT } QMutexPrivate *d = static_cast(copy); - if (timeout == 0 && !d->possiblyUnlocked.load()) + if (timeout == 0 && !d->possiblyUnlocked.loadRelaxed()) return false; // At this point we have a pointer to a QMutexPrivate. But the other thread @@ -541,7 +541,7 @@ bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT // is set to the BigNumber magic value set in unlockInternal() int old_waiters; do { - old_waiters = d->waiters.load(); + old_waiters = d->waiters.loadRelaxed(); if (old_waiters == -QMutexPrivate::BigNumber) { // we are unlocking, and the thread that unlocks is about to change d to 0 // we try to acquire the mutex by changing to dummyLocked() @@ -550,7 +550,7 @@ bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT d->deref(); return true; } else { - Q_ASSERT(d != d_ptr.load()); //else testAndSetAcquire should have succeeded + Q_ASSERT(d != d_ptr.loadRelaxed()); //else testAndSetAcquire should have succeeded // Mutex is likely to bo 0, we should continue the outer-loop, // set old_waiters to the magic value of BigNumber old_waiters = QMutexPrivate::BigNumber; @@ -563,7 +563,7 @@ bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT // The mutex was unlocked before we incremented waiters. if (old_waiters != QMutexPrivate::BigNumber) { //we did not break the previous loop - Q_ASSERT(d->waiters.load() >= 1); + Q_ASSERT(d->waiters.loadRelaxed() >= 1); d->waiters.deref(); } d->deref(); @@ -572,11 +572,11 @@ bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT if (d->wait(timeout)) { // reset the possiblyUnlocked flag if needed (and deref its corresponding reference) - if (d->possiblyUnlocked.load() && d->possiblyUnlocked.testAndSetRelaxed(true, false)) + if (d->possiblyUnlocked.loadRelaxed() && d->possiblyUnlocked.testAndSetRelaxed(true, false)) d->deref(); d->derefWaiters(1); //we got the lock. (do not deref) - Q_ASSERT(d == d_ptr.load()); + Q_ASSERT(d == d_ptr.loadRelaxed()); return true; } else { Q_ASSERT(timeout >= 0); @@ -593,7 +593,7 @@ bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT return false; } } - Q_ASSERT(d_ptr.load() != 0); + Q_ASSERT(d_ptr.loadRelaxed() != 0); return true; } @@ -618,7 +618,7 @@ void QBasicMutex::unlockInternal() noexcept //there is no one waiting on this mutex anymore, set the mutex as unlocked (d = 0) if (d_ptr.testAndSetRelease(d, 0)) { // reset the possiblyUnlocked flag if needed (and deref its corresponding reference) - if (d->possiblyUnlocked.load() && d->possiblyUnlocked.testAndSetRelaxed(true, false)) + if (d->possiblyUnlocked.loadRelaxed() && d->possiblyUnlocked.testAndSetRelaxed(true, false)) d->deref(); } d->derefWaiters(0); @@ -657,20 +657,20 @@ QMutexPrivate *QMutexPrivate::allocate() int i = freelist()->next(); QMutexPrivate *d = &(*freelist())[i]; d->id = i; - Q_ASSERT(d->refCount.load() == 0); + Q_ASSERT(d->refCount.loadRelaxed() == 0); Q_ASSERT(!d->recursive); - Q_ASSERT(!d->possiblyUnlocked.load()); - Q_ASSERT(d->waiters.load() == 0); - d->refCount.store(1); + Q_ASSERT(!d->possiblyUnlocked.loadRelaxed()); + Q_ASSERT(d->waiters.loadRelaxed() == 0); + d->refCount.storeRelaxed(1); return d; } void QMutexPrivate::release() { Q_ASSERT(!recursive); - Q_ASSERT(refCount.load() == 0); - Q_ASSERT(!possiblyUnlocked.load()); - Q_ASSERT(waiters.load() == 0); + Q_ASSERT(refCount.loadRelaxed() == 0); + Q_ASSERT(!possiblyUnlocked.loadRelaxed()); + Q_ASSERT(waiters.loadRelaxed() == 0); freelist()->release(id); } @@ -680,7 +680,7 @@ void QMutexPrivate::derefWaiters(int value) noexcept int old_waiters; int new_waiters; do { - old_waiters = waiters.load(); + old_waiters = waiters.loadRelaxed(); new_waiters = old_waiters; if (new_waiters < 0) { new_waiters += QMutexPrivate::BigNumber; @@ -696,7 +696,7 @@ void QMutexPrivate::derefWaiters(int value) noexcept inline bool QRecursiveMutexPrivate::lock(int timeout) QT_MUTEX_LOCK_NOEXCEPT { Qt::HANDLE self = QThread::currentThreadId(); - if (owner.load() == self) { + if (owner.loadRelaxed() == self) { ++count; Q_ASSERT_X(count != 0, "QMutex::lock", "Overflow in recursion counter"); return true; @@ -709,7 +709,7 @@ inline bool QRecursiveMutexPrivate::lock(int timeout) QT_MUTEX_LOCK_NOEXCEPT } if (success) - owner.store(self); + owner.storeRelaxed(self); return success; } @@ -721,7 +721,7 @@ inline void QRecursiveMutexPrivate::unlock() noexcept if (count > 0) { count--; } else { - owner.store(0); + owner.storeRelaxed(0); mutex.QBasicMutex::unlock(); } } diff --git a/src/corelib/thread/qmutex.h b/src/corelib/thread/qmutex.h index d7796092d1..0de0869cb2 100644 --- a/src/corelib/thread/qmutex.h +++ b/src/corelib/thread/qmutex.h @@ -81,7 +81,7 @@ public: // BasicLockable concept inline void unlock() noexcept { - Q_ASSERT(d_ptr.load()); //mutex must be locked + Q_ASSERT(d_ptr.loadRelaxed()); //mutex must be locked if (!fastTryUnlock()) unlockInternal(); } diff --git a/src/corelib/thread/qmutex_linux.cpp b/src/corelib/thread/qmutex_linux.cpp index b006ff1033..3270875471 100644 --- a/src/corelib/thread/qmutex_linux.cpp +++ b/src/corelib/thread/qmutex_linux.cpp @@ -149,7 +149,7 @@ bool lockInternal_helper(QBasicAtomicPointer &d_ptr, int timeout = - } } - Q_ASSERT(d_ptr.load()); + Q_ASSERT(d_ptr.loadRelaxed()); return true; } @@ -169,7 +169,7 @@ bool QBasicMutex::lockInternal(int timeout) noexcept void QBasicMutex::unlockInternal() noexcept { - QMutexData *d = d_ptr.load(); + QMutexData *d = d_ptr.loadRelaxed(); Q_ASSERT(d); //we must be locked Q_ASSERT(d != dummyLocked()); // testAndSetRelease(dummyLocked(), 0) failed Q_UNUSED(d); diff --git a/src/corelib/thread/qmutex_p.h b/src/corelib/thread/qmutex_p.h index 5025f836b9..048d8707c4 100644 --- a/src/corelib/thread/qmutex_p.h +++ b/src/corelib/thread/qmutex_p.h @@ -99,21 +99,21 @@ public: int id; bool ref() { - Q_ASSERT(refCount.load() >= 0); + Q_ASSERT(refCount.loadRelaxed() >= 0); int c; do { - c = refCount.load(); + c = refCount.loadRelaxed(); if (c == 0) return false; } while (!refCount.testAndSetRelaxed(c, c + 1)); - Q_ASSERT(refCount.load() >= 0); + Q_ASSERT(refCount.loadRelaxed() >= 0); return true; } void deref() { - Q_ASSERT(refCount.load() >= 0); + Q_ASSERT(refCount.loadRelaxed() >= 0); if (!refCount.deref()) release(); - Q_ASSERT(refCount.load() >= 0); + Q_ASSERT(refCount.loadRelaxed() >= 0); } void release(); static QMutexPrivate *allocate(); diff --git a/src/corelib/thread/qmutexpool.cpp b/src/corelib/thread/qmutexpool.cpp index 3f9e8da942..2a02197859 100644 --- a/src/corelib/thread/qmutexpool.cpp +++ b/src/corelib/thread/qmutexpool.cpp @@ -93,7 +93,7 @@ QMutexPool::QMutexPool(QMutex::RecursionMode recursionMode, int size) : mutexes(size), recursionMode(recursionMode) { for (int index = 0; index < mutexes.count(); ++index) { - mutexes[index].store(0); + mutexes[index].storeRelaxed(0); } } @@ -104,7 +104,7 @@ QMutexPool::QMutexPool(QMutex::RecursionMode recursionMode, int size) QMutexPool::~QMutexPool() { for (int index = 0; index < mutexes.count(); ++index) - delete mutexes[index].load(); + delete mutexes[index].loadRelaxed(); } /*! @@ -131,7 +131,7 @@ QMutex *QMutexPool::createMutex(int index) QMutex *newMutex = new QMutex(recursionMode); if (!mutexes[index].testAndSetRelease(0, newMutex)) delete newMutex; - return mutexes[index].load(); + return mutexes[index].loadRelaxed(); } /*! diff --git a/src/corelib/thread/qmutexpool_p.h b/src/corelib/thread/qmutexpool_p.h index 89d006ac29..1a47231abc 100644 --- a/src/corelib/thread/qmutexpool_p.h +++ b/src/corelib/thread/qmutexpool_p.h @@ -68,7 +68,7 @@ public: inline QMutex *get(const void *address) { int index = uint(quintptr(address)) % mutexes.count(); - QMutex *m = mutexes[index].load(); + QMutex *m = mutexes[index].loadRelaxed(); if (m) return m; else diff --git a/src/corelib/thread/qreadwritelock.cpp b/src/corelib/thread/qreadwritelock.cpp index d7cf7a7284..30e9b95a52 100644 --- a/src/corelib/thread/qreadwritelock.cpp +++ b/src/corelib/thread/qreadwritelock.cpp @@ -143,7 +143,7 @@ inline bool isUncontendedLocked(const QReadWriteLockPrivate *d) QReadWriteLock::QReadWriteLock(RecursionMode recursionMode) : d_ptr(recursionMode == Recursive ? new QReadWriteLockPrivate(true) : nullptr) { - Q_ASSERT_X(!(quintptr(d_ptr.load()) & StateMask), "QReadWriteLock::QReadWriteLock", "bad d_ptr alignment"); + Q_ASSERT_X(!(quintptr(d_ptr.loadRelaxed()) & StateMask), "QReadWriteLock::QReadWriteLock", "bad d_ptr alignment"); } /*! @@ -154,7 +154,7 @@ QReadWriteLock::QReadWriteLock(RecursionMode recursionMode) */ QReadWriteLock::~QReadWriteLock() { - auto d = d_ptr.load(); + auto d = d_ptr.loadRelaxed(); if (isUncontendedLocked(d)) { qWarning("QReadWriteLock: destroying locked QReadWriteLock"); return; @@ -263,7 +263,7 @@ bool QReadWriteLock::tryLockForRead(int timeout) return d->recursiveLockForRead(timeout); QMutexLocker lock(&d->mutex); - if (d != d_ptr.load()) { + if (d != d_ptr.loadRelaxed()) { // d_ptr has changed: this QReadWriteLock was unlocked before we had // time to lock d->mutex. // We are holding a lock to a mutex within a QReadWriteLockPrivate @@ -370,7 +370,7 @@ bool QReadWriteLock::tryLockForWrite(int timeout) return d->recursiveLockForWrite(timeout); QMutexLocker lock(&d->mutex); - if (d != d_ptr.load()) { + if (d != d_ptr.loadRelaxed()) { // The mutex was unlocked before we had time to lock the mutex. // We are holding to a mutex within a QReadWriteLockPrivate that is already released // (or even is already re-used) but that's ok because the QFreeList never frees them. @@ -433,7 +433,7 @@ void QReadWriteLock::unlock() if (d->waitingReaders || d->waitingWriters) { d->unlock(); } else { - Q_ASSERT(d_ptr.load() == d); // should not change when we still hold the mutex + Q_ASSERT(d_ptr.loadRelaxed() == d); // should not change when we still hold the mutex d_ptr.storeRelease(nullptr); d->release(); } @@ -444,7 +444,7 @@ void QReadWriteLock::unlock() /*! \internal Helper for QWaitCondition::wait */ QReadWriteLock::StateForWaitCondition QReadWriteLock::stateForWaitCondition() const { - QReadWriteLockPrivate *d = d_ptr.load(); + QReadWriteLockPrivate *d = d_ptr.loadRelaxed(); switch (quintptr(d) & StateMask) { case StateLockedForRead: return LockedForRead; case StateLockedForWrite: return LockedForWrite; diff --git a/src/corelib/thread/qsemaphore.cpp b/src/corelib/thread/qsemaphore.cpp index 2e0b6f2bc0..d4fb756b94 100644 --- a/src/corelib/thread/qsemaphore.cpp +++ b/src/corelib/thread/qsemaphore.cpp @@ -264,7 +264,7 @@ template bool futexSemaphoreTryAcquire(QBasicAtomicIntegerload() & 0x7fffffffU); + Q_ASSERT(futexHigh32(&u)->loadRelaxed() & 0x7fffffffU); u.fetchAndSubRelaxed(oneWaiter); } return false; @@ -293,7 +293,7 @@ QSemaphore::QSemaphore(int n) quintptr nn = unsigned(n); if (futexHasWaiterCount) nn |= quint64(nn) << 32; // token count replicated in high word - u.store(nn); + u.storeRelaxed(nn); } else { d = new QSemaphorePrivate(n); } @@ -425,7 +425,7 @@ void QSemaphore::release(int n) int QSemaphore::available() const { if (futexAvailable()) - return futexAvailCounter(u.load()); + return futexAvailCounter(u.loadRelaxed()); QMutexLocker locker(&d->mutex); return d->avail; diff --git a/src/corelib/thread/qthread.cpp b/src/corelib/thread/qthread.cpp index b9b9f3e354..1eac7db11d 100644 --- a/src/corelib/thread/qthread.cpp +++ b/src/corelib/thread/qthread.cpp @@ -65,7 +65,7 @@ QThreadData::QThreadData(int initialRefCount) QThreadData::~QThreadData() { - Q_ASSERT(_ref.load() == 0); + Q_ASSERT(_ref.loadRelaxed() == 0); // In the odd case that Qt is running on a secondary thread, the main // thread instance will have been dereffed asunder because of the deref in @@ -105,7 +105,7 @@ void QThreadData::ref() { #if QT_CONFIG(thread) (void) _ref.ref(); - Q_ASSERT(_ref.load() != 0); + Q_ASSERT(_ref.loadRelaxed() != 0); #endif } @@ -878,11 +878,11 @@ QThreadData *QThreadData::current(bool createIfNecessary) if (!data && createIfNecessary) { data = new QThreadData; data->thread = new QAdoptedThread(data); - data->threadId.store(Qt::HANDLE(data->thread)); + data->threadId.storeRelaxed(Qt::HANDLE(data->thread)); data->deref(); data->isAdopted = true; if (!QCoreApplicationPrivate::theMainThread) - QCoreApplicationPrivate::theMainThread = data->thread.load(); + QCoreApplicationPrivate::theMainThread = data->thread.loadRelaxed(); } return data; } @@ -925,7 +925,7 @@ QThreadPrivate::~QThreadPrivate() QAbstractEventDispatcher *QThread::eventDispatcher() const { Q_D(const QThread); - return d->data->eventDispatcher.load(); + return d->data->eventDispatcher.loadRelaxed(); } /*! diff --git a/src/corelib/thread/qthread_p.h b/src/corelib/thread/qthread_p.h index fe54ec8f07..b2d1628e6e 100644 --- a/src/corelib/thread/qthread_p.h +++ b/src/corelib/thread/qthread_p.h @@ -257,11 +257,11 @@ public: void ref(); void deref(); inline bool hasEventDispatcher() const - { return eventDispatcher.load() != nullptr; } + { return eventDispatcher.loadRelaxed() != nullptr; } QAbstractEventDispatcher *createEventDispatcher(); QAbstractEventDispatcher *ensureEventDispatcher() { - QAbstractEventDispatcher *ed = eventDispatcher.load(); + QAbstractEventDispatcher *ed = eventDispatcher.loadRelaxed(); if (Q_LIKELY(ed)) return ed; return createEventDispatcher(); diff --git a/src/corelib/thread/qthread_unix.cpp b/src/corelib/thread/qthread_unix.cpp index 978c18c1b6..7926323a4d 100644 --- a/src/corelib/thread/qthread_unix.cpp +++ b/src/corelib/thread/qthread_unix.cpp @@ -252,9 +252,9 @@ QThreadData *QThreadData::current(bool createIfNecessary) } data->deref(); data->isAdopted = true; - data->threadId.store(to_HANDLE(pthread_self())); + data->threadId.storeRelaxed(to_HANDLE(pthread_self())); if (!QCoreApplicationPrivate::theMainThread) - QCoreApplicationPrivate::theMainThread = data->thread.load(); + QCoreApplicationPrivate::theMainThread = data->thread.loadRelaxed(); } return data; } @@ -334,7 +334,7 @@ void *QThreadPrivate::start(void *arg) thr->d_func()->setPriority(QThread::Priority(thr->d_func()->priority & ~ThreadPriorityResetFlag)); } - data->threadId.store(to_HANDLE(pthread_self())); + data->threadId.storeRelaxed(to_HANDLE(pthread_self())); set_thread_data(data); data->ref(); @@ -404,7 +404,7 @@ void QThreadPrivate::finish(void *arg) QThreadStorageData::finish((void **)data); locker.relock(); - QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher.load(); + QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher.loadRelaxed(); if (eventDispatcher) { d->data->eventDispatcher = 0; locker.unlock(); @@ -737,7 +737,7 @@ void QThread::start(Priority priority) #endif code = pthread_create(&threadId, &attr, QThreadPrivate::start, this); } - d->data->threadId.store(to_HANDLE(threadId)); + d->data->threadId.storeRelaxed(to_HANDLE(threadId)); pthread_attr_destroy(&attr); @@ -746,7 +746,7 @@ void QThread::start(Priority priority) d->running = false; d->finished = false; - d->data->threadId.store(nullptr); + d->data->threadId.storeRelaxed(nullptr); } } @@ -756,10 +756,10 @@ void QThread::terminate() Q_D(QThread); QMutexLocker locker(&d->mutex); - if (!d->data->threadId.load()) + if (!d->data->threadId.loadRelaxed()) return; - int code = pthread_cancel(from_HANDLE(d->data->threadId.load())); + int code = pthread_cancel(from_HANDLE(d->data->threadId.loadRelaxed())); if (code) { qErrnoWarning(code, "QThread::start: Thread termination error"); } @@ -771,7 +771,7 @@ bool QThread::wait(unsigned long time) Q_D(QThread); QMutexLocker locker(&d->mutex); - if (from_HANDLE(d->data->threadId.load()) == pthread_self()) { + if (from_HANDLE(d->data->threadId.loadRelaxed()) == pthread_self()) { qWarning("QThread::wait: Thread tried to wait on itself"); return false; } @@ -813,7 +813,7 @@ void QThreadPrivate::setPriority(QThread::Priority threadPriority) int sched_policy; sched_param param; - if (pthread_getschedparam(from_HANDLE(data->threadId.load()), &sched_policy, ¶m) != 0) { + if (pthread_getschedparam(from_HANDLE(data->threadId.loadRelaxed()), &sched_policy, ¶m) != 0) { // failed to get the scheduling policy, don't bother setting // the priority qWarning("QThread::setPriority: Cannot get scheduler parameters"); @@ -829,15 +829,15 @@ void QThreadPrivate::setPriority(QThread::Priority threadPriority) } param.sched_priority = prio; - int status = pthread_setschedparam(from_HANDLE(data->threadId.load()), sched_policy, ¶m); + int status = pthread_setschedparam(from_HANDLE(data->threadId.loadRelaxed()), sched_policy, ¶m); # ifdef SCHED_IDLE // were we trying to set to idle priority and failed? if (status == -1 && sched_policy == SCHED_IDLE && errno == EINVAL) { // reset to lowest priority possible - pthread_getschedparam(from_HANDLE(data->threadId.load()), &sched_policy, ¶m); + pthread_getschedparam(from_HANDLE(data->threadId.loadRelaxed()), &sched_policy, ¶m); param.sched_priority = sched_get_priority_min(sched_policy); - pthread_setschedparam(from_HANDLE(data->threadId.load()), sched_policy, ¶m); + pthread_setschedparam(from_HANDLE(data->threadId.loadRelaxed()), sched_policy, ¶m); } # else Q_UNUSED(status); diff --git a/src/corelib/thread/qthread_win.cpp b/src/corelib/thread/qthread_win.cpp index ee73280707..f8a0b0abaa 100644 --- a/src/corelib/thread/qthread_win.cpp +++ b/src/corelib/thread/qthread_win.cpp @@ -138,11 +138,11 @@ QThreadData *QThreadData::current(bool createIfNecessary) } threadData->deref(); threadData->isAdopted = true; - threadData->threadId.store(reinterpret_cast(quintptr(GetCurrentThreadId()))); + threadData->threadId.storeRelaxed(reinterpret_cast(quintptr(GetCurrentThreadId()))); #ifndef Q_OS_WINRT if (!QCoreApplicationPrivate::theMainThread) { - QCoreApplicationPrivate::theMainThread = threadData->thread.load(); + QCoreApplicationPrivate::theMainThread = threadData->thread.loadRelaxed(); } else { #else // for winrt the main thread is set explicitly in QCoreApplication's constructor as the @@ -184,9 +184,9 @@ void QThreadData::setMainThread() } threadData->deref(); threadData->isAdopted = true; - threadData->threadId.store(reinterpret_cast(quintptr(GetCurrentThreadId()))); + threadData->threadId.storeRelaxed(reinterpret_cast(quintptr(GetCurrentThreadId()))); } - QCoreApplicationPrivate::theMainThread = threadData->thread.load(); + QCoreApplicationPrivate::theMainThread = threadData->thread.loadRelaxed(); } #endif @@ -379,7 +379,7 @@ unsigned int __stdcall QT_ENSURE_STACK_ALIGNED_FOR_SSE QThreadPrivate::start(voi qt_create_tls(); TlsSetValue(qt_current_thread_data_tls_index, data); - data->threadId.store(reinterpret_cast(quintptr(GetCurrentThreadId()))); + data->threadId.storeRelaxed(reinterpret_cast(quintptr(GetCurrentThreadId()))); QThread::setTerminationEnabled(false); @@ -421,7 +421,7 @@ void QThreadPrivate::finish(void *arg, bool lockAnyway) noexcept QThreadStorageData::finish(tls_data); locker.relock(); - QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher.load(); + QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher.loadRelaxed(); if (eventDispatcher) { d->data->eventDispatcher = 0; locker.unlock(); diff --git a/src/corelib/thread/qthreadstorage.cpp b/src/corelib/thread/qthreadstorage.cpp index 8b82118a5c..fdc484d2d2 100644 --- a/src/corelib/thread/qthreadstorage.cpp +++ b/src/corelib/thread/qthreadstorage.cpp @@ -126,7 +126,7 @@ void **QThreadStorageData::get() const DEBUG_MSG("QThreadStorageData: Returning storage %d, data %p, for thread %p", id, *v, - data->thread.load()); + data->thread.loadRelaxed()); return *v ? v : 0; } @@ -148,7 +148,7 @@ void **QThreadStorageData::set(void *p) DEBUG_MSG("QThreadStorageData: Deleting previous storage %d, data %p, for thread %p", id, value, - data->thread.load()); + data->thread.loadRelaxed()); QMutexLocker locker(&destructorsMutex); DestructorMap *destr = destructors(); @@ -164,7 +164,7 @@ void **QThreadStorageData::set(void *p) // store new data value = p; - DEBUG_MSG("QThreadStorageData: Set storage %d for thread %p to %p", id, data->thread.load(), p); + DEBUG_MSG("QThreadStorageData: Set storage %d for thread %p to %p", id, data->thread.loadRelaxed(), p); return &value; } diff --git a/src/corelib/time/qdatetime.cpp b/src/corelib/time/qdatetime.cpp index a18bc1c498..e34ce71212 100644 --- a/src/corelib/time/qdatetime.cpp +++ b/src/corelib/time/qdatetime.cpp @@ -3181,13 +3181,13 @@ inline void QDateTime::Data::detach() x->m_status = QDateTimePrivate::StatusFlag(data.status & ~QDateTimePrivate::ShortData); x->m_msecs = data.msecs; } else { - if (d->ref.load() == 1) + if (d->ref.loadRelaxed() == 1) return; x = new QDateTimePrivate(*d); } - x->ref.store(1); + x->ref.storeRelaxed(1); if (!wasShort && !d->ref.deref()) delete d; d = x; @@ -3203,7 +3203,7 @@ inline QDateTimePrivate *QDateTime::Data::operator->() { // should we attempt to detach here? Q_ASSERT(!isShort()); - Q_ASSERT(d->ref.load() == 1); + Q_ASSERT(d->ref.loadRelaxed() == 1); return d; } diff --git a/src/corelib/tools/qarraydata.cpp b/src/corelib/tools/qarraydata.cpp index 844cde5563..234a44f6b6 100644 --- a/src/corelib/tools/qarraydata.cpp +++ b/src/corelib/tools/qarraydata.cpp @@ -224,9 +224,9 @@ QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment, & ~(alignment - 1); #if !defined(QT_NO_UNSHARABLE_CONTAINERS) - header->ref.atomic.store(bool(!(options & Unsharable))); + header->ref.atomic.storeRelaxed(bool(!(options & Unsharable))); #else - header->ref.atomic.store(1); + header->ref.atomic.storeRelaxed(1); #endif header->size = 0; header->alloc = capacity; diff --git a/src/corelib/tools/qarraydataops.h b/src/corelib/tools/qarraydataops.h index 7e1b43f9b1..8e19525f07 100644 --- a/src/corelib/tools/qarraydataops.h +++ b/src/corelib/tools/qarraydataops.h @@ -106,7 +106,7 @@ struct QPodArrayOps void destroyAll() // Call from destructors, ONLY! { Q_ASSERT(this->isMutable()); - Q_ASSERT(this->ref.atomic.load() == 0); + Q_ASSERT(this->ref.atomic.loadRelaxed() == 0); // As this is to be called only from destructor, it doesn't need to be // exception safe; size not updated. @@ -204,7 +204,7 @@ struct QGenericArrayOps // As this is to be called only from destructor, it doesn't need to be // exception safe; size not updated. - Q_ASSERT(this->ref.atomic.load() == 0); + Q_ASSERT(this->ref.atomic.loadRelaxed() == 0); const T *const b = this->begin(); const T *i = this->end(); diff --git a/src/corelib/tools/qcollator.cpp b/src/corelib/tools/qcollator.cpp index 6e85027462..958216bde8 100644 --- a/src/corelib/tools/qcollator.cpp +++ b/src/corelib/tools/qcollator.cpp @@ -166,7 +166,7 @@ QCollator &QCollator::operator=(const QCollator &other) */ void QCollator::detach() { - if (d->ref.load() != 1) { + if (d->ref.loadRelaxed() != 1) { QCollatorPrivate *x = new QCollatorPrivate(d->locale); if (!d->ref.deref()) delete d; diff --git a/src/corelib/tools/qcontiguouscache.h b/src/corelib/tools/qcontiguouscache.h index 592e31bfd2..7b74b4f526 100644 --- a/src/corelib/tools/qcontiguouscache.h +++ b/src/corelib/tools/qcontiguouscache.h @@ -100,8 +100,8 @@ public: inline ~QContiguousCache() { if (!d) return; if (!d->ref.deref()) freeData(p); } - inline void detach() { if (d->ref.load() != 1) detach_helper(); } - inline bool isDetached() const { return d->ref.load() == 1; } + inline void detach() { if (d->ref.loadRelaxed() != 1) detach_helper(); } + inline bool isDetached() const { return d->ref.loadRelaxed() == 1; } #if !defined(QT_NO_UNSHARABLE_CONTAINERS) inline void setSharable(bool sharable) { if (!sharable) detach(); d->sharable = sharable; } #endif @@ -176,7 +176,7 @@ void QContiguousCache::detach_helper() union { QContiguousCacheData *d; QContiguousCacheTypedData *p; } x; x.d = allocateData(d->alloc); - x.d->ref.store(1); + x.d->ref.storeRelaxed(1); x.d->count = d->count; x.d->start = d->start; x.d->offset = d->offset; @@ -215,7 +215,7 @@ void QContiguousCache::setCapacity(int asize) detach(); union { QContiguousCacheData *d; QContiguousCacheTypedData *p; } x; x.d = allocateData(asize); - x.d->ref.store(1); + x.d->ref.storeRelaxed(1); x.d->alloc = asize; x.d->count = qMin(d->count, asize); x.d->offset = d->offset + d->count - x.d->count; @@ -251,7 +251,7 @@ void QContiguousCache::setCapacity(int asize) template void QContiguousCache::clear() { - if (d->ref.load() == 1) { + if (d->ref.loadRelaxed() == 1) { if (QTypeInfo::isComplex) { int oldcount = d->count; T * i = p->array + d->start; @@ -267,7 +267,7 @@ void QContiguousCache::clear() } else { union { QContiguousCacheData *d; QContiguousCacheTypedData *p; } x; x.d = allocateData(d->alloc); - x.d->ref.store(1); + x.d->ref.storeRelaxed(1); x.d->alloc = d->alloc; x.d->count = x.d->start = x.d->offset = 0; x.d->sharable = true; @@ -287,7 +287,7 @@ QContiguousCache::QContiguousCache(int cap) { Q_ASSERT(cap >= 0); d = allocateData(cap); - d->ref.store(1); + d->ref.storeRelaxed(1); d->alloc = cap; d->count = d->start = d->offset = 0; d->sharable = true; diff --git a/src/corelib/tools/qfreelist_p.h b/src/corelib/tools/qfreelist_p.h index d72d6e1b4b..dcaf5688dc 100644 --- a/src/corelib/tools/qfreelist_p.h +++ b/src/corelib/tools/qfreelist_p.h @@ -171,7 +171,7 @@ class QFreeList // qDebug("QFreeList: allocating %d elements (%ld bytes) with offset %d", size, size * sizeof(ElementType), offset); ElementType *v = new ElementType[size]; for (int i = 0; i < size; ++i) - v[i].next.store(offset + i + 1); + v[i].next.storeRelaxed(offset + i + 1); return v; } @@ -218,21 +218,21 @@ template inline QFreeList::~QFreeList() { for (int i = 0; i < ConstantsType::BlockCount; ++i) - delete [] _v[i].load(); + delete [] _v[i].loadRelaxed(); } template inline typename QFreeList::ConstReferenceType QFreeList::at(int x) const { const int block = blockfor(x); - return (_v[block].load())[x].t(); + return (_v[block].loadRelaxed())[x].t(); } template inline typename QFreeList::ReferenceType QFreeList::operator[](int x) { const int block = blockfor(x); - return (_v[block].load())[x].t(); + return (_v[block].loadRelaxed())[x].t(); } template @@ -257,7 +257,7 @@ inline int QFreeList::next() } } - newid = v[at].next.load() | (id & ~ConstantsType::IndexMask); + newid = v[at].next.loadRelaxed() | (id & ~ConstantsType::IndexMask); } while (!_next.testAndSetRelease(id, newid)); // qDebug("QFreeList::next(): returning %d (_next now %d, serial %d)", // id & ConstantsType::IndexMask, @@ -271,12 +271,12 @@ inline void QFreeList::release(int id) { int at = id & ConstantsType::IndexMask; const int block = blockfor(at); - ElementType *v = _v[block].load(); + ElementType *v = _v[block].loadRelaxed(); int x, newid; do { x = _next.loadAcquire(); - v[at].next.store(x & ConstantsType::IndexMask); + v[at].next.storeRelaxed(x & ConstantsType::IndexMask); newid = incrementserial(x, id); } while (!_next.testAndSetRelease(x, newid)); diff --git a/src/corelib/tools/qhash.cpp b/src/corelib/tools/qhash.cpp index 6b003fe739..a53d6db997 100644 --- a/src/corelib/tools/qhash.cpp +++ b/src/corelib/tools/qhash.cpp @@ -321,7 +321,7 @@ static QBasicAtomicInt qt_qhash_seed = Q_BASIC_ATOMIC_INITIALIZER(-1); */ static void qt_initialize_qhash_seed() { - if (qt_qhash_seed.load() == -1) { + if (qt_qhash_seed.loadRelaxed() == -1) { int x(qt_create_qhash_seed() & INT_MAX); qt_qhash_seed.testAndSetRelaxed(-1, x); } @@ -340,7 +340,7 @@ static void qt_initialize_qhash_seed() int qGlobalQHashSeed() { qt_initialize_qhash_seed(); - return qt_qhash_seed.load(); + return qt_qhash_seed.loadRelaxed(); } /*! \relates QHash @@ -372,14 +372,14 @@ void qSetGlobalQHashSeed(int newSeed) return; if (newSeed == -1) { int x(qt_create_qhash_seed() & INT_MAX); - qt_qhash_seed.store(x); + qt_qhash_seed.storeRelaxed(x); } else { if (newSeed) { // can't use qWarning here (reentrancy) fprintf(stderr, "qSetGlobalQHashSeed: forced seed value is not 0, cannot guarantee that the " "hashing functions will produce a stable value."); } - qt_qhash_seed.store(newSeed & INT_MAX); + qt_qhash_seed.storeRelaxed(newSeed & INT_MAX); } } @@ -509,7 +509,7 @@ QHashData *QHashData::detach_helper(void (*node_duplicate)(Node *, void *), d->userNumBits = userNumBits; d->numBits = numBits; d->numBuckets = numBuckets; - d->seed = (this == &shared_null) ? uint(qt_qhash_seed.load()) : seed; + d->seed = (this == &shared_null) ? uint(qt_qhash_seed.loadRelaxed()) : seed; d->sharable = true; d->strictAlignment = nodeAlign > 8; d->reserved = 0; diff --git a/src/corelib/tools/qlinkedlist.h b/src/corelib/tools/qlinkedlist.h index 6bc053a4c0..d4e5bca0ed 100644 --- a/src/corelib/tools/qlinkedlist.h +++ b/src/corelib/tools/qlinkedlist.h @@ -340,7 +340,7 @@ void QLinkedList::freeData(QLinkedListData *x) { Node *y = reinterpret_cast(x); Node *i = y->n; - Q_ASSERT(x->ref.atomic.load() == 0); + Q_ASSERT(x->ref.atomic.loadRelaxed() == 0); while (i != y) { Node *n = i; i = i->n; diff --git a/src/corelib/tools/qlocale_p.h b/src/corelib/tools/qlocale_p.h index 59cc33700d..1e3da35a02 100644 --- a/src/corelib/tools/qlocale_p.h +++ b/src/corelib/tools/qlocale_p.h @@ -337,7 +337,7 @@ public: { QLocalePrivate *retval = new QLocalePrivate; retval->m_data = data; - retval->ref.store(0); + retval->ref.storeRelaxed(0); retval->m_numberOptions = numberOptions; return retval; } diff --git a/src/corelib/tools/qrefcount.h b/src/corelib/tools/qrefcount.h index 71adb41f28..2e5388ad9a 100644 --- a/src/corelib/tools/qrefcount.h +++ b/src/corelib/tools/qrefcount.h @@ -52,7 +52,7 @@ class RefCount { public: inline bool ref() noexcept { - int count = atomic.load(); + int count = atomic.loadRelaxed(); #if !defined(QT_NO_UNSHARABLE_CONTAINERS) if (count == 0) // !isSharable return false; @@ -63,7 +63,7 @@ public: } inline bool deref() noexcept { - int count = atomic.load(); + int count = atomic.loadRelaxed(); #if !defined(QT_NO_UNSHARABLE_CONTAINERS) if (count == 0) // !isSharable return false; @@ -86,24 +86,24 @@ public: bool isSharable() const noexcept { // Sharable === Shared ownership. - return atomic.load() != 0; + return atomic.loadRelaxed() != 0; } #endif bool isStatic() const noexcept { // Persistent object, never deleted - return atomic.load() == -1; + return atomic.loadRelaxed() == -1; } bool isShared() const noexcept { - int count = atomic.load(); + int count = atomic.loadRelaxed(); return (count != 1) && (count != 0); } - void initializeOwned() noexcept { atomic.store(1); } - void initializeUnsharable() noexcept { atomic.store(0); } + void initializeOwned() noexcept { atomic.storeRelaxed(1); } + void initializeUnsharable() noexcept { atomic.storeRelaxed(0); } QBasicAtomicInt atomic; }; diff --git a/src/corelib/tools/qregexp.cpp b/src/corelib/tools/qregexp.cpp index e64610b93b..128df84053 100644 --- a/src/corelib/tools/qregexp.cpp +++ b/src/corelib/tools/qregexp.cpp @@ -1707,7 +1707,7 @@ void QRegExpEngine::dump() const void QRegExpEngine::setup() { - ref.store(1); + ref.storeRelaxed(1); #ifndef QT_NO_REGEXP_CAPTURE f.resize(32); nf = 0; diff --git a/src/corelib/tools/qshareddata.h b/src/corelib/tools/qshareddata.h index 816583c766..ab54c76720 100644 --- a/src/corelib/tools/qshareddata.h +++ b/src/corelib/tools/qshareddata.h @@ -75,7 +75,7 @@ public: typedef T Type; typedef T *pointer; - inline void detach() { if (d && d->ref.load() != 1) detach_helper(); } + inline void detach() { if (d && d->ref.loadRelaxed() != 1) detach_helper(); } inline T &operator*() { detach(); return *d; } inline const T &operator*() const { return *d; } inline T *operator->() { detach(); return d; } @@ -163,7 +163,7 @@ public: inline const T *constData() const { return d; } inline T *take() { T *x = d; d = nullptr; return x; } - inline void detach() { if (d && d->ref.load() != 1) detach_helper(); } + inline void detach() { if (d && d->ref.loadRelaxed() != 1) detach_helper(); } inline void reset() { diff --git a/src/corelib/tools/qsharedpointer.cpp b/src/corelib/tools/qsharedpointer.cpp index e4de5a2ba9..f185d2f23f 100644 --- a/src/corelib/tools/qsharedpointer.cpp +++ b/src/corelib/tools/qsharedpointer.cpp @@ -1380,7 +1380,7 @@ void QtSharedPointer::ExternalRefCountData::setQObjectShared(const QObject *, bo */ void QtSharedPointer::ExternalRefCountData::checkQObjectShared(const QObject *) { - if (strongref.load() < 0) + if (strongref.loadRelaxed() < 0) qWarning("QSharedPointer: cannot create a QSharedPointer from a QObject-tracking QWeakPointer"); } @@ -1390,7 +1390,7 @@ QtSharedPointer::ExternalRefCountData *QtSharedPointer::ExternalRefCountData::ge QObjectPrivate *d = QObjectPrivate::get(const_cast(obj)); Q_ASSERT_X(!d->wasDeleted, "QWeakPointer", "Detected QWeakPointer creation in a QObject being deleted"); - ExternalRefCountData *that = d->sharedRefcount.load(); + ExternalRefCountData *that = d->sharedRefcount.loadRelaxed(); if (that) { that->weakref.ref(); return that; @@ -1398,8 +1398,8 @@ QtSharedPointer::ExternalRefCountData *QtSharedPointer::ExternalRefCountData::ge // we can create the refcount data because it doesn't exist ExternalRefCountData *x = new ExternalRefCountData(Qt::Uninitialized); - x->strongref.store(-1); - x->weakref.store(2); // the QWeakPointer that called us plus the QObject itself + x->strongref.storeRelaxed(-1); + x->weakref.storeRelaxed(2); // the QWeakPointer that called us plus the QObject itself ExternalRefCountData *ret; if (d->sharedRefcount.testAndSetOrdered(nullptr, x, ret)) { // ought to be release+acquire; this is acq_rel+acquire @@ -1407,7 +1407,7 @@ QtSharedPointer::ExternalRefCountData *QtSharedPointer::ExternalRefCountData::ge } else { // ~ExternalRefCountData has a Q_ASSERT, so we use this trick to // only execute this if Q_ASSERTs are enabled - Q_ASSERT((x->weakref.store(0), true)); + Q_ASSERT((x->weakref.storeRelaxed(0), true)); delete x; ret->weakref.ref(); } diff --git a/src/corelib/tools/qsharedpointer_impl.h b/src/corelib/tools/qsharedpointer_impl.h index f352e433c5..198cc58c38 100644 --- a/src/corelib/tools/qsharedpointer_impl.h +++ b/src/corelib/tools/qsharedpointer_impl.h @@ -154,11 +154,11 @@ namespace QtSharedPointer { inline ExternalRefCountData(DestroyerFn d) : destroyer(d) { - strongref.store(1); - weakref.store(1); + strongref.storeRelaxed(1); + weakref.storeRelaxed(1); } inline ExternalRefCountData(Qt::Initialization) { } - ~ExternalRefCountData() { Q_ASSERT(!weakref.load()); Q_ASSERT(strongref.load() <= 0); } + ~ExternalRefCountData() { Q_ASSERT(!weakref.loadRelaxed()); Q_ASSERT(strongref.loadRelaxed() <= 0); } void destroy() { destroyer(this); } @@ -522,12 +522,12 @@ public: if (o) { // increase the strongref, but never up from zero // or less (-1 is used by QWeakPointer on untracked QObject) - int tmp = o->strongref.load(); + int tmp = o->strongref.loadRelaxed(); while (tmp > 0) { // try to increment from "tmp" to "tmp + 1" if (o->strongref.testAndSetRelaxed(tmp, tmp + 1)) break; // succeeded - tmp = o->strongref.load(); // failed, try again + tmp = o->strongref.loadRelaxed(); // failed, try again } if (tmp > 0) { @@ -540,7 +540,7 @@ public: qSwap(d, o); qSwap(this->value, actual); - if (!d || d->strongref.load() == 0) + if (!d || d->strongref.loadRelaxed() == 0) this->value = nullptr; // dereference saved data @@ -566,7 +566,7 @@ public: typedef const value_type &const_reference; typedef qptrdiff difference_type; - bool isNull() const noexcept { return d == nullptr || d->strongref.load() == 0 || value == nullptr; } + bool isNull() const noexcept { return d == nullptr || d->strongref.loadRelaxed() == 0 || value == nullptr; } operator RestrictedBool() const noexcept { return isNull() ? nullptr : &QWeakPointer::value; } bool operator !() const noexcept { return isNull(); } @@ -709,7 +709,7 @@ public: // a weak pointer's data but the weak pointer itself inline T *internalData() const noexcept { - return d == nullptr || d->strongref.load() == 0 ? nullptr : value; + return d == nullptr || d->strongref.loadRelaxed() == 0 ? nullptr : value; } Data *d; diff --git a/src/corelib/tools/qsimd.cpp b/src/corelib/tools/qsimd.cpp index ddd715f745..ecf1822e42 100644 --- a/src/corelib/tools/qsimd.cpp +++ b/src/corelib/tools/qsimd.cpp @@ -563,9 +563,9 @@ quint64 qDetectCpuFeatures() features_string + features_indices[qCountTrailingZeroBits(missing)]); } - qt_cpu_features[0].store(f | quint32(QSimdInitialized)); + qt_cpu_features[0].storeRelaxed(f | quint32(QSimdInitialized)); #ifndef Q_ATOMIC_INT64_IS_SUPPORTED - qt_cpu_features[1].store(f >> 32); + qt_cpu_features[1].storeRelaxed(f >> 32); #endif return f; } diff --git a/src/corelib/tools/qsimd_p.h b/src/corelib/tools/qsimd_p.h index c36e1e484f..db2f546651 100644 --- a/src/corelib/tools/qsimd_p.h +++ b/src/corelib/tools/qsimd_p.h @@ -348,9 +348,9 @@ Q_CORE_EXPORT quint64 qDetectCpuFeatures(); static inline quint64 qCpuFeatures() { - quint64 features = qt_cpu_features[0].load(); + quint64 features = qt_cpu_features[0].loadRelaxed(); #ifndef Q_ATOMIC_INT64_IS_SUPPORTED - features |= quint64(qt_cpu_features[1].load()) << 32; + features |= quint64(qt_cpu_features[1].loadRelaxed()) << 32; #endif if (Q_UNLIKELY(features == 0)) { features = qDetectCpuFeatures(); diff --git a/src/dbus/qdbusargument.cpp b/src/dbus/qdbusargument.cpp index 2d1373006d..764bc24165 100644 --- a/src/dbus/qdbusargument.cpp +++ b/src/dbus/qdbusargument.cpp @@ -111,7 +111,7 @@ bool QDBusArgumentPrivate::checkWrite(QDBusArgumentPrivate *&d) if (!d->marshaller()->ok) return false; - if (d->message && d->ref.load() != 1) { + if (d->message && d->ref.loadRelaxed() != 1) { QDBusMarshaller *dd = new QDBusMarshaller(d->capabilities); dd->message = q_dbus_message_copy(d->message); q_dbus_message_iter_init_append(dd->message, &dd->iterator); @@ -152,7 +152,7 @@ bool QDBusArgumentPrivate::checkReadAndDetach(QDBusArgumentPrivate *&d) if (!checkRead(d)) return false; // don't bother - if (d->ref.load() == 1) + if (d->ref.loadRelaxed() == 1) return true; // no need to detach QDBusDemarshaller *dd = new QDBusDemarshaller(d->capabilities); diff --git a/src/dbus/qdbusintegrator.cpp b/src/dbus/qdbusintegrator.cpp index 640acc2425..af16940d4f 100644 --- a/src/dbus/qdbusintegrator.cpp +++ b/src/dbus/qdbusintegrator.cpp @@ -534,7 +534,7 @@ qDBusSignalFilter(DBusConnection *connection, DBusMessage *message, void *data) bool QDBusConnectionPrivate::handleMessage(const QDBusMessage &amsg) { - if (!ref.load()) + if (!ref.loadRelaxed()) return false; // local message are always delivered, regardless of filtering @@ -1077,7 +1077,7 @@ QDBusConnectionPrivate::~QDBusConnectionPrivate() if (lastMode == ClientMode || lastMode == PeerMode) { // the bus service object holds a reference back to us; // we need to destroy it before we finish destroying ourselves - Q_ASSERT(ref.load() == 0); + Q_ASSERT(ref.loadRelaxed() == 0); QObject *obj = (QObject *)busService; if (obj) { disconnect(obj, nullptr, this, nullptr); @@ -2126,11 +2126,11 @@ QDBusPendingCallPrivate *QDBusConnectionPrivate::sendWithReplyAsync(const QDBusM if ((receiver && returnMethod) || errorMethod) { // no one waiting, will delete pcall in processFinishedCall() - pcall->ref.store(1); + pcall->ref.storeRelaxed(1); } else { // set double ref to prevent race between processFinishedCall() and ref counting // by QDBusPendingCall::QExplicitlySharedDataPointer - pcall->ref.store(2); + pcall->ref.storeRelaxed(2); } if (isLoopback) { diff --git a/src/dbus/qdbusmetatype.cpp b/src/dbus/qdbusmetatype.cpp index 0729a3cbbb..58ce4f8930 100644 --- a/src/dbus/qdbusmetatype.cpp +++ b/src/dbus/qdbusmetatype.cpp @@ -93,7 +93,7 @@ void QDBusMetaTypeId::init() // reentrancy is not a problem since everything else is locked on their own // set the guard variable at the end - if (!initialized.load()) { + if (!initialized.loadRelaxed()) { // register our types with Qt Core (calling qMetaTypeId() does this implicitly) (void)message(); (void)argument(); @@ -145,7 +145,7 @@ void QDBusMetaTypeId::init() qDBusRegisterMetaType >(); #endif - initialized.store(true); + initialized.storeRelaxed(true); } } diff --git a/src/dbus/qdbuspendingcall.cpp b/src/dbus/qdbuspendingcall.cpp index 4d0131afff..b51fae9b65 100644 --- a/src/dbus/qdbuspendingcall.cpp +++ b/src/dbus/qdbuspendingcall.cpp @@ -473,7 +473,7 @@ QDBusPendingCall QDBusPendingCall::fromCompletedCall(const QDBusMessage &msg) msg.type() == QDBusMessage::ReplyMessage) { d = new QDBusPendingCallPrivate(QDBusMessage(), 0); d->replyMessage = msg; - d->ref.store(1); + d->ref.storeRelaxed(1); } return QDBusPendingCall(d); diff --git a/src/dbus/qdbusserver.cpp b/src/dbus/qdbusserver.cpp index a2dfb86164..2899fb7bea 100644 --- a/src/dbus/qdbusserver.cpp +++ b/src/dbus/qdbusserver.cpp @@ -117,7 +117,7 @@ QDBusServer::~QDBusServer() d->serverConnectionNames.clear(); } d->serverObject = nullptr; - d->ref.store(0); + d->ref.storeRelaxed(0); d->deleteLater(); } diff --git a/src/dbus/qdbusunixfiledescriptor.cpp b/src/dbus/qdbusunixfiledescriptor.cpp index 459cfc6c66..73d1db2680 100644 --- a/src/dbus/qdbusunixfiledescriptor.cpp +++ b/src/dbus/qdbusunixfiledescriptor.cpp @@ -208,7 +208,7 @@ QDBusUnixFileDescriptor::~QDBusUnixFileDescriptor() */ bool QDBusUnixFileDescriptor::isValid() const { - return d ? d->fd.load() != -1 : false; + return d ? d->fd.loadRelaxed() != -1 : false; } /*! @@ -226,7 +226,7 @@ bool QDBusUnixFileDescriptor::isValid() const */ int QDBusUnixFileDescriptor::fileDescriptor() const { - return d ? d->fd.load() : -1; + return d ? d->fd.loadRelaxed() : -1; } // actual implementation @@ -283,12 +283,12 @@ void QDBusUnixFileDescriptor::giveFileDescriptor(int fileDescriptor) else d = new QDBusUnixFileDescriptorPrivate; - const int fdl = d->fd.load(); + const int fdl = d->fd.loadRelaxed(); if (fdl != -1) qt_safe_close(fdl); if (fileDescriptor != -1) - d->fd.store(fileDescriptor); + d->fd.storeRelaxed(fileDescriptor); } /*! @@ -309,7 +309,7 @@ int QDBusUnixFileDescriptor::takeFileDescriptor() QDBusUnixFileDescriptorPrivate::~QDBusUnixFileDescriptorPrivate() { - const int fdl = fd.load(); + const int fdl = fd.loadRelaxed(); if (fdl != -1) qt_safe_close(fdl); } diff --git a/src/gui/image/qicon.cpp b/src/gui/image/qicon.cpp index 1d1b6d3c49..df8220a0c6 100644 --- a/src/gui/image/qicon.cpp +++ b/src/gui/image/qicon.cpp @@ -987,7 +987,7 @@ bool QIcon::isNull() const */ bool QIcon::isDetached() const { - return !d || d->ref.load() == 1; + return !d || d->ref.loadRelaxed() == 1; } /*! \internal @@ -1000,7 +1000,7 @@ void QIcon::detach() delete d; d = 0; return; - } else if (d->ref.load() != 1) { + } else if (d->ref.loadRelaxed() != 1) { QIconPrivate *x = new QIconPrivate(d->engine->clone()); if (!d->ref.deref()) delete d; diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index 917dde3b0f..61d32b0dec 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -1082,10 +1082,10 @@ QImage::operator QVariant() const void QImage::detach() { if (d) { - if (d->is_cached && d->ref.load() == 1) + if (d->is_cached && d->ref.loadRelaxed() == 1) QImagePixmapCleanupHooks::executeImageHooks(cacheKey()); - if (d->ref.load() != 1 || d->ro_data) + if (d->ref.loadRelaxed() != 1 || d->ro_data) *this = copy(); if (d) @@ -4422,7 +4422,7 @@ qint64 QImage::cacheKey() const bool QImage::isDetached() const { - return d && d->ref.load() == 1; + return d && d->ref.loadRelaxed() == 1; } @@ -5087,7 +5087,7 @@ bool QImageData::convertInPlace(QImage::Format newFormat, Qt::ImageConversionFla return true; // No in-place conversion if we have to detach - if (ref.load() > 1 || !own_data) + if (ref.loadRelaxed() > 1 || !own_data) return false; InPlace_Image_Converter converter = qimage_inplace_converter_map[format][newFormat]; diff --git a/src/gui/image/qpicture.cpp b/src/gui/image/qpicture.cpp index fe1e4d2c9b..8548f1857e 100644 --- a/src/gui/image/qpicture.cpp +++ b/src/gui/image/qpicture.cpp @@ -235,7 +235,7 @@ void QPicture::detach() bool QPicture::isDetached() const { - return d_func()->ref.load() == 1; + return d_func()->ref.loadRelaxed() == 1; } /*! diff --git a/src/gui/image/qpixmap.cpp b/src/gui/image/qpixmap.cpp index 399ad7453d..b6e41f16a5 100644 --- a/src/gui/image/qpixmap.cpp +++ b/src/gui/image/qpixmap.cpp @@ -262,7 +262,7 @@ QPixmap::QPixmap(const char * const xpm[]) QPixmap::~QPixmap() { - Q_ASSERT(!data || data->ref.load() >= 1); // Catch if ref-counting changes again + Q_ASSERT(!data || data->ref.loadRelaxed() >= 1); // Catch if ref-counting changes again } /*! @@ -910,7 +910,7 @@ void QPixmap::fill(const QColor &color) return; } - if (data->ref.load() == 1) { + if (data->ref.loadRelaxed() == 1) { // detach() will also remove this pixmap from caches, so // it has to be called even when ref == 1. detach(); @@ -1053,7 +1053,7 @@ QDataStream &operator>>(QDataStream &stream, QPixmap &pixmap) bool QPixmap::isDetached() const { - return data && data->ref.load() == 1; + return data && data->ref.loadRelaxed() == 1; } /*! @@ -1523,10 +1523,10 @@ void QPixmap::detach() rasterData->image.detach(); } - if (data->is_cached && data->ref.load() == 1) + if (data->is_cached && data->ref.loadRelaxed() == 1) QImagePixmapCleanupHooks::executePlatformPixmapModificationHooks(data.data()); - if (data->ref.load() != 1) { + if (data->ref.loadRelaxed() != 1) { *this = copy(); } ++data->detach_no; diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index f327c2d43e..e1d685b1b0 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -4921,7 +4921,7 @@ QVector QTouchEvent::TouchPoint::rawScreenPositions() const /*! \internal */ void QTouchEvent::TouchPoint::setId(int id) { - if (d->ref.load() != 1) + if (d->ref.loadRelaxed() != 1) d = d->detach(); d->id = id; } @@ -4929,7 +4929,7 @@ void QTouchEvent::TouchPoint::setId(int id) /*! \internal */ void QTouchEvent::TouchPoint::setUniqueId(qint64 uid) { - if (d->ref.load() != 1) + if (d->ref.loadRelaxed() != 1) d = d->detach(); d->uniqueId = QPointingDeviceUniqueId::fromNumericId(uid); } @@ -4937,7 +4937,7 @@ void QTouchEvent::TouchPoint::setUniqueId(qint64 uid) /*! \internal */ void QTouchEvent::TouchPoint::setState(Qt::TouchPointStates state) { - if (d->ref.load() != 1) + if (d->ref.loadRelaxed() != 1) d = d->detach(); d->state = state; } @@ -4945,7 +4945,7 @@ void QTouchEvent::TouchPoint::setState(Qt::TouchPointStates state) /*! \internal */ void QTouchEvent::TouchPoint::setPos(const QPointF &pos) { - if (d->ref.load() != 1) + if (d->ref.loadRelaxed() != 1) d = d->detach(); d->pos = pos; } @@ -4953,7 +4953,7 @@ void QTouchEvent::TouchPoint::setPos(const QPointF &pos) /*! \internal */ void QTouchEvent::TouchPoint::setScenePos(const QPointF &scenePos) { - if (d->ref.load() != 1) + if (d->ref.loadRelaxed() != 1) d = d->detach(); d->scenePos = scenePos; } @@ -4961,7 +4961,7 @@ void QTouchEvent::TouchPoint::setScenePos(const QPointF &scenePos) /*! \internal */ void QTouchEvent::TouchPoint::setScreenPos(const QPointF &screenPos) { - if (d->ref.load() != 1) + if (d->ref.loadRelaxed() != 1) d = d->detach(); d->screenPos = screenPos; } @@ -4969,7 +4969,7 @@ void QTouchEvent::TouchPoint::setScreenPos(const QPointF &screenPos) /*! \internal */ void QTouchEvent::TouchPoint::setNormalizedPos(const QPointF &normalizedPos) { - if (d->ref.load() != 1) + if (d->ref.loadRelaxed() != 1) d = d->detach(); d->normalizedPos = normalizedPos; } @@ -4977,7 +4977,7 @@ void QTouchEvent::TouchPoint::setNormalizedPos(const QPointF &normalizedPos) /*! \internal */ void QTouchEvent::TouchPoint::setStartPos(const QPointF &startPos) { - if (d->ref.load() != 1) + if (d->ref.loadRelaxed() != 1) d = d->detach(); d->startPos = startPos; } @@ -4985,7 +4985,7 @@ void QTouchEvent::TouchPoint::setStartPos(const QPointF &startPos) /*! \internal */ void QTouchEvent::TouchPoint::setStartScenePos(const QPointF &startScenePos) { - if (d->ref.load() != 1) + if (d->ref.loadRelaxed() != 1) d = d->detach(); d->startScenePos = startScenePos; } @@ -4993,7 +4993,7 @@ void QTouchEvent::TouchPoint::setStartScenePos(const QPointF &startScenePos) /*! \internal */ void QTouchEvent::TouchPoint::setStartScreenPos(const QPointF &startScreenPos) { - if (d->ref.load() != 1) + if (d->ref.loadRelaxed() != 1) d = d->detach(); d->startScreenPos = startScreenPos; } @@ -5001,7 +5001,7 @@ void QTouchEvent::TouchPoint::setStartScreenPos(const QPointF &startScreenPos) /*! \internal */ void QTouchEvent::TouchPoint::setStartNormalizedPos(const QPointF &startNormalizedPos) { - if (d->ref.load() != 1) + if (d->ref.loadRelaxed() != 1) d = d->detach(); d->startNormalizedPos = startNormalizedPos; } @@ -5009,7 +5009,7 @@ void QTouchEvent::TouchPoint::setStartNormalizedPos(const QPointF &startNormaliz /*! \internal */ void QTouchEvent::TouchPoint::setLastPos(const QPointF &lastPos) { - if (d->ref.load() != 1) + if (d->ref.loadRelaxed() != 1) d = d->detach(); d->lastPos = lastPos; } @@ -5017,7 +5017,7 @@ void QTouchEvent::TouchPoint::setLastPos(const QPointF &lastPos) /*! \internal */ void QTouchEvent::TouchPoint::setLastScenePos(const QPointF &lastScenePos) { - if (d->ref.load() != 1) + if (d->ref.loadRelaxed() != 1) d = d->detach(); d->lastScenePos = lastScenePos; } @@ -5025,7 +5025,7 @@ void QTouchEvent::TouchPoint::setLastScenePos(const QPointF &lastScenePos) /*! \internal */ void QTouchEvent::TouchPoint::setLastScreenPos(const QPointF &lastScreenPos) { - if (d->ref.load() != 1) + if (d->ref.loadRelaxed() != 1) d = d->detach(); d->lastScreenPos = lastScreenPos; } @@ -5033,7 +5033,7 @@ void QTouchEvent::TouchPoint::setLastScreenPos(const QPointF &lastScreenPos) /*! \internal */ void QTouchEvent::TouchPoint::setLastNormalizedPos(const QPointF &lastNormalizedPos) { - if (d->ref.load() != 1) + if (d->ref.loadRelaxed() != 1) d = d->detach(); d->lastNormalizedPos = lastNormalizedPos; } @@ -5044,7 +5044,7 @@ void QTouchEvent::TouchPoint::setLastNormalizedPos(const QPointF &lastNormalized */ void QTouchEvent::TouchPoint::setRect(const QRectF &rect) { - if (d->ref.load() != 1) + if (d->ref.loadRelaxed() != 1) d = d->detach(); d->pos = rect.center(); d->ellipseDiameters = rect.size(); @@ -5055,7 +5055,7 @@ void QTouchEvent::TouchPoint::setRect(const QRectF &rect) */ void QTouchEvent::TouchPoint::setSceneRect(const QRectF &sceneRect) { - if (d->ref.load() != 1) + if (d->ref.loadRelaxed() != 1) d = d->detach(); d->scenePos = sceneRect.center(); d->ellipseDiameters = sceneRect.size(); @@ -5066,7 +5066,7 @@ void QTouchEvent::TouchPoint::setSceneRect(const QRectF &sceneRect) */ void QTouchEvent::TouchPoint::setScreenRect(const QRectF &screenRect) { - if (d->ref.load() != 1) + if (d->ref.loadRelaxed() != 1) d = d->detach(); d->screenPos = screenRect.center(); d->ellipseDiameters = screenRect.size(); @@ -5075,7 +5075,7 @@ void QTouchEvent::TouchPoint::setScreenRect(const QRectF &screenRect) /*! \internal */ void QTouchEvent::TouchPoint::setPressure(qreal pressure) { - if (d->ref.load() != 1) + if (d->ref.loadRelaxed() != 1) d = d->detach(); d->pressure = pressure; } @@ -5083,7 +5083,7 @@ void QTouchEvent::TouchPoint::setPressure(qreal pressure) /*! \internal */ void QTouchEvent::TouchPoint::setRotation(qreal angle) { - if (d->ref.load() != 1) + if (d->ref.loadRelaxed() != 1) d = d->detach(); d->rotation = angle; } @@ -5091,7 +5091,7 @@ void QTouchEvent::TouchPoint::setRotation(qreal angle) /*! \internal */ void QTouchEvent::TouchPoint::setEllipseDiameters(const QSizeF &dia) { - if (d->ref.load() != 1) + if (d->ref.loadRelaxed() != 1) d = d->detach(); d->ellipseDiameters = dia; } @@ -5099,7 +5099,7 @@ void QTouchEvent::TouchPoint::setEllipseDiameters(const QSizeF &dia) /*! \internal */ void QTouchEvent::TouchPoint::setVelocity(const QVector2D &v) { - if (d->ref.load() != 1) + if (d->ref.loadRelaxed() != 1) d = d->detach(); d->velocity = v; } @@ -5107,7 +5107,7 @@ void QTouchEvent::TouchPoint::setVelocity(const QVector2D &v) /*! \internal */ void QTouchEvent::TouchPoint::setRawScreenPositions(const QVector &positions) { - if (d->ref.load() != 1) + if (d->ref.loadRelaxed() != 1) d = d->detach(); d->rawScreenPositions = positions; } @@ -5117,7 +5117,7 @@ void QTouchEvent::TouchPoint::setRawScreenPositions(const QVector &posi */ void QTouchEvent::TouchPoint::setFlags(InfoFlags flags) { - if (d->ref.load() != 1) + if (d->ref.loadRelaxed() != 1) d = d->detach(); d->flags = flags; } diff --git a/src/gui/kernel/qevent_p.h b/src/gui/kernel/qevent_p.h index 7df4a1e25b..c2d8bd72b9 100644 --- a/src/gui/kernel/qevent_p.h +++ b/src/gui/kernel/qevent_p.h @@ -73,7 +73,7 @@ public: inline QTouchEventTouchPointPrivate *detach() { QTouchEventTouchPointPrivate *d = new QTouchEventTouchPointPrivate(*this); - d->ref.store(1); + d->ref.storeRelaxed(1); if (!this->ref.deref()) delete this; return d; diff --git a/src/gui/kernel/qguiapplication_p.h b/src/gui/kernel/qguiapplication_p.h index 6902a99eca..2ecde0354a 100644 --- a/src/gui/kernel/qguiapplication_p.h +++ b/src/gui/kernel/qguiapplication_p.h @@ -114,7 +114,7 @@ public: static QAbstractEventDispatcher *qt_qpa_core_dispatcher() { if (QCoreApplication::instance()) - return QCoreApplication::instance()->d_func()->threadData->eventDispatcher.load(); + return QCoreApplication::instance()->d_func()->threadData->eventDispatcher.loadRelaxed(); else return nullptr; } diff --git a/src/gui/kernel/qkeysequence.cpp b/src/gui/kernel/qkeysequence.cpp index 8688bb8403..2a86b340af 100644 --- a/src/gui/kernel/qkeysequence.cpp +++ b/src/gui/kernel/qkeysequence.cpp @@ -1518,7 +1518,7 @@ bool QKeySequence::operator< (const QKeySequence &other) const */ bool QKeySequence::isDetached() const { - return d->ref.load() == 1; + return d->ref.loadRelaxed() == 1; } /*! diff --git a/src/gui/kernel/qopenglcontext.cpp b/src/gui/kernel/qopenglcontext.cpp index 5bc6e27eb2..a3b2ea5f86 100644 --- a/src/gui/kernel/qopenglcontext.cpp +++ b/src/gui/kernel/qopenglcontext.cpp @@ -1634,7 +1634,7 @@ QOpenGLMultiGroupSharedResource::~QOpenGLMultiGroupSharedResource() active.deref(); } #ifndef QT_NO_DEBUG - if (active.load() != 0) { + if (active.loadRelaxed() != 0) { qWarning("QtGui: Resources are still available at program shutdown.\n" " This is possibly caused by a leaked QOpenGLWidget, \n" " QOpenGLFramebufferObject or QOpenGLPixelBuffer."); diff --git a/src/gui/kernel/qpalette.cpp b/src/gui/kernel/qpalette.cpp index 90471e15e1..61dccd77ac 100644 --- a/src/gui/kernel/qpalette.cpp +++ b/src/gui/kernel/qpalette.cpp @@ -830,7 +830,7 @@ bool QPalette::isBrushSet(ColorGroup cg, ColorRole cr) const */ void QPalette::detach() { - if (d->ref.load() != 1) { + if (d->ref.loadRelaxed() != 1) { QPalettePrivate *x = new QPalettePrivate; for(int grp = 0; grp < (int)NColorGroups; grp++) { for(int role = 0; role < (int)NColorRoles; role++) diff --git a/src/gui/kernel/qsurfaceformat.cpp b/src/gui/kernel/qsurfaceformat.cpp index 1a814ec21f..2e2738ec81 100644 --- a/src/gui/kernel/qsurfaceformat.cpp +++ b/src/gui/kernel/qsurfaceformat.cpp @@ -246,7 +246,7 @@ QSurfaceFormat::QSurfaceFormat(QSurfaceFormat::FormatOptions options) : */ void QSurfaceFormat::detach() { - if (d->ref.load() != 1) { + if (d->ref.loadRelaxed() != 1) { QSurfaceFormatPrivate *newd = new QSurfaceFormatPrivate(d); if (!d->ref.deref()) delete d; diff --git a/src/gui/kernel/qwindowsysteminterface.cpp b/src/gui/kernel/qwindowsysteminterface.cpp index 6f3edb10b4..4b8cb3646a 100644 --- a/src/gui/kernel/qwindowsysteminterface.cpp +++ b/src/gui/kernel/qwindowsysteminterface.cpp @@ -1115,7 +1115,7 @@ bool QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ProcessEventsFl } else { sendWindowSystemEvents(flags); } - return QWindowSystemInterfacePrivate::eventAccepted.load() > 0; + return QWindowSystemInterfacePrivate::eventAccepted.loadRelaxed() > 0; } void QWindowSystemInterface::deferredFlushWindowSystemEvents(QEventLoop::ProcessEventsFlags flags) @@ -1156,7 +1156,7 @@ bool QWindowSystemInterface::sendWindowSystemEvents(QEventLoop::ProcessEventsFla // (excluding flush events). This state can then be // returned by flushWindowSystemEvents(). if (event->type != QWindowSystemInterfacePrivate::FlushEvents) - QWindowSystemInterfacePrivate::eventAccepted.store(event->eventAccepted); + QWindowSystemInterfacePrivate::eventAccepted.storeRelaxed(event->eventAccepted); delete event; } diff --git a/src/gui/opengl/qopenglframebufferobject.cpp b/src/gui/opengl/qopenglframebufferobject.cpp index e7631b09ce..5d30891565 100644 --- a/src/gui/opengl/qopenglframebufferobject.cpp +++ b/src/gui/opengl/qopenglframebufferobject.cpp @@ -183,7 +183,7 @@ QT_BEGIN_NAMESPACE */ void QOpenGLFramebufferObjectFormat::detach() { - if (d->ref.load() != 1) { + if (d->ref.loadRelaxed() != 1) { QOpenGLFramebufferObjectFormatPrivate *newd = new QOpenGLFramebufferObjectFormatPrivate(d); if (!d->ref.deref()) diff --git a/src/gui/opengl/qopenglfunctions_1_0.cpp b/src/gui/opengl/qopenglfunctions_1_0.cpp index 4235c9a339..f017c68fd9 100644 --- a/src/gui/opengl/qopenglfunctions_1_0.cpp +++ b/src/gui/opengl/qopenglfunctions_1_0.cpp @@ -76,11 +76,11 @@ QOpenGLFunctions_1_0::~QOpenGLFunctions_1_0() { if (d_1_0_Core) { d_1_0_Core->refs.deref(); - Q_ASSERT(d_1_0_Core->refs.load()); + Q_ASSERT(d_1_0_Core->refs.loadRelaxed()); } if (d_1_0_Deprecated) { d_1_0_Deprecated->refs.deref(); - Q_ASSERT(d_1_0_Deprecated->refs.load()); + Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed()); } } diff --git a/src/gui/opengl/qopenglfunctions_1_1.cpp b/src/gui/opengl/qopenglfunctions_1_1.cpp index 7d09bb40c1..a819d499f8 100644 --- a/src/gui/opengl/qopenglfunctions_1_1.cpp +++ b/src/gui/opengl/qopenglfunctions_1_1.cpp @@ -78,19 +78,19 @@ QOpenGLFunctions_1_1::~QOpenGLFunctions_1_1() { if (d_1_0_Core) { d_1_0_Core->refs.deref(); - Q_ASSERT(d_1_0_Core->refs.load()); + Q_ASSERT(d_1_0_Core->refs.loadRelaxed()); } if (d_1_1_Core) { d_1_1_Core->refs.deref(); - Q_ASSERT(d_1_1_Core->refs.load()); + Q_ASSERT(d_1_1_Core->refs.loadRelaxed()); } if (d_1_0_Deprecated) { d_1_0_Deprecated->refs.deref(); - Q_ASSERT(d_1_0_Deprecated->refs.load()); + Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed()); } if (d_1_1_Deprecated) { d_1_1_Deprecated->refs.deref(); - Q_ASSERT(d_1_1_Deprecated->refs.load()); + Q_ASSERT(d_1_1_Deprecated->refs.loadRelaxed()); } } diff --git a/src/gui/opengl/qopenglfunctions_1_2.cpp b/src/gui/opengl/qopenglfunctions_1_2.cpp index 94a9d64660..61db2b4e0f 100644 --- a/src/gui/opengl/qopenglfunctions_1_2.cpp +++ b/src/gui/opengl/qopenglfunctions_1_2.cpp @@ -80,27 +80,27 @@ QOpenGLFunctions_1_2::~QOpenGLFunctions_1_2() { if (d_1_0_Core) { d_1_0_Core->refs.deref(); - Q_ASSERT(d_1_0_Core->refs.load()); + Q_ASSERT(d_1_0_Core->refs.loadRelaxed()); } if (d_1_1_Core) { d_1_1_Core->refs.deref(); - Q_ASSERT(d_1_1_Core->refs.load()); + Q_ASSERT(d_1_1_Core->refs.loadRelaxed()); } if (d_1_2_Core) { d_1_2_Core->refs.deref(); - Q_ASSERT(d_1_2_Core->refs.load()); + Q_ASSERT(d_1_2_Core->refs.loadRelaxed()); } if (d_1_0_Deprecated) { d_1_0_Deprecated->refs.deref(); - Q_ASSERT(d_1_0_Deprecated->refs.load()); + Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed()); } if (d_1_1_Deprecated) { d_1_1_Deprecated->refs.deref(); - Q_ASSERT(d_1_1_Deprecated->refs.load()); + Q_ASSERT(d_1_1_Deprecated->refs.loadRelaxed()); } if (d_1_2_Deprecated) { d_1_2_Deprecated->refs.deref(); - Q_ASSERT(d_1_2_Deprecated->refs.load()); + Q_ASSERT(d_1_2_Deprecated->refs.loadRelaxed()); } } diff --git a/src/gui/opengl/qopenglfunctions_1_3.cpp b/src/gui/opengl/qopenglfunctions_1_3.cpp index 972ef9ff70..acc223ea74 100644 --- a/src/gui/opengl/qopenglfunctions_1_3.cpp +++ b/src/gui/opengl/qopenglfunctions_1_3.cpp @@ -82,35 +82,35 @@ QOpenGLFunctions_1_3::~QOpenGLFunctions_1_3() { if (d_1_0_Core) { d_1_0_Core->refs.deref(); - Q_ASSERT(d_1_0_Core->refs.load()); + Q_ASSERT(d_1_0_Core->refs.loadRelaxed()); } if (d_1_1_Core) { d_1_1_Core->refs.deref(); - Q_ASSERT(d_1_1_Core->refs.load()); + Q_ASSERT(d_1_1_Core->refs.loadRelaxed()); } if (d_1_2_Core) { d_1_2_Core->refs.deref(); - Q_ASSERT(d_1_2_Core->refs.load()); + Q_ASSERT(d_1_2_Core->refs.loadRelaxed()); } if (d_1_3_Core) { d_1_3_Core->refs.deref(); - Q_ASSERT(d_1_3_Core->refs.load()); + Q_ASSERT(d_1_3_Core->refs.loadRelaxed()); } if (d_1_0_Deprecated) { d_1_0_Deprecated->refs.deref(); - Q_ASSERT(d_1_0_Deprecated->refs.load()); + Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed()); } if (d_1_1_Deprecated) { d_1_1_Deprecated->refs.deref(); - Q_ASSERT(d_1_1_Deprecated->refs.load()); + Q_ASSERT(d_1_1_Deprecated->refs.loadRelaxed()); } if (d_1_2_Deprecated) { d_1_2_Deprecated->refs.deref(); - Q_ASSERT(d_1_2_Deprecated->refs.load()); + Q_ASSERT(d_1_2_Deprecated->refs.loadRelaxed()); } if (d_1_3_Deprecated) { d_1_3_Deprecated->refs.deref(); - Q_ASSERT(d_1_3_Deprecated->refs.load()); + Q_ASSERT(d_1_3_Deprecated->refs.loadRelaxed()); } } diff --git a/src/gui/opengl/qopenglfunctions_1_4.cpp b/src/gui/opengl/qopenglfunctions_1_4.cpp index 4b78253301..8e2349dc08 100644 --- a/src/gui/opengl/qopenglfunctions_1_4.cpp +++ b/src/gui/opengl/qopenglfunctions_1_4.cpp @@ -84,43 +84,43 @@ QOpenGLFunctions_1_4::~QOpenGLFunctions_1_4() { if (d_1_0_Core) { d_1_0_Core->refs.deref(); - Q_ASSERT(d_1_0_Core->refs.load()); + Q_ASSERT(d_1_0_Core->refs.loadRelaxed()); } if (d_1_1_Core) { d_1_1_Core->refs.deref(); - Q_ASSERT(d_1_1_Core->refs.load()); + Q_ASSERT(d_1_1_Core->refs.loadRelaxed()); } if (d_1_2_Core) { d_1_2_Core->refs.deref(); - Q_ASSERT(d_1_2_Core->refs.load()); + Q_ASSERT(d_1_2_Core->refs.loadRelaxed()); } if (d_1_3_Core) { d_1_3_Core->refs.deref(); - Q_ASSERT(d_1_3_Core->refs.load()); + Q_ASSERT(d_1_3_Core->refs.loadRelaxed()); } if (d_1_4_Core) { d_1_4_Core->refs.deref(); - Q_ASSERT(d_1_4_Core->refs.load()); + Q_ASSERT(d_1_4_Core->refs.loadRelaxed()); } if (d_1_0_Deprecated) { d_1_0_Deprecated->refs.deref(); - Q_ASSERT(d_1_0_Deprecated->refs.load()); + Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed()); } if (d_1_1_Deprecated) { d_1_1_Deprecated->refs.deref(); - Q_ASSERT(d_1_1_Deprecated->refs.load()); + Q_ASSERT(d_1_1_Deprecated->refs.loadRelaxed()); } if (d_1_2_Deprecated) { d_1_2_Deprecated->refs.deref(); - Q_ASSERT(d_1_2_Deprecated->refs.load()); + Q_ASSERT(d_1_2_Deprecated->refs.loadRelaxed()); } if (d_1_3_Deprecated) { d_1_3_Deprecated->refs.deref(); - Q_ASSERT(d_1_3_Deprecated->refs.load()); + Q_ASSERT(d_1_3_Deprecated->refs.loadRelaxed()); } if (d_1_4_Deprecated) { d_1_4_Deprecated->refs.deref(); - Q_ASSERT(d_1_4_Deprecated->refs.load()); + Q_ASSERT(d_1_4_Deprecated->refs.loadRelaxed()); } } diff --git a/src/gui/opengl/qopenglfunctions_1_5.cpp b/src/gui/opengl/qopenglfunctions_1_5.cpp index 2a0820d0cb..cd81cf8b35 100644 --- a/src/gui/opengl/qopenglfunctions_1_5.cpp +++ b/src/gui/opengl/qopenglfunctions_1_5.cpp @@ -85,47 +85,47 @@ QOpenGLFunctions_1_5::~QOpenGLFunctions_1_5() { if (d_1_0_Core) { d_1_0_Core->refs.deref(); - Q_ASSERT(d_1_0_Core->refs.load()); + Q_ASSERT(d_1_0_Core->refs.loadRelaxed()); } if (d_1_1_Core) { d_1_1_Core->refs.deref(); - Q_ASSERT(d_1_1_Core->refs.load()); + Q_ASSERT(d_1_1_Core->refs.loadRelaxed()); } if (d_1_2_Core) { d_1_2_Core->refs.deref(); - Q_ASSERT(d_1_2_Core->refs.load()); + Q_ASSERT(d_1_2_Core->refs.loadRelaxed()); } if (d_1_3_Core) { d_1_3_Core->refs.deref(); - Q_ASSERT(d_1_3_Core->refs.load()); + Q_ASSERT(d_1_3_Core->refs.loadRelaxed()); } if (d_1_4_Core) { d_1_4_Core->refs.deref(); - Q_ASSERT(d_1_4_Core->refs.load()); + Q_ASSERT(d_1_4_Core->refs.loadRelaxed()); } if (d_1_5_Core) { d_1_5_Core->refs.deref(); - Q_ASSERT(d_1_5_Core->refs.load()); + Q_ASSERT(d_1_5_Core->refs.loadRelaxed()); } if (d_1_0_Deprecated) { d_1_0_Deprecated->refs.deref(); - Q_ASSERT(d_1_0_Deprecated->refs.load()); + Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed()); } if (d_1_1_Deprecated) { d_1_1_Deprecated->refs.deref(); - Q_ASSERT(d_1_1_Deprecated->refs.load()); + Q_ASSERT(d_1_1_Deprecated->refs.loadRelaxed()); } if (d_1_2_Deprecated) { d_1_2_Deprecated->refs.deref(); - Q_ASSERT(d_1_2_Deprecated->refs.load()); + Q_ASSERT(d_1_2_Deprecated->refs.loadRelaxed()); } if (d_1_3_Deprecated) { d_1_3_Deprecated->refs.deref(); - Q_ASSERT(d_1_3_Deprecated->refs.load()); + Q_ASSERT(d_1_3_Deprecated->refs.loadRelaxed()); } if (d_1_4_Deprecated) { d_1_4_Deprecated->refs.deref(); - Q_ASSERT(d_1_4_Deprecated->refs.load()); + Q_ASSERT(d_1_4_Deprecated->refs.loadRelaxed()); } } diff --git a/src/gui/opengl/qopenglfunctions_2_0.cpp b/src/gui/opengl/qopenglfunctions_2_0.cpp index 212723aa00..97a8c72fa6 100644 --- a/src/gui/opengl/qopenglfunctions_2_0.cpp +++ b/src/gui/opengl/qopenglfunctions_2_0.cpp @@ -87,51 +87,51 @@ QOpenGLFunctions_2_0::~QOpenGLFunctions_2_0() { if (d_1_0_Core) { d_1_0_Core->refs.deref(); - Q_ASSERT(d_1_0_Core->refs.load()); + Q_ASSERT(d_1_0_Core->refs.loadRelaxed()); } if (d_1_1_Core) { d_1_1_Core->refs.deref(); - Q_ASSERT(d_1_1_Core->refs.load()); + Q_ASSERT(d_1_1_Core->refs.loadRelaxed()); } if (d_1_2_Core) { d_1_2_Core->refs.deref(); - Q_ASSERT(d_1_2_Core->refs.load()); + Q_ASSERT(d_1_2_Core->refs.loadRelaxed()); } if (d_1_3_Core) { d_1_3_Core->refs.deref(); - Q_ASSERT(d_1_3_Core->refs.load()); + Q_ASSERT(d_1_3_Core->refs.loadRelaxed()); } if (d_1_4_Core) { d_1_4_Core->refs.deref(); - Q_ASSERT(d_1_4_Core->refs.load()); + Q_ASSERT(d_1_4_Core->refs.loadRelaxed()); } if (d_1_5_Core) { d_1_5_Core->refs.deref(); - Q_ASSERT(d_1_5_Core->refs.load()); + Q_ASSERT(d_1_5_Core->refs.loadRelaxed()); } if (d_2_0_Core) { d_2_0_Core->refs.deref(); - Q_ASSERT(d_2_0_Core->refs.load()); + Q_ASSERT(d_2_0_Core->refs.loadRelaxed()); } if (d_1_0_Deprecated) { d_1_0_Deprecated->refs.deref(); - Q_ASSERT(d_1_0_Deprecated->refs.load()); + Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed()); } if (d_1_1_Deprecated) { d_1_1_Deprecated->refs.deref(); - Q_ASSERT(d_1_1_Deprecated->refs.load()); + Q_ASSERT(d_1_1_Deprecated->refs.loadRelaxed()); } if (d_1_2_Deprecated) { d_1_2_Deprecated->refs.deref(); - Q_ASSERT(d_1_2_Deprecated->refs.load()); + Q_ASSERT(d_1_2_Deprecated->refs.loadRelaxed()); } if (d_1_3_Deprecated) { d_1_3_Deprecated->refs.deref(); - Q_ASSERT(d_1_3_Deprecated->refs.load()); + Q_ASSERT(d_1_3_Deprecated->refs.loadRelaxed()); } if (d_1_4_Deprecated) { d_1_4_Deprecated->refs.deref(); - Q_ASSERT(d_1_4_Deprecated->refs.load()); + Q_ASSERT(d_1_4_Deprecated->refs.loadRelaxed()); } } diff --git a/src/gui/opengl/qopenglfunctions_2_1.cpp b/src/gui/opengl/qopenglfunctions_2_1.cpp index b8b255014c..00bdc1bbba 100644 --- a/src/gui/opengl/qopenglfunctions_2_1.cpp +++ b/src/gui/opengl/qopenglfunctions_2_1.cpp @@ -88,55 +88,55 @@ QOpenGLFunctions_2_1::~QOpenGLFunctions_2_1() { if (d_1_0_Core) { d_1_0_Core->refs.deref(); - Q_ASSERT(d_1_0_Core->refs.load()); + Q_ASSERT(d_1_0_Core->refs.loadRelaxed()); } if (d_1_1_Core) { d_1_1_Core->refs.deref(); - Q_ASSERT(d_1_1_Core->refs.load()); + Q_ASSERT(d_1_1_Core->refs.loadRelaxed()); } if (d_1_2_Core) { d_1_2_Core->refs.deref(); - Q_ASSERT(d_1_2_Core->refs.load()); + Q_ASSERT(d_1_2_Core->refs.loadRelaxed()); } if (d_1_3_Core) { d_1_3_Core->refs.deref(); - Q_ASSERT(d_1_3_Core->refs.load()); + Q_ASSERT(d_1_3_Core->refs.loadRelaxed()); } if (d_1_4_Core) { d_1_4_Core->refs.deref(); - Q_ASSERT(d_1_4_Core->refs.load()); + Q_ASSERT(d_1_4_Core->refs.loadRelaxed()); } if (d_1_5_Core) { d_1_5_Core->refs.deref(); - Q_ASSERT(d_1_5_Core->refs.load()); + Q_ASSERT(d_1_5_Core->refs.loadRelaxed()); } if (d_2_0_Core) { d_2_0_Core->refs.deref(); - Q_ASSERT(d_2_0_Core->refs.load()); + Q_ASSERT(d_2_0_Core->refs.loadRelaxed()); } if (d_2_1_Core) { d_2_1_Core->refs.deref(); - Q_ASSERT(d_2_1_Core->refs.load()); + Q_ASSERT(d_2_1_Core->refs.loadRelaxed()); } if (d_1_0_Deprecated) { d_1_0_Deprecated->refs.deref(); - Q_ASSERT(d_1_0_Deprecated->refs.load()); + Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed()); } if (d_1_1_Deprecated) { d_1_1_Deprecated->refs.deref(); - Q_ASSERT(d_1_1_Deprecated->refs.load()); + Q_ASSERT(d_1_1_Deprecated->refs.loadRelaxed()); } if (d_1_2_Deprecated) { d_1_2_Deprecated->refs.deref(); - Q_ASSERT(d_1_2_Deprecated->refs.load()); + Q_ASSERT(d_1_2_Deprecated->refs.loadRelaxed()); } if (d_1_3_Deprecated) { d_1_3_Deprecated->refs.deref(); - Q_ASSERT(d_1_3_Deprecated->refs.load()); + Q_ASSERT(d_1_3_Deprecated->refs.loadRelaxed()); } if (d_1_4_Deprecated) { d_1_4_Deprecated->refs.deref(); - Q_ASSERT(d_1_4_Deprecated->refs.load()); + Q_ASSERT(d_1_4_Deprecated->refs.loadRelaxed()); } } diff --git a/src/gui/opengl/qopenglfunctions_3_0.cpp b/src/gui/opengl/qopenglfunctions_3_0.cpp index 4972c03b1e..2c239dba1f 100644 --- a/src/gui/opengl/qopenglfunctions_3_0.cpp +++ b/src/gui/opengl/qopenglfunctions_3_0.cpp @@ -90,59 +90,59 @@ QOpenGLFunctions_3_0::~QOpenGLFunctions_3_0() { if (d_1_0_Core) { d_1_0_Core->refs.deref(); - Q_ASSERT(d_1_0_Core->refs.load()); + Q_ASSERT(d_1_0_Core->refs.loadRelaxed()); } if (d_1_1_Core) { d_1_1_Core->refs.deref(); - Q_ASSERT(d_1_1_Core->refs.load()); + Q_ASSERT(d_1_1_Core->refs.loadRelaxed()); } if (d_1_2_Core) { d_1_2_Core->refs.deref(); - Q_ASSERT(d_1_2_Core->refs.load()); + Q_ASSERT(d_1_2_Core->refs.loadRelaxed()); } if (d_1_3_Core) { d_1_3_Core->refs.deref(); - Q_ASSERT(d_1_3_Core->refs.load()); + Q_ASSERT(d_1_3_Core->refs.loadRelaxed()); } if (d_1_4_Core) { d_1_4_Core->refs.deref(); - Q_ASSERT(d_1_4_Core->refs.load()); + Q_ASSERT(d_1_4_Core->refs.loadRelaxed()); } if (d_1_5_Core) { d_1_5_Core->refs.deref(); - Q_ASSERT(d_1_5_Core->refs.load()); + Q_ASSERT(d_1_5_Core->refs.loadRelaxed()); } if (d_2_0_Core) { d_2_0_Core->refs.deref(); - Q_ASSERT(d_2_0_Core->refs.load()); + Q_ASSERT(d_2_0_Core->refs.loadRelaxed()); } if (d_2_1_Core) { d_2_1_Core->refs.deref(); - Q_ASSERT(d_2_1_Core->refs.load()); + Q_ASSERT(d_2_1_Core->refs.loadRelaxed()); } if (d_3_0_Core) { d_3_0_Core->refs.deref(); - Q_ASSERT(d_3_0_Core->refs.load()); + Q_ASSERT(d_3_0_Core->refs.loadRelaxed()); } if (d_1_0_Deprecated) { d_1_0_Deprecated->refs.deref(); - Q_ASSERT(d_1_0_Deprecated->refs.load()); + Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed()); } if (d_1_1_Deprecated) { d_1_1_Deprecated->refs.deref(); - Q_ASSERT(d_1_1_Deprecated->refs.load()); + Q_ASSERT(d_1_1_Deprecated->refs.loadRelaxed()); } if (d_1_2_Deprecated) { d_1_2_Deprecated->refs.deref(); - Q_ASSERT(d_1_2_Deprecated->refs.load()); + Q_ASSERT(d_1_2_Deprecated->refs.loadRelaxed()); } if (d_1_3_Deprecated) { d_1_3_Deprecated->refs.deref(); - Q_ASSERT(d_1_3_Deprecated->refs.load()); + Q_ASSERT(d_1_3_Deprecated->refs.loadRelaxed()); } if (d_1_4_Deprecated) { d_1_4_Deprecated->refs.deref(); - Q_ASSERT(d_1_4_Deprecated->refs.load()); + Q_ASSERT(d_1_4_Deprecated->refs.loadRelaxed()); } } diff --git a/src/gui/opengl/qopenglfunctions_3_1.cpp b/src/gui/opengl/qopenglfunctions_3_1.cpp index 9328f5ca89..f62f555c8e 100644 --- a/src/gui/opengl/qopenglfunctions_3_1.cpp +++ b/src/gui/opengl/qopenglfunctions_3_1.cpp @@ -84,43 +84,43 @@ QOpenGLFunctions_3_1::~QOpenGLFunctions_3_1() { if (d_1_0_Core) { d_1_0_Core->refs.deref(); - Q_ASSERT(d_1_0_Core->refs.load()); + Q_ASSERT(d_1_0_Core->refs.loadRelaxed()); } if (d_1_1_Core) { d_1_1_Core->refs.deref(); - Q_ASSERT(d_1_1_Core->refs.load()); + Q_ASSERT(d_1_1_Core->refs.loadRelaxed()); } if (d_1_2_Core) { d_1_2_Core->refs.deref(); - Q_ASSERT(d_1_2_Core->refs.load()); + Q_ASSERT(d_1_2_Core->refs.loadRelaxed()); } if (d_1_3_Core) { d_1_3_Core->refs.deref(); - Q_ASSERT(d_1_3_Core->refs.load()); + Q_ASSERT(d_1_3_Core->refs.loadRelaxed()); } if (d_1_4_Core) { d_1_4_Core->refs.deref(); - Q_ASSERT(d_1_4_Core->refs.load()); + Q_ASSERT(d_1_4_Core->refs.loadRelaxed()); } if (d_1_5_Core) { d_1_5_Core->refs.deref(); - Q_ASSERT(d_1_5_Core->refs.load()); + Q_ASSERT(d_1_5_Core->refs.loadRelaxed()); } if (d_2_0_Core) { d_2_0_Core->refs.deref(); - Q_ASSERT(d_2_0_Core->refs.load()); + Q_ASSERT(d_2_0_Core->refs.loadRelaxed()); } if (d_2_1_Core) { d_2_1_Core->refs.deref(); - Q_ASSERT(d_2_1_Core->refs.load()); + Q_ASSERT(d_2_1_Core->refs.loadRelaxed()); } if (d_3_0_Core) { d_3_0_Core->refs.deref(); - Q_ASSERT(d_3_0_Core->refs.load()); + Q_ASSERT(d_3_0_Core->refs.loadRelaxed()); } if (d_3_1_Core) { d_3_1_Core->refs.deref(); - Q_ASSERT(d_3_1_Core->refs.load()); + Q_ASSERT(d_3_1_Core->refs.loadRelaxed()); } } diff --git a/src/gui/opengl/qopenglfunctions_3_2_compatibility.cpp b/src/gui/opengl/qopenglfunctions_3_2_compatibility.cpp index 709f65edf8..ba7be2d893 100644 --- a/src/gui/opengl/qopenglfunctions_3_2_compatibility.cpp +++ b/src/gui/opengl/qopenglfunctions_3_2_compatibility.cpp @@ -92,67 +92,67 @@ QOpenGLFunctions_3_2_Compatibility::~QOpenGLFunctions_3_2_Compatibility() { if (d_1_0_Core) { d_1_0_Core->refs.deref(); - Q_ASSERT(d_1_0_Core->refs.load()); + Q_ASSERT(d_1_0_Core->refs.loadRelaxed()); } if (d_1_1_Core) { d_1_1_Core->refs.deref(); - Q_ASSERT(d_1_1_Core->refs.load()); + Q_ASSERT(d_1_1_Core->refs.loadRelaxed()); } if (d_1_2_Core) { d_1_2_Core->refs.deref(); - Q_ASSERT(d_1_2_Core->refs.load()); + Q_ASSERT(d_1_2_Core->refs.loadRelaxed()); } if (d_1_3_Core) { d_1_3_Core->refs.deref(); - Q_ASSERT(d_1_3_Core->refs.load()); + Q_ASSERT(d_1_3_Core->refs.loadRelaxed()); } if (d_1_4_Core) { d_1_4_Core->refs.deref(); - Q_ASSERT(d_1_4_Core->refs.load()); + Q_ASSERT(d_1_4_Core->refs.loadRelaxed()); } if (d_1_5_Core) { d_1_5_Core->refs.deref(); - Q_ASSERT(d_1_5_Core->refs.load()); + Q_ASSERT(d_1_5_Core->refs.loadRelaxed()); } if (d_2_0_Core) { d_2_0_Core->refs.deref(); - Q_ASSERT(d_2_0_Core->refs.load()); + Q_ASSERT(d_2_0_Core->refs.loadRelaxed()); } if (d_2_1_Core) { d_2_1_Core->refs.deref(); - Q_ASSERT(d_2_1_Core->refs.load()); + Q_ASSERT(d_2_1_Core->refs.loadRelaxed()); } if (d_3_0_Core) { d_3_0_Core->refs.deref(); - Q_ASSERT(d_3_0_Core->refs.load()); + Q_ASSERT(d_3_0_Core->refs.loadRelaxed()); } if (d_3_1_Core) { d_3_1_Core->refs.deref(); - Q_ASSERT(d_3_1_Core->refs.load()); + Q_ASSERT(d_3_1_Core->refs.loadRelaxed()); } if (d_3_2_Core) { d_3_2_Core->refs.deref(); - Q_ASSERT(d_3_2_Core->refs.load()); + Q_ASSERT(d_3_2_Core->refs.loadRelaxed()); } if (d_1_0_Deprecated) { d_1_0_Deprecated->refs.deref(); - Q_ASSERT(d_1_0_Deprecated->refs.load()); + Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed()); } if (d_1_1_Deprecated) { d_1_1_Deprecated->refs.deref(); - Q_ASSERT(d_1_1_Deprecated->refs.load()); + Q_ASSERT(d_1_1_Deprecated->refs.loadRelaxed()); } if (d_1_2_Deprecated) { d_1_2_Deprecated->refs.deref(); - Q_ASSERT(d_1_2_Deprecated->refs.load()); + Q_ASSERT(d_1_2_Deprecated->refs.loadRelaxed()); } if (d_1_3_Deprecated) { d_1_3_Deprecated->refs.deref(); - Q_ASSERT(d_1_3_Deprecated->refs.load()); + Q_ASSERT(d_1_3_Deprecated->refs.loadRelaxed()); } if (d_1_4_Deprecated) { d_1_4_Deprecated->refs.deref(); - Q_ASSERT(d_1_4_Deprecated->refs.load()); + Q_ASSERT(d_1_4_Deprecated->refs.loadRelaxed()); } } diff --git a/src/gui/opengl/qopenglfunctions_3_2_core.cpp b/src/gui/opengl/qopenglfunctions_3_2_core.cpp index 02c0c78b01..4c1e3eb3da 100644 --- a/src/gui/opengl/qopenglfunctions_3_2_core.cpp +++ b/src/gui/opengl/qopenglfunctions_3_2_core.cpp @@ -85,47 +85,47 @@ QOpenGLFunctions_3_2_Core::~QOpenGLFunctions_3_2_Core() { if (d_1_0_Core) { d_1_0_Core->refs.deref(); - Q_ASSERT(d_1_0_Core->refs.load()); + Q_ASSERT(d_1_0_Core->refs.loadRelaxed()); } if (d_1_1_Core) { d_1_1_Core->refs.deref(); - Q_ASSERT(d_1_1_Core->refs.load()); + Q_ASSERT(d_1_1_Core->refs.loadRelaxed()); } if (d_1_2_Core) { d_1_2_Core->refs.deref(); - Q_ASSERT(d_1_2_Core->refs.load()); + Q_ASSERT(d_1_2_Core->refs.loadRelaxed()); } if (d_1_3_Core) { d_1_3_Core->refs.deref(); - Q_ASSERT(d_1_3_Core->refs.load()); + Q_ASSERT(d_1_3_Core->refs.loadRelaxed()); } if (d_1_4_Core) { d_1_4_Core->refs.deref(); - Q_ASSERT(d_1_4_Core->refs.load()); + Q_ASSERT(d_1_4_Core->refs.loadRelaxed()); } if (d_1_5_Core) { d_1_5_Core->refs.deref(); - Q_ASSERT(d_1_5_Core->refs.load()); + Q_ASSERT(d_1_5_Core->refs.loadRelaxed()); } if (d_2_0_Core) { d_2_0_Core->refs.deref(); - Q_ASSERT(d_2_0_Core->refs.load()); + Q_ASSERT(d_2_0_Core->refs.loadRelaxed()); } if (d_2_1_Core) { d_2_1_Core->refs.deref(); - Q_ASSERT(d_2_1_Core->refs.load()); + Q_ASSERT(d_2_1_Core->refs.loadRelaxed()); } if (d_3_0_Core) { d_3_0_Core->refs.deref(); - Q_ASSERT(d_3_0_Core->refs.load()); + Q_ASSERT(d_3_0_Core->refs.loadRelaxed()); } if (d_3_1_Core) { d_3_1_Core->refs.deref(); - Q_ASSERT(d_3_1_Core->refs.load()); + Q_ASSERT(d_3_1_Core->refs.loadRelaxed()); } if (d_3_2_Core) { d_3_2_Core->refs.deref(); - Q_ASSERT(d_3_2_Core->refs.load()); + Q_ASSERT(d_3_2_Core->refs.loadRelaxed()); } } diff --git a/src/gui/opengl/qopenglfunctions_3_3_compatibility.cpp b/src/gui/opengl/qopenglfunctions_3_3_compatibility.cpp index b034391c86..c750c6e0cc 100644 --- a/src/gui/opengl/qopenglfunctions_3_3_compatibility.cpp +++ b/src/gui/opengl/qopenglfunctions_3_3_compatibility.cpp @@ -93,75 +93,75 @@ QOpenGLFunctions_3_3_Compatibility::~QOpenGLFunctions_3_3_Compatibility() { if (d_1_0_Core) { d_1_0_Core->refs.deref(); - Q_ASSERT(d_1_0_Core->refs.load()); + Q_ASSERT(d_1_0_Core->refs.loadRelaxed()); } if (d_1_1_Core) { d_1_1_Core->refs.deref(); - Q_ASSERT(d_1_1_Core->refs.load()); + Q_ASSERT(d_1_1_Core->refs.loadRelaxed()); } if (d_1_2_Core) { d_1_2_Core->refs.deref(); - Q_ASSERT(d_1_2_Core->refs.load()); + Q_ASSERT(d_1_2_Core->refs.loadRelaxed()); } if (d_1_3_Core) { d_1_3_Core->refs.deref(); - Q_ASSERT(d_1_3_Core->refs.load()); + Q_ASSERT(d_1_3_Core->refs.loadRelaxed()); } if (d_1_4_Core) { d_1_4_Core->refs.deref(); - Q_ASSERT(d_1_4_Core->refs.load()); + Q_ASSERT(d_1_4_Core->refs.loadRelaxed()); } if (d_1_5_Core) { d_1_5_Core->refs.deref(); - Q_ASSERT(d_1_5_Core->refs.load()); + Q_ASSERT(d_1_5_Core->refs.loadRelaxed()); } if (d_2_0_Core) { d_2_0_Core->refs.deref(); - Q_ASSERT(d_2_0_Core->refs.load()); + Q_ASSERT(d_2_0_Core->refs.loadRelaxed()); } if (d_2_1_Core) { d_2_1_Core->refs.deref(); - Q_ASSERT(d_2_1_Core->refs.load()); + Q_ASSERT(d_2_1_Core->refs.loadRelaxed()); } if (d_3_0_Core) { d_3_0_Core->refs.deref(); - Q_ASSERT(d_3_0_Core->refs.load()); + Q_ASSERT(d_3_0_Core->refs.loadRelaxed()); } if (d_3_1_Core) { d_3_1_Core->refs.deref(); - Q_ASSERT(d_3_1_Core->refs.load()); + Q_ASSERT(d_3_1_Core->refs.loadRelaxed()); } if (d_3_2_Core) { d_3_2_Core->refs.deref(); - Q_ASSERT(d_3_2_Core->refs.load()); + Q_ASSERT(d_3_2_Core->refs.loadRelaxed()); } if (d_3_3_Core) { d_3_3_Core->refs.deref(); - Q_ASSERT(d_3_3_Core->refs.load()); + Q_ASSERT(d_3_3_Core->refs.loadRelaxed()); } if (d_1_0_Deprecated) { d_1_0_Deprecated->refs.deref(); - Q_ASSERT(d_1_0_Deprecated->refs.load()); + Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed()); } if (d_1_1_Deprecated) { d_1_1_Deprecated->refs.deref(); - Q_ASSERT(d_1_1_Deprecated->refs.load()); + Q_ASSERT(d_1_1_Deprecated->refs.loadRelaxed()); } if (d_1_2_Deprecated) { d_1_2_Deprecated->refs.deref(); - Q_ASSERT(d_1_2_Deprecated->refs.load()); + Q_ASSERT(d_1_2_Deprecated->refs.loadRelaxed()); } if (d_1_3_Deprecated) { d_1_3_Deprecated->refs.deref(); - Q_ASSERT(d_1_3_Deprecated->refs.load()); + Q_ASSERT(d_1_3_Deprecated->refs.loadRelaxed()); } if (d_1_4_Deprecated) { d_1_4_Deprecated->refs.deref(); - Q_ASSERT(d_1_4_Deprecated->refs.load()); + Q_ASSERT(d_1_4_Deprecated->refs.loadRelaxed()); } if (d_3_3_Deprecated) { d_3_3_Deprecated->refs.deref(); - Q_ASSERT(d_3_3_Deprecated->refs.load()); + Q_ASSERT(d_3_3_Deprecated->refs.loadRelaxed()); } } diff --git a/src/gui/opengl/qopenglfunctions_3_3_core.cpp b/src/gui/opengl/qopenglfunctions_3_3_core.cpp index 7779d92b6a..5723509e32 100644 --- a/src/gui/opengl/qopenglfunctions_3_3_core.cpp +++ b/src/gui/opengl/qopenglfunctions_3_3_core.cpp @@ -86,51 +86,51 @@ QOpenGLFunctions_3_3_Core::~QOpenGLFunctions_3_3_Core() { if (d_1_0_Core) { d_1_0_Core->refs.deref(); - Q_ASSERT(d_1_0_Core->refs.load()); + Q_ASSERT(d_1_0_Core->refs.loadRelaxed()); } if (d_1_1_Core) { d_1_1_Core->refs.deref(); - Q_ASSERT(d_1_1_Core->refs.load()); + Q_ASSERT(d_1_1_Core->refs.loadRelaxed()); } if (d_1_2_Core) { d_1_2_Core->refs.deref(); - Q_ASSERT(d_1_2_Core->refs.load()); + Q_ASSERT(d_1_2_Core->refs.loadRelaxed()); } if (d_1_3_Core) { d_1_3_Core->refs.deref(); - Q_ASSERT(d_1_3_Core->refs.load()); + Q_ASSERT(d_1_3_Core->refs.loadRelaxed()); } if (d_1_4_Core) { d_1_4_Core->refs.deref(); - Q_ASSERT(d_1_4_Core->refs.load()); + Q_ASSERT(d_1_4_Core->refs.loadRelaxed()); } if (d_1_5_Core) { d_1_5_Core->refs.deref(); - Q_ASSERT(d_1_5_Core->refs.load()); + Q_ASSERT(d_1_5_Core->refs.loadRelaxed()); } if (d_2_0_Core) { d_2_0_Core->refs.deref(); - Q_ASSERT(d_2_0_Core->refs.load()); + Q_ASSERT(d_2_0_Core->refs.loadRelaxed()); } if (d_2_1_Core) { d_2_1_Core->refs.deref(); - Q_ASSERT(d_2_1_Core->refs.load()); + Q_ASSERT(d_2_1_Core->refs.loadRelaxed()); } if (d_3_0_Core) { d_3_0_Core->refs.deref(); - Q_ASSERT(d_3_0_Core->refs.load()); + Q_ASSERT(d_3_0_Core->refs.loadRelaxed()); } if (d_3_1_Core) { d_3_1_Core->refs.deref(); - Q_ASSERT(d_3_1_Core->refs.load()); + Q_ASSERT(d_3_1_Core->refs.loadRelaxed()); } if (d_3_2_Core) { d_3_2_Core->refs.deref(); - Q_ASSERT(d_3_2_Core->refs.load()); + Q_ASSERT(d_3_2_Core->refs.loadRelaxed()); } if (d_3_3_Core) { d_3_3_Core->refs.deref(); - Q_ASSERT(d_3_3_Core->refs.load()); + Q_ASSERT(d_3_3_Core->refs.loadRelaxed()); } } diff --git a/src/gui/opengl/qopenglfunctions_4_0_compatibility.cpp b/src/gui/opengl/qopenglfunctions_4_0_compatibility.cpp index 4fe4526efc..6ae7643eb5 100644 --- a/src/gui/opengl/qopenglfunctions_4_0_compatibility.cpp +++ b/src/gui/opengl/qopenglfunctions_4_0_compatibility.cpp @@ -94,79 +94,79 @@ QOpenGLFunctions_4_0_Compatibility::~QOpenGLFunctions_4_0_Compatibility() { if (d_1_0_Core) { d_1_0_Core->refs.deref(); - Q_ASSERT(d_1_0_Core->refs.load()); + Q_ASSERT(d_1_0_Core->refs.loadRelaxed()); } if (d_1_1_Core) { d_1_1_Core->refs.deref(); - Q_ASSERT(d_1_1_Core->refs.load()); + Q_ASSERT(d_1_1_Core->refs.loadRelaxed()); } if (d_1_2_Core) { d_1_2_Core->refs.deref(); - Q_ASSERT(d_1_2_Core->refs.load()); + Q_ASSERT(d_1_2_Core->refs.loadRelaxed()); } if (d_1_3_Core) { d_1_3_Core->refs.deref(); - Q_ASSERT(d_1_3_Core->refs.load()); + Q_ASSERT(d_1_3_Core->refs.loadRelaxed()); } if (d_1_4_Core) { d_1_4_Core->refs.deref(); - Q_ASSERT(d_1_4_Core->refs.load()); + Q_ASSERT(d_1_4_Core->refs.loadRelaxed()); } if (d_1_5_Core) { d_1_5_Core->refs.deref(); - Q_ASSERT(d_1_5_Core->refs.load()); + Q_ASSERT(d_1_5_Core->refs.loadRelaxed()); } if (d_2_0_Core) { d_2_0_Core->refs.deref(); - Q_ASSERT(d_2_0_Core->refs.load()); + Q_ASSERT(d_2_0_Core->refs.loadRelaxed()); } if (d_2_1_Core) { d_2_1_Core->refs.deref(); - Q_ASSERT(d_2_1_Core->refs.load()); + Q_ASSERT(d_2_1_Core->refs.loadRelaxed()); } if (d_3_0_Core) { d_3_0_Core->refs.deref(); - Q_ASSERT(d_3_0_Core->refs.load()); + Q_ASSERT(d_3_0_Core->refs.loadRelaxed()); } if (d_3_1_Core) { d_3_1_Core->refs.deref(); - Q_ASSERT(d_3_1_Core->refs.load()); + Q_ASSERT(d_3_1_Core->refs.loadRelaxed()); } if (d_3_2_Core) { d_3_2_Core->refs.deref(); - Q_ASSERT(d_3_2_Core->refs.load()); + Q_ASSERT(d_3_2_Core->refs.loadRelaxed()); } if (d_3_3_Core) { d_3_3_Core->refs.deref(); - Q_ASSERT(d_3_3_Core->refs.load()); + Q_ASSERT(d_3_3_Core->refs.loadRelaxed()); } if (d_4_0_Core) { d_4_0_Core->refs.deref(); - Q_ASSERT(d_4_0_Core->refs.load()); + Q_ASSERT(d_4_0_Core->refs.loadRelaxed()); } if (d_1_0_Deprecated) { d_1_0_Deprecated->refs.deref(); - Q_ASSERT(d_1_0_Deprecated->refs.load()); + Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed()); } if (d_1_1_Deprecated) { d_1_1_Deprecated->refs.deref(); - Q_ASSERT(d_1_1_Deprecated->refs.load()); + Q_ASSERT(d_1_1_Deprecated->refs.loadRelaxed()); } if (d_1_2_Deprecated) { d_1_2_Deprecated->refs.deref(); - Q_ASSERT(d_1_2_Deprecated->refs.load()); + Q_ASSERT(d_1_2_Deprecated->refs.loadRelaxed()); } if (d_1_3_Deprecated) { d_1_3_Deprecated->refs.deref(); - Q_ASSERT(d_1_3_Deprecated->refs.load()); + Q_ASSERT(d_1_3_Deprecated->refs.loadRelaxed()); } if (d_1_4_Deprecated) { d_1_4_Deprecated->refs.deref(); - Q_ASSERT(d_1_4_Deprecated->refs.load()); + Q_ASSERT(d_1_4_Deprecated->refs.loadRelaxed()); } if (d_3_3_Deprecated) { d_3_3_Deprecated->refs.deref(); - Q_ASSERT(d_3_3_Deprecated->refs.load()); + Q_ASSERT(d_3_3_Deprecated->refs.loadRelaxed()); } } diff --git a/src/gui/opengl/qopenglfunctions_4_0_core.cpp b/src/gui/opengl/qopenglfunctions_4_0_core.cpp index 4e4e8cc547..cd4fdb8b2b 100644 --- a/src/gui/opengl/qopenglfunctions_4_0_core.cpp +++ b/src/gui/opengl/qopenglfunctions_4_0_core.cpp @@ -87,55 +87,55 @@ QOpenGLFunctions_4_0_Core::~QOpenGLFunctions_4_0_Core() { if (d_1_0_Core) { d_1_0_Core->refs.deref(); - Q_ASSERT(d_1_0_Core->refs.load()); + Q_ASSERT(d_1_0_Core->refs.loadRelaxed()); } if (d_1_1_Core) { d_1_1_Core->refs.deref(); - Q_ASSERT(d_1_1_Core->refs.load()); + Q_ASSERT(d_1_1_Core->refs.loadRelaxed()); } if (d_1_2_Core) { d_1_2_Core->refs.deref(); - Q_ASSERT(d_1_2_Core->refs.load()); + Q_ASSERT(d_1_2_Core->refs.loadRelaxed()); } if (d_1_3_Core) { d_1_3_Core->refs.deref(); - Q_ASSERT(d_1_3_Core->refs.load()); + Q_ASSERT(d_1_3_Core->refs.loadRelaxed()); } if (d_1_4_Core) { d_1_4_Core->refs.deref(); - Q_ASSERT(d_1_4_Core->refs.load()); + Q_ASSERT(d_1_4_Core->refs.loadRelaxed()); } if (d_1_5_Core) { d_1_5_Core->refs.deref(); - Q_ASSERT(d_1_5_Core->refs.load()); + Q_ASSERT(d_1_5_Core->refs.loadRelaxed()); } if (d_2_0_Core) { d_2_0_Core->refs.deref(); - Q_ASSERT(d_2_0_Core->refs.load()); + Q_ASSERT(d_2_0_Core->refs.loadRelaxed()); } if (d_2_1_Core) { d_2_1_Core->refs.deref(); - Q_ASSERT(d_2_1_Core->refs.load()); + Q_ASSERT(d_2_1_Core->refs.loadRelaxed()); } if (d_3_0_Core) { d_3_0_Core->refs.deref(); - Q_ASSERT(d_3_0_Core->refs.load()); + Q_ASSERT(d_3_0_Core->refs.loadRelaxed()); } if (d_3_1_Core) { d_3_1_Core->refs.deref(); - Q_ASSERT(d_3_1_Core->refs.load()); + Q_ASSERT(d_3_1_Core->refs.loadRelaxed()); } if (d_3_2_Core) { d_3_2_Core->refs.deref(); - Q_ASSERT(d_3_2_Core->refs.load()); + Q_ASSERT(d_3_2_Core->refs.loadRelaxed()); } if (d_3_3_Core) { d_3_3_Core->refs.deref(); - Q_ASSERT(d_3_3_Core->refs.load()); + Q_ASSERT(d_3_3_Core->refs.loadRelaxed()); } if (d_4_0_Core) { d_4_0_Core->refs.deref(); - Q_ASSERT(d_4_0_Core->refs.load()); + Q_ASSERT(d_4_0_Core->refs.loadRelaxed()); } } diff --git a/src/gui/opengl/qopenglfunctions_4_1_compatibility.cpp b/src/gui/opengl/qopenglfunctions_4_1_compatibility.cpp index 41ecb4672a..d104c74bc2 100644 --- a/src/gui/opengl/qopenglfunctions_4_1_compatibility.cpp +++ b/src/gui/opengl/qopenglfunctions_4_1_compatibility.cpp @@ -95,83 +95,83 @@ QOpenGLFunctions_4_1_Compatibility::~QOpenGLFunctions_4_1_Compatibility() { if (d_1_0_Core) { d_1_0_Core->refs.deref(); - Q_ASSERT(d_1_0_Core->refs.load()); + Q_ASSERT(d_1_0_Core->refs.loadRelaxed()); } if (d_1_1_Core) { d_1_1_Core->refs.deref(); - Q_ASSERT(d_1_1_Core->refs.load()); + Q_ASSERT(d_1_1_Core->refs.loadRelaxed()); } if (d_1_2_Core) { d_1_2_Core->refs.deref(); - Q_ASSERT(d_1_2_Core->refs.load()); + Q_ASSERT(d_1_2_Core->refs.loadRelaxed()); } if (d_1_3_Core) { d_1_3_Core->refs.deref(); - Q_ASSERT(d_1_3_Core->refs.load()); + Q_ASSERT(d_1_3_Core->refs.loadRelaxed()); } if (d_1_4_Core) { d_1_4_Core->refs.deref(); - Q_ASSERT(d_1_4_Core->refs.load()); + Q_ASSERT(d_1_4_Core->refs.loadRelaxed()); } if (d_1_5_Core) { d_1_5_Core->refs.deref(); - Q_ASSERT(d_1_5_Core->refs.load()); + Q_ASSERT(d_1_5_Core->refs.loadRelaxed()); } if (d_2_0_Core) { d_2_0_Core->refs.deref(); - Q_ASSERT(d_2_0_Core->refs.load()); + Q_ASSERT(d_2_0_Core->refs.loadRelaxed()); } if (d_2_1_Core) { d_2_1_Core->refs.deref(); - Q_ASSERT(d_2_1_Core->refs.load()); + Q_ASSERT(d_2_1_Core->refs.loadRelaxed()); } if (d_3_0_Core) { d_3_0_Core->refs.deref(); - Q_ASSERT(d_3_0_Core->refs.load()); + Q_ASSERT(d_3_0_Core->refs.loadRelaxed()); } if (d_3_1_Core) { d_3_1_Core->refs.deref(); - Q_ASSERT(d_3_1_Core->refs.load()); + Q_ASSERT(d_3_1_Core->refs.loadRelaxed()); } if (d_3_2_Core) { d_3_2_Core->refs.deref(); - Q_ASSERT(d_3_2_Core->refs.load()); + Q_ASSERT(d_3_2_Core->refs.loadRelaxed()); } if (d_3_3_Core) { d_3_3_Core->refs.deref(); - Q_ASSERT(d_3_3_Core->refs.load()); + Q_ASSERT(d_3_3_Core->refs.loadRelaxed()); } if (d_4_0_Core) { d_4_0_Core->refs.deref(); - Q_ASSERT(d_4_0_Core->refs.load()); + Q_ASSERT(d_4_0_Core->refs.loadRelaxed()); } if (d_4_1_Core) { d_4_1_Core->refs.deref(); - Q_ASSERT(d_4_1_Core->refs.load()); + Q_ASSERT(d_4_1_Core->refs.loadRelaxed()); } if (d_1_0_Deprecated) { d_1_0_Deprecated->refs.deref(); - Q_ASSERT(d_1_0_Deprecated->refs.load()); + Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed()); } if (d_1_1_Deprecated) { d_1_1_Deprecated->refs.deref(); - Q_ASSERT(d_1_1_Deprecated->refs.load()); + Q_ASSERT(d_1_1_Deprecated->refs.loadRelaxed()); } if (d_1_2_Deprecated) { d_1_2_Deprecated->refs.deref(); - Q_ASSERT(d_1_2_Deprecated->refs.load()); + Q_ASSERT(d_1_2_Deprecated->refs.loadRelaxed()); } if (d_1_3_Deprecated) { d_1_3_Deprecated->refs.deref(); - Q_ASSERT(d_1_3_Deprecated->refs.load()); + Q_ASSERT(d_1_3_Deprecated->refs.loadRelaxed()); } if (d_1_4_Deprecated) { d_1_4_Deprecated->refs.deref(); - Q_ASSERT(d_1_4_Deprecated->refs.load()); + Q_ASSERT(d_1_4_Deprecated->refs.loadRelaxed()); } if (d_3_3_Deprecated) { d_3_3_Deprecated->refs.deref(); - Q_ASSERT(d_3_3_Deprecated->refs.load()); + Q_ASSERT(d_3_3_Deprecated->refs.loadRelaxed()); } } diff --git a/src/gui/opengl/qopenglfunctions_4_1_core.cpp b/src/gui/opengl/qopenglfunctions_4_1_core.cpp index 5a1e1eb42f..7527aba620 100644 --- a/src/gui/opengl/qopenglfunctions_4_1_core.cpp +++ b/src/gui/opengl/qopenglfunctions_4_1_core.cpp @@ -88,59 +88,59 @@ QOpenGLFunctions_4_1_Core::~QOpenGLFunctions_4_1_Core() { if (d_1_0_Core) { d_1_0_Core->refs.deref(); - Q_ASSERT(d_1_0_Core->refs.load()); + Q_ASSERT(d_1_0_Core->refs.loadRelaxed()); } if (d_1_1_Core) { d_1_1_Core->refs.deref(); - Q_ASSERT(d_1_1_Core->refs.load()); + Q_ASSERT(d_1_1_Core->refs.loadRelaxed()); } if (d_1_2_Core) { d_1_2_Core->refs.deref(); - Q_ASSERT(d_1_2_Core->refs.load()); + Q_ASSERT(d_1_2_Core->refs.loadRelaxed()); } if (d_1_3_Core) { d_1_3_Core->refs.deref(); - Q_ASSERT(d_1_3_Core->refs.load()); + Q_ASSERT(d_1_3_Core->refs.loadRelaxed()); } if (d_1_4_Core) { d_1_4_Core->refs.deref(); - Q_ASSERT(d_1_4_Core->refs.load()); + Q_ASSERT(d_1_4_Core->refs.loadRelaxed()); } if (d_1_5_Core) { d_1_5_Core->refs.deref(); - Q_ASSERT(d_1_5_Core->refs.load()); + Q_ASSERT(d_1_5_Core->refs.loadRelaxed()); } if (d_2_0_Core) { d_2_0_Core->refs.deref(); - Q_ASSERT(d_2_0_Core->refs.load()); + Q_ASSERT(d_2_0_Core->refs.loadRelaxed()); } if (d_2_1_Core) { d_2_1_Core->refs.deref(); - Q_ASSERT(d_2_1_Core->refs.load()); + Q_ASSERT(d_2_1_Core->refs.loadRelaxed()); } if (d_3_0_Core) { d_3_0_Core->refs.deref(); - Q_ASSERT(d_3_0_Core->refs.load()); + Q_ASSERT(d_3_0_Core->refs.loadRelaxed()); } if (d_3_1_Core) { d_3_1_Core->refs.deref(); - Q_ASSERT(d_3_1_Core->refs.load()); + Q_ASSERT(d_3_1_Core->refs.loadRelaxed()); } if (d_3_2_Core) { d_3_2_Core->refs.deref(); - Q_ASSERT(d_3_2_Core->refs.load()); + Q_ASSERT(d_3_2_Core->refs.loadRelaxed()); } if (d_3_3_Core) { d_3_3_Core->refs.deref(); - Q_ASSERT(d_3_3_Core->refs.load()); + Q_ASSERT(d_3_3_Core->refs.loadRelaxed()); } if (d_4_0_Core) { d_4_0_Core->refs.deref(); - Q_ASSERT(d_4_0_Core->refs.load()); + Q_ASSERT(d_4_0_Core->refs.loadRelaxed()); } if (d_4_1_Core) { d_4_1_Core->refs.deref(); - Q_ASSERT(d_4_1_Core->refs.load()); + Q_ASSERT(d_4_1_Core->refs.loadRelaxed()); } } diff --git a/src/gui/opengl/qopenglfunctions_4_2_compatibility.cpp b/src/gui/opengl/qopenglfunctions_4_2_compatibility.cpp index fcc049c67b..a5b1b37495 100644 --- a/src/gui/opengl/qopenglfunctions_4_2_compatibility.cpp +++ b/src/gui/opengl/qopenglfunctions_4_2_compatibility.cpp @@ -96,87 +96,87 @@ QOpenGLFunctions_4_2_Compatibility::~QOpenGLFunctions_4_2_Compatibility() { if (d_1_0_Core) { d_1_0_Core->refs.deref(); - Q_ASSERT(d_1_0_Core->refs.load()); + Q_ASSERT(d_1_0_Core->refs.loadRelaxed()); } if (d_1_1_Core) { d_1_1_Core->refs.deref(); - Q_ASSERT(d_1_1_Core->refs.load()); + Q_ASSERT(d_1_1_Core->refs.loadRelaxed()); } if (d_1_2_Core) { d_1_2_Core->refs.deref(); - Q_ASSERT(d_1_2_Core->refs.load()); + Q_ASSERT(d_1_2_Core->refs.loadRelaxed()); } if (d_1_3_Core) { d_1_3_Core->refs.deref(); - Q_ASSERT(d_1_3_Core->refs.load()); + Q_ASSERT(d_1_3_Core->refs.loadRelaxed()); } if (d_1_4_Core) { d_1_4_Core->refs.deref(); - Q_ASSERT(d_1_4_Core->refs.load()); + Q_ASSERT(d_1_4_Core->refs.loadRelaxed()); } if (d_1_5_Core) { d_1_5_Core->refs.deref(); - Q_ASSERT(d_1_5_Core->refs.load()); + Q_ASSERT(d_1_5_Core->refs.loadRelaxed()); } if (d_2_0_Core) { d_2_0_Core->refs.deref(); - Q_ASSERT(d_2_0_Core->refs.load()); + Q_ASSERT(d_2_0_Core->refs.loadRelaxed()); } if (d_2_1_Core) { d_2_1_Core->refs.deref(); - Q_ASSERT(d_2_1_Core->refs.load()); + Q_ASSERT(d_2_1_Core->refs.loadRelaxed()); } if (d_3_0_Core) { d_3_0_Core->refs.deref(); - Q_ASSERT(d_3_0_Core->refs.load()); + Q_ASSERT(d_3_0_Core->refs.loadRelaxed()); } if (d_3_1_Core) { d_3_1_Core->refs.deref(); - Q_ASSERT(d_3_1_Core->refs.load()); + Q_ASSERT(d_3_1_Core->refs.loadRelaxed()); } if (d_3_2_Core) { d_3_2_Core->refs.deref(); - Q_ASSERT(d_3_2_Core->refs.load()); + Q_ASSERT(d_3_2_Core->refs.loadRelaxed()); } if (d_3_3_Core) { d_3_3_Core->refs.deref(); - Q_ASSERT(d_3_3_Core->refs.load()); + Q_ASSERT(d_3_3_Core->refs.loadRelaxed()); } if (d_4_0_Core) { d_4_0_Core->refs.deref(); - Q_ASSERT(d_4_0_Core->refs.load()); + Q_ASSERT(d_4_0_Core->refs.loadRelaxed()); } if (d_4_1_Core) { d_4_1_Core->refs.deref(); - Q_ASSERT(d_4_1_Core->refs.load()); + Q_ASSERT(d_4_1_Core->refs.loadRelaxed()); } if (d_4_2_Core) { d_4_2_Core->refs.deref(); - Q_ASSERT(d_4_2_Core->refs.load()); + Q_ASSERT(d_4_2_Core->refs.loadRelaxed()); } if (d_1_0_Deprecated) { d_1_0_Deprecated->refs.deref(); - Q_ASSERT(d_1_0_Deprecated->refs.load()); + Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed()); } if (d_1_1_Deprecated) { d_1_1_Deprecated->refs.deref(); - Q_ASSERT(d_1_1_Deprecated->refs.load()); + Q_ASSERT(d_1_1_Deprecated->refs.loadRelaxed()); } if (d_1_2_Deprecated) { d_1_2_Deprecated->refs.deref(); - Q_ASSERT(d_1_2_Deprecated->refs.load()); + Q_ASSERT(d_1_2_Deprecated->refs.loadRelaxed()); } if (d_1_3_Deprecated) { d_1_3_Deprecated->refs.deref(); - Q_ASSERT(d_1_3_Deprecated->refs.load()); + Q_ASSERT(d_1_3_Deprecated->refs.loadRelaxed()); } if (d_1_4_Deprecated) { d_1_4_Deprecated->refs.deref(); - Q_ASSERT(d_1_4_Deprecated->refs.load()); + Q_ASSERT(d_1_4_Deprecated->refs.loadRelaxed()); } if (d_3_3_Deprecated) { d_3_3_Deprecated->refs.deref(); - Q_ASSERT(d_3_3_Deprecated->refs.load()); + Q_ASSERT(d_3_3_Deprecated->refs.loadRelaxed()); } } diff --git a/src/gui/opengl/qopenglfunctions_4_2_core.cpp b/src/gui/opengl/qopenglfunctions_4_2_core.cpp index fdfb4db455..1381236926 100644 --- a/src/gui/opengl/qopenglfunctions_4_2_core.cpp +++ b/src/gui/opengl/qopenglfunctions_4_2_core.cpp @@ -89,63 +89,63 @@ QOpenGLFunctions_4_2_Core::~QOpenGLFunctions_4_2_Core() { if (d_1_0_Core) { d_1_0_Core->refs.deref(); - Q_ASSERT(d_1_0_Core->refs.load()); + Q_ASSERT(d_1_0_Core->refs.loadRelaxed()); } if (d_1_1_Core) { d_1_1_Core->refs.deref(); - Q_ASSERT(d_1_1_Core->refs.load()); + Q_ASSERT(d_1_1_Core->refs.loadRelaxed()); } if (d_1_2_Core) { d_1_2_Core->refs.deref(); - Q_ASSERT(d_1_2_Core->refs.load()); + Q_ASSERT(d_1_2_Core->refs.loadRelaxed()); } if (d_1_3_Core) { d_1_3_Core->refs.deref(); - Q_ASSERT(d_1_3_Core->refs.load()); + Q_ASSERT(d_1_3_Core->refs.loadRelaxed()); } if (d_1_4_Core) { d_1_4_Core->refs.deref(); - Q_ASSERT(d_1_4_Core->refs.load()); + Q_ASSERT(d_1_4_Core->refs.loadRelaxed()); } if (d_1_5_Core) { d_1_5_Core->refs.deref(); - Q_ASSERT(d_1_5_Core->refs.load()); + Q_ASSERT(d_1_5_Core->refs.loadRelaxed()); } if (d_2_0_Core) { d_2_0_Core->refs.deref(); - Q_ASSERT(d_2_0_Core->refs.load()); + Q_ASSERT(d_2_0_Core->refs.loadRelaxed()); } if (d_2_1_Core) { d_2_1_Core->refs.deref(); - Q_ASSERT(d_2_1_Core->refs.load()); + Q_ASSERT(d_2_1_Core->refs.loadRelaxed()); } if (d_3_0_Core) { d_3_0_Core->refs.deref(); - Q_ASSERT(d_3_0_Core->refs.load()); + Q_ASSERT(d_3_0_Core->refs.loadRelaxed()); } if (d_3_1_Core) { d_3_1_Core->refs.deref(); - Q_ASSERT(d_3_1_Core->refs.load()); + Q_ASSERT(d_3_1_Core->refs.loadRelaxed()); } if (d_3_2_Core) { d_3_2_Core->refs.deref(); - Q_ASSERT(d_3_2_Core->refs.load()); + Q_ASSERT(d_3_2_Core->refs.loadRelaxed()); } if (d_3_3_Core) { d_3_3_Core->refs.deref(); - Q_ASSERT(d_3_3_Core->refs.load()); + Q_ASSERT(d_3_3_Core->refs.loadRelaxed()); } if (d_4_0_Core) { d_4_0_Core->refs.deref(); - Q_ASSERT(d_4_0_Core->refs.load()); + Q_ASSERT(d_4_0_Core->refs.loadRelaxed()); } if (d_4_1_Core) { d_4_1_Core->refs.deref(); - Q_ASSERT(d_4_1_Core->refs.load()); + Q_ASSERT(d_4_1_Core->refs.loadRelaxed()); } if (d_4_2_Core) { d_4_2_Core->refs.deref(); - Q_ASSERT(d_4_2_Core->refs.load()); + Q_ASSERT(d_4_2_Core->refs.loadRelaxed()); } } diff --git a/src/gui/opengl/qopenglfunctions_4_3_compatibility.cpp b/src/gui/opengl/qopenglfunctions_4_3_compatibility.cpp index 131ebc810f..5c0c711d1c 100644 --- a/src/gui/opengl/qopenglfunctions_4_3_compatibility.cpp +++ b/src/gui/opengl/qopenglfunctions_4_3_compatibility.cpp @@ -97,91 +97,91 @@ QOpenGLFunctions_4_3_Compatibility::~QOpenGLFunctions_4_3_Compatibility() { if (d_1_0_Core) { d_1_0_Core->refs.deref(); - Q_ASSERT(d_1_0_Core->refs.load()); + Q_ASSERT(d_1_0_Core->refs.loadRelaxed()); } if (d_1_1_Core) { d_1_1_Core->refs.deref(); - Q_ASSERT(d_1_1_Core->refs.load()); + Q_ASSERT(d_1_1_Core->refs.loadRelaxed()); } if (d_1_2_Core) { d_1_2_Core->refs.deref(); - Q_ASSERT(d_1_2_Core->refs.load()); + Q_ASSERT(d_1_2_Core->refs.loadRelaxed()); } if (d_1_3_Core) { d_1_3_Core->refs.deref(); - Q_ASSERT(d_1_3_Core->refs.load()); + Q_ASSERT(d_1_3_Core->refs.loadRelaxed()); } if (d_1_4_Core) { d_1_4_Core->refs.deref(); - Q_ASSERT(d_1_4_Core->refs.load()); + Q_ASSERT(d_1_4_Core->refs.loadRelaxed()); } if (d_1_5_Core) { d_1_5_Core->refs.deref(); - Q_ASSERT(d_1_5_Core->refs.load()); + Q_ASSERT(d_1_5_Core->refs.loadRelaxed()); } if (d_2_0_Core) { d_2_0_Core->refs.deref(); - Q_ASSERT(d_2_0_Core->refs.load()); + Q_ASSERT(d_2_0_Core->refs.loadRelaxed()); } if (d_2_1_Core) { d_2_1_Core->refs.deref(); - Q_ASSERT(d_2_1_Core->refs.load()); + Q_ASSERT(d_2_1_Core->refs.loadRelaxed()); } if (d_3_0_Core) { d_3_0_Core->refs.deref(); - Q_ASSERT(d_3_0_Core->refs.load()); + Q_ASSERT(d_3_0_Core->refs.loadRelaxed()); } if (d_3_1_Core) { d_3_1_Core->refs.deref(); - Q_ASSERT(d_3_1_Core->refs.load()); + Q_ASSERT(d_3_1_Core->refs.loadRelaxed()); } if (d_3_2_Core) { d_3_2_Core->refs.deref(); - Q_ASSERT(d_3_2_Core->refs.load()); + Q_ASSERT(d_3_2_Core->refs.loadRelaxed()); } if (d_3_3_Core) { d_3_3_Core->refs.deref(); - Q_ASSERT(d_3_3_Core->refs.load()); + Q_ASSERT(d_3_3_Core->refs.loadRelaxed()); } if (d_4_0_Core) { d_4_0_Core->refs.deref(); - Q_ASSERT(d_4_0_Core->refs.load()); + Q_ASSERT(d_4_0_Core->refs.loadRelaxed()); } if (d_4_1_Core) { d_4_1_Core->refs.deref(); - Q_ASSERT(d_4_1_Core->refs.load()); + Q_ASSERT(d_4_1_Core->refs.loadRelaxed()); } if (d_4_2_Core) { d_4_2_Core->refs.deref(); - Q_ASSERT(d_4_2_Core->refs.load()); + Q_ASSERT(d_4_2_Core->refs.loadRelaxed()); } if (d_4_3_Core) { d_4_3_Core->refs.deref(); - Q_ASSERT(d_4_3_Core->refs.load()); + Q_ASSERT(d_4_3_Core->refs.loadRelaxed()); } if (d_1_0_Deprecated) { d_1_0_Deprecated->refs.deref(); - Q_ASSERT(d_1_0_Deprecated->refs.load()); + Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed()); } if (d_1_1_Deprecated) { d_1_1_Deprecated->refs.deref(); - Q_ASSERT(d_1_1_Deprecated->refs.load()); + Q_ASSERT(d_1_1_Deprecated->refs.loadRelaxed()); } if (d_1_2_Deprecated) { d_1_2_Deprecated->refs.deref(); - Q_ASSERT(d_1_2_Deprecated->refs.load()); + Q_ASSERT(d_1_2_Deprecated->refs.loadRelaxed()); } if (d_1_3_Deprecated) { d_1_3_Deprecated->refs.deref(); - Q_ASSERT(d_1_3_Deprecated->refs.load()); + Q_ASSERT(d_1_3_Deprecated->refs.loadRelaxed()); } if (d_1_4_Deprecated) { d_1_4_Deprecated->refs.deref(); - Q_ASSERT(d_1_4_Deprecated->refs.load()); + Q_ASSERT(d_1_4_Deprecated->refs.loadRelaxed()); } if (d_3_3_Deprecated) { d_3_3_Deprecated->refs.deref(); - Q_ASSERT(d_3_3_Deprecated->refs.load()); + Q_ASSERT(d_3_3_Deprecated->refs.loadRelaxed()); } } diff --git a/src/gui/opengl/qopenglfunctions_4_3_core.cpp b/src/gui/opengl/qopenglfunctions_4_3_core.cpp index 95e2d7bc43..34460b841e 100644 --- a/src/gui/opengl/qopenglfunctions_4_3_core.cpp +++ b/src/gui/opengl/qopenglfunctions_4_3_core.cpp @@ -90,67 +90,67 @@ QOpenGLFunctions_4_3_Core::~QOpenGLFunctions_4_3_Core() { if (d_1_0_Core) { d_1_0_Core->refs.deref(); - Q_ASSERT(d_1_0_Core->refs.load()); + Q_ASSERT(d_1_0_Core->refs.loadRelaxed()); } if (d_1_1_Core) { d_1_1_Core->refs.deref(); - Q_ASSERT(d_1_1_Core->refs.load()); + Q_ASSERT(d_1_1_Core->refs.loadRelaxed()); } if (d_1_2_Core) { d_1_2_Core->refs.deref(); - Q_ASSERT(d_1_2_Core->refs.load()); + Q_ASSERT(d_1_2_Core->refs.loadRelaxed()); } if (d_1_3_Core) { d_1_3_Core->refs.deref(); - Q_ASSERT(d_1_3_Core->refs.load()); + Q_ASSERT(d_1_3_Core->refs.loadRelaxed()); } if (d_1_4_Core) { d_1_4_Core->refs.deref(); - Q_ASSERT(d_1_4_Core->refs.load()); + Q_ASSERT(d_1_4_Core->refs.loadRelaxed()); } if (d_1_5_Core) { d_1_5_Core->refs.deref(); - Q_ASSERT(d_1_5_Core->refs.load()); + Q_ASSERT(d_1_5_Core->refs.loadRelaxed()); } if (d_2_0_Core) { d_2_0_Core->refs.deref(); - Q_ASSERT(d_2_0_Core->refs.load()); + Q_ASSERT(d_2_0_Core->refs.loadRelaxed()); } if (d_2_1_Core) { d_2_1_Core->refs.deref(); - Q_ASSERT(d_2_1_Core->refs.load()); + Q_ASSERT(d_2_1_Core->refs.loadRelaxed()); } if (d_3_0_Core) { d_3_0_Core->refs.deref(); - Q_ASSERT(d_3_0_Core->refs.load()); + Q_ASSERT(d_3_0_Core->refs.loadRelaxed()); } if (d_3_1_Core) { d_3_1_Core->refs.deref(); - Q_ASSERT(d_3_1_Core->refs.load()); + Q_ASSERT(d_3_1_Core->refs.loadRelaxed()); } if (d_3_2_Core) { d_3_2_Core->refs.deref(); - Q_ASSERT(d_3_2_Core->refs.load()); + Q_ASSERT(d_3_2_Core->refs.loadRelaxed()); } if (d_3_3_Core) { d_3_3_Core->refs.deref(); - Q_ASSERT(d_3_3_Core->refs.load()); + Q_ASSERT(d_3_3_Core->refs.loadRelaxed()); } if (d_4_0_Core) { d_4_0_Core->refs.deref(); - Q_ASSERT(d_4_0_Core->refs.load()); + Q_ASSERT(d_4_0_Core->refs.loadRelaxed()); } if (d_4_1_Core) { d_4_1_Core->refs.deref(); - Q_ASSERT(d_4_1_Core->refs.load()); + Q_ASSERT(d_4_1_Core->refs.loadRelaxed()); } if (d_4_2_Core) { d_4_2_Core->refs.deref(); - Q_ASSERT(d_4_2_Core->refs.load()); + Q_ASSERT(d_4_2_Core->refs.loadRelaxed()); } if (d_4_3_Core) { d_4_3_Core->refs.deref(); - Q_ASSERT(d_4_3_Core->refs.load()); + Q_ASSERT(d_4_3_Core->refs.loadRelaxed()); } } diff --git a/src/gui/opengl/qopenglfunctions_4_4_compatibility.cpp b/src/gui/opengl/qopenglfunctions_4_4_compatibility.cpp index 5c7170b8fa..907994a3c4 100644 --- a/src/gui/opengl/qopenglfunctions_4_4_compatibility.cpp +++ b/src/gui/opengl/qopenglfunctions_4_4_compatibility.cpp @@ -97,95 +97,95 @@ QOpenGLFunctions_4_4_Compatibility::~QOpenGLFunctions_4_4_Compatibility() { if (d_1_0_Core) { d_1_0_Core->refs.deref(); - Q_ASSERT(d_1_0_Core->refs.load()); + Q_ASSERT(d_1_0_Core->refs.loadRelaxed()); } if (d_1_1_Core) { d_1_1_Core->refs.deref(); - Q_ASSERT(d_1_1_Core->refs.load()); + Q_ASSERT(d_1_1_Core->refs.loadRelaxed()); } if (d_1_2_Core) { d_1_2_Core->refs.deref(); - Q_ASSERT(d_1_2_Core->refs.load()); + Q_ASSERT(d_1_2_Core->refs.loadRelaxed()); } if (d_1_3_Core) { d_1_3_Core->refs.deref(); - Q_ASSERT(d_1_3_Core->refs.load()); + Q_ASSERT(d_1_3_Core->refs.loadRelaxed()); } if (d_1_4_Core) { d_1_4_Core->refs.deref(); - Q_ASSERT(d_1_4_Core->refs.load()); + Q_ASSERT(d_1_4_Core->refs.loadRelaxed()); } if (d_1_5_Core) { d_1_5_Core->refs.deref(); - Q_ASSERT(d_1_5_Core->refs.load()); + Q_ASSERT(d_1_5_Core->refs.loadRelaxed()); } if (d_2_0_Core) { d_2_0_Core->refs.deref(); - Q_ASSERT(d_2_0_Core->refs.load()); + Q_ASSERT(d_2_0_Core->refs.loadRelaxed()); } if (d_2_1_Core) { d_2_1_Core->refs.deref(); - Q_ASSERT(d_2_1_Core->refs.load()); + Q_ASSERT(d_2_1_Core->refs.loadRelaxed()); } if (d_3_0_Core) { d_3_0_Core->refs.deref(); - Q_ASSERT(d_3_0_Core->refs.load()); + Q_ASSERT(d_3_0_Core->refs.loadRelaxed()); } if (d_3_1_Core) { d_3_1_Core->refs.deref(); - Q_ASSERT(d_3_1_Core->refs.load()); + Q_ASSERT(d_3_1_Core->refs.loadRelaxed()); } if (d_3_2_Core) { d_3_2_Core->refs.deref(); - Q_ASSERT(d_3_2_Core->refs.load()); + Q_ASSERT(d_3_2_Core->refs.loadRelaxed()); } if (d_3_3_Core) { d_3_3_Core->refs.deref(); - Q_ASSERT(d_3_3_Core->refs.load()); + Q_ASSERT(d_3_3_Core->refs.loadRelaxed()); } if (d_4_0_Core) { d_4_0_Core->refs.deref(); - Q_ASSERT(d_4_0_Core->refs.load()); + Q_ASSERT(d_4_0_Core->refs.loadRelaxed()); } if (d_4_1_Core) { d_4_1_Core->refs.deref(); - Q_ASSERT(d_4_1_Core->refs.load()); + Q_ASSERT(d_4_1_Core->refs.loadRelaxed()); } if (d_4_2_Core) { d_4_2_Core->refs.deref(); - Q_ASSERT(d_4_2_Core->refs.load()); + Q_ASSERT(d_4_2_Core->refs.loadRelaxed()); } if (d_4_3_Core) { d_4_3_Core->refs.deref(); - Q_ASSERT(d_4_3_Core->refs.load()); + Q_ASSERT(d_4_3_Core->refs.loadRelaxed()); } if (d_4_4_Core) { d_4_4_Core->refs.deref(); - Q_ASSERT(d_4_4_Core->refs.load()); + Q_ASSERT(d_4_4_Core->refs.loadRelaxed()); } if (d_1_0_Deprecated) { d_1_0_Deprecated->refs.deref(); - Q_ASSERT(d_1_0_Deprecated->refs.load()); + Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed()); } if (d_1_1_Deprecated) { d_1_1_Deprecated->refs.deref(); - Q_ASSERT(d_1_1_Deprecated->refs.load()); + Q_ASSERT(d_1_1_Deprecated->refs.loadRelaxed()); } if (d_1_2_Deprecated) { d_1_2_Deprecated->refs.deref(); - Q_ASSERT(d_1_2_Deprecated->refs.load()); + Q_ASSERT(d_1_2_Deprecated->refs.loadRelaxed()); } if (d_1_3_Deprecated) { d_1_3_Deprecated->refs.deref(); - Q_ASSERT(d_1_3_Deprecated->refs.load()); + Q_ASSERT(d_1_3_Deprecated->refs.loadRelaxed()); } if (d_1_4_Deprecated) { d_1_4_Deprecated->refs.deref(); - Q_ASSERT(d_1_4_Deprecated->refs.load()); + Q_ASSERT(d_1_4_Deprecated->refs.loadRelaxed()); } if (d_3_3_Deprecated) { d_3_3_Deprecated->refs.deref(); - Q_ASSERT(d_3_3_Deprecated->refs.load()); + Q_ASSERT(d_3_3_Deprecated->refs.loadRelaxed()); } } diff --git a/src/gui/opengl/qopenglfunctions_4_4_core.cpp b/src/gui/opengl/qopenglfunctions_4_4_core.cpp index 54833f9058..76c0323f6d 100644 --- a/src/gui/opengl/qopenglfunctions_4_4_core.cpp +++ b/src/gui/opengl/qopenglfunctions_4_4_core.cpp @@ -91,71 +91,71 @@ QOpenGLFunctions_4_4_Core::~QOpenGLFunctions_4_4_Core() { if (d_1_0_Core) { d_1_0_Core->refs.deref(); - Q_ASSERT(d_1_0_Core->refs.load()); + Q_ASSERT(d_1_0_Core->refs.loadRelaxed()); } if (d_1_1_Core) { d_1_1_Core->refs.deref(); - Q_ASSERT(d_1_1_Core->refs.load()); + Q_ASSERT(d_1_1_Core->refs.loadRelaxed()); } if (d_1_2_Core) { d_1_2_Core->refs.deref(); - Q_ASSERT(d_1_2_Core->refs.load()); + Q_ASSERT(d_1_2_Core->refs.loadRelaxed()); } if (d_1_3_Core) { d_1_3_Core->refs.deref(); - Q_ASSERT(d_1_3_Core->refs.load()); + Q_ASSERT(d_1_3_Core->refs.loadRelaxed()); } if (d_1_4_Core) { d_1_4_Core->refs.deref(); - Q_ASSERT(d_1_4_Core->refs.load()); + Q_ASSERT(d_1_4_Core->refs.loadRelaxed()); } if (d_1_5_Core) { d_1_5_Core->refs.deref(); - Q_ASSERT(d_1_5_Core->refs.load()); + Q_ASSERT(d_1_5_Core->refs.loadRelaxed()); } if (d_2_0_Core) { d_2_0_Core->refs.deref(); - Q_ASSERT(d_2_0_Core->refs.load()); + Q_ASSERT(d_2_0_Core->refs.loadRelaxed()); } if (d_2_1_Core) { d_2_1_Core->refs.deref(); - Q_ASSERT(d_2_1_Core->refs.load()); + Q_ASSERT(d_2_1_Core->refs.loadRelaxed()); } if (d_3_0_Core) { d_3_0_Core->refs.deref(); - Q_ASSERT(d_3_0_Core->refs.load()); + Q_ASSERT(d_3_0_Core->refs.loadRelaxed()); } if (d_3_1_Core) { d_3_1_Core->refs.deref(); - Q_ASSERT(d_3_1_Core->refs.load()); + Q_ASSERT(d_3_1_Core->refs.loadRelaxed()); } if (d_3_2_Core) { d_3_2_Core->refs.deref(); - Q_ASSERT(d_3_2_Core->refs.load()); + Q_ASSERT(d_3_2_Core->refs.loadRelaxed()); } if (d_3_3_Core) { d_3_3_Core->refs.deref(); - Q_ASSERT(d_3_3_Core->refs.load()); + Q_ASSERT(d_3_3_Core->refs.loadRelaxed()); } if (d_4_0_Core) { d_4_0_Core->refs.deref(); - Q_ASSERT(d_4_0_Core->refs.load()); + Q_ASSERT(d_4_0_Core->refs.loadRelaxed()); } if (d_4_1_Core) { d_4_1_Core->refs.deref(); - Q_ASSERT(d_4_1_Core->refs.load()); + Q_ASSERT(d_4_1_Core->refs.loadRelaxed()); } if (d_4_2_Core) { d_4_2_Core->refs.deref(); - Q_ASSERT(d_4_2_Core->refs.load()); + Q_ASSERT(d_4_2_Core->refs.loadRelaxed()); } if (d_4_3_Core) { d_4_3_Core->refs.deref(); - Q_ASSERT(d_4_3_Core->refs.load()); + Q_ASSERT(d_4_3_Core->refs.loadRelaxed()); } if (d_4_4_Core) { d_4_4_Core->refs.deref(); - Q_ASSERT(d_4_4_Core->refs.load()); + Q_ASSERT(d_4_4_Core->refs.loadRelaxed()); } } diff --git a/src/gui/opengl/qopenglfunctions_4_5_compatibility.cpp b/src/gui/opengl/qopenglfunctions_4_5_compatibility.cpp index f9d2bb9ceb..c415bb06ff 100644 --- a/src/gui/opengl/qopenglfunctions_4_5_compatibility.cpp +++ b/src/gui/opengl/qopenglfunctions_4_5_compatibility.cpp @@ -99,103 +99,103 @@ QOpenGLFunctions_4_5_Compatibility::~QOpenGLFunctions_4_5_Compatibility() { if (d_1_0_Core) { d_1_0_Core->refs.deref(); - Q_ASSERT(d_1_0_Core->refs.load()); + Q_ASSERT(d_1_0_Core->refs.loadRelaxed()); } if (d_1_1_Core) { d_1_1_Core->refs.deref(); - Q_ASSERT(d_1_1_Core->refs.load()); + Q_ASSERT(d_1_1_Core->refs.loadRelaxed()); } if (d_1_2_Core) { d_1_2_Core->refs.deref(); - Q_ASSERT(d_1_2_Core->refs.load()); + Q_ASSERT(d_1_2_Core->refs.loadRelaxed()); } if (d_1_3_Core) { d_1_3_Core->refs.deref(); - Q_ASSERT(d_1_3_Core->refs.load()); + Q_ASSERT(d_1_3_Core->refs.loadRelaxed()); } if (d_1_4_Core) { d_1_4_Core->refs.deref(); - Q_ASSERT(d_1_4_Core->refs.load()); + Q_ASSERT(d_1_4_Core->refs.loadRelaxed()); } if (d_1_5_Core) { d_1_5_Core->refs.deref(); - Q_ASSERT(d_1_5_Core->refs.load()); + Q_ASSERT(d_1_5_Core->refs.loadRelaxed()); } if (d_2_0_Core) { d_2_0_Core->refs.deref(); - Q_ASSERT(d_2_0_Core->refs.load()); + Q_ASSERT(d_2_0_Core->refs.loadRelaxed()); } if (d_2_1_Core) { d_2_1_Core->refs.deref(); - Q_ASSERT(d_2_1_Core->refs.load()); + Q_ASSERT(d_2_1_Core->refs.loadRelaxed()); } if (d_3_0_Core) { d_3_0_Core->refs.deref(); - Q_ASSERT(d_3_0_Core->refs.load()); + Q_ASSERT(d_3_0_Core->refs.loadRelaxed()); } if (d_3_1_Core) { d_3_1_Core->refs.deref(); - Q_ASSERT(d_3_1_Core->refs.load()); + Q_ASSERT(d_3_1_Core->refs.loadRelaxed()); } if (d_3_2_Core) { d_3_2_Core->refs.deref(); - Q_ASSERT(d_3_2_Core->refs.load()); + Q_ASSERT(d_3_2_Core->refs.loadRelaxed()); } if (d_3_3_Core) { d_3_3_Core->refs.deref(); - Q_ASSERT(d_3_3_Core->refs.load()); + Q_ASSERT(d_3_3_Core->refs.loadRelaxed()); } if (d_4_0_Core) { d_4_0_Core->refs.deref(); - Q_ASSERT(d_4_0_Core->refs.load()); + Q_ASSERT(d_4_0_Core->refs.loadRelaxed()); } if (d_4_1_Core) { d_4_1_Core->refs.deref(); - Q_ASSERT(d_4_1_Core->refs.load()); + Q_ASSERT(d_4_1_Core->refs.loadRelaxed()); } if (d_4_2_Core) { d_4_2_Core->refs.deref(); - Q_ASSERT(d_4_2_Core->refs.load()); + Q_ASSERT(d_4_2_Core->refs.loadRelaxed()); } if (d_4_3_Core) { d_4_3_Core->refs.deref(); - Q_ASSERT(d_4_3_Core->refs.load()); + Q_ASSERT(d_4_3_Core->refs.loadRelaxed()); } if (d_4_4_Core) { d_4_4_Core->refs.deref(); - Q_ASSERT(d_4_4_Core->refs.load()); + Q_ASSERT(d_4_4_Core->refs.loadRelaxed()); } if (d_4_5_Core) { d_4_5_Core->refs.deref(); - Q_ASSERT(d_4_5_Core->refs.load()); + Q_ASSERT(d_4_5_Core->refs.loadRelaxed()); } if (d_1_0_Deprecated) { d_1_0_Deprecated->refs.deref(); - Q_ASSERT(d_1_0_Deprecated->refs.load()); + Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed()); } if (d_1_1_Deprecated) { d_1_1_Deprecated->refs.deref(); - Q_ASSERT(d_1_1_Deprecated->refs.load()); + Q_ASSERT(d_1_1_Deprecated->refs.loadRelaxed()); } if (d_1_2_Deprecated) { d_1_2_Deprecated->refs.deref(); - Q_ASSERT(d_1_2_Deprecated->refs.load()); + Q_ASSERT(d_1_2_Deprecated->refs.loadRelaxed()); } if (d_1_3_Deprecated) { d_1_3_Deprecated->refs.deref(); - Q_ASSERT(d_1_3_Deprecated->refs.load()); + Q_ASSERT(d_1_3_Deprecated->refs.loadRelaxed()); } if (d_1_4_Deprecated) { d_1_4_Deprecated->refs.deref(); - Q_ASSERT(d_1_4_Deprecated->refs.load()); + Q_ASSERT(d_1_4_Deprecated->refs.loadRelaxed()); } if (d_3_3_Deprecated) { d_3_3_Deprecated->refs.deref(); - Q_ASSERT(d_3_3_Deprecated->refs.load()); + Q_ASSERT(d_3_3_Deprecated->refs.loadRelaxed()); } if (d_4_5_Deprecated) { d_4_5_Deprecated->refs.deref(); - Q_ASSERT(d_4_5_Deprecated->refs.load()); + Q_ASSERT(d_4_5_Deprecated->refs.loadRelaxed()); } } diff --git a/src/gui/opengl/qopenglfunctions_4_5_core.cpp b/src/gui/opengl/qopenglfunctions_4_5_core.cpp index a47ebb9ee9..4dfac3579c 100644 --- a/src/gui/opengl/qopenglfunctions_4_5_core.cpp +++ b/src/gui/opengl/qopenglfunctions_4_5_core.cpp @@ -92,75 +92,75 @@ QOpenGLFunctions_4_5_Core::~QOpenGLFunctions_4_5_Core() { if (d_1_0_Core) { d_1_0_Core->refs.deref(); - Q_ASSERT(d_1_0_Core->refs.load()); + Q_ASSERT(d_1_0_Core->refs.loadRelaxed()); } if (d_1_1_Core) { d_1_1_Core->refs.deref(); - Q_ASSERT(d_1_1_Core->refs.load()); + Q_ASSERT(d_1_1_Core->refs.loadRelaxed()); } if (d_1_2_Core) { d_1_2_Core->refs.deref(); - Q_ASSERT(d_1_2_Core->refs.load()); + Q_ASSERT(d_1_2_Core->refs.loadRelaxed()); } if (d_1_3_Core) { d_1_3_Core->refs.deref(); - Q_ASSERT(d_1_3_Core->refs.load()); + Q_ASSERT(d_1_3_Core->refs.loadRelaxed()); } if (d_1_4_Core) { d_1_4_Core->refs.deref(); - Q_ASSERT(d_1_4_Core->refs.load()); + Q_ASSERT(d_1_4_Core->refs.loadRelaxed()); } if (d_1_5_Core) { d_1_5_Core->refs.deref(); - Q_ASSERT(d_1_5_Core->refs.load()); + Q_ASSERT(d_1_5_Core->refs.loadRelaxed()); } if (d_2_0_Core) { d_2_0_Core->refs.deref(); - Q_ASSERT(d_2_0_Core->refs.load()); + Q_ASSERT(d_2_0_Core->refs.loadRelaxed()); } if (d_2_1_Core) { d_2_1_Core->refs.deref(); - Q_ASSERT(d_2_1_Core->refs.load()); + Q_ASSERT(d_2_1_Core->refs.loadRelaxed()); } if (d_3_0_Core) { d_3_0_Core->refs.deref(); - Q_ASSERT(d_3_0_Core->refs.load()); + Q_ASSERT(d_3_0_Core->refs.loadRelaxed()); } if (d_3_1_Core) { d_3_1_Core->refs.deref(); - Q_ASSERT(d_3_1_Core->refs.load()); + Q_ASSERT(d_3_1_Core->refs.loadRelaxed()); } if (d_3_2_Core) { d_3_2_Core->refs.deref(); - Q_ASSERT(d_3_2_Core->refs.load()); + Q_ASSERT(d_3_2_Core->refs.loadRelaxed()); } if (d_3_3_Core) { d_3_3_Core->refs.deref(); - Q_ASSERT(d_3_3_Core->refs.load()); + Q_ASSERT(d_3_3_Core->refs.loadRelaxed()); } if (d_4_0_Core) { d_4_0_Core->refs.deref(); - Q_ASSERT(d_4_0_Core->refs.load()); + Q_ASSERT(d_4_0_Core->refs.loadRelaxed()); } if (d_4_1_Core) { d_4_1_Core->refs.deref(); - Q_ASSERT(d_4_1_Core->refs.load()); + Q_ASSERT(d_4_1_Core->refs.loadRelaxed()); } if (d_4_2_Core) { d_4_2_Core->refs.deref(); - Q_ASSERT(d_4_2_Core->refs.load()); + Q_ASSERT(d_4_2_Core->refs.loadRelaxed()); } if (d_4_3_Core) { d_4_3_Core->refs.deref(); - Q_ASSERT(d_4_3_Core->refs.load()); + Q_ASSERT(d_4_3_Core->refs.loadRelaxed()); } if (d_4_4_Core) { d_4_4_Core->refs.deref(); - Q_ASSERT(d_4_4_Core->refs.load()); + Q_ASSERT(d_4_4_Core->refs.loadRelaxed()); } if (d_4_5_Core) { d_4_5_Core->refs.deref(); - Q_ASSERT(d_4_5_Core->refs.load()); + Q_ASSERT(d_4_5_Core->refs.loadRelaxed()); } } diff --git a/src/gui/painting/qbrush.cpp b/src/gui/painting/qbrush.cpp index 21e41979af..abb3268dfa 100644 --- a/src/gui/painting/qbrush.cpp +++ b/src/gui/painting/qbrush.cpp @@ -352,7 +352,7 @@ public: QBrushData *brush; QNullBrushData() : brush(new QBrushData) { - brush->ref.store(1); + brush->ref.storeRelaxed(1); brush->style = Qt::BrushStyle(0); brush->color = Qt::black; } @@ -411,7 +411,7 @@ void QBrush::init(const QColor &color, Qt::BrushStyle style) d.reset(new QBrushData); break; } - d->ref.store(1); + d->ref.storeRelaxed(1); d->style = style; d->color = color; } @@ -585,7 +585,7 @@ static Q_DECL_CONSTEXPR inline bool use_same_brushdata(Qt::BrushStyle lhs, Qt::B void QBrush::detach(Qt::BrushStyle newStyle) { - if (use_same_brushdata(newStyle, d->style) && d->ref.load() == 1) { + if (use_same_brushdata(newStyle, d->style) && d->ref.loadRelaxed() == 1) { d->style = newStyle; return; } @@ -625,7 +625,7 @@ void QBrush::detach(Qt::BrushStyle newStyle) x.reset(new QBrushData); break; } - x->ref.store(1); // must be first lest the QBrushDataPointerDeleter turns into a no-op + x->ref.storeRelaxed(1); // must be first lest the QBrushDataPointerDeleter turns into a no-op x->style = newStyle; x->color = d->color; x->transform = d->transform; diff --git a/src/gui/painting/qbrush.h b/src/gui/painting/qbrush.h index 43e1eb7472..6a4ffab1c5 100644 --- a/src/gui/painting/qbrush.h +++ b/src/gui/painting/qbrush.h @@ -159,7 +159,7 @@ inline Qt::BrushStyle QBrush::style() const { return d->style; } inline const QColor &QBrush::color() const { return d->color; } inline const QMatrix &QBrush::matrix() const { return d->transform.toAffine(); } inline QTransform QBrush::transform() const { return d->transform; } -inline bool QBrush::isDetached() const { return d->ref.load() == 1; } +inline bool QBrush::isDetached() const { return d->ref.loadRelaxed() == 1; } /******************************************************************************* diff --git a/src/gui/painting/qcolorspace_p.h b/src/gui/painting/qcolorspace_p.h index a49c46f195..95e0655d0c 100644 --- a/src/gui/painting/qcolorspace_p.h +++ b/src/gui/painting/qcolorspace_p.h @@ -123,7 +123,7 @@ public: table[0] = other.table[0]; table[1] = other.table[1]; table[2] = other.table[2]; - generated.store(1); + generated.storeRelaxed(1); } } QSharedPointer &operator[](int i) { return table[i]; } diff --git a/src/gui/painting/qcolortransform.cpp b/src/gui/painting/qcolortransform.cpp index 2f81449693..de08bf4221 100644 --- a/src/gui/painting/qcolortransform.cpp +++ b/src/gui/painting/qcolortransform.cpp @@ -71,7 +71,7 @@ void QColorTransformPrivate::updateLutsIn() const if (colorSpaceIn->lut.generated.loadAcquire()) return; QMutexLocker lock(&QColorSpacePrivate::s_lutWriteLock); - if (colorSpaceIn->lut.generated.load()) + if (colorSpaceIn->lut.generated.loadRelaxed()) return; for (int i = 0; i < 3; ++i) { @@ -96,7 +96,7 @@ void QColorTransformPrivate::updateLutsOut() const if (colorSpaceOut->lut.generated.loadAcquire()) return; QMutexLocker lock(&QColorSpacePrivate::s_lutWriteLock); - if (colorSpaceOut->lut.generated.load()) + if (colorSpaceOut->lut.generated.loadRelaxed()) return; for (int i = 0; i < 3; ++i) { if (!colorSpaceOut->trc[i].isValid()) diff --git a/src/gui/painting/qpainterpath.cpp b/src/gui/painting/qpainterpath.cpp index a0db35ebd3..70ad6bdc97 100644 --- a/src/gui/painting/qpainterpath.cpp +++ b/src/gui/painting/qpainterpath.cpp @@ -578,7 +578,7 @@ QPainterPath::QPainterPath(const QPointF &startPoint) void QPainterPath::detach() { - if (d_ptr->ref.load() != 1) + if (d_ptr->ref.loadRelaxed() != 1) detach_helper(); setDirty(true); } diff --git a/src/gui/painting/qpainterpath_p.h b/src/gui/painting/qpainterpath_p.h index 4eb541ec65..22bdbde2a9 100644 --- a/src/gui/painting/qpainterpath_p.h +++ b/src/gui/painting/qpainterpath_p.h @@ -281,7 +281,7 @@ inline bool QPainterPathData::isClosed() const inline void QPainterPathData::close() { - Q_ASSERT(ref.load() == 1); + Q_ASSERT(ref.loadRelaxed() == 1); require_moveTo = true; const QPainterPath::Element &first = elements.at(cStart); QPainterPath::Element &last = elements.last(); @@ -308,7 +308,7 @@ inline void QPainterPathData::maybeMoveTo() inline void QPainterPathData::clear() { - Q_ASSERT(ref.load() == 1); + Q_ASSERT(ref.loadRelaxed() == 1); elements.clear(); diff --git a/src/gui/painting/qpen.cpp b/src/gui/painting/qpen.cpp index 58a1716037..dc6e3e04d0 100644 --- a/src/gui/painting/qpen.cpp +++ b/src/gui/painting/qpen.cpp @@ -363,13 +363,13 @@ QPen::~QPen() void QPen::detach() { - if (d->ref.load() == 1) + if (d->ref.loadRelaxed() == 1) return; QPenData *x = new QPenData(*static_cast(d)); if (!d->ref.deref()) delete d; - x->ref.store(1); + x->ref.storeRelaxed(1); d = x; } @@ -885,7 +885,7 @@ bool QPen::operator==(const QPen &p) const bool QPen::isDetached() { - return d->ref.load() == 1; + return d->ref.loadRelaxed() == 1; } diff --git a/src/gui/text/qdistancefield.cpp b/src/gui/text/qdistancefield.cpp index 5967c8d3de..3f7129383b 100644 --- a/src/gui/text/qdistancefield.cpp +++ b/src/gui/text/qdistancefield.cpp @@ -782,7 +782,7 @@ bool qt_fontHasNarrowOutlines(QFontEngine *fontEngine) if (glyph != 0) im = fe->alphaMapForGlyph(glyph, QFixed(), QTransform()); - Q_ASSERT(fe->ref.load() == 0); + Q_ASSERT(fe->ref.loadRelaxed() == 0); delete fe; return imageHasNarrowOutlines(im); diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp index 0249a20cc6..5555422b82 100644 --- a/src/gui/text/qfont.cpp +++ b/src/gui/text/qfont.cpp @@ -336,7 +336,7 @@ QFontEngineData::QFontEngineData() QFontEngineData::~QFontEngineData() { - Q_ASSERT(ref.load() == 0); + Q_ASSERT(ref.loadRelaxed() == 0); for (int i = 0; i < QChar::ScriptCount; ++i) { if (engines[i]) { if (!engines[i]->ref.deref()) @@ -604,7 +604,7 @@ QFont::QFont(QFontPrivate *data) */ void QFont::detach() { - if (d->ref.load() == 1) { + if (d->ref.loadRelaxed() == 1) { if (d->engineData && !d->engineData->ref.deref()) delete d->engineData; d->engineData = 0; @@ -625,7 +625,7 @@ void QFont::detach() */ void QFontPrivate::detachButKeepEngineData(QFont *font) { - if (font->d->ref.load() == 1) + if (font->d->ref.loadRelaxed() == 1) return; QFontEngineData *engineData = font->d->engineData; @@ -2833,7 +2833,7 @@ void QFontCache::clear() delete data; } else { FC_DEBUG("QFontCache::clear: engineData %p still has refcount %d", - data, data->ref.load()); + data, data->ref.loadRelaxed()); } ++it; } @@ -2857,7 +2857,7 @@ void QFontCache::clear() delete engine; } else if (cacheCount == 0) { FC_DEBUG("QFontCache::clear: engine %p still has refcount %d", - engine, engine->ref.load()); + engine, engine->ref.loadRelaxed()); } it.value().data = 0; } @@ -2927,7 +2927,7 @@ void QFontCache::updateHitCountAndTimeStamp(Engine &value) FC_DEBUG("QFontCache: found font engine\n" " %p: timestamp %4u hits %3u ref %2d/%2d, type %d", value.data, value.timestamp, value.hits, - value.data->ref.load(), engineCacheCount.value(value.data), + value.data->ref.loadRelaxed(), engineCacheCount.value(value.data), value.data->type()); } @@ -2937,7 +2937,7 @@ void QFontCache::insertEngine(const Key &key, QFontEngine *engine, bool insertMu Q_ASSERT(key.multi == (engine->type() == QFontEngine::Multi)); #ifdef QFONTCACHE_DEBUG - FC_DEBUG("QFontCache: inserting new engine %p, refcount %d", engine, engine->ref.load()); + FC_DEBUG("QFontCache: inserting new engine %p, refcount %d", engine, engine->ref.loadRelaxed()); if (!insertMulti && engineCache.contains(key)) { FC_DEBUG(" QFontCache already contains engine %p for key=(%g %g %d %d %d)", engineCache.value(key).data, key.def.pointSize, @@ -3026,9 +3026,9 @@ void QFontCache::decreaseCache() EngineDataCache::ConstIterator it = engineDataCache.constBegin(), end = engineDataCache.constEnd(); for (; it != end; ++it) { - FC_DEBUG(" %p: ref %2d", it.value(), int(it.value()->ref.load())); + FC_DEBUG(" %p: ref %2d", it.value(), int(it.value()->ref.loadRelaxed())); - if (it.value()->ref.load() != 1) + if (it.value()->ref.loadRelaxed() != 1) in_use_cost += engine_data_cost; } } @@ -3041,10 +3041,10 @@ void QFontCache::decreaseCache() for (; it != end; ++it) { FC_DEBUG(" %p: timestamp %4u hits %2u ref %2d/%2d, cost %u bytes", it.value().data, it.value().timestamp, it.value().hits, - it.value().data->ref.load(), engineCacheCount.value(it.value().data), + it.value().data->ref.loadRelaxed(), engineCacheCount.value(it.value().data), it.value().data->cache_cost); - if (it.value().data->ref.load() > engineCacheCount.value(it.value().data)) + if (it.value().data->ref.loadRelaxed() > engineCacheCount.value(it.value().data)) in_use_cost += it.value().data->cache_cost / engineCacheCount.value(it.value().data); } @@ -3093,7 +3093,7 @@ void QFontCache::decreaseCache() // clean out all unused engine data EngineDataCache::Iterator it = engineDataCache.begin(); while (it != engineDataCache.end()) { - if (it.value()->ref.load() == 1) { + if (it.value()->ref.loadRelaxed() == 1) { FC_DEBUG(" %p", it.value()); decreaseCost(sizeof(QFontEngineData)); it.value()->ref.deref(); @@ -3121,7 +3121,7 @@ void QFontCache::decreaseCache() EngineCache::Iterator jt = end; for ( ; it != end; ++it) { - if (it.value().data->ref.load() != engineCacheCount.value(it.value().data)) + if (it.value().data->ref.loadRelaxed() != engineCacheCount.value(it.value().data)) continue; if (it.value().timestamp < oldest && it.value().hits <= least_popular) { @@ -3135,7 +3135,7 @@ void QFontCache::decreaseCache() if (it != end) { FC_DEBUG(" %p: timestamp %4u hits %2u ref %2d/%2d, type %d", it.value().data, it.value().timestamp, it.value().hits, - it.value().data->ref.load(), engineCacheCount.value(it.value().data), + it.value().data->ref.loadRelaxed(), engineCacheCount.value(it.value().data), it.value().data->type()); QFontEngine *fontEngine = it.value().data; @@ -3150,7 +3150,7 @@ void QFontCache::decreaseCache() } } // and delete the last occurrence - Q_ASSERT(fontEngine->ref.load() == 0); + Q_ASSERT(fontEngine->ref.loadRelaxed() == 0); decreaseCost(fontEngine->cache_cost); delete fontEngine; engineCacheCount.remove(fontEngine); diff --git a/src/gui/text/qfontdatabase.cpp b/src/gui/text/qfontdatabase.cpp index 36104991c3..5350a9c5ec 100644 --- a/src/gui/text/qfontdatabase.cpp +++ b/src/gui/text/qfontdatabase.cpp @@ -978,7 +978,7 @@ QFontEngine *loadSingleEngine(int script, if (!engine->supportsScript(QChar::Script(script))) { qWarning(" OpenType support missing for \"%s\", script %d", + qPrintable(def.family), script); - if (engine->ref.load() == 0) + if (engine->ref.loadRelaxed() == 0) delete engine; return 0; } @@ -2827,7 +2827,7 @@ void QFontDatabase::load(const QFontPrivate *d, int script) fe = QFontDatabase::findFont(req, script); if (fe) { if (fe->type() == QFontEngine::Box && !req.families.at(0).isEmpty()) { - if (fe->ref.load() == 0) + if (fe->ref.loadRelaxed() == 0) delete fe; fe = 0; } else { diff --git a/src/gui/text/qglyphrun.cpp b/src/gui/text/qglyphrun.cpp index bd44e11dce..3c16c3bf62 100644 --- a/src/gui/text/qglyphrun.cpp +++ b/src/gui/text/qglyphrun.cpp @@ -137,7 +137,7 @@ QGlyphRun::~QGlyphRun() */ void QGlyphRun::detach() { - if (d->ref.load() != 1) + if (d->ref.loadRelaxed() != 1) d.detach(); } diff --git a/src/gui/text/qplatformfontdatabase.cpp b/src/gui/text/qplatformfontdatabase.cpp index 715b00d838..90322b24da 100644 --- a/src/gui/text/qplatformfontdatabase.cpp +++ b/src/gui/text/qplatformfontdatabase.cpp @@ -235,7 +235,7 @@ QSupportedWritingSystems::~QSupportedWritingSystems() */ void QSupportedWritingSystems::detach() { - if (d->ref.load() != 1) { + if (d->ref.loadRelaxed() != 1) { QWritingSystemsPrivate *newd = new QWritingSystemsPrivate(d); if (!d->ref.deref()) delete d; diff --git a/src/gui/text/qrawfont_p.h b/src/gui/text/qrawfont_p.h index dced165475..03259a94ed 100644 --- a/src/gui/text/qrawfont_p.h +++ b/src/gui/text/qrawfont_p.h @@ -87,7 +87,7 @@ public: ~QRawFontPrivate() { #ifndef QT_NO_DEBUG - Q_ASSERT(ref.load() == 0); + Q_ASSERT(ref.loadRelaxed() == 0); #endif cleanUp(); } diff --git a/src/gui/text/qstatictext.cpp b/src/gui/text/qstatictext.cpp index dd894f4d32..490e0b6b8f 100644 --- a/src/gui/text/qstatictext.cpp +++ b/src/gui/text/qstatictext.cpp @@ -181,7 +181,7 @@ QStaticText::QStaticText(const QStaticText &other) */ QStaticText::~QStaticText() { - Q_ASSERT(!data || data->ref.load() >= 1); + Q_ASSERT(!data || data->ref.loadRelaxed() >= 1); } /*! @@ -189,7 +189,7 @@ QStaticText::~QStaticText() */ void QStaticText::detach() { - if (data->ref.load() != 1) + if (data->ref.loadRelaxed() != 1) data.detach(); } diff --git a/src/network/access/qnetworkaccessbackend.cpp b/src/network/access/qnetworkaccessbackend.cpp index 272dd22097..848fc84de7 100644 --- a/src/network/access/qnetworkaccessbackend.cpp +++ b/src/network/access/qnetworkaccessbackend.cpp @@ -83,7 +83,7 @@ QNetworkAccessBackendFactory::QNetworkAccessBackendFactory() QNetworkAccessBackendFactory::~QNetworkAccessBackendFactory() { - if (QNetworkAccessBackendFactoryData::valid.load()) { + if (QNetworkAccessBackendFactoryData::valid.loadRelaxed()) { QMutexLocker locker(&factoryData()->mutex); factoryData()->removeAll(this); } @@ -92,7 +92,7 @@ QNetworkAccessBackendFactory::~QNetworkAccessBackendFactory() QNetworkAccessBackend *QNetworkAccessManagerPrivate::findBackend(QNetworkAccessManager::Operation op, const QNetworkRequest &request) { - if (QNetworkAccessBackendFactoryData::valid.load()) { + if (QNetworkAccessBackendFactoryData::valid.loadRelaxed()) { QMutexLocker locker(&factoryData()->mutex); QNetworkAccessBackendFactoryData::ConstIterator it = factoryData()->constBegin(), end = factoryData()->constEnd(); @@ -110,7 +110,7 @@ QNetworkAccessBackend *QNetworkAccessManagerPrivate::findBackend(QNetworkAccessM QStringList QNetworkAccessManagerPrivate::backendSupportedSchemes() const { - if (QNetworkAccessBackendFactoryData::valid.load()) { + if (QNetworkAccessBackendFactoryData::valid.loadRelaxed()) { QMutexLocker locker(&factoryData()->mutex); QNetworkAccessBackendFactoryData::ConstIterator it = factoryData()->constBegin(); QNetworkAccessBackendFactoryData::ConstIterator end = factoryData()->constEnd(); diff --git a/src/network/bearer/qbearerengine.cpp b/src/network/bearer/qbearerengine.cpp index 677da08cb6..c06adb202f 100644 --- a/src/network/bearer/qbearerengine.cpp +++ b/src/network/bearer/qbearerengine.cpp @@ -56,7 +56,7 @@ static void cleanUpConfigurations(QHash &configurations) { auto isUsed = [](const QNetworkConfigurationPrivatePointer &ptr) { - return ptr->ref.load() > 1; + return ptr->ref.loadRelaxed() > 1; }; const auto end = configurations.end(); return std::find_if(configurations.begin(), end, isUsed) != end; diff --git a/src/network/kernel/qnetworkproxy.cpp b/src/network/kernel/qnetworkproxy.cpp index 3646a9526a..2f840e9e13 100644 --- a/src/network/kernel/qnetworkproxy.cpp +++ b/src/network/kernel/qnetworkproxy.cpp @@ -483,7 +483,7 @@ public: template<> void QSharedDataPointer::detach() { - if (d && d->ref.load() == 1) + if (d && d->ref.loadRelaxed() == 1) return; QNetworkProxyPrivate *x = (d ? new QNetworkProxyPrivate(*d) : new QNetworkProxyPrivate); @@ -925,7 +925,7 @@ public: template<> void QSharedDataPointer::detach() { - if (d && d->ref.load() == 1) + if (d && d->ref.loadRelaxed() == 1) return; QNetworkProxyQueryPrivate *x = (d ? new QNetworkProxyQueryPrivate(*d) : new QNetworkProxyQueryPrivate); diff --git a/src/network/ssl/qdtls.cpp b/src/network/ssl/qdtls.cpp index 3185bfa124..a2280a7d10 100644 --- a/src/network/ssl/qdtls.cpp +++ b/src/network/ssl/qdtls.cpp @@ -342,7 +342,7 @@ QT_BEGIN_NAMESPACE QSslConfiguration QDtlsBasePrivate::configuration() const { auto copyPrivate = new QSslConfigurationPrivate(dtlsConfiguration); - copyPrivate->ref.store(0); // the QSslConfiguration constructor refs up + copyPrivate->ref.storeRelaxed(0); // the QSslConfiguration constructor refs up QSslConfiguration copy(copyPrivate); copyPrivate->sessionCipher = sessionCipher; copyPrivate->sessionProtocol = sessionProtocol; diff --git a/src/network/ssl/qdtls_openssl.cpp b/src/network/ssl/qdtls_openssl.cpp index 8be53df24f..d9ddcceb40 100644 --- a/src/network/ssl/qdtls_openssl.cpp +++ b/src/network/ssl/qdtls_openssl.cpp @@ -729,7 +729,7 @@ bool DtlsState::initCtxAndConnection(QDtlsBasePrivate *dtlsBase) // Create a deep copy of our configuration auto configurationCopy = new QSslConfigurationPrivate(dtlsBase->dtlsConfiguration); - configurationCopy->ref.store(0); // the QSslConfiguration constructor refs up + configurationCopy->ref.storeRelaxed(0); // the QSslConfiguration constructor refs up // DTLSTODO: check we do not set something DTLS-incompatible there ... TlsContext newContext(QSslContext::sharedFromConfiguration(dtlsBase->mode, diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp index fe81bd5fcf..ca6c58117d 100644 --- a/src/network/ssl/qsslsocket.cpp +++ b/src/network/ssl/qsslsocket.cpp @@ -924,7 +924,7 @@ QSslConfiguration QSslSocket::sslConfiguration() const // create a deep copy of our configuration QSslConfigurationPrivate *copy = new QSslConfigurationPrivate(d->configuration); - copy->ref.store(0); // the QSslConfiguration constructor refs up + copy->ref.storeRelaxed(0); // the QSslConfiguration constructor refs up copy->sessionCipher = d->sessionCipher(); copy->sessionProtocol = d->sessionProtocol(); @@ -2378,7 +2378,7 @@ void QSslConfigurationPrivate::deepCopyDefaultConfiguration(QSslConfigurationPri if (!global) return; - ptr->ref.store(1); + ptr->ref.storeRelaxed(1); ptr->peerCertificate = global->peerCertificate; ptr->peerCertificateChain = global->peerCertificateChain; ptr->localCertificateChain = global->localCertificateChain; diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index 6a8269b521..3927a592cb 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -481,7 +481,7 @@ bool QSslSocketBackendPrivate::initSslContext() if (!sslContextPointer) { // create a deep copy of our configuration QSslConfigurationPrivate *configurationCopy = new QSslConfigurationPrivate(configuration); - configurationCopy->ref.store(0); // the QSslConfiguration constructor refs up + configurationCopy->ref.storeRelaxed(0); // the QSslConfiguration constructor refs up sslContextPointer = QSslContext::sharedFromConfiguration(mode, configurationCopy, allowRootCertOnDemandLoading); } diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index 799e984a68..2b4af3ef9f 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -397,7 +397,7 @@ QGLFormat::QGLFormat(QGL::FormatOptions options, int plane) */ void QGLFormat::detach() { - if (d->ref.load() != 1) { + if (d->ref.loadRelaxed() != 1) { QGLFormatPrivate *newd = new QGLFormatPrivate(d); if (!d->ref.deref()) delete d; @@ -5208,7 +5208,7 @@ void QGLContextGroup::addShare(const QGLContext *context, const QGLContext *shar return; // Make sure 'context' is not already shared with another group of contexts. - Q_ASSERT(context->d_ptr->group->m_refs.load() == 1); + Q_ASSERT(context->d_ptr->group->m_refs.loadRelaxed() == 1); // Free 'context' group resources and make it use the same resources as 'share'. QGLContextGroup *group = share->d_ptr->group; diff --git a/src/opengl/qglcolormap.cpp b/src/opengl/qglcolormap.cpp index d607363ac0..f314a9715d 100644 --- a/src/opengl/qglcolormap.cpp +++ b/src/opengl/qglcolormap.cpp @@ -153,7 +153,7 @@ QGLColormap & QGLColormap::operator=(const QGLColormap &map) void QGLColormap::detach_helper() { QGLColormapData *x = new QGLColormapData; - x->ref.store(1); + x->ref.storeRelaxed(1); x->cmapHandle = 0; x->cells = 0; if (d->cells) { diff --git a/src/opengl/qglcolormap.h b/src/opengl/qglcolormap.h index 772e327e34..b59b56e040 100644 --- a/src/opengl/qglcolormap.h +++ b/src/opengl/qglcolormap.h @@ -90,7 +90,7 @@ private: inline void QGLColormap::detach() { - if (d->ref.load() != 1) + if (d->ref.loadRelaxed() != 1) detach_helper(); } diff --git a/src/opengl/qglframebufferobject.cpp b/src/opengl/qglframebufferobject.cpp index b2158ebfaa..d0f82a85fa 100644 --- a/src/opengl/qglframebufferobject.cpp +++ b/src/opengl/qglframebufferobject.cpp @@ -145,7 +145,7 @@ extern QImage qt_gl_read_frame_buffer(const QSize&, bool, bool); */ void QGLFramebufferObjectFormat::detach() { - if (d->ref.load() != 1) { + if (d->ref.loadRelaxed() != 1) { QGLFramebufferObjectFormatPrivate *newd = new QGLFramebufferObjectFormatPrivate(d); if (!d->ref.deref()) diff --git a/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp b/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp index 99666bcec6..8d0a20f7b9 100644 --- a/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp +++ b/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp @@ -254,7 +254,7 @@ QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id, } newFreetype->face = face; - newFreetype->ref.store(1); + newFreetype->ref.storeRelaxed(1); newFreetype->xsize = 0; newFreetype->ysize = 0; newFreetype->matrix.xx = 0x10000; diff --git a/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase.cpp b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase.cpp index 4dff8f8f24..79f7eb3d43 100644 --- a/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase.cpp +++ b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase.cpp @@ -1364,11 +1364,11 @@ QT_WARNING_POP if (request.family != fontEngine->fontDef.family) { qWarning("%s: Failed to load font. Got fallback instead: %s", __FUNCTION__, qPrintable(fontEngine->fontDef.family)); - if (fontEngine->ref.load() == 0) + if (fontEngine->ref.loadRelaxed() == 0) delete fontEngine; fontEngine = 0; } else { - Q_ASSERT(fontEngine->ref.load() == 0); + Q_ASSERT(fontEngine->ref.loadRelaxed() == 0); // Override the generated font name switch (fontEngine->type()) { diff --git a/src/plugins/platforms/android/qandroideventdispatcher.cpp b/src/plugins/platforms/android/qandroideventdispatcher.cpp index e12551283f..3a1fb7a6de 100644 --- a/src/plugins/platforms/android/qandroideventdispatcher.cpp +++ b/src/plugins/platforms/android/qandroideventdispatcher.cpp @@ -78,14 +78,14 @@ void QAndroidEventDispatcher::stop() void QAndroidEventDispatcher::goingToStop(bool stop) { - m_goingToStop.store(stop ? 1 : 0); + m_goingToStop.storeRelaxed(stop ? 1 : 0); if (!stop) wakeUp(); } bool QAndroidEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags) { - if (m_goingToStop.load()) + if (m_goingToStop.loadRelaxed()) flags |= QEventLoop::ExcludeSocketNotifiers | QEventLoop::X11ExcludeTimers; { diff --git a/src/plugins/platforms/android/qandroideventdispatcher.h b/src/plugins/platforms/android/qandroideventdispatcher.h index e6f903bced..4fdd7af7a5 100644 --- a/src/plugins/platforms/android/qandroideventdispatcher.h +++ b/src/plugins/platforms/android/qandroideventdispatcher.h @@ -68,7 +68,7 @@ class QAndroidEventDispatcherStopper { public: static QAndroidEventDispatcherStopper *instance(); - static bool stopped() {return !instance()->m_started.load(); } + static bool stopped() {return !instance()->m_started.loadRelaxed(); } void startAll(); void stopAll(); void addEventDispatcher(QAndroidEventDispatcher *dispatcher); diff --git a/src/plugins/platforms/android/qandroidinputcontext.cpp b/src/plugins/platforms/android/qandroidinputcontext.cpp index db40c30d7d..297c08b4a4 100644 --- a/src/plugins/platforms/android/qandroidinputcontext.cpp +++ b/src/plugins/platforms/android/qandroidinputcontext.cpp @@ -666,7 +666,7 @@ void QAndroidInputContext::updateSelectionHandles() */ void QAndroidInputContext::handleLocationChanged(int handleId, int x, int y) { - if (m_batchEditNestingLevel.load() || m_blockUpdateSelection) { + if (m_batchEditNestingLevel.loadRelaxed() || m_blockUpdateSelection) { qWarning() << "QAndroidInputContext::handleLocationChanged returned"; return; } diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.cpp index 2e84915c80..07b2de7c58 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.cpp @@ -66,7 +66,7 @@ QAtomicInt running; void EventReader::run() { xcb_generic_event_t *event = nullptr; - while (running.load() && (event = xcb_wait_for_event(m_integration->connection()))) { + while (running.loadRelaxed() && (event = xcb_wait_for_event(m_integration->connection()))) { uint response_type = event->response_type & ~0x80; switch (response_type) { case XCB_CLIENT_MESSAGE: { diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp index b2074a0795..437c9562ab 100644 --- a/src/plugins/platforms/windows/qwindowstheme.cpp +++ b/src/plugins/platforms/windows/qwindowstheme.cpp @@ -162,8 +162,8 @@ public: m_init = CoInitializeEx(nullptr, COINIT_MULTITHREADED); QMutexLocker readyLocker(&m_readyMutex); - while (!m_cancelled.load()) { - if (!m_params && !m_cancelled.load() + while (!m_cancelled.loadRelaxed()) { + if (!m_params && !m_cancelled.loadRelaxed() && !m_readyCondition.wait(&m_readyMutex, 1000)) continue; @@ -174,7 +174,7 @@ public: m_params->attributes, &info, sizeof(SHFILEINFO), m_params->flags); m_doneMutex.lock(); - if (!m_cancelled.load()) { + if (!m_cancelled.loadRelaxed()) { *m_params->result = result; memcpy(m_params->info, &info, sizeof(SHFILEINFO)); } @@ -204,7 +204,7 @@ public: void cancel() { QMutexLocker doneLocker(&m_doneMutex); - m_cancelled.store(1); + m_cancelled.storeRelaxed(1); m_readyCondition.wakeAll(); } diff --git a/src/plugins/platforms/winrt/qwinrtscreen.cpp b/src/plugins/platforms/winrt/qwinrtscreen.cpp index e611c7be24..0d77889a36 100644 --- a/src/plugins/platforms/winrt/qwinrtscreen.cpp +++ b/src/plugins/platforms/winrt/qwinrtscreen.cpp @@ -1097,7 +1097,7 @@ HRESULT QWinRTScreen::onPointerEntered(ICoreWindow *, IPointerEventArgs *args) d->currentTargetWindow = topWindow(); if (d->mouseGrabWindow) - d->currentTargetWindow = d->mouseGrabWindow.load()->window(); + d->currentTargetWindow = d->mouseGrabWindow.loadRelaxed()->window(); qCDebug(lcQpaEvents) << __FUNCTION__ << "handleEnterEvent" << d->currentTargetWindow << pos; QWindowSystemInterface::handleEnterEvent(d->currentTargetWindow, pos, pos); @@ -1121,7 +1121,7 @@ HRESULT QWinRTScreen::onPointerExited(ICoreWindow *, IPointerEventArgs *args) d->touchPoints.remove(id); if (d->mouseGrabWindow) - d->currentTargetWindow = d->mouseGrabWindow.load()->window(); + d->currentTargetWindow = d->mouseGrabWindow.loadRelaxed()->window(); qCDebug(lcQpaEvents) << __FUNCTION__ << "handleLeaveEvent" << d->currentTargetWindow; QWindowSystemInterface::handleLeaveEvent(d->currentTargetWindow); @@ -1152,7 +1152,7 @@ HRESULT QWinRTScreen::onPointerUpdated(ICoreWindow *, IPointerEventArgs *args) d->currentTargetWindow = windowUnderPointer; if (d->mouseGrabWindow) - d->currentTargetWindow = d->mouseGrabWindow.load()->window(); + d->currentTargetWindow = d->mouseGrabWindow.loadRelaxed()->window(); if (d->currentTargetWindow) { const QPointF globalPosDelta = pos - posPoint; @@ -1354,7 +1354,7 @@ void QWinRTScreen::emulateMouseMove(const QPointF &point, MousePositionTransitio d->currentTargetWindow = windowUnderPointer; if (d->mouseGrabWindow) - d->currentTargetWindow = d->mouseGrabWindow.load()->window(); + d->currentTargetWindow = d->mouseGrabWindow.loadRelaxed()->window(); if (d->currentTargetWindow) { const QPointF globalPosDelta = pos - posPoint; diff --git a/src/sql/kernel/qsqldatabase.cpp b/src/sql/kernel/qsqldatabase.cpp index d63a9e59a8..99aa3e96c4 100644 --- a/src/sql/kernel/qsqldatabase.cpp +++ b/src/sql/kernel/qsqldatabase.cpp @@ -184,7 +184,7 @@ QSqlDatabasePrivate *QSqlDatabasePrivate::shared_null() void QSqlDatabasePrivate::invalidateDb(const QSqlDatabase &db, const QString &name, bool doWarn) { - if (db.d->ref.load() != 1 && doWarn) { + if (db.d->ref.loadRelaxed() != 1 && doWarn) { qWarning("QSqlDatabasePrivate::removeDatabase: connection '%s' is still in use, " "all queries will cease to work.", name.toLocal8Bit().constData()); db.d->disable(); diff --git a/src/sql/kernel/qsqlquery.cpp b/src/sql/kernel/qsqlquery.cpp index daadcb8a0e..e7c444f5b9 100644 --- a/src/sql/kernel/qsqlquery.cpp +++ b/src/sql/kernel/qsqlquery.cpp @@ -374,7 +374,7 @@ bool QSqlQuery::exec(const QString& query) QElapsedTimer t; t.start(); #endif - if (d->ref.load() != 1) { + if (d->ref.loadRelaxed() != 1) { bool fo = isForwardOnly(); *this = QSqlQuery(driver()->createResult()); d->sqlResult->setNumericalPrecisionPolicy(d->sqlResult->numericalPrecisionPolicy()); @@ -960,7 +960,7 @@ void QSqlQuery::clear() */ bool QSqlQuery::prepare(const QString& query) { - if (d->ref.load() != 1) { + if (d->ref.loadRelaxed() != 1) { bool fo = isForwardOnly(); *this = QSqlQuery(driver()->createResult()); setForwardOnly(fo); diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index 5faafba38d..ac309374e3 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -1012,14 +1012,14 @@ public: WatchDog() { QMutexLocker locker(&mutex); - timeout.store(-1); + timeout.storeRelaxed(-1); start(); waitCondition.wait(&mutex); } ~WatchDog() { { QMutexLocker locker(&mutex); - timeout.store(0); + timeout.storeRelaxed(0); waitCondition.wakeAll(); } wait(); @@ -1027,13 +1027,13 @@ public: void beginTest() { QMutexLocker locker(&mutex); - timeout.store(defaultTimeout()); + timeout.storeRelaxed(defaultTimeout()); waitCondition.wakeAll(); } void testFinished() { QMutexLocker locker(&mutex); - timeout.store(-1); + timeout.storeRelaxed(-1); waitCondition.wakeAll(); } @@ -1041,7 +1041,7 @@ public: QMutexLocker locker(&mutex); waitCondition.wakeAll(); while (1) { - int t = timeout.load(); + int t = timeout.loadRelaxed(); if (!t) break; if (Q_UNLIKELY(!waitCondition.wait(&mutex, t))) { diff --git a/src/testlib/qtestlog.cpp b/src/testlib/qtestlog.cpp index faef3912c4..14543c3cde 100644 --- a/src/testlib/qtestlog.cpp +++ b/src/testlib/qtestlog.cpp @@ -219,7 +219,7 @@ namespace QTest { } if (type != QtFatalMsg) { - if (counter.load() <= 0) + if (counter.loadRelaxed() <= 0) return; if (!counter.deref()) { diff --git a/src/widgets/dialogs/qfileinfogatherer.cpp b/src/widgets/dialogs/qfileinfogatherer.cpp index 1e03ad8b06..f39ae2b53e 100644 --- a/src/widgets/dialogs/qfileinfogatherer.cpp +++ b/src/widgets/dialogs/qfileinfogatherer.cpp @@ -54,12 +54,12 @@ QT_BEGIN_NAMESPACE static QBasicAtomicInt fetchedRoot = Q_BASIC_ATOMIC_INITIALIZER(false); Q_AUTOTEST_EXPORT void qt_test_resetFetchedRoot() { - fetchedRoot.store(false); + fetchedRoot.storeRelaxed(false); } Q_AUTOTEST_EXPORT bool qt_test_isFetchedRoot() { - return fetchedRoot.load(); + return fetchedRoot.loadRelaxed(); } #endif @@ -111,7 +111,7 @@ QFileInfoGatherer::QFileInfoGatherer(QObject *parent) */ QFileInfoGatherer::~QFileInfoGatherer() { - abort.store(true); + abort.storeRelaxed(true); QMutexLocker locker(&mutex); condition.wakeAll(); locker.unlock(); @@ -247,9 +247,9 @@ void QFileInfoGatherer::run() { forever { QMutexLocker locker(&mutex); - while (!abort.load() && path.isEmpty()) + while (!abort.loadRelaxed() && path.isEmpty()) condition.wait(&mutex); - if (abort.load()) + if (abort.loadRelaxed()) return; const QString thisPath = qAsConst(path).front(); path.pop_front(); @@ -303,7 +303,7 @@ void QFileInfoGatherer::getFileInfos(const QString &path, const QStringList &fil // List drives if (path.isEmpty()) { #ifdef QT_BUILD_INTERNAL - fetchedRoot.store(true); + fetchedRoot.storeRelaxed(true); #endif QFileInfoList infoList; if (files.isEmpty()) { @@ -332,7 +332,7 @@ void QFileInfoGatherer::getFileInfos(const QString &path, const QStringList &fil QStringList allFiles; if (files.isEmpty()) { QDirIterator dirIt(path, QDir::AllEntries | QDir::System | QDir::Hidden); - while (!abort.load() && dirIt.hasNext()) { + while (!abort.loadRelaxed() && dirIt.hasNext()) { dirIt.next(); fileInfo = dirIt.fileInfo(); allFiles.append(fileInfo.fileName()); @@ -343,7 +343,7 @@ void QFileInfoGatherer::getFileInfos(const QString &path, const QStringList &fil emit newListOfFiles(path, allFiles); QStringList::const_iterator filesIt = filesToCheck.constBegin(); - while (!abort.load() && filesIt != filesToCheck.constEnd()) { + while (!abort.loadRelaxed() && filesIt != filesToCheck.constEnd()) { fileInfo.setFile(path + QDir::separator() + *filesIt); ++filesIt; fetch(fileInfo, base, firstTime, updatedFiles, path); diff --git a/src/xml/dom/qdom.cpp b/src/xml/dom/qdom.cpp index 25655c09b2..8d232237bf 100644 --- a/src/xml/dom/qdom.cpp +++ b/src/xml/dom/qdom.cpp @@ -4489,7 +4489,7 @@ void QDomElementPrivate::setAttributeNS(const QString& nsURI, const QString& qNa void QDomElementPrivate::removeAttribute(const QString& aname) { QDomNodePrivate* p = m_attr->removeNamedItem(aname); - if (p && p->ref.load() == 0) + if (p && p->ref.loadRelaxed() == 0) delete p; } -- cgit v1.2.3 From cefaa2ff4c1d206c6daaba995d5c3572d089ac00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Arve=20S=C3=A6ther?= Date: Wed, 19 Jun 2019 11:15:41 +0200 Subject: Fix androidtestrunner for windows First, we cannot quote the whole command in double quotes, since the command itself very often have double quotes inside it. Secondly, we should use the same that the ssh(1) command does: Instead of adb shell setprop foo 'a b' we should use adb shell setprop foo "'a b'" as explained here: [https://developer.android.com/studio/command-line/adb#shellcommands] Last hunk in isRunning(): The pipe character got eaten by the shell if we used a single quote (so the stuff after the pipe (grep) was actually run locally). Switching to ssh-style quoting fixed that. Change-Id: I3075cdf8595ac2549cec8019f2cba79f77815e0b Reviewed-by: BogDan Vatra --- src/tools/androidtestrunner/main.cpp | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/tools/androidtestrunner/main.cpp b/src/tools/androidtestrunner/main.cpp index 043c827403..bb69b7b914 100644 --- a/src/tools/androidtestrunner/main.cpp +++ b/src/tools/androidtestrunner/main.cpp @@ -108,18 +108,12 @@ static Options g_options; static bool execCommand(const QString &command, QByteArray *output = nullptr, bool verbose = false) { -#if defined(Q_OS_WIN32) - QString processedCommand = QLatin1Char('\"') + command + QLatin1Char('\"'); -#else - const QString& processedCommand = command; -#endif - if (verbose) - fprintf(stdout, "Execute %s\n", processedCommand.toUtf8().constData()); - FILE *process = popen(processedCommand.toUtf8().constData(), QT_POPEN_READ); + fprintf(stdout, "Execute %s\n", command.toUtf8().constData()); + FILE *process = popen(command.toUtf8().constData(), QT_POPEN_READ); if (!process) { - fprintf(stderr, "Cannot execute command %s", qPrintable(processedCommand)); + fprintf(stderr, "Cannot execute command %s", qPrintable(command)); return false; } char buffer[512]; @@ -368,7 +362,7 @@ static bool parseTestArgs() g_options.testArgs += QStringLiteral(" -o output.%1,%1").arg(format); g_options.testArgs += unhandledArgs; - g_options.testArgs = QStringLiteral("shell am start -e applicationArguments \"%1\" -n %2/%3").arg(shellQuote(g_options.testArgs.trimmed()), + g_options.testArgs = QStringLiteral("shell am start -e applicationArguments \"'%1'\" -n %2/%3").arg(shellQuote(g_options.testArgs.trimmed()), g_options.package, g_options.activity); return true; @@ -376,8 +370,8 @@ static bool parseTestArgs() static bool isRunning() { QByteArray output; - if (!execCommand(QStringLiteral("%1 shell 'ps | grep \" %2\"'").arg(g_options.adbCommand, - shellQuoteUnix(g_options.package)), &output)) { + if (!execCommand(QStringLiteral("%1 shell \"ps | grep ' %2'\"").arg(g_options.adbCommand, + shellQuote(g_options.package)), &output)) { return false; } -- cgit v1.2.3 From 6492541df306e20d16e3a64d9df174c99c847945 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Tue, 18 Jun 2019 16:08:06 +0200 Subject: Support copy-pasting foreground images within same document When using a texture for foreground color in text and copy-pasting the text inside the same QTextEdit, the formatting would disappear. Fixing this in a general way would require implementing some other carrier format in the mime data than HTML, such as e.g ODF, but it can quite easily be fixed for the case where the data is pasted in the same document, or even different documents as long as they have a reference to the image in the formats. [ChangeLog][QtWidgets][QTextEdit] Added support for copy-pasting foreground brushes with textures within same document. Task-number: QTBUG-75931 Change-Id: I8b39dce289c64eea39e25cb8eb207e2534bcd2eb Reviewed-by: Eirik Aavitsland --- src/gui/text/qcssparser.cpp | 1 + src/gui/text/qcssparser_p.h | 1 + src/gui/text/qtextdocument.cpp | 16 ++++++++++++--- src/gui/text/qtextdocument_p.h | 1 + src/gui/text/qtexthtmlparser.cpp | 42 ++++++++++++++++++++++++++++++++++++++++ src/gui/text/qtexthtmlparser_p.h | 1 + 6 files changed, 59 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/gui/text/qcssparser.cpp b/src/gui/text/qcssparser.cpp index dc7e128bcd..b5489c7ed9 100644 --- a/src/gui/text/qcssparser.cpp +++ b/src/gui/text/qcssparser.cpp @@ -67,6 +67,7 @@ struct QCssKnownValue static const QCssKnownValue properties[NumProperties - 1] = { { "-qt-background-role", QtBackgroundRole }, { "-qt-block-indent", QtBlockIndent }, + { "-qt-fg-texture-cachekey", QtForegroundTextureCacheKey }, { "-qt-line-height-type", QtLineHeightType }, { "-qt-list-indent", QtListIndent }, { "-qt-list-number-prefix", QtListNumberPrefix }, diff --git a/src/gui/text/qcssparser_p.h b/src/gui/text/qcssparser_p.h index 62578f75e5..b0fa4be682 100644 --- a/src/gui/text/qcssparser_p.h +++ b/src/gui/text/qcssparser_p.h @@ -196,6 +196,7 @@ enum Property { LineHeight, QtLineHeightType, FontKerning, + QtForegroundTextureCacheKey, NumProperties }; diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp index 757e2086e5..2ec3f41f42 100644 --- a/src/gui/text/qtextdocument.cpp +++ b/src/gui/text/qtextdocument.cpp @@ -2470,9 +2470,19 @@ bool QTextHtmlExporter::emitCharFormatStyle(const QTextCharFormat &format) if (format.foreground() != defaultCharFormat.foreground() && format.foreground().style() != Qt::NoBrush) { - html += QLatin1String(" color:"); - html += colorValue(format.foreground().color()); - html += QLatin1Char(';'); + QBrush brush = format.foreground(); + if (brush.style() == Qt::TexturePattern) { + const bool isPixmap = qHasPixmapTexture(brush); + const qint64 cacheKey = isPixmap ? brush.texture().cacheKey() : brush.textureImage().cacheKey(); + + html += QLatin1String(" -qt-fg-texture-cachekey:"); + html += QString::number(cacheKey); + html += QLatin1String(";"); + } else { + html += QLatin1String(" color:"); + html += colorValue(brush.color()); + html += QLatin1Char(';'); + } attributesEmitted = true; } diff --git a/src/gui/text/qtextdocument_p.h b/src/gui/text/qtextdocument_p.h index a8e17bfc08..df00fb7d84 100644 --- a/src/gui/text/qtextdocument_p.h +++ b/src/gui/text/qtextdocument_p.h @@ -357,6 +357,7 @@ public: void mergeCachedResources(const QTextDocumentPrivate *priv); + friend struct QTextHtmlParserNode; friend class QTextHtmlExporter; friend class QTextCursor; }; diff --git a/src/gui/text/qtexthtmlparser.cpp b/src/gui/text/qtexthtmlparser.cpp index 642f0893b4..0b1a23f399 100644 --- a/src/gui/text/qtexthtmlparser.cpp +++ b/src/gui/text/qtexthtmlparser.cpp @@ -1335,6 +1335,17 @@ void QTextHtmlParserNode::applyCssDeclarations(const QVector default: break; } break; + + case QCss::QtForegroundTextureCacheKey: + { + if (resourceProvider != nullptr && resourceProvider->docHandle() != nullptr) { + bool ok; + qint64 searchKey = decl.d->values.first().variant.toLongLong(&ok); + if (ok) + applyForegroundImage(searchKey, resourceProvider); + } + break; + } default: break; } } @@ -1367,6 +1378,37 @@ void QTextHtmlParserNode::applyCssDeclarations(const QVector #endif // QT_NO_CSSPARSER +void QTextHtmlParserNode::applyForegroundImage(qint64 searchKey, const QTextDocument *resourceProvider) +{ + QTextDocumentPrivate *priv = resourceProvider->docHandle(); + for (int i = 0; i < priv->formats.numFormats(); ++i) { + QTextCharFormat format = priv->formats.charFormat(i); + if (format.isValid()) { + QBrush brush = format.foreground(); + if (brush.style() == Qt::TexturePattern) { + const bool isPixmap = qHasPixmapTexture(brush); + + if (isPixmap && QCoreApplication::instance()->thread() != QThread::currentThread()) { + qWarning("Can't apply QPixmap outside of GUI thread"); + return; + } + + const qint64 cacheKey = isPixmap ? brush.texture().cacheKey() : brush.textureImage().cacheKey(); + if (cacheKey == searchKey) { + QBrush b; + if (isPixmap) + b.setTexture(brush.texture()); + else + b.setTextureImage(brush.textureImage()); + b.setStyle(Qt::TexturePattern); + charFormat.setForeground(b); + } + } + } + } + +} + void QTextHtmlParserNode::applyBackgroundImage(const QString &url, const QTextDocument *resourceProvider) { if (!url.isEmpty() && resourceProvider) { diff --git a/src/gui/text/qtexthtmlparser_p.h b/src/gui/text/qtexthtmlparser_p.h index 6ce294d211..c174b54a61 100644 --- a/src/gui/text/qtexthtmlparser_p.h +++ b/src/gui/text/qtexthtmlparser_p.h @@ -251,6 +251,7 @@ struct QTextHtmlParserNode { void setListStyle(const QVector &cssValues); #endif + void applyForegroundImage(qint64 cacheKey, const QTextDocument *resourceProvider); void applyBackgroundImage(const QString &url, const QTextDocument *resourceProvider); bool hasOnlyWhitespace() const; -- cgit v1.2.3 From 26e5aa45e1f4bd980f894a5e0621ecde03464d33 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Wed, 13 Mar 2019 20:33:52 -0700 Subject: De-bootstrap the qfloat16 generator tool It doesn't really need Qt. So remove the dependency. I've confirmed that the output is identical to what used to be generated. This ought to be replaced by a script. Or just committed to Git, since the generated output is not really supposed to change, ever. Change-Id: I46363e5b8944459e8c48fffd158bb5d74fb6184c Reviewed-by: Allan Sandfeld Jensen --- src/corelib/global/global.pri | 2 +- src/tools/qfloat16-tables/gen_qfloat16_tables.cpp | 96 ++++++++++------------- src/tools/qfloat16-tables/qfloat16-tables.pro | 2 +- 3 files changed, 44 insertions(+), 56 deletions(-) (limited to 'src') diff --git a/src/corelib/global/global.pri b/src/corelib/global/global.pri index 10af46b41a..b4d9e40f95 100644 --- a/src/corelib/global/global.pri +++ b/src/corelib/global/global.pri @@ -129,7 +129,7 @@ QMAKE_QFLOAT16_TABLES_GENERATE = global/qfloat16.h qtPrepareTool(QMAKE_QFLOAT16_TABLES, qfloat16-tables) -qfloat16_tables.commands = $$QMAKE_QFLOAT16_TABLES ${QMAKE_FILE_OUT} +qfloat16_tables.commands = $$QMAKE_QFLOAT16_TABLES > ${QMAKE_FILE_OUT} qfloat16_tables.output = global/qfloat16tables.cpp qfloat16_tables.depends = $$QMAKE_QFLOAT16_TABLES_EXE qfloat16_tables.input = QMAKE_QFLOAT16_TABLES_GENERATE diff --git a/src/tools/qfloat16-tables/gen_qfloat16_tables.cpp b/src/tools/qfloat16-tables/gen_qfloat16_tables.cpp index 17fc978039..5aca0235e3 100644 --- a/src/tools/qfloat16-tables/gen_qfloat16_tables.cpp +++ b/src/tools/qfloat16-tables/gen_qfloat16_tables.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2016 by Southwest Research Institute (R) +** Copyright (C) 2019 Intel Corporation. ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -37,13 +38,13 @@ ** ****************************************************************************/ -#include -#include +#include +#include -quint32 convertmantissa(qint32 i) +uint32_t convertmantissa(int32_t i) { - quint32 m = i << 13; // Zero pad mantissa bits - quint32 e = 0; // Zero exponent + uint32_t m = i << 13; // Zero pad mantissa bits + uint32_t e = 0; // Zero exponent while (!(m & 0x00800000)) { // While not normalized e -= 0x00800000; // Decrement exponent (1<<23) @@ -56,60 +57,48 @@ quint32 convertmantissa(qint32 i) // we first build these tables up and then print them out as a separate step in order // to more closely map the implementation given in the paper. -quint32 basetable[512]; -quint32 shifttable[512]; +uint32_t basetable[512]; +uint32_t shifttable[512]; -#define PRINTHEX(a) "0x" + QByteArray::number(a,16).toUpper() + "U,\n" - -qint32 main(qint32 argc, char **argv) +int main() { - if (argc < 2) { - qWarning() << "Must provide output filename as argument."; - return -1; - } - - QFile fid(QFile::decodeName(argv[1])); - if (!fid.open(QIODevice::WriteOnly | QIODevice::Text)) { - qWarning() << "Abort: Failed to open/create file" << fid.fileName(); - return -1; - } - quint32 i; + uint32_t i; - fid.write("/* This file was generated by gen_qfloat16_tables.cpp */\n\n"); - fid.write("#include \n\n"); + printf("/* This file was generated by gen_qfloat16_tables.cpp */\n\n"); + printf("#include \n\n"); - fid.write("QT_BEGIN_NAMESPACE\n\n"); - fid.write("#if !defined(__F16C__) && !defined(__ARM_FP16_FORMAT_IEEE)\n\n"); + printf("QT_BEGIN_NAMESPACE\n\n"); + printf("#if !defined(__F16C__) && !defined(__ARM_FP16_FORMAT_IEEE)\n\n"); - fid.write("const quint32 qfloat16::mantissatable[2048] = {\n"); - fid.write("0,\n"); + printf("const quint32 qfloat16::mantissatable[2048] = {\n"); + printf("0,\n"); for (i = 1; i < 1024; i++) - fid.write(PRINTHEX(convertmantissa(i))); + printf("0x%XU,\n", convertmantissa(i)); for (i = 1024; i < 2048; i++) - fid.write(PRINTHEX(0x38000000U + ((i - 1024) << 13))); - fid.write("};\n\n"); + printf("0x%XU,\n", 0x38000000U + ((i - 1024) << 13)); + printf("};\n\n"); - fid.write("const quint32 qfloat16::exponenttable[64] = {\n"); - fid.write("0,\n"); + printf("const quint32 qfloat16::exponenttable[64] = {\n"); + printf("0,\n"); for (i = 1; i < 31; i++) - fid.write(PRINTHEX(i << 23)); - fid.write("0x47800000U,\n"); // 31 - fid.write("0x80000000U,\n"); // 32 + printf("0x%XU,\n", i << 23); + printf("0x47800000U,\n"); // 31 + printf("0x80000000U,\n"); // 32 for (i = 33; i < 63; i++) - fid.write(PRINTHEX(0x80000000U + ((i - 32) << 23))); - fid.write("0xC7800000U,\n"); // 63 - fid.write("};\n\n"); + printf("0x%XU,\n", 0x80000000U + ((i - 32) << 23)); + printf("0xC7800000U,\n"); // 63 + printf("};\n\n"); - fid.write("const quint32 qfloat16::offsettable[64] = {\n"); - fid.write("0,\n"); + printf("const quint32 qfloat16::offsettable[64] = {\n"); + printf("0,\n"); for (i = 1; i < 32; i++) - fid.write("1024U,\n"); - fid.write("0,\n"); + printf("1024U,\n"); + printf("0,\n"); for (i = 33; i < 64; i++) - fid.write("1024U,\n"); - fid.write("};\n\n"); + printf("1024U,\n"); + printf("};\n\n"); - qint32 e; + int32_t e; for (i = 0; i < 256; ++i) { e = i - 127; if (e < -24) { // Very small numbers map to zero @@ -144,20 +133,19 @@ qint32 main(qint32 argc, char **argv) } } - fid.write("const quint32 qfloat16::basetable[512] = {\n"); + printf("const quint32 qfloat16::basetable[512] = {\n"); for (i = 0; i < 512; i++) - fid.write(PRINTHEX(basetable[i])); + printf("0x%XU,\n", basetable[i]); - fid.write("};\n\n"); + printf("};\n\n"); - fid.write("const quint32 qfloat16::shifttable[512] = {\n"); + printf("const quint32 qfloat16::shifttable[512] = {\n"); for (i = 0; i < 512; i++) - fid.write(PRINTHEX(shifttable[i])); + printf("0x%XU,\n", shifttable[i]); - fid.write("};\n\n"); + printf("};\n\n"); - fid.write("#endif // !__F16C__ && !__ARM_FP16_FORMAT_IEEE\n\n"); - fid.write("QT_END_NAMESPACE\n"); - fid.close(); + printf("#endif // !__F16C__ && !__ARM_FP16_FORMAT_IEEE\n\n"); + printf("QT_END_NAMESPACE\n"); return 0; } diff --git a/src/tools/qfloat16-tables/qfloat16-tables.pro b/src/tools/qfloat16-tables/qfloat16-tables.pro index a7d10ac197..12878ce6c7 100644 --- a/src/tools/qfloat16-tables/qfloat16-tables.pro +++ b/src/tools/qfloat16-tables/qfloat16-tables.pro @@ -1,6 +1,6 @@ option(host_build) -CONFIG += force_bootstrap +CONFIG -= qt SOURCES += gen_qfloat16_tables.cpp load(qt_tool) -- cgit v1.2.3 From 5e40d3d982d014cd01db4dbe6aecc6ea6baf840a Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 31 May 2019 13:35:04 -0700 Subject: qfloat16: Check in the tables to Git It's a 38k source file, which makes it MUCH smaller than other generated files like qlocale_data_p.h (982k) and qunicodetables.cpp (718k). The constants are platform-independent, since they are defined by IEEE 754, so they will never change. The generator tool is moved to util/ and removed from the build. That's one fewer bootstrapped tool to have to worry about. The output file is committed as .cpp so it won't get installed. Fixes: QTBUG-76165 Change-Id: I2b1955a995ad40f3b89afffd15a3ded58dc3e35f Reviewed-by: Allan Sandfeld Jensen --- src/corelib/global/global.pri | 15 +- src/corelib/global/qfloat16.cpp | 1 + src/corelib/global/qfloat16tables.cpp | 3266 +++++++++++++++++++++ src/src.pro | 10 +- src/tools/qfloat16-tables/gen_qfloat16_tables.cpp | 151 - src/tools/qfloat16-tables/qfloat16-tables.pro | 9 - 6 files changed, 3274 insertions(+), 178 deletions(-) create mode 100644 src/corelib/global/qfloat16tables.cpp delete mode 100644 src/tools/qfloat16-tables/gen_qfloat16_tables.cpp delete mode 100644 src/tools/qfloat16-tables/qfloat16-tables.pro (limited to 'src') diff --git a/src/corelib/global/global.pri b/src/corelib/global/global.pri index b4d9e40f95..1da69aba9b 100644 --- a/src/corelib/global/global.pri +++ b/src/corelib/global/global.pri @@ -40,6 +40,10 @@ SOURCES += \ global/qrandom.cpp \ global/qhooks.cpp +# To get listed in IDEs +false: SOURCES += \ + global/qfloat16tables.cpp + # Only add global/qfloat16_f16c.c if qfloat16.cpp can't #include it. # Any compiler: if it is already generating F16C code, let qfloat16.cpp do it # Clang: ICE if not generating F16C code, so use qfloat16_f16c.c @@ -124,14 +128,3 @@ gcc:ltcg { } else { SOURCES += $$VERSIONTAGGING_SOURCES } - -QMAKE_QFLOAT16_TABLES_GENERATE = global/qfloat16.h - -qtPrepareTool(QMAKE_QFLOAT16_TABLES, qfloat16-tables) - -qfloat16_tables.commands = $$QMAKE_QFLOAT16_TABLES > ${QMAKE_FILE_OUT} -qfloat16_tables.output = global/qfloat16tables.cpp -qfloat16_tables.depends = $$QMAKE_QFLOAT16_TABLES_EXE -qfloat16_tables.input = QMAKE_QFLOAT16_TABLES_GENERATE -qfloat16_tables.variable_out = SOURCES -QMAKE_EXTRA_COMPILERS += qfloat16_tables diff --git a/src/corelib/global/qfloat16.cpp b/src/corelib/global/qfloat16.cpp index 68763c0606..ff2997b73a 100644 --- a/src/corelib/global/qfloat16.cpp +++ b/src/corelib/global/qfloat16.cpp @@ -239,6 +239,7 @@ Q_CORE_EXPORT void qFloatFromFloat16(float *out, const qfloat16 *in, qsizetype l QT_END_NAMESPACE +#include "qfloat16tables.cpp" #ifdef QFLOAT16_INCLUDE_FAST # include "qfloat16_f16c.c" #endif diff --git a/src/corelib/global/qfloat16tables.cpp b/src/corelib/global/qfloat16tables.cpp new file mode 100644 index 0000000000..3d764937d7 --- /dev/null +++ b/src/corelib/global/qfloat16tables.cpp @@ -0,0 +1,3266 @@ +/**************************************************************************** +** +** Copyright (C) 2016 by Southwest Research Institute (R) +** Copyright (C) 2019 Intel Corporation. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* This file was generated by gen_qfloat16_tables.cpp */ + +#include + +QT_BEGIN_NAMESPACE + +#if !defined(__F16C__) && !defined(__ARM_FP16_FORMAT_IEEE) + +const quint32 qfloat16::mantissatable[2048] = { +0, +0x33800000U, +0x34000000U, +0x34400000U, +0x34800000U, +0x34A00000U, +0x34C00000U, +0x34E00000U, +0x35000000U, +0x35100000U, +0x35200000U, +0x35300000U, +0x35400000U, +0x35500000U, +0x35600000U, +0x35700000U, +0x35800000U, +0x35880000U, +0x35900000U, +0x35980000U, +0x35A00000U, +0x35A80000U, +0x35B00000U, +0x35B80000U, +0x35C00000U, +0x35C80000U, +0x35D00000U, +0x35D80000U, +0x35E00000U, +0x35E80000U, +0x35F00000U, +0x35F80000U, +0x36000000U, +0x36040000U, +0x36080000U, +0x360C0000U, +0x36100000U, +0x36140000U, +0x36180000U, +0x361C0000U, +0x36200000U, +0x36240000U, +0x36280000U, +0x362C0000U, +0x36300000U, +0x36340000U, +0x36380000U, +0x363C0000U, +0x36400000U, +0x36440000U, +0x36480000U, +0x364C0000U, +0x36500000U, +0x36540000U, +0x36580000U, +0x365C0000U, +0x36600000U, +0x36640000U, +0x36680000U, +0x366C0000U, +0x36700000U, +0x36740000U, +0x36780000U, +0x367C0000U, +0x36800000U, +0x36820000U, +0x36840000U, +0x36860000U, +0x36880000U, +0x368A0000U, +0x368C0000U, +0x368E0000U, +0x36900000U, +0x36920000U, +0x36940000U, +0x36960000U, +0x36980000U, +0x369A0000U, +0x369C0000U, +0x369E0000U, +0x36A00000U, +0x36A20000U, +0x36A40000U, +0x36A60000U, +0x36A80000U, +0x36AA0000U, +0x36AC0000U, +0x36AE0000U, +0x36B00000U, +0x36B20000U, +0x36B40000U, +0x36B60000U, +0x36B80000U, +0x36BA0000U, +0x36BC0000U, +0x36BE0000U, +0x36C00000U, +0x36C20000U, +0x36C40000U, +0x36C60000U, +0x36C80000U, +0x36CA0000U, +0x36CC0000U, +0x36CE0000U, +0x36D00000U, +0x36D20000U, +0x36D40000U, +0x36D60000U, +0x36D80000U, +0x36DA0000U, +0x36DC0000U, +0x36DE0000U, +0x36E00000U, +0x36E20000U, +0x36E40000U, +0x36E60000U, +0x36E80000U, +0x36EA0000U, +0x36EC0000U, +0x36EE0000U, +0x36F00000U, +0x36F20000U, +0x36F40000U, +0x36F60000U, +0x36F80000U, +0x36FA0000U, +0x36FC0000U, +0x36FE0000U, +0x37000000U, +0x37010000U, +0x37020000U, +0x37030000U, +0x37040000U, +0x37050000U, +0x37060000U, +0x37070000U, +0x37080000U, +0x37090000U, +0x370A0000U, +0x370B0000U, +0x370C0000U, +0x370D0000U, +0x370E0000U, +0x370F0000U, +0x37100000U, +0x37110000U, +0x37120000U, +0x37130000U, +0x37140000U, +0x37150000U, +0x37160000U, +0x37170000U, +0x37180000U, +0x37190000U, +0x371A0000U, +0x371B0000U, +0x371C0000U, +0x371D0000U, +0x371E0000U, +0x371F0000U, +0x37200000U, +0x37210000U, +0x37220000U, +0x37230000U, +0x37240000U, +0x37250000U, +0x37260000U, +0x37270000U, +0x37280000U, +0x37290000U, +0x372A0000U, +0x372B0000U, +0x372C0000U, +0x372D0000U, +0x372E0000U, +0x372F0000U, +0x37300000U, +0x37310000U, +0x37320000U, +0x37330000U, +0x37340000U, +0x37350000U, +0x37360000U, +0x37370000U, +0x37380000U, +0x37390000U, +0x373A0000U, +0x373B0000U, +0x373C0000U, +0x373D0000U, +0x373E0000U, +0x373F0000U, +0x37400000U, +0x37410000U, +0x37420000U, +0x37430000U, +0x37440000U, +0x37450000U, +0x37460000U, +0x37470000U, +0x37480000U, +0x37490000U, +0x374A0000U, +0x374B0000U, +0x374C0000U, +0x374D0000U, +0x374E0000U, +0x374F0000U, +0x37500000U, +0x37510000U, +0x37520000U, +0x37530000U, +0x37540000U, +0x37550000U, +0x37560000U, +0x37570000U, +0x37580000U, +0x37590000U, +0x375A0000U, +0x375B0000U, +0x375C0000U, +0x375D0000U, +0x375E0000U, +0x375F0000U, +0x37600000U, +0x37610000U, +0x37620000U, +0x37630000U, +0x37640000U, +0x37650000U, +0x37660000U, +0x37670000U, +0x37680000U, +0x37690000U, +0x376A0000U, +0x376B0000U, +0x376C0000U, +0x376D0000U, +0x376E0000U, +0x376F0000U, +0x37700000U, +0x37710000U, +0x37720000U, +0x37730000U, +0x37740000U, +0x37750000U, +0x37760000U, +0x37770000U, +0x37780000U, +0x37790000U, +0x377A0000U, +0x377B0000U, +0x377C0000U, +0x377D0000U, +0x377E0000U, +0x377F0000U, +0x37800000U, +0x37808000U, +0x37810000U, +0x37818000U, +0x37820000U, +0x37828000U, +0x37830000U, +0x37838000U, +0x37840000U, +0x37848000U, +0x37850000U, +0x37858000U, +0x37860000U, +0x37868000U, +0x37870000U, +0x37878000U, +0x37880000U, +0x37888000U, +0x37890000U, +0x37898000U, +0x378A0000U, +0x378A8000U, +0x378B0000U, +0x378B8000U, +0x378C0000U, +0x378C8000U, +0x378D0000U, +0x378D8000U, +0x378E0000U, +0x378E8000U, +0x378F0000U, +0x378F8000U, +0x37900000U, +0x37908000U, +0x37910000U, +0x37918000U, +0x37920000U, +0x37928000U, +0x37930000U, +0x37938000U, +0x37940000U, +0x37948000U, +0x37950000U, +0x37958000U, +0x37960000U, +0x37968000U, +0x37970000U, +0x37978000U, +0x37980000U, +0x37988000U, +0x37990000U, +0x37998000U, +0x379A0000U, +0x379A8000U, +0x379B0000U, +0x379B8000U, +0x379C0000U, +0x379C8000U, +0x379D0000U, +0x379D8000U, +0x379E0000U, +0x379E8000U, +0x379F0000U, +0x379F8000U, +0x37A00000U, +0x37A08000U, +0x37A10000U, +0x37A18000U, +0x37A20000U, +0x37A28000U, +0x37A30000U, +0x37A38000U, +0x37A40000U, +0x37A48000U, +0x37A50000U, +0x37A58000U, +0x37A60000U, +0x37A68000U, +0x37A70000U, +0x37A78000U, +0x37A80000U, +0x37A88000U, +0x37A90000U, +0x37A98000U, +0x37AA0000U, +0x37AA8000U, +0x37AB0000U, +0x37AB8000U, +0x37AC0000U, +0x37AC8000U, +0x37AD0000U, +0x37AD8000U, +0x37AE0000U, +0x37AE8000U, +0x37AF0000U, +0x37AF8000U, +0x37B00000U, +0x37B08000U, +0x37B10000U, +0x37B18000U, +0x37B20000U, +0x37B28000U, +0x37B30000U, +0x37B38000U, +0x37B40000U, +0x37B48000U, +0x37B50000U, +0x37B58000U, +0x37B60000U, +0x37B68000U, +0x37B70000U, +0x37B78000U, +0x37B80000U, +0x37B88000U, +0x37B90000U, +0x37B98000U, +0x37BA0000U, +0x37BA8000U, +0x37BB0000U, +0x37BB8000U, +0x37BC0000U, +0x37BC8000U, +0x37BD0000U, +0x37BD8000U, +0x37BE0000U, +0x37BE8000U, +0x37BF0000U, +0x37BF8000U, +0x37C00000U, +0x37C08000U, +0x37C10000U, +0x37C18000U, +0x37C20000U, +0x37C28000U, +0x37C30000U, +0x37C38000U, +0x37C40000U, +0x37C48000U, +0x37C50000U, +0x37C58000U, +0x37C60000U, +0x37C68000U, +0x37C70000U, +0x37C78000U, +0x37C80000U, +0x37C88000U, +0x37C90000U, +0x37C98000U, +0x37CA0000U, +0x37CA8000U, +0x37CB0000U, +0x37CB8000U, +0x37CC0000U, +0x37CC8000U, +0x37CD0000U, +0x37CD8000U, +0x37CE0000U, +0x37CE8000U, +0x37CF0000U, +0x37CF8000U, +0x37D00000U, +0x37D08000U, +0x37D10000U, +0x37D18000U, +0x37D20000U, +0x37D28000U, +0x37D30000U, +0x37D38000U, +0x37D40000U, +0x37D48000U, +0x37D50000U, +0x37D58000U, +0x37D60000U, +0x37D68000U, +0x37D70000U, +0x37D78000U, +0x37D80000U, +0x37D88000U, +0x37D90000U, +0x37D98000U, +0x37DA0000U, +0x37DA8000U, +0x37DB0000U, +0x37DB8000U, +0x37DC0000U, +0x37DC8000U, +0x37DD0000U, +0x37DD8000U, +0x37DE0000U, +0x37DE8000U, +0x37DF0000U, +0x37DF8000U, +0x37E00000U, +0x37E08000U, +0x37E10000U, +0x37E18000U, +0x37E20000U, +0x37E28000U, +0x37E30000U, +0x37E38000U, +0x37E40000U, +0x37E48000U, +0x37E50000U, +0x37E58000U, +0x37E60000U, +0x37E68000U, +0x37E70000U, +0x37E78000U, +0x37E80000U, +0x37E88000U, +0x37E90000U, +0x37E98000U, +0x37EA0000U, +0x37EA8000U, +0x37EB0000U, +0x37EB8000U, +0x37EC0000U, +0x37EC8000U, +0x37ED0000U, +0x37ED8000U, +0x37EE0000U, +0x37EE8000U, +0x37EF0000U, +0x37EF8000U, +0x37F00000U, +0x37F08000U, +0x37F10000U, +0x37F18000U, +0x37F20000U, +0x37F28000U, +0x37F30000U, +0x37F38000U, +0x37F40000U, +0x37F48000U, +0x37F50000U, +0x37F58000U, +0x37F60000U, +0x37F68000U, +0x37F70000U, +0x37F78000U, +0x37F80000U, +0x37F88000U, +0x37F90000U, +0x37F98000U, +0x37FA0000U, +0x37FA8000U, +0x37FB0000U, +0x37FB8000U, +0x37FC0000U, +0x37FC8000U, +0x37FD0000U, +0x37FD8000U, +0x37FE0000U, +0x37FE8000U, +0x37FF0000U, +0x37FF8000U, +0x38000000U, +0x38004000U, +0x38008000U, +0x3800C000U, +0x38010000U, +0x38014000U, +0x38018000U, +0x3801C000U, +0x38020000U, +0x38024000U, +0x38028000U, +0x3802C000U, +0x38030000U, +0x38034000U, +0x38038000U, +0x3803C000U, +0x38040000U, +0x38044000U, +0x38048000U, +0x3804C000U, +0x38050000U, +0x38054000U, +0x38058000U, +0x3805C000U, +0x38060000U, +0x38064000U, +0x38068000U, +0x3806C000U, +0x38070000U, +0x38074000U, +0x38078000U, +0x3807C000U, +0x38080000U, +0x38084000U, +0x38088000U, +0x3808C000U, +0x38090000U, +0x38094000U, +0x38098000U, +0x3809C000U, +0x380A0000U, +0x380A4000U, +0x380A8000U, +0x380AC000U, +0x380B0000U, +0x380B4000U, +0x380B8000U, +0x380BC000U, +0x380C0000U, +0x380C4000U, +0x380C8000U, +0x380CC000U, +0x380D0000U, +0x380D4000U, +0x380D8000U, +0x380DC000U, +0x380E0000U, +0x380E4000U, +0x380E8000U, +0x380EC000U, +0x380F0000U, +0x380F4000U, +0x380F8000U, +0x380FC000U, +0x38100000U, +0x38104000U, +0x38108000U, +0x3810C000U, +0x38110000U, +0x38114000U, +0x38118000U, +0x3811C000U, +0x38120000U, +0x38124000U, +0x38128000U, +0x3812C000U, +0x38130000U, +0x38134000U, +0x38138000U, +0x3813C000U, +0x38140000U, +0x38144000U, +0x38148000U, +0x3814C000U, +0x38150000U, +0x38154000U, +0x38158000U, +0x3815C000U, +0x38160000U, +0x38164000U, +0x38168000U, +0x3816C000U, +0x38170000U, +0x38174000U, +0x38178000U, +0x3817C000U, +0x38180000U, +0x38184000U, +0x38188000U, +0x3818C000U, +0x38190000U, +0x38194000U, +0x38198000U, +0x3819C000U, +0x381A0000U, +0x381A4000U, +0x381A8000U, +0x381AC000U, +0x381B0000U, +0x381B4000U, +0x381B8000U, +0x381BC000U, +0x381C0000U, +0x381C4000U, +0x381C8000U, +0x381CC000U, +0x381D0000U, +0x381D4000U, +0x381D8000U, +0x381DC000U, +0x381E0000U, +0x381E4000U, +0x381E8000U, +0x381EC000U, +0x381F0000U, +0x381F4000U, +0x381F8000U, +0x381FC000U, +0x38200000U, +0x38204000U, +0x38208000U, +0x3820C000U, +0x38210000U, +0x38214000U, +0x38218000U, +0x3821C000U, +0x38220000U, +0x38224000U, +0x38228000U, +0x3822C000U, +0x38230000U, +0x38234000U, +0x38238000U, +0x3823C000U, +0x38240000U, +0x38244000U, +0x38248000U, +0x3824C000U, +0x38250000U, +0x38254000U, +0x38258000U, +0x3825C000U, +0x38260000U, +0x38264000U, +0x38268000U, +0x3826C000U, +0x38270000U, +0x38274000U, +0x38278000U, +0x3827C000U, +0x38280000U, +0x38284000U, +0x38288000U, +0x3828C000U, +0x38290000U, +0x38294000U, +0x38298000U, +0x3829C000U, +0x382A0000U, +0x382A4000U, +0x382A8000U, +0x382AC000U, +0x382B0000U, +0x382B4000U, +0x382B8000U, +0x382BC000U, +0x382C0000U, +0x382C4000U, +0x382C8000U, +0x382CC000U, +0x382D0000U, +0x382D4000U, +0x382D8000U, +0x382DC000U, +0x382E0000U, +0x382E4000U, +0x382E8000U, +0x382EC000U, +0x382F0000U, +0x382F4000U, +0x382F8000U, +0x382FC000U, +0x38300000U, +0x38304000U, +0x38308000U, +0x3830C000U, +0x38310000U, +0x38314000U, +0x38318000U, +0x3831C000U, +0x38320000U, +0x38324000U, +0x38328000U, +0x3832C000U, +0x38330000U, +0x38334000U, +0x38338000U, +0x3833C000U, +0x38340000U, +0x38344000U, +0x38348000U, +0x3834C000U, +0x38350000U, +0x38354000U, +0x38358000U, +0x3835C000U, +0x38360000U, +0x38364000U, +0x38368000U, +0x3836C000U, +0x38370000U, +0x38374000U, +0x38378000U, +0x3837C000U, +0x38380000U, +0x38384000U, +0x38388000U, +0x3838C000U, +0x38390000U, +0x38394000U, +0x38398000U, +0x3839C000U, +0x383A0000U, +0x383A4000U, +0x383A8000U, +0x383AC000U, +0x383B0000U, +0x383B4000U, +0x383B8000U, +0x383BC000U, +0x383C0000U, +0x383C4000U, +0x383C8000U, +0x383CC000U, +0x383D0000U, +0x383D4000U, +0x383D8000U, +0x383DC000U, +0x383E0000U, +0x383E4000U, +0x383E8000U, +0x383EC000U, +0x383F0000U, +0x383F4000U, +0x383F8000U, +0x383FC000U, +0x38400000U, +0x38404000U, +0x38408000U, +0x3840C000U, +0x38410000U, +0x38414000U, +0x38418000U, +0x3841C000U, +0x38420000U, +0x38424000U, +0x38428000U, +0x3842C000U, +0x38430000U, +0x38434000U, +0x38438000U, +0x3843C000U, +0x38440000U, +0x38444000U, +0x38448000U, +0x3844C000U, +0x38450000U, +0x38454000U, +0x38458000U, +0x3845C000U, +0x38460000U, +0x38464000U, +0x38468000U, +0x3846C000U, +0x38470000U, +0x38474000U, +0x38478000U, +0x3847C000U, +0x38480000U, +0x38484000U, +0x38488000U, +0x3848C000U, +0x38490000U, +0x38494000U, +0x38498000U, +0x3849C000U, +0x384A0000U, +0x384A4000U, +0x384A8000U, +0x384AC000U, +0x384B0000U, +0x384B4000U, +0x384B8000U, +0x384BC000U, +0x384C0000U, +0x384C4000U, +0x384C8000U, +0x384CC000U, +0x384D0000U, +0x384D4000U, +0x384D8000U, +0x384DC000U, +0x384E0000U, +0x384E4000U, +0x384E8000U, +0x384EC000U, +0x384F0000U, +0x384F4000U, +0x384F8000U, +0x384FC000U, +0x38500000U, +0x38504000U, +0x38508000U, +0x3850C000U, +0x38510000U, +0x38514000U, +0x38518000U, +0x3851C000U, +0x38520000U, +0x38524000U, +0x38528000U, +0x3852C000U, +0x38530000U, +0x38534000U, +0x38538000U, +0x3853C000U, +0x38540000U, +0x38544000U, +0x38548000U, +0x3854C000U, +0x38550000U, +0x38554000U, +0x38558000U, +0x3855C000U, +0x38560000U, +0x38564000U, +0x38568000U, +0x3856C000U, +0x38570000U, +0x38574000U, +0x38578000U, +0x3857C000U, +0x38580000U, +0x38584000U, +0x38588000U, +0x3858C000U, +0x38590000U, +0x38594000U, +0x38598000U, +0x3859C000U, +0x385A0000U, +0x385A4000U, +0x385A8000U, +0x385AC000U, +0x385B0000U, +0x385B4000U, +0x385B8000U, +0x385BC000U, +0x385C0000U, +0x385C4000U, +0x385C8000U, +0x385CC000U, +0x385D0000U, +0x385D4000U, +0x385D8000U, +0x385DC000U, +0x385E0000U, +0x385E4000U, +0x385E8000U, +0x385EC000U, +0x385F0000U, +0x385F4000U, +0x385F8000U, +0x385FC000U, +0x38600000U, +0x38604000U, +0x38608000U, +0x3860C000U, +0x38610000U, +0x38614000U, +0x38618000U, +0x3861C000U, +0x38620000U, +0x38624000U, +0x38628000U, +0x3862C000U, +0x38630000U, +0x38634000U, +0x38638000U, +0x3863C000U, +0x38640000U, +0x38644000U, +0x38648000U, +0x3864C000U, +0x38650000U, +0x38654000U, +0x38658000U, +0x3865C000U, +0x38660000U, +0x38664000U, +0x38668000U, +0x3866C000U, +0x38670000U, +0x38674000U, +0x38678000U, +0x3867C000U, +0x38680000U, +0x38684000U, +0x38688000U, +0x3868C000U, +0x38690000U, +0x38694000U, +0x38698000U, +0x3869C000U, +0x386A0000U, +0x386A4000U, +0x386A8000U, +0x386AC000U, +0x386B0000U, +0x386B4000U, +0x386B8000U, +0x386BC000U, +0x386C0000U, +0x386C4000U, +0x386C8000U, +0x386CC000U, +0x386D0000U, +0x386D4000U, +0x386D8000U, +0x386DC000U, +0x386E0000U, +0x386E4000U, +0x386E8000U, +0x386EC000U, +0x386F0000U, +0x386F4000U, +0x386F8000U, +0x386FC000U, +0x38700000U, +0x38704000U, +0x38708000U, +0x3870C000U, +0x38710000U, +0x38714000U, +0x38718000U, +0x3871C000U, +0x38720000U, +0x38724000U, +0x38728000U, +0x3872C000U, +0x38730000U, +0x38734000U, +0x38738000U, +0x3873C000U, +0x38740000U, +0x38744000U, +0x38748000U, +0x3874C000U, +0x38750000U, +0x38754000U, +0x38758000U, +0x3875C000U, +0x38760000U, +0x38764000U, +0x38768000U, +0x3876C000U, +0x38770000U, +0x38774000U, +0x38778000U, +0x3877C000U, +0x38780000U, +0x38784000U, +0x38788000U, +0x3878C000U, +0x38790000U, +0x38794000U, +0x38798000U, +0x3879C000U, +0x387A0000U, +0x387A4000U, +0x387A8000U, +0x387AC000U, +0x387B0000U, +0x387B4000U, +0x387B8000U, +0x387BC000U, +0x387C0000U, +0x387C4000U, +0x387C8000U, +0x387CC000U, +0x387D0000U, +0x387D4000U, +0x387D8000U, +0x387DC000U, +0x387E0000U, +0x387E4000U, +0x387E8000U, +0x387EC000U, +0x387F0000U, +0x387F4000U, +0x387F8000U, +0x387FC000U, +0x38000000U, +0x38002000U, +0x38004000U, +0x38006000U, +0x38008000U, +0x3800A000U, +0x3800C000U, +0x3800E000U, +0x38010000U, +0x38012000U, +0x38014000U, +0x38016000U, +0x38018000U, +0x3801A000U, +0x3801C000U, +0x3801E000U, +0x38020000U, +0x38022000U, +0x38024000U, +0x38026000U, +0x38028000U, +0x3802A000U, +0x3802C000U, +0x3802E000U, +0x38030000U, +0x38032000U, +0x38034000U, +0x38036000U, +0x38038000U, +0x3803A000U, +0x3803C000U, +0x3803E000U, +0x38040000U, +0x38042000U, +0x38044000U, +0x38046000U, +0x38048000U, +0x3804A000U, +0x3804C000U, +0x3804E000U, +0x38050000U, +0x38052000U, +0x38054000U, +0x38056000U, +0x38058000U, +0x3805A000U, +0x3805C000U, +0x3805E000U, +0x38060000U, +0x38062000U, +0x38064000U, +0x38066000U, +0x38068000U, +0x3806A000U, +0x3806C000U, +0x3806E000U, +0x38070000U, +0x38072000U, +0x38074000U, +0x38076000U, +0x38078000U, +0x3807A000U, +0x3807C000U, +0x3807E000U, +0x38080000U, +0x38082000U, +0x38084000U, +0x38086000U, +0x38088000U, +0x3808A000U, +0x3808C000U, +0x3808E000U, +0x38090000U, +0x38092000U, +0x38094000U, +0x38096000U, +0x38098000U, +0x3809A000U, +0x3809C000U, +0x3809E000U, +0x380A0000U, +0x380A2000U, +0x380A4000U, +0x380A6000U, +0x380A8000U, +0x380AA000U, +0x380AC000U, +0x380AE000U, +0x380B0000U, +0x380B2000U, +0x380B4000U, +0x380B6000U, +0x380B8000U, +0x380BA000U, +0x380BC000U, +0x380BE000U, +0x380C0000U, +0x380C2000U, +0x380C4000U, +0x380C6000U, +0x380C8000U, +0x380CA000U, +0x380CC000U, +0x380CE000U, +0x380D0000U, +0x380D2000U, +0x380D4000U, +0x380D6000U, +0x380D8000U, +0x380DA000U, +0x380DC000U, +0x380DE000U, +0x380E0000U, +0x380E2000U, +0x380E4000U, +0x380E6000U, +0x380E8000U, +0x380EA000U, +0x380EC000U, +0x380EE000U, +0x380F0000U, +0x380F2000U, +0x380F4000U, +0x380F6000U, +0x380F8000U, +0x380FA000U, +0x380FC000U, +0x380FE000U, +0x38100000U, +0x38102000U, +0x38104000U, +0x38106000U, +0x38108000U, +0x3810A000U, +0x3810C000U, +0x3810E000U, +0x38110000U, +0x38112000U, +0x38114000U, +0x38116000U, +0x38118000U, +0x3811A000U, +0x3811C000U, +0x3811E000U, +0x38120000U, +0x38122000U, +0x38124000U, +0x38126000U, +0x38128000U, +0x3812A000U, +0x3812C000U, +0x3812E000U, +0x38130000U, +0x38132000U, +0x38134000U, +0x38136000U, +0x38138000U, +0x3813A000U, +0x3813C000U, +0x3813E000U, +0x38140000U, +0x38142000U, +0x38144000U, +0x38146000U, +0x38148000U, +0x3814A000U, +0x3814C000U, +0x3814E000U, +0x38150000U, +0x38152000U, +0x38154000U, +0x38156000U, +0x38158000U, +0x3815A000U, +0x3815C000U, +0x3815E000U, +0x38160000U, +0x38162000U, +0x38164000U, +0x38166000U, +0x38168000U, +0x3816A000U, +0x3816C000U, +0x3816E000U, +0x38170000U, +0x38172000U, +0x38174000U, +0x38176000U, +0x38178000U, +0x3817A000U, +0x3817C000U, +0x3817E000U, +0x38180000U, +0x38182000U, +0x38184000U, +0x38186000U, +0x38188000U, +0x3818A000U, +0x3818C000U, +0x3818E000U, +0x38190000U, +0x38192000U, +0x38194000U, +0x38196000U, +0x38198000U, +0x3819A000U, +0x3819C000U, +0x3819E000U, +0x381A0000U, +0x381A2000U, +0x381A4000U, +0x381A6000U, +0x381A8000U, +0x381AA000U, +0x381AC000U, +0x381AE000U, +0x381B0000U, +0x381B2000U, +0x381B4000U, +0x381B6000U, +0x381B8000U, +0x381BA000U, +0x381BC000U, +0x381BE000U, +0x381C0000U, +0x381C2000U, +0x381C4000U, +0x381C6000U, +0x381C8000U, +0x381CA000U, +0x381CC000U, +0x381CE000U, +0x381D0000U, +0x381D2000U, +0x381D4000U, +0x381D6000U, +0x381D8000U, +0x381DA000U, +0x381DC000U, +0x381DE000U, +0x381E0000U, +0x381E2000U, +0x381E4000U, +0x381E6000U, +0x381E8000U, +0x381EA000U, +0x381EC000U, +0x381EE000U, +0x381F0000U, +0x381F2000U, +0x381F4000U, +0x381F6000U, +0x381F8000U, +0x381FA000U, +0x381FC000U, +0x381FE000U, +0x38200000U, +0x38202000U, +0x38204000U, +0x38206000U, +0x38208000U, +0x3820A000U, +0x3820C000U, +0x3820E000U, +0x38210000U, +0x38212000U, +0x38214000U, +0x38216000U, +0x38218000U, +0x3821A000U, +0x3821C000U, +0x3821E000U, +0x38220000U, +0x38222000U, +0x38224000U, +0x38226000U, +0x38228000U, +0x3822A000U, +0x3822C000U, +0x3822E000U, +0x38230000U, +0x38232000U, +0x38234000U, +0x38236000U, +0x38238000U, +0x3823A000U, +0x3823C000U, +0x3823E000U, +0x38240000U, +0x38242000U, +0x38244000U, +0x38246000U, +0x38248000U, +0x3824A000U, +0x3824C000U, +0x3824E000U, +0x38250000U, +0x38252000U, +0x38254000U, +0x38256000U, +0x38258000U, +0x3825A000U, +0x3825C000U, +0x3825E000U, +0x38260000U, +0x38262000U, +0x38264000U, +0x38266000U, +0x38268000U, +0x3826A000U, +0x3826C000U, +0x3826E000U, +0x38270000U, +0x38272000U, +0x38274000U, +0x38276000U, +0x38278000U, +0x3827A000U, +0x3827C000U, +0x3827E000U, +0x38280000U, +0x38282000U, +0x38284000U, +0x38286000U, +0x38288000U, +0x3828A000U, +0x3828C000U, +0x3828E000U, +0x38290000U, +0x38292000U, +0x38294000U, +0x38296000U, +0x38298000U, +0x3829A000U, +0x3829C000U, +0x3829E000U, +0x382A0000U, +0x382A2000U, +0x382A4000U, +0x382A6000U, +0x382A8000U, +0x382AA000U, +0x382AC000U, +0x382AE000U, +0x382B0000U, +0x382B2000U, +0x382B4000U, +0x382B6000U, +0x382B8000U, +0x382BA000U, +0x382BC000U, +0x382BE000U, +0x382C0000U, +0x382C2000U, +0x382C4000U, +0x382C6000U, +0x382C8000U, +0x382CA000U, +0x382CC000U, +0x382CE000U, +0x382D0000U, +0x382D2000U, +0x382D4000U, +0x382D6000U, +0x382D8000U, +0x382DA000U, +0x382DC000U, +0x382DE000U, +0x382E0000U, +0x382E2000U, +0x382E4000U, +0x382E6000U, +0x382E8000U, +0x382EA000U, +0x382EC000U, +0x382EE000U, +0x382F0000U, +0x382F2000U, +0x382F4000U, +0x382F6000U, +0x382F8000U, +0x382FA000U, +0x382FC000U, +0x382FE000U, +0x38300000U, +0x38302000U, +0x38304000U, +0x38306000U, +0x38308000U, +0x3830A000U, +0x3830C000U, +0x3830E000U, +0x38310000U, +0x38312000U, +0x38314000U, +0x38316000U, +0x38318000U, +0x3831A000U, +0x3831C000U, +0x3831E000U, +0x38320000U, +0x38322000U, +0x38324000U, +0x38326000U, +0x38328000U, +0x3832A000U, +0x3832C000U, +0x3832E000U, +0x38330000U, +0x38332000U, +0x38334000U, +0x38336000U, +0x38338000U, +0x3833A000U, +0x3833C000U, +0x3833E000U, +0x38340000U, +0x38342000U, +0x38344000U, +0x38346000U, +0x38348000U, +0x3834A000U, +0x3834C000U, +0x3834E000U, +0x38350000U, +0x38352000U, +0x38354000U, +0x38356000U, +0x38358000U, +0x3835A000U, +0x3835C000U, +0x3835E000U, +0x38360000U, +0x38362000U, +0x38364000U, +0x38366000U, +0x38368000U, +0x3836A000U, +0x3836C000U, +0x3836E000U, +0x38370000U, +0x38372000U, +0x38374000U, +0x38376000U, +0x38378000U, +0x3837A000U, +0x3837C000U, +0x3837E000U, +0x38380000U, +0x38382000U, +0x38384000U, +0x38386000U, +0x38388000U, +0x3838A000U, +0x3838C000U, +0x3838E000U, +0x38390000U, +0x38392000U, +0x38394000U, +0x38396000U, +0x38398000U, +0x3839A000U, +0x3839C000U, +0x3839E000U, +0x383A0000U, +0x383A2000U, +0x383A4000U, +0x383A6000U, +0x383A8000U, +0x383AA000U, +0x383AC000U, +0x383AE000U, +0x383B0000U, +0x383B2000U, +0x383B4000U, +0x383B6000U, +0x383B8000U, +0x383BA000U, +0x383BC000U, +0x383BE000U, +0x383C0000U, +0x383C2000U, +0x383C4000U, +0x383C6000U, +0x383C8000U, +0x383CA000U, +0x383CC000U, +0x383CE000U, +0x383D0000U, +0x383D2000U, +0x383D4000U, +0x383D6000U, +0x383D8000U, +0x383DA000U, +0x383DC000U, +0x383DE000U, +0x383E0000U, +0x383E2000U, +0x383E4000U, +0x383E6000U, +0x383E8000U, +0x383EA000U, +0x383EC000U, +0x383EE000U, +0x383F0000U, +0x383F2000U, +0x383F4000U, +0x383F6000U, +0x383F8000U, +0x383FA000U, +0x383FC000U, +0x383FE000U, +0x38400000U, +0x38402000U, +0x38404000U, +0x38406000U, +0x38408000U, +0x3840A000U, +0x3840C000U, +0x3840E000U, +0x38410000U, +0x38412000U, +0x38414000U, +0x38416000U, +0x38418000U, +0x3841A000U, +0x3841C000U, +0x3841E000U, +0x38420000U, +0x38422000U, +0x38424000U, +0x38426000U, +0x38428000U, +0x3842A000U, +0x3842C000U, +0x3842E000U, +0x38430000U, +0x38432000U, +0x38434000U, +0x38436000U, +0x38438000U, +0x3843A000U, +0x3843C000U, +0x3843E000U, +0x38440000U, +0x38442000U, +0x38444000U, +0x38446000U, +0x38448000U, +0x3844A000U, +0x3844C000U, +0x3844E000U, +0x38450000U, +0x38452000U, +0x38454000U, +0x38456000U, +0x38458000U, +0x3845A000U, +0x3845C000U, +0x3845E000U, +0x38460000U, +0x38462000U, +0x38464000U, +0x38466000U, +0x38468000U, +0x3846A000U, +0x3846C000U, +0x3846E000U, +0x38470000U, +0x38472000U, +0x38474000U, +0x38476000U, +0x38478000U, +0x3847A000U, +0x3847C000U, +0x3847E000U, +0x38480000U, +0x38482000U, +0x38484000U, +0x38486000U, +0x38488000U, +0x3848A000U, +0x3848C000U, +0x3848E000U, +0x38490000U, +0x38492000U, +0x38494000U, +0x38496000U, +0x38498000U, +0x3849A000U, +0x3849C000U, +0x3849E000U, +0x384A0000U, +0x384A2000U, +0x384A4000U, +0x384A6000U, +0x384A8000U, +0x384AA000U, +0x384AC000U, +0x384AE000U, +0x384B0000U, +0x384B2000U, +0x384B4000U, +0x384B6000U, +0x384B8000U, +0x384BA000U, +0x384BC000U, +0x384BE000U, +0x384C0000U, +0x384C2000U, +0x384C4000U, +0x384C6000U, +0x384C8000U, +0x384CA000U, +0x384CC000U, +0x384CE000U, +0x384D0000U, +0x384D2000U, +0x384D4000U, +0x384D6000U, +0x384D8000U, +0x384DA000U, +0x384DC000U, +0x384DE000U, +0x384E0000U, +0x384E2000U, +0x384E4000U, +0x384E6000U, +0x384E8000U, +0x384EA000U, +0x384EC000U, +0x384EE000U, +0x384F0000U, +0x384F2000U, +0x384F4000U, +0x384F6000U, +0x384F8000U, +0x384FA000U, +0x384FC000U, +0x384FE000U, +0x38500000U, +0x38502000U, +0x38504000U, +0x38506000U, +0x38508000U, +0x3850A000U, +0x3850C000U, +0x3850E000U, +0x38510000U, +0x38512000U, +0x38514000U, +0x38516000U, +0x38518000U, +0x3851A000U, +0x3851C000U, +0x3851E000U, +0x38520000U, +0x38522000U, +0x38524000U, +0x38526000U, +0x38528000U, +0x3852A000U, +0x3852C000U, +0x3852E000U, +0x38530000U, +0x38532000U, +0x38534000U, +0x38536000U, +0x38538000U, +0x3853A000U, +0x3853C000U, +0x3853E000U, +0x38540000U, +0x38542000U, +0x38544000U, +0x38546000U, +0x38548000U, +0x3854A000U, +0x3854C000U, +0x3854E000U, +0x38550000U, +0x38552000U, +0x38554000U, +0x38556000U, +0x38558000U, +0x3855A000U, +0x3855C000U, +0x3855E000U, +0x38560000U, +0x38562000U, +0x38564000U, +0x38566000U, +0x38568000U, +0x3856A000U, +0x3856C000U, +0x3856E000U, +0x38570000U, +0x38572000U, +0x38574000U, +0x38576000U, +0x38578000U, +0x3857A000U, +0x3857C000U, +0x3857E000U, +0x38580000U, +0x38582000U, +0x38584000U, +0x38586000U, +0x38588000U, +0x3858A000U, +0x3858C000U, +0x3858E000U, +0x38590000U, +0x38592000U, +0x38594000U, +0x38596000U, +0x38598000U, +0x3859A000U, +0x3859C000U, +0x3859E000U, +0x385A0000U, +0x385A2000U, +0x385A4000U, +0x385A6000U, +0x385A8000U, +0x385AA000U, +0x385AC000U, +0x385AE000U, +0x385B0000U, +0x385B2000U, +0x385B4000U, +0x385B6000U, +0x385B8000U, +0x385BA000U, +0x385BC000U, +0x385BE000U, +0x385C0000U, +0x385C2000U, +0x385C4000U, +0x385C6000U, +0x385C8000U, +0x385CA000U, +0x385CC000U, +0x385CE000U, +0x385D0000U, +0x385D2000U, +0x385D4000U, +0x385D6000U, +0x385D8000U, +0x385DA000U, +0x385DC000U, +0x385DE000U, +0x385E0000U, +0x385E2000U, +0x385E4000U, +0x385E6000U, +0x385E8000U, +0x385EA000U, +0x385EC000U, +0x385EE000U, +0x385F0000U, +0x385F2000U, +0x385F4000U, +0x385F6000U, +0x385F8000U, +0x385FA000U, +0x385FC000U, +0x385FE000U, +0x38600000U, +0x38602000U, +0x38604000U, +0x38606000U, +0x38608000U, +0x3860A000U, +0x3860C000U, +0x3860E000U, +0x38610000U, +0x38612000U, +0x38614000U, +0x38616000U, +0x38618000U, +0x3861A000U, +0x3861C000U, +0x3861E000U, +0x38620000U, +0x38622000U, +0x38624000U, +0x38626000U, +0x38628000U, +0x3862A000U, +0x3862C000U, +0x3862E000U, +0x38630000U, +0x38632000U, +0x38634000U, +0x38636000U, +0x38638000U, +0x3863A000U, +0x3863C000U, +0x3863E000U, +0x38640000U, +0x38642000U, +0x38644000U, +0x38646000U, +0x38648000U, +0x3864A000U, +0x3864C000U, +0x3864E000U, +0x38650000U, +0x38652000U, +0x38654000U, +0x38656000U, +0x38658000U, +0x3865A000U, +0x3865C000U, +0x3865E000U, +0x38660000U, +0x38662000U, +0x38664000U, +0x38666000U, +0x38668000U, +0x3866A000U, +0x3866C000U, +0x3866E000U, +0x38670000U, +0x38672000U, +0x38674000U, +0x38676000U, +0x38678000U, +0x3867A000U, +0x3867C000U, +0x3867E000U, +0x38680000U, +0x38682000U, +0x38684000U, +0x38686000U, +0x38688000U, +0x3868A000U, +0x3868C000U, +0x3868E000U, +0x38690000U, +0x38692000U, +0x38694000U, +0x38696000U, +0x38698000U, +0x3869A000U, +0x3869C000U, +0x3869E000U, +0x386A0000U, +0x386A2000U, +0x386A4000U, +0x386A6000U, +0x386A8000U, +0x386AA000U, +0x386AC000U, +0x386AE000U, +0x386B0000U, +0x386B2000U, +0x386B4000U, +0x386B6000U, +0x386B8000U, +0x386BA000U, +0x386BC000U, +0x386BE000U, +0x386C0000U, +0x386C2000U, +0x386C4000U, +0x386C6000U, +0x386C8000U, +0x386CA000U, +0x386CC000U, +0x386CE000U, +0x386D0000U, +0x386D2000U, +0x386D4000U, +0x386D6000U, +0x386D8000U, +0x386DA000U, +0x386DC000U, +0x386DE000U, +0x386E0000U, +0x386E2000U, +0x386E4000U, +0x386E6000U, +0x386E8000U, +0x386EA000U, +0x386EC000U, +0x386EE000U, +0x386F0000U, +0x386F2000U, +0x386F4000U, +0x386F6000U, +0x386F8000U, +0x386FA000U, +0x386FC000U, +0x386FE000U, +0x38700000U, +0x38702000U, +0x38704000U, +0x38706000U, +0x38708000U, +0x3870A000U, +0x3870C000U, +0x3870E000U, +0x38710000U, +0x38712000U, +0x38714000U, +0x38716000U, +0x38718000U, +0x3871A000U, +0x3871C000U, +0x3871E000U, +0x38720000U, +0x38722000U, +0x38724000U, +0x38726000U, +0x38728000U, +0x3872A000U, +0x3872C000U, +0x3872E000U, +0x38730000U, +0x38732000U, +0x38734000U, +0x38736000U, +0x38738000U, +0x3873A000U, +0x3873C000U, +0x3873E000U, +0x38740000U, +0x38742000U, +0x38744000U, +0x38746000U, +0x38748000U, +0x3874A000U, +0x3874C000U, +0x3874E000U, +0x38750000U, +0x38752000U, +0x38754000U, +0x38756000U, +0x38758000U, +0x3875A000U, +0x3875C000U, +0x3875E000U, +0x38760000U, +0x38762000U, +0x38764000U, +0x38766000U, +0x38768000U, +0x3876A000U, +0x3876C000U, +0x3876E000U, +0x38770000U, +0x38772000U, +0x38774000U, +0x38776000U, +0x38778000U, +0x3877A000U, +0x3877C000U, +0x3877E000U, +0x38780000U, +0x38782000U, +0x38784000U, +0x38786000U, +0x38788000U, +0x3878A000U, +0x3878C000U, +0x3878E000U, +0x38790000U, +0x38792000U, +0x38794000U, +0x38796000U, +0x38798000U, +0x3879A000U, +0x3879C000U, +0x3879E000U, +0x387A0000U, +0x387A2000U, +0x387A4000U, +0x387A6000U, +0x387A8000U, +0x387AA000U, +0x387AC000U, +0x387AE000U, +0x387B0000U, +0x387B2000U, +0x387B4000U, +0x387B6000U, +0x387B8000U, +0x387BA000U, +0x387BC000U, +0x387BE000U, +0x387C0000U, +0x387C2000U, +0x387C4000U, +0x387C6000U, +0x387C8000U, +0x387CA000U, +0x387CC000U, +0x387CE000U, +0x387D0000U, +0x387D2000U, +0x387D4000U, +0x387D6000U, +0x387D8000U, +0x387DA000U, +0x387DC000U, +0x387DE000U, +0x387E0000U, +0x387E2000U, +0x387E4000U, +0x387E6000U, +0x387E8000U, +0x387EA000U, +0x387EC000U, +0x387EE000U, +0x387F0000U, +0x387F2000U, +0x387F4000U, +0x387F6000U, +0x387F8000U, +0x387FA000U, +0x387FC000U, +0x387FE000U, +}; + +const quint32 qfloat16::exponenttable[64] = { +0, +0x800000U, +0x1000000U, +0x1800000U, +0x2000000U, +0x2800000U, +0x3000000U, +0x3800000U, +0x4000000U, +0x4800000U, +0x5000000U, +0x5800000U, +0x6000000U, +0x6800000U, +0x7000000U, +0x7800000U, +0x8000000U, +0x8800000U, +0x9000000U, +0x9800000U, +0xA000000U, +0xA800000U, +0xB000000U, +0xB800000U, +0xC000000U, +0xC800000U, +0xD000000U, +0xD800000U, +0xE000000U, +0xE800000U, +0xF000000U, +0x47800000U, +0x80000000U, +0x80800000U, +0x81000000U, +0x81800000U, +0x82000000U, +0x82800000U, +0x83000000U, +0x83800000U, +0x84000000U, +0x84800000U, +0x85000000U, +0x85800000U, +0x86000000U, +0x86800000U, +0x87000000U, +0x87800000U, +0x88000000U, +0x88800000U, +0x89000000U, +0x89800000U, +0x8A000000U, +0x8A800000U, +0x8B000000U, +0x8B800000U, +0x8C000000U, +0x8C800000U, +0x8D000000U, +0x8D800000U, +0x8E000000U, +0x8E800000U, +0x8F000000U, +0xC7800000U, +}; + +const quint32 qfloat16::offsettable[64] = { +0, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +0, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +1024U, +}; + +const quint32 qfloat16::basetable[512] = { +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x0U, +0x1U, +0x2U, +0x4U, +0x8U, +0x10U, +0x20U, +0x40U, +0x80U, +0x100U, +0x200U, +0x400U, +0x800U, +0xC00U, +0x1000U, +0x1400U, +0x1800U, +0x1C00U, +0x2000U, +0x2400U, +0x2800U, +0x2C00U, +0x3000U, +0x3400U, +0x3800U, +0x3C00U, +0x4000U, +0x4400U, +0x4800U, +0x4C00U, +0x5000U, +0x5400U, +0x5800U, +0x5C00U, +0x6000U, +0x6400U, +0x6800U, +0x6C00U, +0x7000U, +0x7400U, +0x7800U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x7C00U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8000U, +0x8001U, +0x8002U, +0x8004U, +0x8008U, +0x8010U, +0x8020U, +0x8040U, +0x8080U, +0x8100U, +0x8200U, +0x8400U, +0x8800U, +0x8C00U, +0x9000U, +0x9400U, +0x9800U, +0x9C00U, +0xA000U, +0xA400U, +0xA800U, +0xAC00U, +0xB000U, +0xB400U, +0xB800U, +0xBC00U, +0xC000U, +0xC400U, +0xC800U, +0xCC00U, +0xD000U, +0xD400U, +0xD800U, +0xDC00U, +0xE000U, +0xE400U, +0xE800U, +0xEC00U, +0xF000U, +0xF400U, +0xF800U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +0xFC00U, +}; + +const quint32 qfloat16::shifttable[512] = { +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x17U, +0x16U, +0x15U, +0x14U, +0x13U, +0x12U, +0x11U, +0x10U, +0xFU, +0xEU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0xDU, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x17U, +0x16U, +0x15U, +0x14U, +0x13U, +0x12U, +0x11U, +0x10U, +0xFU, +0xEU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0xDU, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0x18U, +0xDU, +}; + +#endif // !__F16C__ && !__ARM_FP16_FORMAT_IEEE + +QT_END_NAMESPACE diff --git a/src/src.pro b/src/src.pro index a39b718e10..b1afdd27a5 100644 --- a/src/src.pro +++ b/src/src.pro @@ -21,10 +21,6 @@ src_tools_rcc.subdir = tools/rcc src_tools_rcc.target = sub-rcc src_tools_rcc.depends = src_tools_bootstrap -src_tools_qfloat16_tables.subdir = tools/qfloat16-tables -src_tools_qfloat16_tables.target = sub-qfloat16-tables -src_tools_qfloat16_tables.depends = src_tools_bootstrap - src_tools_qlalr.subdir = tools/qlalr src_tools_qlalr.target = sub-qlalr force_bootstrap: src_tools_qlalr.depends = src_tools_bootstrap @@ -72,7 +68,7 @@ src_winmain.depends = sub-corelib # just for the module .pri file src_corelib.subdir = $$PWD/corelib src_corelib.target = sub-corelib -src_corelib.depends = src_tools_moc src_tools_rcc src_tools_qfloat16_tables +src_corelib.depends = src_tools_moc src_tools_rcc src_xml.subdir = $$PWD/xml src_xml.target = sub-xml @@ -159,12 +155,12 @@ src_android.subdir = $$PWD/android src_3rdparty_freetype.depends += src_corelib } } -SUBDIRS += src_tools_bootstrap src_tools_moc src_tools_rcc src_tools_qfloat16_tables +SUBDIRS += src_tools_bootstrap src_tools_moc src_tools_rcc qtConfig(regularexpression):pcre2 { SUBDIRS += src_3rdparty_pcre2 src_corelib.depends += src_3rdparty_pcre2 } -TOOLS = src_tools_moc src_tools_rcc src_tools_qlalr src_tools_qfloat16_tables +TOOLS = src_tools_moc src_tools_rcc src_tools_qlalr !force_bootstrap:if(qtConfig(lttng)|qtConfig(etw)) { SUBDIRS += src_tools_tracegen src_corelib.depends += src_tools_tracegen diff --git a/src/tools/qfloat16-tables/gen_qfloat16_tables.cpp b/src/tools/qfloat16-tables/gen_qfloat16_tables.cpp deleted file mode 100644 index 5aca0235e3..0000000000 --- a/src/tools/qfloat16-tables/gen_qfloat16_tables.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 by Southwest Research Institute (R) -** Copyright (C) 2019 Intel Corporation. -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include - -uint32_t convertmantissa(int32_t i) -{ - uint32_t m = i << 13; // Zero pad mantissa bits - uint32_t e = 0; // Zero exponent - - while (!(m & 0x00800000)) { // While not normalized - e -= 0x00800000; // Decrement exponent (1<<23) - m <<= 1; // Shift mantissa - } - m &= ~0x00800000; // Clear leading 1 bit - e += 0x38800000; // Adjust bias ((127-14)<<23) - return m | e; // Return combined number -} - -// we first build these tables up and then print them out as a separate step in order -// to more closely map the implementation given in the paper. -uint32_t basetable[512]; -uint32_t shifttable[512]; - -int main() -{ - uint32_t i; - - printf("/* This file was generated by gen_qfloat16_tables.cpp */\n\n"); - printf("#include \n\n"); - - printf("QT_BEGIN_NAMESPACE\n\n"); - printf("#if !defined(__F16C__) && !defined(__ARM_FP16_FORMAT_IEEE)\n\n"); - - printf("const quint32 qfloat16::mantissatable[2048] = {\n"); - printf("0,\n"); - for (i = 1; i < 1024; i++) - printf("0x%XU,\n", convertmantissa(i)); - for (i = 1024; i < 2048; i++) - printf("0x%XU,\n", 0x38000000U + ((i - 1024) << 13)); - printf("};\n\n"); - - printf("const quint32 qfloat16::exponenttable[64] = {\n"); - printf("0,\n"); - for (i = 1; i < 31; i++) - printf("0x%XU,\n", i << 23); - printf("0x47800000U,\n"); // 31 - printf("0x80000000U,\n"); // 32 - for (i = 33; i < 63; i++) - printf("0x%XU,\n", 0x80000000U + ((i - 32) << 23)); - printf("0xC7800000U,\n"); // 63 - printf("};\n\n"); - - printf("const quint32 qfloat16::offsettable[64] = {\n"); - printf("0,\n"); - for (i = 1; i < 32; i++) - printf("1024U,\n"); - printf("0,\n"); - for (i = 33; i < 64; i++) - printf("1024U,\n"); - printf("};\n\n"); - - int32_t e; - for (i = 0; i < 256; ++i) { - e = i - 127; - if (e < -24) { // Very small numbers map to zero - basetable[i | 0x000] = 0x0000; - basetable[i | 0x100] = 0x8000; - shifttable[i | 0x000] = 24; - shifttable[i | 0x100] = 24; - - } else if (e < -14) { // Small numbers map to denorms - basetable[i | 0x000] = (0x0400 >> (-e - 14)); - basetable[i | 0x100] = (0x0400 >> (-e - 14)) | 0x8000; - shifttable[i | 0x000] = -e - 1; - shifttable[i | 0x100] = -e - 1; - - } else if (e <= 15) { // Normal numbers just lose precision - basetable[i | 0x000] = ((e + 15) << 10); - basetable[i | 0x100] = ((e + 15) << 10) | 0x8000; - shifttable[i | 0x000] = 13; - shifttable[i | 0x100] = 13; - - } else if (e < 128) { // Large numbers map to Infinity - basetable[i | 0x000] = 0x7C00; - basetable[i | 0x100] = 0xFC00; - shifttable[i | 0x000] = 24; - shifttable[i | 0x100] = 24; - - } else { // Infinity and NaN's stay Infinity and NaN's - basetable[i | 0x000] = 0x7C00; - basetable[i | 0x100] = 0xFC00; - shifttable[i | 0x000] = 13; - shifttable[i | 0x100] = 13; - } - } - - printf("const quint32 qfloat16::basetable[512] = {\n"); - for (i = 0; i < 512; i++) - printf("0x%XU,\n", basetable[i]); - - printf("};\n\n"); - - printf("const quint32 qfloat16::shifttable[512] = {\n"); - for (i = 0; i < 512; i++) - printf("0x%XU,\n", shifttable[i]); - - printf("};\n\n"); - - printf("#endif // !__F16C__ && !__ARM_FP16_FORMAT_IEEE\n\n"); - printf("QT_END_NAMESPACE\n"); - return 0; -} diff --git a/src/tools/qfloat16-tables/qfloat16-tables.pro b/src/tools/qfloat16-tables/qfloat16-tables.pro deleted file mode 100644 index 12878ce6c7..0000000000 --- a/src/tools/qfloat16-tables/qfloat16-tables.pro +++ /dev/null @@ -1,9 +0,0 @@ -option(host_build) - -CONFIG -= qt -SOURCES += gen_qfloat16_tables.cpp - -load(qt_tool) - -lib.CONFIG = dummy_install -INSTALLS = lib -- cgit v1.2.3 From 84536ae61ed887f179deafb75fd28757f1cf9aba Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Mon, 13 May 2019 20:12:23 +0200 Subject: QNetworkAcesssCache: replace a QQueue with a std::vector The code didn't really use the queue as a queue, because it enqueued one at a time, but dequeued potentially many. Even if it actually decays to actual queue behavior: for the small number of elements expected, a vector would still be preferable over the per-element allocation performed by QQueue. Because std::vector doesn't have pop_front(), I took the liberty of making the dequeue code easier to read by using find_if with an aptly-named lambda. Change-Id: I09bd7ede50cb8ab5af5d1fc2ede37a3731f67070 Reviewed-by: Timur Pocheptsov --- src/network/access/qnetworkaccesscache.cpp | 32 +++++++++++++++--------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/network/access/qnetworkaccesscache.cpp b/src/network/access/qnetworkaccesscache.cpp index 00bb18cb82..b694a2c999 100644 --- a/src/network/access/qnetworkaccesscache.cpp +++ b/src/network/access/qnetworkaccesscache.cpp @@ -40,11 +40,12 @@ #include "qnetworkaccesscache_p.h" #include "QtCore/qpointer.h" #include "QtCore/qdatetime.h" -#include "QtCore/qqueue.h" #include "qnetworkaccessmanager_p.h" #include "qnetworkreply_p.h" #include "qnetworkrequest.h" +#include + QT_BEGIN_NAMESPACE enum ExpiryTimeEnum { @@ -63,7 +64,7 @@ namespace { struct QNetworkAccessCache::Node { QDateTime timestamp; - QQueue receiverQueue; + std::vector receiverQueue; QByteArray key; Node *older, *newer; @@ -277,10 +278,7 @@ bool QNetworkAccessCache::requestEntry(const QByteArray &key, QObject *target, c // object is not shareable and is in use // queue for later use Q_ASSERT(node->older == 0 && node->newer == 0); - Receiver receiver; - receiver.object = target; - receiver.member = member; - node->receiverQueue.enqueue(receiver); + node->receiverQueue.push_back({target, member}); // request queued return true; @@ -331,17 +329,19 @@ void QNetworkAccessCache::releaseEntry(const QByteArray &key) Q_ASSERT(node->useCount > 0); // are there other objects waiting? - if (!node->receiverQueue.isEmpty()) { + const auto objectStillExists = [](const Receiver &r) { return !r.object.isNull(); }; + + auto &queue = node->receiverQueue; + auto qit = std::find_if(queue.begin(), queue.end(), objectStillExists); + + const Receiver receiver = qit == queue.end() ? Receiver{} : std::move(*qit++) ; + + queue.erase(queue.begin(), qit); + + if (receiver.object) { // queue another activation - Receiver receiver; - do { - receiver = node->receiverQueue.dequeue(); - } while (receiver.object.isNull() && !node->receiverQueue.isEmpty()); - - if (!receiver.object.isNull()) { - emitEntryReady(node, receiver.object, receiver.member); - return; - } + emitEntryReady(node, receiver.object, receiver.member); + return; } if (!--node->useCount) { -- cgit v1.2.3 From b149f5d77a3c362af1e82e95ddf0ae13954d2538 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Sat, 25 May 2019 12:09:53 +0200 Subject: Overload QTextBrowser::setSource() to add optional type argument Now that it's trying to guess whether the type is markdown based on the file extension, there needs to be a way to override it. For example it might be arranged that directory listings will be generated in markdown format instead of HTML; then when loading a source URL that is a directory, the application may override the type. The type for the single-argument setSource(url) is UnknownResource to preserve the existing behavior, but the user can override the guessing by setting a specific type. Change-Id: Id111efd24de7d8fd18c47b16a2d58f5b09d77891 Reviewed-by: Gatis Paeglis --- src/gui/text/qtextdocument.h | 2 + src/widgets/widgets/qtextbrowser.cpp | 84 +++++++++++++++++++++++++++++++----- src/widgets/widgets/qtextbrowser.h | 11 +++++ 3 files changed, 86 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/gui/text/qtextdocument.h b/src/gui/text/qtextdocument.h index edb6bd9310..2fadc40cd2 100644 --- a/src/gui/text/qtextdocument.h +++ b/src/gui/text/qtextdocument.h @@ -225,6 +225,7 @@ public: void print(QPagedPaintDevice *printer) const; enum ResourceType { + UnknownResource = 0, HtmlResource = 1, ImageResource = 2, StyleSheetResource = 3, @@ -232,6 +233,7 @@ public: UserResource = 100 }; + Q_ENUM(ResourceType) QVariant resource(int type, const QUrl &name) const; void addResource(int type, const QUrl &name, const QVariant &resource); diff --git a/src/widgets/widgets/qtextbrowser.cpp b/src/widgets/widgets/qtextbrowser.cpp index 8e06efc75e..bee1021950 100644 --- a/src/widgets/widgets/qtextbrowser.cpp +++ b/src/widgets/widgets/qtextbrowser.cpp @@ -86,6 +86,7 @@ public: int hpos; int vpos; int focusIndicatorPosition, focusIndicatorAnchor; + QTextDocument::ResourceType type = QTextDocument::UnknownResource; }; HistoryEntry history(int i) const @@ -122,6 +123,8 @@ public: bool openExternalLinks; bool openLinks; + QTextDocument::ResourceType currentType; + #ifndef QT_NO_CURSOR QCursor oldCursor; #endif @@ -137,7 +140,7 @@ public: void _q_activateAnchor(const QString &href); void _q_highlightLink(const QString &href); - void setSource(const QUrl &url); + void setSource(const QUrl &url, QTextDocument::ResourceType type); // re-imlemented from QTextEditPrivate virtual QUrl resolveUrl(const QUrl &url) const override; @@ -274,7 +277,7 @@ void QTextBrowserPrivate::_q_highlightLink(const QString &anchor) } } -void QTextBrowserPrivate::setSource(const QUrl &url) +void QTextBrowserPrivate::setSource(const QUrl &url, QTextDocument::ResourceType type) { Q_Q(QTextBrowser); #ifndef QT_NO_CURSOR @@ -291,14 +294,18 @@ void QTextBrowserPrivate::setSource(const QUrl &url) currentUrlWithoutFragment.setFragment(QString()); QUrl newUrlWithoutFragment = currentURL.resolved(url); newUrlWithoutFragment.setFragment(QString()); - QTextDocument::ResourceType type = QTextDocument::HtmlResource; QString fileName = url.fileName(); + if (type == QTextDocument::UnknownResource) { #if QT_CONFIG(textmarkdownreader) - if (fileName.endsWith(QLatin1String(".md")) || - fileName.endsWith(QLatin1String(".mkd")) || - fileName.endsWith(QLatin1String(".markdown"))) - type = QTextDocument::MarkdownResource; + if (fileName.endsWith(QLatin1String(".md")) || + fileName.endsWith(QLatin1String(".mkd")) || + fileName.endsWith(QLatin1String(".markdown"))) + type = QTextDocument::MarkdownResource; + else #endif + type = QTextDocument::HtmlResource; + } + currentType = type; if (url.isValid() && (newUrlWithoutFragment != currentUrlWithoutFragment || forceLoadOnSourceChange)) { @@ -574,6 +581,7 @@ QTextBrowserPrivate::HistoryEntry QTextBrowserPrivate::createHistoryEntry() cons { HistoryEntry entry; entry.url = q_func()->source(); + entry.type = q_func()->sourceType(); entry.title = q_func()->documentTitle(); entry.hpos = hbar->value(); entry.vpos = vbar->value(); @@ -590,7 +598,7 @@ QTextBrowserPrivate::HistoryEntry QTextBrowserPrivate::createHistoryEntry() cons void QTextBrowserPrivate::restoreHistoryEntry(const HistoryEntry &entry) { - setSource(entry.url); + setSource(entry.url, entry.type); hbar->setValue(entry.hpos); vbar->setValue(entry.vpos); if (entry.focusIndicatorAnchor != -1 && entry.focusIndicatorPosition != -1) { @@ -732,7 +740,13 @@ QTextBrowser::~QTextBrowser() document is displayed as a popup rather than as new document in the browser window itself. Otherwise, the document is displayed normally in the text browser with the text set to the contents of - the named document with setHtml(). + the named document with \l QTextDocument::setHtml() or + \l QTextDocument::setMarkdown(), depending on whether the filename ends + with any of the known Markdown file extensions. + + If you would like to avoid automatic type detection + and specify the type explicitly, call setSource() rather than + setting this property. By default, this property contains an empty URL. */ @@ -745,6 +759,23 @@ QUrl QTextBrowser::source() const return d->stack.top().url; } +/*! + \property QTextBrowser::sourceType + \brief the type of the displayed document + + This is QTextDocument::UnknownResource if no document is displayed or if + the type of the source is unknown. Otherwise it holds the type that was + detected, or the type that was specified when setSource() was called. +*/ +QTextDocument::ResourceType QTextBrowser::sourceType() const +{ + Q_D(const QTextBrowser); + if (d->stack.isEmpty()) + return QTextDocument::UnknownResource; + else + return d->stack.top().type; +} + /*! \property QTextBrowser::searchPaths \brief the search paths used by the text browser to find supporting @@ -775,16 +806,46 @@ void QTextBrowser::reload() Q_D(QTextBrowser); QUrl s = d->currentURL; d->currentURL = QUrl(); - setSource(s); + setSource(s, d->currentType); } +#if QT_VERSION < QT_VERSION_CHECK(6,0,0) void QTextBrowser::setSource(const QUrl &url) +{ + setSource(url, QTextDocument::UnknownResource); +} +#endif + +/*! + Attempts to load the document at the given \a url with the specified \a type. + + If \a type is \l {QTextDocument::ResourceType::UnknownResource}{UnknownResource} + (the default), the document type will be detected: that is, if the url ends + with an extension of \c{.md}, \c{.mkd} or \c{.markdown}, the document will be + loaded via \l QTextDocument::setMarkdown(); otherwise it will be loaded via + \l QTextDocument::setHtml(). This detection can be bypassed by specifying + the \a type explicitly. +*/ +void QTextBrowser::setSource(const QUrl &url, QTextDocument::ResourceType type) +{ + doSetSource(url, type); +} + +#if QT_VERSION >= QT_VERSION_CHECK(6,0,0) +/*! + Attempts to load the document at the given \a url with the specified \a type. + + setSource() calls doSetSource. In Qt 5, setSource(const QUrl &url) was virtual. + In Qt 6, doSetSource() is virtual instead, so that it can be overridden in subclasses. +*/ +#endif +void QTextBrowser::doSetSource(const QUrl &url, QTextDocument::ResourceType type) { Q_D(QTextBrowser); const QTextBrowserPrivate::HistoryEntry historyEntry = d->createHistoryEntry(); - d->setSource(url); + d->setSource(url, type); if (!url.isValid()) return; @@ -798,6 +859,7 @@ void QTextBrowser::setSource(const QUrl &url) QTextBrowserPrivate::HistoryEntry entry; entry.url = url; + entry.type = d->currentType; entry.title = documentTitle(); entry.hpos = 0; entry.vpos = 0; diff --git a/src/widgets/widgets/qtextbrowser.h b/src/widgets/widgets/qtextbrowser.h index ea81256f50..33e5b3980c 100644 --- a/src/widgets/widgets/qtextbrowser.h +++ b/src/widgets/widgets/qtextbrowser.h @@ -55,6 +55,7 @@ class Q_WIDGETS_EXPORT QTextBrowser : public QTextEdit Q_OBJECT Q_PROPERTY(QUrl source READ source WRITE setSource) + Q_PROPERTY(QTextDocument::ResourceType sourceType READ sourceType) Q_OVERRIDE(bool modified SCRIPTABLE false) Q_OVERRIDE(bool readOnly DESIGNABLE false SCRIPTABLE false) Q_OVERRIDE(bool undoRedoEnabled DESIGNABLE false SCRIPTABLE false) @@ -67,6 +68,7 @@ public: virtual ~QTextBrowser(); QUrl source() const; + QTextDocument::ResourceType sourceType() const; QStringList searchPaths() const; void setSearchPaths(const QStringList &paths); @@ -88,7 +90,12 @@ public: void setOpenLinks(bool open); public Q_SLOTS: +#if QT_VERSION < QT_VERSION_CHECK(6,0,0) virtual void setSource(const QUrl &name); + void setSource(const QUrl &name, QTextDocument::ResourceType type); +#else + void setSource(const QUrl &name, QTextDocument::ResourceType type = QTextDocument::UnknownResource); +#endif virtual void backward(); virtual void forward(); virtual void home(); @@ -112,6 +119,10 @@ protected: virtual void focusOutEvent(QFocusEvent *ev) override; virtual bool focusNextPrevChild(bool next) override; virtual void paintEvent(QPaintEvent *e) override; +#if QT_VERSION >= QT_VERSION_CHECK(6,0,0) + virtual +#endif + void doSetSource(const QUrl &name, QTextDocument::ResourceType type = QTextDocument::UnknownResource); private: Q_DISABLE_COPY(QTextBrowser) -- cgit v1.2.3 From 6bcfe0453597c81573bbfb838b5ec087d74f07de Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Thu, 20 Jun 2019 14:06:37 +0200 Subject: QSysInfo: fix warning on non-Linux about unconditional adjacent returns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit E.g. as seen on Integrity: "global/qglobal.cpp", line 2967: warning #111-D: statement is unreachable Change-Id: I6845192ee7fb14b66700b68118355c871b1a4baf Reviewed-by: Mårten Nordheim --- src/corelib/global/qglobal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp index 990554a682..a8ed8ca6fd 100644 --- a/src/corelib/global/qglobal.cpp +++ b/src/corelib/global/qglobal.cpp @@ -2952,6 +2952,7 @@ QString QSysInfo::machineHostName() struct utsname u; if (uname(&u) == 0) return QString::fromLocal8Bit(u.nodename); + return QString(); #else # ifdef Q_OS_WIN // Important: QtNetwork depends on machineHostName() initializing ws2_32.dll @@ -2964,7 +2965,6 @@ QString QSysInfo::machineHostName() hostName[sizeof(hostName) - 1] = '\0'; return QString::fromLocal8Bit(hostName); #endif - return QString(); } #endif // QT_BOOTSTRAPPED -- cgit v1.2.3 From 08103d3a527d32c782ba9e3f6701e6676ac81a57 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Thu, 1 Sep 2016 20:37:12 +0200 Subject: QDBusAbstractInterface: make call() and asyncCall() variadic templates ... and use a variant of the QString::multiArg() trick to pass the variable number of arguments into an out-of-line function. The idea of this patch is to make the lowly asyncCall() and call() functions the principal interface for DBus calls again. Currently, it is more efficient to build up a QList and pass that to the *WithArgumentList() methods. With more efficient, I mean that the equivalent calls with QList expand to less client code, probably because of the need to construct eight QVariant instances, destroy them again, and then take the hit for a function call with so many arguments, which can all but be efficient. Consequently, when porting the NM bearer plugin to use call() instead of callWithArgumentList(), text size increased by ~3KiB on my machine. So, looking for a way to avoid the overhead of so many default arguments, while at the same time allowing to pass even more arguments than the predefined eight, I considered the QString::arg() method, but discarded it because it does not scale to arbitrarily (ie. SEP-limited) many arguments. Variadic templates scale to SEP-limited many arguments, but they are templates and deduce the exact type, so constraining the template to only QVariants would have broken all users of the API which pass non-QVariants and expect an implicit con- version to resolve the call. So I decided to make a virtue of necessity, accept all argument types and convert them to QVariant when constructing the QString::multiArg()-inspired QVariant array. To bring this patch to its consequential end would require to pass the arguments as arrays instead of QLists, but to get a feeling of how much impact this new API has, I backed the new implementation by converting the array to a QList and calling the *WithArgumentList() methods. The result is, if I may say so, satisfying. Said bearer plugin drops from +3KiB (baseline: callWithArgumentList()) to -5KiB (2.0% and 3.2%, resp.); the (unmodified) connman bearer plugin, which already used call(), dropped by almost 6KiB (2.5%). While GCC can deal with the zero-sized array in a nullary call to the variadic templates, MSVC cannot, so an extra overload provides an alternative for zero arguments. [ChangeLog][QtDBus][QDBusAbstractInterface] The call() and asyncCall() methods now accept more than eight QVariant arguments. Change-Id: Ic85faca40949f9bb8c08756be3bfe945c9cdd952 Reviewed-by: Thiago Macieira --- src/dbus/qdbusabstractinterface.cpp | 111 ++++++++++++++++++++++++++++-------- src/dbus/qdbusabstractinterface.h | 94 ++++++++++++++++++++++-------- 2 files changed, 158 insertions(+), 47 deletions(-) (limited to 'src') diff --git a/src/dbus/qdbusabstractinterface.cpp b/src/dbus/qdbusabstractinterface.cpp index 220223d74d..8b62d0ac87 100644 --- a/src/dbus/qdbusabstractinterface.cpp +++ b/src/dbus/qdbusabstractinterface.cpp @@ -691,18 +691,20 @@ void QDBusAbstractInterface::internalPropSet(const char *propname, const QVarian } /*! - Calls the method \a method on this interface and passes the parameters to this function to the - method. + \fn QDBusAbstractInterface::call(const QString &message) + \internal +*/ + +/*! + \fn QDBusAbstractInterface::call(const QString &message, Args&&...args) + + Calls the method \a method on this interface and passes \a args to the method. + All \a args must be convertible to QVariant. The parameters to \c call are passed on to the remote function via D-Bus as input arguments. Output arguments are returned in the QDBusMessage reply. If the reply is an error reply, lastError() will also be set to the contents of the error message. - This function can be used with up to 8 parameters, passed in arguments \a arg1, \a arg2, - \a arg3, \a arg4, \a arg5, \a arg6, \a arg7 and \a arg8. If you need more than 8 - parameters or if you have a variable number of parameters to be passed, use - callWithArgumentList(). - It can be used the following way: \snippet code/src_qdbus_qdbusabstractinterface.cpp 0 @@ -710,6 +712,19 @@ void QDBusAbstractInterface::internalPropSet(const char *propname, const QVarian This example illustrates function calling with 0, 1 and 2 parameters and illustrates different parameter types passed in each (the first call to \c "ProcessWorkUnicode" will contain one Unicode string, the second call to \c "ProcessWork" will contain one string and one byte array). + + \note Before Qt 5.14, this function accepted a maximum of just eight (8) arguments. + + \sa callWithArgumentList() +*/ + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +/*! + \internal + + This function exists for binary compatibility with Qt versions < 5.14. + Programs recompiled against Qt 5.14 will use the variadic template function + instead. */ QDBusMessage QDBusAbstractInterface::call(const QString &method, const QVariant &arg1, const QVariant &arg2, @@ -722,27 +737,44 @@ QDBusMessage QDBusAbstractInterface::call(const QString &method, const QVariant { return call(QDBus::AutoDetect, method, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); } +#endif + +/*! + \fn QDBusAbstractInterface::call(QDBus::CallMode mode, const QString &message) + \internal +*/ /*! + \fn QDBusAbstractInterface::call(QDBus::CallMode mode, const QString &message, Args&&...args) + \overload - Calls the method \a method on this interface and passes the - parameters to this function to the method. If \a mode is \c - NoWaitForReply, then this function will return immediately after + Calls the method \a method on this interface and passes \a args to the method. + All \a args must be convertible to QVariant. + + If \a mode is \c NoWaitForReply, then this function will return immediately after placing the call, without waiting for a reply from the remote method. Otherwise, \a mode indicates whether this function should activate the Qt Event Loop while waiting for the reply to arrive. - This function can be used with up to 8 parameters, passed in arguments \a arg1, \a arg2, - \a arg3, \a arg4, \a arg5, \a arg6, \a arg7 and \a arg8. If you need more than 8 - parameters or if you have a variable number of parameters to be passed, use - callWithArgumentList(). - If this function reenters the Qt event loop in order to wait for the reply, it will exclude user input. During the wait, it may deliver signals and other method calls to your application. Therefore, it must be prepared to handle a reentrancy whenever a call is placed with call(). + + \note Before Qt 5.14, this function accepted a maximum of just eight (8) arguments. + + \sa callWithArgumentList() +*/ + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +/*! + \internal + + This function exists for binary compatibility with Qt versions < 5.14. + Programs recompiled against Qt 5.14 will use the variadic template function + instead. */ QDBusMessage QDBusAbstractInterface::call(QDBus::CallMode mode, const QString &method, const QVariant &arg1, @@ -787,22 +819,23 @@ QDBusMessage QDBusAbstractInterface::call(QDBus::CallMode mode, const QString &m return callWithArgumentList(mode, method, argList); } +#endif // Qt 5 +/*! + \fn QDBusAbstractInterface::asyncCall(const QString &message) + \internal +*/ /*! - \since 4.5 - Calls the method \a method on this interface and passes the parameters to this function to the - method. + \fn QDBusAbstractInterface::asyncCall(const QString &message, Args&&...args) + + Calls the method \a method on this interface and passes \a args to the method. + All \a args must be convertible to QVariant. The parameters to \c call are passed on to the remote function via D-Bus as input arguments. The returned QDBusPendingCall object can be used to find out information about the reply. - This function can be used with up to 8 parameters, passed in arguments \a arg1, \a arg2, - \a arg3, \a arg4, \a arg5, \a arg6, \a arg7 and \a arg8. If you need more than 8 - parameters or if you have a variable number of parameters to be passed, use - asyncCallWithArgumentList(). - It can be used the following way: \snippet code/src_qdbus_qdbusabstractinterface.cpp 1 @@ -810,6 +843,19 @@ QDBusMessage QDBusAbstractInterface::call(QDBus::CallMode mode, const QString &m This example illustrates function calling with 0, 1 and 2 parameters and illustrates different parameter types passed in each (the first call to \c "ProcessWorkUnicode" will contain one Unicode string, the second call to \c "ProcessWork" will contain one string and one byte array). + + \note Before Qt 5.14, this function accepted a maximum of just eight (8) arguments. + + \sa asyncCallWithArgumentList() +*/ + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +/*! + \internal + + This function exists for binary compatibility with Qt versions < 5.14. + Programs recompiled against Qt 5.14 will use the variadic template function + instead. */ QDBusPendingCall QDBusAbstractInterface::asyncCall(const QString &method, const QVariant &arg1, const QVariant &arg2, @@ -853,6 +899,7 @@ QDBusPendingCall QDBusAbstractInterface::asyncCall(const QString &method, const return asyncCallWithArgumentList(method, argList); } +#endif // Qt 5 /*! \internal @@ -865,6 +912,24 @@ QDBusMessage QDBusAbstractInterface::internalConstCall(QDBus::CallMode mode, return const_cast(this)->callWithArgumentList(mode, method, args); } +QDBusMessage QDBusAbstractInterface::doCall(QDBus::CallMode mode, const QString &method, const QVariant *args, size_t numArgs) +{ + QList list; + list.reserve(int(numArgs)); + for (size_t i = 0; i < numArgs; ++i) + list.append(args[i]); + return callWithArgumentList(mode, method, list); +} + +QDBusPendingCall QDBusAbstractInterface::doAsyncCall(const QString &method, const QVariant *args, size_t numArgs) +{ + QList list; + list.reserve(int(numArgs)); + for (size_t i = 0; i < numArgs; ++i) + list.append(args[i]); + return asyncCallWithArgumentList(method, list); +} + QT_END_NAMESPACE #endif // QT_NO_DBUS diff --git a/src/dbus/qdbusabstractinterface.h b/src/dbus/qdbusabstractinterface.h index d6b0870787..4f4c7430a5 100644 --- a/src/dbus/qdbusabstractinterface.h +++ b/src/dbus/qdbusabstractinterface.h @@ -49,6 +49,7 @@ #include #include #include +#include #ifdef interface #undef interface @@ -98,26 +99,52 @@ public: void setTimeout(int timeout); int timeout() const; + QDBusMessage call(const QString &method) + { + return doCall(QDBus::AutoDetect, method, nullptr, 0); + } + + template + QDBusMessage call(const QString &method, Args &&...args) + { + const QVariant variants[] = { QVariant(std::forward(args))... }; + return doCall(QDBus::AutoDetect, method, variants, sizeof...(args)); + } + + QDBusMessage call(QDBus::CallMode mode, const QString &method) + { + return doCall(mode, method, nullptr, 0); + } + + template + QDBusMessage call(QDBus::CallMode mode, const QString &method, Args &&...args) + { + const QVariant variants[] = { QVariant(std::forward(args))... }; + return doCall(mode, method, variants, sizeof...(args)); + } + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) QDBusMessage call(const QString &method, - const QVariant &arg1 = QVariant(), - const QVariant &arg2 = QVariant(), - const QVariant &arg3 = QVariant(), - const QVariant &arg4 = QVariant(), - const QVariant &arg5 = QVariant(), - const QVariant &arg6 = QVariant(), - const QVariant &arg7 = QVariant(), - const QVariant &arg8 = QVariant()); + const QVariant &arg1, + const QVariant &arg2, + const QVariant &arg3, + const QVariant &arg4, + const QVariant &arg5, + const QVariant &arg6, + const QVariant &arg7, + const QVariant &arg8); QDBusMessage call(QDBus::CallMode mode, const QString &method, - const QVariant &arg1 = QVariant(), - const QVariant &arg2 = QVariant(), - const QVariant &arg3 = QVariant(), - const QVariant &arg4 = QVariant(), - const QVariant &arg5 = QVariant(), - const QVariant &arg6 = QVariant(), - const QVariant &arg7 = QVariant(), - const QVariant &arg8 = QVariant()); + const QVariant &arg1, + const QVariant &arg2, + const QVariant &arg3, + const QVariant &arg4, + const QVariant &arg5, + const QVariant &arg6, + const QVariant &arg7, + const QVariant &arg8); +#endif // Qt 5 QDBusMessage callWithArgumentList(QDBus::CallMode mode, const QString &method, @@ -130,15 +157,30 @@ public: const QList &args, QObject *receiver, const char *member); + QDBusPendingCall asyncCall(const QString &method) + { + return doAsyncCall(method, nullptr, 0); + } + + template + QDBusPendingCall asyncCall(const QString &method, Args&&...args) + { + const QVariant variants[] = { QVariant(std::forward(args))... }; + return doAsyncCall(method, variants, sizeof...(args)); + } + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) QDBusPendingCall asyncCall(const QString &method, - const QVariant &arg1 = QVariant(), - const QVariant &arg2 = QVariant(), - const QVariant &arg3 = QVariant(), - const QVariant &arg4 = QVariant(), - const QVariant &arg5 = QVariant(), - const QVariant &arg6 = QVariant(), - const QVariant &arg7 = QVariant(), - const QVariant &arg8 = QVariant()); + const QVariant &arg1, + const QVariant &arg2, + const QVariant &arg3, + const QVariant &arg4, + const QVariant &arg5, + const QVariant &arg6, + const QVariant &arg7, + const QVariant &arg8); +#endif // Qt 5 + QDBusPendingCall asyncCallWithArgumentList(const QString &method, const QList &args); @@ -155,6 +197,10 @@ protected: const QString &method, const QList &args = QList()) const; +private: + QDBusMessage doCall(QDBus::CallMode mode, const QString &method, const QVariant *args, size_t numArgs); + QDBusPendingCall doAsyncCall(const QString &method, const QVariant *args, size_t numArgs); + private: Q_DECLARE_PRIVATE(QDBusAbstractInterface) Q_PRIVATE_SLOT(d_func(), void _q_serviceOwnerChanged(QString,QString,QString)) -- cgit v1.2.3 From 322ac729450351c4f1faf5a96a564f16bfbc6547 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Thu, 20 Jun 2019 02:13:12 +0200 Subject: QObject: fix memory order on load/store of signal spy callback set QSignalSpyCallbackSet is a set of pointers, so when we store a pointer to it for later dereferencing, we need to use a release fence for the store and a corresponding acquire on load, lest the two don't synchronize with each other and we end up with a data race. Amends a65752c71bd25bbb66bf33d3a82f7901419c5d95. Change-Id: Ic2983d76237c5c5b00eb2a3575b10beb84d57190 Reviewed-by: Thiago Macieira Reviewed-by: Olivier Goffart (Woboq GmbH) --- src/corelib/kernel/qobject.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 7eba9b05ff..8f80be30bd 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -82,7 +82,7 @@ Q_CORE_EXPORT QBasicAtomicPointer qt_signal_spy_callback_ void qt_register_signal_spy_callbacks(QSignalSpyCallbackSet *callback_set) { - qt_signal_spy_callback_set.store(callback_set); + qt_signal_spy_callback_set.storeRelease(callback_set); } QDynamicMetaObjectData::~QDynamicMetaObjectData() @@ -3696,7 +3696,7 @@ void doActivate(QObject *sender, int signal_index, void **argv) signal_index, argv); } - const QSignalSpyCallbackSet *signal_spy_set = callbacks_enabled ? qt_signal_spy_callback_set.load() : nullptr; + const QSignalSpyCallbackSet *signal_spy_set = callbacks_enabled ? qt_signal_spy_callback_set.loadAcquire() : nullptr; void *empty_argv[] = { nullptr }; if (!argv) -- cgit v1.2.3 From 6f84829031f318bfda1deff5f409b5ea6c6a5c5f Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Fri, 21 Jun 2019 18:35:22 +0200 Subject: QSimpleTextCodec: fix load memory order of atomic pointer The pointer value is not the only data we're interested in, but instead points to indirect data, so we need a release fence on store (present) and a corresponding acquire fence on load (was missing). Change-Id: I51f8251c0c7f4056192880430f2be5e0836dbed6 Reviewed-by: Thiago Macieira --- src/corelib/codecs/qsimplecodec.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/corelib/codecs/qsimplecodec.cpp b/src/corelib/codecs/qsimplecodec.cpp index 580461321a..16a9b8a7c3 100644 --- a/src/corelib/codecs/qsimplecodec.cpp +++ b/src/corelib/codecs/qsimplecodec.cpp @@ -610,7 +610,7 @@ QSimpleTextCodec::QSimpleTextCodec(int i) : forwardIndex(i), reverseMap(0) QSimpleTextCodec::~QSimpleTextCodec() { - delete reverseMap.loadRelaxed(); + delete reverseMap.loadAcquire(); } static QByteArray *buildReverseMap(int forwardIndex) @@ -662,12 +662,12 @@ QByteArray QSimpleTextCodec::convertFromUnicode(const QChar *in, int length, Con const char replacement = (state && state->flags & ConvertInvalidToNull) ? 0 : '?'; int invalid = 0; - QByteArray *rmap = reverseMap.loadRelaxed(); + QByteArray *rmap = reverseMap.loadAcquire(); if (!rmap){ rmap = buildReverseMap(this->forwardIndex); if (!reverseMap.testAndSetRelease(0, rmap)) { delete rmap; - rmap = reverseMap.loadRelaxed(); + rmap = reverseMap.loadAcquire(); } } -- cgit v1.2.3 From 35431062bd7bf0d27b9bf785ab3cbc7fac5a69da Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Mon, 3 Jun 2019 11:43:24 +0200 Subject: QString: towards QStringView::arg() pt.3: Long live QStringView/QLatin1String::arg() This version of arg(), unlike its QString counterpart, transparently accepts views without conversion to QString, and is also extensible to further argument types, say a future QFormattedNumber. [ChangeLog][QtCore][QStringView/QLatin1String] Added arg(), taking arbitrarily many strings. Change-Id: If40ef3c445f63383e32573f3f515fdda84c7fe3a Reviewed-by: Thiago Macieira --- src/corelib/tools/qstring.cpp | 108 ++++++++++++++++++++++++++++++-------- src/corelib/tools/qstring.h | 55 +++++++++++++++++++ src/corelib/tools/qstringview.cpp | 18 +++++++ src/corelib/tools/qstringview.h | 3 ++ 4 files changed, 161 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index 53513d4abb..89ab866703 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -8754,19 +8754,23 @@ QString QString::arg(double a, int fieldWidth, char fmt, int prec, QChar fillCha return replaceArgEscapes(*this, d, fieldWidth, arg, locale_arg, fillChar); } -static int getEscape(const QChar *uc, qsizetype *pos, qsizetype len, int maxNumber = 999) +static inline ushort to_unicode(const QChar c) { return c.unicode(); } +static inline ushort to_unicode(const char c) { return QLatin1Char{c}.unicode(); } + +template +static int getEscape(const Char *uc, qsizetype *pos, qsizetype len, int maxNumber = 999) { int i = *pos; ++i; if (i < len && uc[i] == QLatin1Char('L')) ++i; if (i < len) { - int escape = uc[i].unicode() - '0'; + int escape = to_unicode(uc[i]) - '0'; if (uint(escape) >= 10U) return -1; ++i; while (i < len) { - int digit = uc[i].unicode() - '0'; + int digit = to_unicode(uc[i]) - '0'; if (uint(digit) >= 10U) break; escape = (escape * 10) + digit; @@ -8817,18 +8821,23 @@ static int getEscape(const QChar *uc, qsizetype *pos, qsizetype len, int maxNumb namespace { struct Part { - Q_DECL_CONSTEXPR Part() : string{}, number{0} {} + Part() = default; // for QVarLengthArray; do not use Q_DECL_CONSTEXPR Part(QStringView s, int num = -1) - : string{s}, number{num} {} + : tag{QtPrivate::ArgBase::U16}, number{num}, data{s.utf16()}, size{s.size()} {} + Q_DECL_CONSTEXPR Part(QLatin1String s, int num = -1) + : tag{QtPrivate::ArgBase::L1}, number{num}, data{s.data()}, size{s.size()} {} + + void reset(QStringView s) noexcept { *this = {s, number}; } + void reset(QLatin1String s) noexcept { *this = {s, number}; } - QStringView string; + QtPrivate::ArgBase::Tag tag; int number; + const void *data; + qsizetype size; }; } // unnamed namespace -template <> -class QTypeInfo : public QTypeInfoMerger {}; // Q_DECLARE_METATYPE - +Q_DECLARE_TYPEINFO(Part, Q_PRIMITIVE_TYPE); namespace { @@ -8837,7 +8846,8 @@ enum { ExpectedParts = 32 }; typedef QVarLengthArray ParseResult; typedef QVarLengthArray ArgIndexToPlaceholderMap; -static ParseResult parseMultiArgFormatString(QStringView s) +template +static ParseResult parseMultiArgFormatString(StringView s) { ParseResult result; @@ -8884,16 +8894,29 @@ static ArgIndexToPlaceholderMap makeArgIndexToPlaceholderMap(const ParseResult & return result; } -static qsizetype resolveStringRefsAndReturnTotalSize(ParseResult &parts, const ArgIndexToPlaceholderMap &argIndexToPlaceholderMap, const QString *args[]) +static qsizetype resolveStringRefsAndReturnTotalSize(ParseResult &parts, const ArgIndexToPlaceholderMap &argIndexToPlaceholderMap, const QtPrivate::ArgBase *args[]) { + using namespace QtPrivate; qsizetype totalSize = 0; for (Part &part : parts) { if (part.number != -1) { const auto it = std::find(argIndexToPlaceholderMap.begin(), argIndexToPlaceholderMap.end(), part.number); - if (it != argIndexToPlaceholderMap.end()) - part.string = *args[it - argIndexToPlaceholderMap.begin()]; + if (it != argIndexToPlaceholderMap.end()) { + const auto &arg = *args[it - argIndexToPlaceholderMap.begin()]; + switch (arg.tag) { + case ArgBase::L1: + part.reset(static_cast(arg).string); + break; + case ArgBase::U8: + Q_UNREACHABLE(); // waiting for QUtf8String... + break; + case ArgBase::U16: + part.reset(static_cast(arg).string); + break; + } + } } - totalSize += part.string.size(); + totalSize += part.size; } return totalSize; } @@ -8901,18 +8924,35 @@ static qsizetype resolveStringRefsAndReturnTotalSize(ParseResult &parts, const A } // unnamed namespace QString QString::multiArg(int numArgs, const QString **args) const +{ + QVarLengthArray sva; + sva.reserve(numArgs); + QVarLengthArray pointers; + pointers.reserve(numArgs); + for (int i = 0; i < numArgs; ++i) { + sva.push_back(QtPrivate::qStringLikeToArg(*args[i])); + pointers.push_back(&sva.back()); + } + return QtPrivate::argToQString(qToStringViewIgnoringNull(*this), static_cast(numArgs), pointers.data()); +} + +Q_ALWAYS_INLINE QString to_string(QLatin1String s) noexcept { return s; } +Q_ALWAYS_INLINE QString to_string(QStringView s) noexcept { return s.toString(); } + +template +static QString argToQStringImpl(StringView pattern, size_t numArgs, const QtPrivate::ArgBase **args) { // Step 1-2 above - ParseResult parts = parseMultiArgFormatString(qToStringViewIgnoringNull(*this)); + ParseResult parts = parseMultiArgFormatString(pattern); // 3-4 ArgIndexToPlaceholderMap argIndexToPlaceholderMap = makeArgIndexToPlaceholderMap(parts); - if (argIndexToPlaceholderMap.size() > numArgs) // 3a - argIndexToPlaceholderMap.resize(numArgs); - else if (argIndexToPlaceholderMap.size() < numArgs) // 3b - qWarning("QString::arg: %d argument(s) missing in %s", - numArgs - argIndexToPlaceholderMap.size(), toLocal8Bit().data()); + if (static_cast(argIndexToPlaceholderMap.size()) > numArgs) // 3a + argIndexToPlaceholderMap.resize(int(numArgs)); + else if (Q_UNLIKELY(static_cast(argIndexToPlaceholderMap.size()) < numArgs)) // 3b + qWarning("QString::arg: %d argument(s) missing in %ls", + int(numArgs - argIndexToPlaceholderMap.size()), qUtf16Printable(to_string(pattern))); // 5 const qsizetype totalSize = resolveStringRefsAndReturnTotalSize(parts, argIndexToPlaceholderMap, args); @@ -8922,15 +8962,37 @@ QString QString::multiArg(int numArgs, const QString **args) const auto out = const_cast(result.constData()); for (Part part : parts) { - if (const qsizetype sz = part.string.size()) { - memcpy(out, part.string.data(), sz * sizeof(QChar)); - out += sz; + switch (part.tag) { + case QtPrivate::ArgBase::L1: + if (part.size) { + qt_from_latin1(reinterpret_cast(out), + reinterpret_cast(part.data), part.size); + } + break; + case QtPrivate::ArgBase::U8: + Q_UNREACHABLE(); // waiting for QUtf8String + break; + case QtPrivate::ArgBase::U16: + if (part.size) + memcpy(out, part.data, part.size * sizeof(QChar)); + break; } + out += part.size; } return result; } +QString QtPrivate::argToQString(QStringView pattern, size_t n, const ArgBase **args) +{ + return argToQStringImpl(pattern, n, args); +} + +QString QtPrivate::argToQString(QLatin1String pattern, size_t n, const ArgBase **args) +{ + return argToQStringImpl(pattern, n, args); +} + /*! \fn bool QString::isSimpleText() const \internal diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index 89b25821ee..6820440ca2 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -106,6 +106,9 @@ public: Q_DECL_CONSTEXPR bool isNull() const noexcept { return !data(); } Q_DECL_CONSTEXPR bool isEmpty() const noexcept { return !size(); } + template + Q_REQUIRED_RESULT inline QString arg(Args &&...args) const; + Q_DECL_CONSTEXPR QLatin1Char at(int i) const { return Q_ASSERT(i >= 0), Q_ASSERT(i < size()), QLatin1Char(m_data[i]); } Q_DECL_CONSTEXPR QLatin1Char operator[](int i) const { return at(i); } @@ -1979,6 +1982,58 @@ inline const QString &asString(const QString &s) { return s; } inline QString &&asString(QString &&s) { return std::move(s); } } +// +// QStringView::arg() implementation +// + +namespace QtPrivate { + +struct ArgBase { + enum Tag : uchar { L1, U8, U16 } tag; +}; + +struct QStringViewArg : ArgBase { + QStringView string; + QStringViewArg() = default; + Q_DECL_CONSTEXPR explicit QStringViewArg(QStringView v) noexcept : ArgBase{U16}, string{v} {} +}; + +struct QLatin1StringArg : ArgBase { + QLatin1String string; + QLatin1StringArg() = default; + Q_DECL_CONSTEXPR explicit QLatin1StringArg(QLatin1String v) noexcept : ArgBase{L1}, string{v} {} +}; + +Q_REQUIRED_RESULT Q_CORE_EXPORT QString argToQString(QStringView pattern, size_t n, const ArgBase **args); +Q_REQUIRED_RESULT Q_CORE_EXPORT QString argToQString(QLatin1String pattern, size_t n, const ArgBase **args); + +template +Q_REQUIRED_RESULT Q_ALWAYS_INLINE QString argToQStringDispatch(StringView pattern, const Args &...args) +{ + const ArgBase *argBases[] = {&args..., /* avoid zero-sized array */ nullptr}; + return QtPrivate::argToQString(pattern, sizeof...(Args), argBases); +} + +Q_DECL_CONSTEXPR inline QStringViewArg qStringLikeToArg(QStringView s) noexcept { return QStringViewArg{s}; } + inline QStringViewArg qStringLikeToArg(const QChar &c) noexcept { return QStringViewArg{QStringView{&c, 1}}; } +Q_DECL_CONSTEXPR inline QLatin1StringArg qStringLikeToArg(QLatin1String s) noexcept { return QLatin1StringArg{s}; } + +} // namespace QtPrivate + +template +Q_ALWAYS_INLINE +QString QStringView::arg(Args &&...args) const +{ + return QtPrivate::argToQStringDispatch(*this, QtPrivate::qStringLikeToArg(args)...); +} + +template +Q_ALWAYS_INLINE +QString QLatin1String::arg(Args &&...args) const +{ + return QtPrivate::argToQStringDispatch(*this, QtPrivate::qStringLikeToArg(args)...); +} + QT_END_NAMESPACE #if defined(QT_USE_FAST_OPERATOR_PLUS) || defined(QT_USE_QSTRINGBUILDER) diff --git a/src/corelib/tools/qstringview.cpp b/src/corelib/tools/qstringview.cpp index d94ed4110e..cc852dd042 100644 --- a/src/corelib/tools/qstringview.cpp +++ b/src/corelib/tools/qstringview.cpp @@ -529,6 +529,24 @@ QT_BEGIN_NAMESPACE \sa operator[](), front(), back() */ +/*! + \fn QString QStringView::arg(Args &&...args) const + \fn QString QLatin1String::arg(Args &&...args) const + \since 5.14 + + Replaces occurrences of \c{%N} in this string with the corresponding + argument from \a args. The arguments are not positional: the first of + the \a args replaces the \c{%N} with the lowest \c{N} (all of them), the + second of the \a args the \c{%N} with the next-lowest \c{N} etc. + + \c Args can consist of anything that implicitly converts to QStringView + or QLatin1String. + + In addition, the following types are also supported: QChar, QLatin1Char. + + \sa QString::arg() +*/ + /*! \fn QChar QStringView::front() const diff --git a/src/corelib/tools/qstringview.h b/src/corelib/tools/qstringview.h index 67f5d2203c..b84b2995b9 100644 --- a/src/corelib/tools/qstringview.h +++ b/src/corelib/tools/qstringview.h @@ -226,6 +226,9 @@ public: // QString API // + template + Q_REQUIRED_RESULT inline QString arg(Args &&...args) const; // defined in qstring.h + Q_REQUIRED_RESULT QByteArray toLatin1() const { return QtPrivate::convertToLatin1(*this); } Q_REQUIRED_RESULT QByteArray toUtf8() const { return QtPrivate::convertToUtf8(*this); } Q_REQUIRED_RESULT QByteArray toLocal8Bit() const { return QtPrivate::convertToLocal8Bit(*this); } -- cgit v1.2.3 From ed02ab78eae652491892b6d8cfd60bcdf06e6bae Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Fri, 2 Sep 2016 09:03:20 +0200 Subject: QNetworkManagerSettings: port DBus calls from callWithArgumentList() to call() With the new variadic QDBusAbstractInterface::call() API, this saves ~5KiB in text size on optimized GCC 6.1 Linux AMD64 builds. Change-Id: Idc43bb07083f98b4b652d7331e545ba79be1e296 Reviewed-by: Thiago Macieira --- .../networkmanager/qnetworkmanagerservice.cpp | 37 ++++++---------------- 1 file changed, 10 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/plugins/bearer/networkmanager/qnetworkmanagerservice.cpp b/src/plugins/bearer/networkmanager/qnetworkmanagerservice.cpp index 3e77580015..35199eb7a2 100644 --- a/src/plugins/bearer/networkmanager/qnetworkmanagerservice.cpp +++ b/src/plugins/bearer/networkmanager/qnetworkmanagerservice.cpp @@ -72,11 +72,9 @@ QNetworkManagerInterface::QNetworkManagerInterface(QObject *parent) QLatin1String(NM_DBUS_PATH), DBUS_PROPERTIES_INTERFACE, QDBusConnection::systemBus()); - QList argumentList; - argumentList << QLatin1String(NM_DBUS_INTERFACE); QDBusPendingReply propsReply - = managerPropertiesInterface.callWithArgumentList(QDBus::Block,QLatin1String("GetAll"), - argumentList); + = managerPropertiesInterface.call(QDBus::Block, QLatin1String("GetAll"), QLatin1String(NM_DBUS_INTERFACE)); + if (!propsReply.isError()) { propertyMap = propsReply.value(); } else { @@ -344,11 +342,8 @@ QNetworkManagerInterfaceDevice::QNetworkManagerInterfaceDevice(const QString &de DBUS_PROPERTIES_INTERFACE, QDBusConnection::systemBus(),parent); - QList argumentList; - argumentList << QLatin1String(NM_DBUS_INTERFACE_DEVICE); QDBusPendingReply propsReply - = devicePropertiesInterface.callWithArgumentList(QDBus::Block,QLatin1String("GetAll"), - argumentList); + = devicePropertiesInterface.call(QDBus::Block, QLatin1String("GetAll"), QLatin1String(NM_DBUS_INTERFACE_DEVICE)); if (!propsReply.isError()) { propertyMap = propsReply.value(); @@ -446,11 +441,8 @@ QNetworkManagerInterfaceDeviceWired::QNetworkManagerInterfaceDeviceWired(const Q DBUS_PROPERTIES_INTERFACE, QDBusConnection::systemBus(),parent); - QList argumentList; - argumentList << QLatin1String(NM_DBUS_INTERFACE_DEVICE_WIRED); QDBusPendingReply propsReply - = deviceWiredPropertiesInterface.callWithArgumentList(QDBus::Block,QLatin1String("GetAll"), - argumentList); + = deviceWiredPropertiesInterface.call(QDBus::Block, QLatin1String("GetAll"), QLatin1String(NM_DBUS_INTERFACE_DEVICE_WIRED)); if (!propsReply.isError()) { propertyMap = propsReply.value(); @@ -543,11 +535,9 @@ QNetworkManagerInterfaceDeviceWireless::QNetworkManagerInterfaceDeviceWireless(c DBUS_PROPERTIES_INTERFACE, QDBusConnection::systemBus(),parent); - QList argumentList; - argumentList << QLatin1String(NM_DBUS_INTERFACE_DEVICE_WIRELESS); QDBusPendingReply propsReply - = deviceWirelessPropertiesInterface.callWithArgumentList(QDBus::Block,QLatin1String("GetAll"), - argumentList); + = deviceWirelessPropertiesInterface.call(QDBus::Block, QLatin1String("GetAll"), QLatin1String(NM_DBUS_INTERFACE_DEVICE_WIRELESS)); + if (!propsReply.isError()) { propertyMap = propsReply.value(); } @@ -647,11 +637,9 @@ QNetworkManagerInterfaceDeviceModem::QNetworkManagerInterfaceDeviceModem(const Q QLatin1String("org.freedesktop.DBus.Properties"), QDBusConnection::systemBus(),parent); - QList argumentList; - argumentList << QLatin1String(NM_DBUS_INTERFACE_DEVICE_MODEM); QDBusPendingReply propsReply - = deviceModemPropertiesInterface.callWithArgumentList(QDBus::Block,QLatin1String("GetAll"), - argumentList); + = deviceModemPropertiesInterface.call(QDBus::Block, QLatin1String("GetAll"), QLatin1String(NM_DBUS_INTERFACE_DEVICE_MODEM)); + if (!propsReply.isError()) { propertyMap = propsReply.value(); } @@ -746,9 +734,7 @@ QList QNetworkManagerSettings::listConnections() QString QNetworkManagerSettings::getConnectionByUuid(const QString &uuid) { - QList argumentList; - argumentList << QVariant::fromValue(uuid); - QDBusReply reply = callWithArgumentList(QDBus::Block,QLatin1String("GetConnectionByUuid"), argumentList); + QDBusReply reply = call(QDBus::Block, QLatin1String("GetConnectionByUuid"), uuid); return reply.value().path(); } @@ -917,11 +903,8 @@ QNetworkManagerConnectionActive::QNetworkManagerConnectionActive(const QString & QDBusConnection::systemBus()); - QList argumentList; - argumentList << QLatin1String(NM_DBUS_INTERFACE_ACTIVE_CONNECTION); QDBusPendingReply propsReply - = connectionActivePropertiesInterface.callWithArgumentList(QDBus::Block,QLatin1String("GetAll"), - argumentList); + = connectionActivePropertiesInterface.call(QDBus::Block, QLatin1String("GetAll"), QLatin1String(NM_DBUS_INTERFACE_ACTIVE_CONNECTION)); if (!propsReply.isError()) { propertyMap = propsReply.value(); -- cgit v1.2.3 From 9c357d64db3aedba6802e9a508abbbdc67f3ef8d Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Mon, 3 Jun 2019 23:08:05 +0200 Subject: QtCore: use new QLatin1String::arg() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Saves ~600B in text size on optimized GCC 9.1 Linux AMD64 builds. Change-Id: I12f4e7c8d28af9549b481859bc96a155aeb6f15c Reviewed-by: Edward Welbourne Reviewed-by: Mårten Nordheim --- src/corelib/io/qfile.cpp | 2 +- src/corelib/io/qstandardpaths_android.cpp | 2 +- src/corelib/io/qurl.cpp | 18 +++++++++--------- src/corelib/kernel/qjni.cpp | 8 ++++---- src/corelib/kernel/qsystemsemaphore_systemv.cpp | 6 +++--- src/corelib/plugin/qlibrary_unix.cpp | 12 ++++++------ 6 files changed, 24 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/corelib/io/qfile.cpp b/src/corelib/io/qfile.cpp index 6959c7a40b..9f9a9e3040 100644 --- a/src/corelib/io/qfile.cpp +++ b/src/corelib/io/qfile.cpp @@ -810,7 +810,7 @@ QFile::copy(const QString &newName) error = true; d->setError(QFile::CopyError, tr("Cannot open %1 for input").arg(d->fileName)); } else { - QString fileTemplate = QLatin1String("%1/qt_temp.XXXXXX"); + const auto fileTemplate = QLatin1String("%1/qt_temp.XXXXXX"); #ifdef QT_NO_TEMPORARYFILE QFile out(fileTemplate.arg(QFileInfo(newName).path())); if (!out.open(QIODevice::ReadWrite)) diff --git a/src/corelib/io/qstandardpaths_android.cpp b/src/corelib/io/qstandardpaths_android.cpp index 83108e4387..1f4e0de1e7 100644 --- a/src/corelib/io/qstandardpaths_android.cpp +++ b/src/corelib/io/qstandardpaths_android.cpp @@ -136,7 +136,7 @@ static QString getExternalStoragePublicDirectory(const char *directoryField) */ static QString getExternalFilesDir(const char *directoryField = 0) { - QString &path = (*androidDirCache)[QString(QLatin1String("APPNAME_%1")).arg(QLatin1String(directoryField))]; + QString &path = (*androidDirCache)[QLatin1String("APPNAME_%1").arg(QLatin1String(directoryField))]; if (!path.isEmpty()) return path; diff --git a/src/corelib/io/qurl.cpp b/src/corelib/io/qurl.cpp index f82ad48b2c..d9ebc6c750 100644 --- a/src/corelib/io/qurl.cpp +++ b/src/corelib/io/qurl.cpp @@ -3991,21 +3991,21 @@ static QString errorMessage(QUrlPrivate::ErrorCode errorCode, const QString &err return QString(); case QUrlPrivate::InvalidSchemeError: { - QString msg = QStringLiteral("Invalid scheme (character '%1' not permitted)"); + auto msg = QLatin1String("Invalid scheme (character '%1' not permitted)"); return msg.arg(c); } case QUrlPrivate::InvalidUserNameError: - return QString(QStringLiteral("Invalid user name (character '%1' not permitted)")) + return QLatin1String("Invalid user name (character '%1' not permitted)") .arg(c); case QUrlPrivate::InvalidPasswordError: - return QString(QStringLiteral("Invalid password (character '%1' not permitted)")) + return QLatin1String("Invalid password (character '%1' not permitted)") .arg(c); case QUrlPrivate::InvalidRegNameError: if (errorPosition != -1) - return QString(QStringLiteral("Invalid hostname (character '%1' not permitted)")) + return QLatin1String("Invalid hostname (character '%1' not permitted)") .arg(c); else return QStringLiteral("Invalid hostname (contains invalid characters)"); @@ -4014,9 +4014,9 @@ static QString errorMessage(QUrlPrivate::ErrorCode errorCode, const QString &err case QUrlPrivate::InvalidIPv6AddressError: return QStringLiteral("Invalid IPv6 address"); case QUrlPrivate::InvalidCharacterInIPv6Error: - return QStringLiteral("Invalid IPv6 address (character '%1' not permitted)").arg(c); + return QLatin1String("Invalid IPv6 address (character '%1' not permitted)").arg(c); case QUrlPrivate::InvalidIPvFutureError: - return QStringLiteral("Invalid IPvFuture address (character '%1' not permitted)").arg(c); + return QLatin1String("Invalid IPvFuture address (character '%1' not permitted)").arg(c); case QUrlPrivate::HostMissingEndBracket: return QStringLiteral("Expected ']' to match '[' in hostname"); @@ -4026,15 +4026,15 @@ static QString errorMessage(QUrlPrivate::ErrorCode errorCode, const QString &err return QStringLiteral("Port field was empty"); case QUrlPrivate::InvalidPathError: - return QString(QStringLiteral("Invalid path (character '%1' not permitted)")) + return QLatin1String("Invalid path (character '%1' not permitted)") .arg(c); case QUrlPrivate::InvalidQueryError: - return QString(QStringLiteral("Invalid query (character '%1' not permitted)")) + return QLatin1String("Invalid query (character '%1' not permitted)") .arg(c); case QUrlPrivate::InvalidFragmentError: - return QString(QStringLiteral("Invalid fragment (character '%1' not permitted)")) + return QLatin1String("Invalid fragment (character '%1' not permitted)") .arg(c); case QUrlPrivate::AuthorityPresentAndPathIsRelative: diff --git a/src/corelib/kernel/qjni.cpp b/src/corelib/kernel/qjni.cpp index 75a2436d9d..5f652d70e3 100644 --- a/src/corelib/kernel/qjni.cpp +++ b/src/corelib/kernel/qjni.cpp @@ -47,9 +47,9 @@ QT_BEGIN_NAMESPACE -static inline QString keyBase() +static inline QLatin1String keyBase() { - return QStringLiteral("%1%2:%3"); + return QLatin1String("%1%2:%3"); } static QString qt_convertJString(jstring string) @@ -154,7 +154,7 @@ static jmethodID getCachedMethodID(JNIEnv *env, if (className.isEmpty()) return getMethodID(env, clazz, name, sig, isStatic); - const QString key = keyBase().arg(QLatin1String(className)).arg(QLatin1String(name)).arg(QLatin1String(sig)); + const QString key = keyBase().arg(QLatin1String(className), QLatin1String(name), QLatin1String(sig)); QHash::const_iterator it; { @@ -206,7 +206,7 @@ static jfieldID getCachedFieldID(JNIEnv *env, if (className.isNull()) return getFieldID(env, clazz, name, sig, isStatic); - const QString key = keyBase().arg(QLatin1String(className)).arg(QLatin1String(name)).arg(QLatin1String(sig)); + const QString key = keyBase().arg(QLatin1String(className), QLatin1String(name), QLatin1String(sig)); QHash::const_iterator it; { diff --git a/src/corelib/kernel/qsystemsemaphore_systemv.cpp b/src/corelib/kernel/qsystemsemaphore_systemv.cpp index 9e438ae2a6..2c38d74d2d 100644 --- a/src/corelib/kernel/qsystemsemaphore_systemv.cpp +++ b/src/corelib/kernel/qsystemsemaphore_systemv.cpp @@ -76,7 +76,7 @@ key_t QSystemSemaphorePrivate::handle(QSystemSemaphore::AccessMode mode) #if QT_CONFIG(translation) QCoreApplication::tr("%1: key is empty", "QSystemSemaphore") #else - QString::fromLatin1("%1: key is empty") + QLatin1String("%1: key is empty") #endif .arg(QLatin1String("QSystemSemaphore::handle:")); error = QSystemSemaphore::KeyError; @@ -94,7 +94,7 @@ key_t QSystemSemaphorePrivate::handle(QSystemSemaphore::AccessMode mode) #if QT_CONFIG(translation) QCoreApplication::tr("%1: unable to make key", "QSystemSemaphore") #else - QString::fromLatin1("%1: unable to make key") + QLatin1String("%1: unable to make key") #endif .arg(QLatin1String("QSystemSemaphore::handle:")); error = QSystemSemaphore::KeyError; @@ -111,7 +111,7 @@ key_t QSystemSemaphorePrivate::handle(QSystemSemaphore::AccessMode mode) #if QT_CONFIG(translation) QCoreApplication::tr("%1: ftok failed", "QSystemSemaphore") #else - QString::fromLatin1("%1: ftok failed") + QLatin1String("%1: ftok failed") #endif .arg(QLatin1String("QSystemSemaphore::handle:")); error = QSystemSemaphore::KeyError; diff --git a/src/corelib/plugin/qlibrary_unix.cpp b/src/corelib/plugin/qlibrary_unix.cpp index e03814984c..84fcc53d52 100644 --- a/src/corelib/plugin/qlibrary_unix.cpp +++ b/src/corelib/plugin/qlibrary_unix.cpp @@ -77,14 +77,14 @@ QStringList QLibraryPrivate::suffixes_sys(const QString& fullVersion) // .so is preferred. # if defined(__ia64) if (!fullVersion.isEmpty()) { - suffixes << QString::fromLatin1(".so.%1").arg(fullVersion); + suffixes << QLatin1String(".so.%1").arg(fullVersion); } else { suffixes << QLatin1String(".so"); } # endif if (!fullVersion.isEmpty()) { - suffixes << QString::fromLatin1(".sl.%1").arg(fullVersion); - suffixes << QString::fromLatin1(".%1").arg(fullVersion); + suffixes << QLatin1String(".sl.%1").arg(fullVersion); + suffixes << QLatin1String(".%1").arg(fullVersion); } else { suffixes << QLatin1String(".sl"); } @@ -93,15 +93,15 @@ QStringList QLibraryPrivate::suffixes_sys(const QString& fullVersion) #else if (!fullVersion.isEmpty()) { - suffixes << QString::fromLatin1(".so.%1").arg(fullVersion); + suffixes << QLatin1String(".so.%1").arg(fullVersion); } else { suffixes << QLatin1String(".so"); } #endif # ifdef Q_OS_MAC if (!fullVersion.isEmpty()) { - suffixes << QString::fromLatin1(".%1.bundle").arg(fullVersion); - suffixes << QString::fromLatin1(".%1.dylib").arg(fullVersion); + suffixes << QLatin1String(".%1.bundle").arg(fullVersion); + suffixes << QLatin1String(".%1.dylib").arg(fullVersion); } else { suffixes << QLatin1String(".bundle") << QLatin1String(".dylib"); } -- cgit v1.2.3 From f8f4a75d82b0fdf7cd19ba241c6ea406e7442d22 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Mon, 3 Jun 2019 23:08:05 +0200 Subject: QtDBus: use new QLatin1String::arg() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Saves more than 4KiB in text size (~0.7%) on optimized GCC 9.1 Linux AMD64 builds. Change-Id: I5b29eae620370abd91e3a7f98e660c32470aed1c Reviewed-by: Thiago Macieira Reviewed-by: Edward Welbourne Reviewed-by: Mårten Nordheim --- src/dbus/qdbusabstractinterface.cpp | 16 ++++++++-------- src/dbus/qdbusintegrator.cpp | 25 +++++++++++++------------ src/dbus/qdbusinternalfilters.cpp | 20 ++++++++++---------- src/dbus/qdbusmarshaller.cpp | 12 ++++++------ src/dbus/qdbusmetaobject.cpp | 2 +- src/dbus/qdbuspendingcall.cpp | 4 ++-- src/dbus/qdbusreply.cpp | 4 ++-- src/dbus/qdbusutil_p.h | 12 ++++++------ src/dbus/qdbusxmlgenerator.cpp | 14 +++++++------- 9 files changed, 55 insertions(+), 54 deletions(-) (limited to 'src') diff --git a/src/dbus/qdbusabstractinterface.cpp b/src/dbus/qdbusabstractinterface.cpp index 8b62d0ac87..87de784fc0 100644 --- a/src/dbus/qdbusabstractinterface.cpp +++ b/src/dbus/qdbusabstractinterface.cpp @@ -164,7 +164,7 @@ bool QDBusAbstractInterfacePrivate::property(const QMetaProperty &mp, void *retu "used to read property %s.%s", mp.typeName(), qPrintable(interface), mp.name()); lastError = QDBusError(QDBusError::Failed, - QString::fromLatin1("Unregistered type %1 cannot be handled") + QLatin1String("Unregistered type %1 cannot be handled") .arg(QLatin1String(mp.typeName()))); return false; } @@ -220,15 +220,15 @@ bool QDBusAbstractInterfacePrivate::property(const QMetaProperty &mp, void *retu } // there was an error... - QString errmsg = QLatin1String("Unexpected `%1' (%2) when retrieving property `%3.%4' " - "(expected type `%5' (%6))"); + const auto errmsg = QLatin1String("Unexpected `%1' (%2) when retrieving property `%3.%4' " + "(expected type `%5' (%6))"); lastError = QDBusError(QDBusError::InvalidSignature, - errmsg.arg(QString::fromLatin1(foundType), - QString::fromLatin1(foundSignature), + errmsg.arg(QLatin1String(foundType), + QLatin1String(foundSignature), interface, - QString::fromUtf8(mp.name()), - QString::fromLatin1(mp.typeName()), - QString::fromLatin1(expectedSignature))); + QLatin1String(mp.name()), + QLatin1String(mp.typeName()), + QLatin1String(expectedSignature))); return false; } diff --git a/src/dbus/qdbusintegrator.cpp b/src/dbus/qdbusintegrator.cpp index af16940d4f..9306498f89 100644 --- a/src/dbus/qdbusintegrator.cpp +++ b/src/dbus/qdbusintegrator.cpp @@ -340,8 +340,9 @@ static QByteArray buildMatchRule(const QString &service, const QString &objectPath, const QString &interface, const QString &member, const QDBusConnectionPrivate::ArgMatchRules &argMatch, const QString & /*signature*/) { - QString result = QLatin1String("type='signal',"); - QString keyValue = QLatin1String("%1='%2',"); + QString result; + result += QLatin1String("type='signal',"); + const auto keyValue = QLatin1String("%1='%2',"); if (!service.isEmpty()) result += keyValue.arg(QLatin1String("sender"), service); @@ -354,13 +355,13 @@ static QByteArray buildMatchRule(const QString &service, // add the argument string-matching now if (!argMatch.args.isEmpty()) { - keyValue = QLatin1String("arg%1='%2',"); + const QString keyValue = QLatin1String("arg%1='%2',"); for (int i = 0; i < argMatch.args.count(); ++i) if (!argMatch.args.at(i).isNull()) result += keyValue.arg(i).arg(argMatch.args.at(i)); } if (!argMatch.arg0namespace.isEmpty()) { - result += QStringLiteral("arg0namespace='%1',").arg(argMatch.arg0namespace); + result += QLatin1String("arg0namespace='%1',").arg(argMatch.arg0namespace); } result.chop(1); // remove ending comma @@ -1369,19 +1370,19 @@ void QDBusConnectionPrivate::sendError(const QDBusMessage &msg, QDBusError::Erro if (msg.interface().isEmpty()) interfaceMsg = QLatin1String("any interface"); else - interfaceMsg = QString::fromLatin1("interface '%1'").arg(msg.interface()); + interfaceMsg = QLatin1String("interface '%1'").arg(msg.interface()); send(msg.createErrorReply(code, - QString::fromLatin1("No such method '%1' in %2 at object path '%3' " - "(signature '%4')") + QLatin1String("No such method '%1' in %2 at object path '%3' " + "(signature '%4')") .arg(msg.member(), interfaceMsg, msg.path(), msg.signature()))); } else if (code == QDBusError::UnknownInterface) { send(msg.createErrorReply(QDBusError::UnknownInterface, - QString::fromLatin1("No such interface '%1' at object path '%2'") + QLatin1String("No such interface '%1' at object path '%2'") .arg(msg.interface(), msg.path()))); } else if (code == QDBusError::UnknownObject) { send(msg.createErrorReply(QDBusError::UnknownObject, - QString::fromLatin1("No such object path '%1'").arg(msg.path()))); + QLatin1String("No such object path '%1'").arg(msg.path()))); } } @@ -1551,8 +1552,8 @@ void QDBusConnectionPrivate::handleObjectCall(const QDBusMessage &msg) objThread = result.obj->thread(); if (!objThread) { send(msg.createErrorReply(QDBusError::InternalError, - QString::fromLatin1("Object '%1' (at path '%2')" - " has no thread. Cannot deliver message.") + QLatin1String("Object '%1' (at path '%2')" + " has no thread. Cannot deliver message.") .arg(result.obj->objectName(), msg.path()))); return; } @@ -2082,7 +2083,7 @@ QDBusMessage QDBusConnectionPrivate::sendWithReplyLocal(const QDBusMessage &mess if (interface.isEmpty()) interface = QLatin1String(""); return QDBusMessage::createError(QDBusError::InternalError, - QString::fromLatin1("Internal error trying to call %1.%2 at %3 (signature '%4'") + QLatin1String("Internal error trying to call %1.%2 at %3 (signature '%4'") .arg(interface, message.member(), message.path(), message.signature())); } diff --git a/src/dbus/qdbusinternalfilters.cpp b/src/dbus/qdbusinternalfilters.cpp index 0ef5061b5f..edee4fc1e5 100644 --- a/src/dbus/qdbusinternalfilters.cpp +++ b/src/dbus/qdbusinternalfilters.cpp @@ -203,7 +203,7 @@ QString qDBusIntrospectObject(const QDBusConnectionPrivate::ObjectTreeNode &node static inline QDBusMessage interfaceNotFoundError(const QDBusMessage &msg, const QString &interface_name) { return msg.createErrorReply(QDBusError::UnknownInterface, - QString::fromLatin1("Interface %1 was not found in object %2") + QLatin1String("Interface %1 was not found in object %2") .arg(interface_name, msg.path())); } @@ -211,10 +211,10 @@ static inline QDBusMessage propertyNotFoundError(const QDBusMessage &msg, const QString &interface_name, const QByteArray &property_name) { return msg.createErrorReply(QDBusError::UnknownProperty, - QString::fromLatin1("Property %1%2%3 was not found in object %4") + QLatin1String("Property %1%2%3 was not found in object %4") .arg(interface_name, - QString::fromLatin1(interface_name.isEmpty() ? "" : "."), - QString::fromLatin1(property_name), + QLatin1String(interface_name.isEmpty() ? "" : "."), + QLatin1String(property_name), msg.path())); } @@ -302,16 +302,16 @@ static QDBusMessage propertyWriteReply(const QDBusMessage &msg, const QString &i return propertyNotFoundError(msg, interface_name, property_name); case PropertyTypeMismatch: return msg.createErrorReply(QDBusError::InvalidArgs, - QString::fromLatin1("Invalid arguments for writing to property %1%2%3") + QLatin1String("Invalid arguments for writing to property %1%2%3") .arg(interface_name, - QString::fromLatin1(interface_name.isEmpty() ? "" : "."), - QString::fromLatin1(property_name))); + QLatin1String(interface_name.isEmpty() ? "" : "."), + QLatin1String(property_name))); case PropertyReadOnly: return msg.createErrorReply(QDBusError::PropertyReadOnly, - QString::fromLatin1("Property %1%2%3 is read-only") + QLatin1String("Property %1%2%3 is read-only") .arg(interface_name, - QString::fromLatin1(interface_name.isEmpty() ? "" : "."), - QString::fromLatin1(property_name))); + QLatin1String(interface_name.isEmpty() ? "" : "."), + QLatin1String(property_name))); case PropertyWriteFailed: return msg.createErrorReply(QDBusError::InternalError, QString::fromLatin1("Internal error")); diff --git a/src/dbus/qdbusmarshaller.cpp b/src/dbus/qdbusmarshaller.cpp index 4ea6cefff6..8e0b3e4598 100644 --- a/src/dbus/qdbusmarshaller.cpp +++ b/src/dbus/qdbusmarshaller.cpp @@ -211,7 +211,7 @@ inline bool QDBusMarshaller::append(const QDBusVariant &arg) qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. " "Use qDBusRegisterMetaType to register it", QMetaType::typeName(id), id); - error(QString::fromLatin1("Unregistered type %1 passed in arguments") + error(QLatin1String("Unregistered type %1 passed in arguments") .arg(QLatin1String(QMetaType::typeName(id)))); return false; } @@ -253,7 +253,7 @@ inline QDBusMarshaller *QDBusMarshaller::beginArray(int id) qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. " "Use qDBusRegisterMetaType to register it", QMetaType::typeName(id), id); - error(QString::fromLatin1("Unregistered type %1 passed in arguments") + error(QLatin1String("Unregistered type %1 passed in arguments") .arg(QLatin1String(QMetaType::typeName(id)))); return this; } @@ -268,14 +268,14 @@ inline QDBusMarshaller *QDBusMarshaller::beginMap(int kid, int vid) qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. " "Use qDBusRegisterMetaType to register it", QMetaType::typeName(kid), kid); - error(QString::fromLatin1("Unregistered type %1 passed in arguments") + error(QLatin1String("Unregistered type %1 passed in arguments") .arg(QLatin1String(QMetaType::typeName(kid)))); return this; } if (ksignature[1] != 0 || !QDBusUtil::isValidBasicType(*ksignature)) { qWarning("QDBusMarshaller: type '%s' (%d) cannot be used as the key type in a D-BUS map.", QMetaType::typeName(kid), kid); - error(QString::fromLatin1("Type %1 passed in arguments cannot be used as a key in a map") + error(QLatin1String("Type %1 passed in arguments cannot be used as a key in a map") .arg(QLatin1String(QMetaType::typeName(kid)))); return this; } @@ -286,7 +286,7 @@ inline QDBusMarshaller *QDBusMarshaller::beginMap(int kid, int vid) qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. " "Use qDBusRegisterMetaType to register it", typeName, vid); - error(QString::fromLatin1("Unregistered type %1 passed in arguments") + error(QLatin1String("Unregistered type %1 passed in arguments") .arg(QLatin1String(typeName))); return this; } @@ -417,7 +417,7 @@ bool QDBusMarshaller::appendVariantInternal(const QVariant &arg) qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. " "Use qDBusRegisterMetaType to register it", QMetaType::typeName(id), id); - error(QString::fromLatin1("Unregistered type %1 passed in arguments") + error(QLatin1String("Unregistered type %1 passed in arguments") .arg(QLatin1String(QMetaType::typeName(id)))); return false; } diff --git a/src/dbus/qdbusmetaobject.cpp b/src/dbus/qdbusmetaobject.cpp index c4296f5c55..806cf7b415 100644 --- a/src/dbus/qdbusmetaobject.cpp +++ b/src/dbus/qdbusmetaobject.cpp @@ -649,7 +649,7 @@ QDBusMetaObject *QDBusMetaObject::createMetaObject(const QString &interface, con // mark as an error error = QDBusError(QDBusError::UnknownInterface, - QString::fromLatin1("Interface '%1' was not found") + QLatin1String("Interface '%1' was not found") .arg(interface)); return 0; } diff --git a/src/dbus/qdbuspendingcall.cpp b/src/dbus/qdbuspendingcall.cpp index b51fae9b65..7c2238900c 100644 --- a/src/dbus/qdbuspendingcall.cpp +++ b/src/dbus/qdbuspendingcall.cpp @@ -221,8 +221,8 @@ void QDBusPendingCallPrivate::checkReceivedSignature() // can't use startsWith here because a null string doesn't start or end with an empty string if (replyMessage.signature().indexOf(expectedReplySignature) != 0) { - QString errorMsg = QLatin1String("Unexpected reply signature: got \"%1\", " - "expected \"%2\""); + const auto errorMsg = QLatin1String("Unexpected reply signature: got \"%1\", " + "expected \"%2\""); replyMessage = QDBusMessage::createError( QDBusError::InvalidSignature, errorMsg.arg(replyMessage.signature(), expectedReplySignature)); diff --git a/src/dbus/qdbusreply.cpp b/src/dbus/qdbusreply.cpp index 6abfaf174c..cf1a70508c 100644 --- a/src/dbus/qdbusreply.cpp +++ b/src/dbus/qdbusreply.cpp @@ -228,14 +228,14 @@ void qDBusReplyFill(const QDBusMessage &reply, QDBusError &error, QVariant &data receivedSignature = ""; QString errorMsg; if (receivedType) { - errorMsg = QString::fromLatin1("Unexpected reply signature: got \"%1\" (%4), " + errorMsg = QLatin1String("Unexpected reply signature: got \"%1\" (%4), " "expected \"%2\" (%3)") .arg(QLatin1String(receivedSignature), QLatin1String(expectedSignature), QLatin1String(data.typeName()), QLatin1String(receivedType)); } else { - errorMsg = QString::fromLatin1("Unexpected reply signature: got \"%1\", " + errorMsg = QLatin1String("Unexpected reply signature: got \"%1\", " "expected \"%2\" (%3)") .arg(QLatin1String(receivedSignature), QLatin1String(expectedSignature), diff --git a/src/dbus/qdbusutil_p.h b/src/dbus/qdbusutil_p.h index 5a4b461194..0814cc8050 100644 --- a/src/dbus/qdbusutil_p.h +++ b/src/dbus/qdbusutil_p.h @@ -106,7 +106,7 @@ namespace Q_DBUS_NO_EXPORT QDBusUtil return false; } if (isValidInterfaceName(name)) return true; - *error = QDBusError(QDBusError::InvalidInterface, QString::fromLatin1("Invalid interface class: %1").arg(name)); + *error = QDBusError(QDBusError::InvalidInterface, QLatin1String("Invalid interface class: %1").arg(name)); return false; } @@ -118,7 +118,7 @@ namespace Q_DBUS_NO_EXPORT QDBusUtil return false; } if (isValidBusName(name)) return true; - *error = QDBusError(QDBusError::InvalidService, QString::fromLatin1("Invalid service name: %1").arg(name)); + *error = QDBusError(QDBusError::InvalidService, QLatin1String("Invalid service name: %1").arg(name)); return false; } @@ -130,7 +130,7 @@ namespace Q_DBUS_NO_EXPORT QDBusUtil return false; } if (isValidObjectPath(path)) return true; - *error = QDBusError(QDBusError::InvalidObjectPath, QString::fromLatin1("Invalid object path: %1").arg(path)); + *error = QDBusError(QDBusError::InvalidObjectPath, QLatin1String("Invalid object path: %1").arg(path)); return false; } @@ -143,8 +143,8 @@ namespace Q_DBUS_NO_EXPORT QDBusUtil return false; } if (isValidMemberName(name)) return true; - *error = QDBusError(QDBusError::InvalidMember, QString::fromLatin1("Invalid %1 name: %2") - .arg(QString::fromLatin1(nameType), name)); + *error = QDBusError(QDBusError::InvalidMember, QLatin1String("Invalid %1 name: %2") + .arg(QLatin1String(nameType), name)); return false; } @@ -156,7 +156,7 @@ namespace Q_DBUS_NO_EXPORT QDBusUtil return false; } if (isValidErrorName(name)) return true; - *error = QDBusError(QDBusError::InvalidInterface, QString::fromLatin1("Invalid error name: %1").arg(name)); + *error = QDBusError(QDBusError::InvalidInterface, QLatin1String("Invalid error name: %1").arg(name)); return false; } diff --git a/src/dbus/qdbusxmlgenerator.cpp b/src/dbus/qdbusxmlgenerator.cpp index e3c503aa0f..c6b3b90508 100644 --- a/src/dbus/qdbusxmlgenerator.cpp +++ b/src/dbus/qdbusxmlgenerator.cpp @@ -108,14 +108,14 @@ static QString generateInterfaceXml(const QMetaObject *mo, int flags, int method if (!signature) continue; - retval += QString::fromLatin1(" \n \n \n") + retval += QLatin1String(">\n \n \n") .arg(typeNameToXml(typeName)); } else { retval += QLatin1String("/>\n"); @@ -157,12 +157,12 @@ static QString generateInterfaceXml(const QMetaObject *mo, int flags, int method if (typeId != QMetaType::UnknownType && typeId != QMetaType::Void) { const char *typeName = QDBusMetaType::typeToSignature(typeId); if (typeName) { - xml += QString::fromLatin1(" \n") + xml += QLatin1String(" \n") .arg(typeNameToXml(typeName)); // do we need to describe this argument? if (QDBusMetaType::signatureToType(typeName) == QVariant::Invalid) - xml += QString::fromLatin1(" \n") + xml += QLatin1String(" \n") .arg(typeNameToXml(QMetaType::typeName(typeId))); } else { qWarning() << "Unsupported return type" << typeId << QMetaType::typeName(typeId) << "in method" << mm.name(); @@ -199,7 +199,7 @@ static QString generateInterfaceXml(const QMetaObject *mo, int flags, int method QString name; if (!names.at(j - 1).isEmpty()) - name = QString::fromLatin1("name=\"%1\" ").arg(QLatin1String(names.at(j - 1))); + name = QLatin1String("name=\"%1\" ").arg(QLatin1String(names.at(j - 1))); bool isOutput = isSignal || j > inputCount; @@ -233,7 +233,7 @@ static QString generateInterfaceXml(const QMetaObject *mo, int flags, int method " value=\"true\"/>\n"); retval += xml; - retval += QString::fromLatin1(" \n") + retval += QLatin1String(" \n") .arg(isSignal ? QLatin1String("signal") : QLatin1String("method")); } @@ -256,7 +256,7 @@ QString qDBusGenerateMetaObjectXml(QString interface, const QMetaObject *mo, if (xml.isEmpty()) return QString(); // don't add an empty interface - return QString::fromLatin1(" \n%2 \n") + return QLatin1String(" \n%2 \n") .arg(interface, xml); } #if 0 -- cgit v1.2.3 From 7000b70821493222ea2af10aae420b8cdd830953 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Mon, 3 Jun 2019 11:43:24 +0200 Subject: QString: towards QStringView::arg() pt.4: port QString::arg() to QStringView::arg() This allows us to drop QString::multiArg() from the build as soon as we can break BC, ie. in Qt 6. Change-Id: Ibdfbf9e9586952e0ec125120bb5966eb56c0ce67 Reviewed-by: Thiago Macieira --- src/corelib/tools/qstring.cpp | 2 ++ src/corelib/tools/qstring.h | 18 +++++++++--------- 2 files changed, 11 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index 89ab866703..f9a9fcfe91 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -8923,6 +8923,7 @@ static qsizetype resolveStringRefsAndReturnTotalSize(ParseResult &parts, const A } // unnamed namespace +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) QString QString::multiArg(int numArgs, const QString **args) const { QVarLengthArray sva; @@ -8935,6 +8936,7 @@ QString QString::multiArg(int numArgs, const QString **args) const } return QtPrivate::argToQString(qToStringViewIgnoringNull(*this), static_cast(numArgs), pointers.data()); } +#endif Q_ALWAYS_INLINE QString to_string(QLatin1String s) noexcept { return s; } Q_ALWAYS_INLINE QString to_string(QStringView s) noexcept { return s.toString(); } diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index 6820440ca2..b56b37edf3 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -924,8 +924,8 @@ private: void reallocData(uint alloc, bool grow = false); #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) void expand(int i); -#endif QString multiArg(int numArgs, const QString **args) const; +#endif static int compare_helper(const QChar *data1, int length1, const QChar *data2, int length2, Qt::CaseSensitivity cs = Qt::CaseSensitive) noexcept; @@ -1054,30 +1054,30 @@ inline QString QString::arg(short a, int fieldWidth, int base, QChar fillChar) c inline QString QString::arg(ushort a, int fieldWidth, int base, QChar fillChar) const { return arg(qulonglong(a), fieldWidth, base, fillChar); } inline QString QString::arg(const QString &a1, const QString &a2) const -{ const QString *args[2] = { &a1, &a2 }; return multiArg(2, args); } +{ return qToStringViewIgnoringNull(*this).arg(a1, a2); } inline QString QString::arg(const QString &a1, const QString &a2, const QString &a3) const -{ const QString *args[3] = { &a1, &a2, &a3 }; return multiArg(3, args); } +{ return qToStringViewIgnoringNull(*this).arg(a1, a2, a3); } inline QString QString::arg(const QString &a1, const QString &a2, const QString &a3, const QString &a4) const -{ const QString *args[4] = { &a1, &a2, &a3, &a4 }; return multiArg(4, args); } +{ return qToStringViewIgnoringNull(*this).arg(a1, a2, a3, a4); } inline QString QString::arg(const QString &a1, const QString &a2, const QString &a3, const QString &a4, const QString &a5) const -{ const QString *args[5] = { &a1, &a2, &a3, &a4, &a5 }; return multiArg(5, args); } +{ return qToStringViewIgnoringNull(*this).arg(a1, a2, a3, a4, a5); } inline QString QString::arg(const QString &a1, const QString &a2, const QString &a3, const QString &a4, const QString &a5, const QString &a6) const -{ const QString *args[6] = { &a1, &a2, &a3, &a4, &a5, &a6 }; return multiArg(6, args); } +{ return qToStringViewIgnoringNull(*this).arg(a1, a2, a3, a4, a5, a6); } inline QString QString::arg(const QString &a1, const QString &a2, const QString &a3, const QString &a4, const QString &a5, const QString &a6, const QString &a7) const -{ const QString *args[7] = { &a1, &a2, &a3, &a4, &a5, &a6, &a7 }; return multiArg(7, args); } +{ return qToStringViewIgnoringNull(*this).arg(a1, a2, a3, a4, a5, a6, a7); } inline QString QString::arg(const QString &a1, const QString &a2, const QString &a3, const QString &a4, const QString &a5, const QString &a6, const QString &a7, const QString &a8) const -{ const QString *args[8] = { &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8 }; return multiArg(8, args); } +{ return qToStringViewIgnoringNull(*this).arg(a1, a2, a3, a4, a5, a6, a7, a8); } inline QString QString::arg(const QString &a1, const QString &a2, const QString &a3, const QString &a4, const QString &a5, const QString &a6, const QString &a7, const QString &a8, const QString &a9) const -{ const QString *args[9] = { &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9 }; return multiArg(9, args); } +{ return qToStringViewIgnoringNull(*this).arg(a1, a2, a3, a4, a5, a6, a7, a8, a9); } inline QString QString::section(QChar asep, int astart, int aend, SectionFlags aflags) const { return section(QString(asep), astart, aend, aflags); } -- cgit v1.2.3 From c086fc7341bcd57d0b7a459bb3ba7cfe1ccbd8be Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Wed, 19 Jun 2019 20:11:19 +0200 Subject: Report correct data when multiple volumes are mounted to the same path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We read the data into the iterator from the system, but then recreate the QStorageInfo object based on the rootPath, and then stat, discarding the data in the iterator. We can overwrite the data with the information in the iterator, which partially fixes the issue. Volume information that can only be retrieved by stat'ing the root path, such as size information, will only be correct for one of the entries. Change-Id: Ie98590876d6a5f525af009f4ff5d595cbc308b3f Fixes: QTBUG-63209 Reviewed-by: Andrius Štikonas Reviewed-by: Thiago Macieira --- src/corelib/io/qstorageinfo_unix.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/corelib/io/qstorageinfo_unix.cpp b/src/corelib/io/qstorageinfo_unix.cpp index b7621b5d2f..11b5af069a 100644 --- a/src/corelib/io/qstorageinfo_unix.cpp +++ b/src/corelib/io/qstorageinfo_unix.cpp @@ -846,6 +846,9 @@ QList QStorageInfoPrivate::mountedVolumes() const QString mountDir = it.rootPath(); QStorageInfo info(mountDir); + info.d->device = it.device(); + info.d->fileSystemType = it.fileSystemType(); + info.d->subvolume = it.subvolume(); if (info.bytesTotal() == 0) continue; volumes.append(info); -- cgit v1.2.3 From fe6e54fb1f5cda652b9489f740763f8d735621dd Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Fri, 14 Jun 2019 11:34:08 +0200 Subject: TLS socket: make verification callback lock-free (OpenSSL) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When our QSslSocketBackendPrivate (OpenSSL backend) was developed, the ancient versions of OpenSSL did not have an API needed to pass an application-specific data into verification callback. Thus the developers resorted to the use of global variables (a list with errors) and locks. Some of our auto-tests use QNAM and in-process server. Whenever the client (essentially qhttpthreadeddelegate) and the server live in different threads, any use of 'https' is dead-lock prone, which recent events demonstrated and which were previously observed but not understood properly (rare occasions, not always easy to reproduce). Now we fix this for good by removing locking. There are two places (in 5.12) where these locks are needed: 1. Before calling SSL_connect/SSL_accept (handshake) - here we reuse the same trick we do in PSK callback ('SSL' has an external data set, and it's 'this', meaning an object of type QSslSocketBackendPrivate). 2. The static member function 'verify', here we do not have 'SSL', but we have our temporary 'X509_STORE', to which we can directly attach an external data - a pointer to a vector to collect verification errors. Note, this change assumes that OpenSSL Qt is build/linked against is at least of version 1.0.1 - we set external data on SSL unconditionally (no version checks). Fixes: QTBUG-76157 Change-Id: I05c98e77dfd5fb0c2c260fb6c463732facf53ffc Reviewed-by: Mårten Nordheim --- src/network/ssl/qsslsocket_openssl.cpp | 106 +++++++++++---------- src/network/ssl/qsslsocket_openssl11_symbols_p.h | 2 + src/network/ssl/qsslsocket_openssl_symbols.cpp | 10 ++ src/network/ssl/qsslsocket_openssl_symbols_p.h | 1 + .../ssl/qsslsocket_opensslpre11_symbols_p.h | 2 + 5 files changed, 70 insertions(+), 51 deletions(-) (limited to 'src') diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index c8bc6e069a..ec772ddb45 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -70,6 +70,10 @@ #include "qwindowscarootfetcher_p.h" #endif +#if !QT_CONFIG(opensslv11) +#include +#endif + #include #include #include @@ -253,44 +257,41 @@ QSslErrorEntry QSslErrorEntry::fromStoreContext(X509_STORE_CTX *ctx) }; } -// ### This list is shared between all threads, and protected by a -// mutex. Investigate using thread local storage instead. -struct QSslErrorList -{ - QMutex mutex; - QVector errors; -}; - -Q_GLOBAL_STATIC(QSslErrorList, _q_sslErrorList) - int q_X509Callback(int ok, X509_STORE_CTX *ctx) { if (!ok) { // Store the error and at which depth the error was detected. - _q_sslErrorList()->errors << QSslErrorEntry::fromStoreContext(ctx); -#if !QT_CONFIG(opensslv11) -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << "verification error: dumping bad certificate"; - qCDebug(lcSsl) << QSslCertificatePrivate::QSslCertificate_from_X509(q_X509_STORE_CTX_get_current_cert(ctx)).toPem(); - qCDebug(lcSsl) << "dumping chain"; - const auto certs = QSslSocketBackendPrivate::STACKOFX509_to_QSslCertificates(q_X509_STORE_CTX_get_chain(ctx)); - for (const QSslCertificate &cert : certs) { - qCDebug(lcSsl) << "Issuer:" << "O=" << cert.issuerInfo(QSslCertificate::Organization) - << "CN=" << cert.issuerInfo(QSslCertificate::CommonName) - << "L=" << cert.issuerInfo(QSslCertificate::LocalityName) - << "OU=" << cert.issuerInfo(QSslCertificate::OrganizationalUnitName) - << "C=" << cert.issuerInfo(QSslCertificate::CountryName) - << "ST=" << cert.issuerInfo(QSslCertificate::StateOrProvinceName); - qCDebug(lcSsl) << "Subject:" << "O=" << cert.subjectInfo(QSslCertificate::Organization) - << "CN=" << cert.subjectInfo(QSslCertificate::CommonName) - << "L=" << cert.subjectInfo(QSslCertificate::LocalityName) - << "OU=" << cert.subjectInfo(QSslCertificate::OrganizationalUnitName) - << "C=" << cert.subjectInfo(QSslCertificate::CountryName) - << "ST=" << cert.subjectInfo(QSslCertificate::StateOrProvinceName); - qCDebug(lcSsl) << "Valid:" << cert.effectiveDate() << '-' << cert.expiryDate(); + + using ErrorListPtr = QVector*; + ErrorListPtr errors = nullptr; + + // Error list is attached to either 'SSL' or 'X509_STORE'. + if (X509_STORE *store = q_X509_STORE_CTX_get0_store(ctx)) { // We try store first: +#if QT_CONFIG(opensslv11) + errors = ErrorListPtr(q_X509_STORE_get_ex_data(store, 0)); +#else + errors = ErrorListPtr(q_CRYPTO_get_ex_data(&store->ex_data, 0)); +#endif // opensslv11 + } + + if (!errors) { + // Not found on store? Try SSL and its external data then. According to the OpenSSL's + // documentation: + // + // "Whenever a X509_STORE_CTX object is created for the verification of the peers certificate + // during a handshake, a pointer to the SSL object is stored into the X509_STORE_CTX object + // to identify the connection affected. To retrieve this pointer the X509_STORE_CTX_get_ex_data() + // function can be used with the correct index." + if (SSL *ssl = static_cast(q_X509_STORE_CTX_get_ex_data(ctx, q_SSL_get_ex_data_X509_STORE_CTX_idx()))) + errors = ErrorListPtr(q_SSL_get_ex_data(ssl, QSslSocketBackendPrivate::s_indexForSSLExtraData + 1)); + } + + if (!errors) { + qCWarning(lcSsl, "Neither X509_STORE, nor SSL contains error list, handshake failure"); + return 0; } -#endif // QSSLSOCKET_DEBUG -#endif // !QT_CONFIG(opensslv11) + + errors->append(QSslErrorEntry::fromStoreContext(ctx)); } // Always return OK to allow verification to continue. We handle the // errors gracefully after collecting all errors, after verification has @@ -445,11 +446,7 @@ bool QSslSocketBackendPrivate::initSslContext() else q_SSL_set_accept_state(ssl); -#if OPENSSL_VERSION_NUMBER >= 0x10001000L - // Save a pointer to this object into the SSL structure. - if (QSslSocket::sslLibraryVersionNumber() >= 0x10001000L) - q_SSL_set_ex_data(ssl, s_indexForSSLExtraData, this); -#endif + q_SSL_set_ex_data(ssl, s_indexForSSLExtraData, this); #if OPENSSL_VERSION_NUMBER >= 0x10001000L && !defined(OPENSSL_NO_PSK) // Set the client callback for PSK @@ -1002,14 +999,14 @@ bool QSslSocketBackendPrivate::startHandshake() if (inSetAndEmitError) return false; - QMutexLocker locker(&_q_sslErrorList()->mutex); - _q_sslErrorList()->errors.clear(); + QVector lastErrors; + q_SSL_set_ex_data(ssl, s_indexForSSLExtraData + 1, &lastErrors); int result = (mode == QSslSocket::SslClientMode) ? q_SSL_connect(ssl) : q_SSL_accept(ssl); + q_SSL_set_ex_data(ssl, s_indexForSSLExtraData + 1, nullptr); - const auto &lastErrors = _q_sslErrorList()->errors; if (!lastErrors.isEmpty()) storePeerCertificates(); - for (const auto ¤tError : lastErrors) { + for (const auto ¤tError : qAsConst(lastErrors)) { emit q->peerVerifyError(_q_OpenSSL_to_QSslError(currentError.code, configuration.peerCertificateChain.value(currentError.depth))); if (q->state() != QAbstractSocket::ConnectedState) @@ -1017,7 +1014,6 @@ bool QSslSocketBackendPrivate::startHandshake() } errorList << lastErrors; - locker.unlock(); // Connection aborted during handshake phase. if (q->state() != QAbstractSocket::ConnectedState) @@ -1420,7 +1416,20 @@ QList QSslSocketBackendPrivate::verify(const QList & } } - QMutexLocker sslErrorListMutexLocker(&_q_sslErrorList()->mutex); + QVector lastErrors; +#if QT_CONFIG(opensslv11) + if (!q_X509_STORE_set_ex_data(certStore, 0, &lastErrors)) { + qCWarning(lcSsl) << "Unable to attach external data (error list) to a store"; + errors << QSslError(QSslError::UnspecifiedError); + return errors; + } +#else + if (!q_CRYPTO_set_ex_data(&certStore->ex_data, 0, &lastErrors)) { + qCWarning(lcSsl) << "Unable to attach external data (error list) to a store"; + errors << QSslError(QSslError::UnspecifiedError); + return errors; + } +#endif // opensslv11 // Register a custom callback to get all verification errors. q_X509_STORE_set_verify_cb(certStore, q_X509Callback); @@ -1470,12 +1479,7 @@ QList QSslSocketBackendPrivate::verify(const QList & q_OPENSSL_sk_free((OPENSSL_STACK *)intermediates); // Now process the errors - const auto errorList = std::move(_q_sslErrorList()->errors); - _q_sslErrorList()->errors.clear(); - sslErrorListMutexLocker.unlock(); - - // Translate the errors if (QSslCertificatePrivate::isBlacklisted(certificateChain[0])) { QSslError error(QSslError::CertificateBlacklisted, certificateChain[0]); errors << error; @@ -1489,8 +1493,8 @@ QList QSslSocketBackendPrivate::verify(const QList & } // Translate errors from the error list into QSslErrors. - errors.reserve(errors.size() + errorList.size()); - for (const auto &error : qAsConst(errorList)) + errors.reserve(errors.size() + lastErrors.size()); + for (const auto &error : qAsConst(lastErrors)) errors << _q_OpenSSL_to_QSslError(error.code, certificateChain.value(error.depth)); q_X509_STORE_free(certStore); diff --git a/src/network/ssl/qsslsocket_openssl11_symbols_p.h b/src/network/ssl/qsslsocket_openssl11_symbols_p.h index ec7e7ea1b8..0c32b0a934 100644 --- a/src/network/ssl/qsslsocket_openssl11_symbols_p.h +++ b/src/network/ssl/qsslsocket_openssl11_symbols_p.h @@ -105,6 +105,8 @@ ASN1_TIME *q_X509_getm_notAfter(X509 *a); long q_X509_get_version(X509 *a); EVP_PKEY *q_X509_get_pubkey(X509 *a); void q_X509_STORE_set_verify_cb(X509_STORE *ctx, X509_STORE_CTX_verify_cb verify_cb); +int q_X509_STORE_set_ex_data(X509_STORE *ctx, int idx, void *data); +void *q_X509_STORE_get_ex_data(X509_STORE *r, int idx); STACK_OF(X509) *q_X509_STORE_CTX_get0_chain(X509_STORE_CTX *ctx); void q_DH_get0_pqg(const DH *dh, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g); int q_DH_bits(DH *dh); diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp index 6a5ab91669..483a2cbad0 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols.cpp +++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp @@ -178,6 +178,8 @@ DEFINEFUNC(ASN1_TIME *, X509_getm_notAfter, X509 *a, a, return nullptr, return) DEFINEFUNC(long, X509_get_version, X509 *a, a, return -1, return) DEFINEFUNC(EVP_PKEY *, X509_get_pubkey, X509 *a, a, return nullptr, return) DEFINEFUNC2(void, X509_STORE_set_verify_cb, X509_STORE *a, a, X509_STORE_CTX_verify_cb verify_cb, verify_cb, return, DUMMYARG) +DEFINEFUNC3(int, X509_STORE_set_ex_data, X509_STORE *a, a, int idx, idx, void *data, data, return 0, return) +DEFINEFUNC2(void *, X509_STORE_get_ex_data, X509_STORE *r, r, int idx, idx, return nullptr, return) DEFINEFUNC(STACK_OF(X509) *, X509_STORE_CTX_get0_chain, X509_STORE_CTX *a, a, return nullptr, return) DEFINEFUNC3(void, CRYPTO_free, void *str, str, const char *file, file, int line, line, return, DUMMYARG) DEFINEFUNC(long, OpenSSL_version_num, void, DUMMYARG, return 0, return) @@ -223,6 +225,8 @@ DEFINEFUNC(int, CRYPTO_num_locks, DUMMYARG, DUMMYARG, return 0, return) DEFINEFUNC(void, CRYPTO_set_locking_callback, void (*a)(int, int, const char *, int), a, return, DUMMYARG) DEFINEFUNC(void, CRYPTO_set_id_callback, unsigned long (*a)(), a, return, DUMMYARG) DEFINEFUNC(void, CRYPTO_free, void *a, a, return, DUMMYARG) +DEFINEFUNC3(int, CRYPTO_set_ex_data, CRYPTO_EX_DATA *ad, ad, int idx, idx, void *val, val, return 0, return) +DEFINEFUNC2(void *, CRYPTO_get_ex_data, const CRYPTO_EX_DATA *ad, ad, int idx, idx, return nullptr, return) DEFINEFUNC(unsigned long, ERR_peek_last_error, DUMMYARG, DUMMYARG, return 0, return) DEFINEFUNC(void, ERR_free_strings, void, DUMMYARG, return, DUMMYARG) DEFINEFUNC(void, EVP_CIPHER_CTX_cleanup, EVP_CIPHER_CTX *a, a, return, DUMMYARG) @@ -533,6 +537,7 @@ DEFINEFUNC2(int, X509_STORE_CTX_set_purpose, X509_STORE_CTX *a, a, int b, b, ret DEFINEFUNC(int, X509_STORE_CTX_get_error, X509_STORE_CTX *a, a, return -1, return) DEFINEFUNC(int, X509_STORE_CTX_get_error_depth, X509_STORE_CTX *a, a, return -1, return) DEFINEFUNC(X509 *, X509_STORE_CTX_get_current_cert, X509_STORE_CTX *a, a, return nullptr, return) +DEFINEFUNC(X509_STORE *, X509_STORE_CTX_get0_store, X509_STORE_CTX *ctx, ctx, return nullptr, return) DEFINEFUNC(X509_STORE_CTX *, X509_STORE_CTX_new, DUMMYARG, DUMMYARG, return nullptr, return) DEFINEFUNC2(void *, X509_STORE_CTX_get_ex_data, X509_STORE_CTX *ctx, ctx, int idx, idx, return nullptr, return) DEFINEFUNC(int, SSL_get_ex_data_X509_STORE_CTX_idx, DUMMYARG, DUMMYARG, return -1, return) @@ -998,6 +1003,8 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(X509_get_version) RESOLVEFUNC(X509_get_pubkey) RESOLVEFUNC(X509_STORE_set_verify_cb) + RESOLVEFUNC(X509_STORE_set_ex_data) + RESOLVEFUNC(X509_STORE_get_ex_data) RESOLVEFUNC(CRYPTO_free) RESOLVEFUNC(OpenSSL_version_num) RESOLVEFUNC(OpenSSL_version) @@ -1046,6 +1053,8 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(CRYPTO_num_locks) RESOLVEFUNC(CRYPTO_set_id_callback) RESOLVEFUNC(CRYPTO_set_locking_callback) + RESOLVEFUNC(CRYPTO_set_ex_data) + RESOLVEFUNC(CRYPTO_get_ex_data) RESOLVEFUNC(ERR_peek_last_error) RESOLVEFUNC(ERR_free_strings) RESOLVEFUNC(EVP_CIPHER_CTX_cleanup) @@ -1305,6 +1314,7 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(X509_STORE_CTX_get_error) RESOLVEFUNC(X509_STORE_CTX_get_error_depth) RESOLVEFUNC(X509_STORE_CTX_get_current_cert) + RESOLVEFUNC(X509_STORE_CTX_get0_store) RESOLVEFUNC(X509_cmp) RESOLVEFUNC(X509_STORE_CTX_get_ex_data) diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h index bfdfbf0efc..3143117621 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols_p.h +++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h @@ -450,6 +450,7 @@ int q_X509_STORE_CTX_set_purpose(X509_STORE_CTX *ctx, int purpose); int q_X509_STORE_CTX_get_error(X509_STORE_CTX *ctx); int q_X509_STORE_CTX_get_error_depth(X509_STORE_CTX *ctx); X509 *q_X509_STORE_CTX_get_current_cert(X509_STORE_CTX *ctx); +X509_STORE *q_X509_STORE_CTX_get0_store(X509_STORE_CTX *ctx); // Diffie-Hellman support DH *q_DH_new(); diff --git a/src/network/ssl/qsslsocket_opensslpre11_symbols_p.h b/src/network/ssl/qsslsocket_opensslpre11_symbols_p.h index b7bac5d2a2..48364ceb1d 100644 --- a/src/network/ssl/qsslsocket_opensslpre11_symbols_p.h +++ b/src/network/ssl/qsslsocket_opensslpre11_symbols_p.h @@ -84,6 +84,8 @@ int q_CRYPTO_num_locks(); void q_CRYPTO_set_locking_callback(void (*a)(int, int, const char *, int)); void q_CRYPTO_set_id_callback(unsigned long (*a)()); void q_CRYPTO_free(void *a); +int q_CRYPTO_set_ex_data(CRYPTO_EX_DATA *ad, int idx, void *val); +void *q_CRYPTO_get_ex_data(const CRYPTO_EX_DATA *ad, int idx); unsigned long q_ERR_peek_last_error(); void q_ERR_free_strings(); void q_EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *a); -- cgit v1.2.3 From 25eb0408a653149b89ee6fe96a4ea70c4d38b515 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Wed, 19 Jun 2019 16:42:35 +0200 Subject: macOS: Don't capture local Q_D in QMacStyle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I01f14295e7e383b583931fc22e3d43151f7918b0 Reviewed-by: Morten Johan Sørvig --- src/plugins/styles/mac/qmacstyle_mac.mm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/plugins/styles/mac/qmacstyle_mac.mm b/src/plugins/styles/mac/qmacstyle_mac.mm index 482adf0433..ce08725684 100644 --- a/src/plugins/styles/mac/qmacstyle_mac.mm +++ b/src/plugins/styles/mac/qmacstyle_mac.mm @@ -2061,7 +2061,8 @@ QMacStyle::QMacStyle() Q_D(QMacStyle); // FIXME: Tie this logic into theme change, or even polish/unpolish if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave) { - d->appearanceObserver = QMacKeyValueObserver(NSApp, @"effectiveAppearance", [&d] { + d->appearanceObserver = QMacKeyValueObserver(NSApp, @"effectiveAppearance", [this] { + Q_D(QMacStyle); for (NSView *b : d->cocoaControls) [b release]; d->cocoaControls.clear(); -- cgit v1.2.3 From 7940791f477fd991bb4e1b833e10cc23c696332e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Thu, 20 Jun 2019 12:46:30 +0200 Subject: Report correct state change when destroying QAbstractAnimation Change-Id: Ibe5310e20268d1baa5b329a4d02a3dc38d875008 Reviewed-by: Simon Hausmann --- src/corelib/animation/qabstractanimation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/corelib/animation/qabstractanimation.cpp b/src/corelib/animation/qabstractanimation.cpp index 78b79e574a..42308b78fb 100644 --- a/src/corelib/animation/qabstractanimation.cpp +++ b/src/corelib/animation/qabstractanimation.cpp @@ -1061,7 +1061,7 @@ QAbstractAnimation::~QAbstractAnimation() if (d->state != Stopped) { QAbstractAnimation::State oldState = d->state; d->state = Stopped; - emit stateChanged(oldState, d->state); + emit stateChanged(d->state, oldState); if (oldState == QAbstractAnimation::Running) QAnimationTimer::unregisterAnimation(this); } -- cgit v1.2.3 From eb144c3c4d23d0d9cb877887ca4243b888500979 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 19 Jun 2019 12:37:51 +0200 Subject: Release program when returning from QOpenGLTextureBlitter::create() Change-Id: I27b9496f9a58ceabc613372463543068cb432bdc Fixes: QTBUG-60453 Reviewed-by: Johan Helsing --- src/gui/opengl/qopengltextureblitter.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/gui/opengl/qopengltextureblitter.cpp b/src/gui/opengl/qopengltextureblitter.cpp index b65df9dc82..b709f2f639 100644 --- a/src/gui/opengl/qopengltextureblitter.cpp +++ b/src/gui/opengl/qopengltextureblitter.cpp @@ -349,6 +349,9 @@ bool QOpenGLTextureBlitterPrivate::buildProgram(ProgramIndex idx, const char *vs p->glProgram->setUniformValue(p->swizzleUniformPos, false); + // minmize state left set after a create() + p->glProgram->release(); + return true; } -- cgit v1.2.3 From 0ef61500059e6ebed30b76140a78e6e9149b318e Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Wed, 19 Jun 2019 17:15:26 -0700 Subject: QCborValue::fromJsonValue: rewrite code to remove UB Converting an out-of-range FP to integer is UB. See comment in qnumeric_p.h. Change-Id: Ief874765cd7b43798de3fffd15a9bfe2c5fbbc01 Reviewed-by: Marc Mutz Reviewed-by: Ulf Hermann --- src/corelib/serialization/qjsoncbor.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/corelib/serialization/qjsoncbor.cpp b/src/corelib/serialization/qjsoncbor.cpp index 4f756df97c..4603750d11 100644 --- a/src/corelib/serialization/qjsoncbor.cpp +++ b/src/corelib/serialization/qjsoncbor.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2018 Intel Corporation. +** Copyright (C) 2019 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -598,10 +598,12 @@ QCborValue QCborValue::fromJsonValue(const QJsonValue &v) switch (v.type()) { case QJsonValue::Bool: return v.b; - case QJsonValue::Double: - if (v.dbl == qint64(v.dbl)) - return qint64(v.dbl); + case QJsonValue::Double: { + qint64 i; + if (convertDoubleTo(v.dbl, &i)) + return i; return v.dbl; + } case QJsonValue::String: return v.toString(); case QJsonValue::Array: -- cgit v1.2.3 From 47691260ca623643107ace846352a3604436e428 Mon Sep 17 00:00:00 2001 From: Rolf Eike Beer Date: Thu, 13 Jun 2019 11:05:12 +0200 Subject: move ENDSESSION_* compat defines to before they are actually used MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit amends 144d33df72e5ca905b1d64134784923d5c. Change-Id: Ic6bc475c9d8c3bb727ee209dbab437a18e219b6d Reviewed-by: Jörg Bornemann --- src/corelib/kernel/qcoreapplication_win.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/corelib/kernel/qcoreapplication_win.cpp b/src/corelib/kernel/qcoreapplication_win.cpp index 75cc813298..4f4a29b41b 100644 --- a/src/corelib/kernel/qcoreapplication_win.cpp +++ b/src/corelib/kernel/qcoreapplication_win.cpp @@ -694,6 +694,12 @@ static const char *winPosInsertAfter(quintptr h) static const char *sessionMgrLogOffOption(uint p) { +#ifndef ENDSESSION_CLOSEAPP +#define ENDSESSION_CLOSEAPP 0x00000001 +#endif +#ifndef ENDSESSION_CRITICAL +#define ENDSESSION_CRITICAL 0x40000000 +#endif static const QWinMessageMapping values[] = { {ENDSESSION_CLOSEAPP, "Close application"}, {ENDSESSION_CRITICAL, "Force application end"}, @@ -887,12 +893,6 @@ QString decodeMSG(const MSG& msg) parameters += QLatin1Char(')'); } break; -#ifndef ENDSESSION_CLOSEAPP -#define ENDSESSION_CLOSEAPP 0x00000001 -#endif -#ifndef ENDSESSION_CRITICAL -#define ENDSESSION_CRITICAL 0x40000000 -#endif case WM_QUERYENDSESSION: parameters = QLatin1String("End session: "); if (const char *logoffOption = sessionMgrLogOffOption(uint(wParam))) -- cgit v1.2.3 From 29670528d880d81cdd9eb55221b439a832437a6a Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Tue, 30 Apr 2019 17:23:08 +0200 Subject: Qt D-Bus: Document CMake commands Task-number: QTBUG-72159 Change-Id: I4b2f29a9d976f4b81acef3170b90016d2e8b5ef8 Reviewed-by: Leena Miettinen --- src/dbus/doc/snippets/cmake/examples.cmake | 3 + src/dbus/doc/src/qtdbus-cmake.qdoc | 224 +++++++++++++++++++++++++++++ 2 files changed, 227 insertions(+) create mode 100644 src/dbus/doc/snippets/cmake/examples.cmake create mode 100644 src/dbus/doc/src/qtdbus-cmake.qdoc (limited to 'src') diff --git a/src/dbus/doc/snippets/cmake/examples.cmake b/src/dbus/doc/snippets/cmake/examples.cmake new file mode 100644 index 0000000000..cb4f86844f --- /dev/null +++ b/src/dbus/doc/snippets/cmake/examples.cmake @@ -0,0 +1,3 @@ +#! [qt5_add_dbus_adaptor] +qt5_add_dbus_adaptor(GENERATED_SOURCES org.example.chat.xml chat.h ChatMainWindow) +#! [qt5_add_dbus_adaptor] diff --git a/src/dbus/doc/src/qtdbus-cmake.qdoc b/src/dbus/doc/src/qtdbus-cmake.qdoc new file mode 100644 index 0000000000..de127fa9f4 --- /dev/null +++ b/src/dbus/doc/src/qtdbus-cmake.qdoc @@ -0,0 +1,224 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! +\page qtdbus-cmake-qt5-add-dbus-interface.html +\ingroup cmake-commands-qtdbus + +\title qt5_add_dbus_interface + +\brief Generates C++ sources implementing an interface for a D-Bus interface +description file. + +\section1 Synopsis + +\badcode +qt5_add_dbus_interface( dbus_spec basename) +\endcode + +\section1 Description + +Generates C++ sources implementing an interface for a D-Bus interface description +file defined in \c{dbus_spec}. The generated files are named after \c{basename}: +\c{basename.h}, \c{basename.cpp}, \c{basename.moc}. The paths of the files +are added to \c{}. + +The function sets up a call to the \l{Qt D-Bus XML compiler (qdbusxml2cpp)} +in interface (proxy) mode. By default, \c{qdbusxml2cpp} generates a C++ +class named after the interface name, with a namespaced alias: + +\table +\header + \li D-Bus Interface Name + \li Class name + \li Namespaced name +\row + \li \c{org.example.chat} + \li \c{OrgExampleChatInterface} + \li \c{org.example.chat} +\endtable + +\section1 Options + +Options can be set using \c set_source_file_property on the \c dbus_spec: + +\table +\header + \li Option + \li Value + \li Description +\row + \li \c CLASSNAME + \li \c class_name + \li Override the default interface class name with \c{class_name}. +\row + \li \c NO_NAMESPACE + \li boolean + \li Do not generate the namespaced name if set to \c{ON}. +\row + \li \c INCLUDE + \li \c path + \li Add an \c{#include "path"} in the generated code. +\endtable +*/ + +/*! +\page qtdbus-cmake-qt5-add-dbus-interfaces.html +\ingroup cmake-commands-qtdbus + +\title qt5_add_dbus_interfaces + +\brief Generates C++ sources implementing interfaces for D-Bus interface +description files. + +\section1 Synopsis + +\badcode +qt5_add_dbus_interfaces( dbus_spec1 [dbus_spec2 ...]) +\endcode + +\section1 Description + +Generates C++ sources implementing D-Bus interfaces defined in \c{dbus_spec1}, +\c{dbus_spec2}, where each argument needs to be the path to a valid D-Bus +interface description file. The paths of the generated files are added to +\c{}. + +For each argument, a call to the \l{Qt D-Bus XML compiler (qdbusxml2cpp)} +in interface (proxy) mode is set up. + +The generated C++ source files are named after the XML file: For the file +\c{org.example.chat.xml} the generated header will be named +\c{orgexamplechatinterface.h}. + +\section1 Options + +Options can be set using \c set_source_file_property on each of the file +arguments: + +\table +\header + \li Option + \li Value + \li Description +\row + \li \c CLASSNAME + \li \c class_name + \li Override the default interface class name with \c{class_name}. +\row + \li \c NO_NAMESPACE + \li boolean + \li Do not generate the namespaced name if set to \c{ON}. +\row + \li \c INCLUDE + \li \c path + \li Add an \c{#include "path"} in the generated code. +\endtable +*/ + +/*! +\page qtdbus-cmake-qt5-generate-dbus-interface.html +\ingroup cmake-commands-qtdbus + +\title qt5_generate_dbus_interface + +\brief Generates a D-Bus interface from a header file. + +\section1 Synopsis + +\badcode +qt5_generate_dbus_interface(header + [customName] + [OPTIONS options] +) +\endcode + +\section1 Description + +Parses the C++ source or header file containing a QObject-derived class +declaration and generates a file containing the D-BUS Introspection XML. + +By default, the generated XML file is stored in the current binary directory, +and has the same base name as the header. You can specify a different name or +path by adding \c{customName} as an optional second argument. + +\section1 Options + +The function sets up a call to the \c{qdbuscpp2xml} command line tool. Further +arguments to the tool can be set after \c{OPTIONS}. +*/ + +/*! +\page qtdbus-cmake-qt5-add-dbus-adaptor.html +\ingroup cmake-commands-qtdbus + +\title qt5_add_dbus_adaptor + +\brief Generates an adaptor class for a D-Bus interface. + +\section1 Synopsis + +\badcode +qt5_add_dbus_adaptor( dbus_spec header parent_class + [basename] + [classname]) +\endcode + +\section1 Description + +Generates a C++ header file implementing an adaptor for a D-Bus interface +description file defined in \c{dbus_spec}. The path of the generated file is +added to \c{}. The generated adaptor class takes a pointer to +\c{parent_class} as QObject parent. \c{parent_class} should be declared in +\c{header}, which is included in the generated code as \c{#include "header"}. + +The function sets up a call to the \l{Qt D-Bus XML compiler (qdbusxml2cpp)} +in adaptor mode. The default file and class name are generated from the last +segment in the \c{dbus_spec} base name: + +\table +\header + \li XML file + \li Header file + \li Class name +\row + \li \c{org.example.chat} + \li \c{chatadaptor.h} + \li \c{ChatAdaptor} +\endtable + + +You can change the name of the header file to be generated by passing +\c{basename} as the fifth argument. The \c{.h} suffix is always added. + +You can change the default class name by passing \c{classname} as the sixth +argument. + +\section1 Examples + +\snippet cmake/examples.cmake qt5_add_dbus_adaptor +*/ -- cgit v1.2.3 From 121692e5408561f486cad5cd170ac8bcd2557eb2 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Wed, 19 Jun 2019 18:02:48 +0200 Subject: Use Xft DPI as basis for HiDPI scaling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GNOME indicates DPI modes by setting high DPI values in Xft.DPI, so we need to use the forced DPI setting instead of the real DPI, as basis for QPA pixel density. Change-Id: I6f25636383b16b89a3d5fe4c904afd079fe001aa Fixes: QTBUG-74836 Task-number: QTBUG-65424 Reviewed-by: Morten Johan Sørvig Reviewed-by: Shawn Rutledge --- src/plugins/platforms/xcb/qxcbscreen.cpp | 20 +++++++++++++++----- src/plugins/platforms/xcb/qxcbscreen.h | 1 + 2 files changed, 16 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp index 57dbdc9bec..39e83e0451 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.cpp +++ b/src/plugins/platforms/xcb/qxcbscreen.cpp @@ -658,16 +658,24 @@ QImage::Format QXcbScreen::format() const return format; } -QDpi QXcbScreen::logicalDpi() const +int QXcbScreen::forcedDpi() const { static const int overrideDpi = qEnvironmentVariableIntValue("QT_FONT_DPI"); if (overrideDpi) - return QDpi(overrideDpi, overrideDpi); + return overrideDpi; const int forcedDpi = m_virtualDesktop->forcedDpi(); - if (forcedDpi > 0) { + if (forcedDpi > 0) + return forcedDpi; + return 0; +} + +QDpi QXcbScreen::logicalDpi() const +{ + const int forcedDpi = this->forcedDpi(); + if (forcedDpi > 0) return QDpi(forcedDpi, forcedDpi); - } + return m_virtualDesktop->dpi(); } @@ -739,7 +747,9 @@ void QXcbScreen::updateGeometry(const QRect &geometry, uint8_t rotation) if (m_sizeMillimeters.isEmpty()) m_sizeMillimeters = sizeInMillimeters(geometry.size(), m_virtualDesktop->dpi()); - qreal dpi = geometry.width() / physicalSize().width() * qreal(25.4); + qreal dpi = forcedDpi(); + if (dpi <= 0) + dpi = geometry.width() / physicalSize().width() * qreal(25.4); // Use 128 as a reference DPI on small screens. This favors "small UI" over "large UI". qreal referenceDpi = physicalSize().width() <= 320 ? 128 : 96; diff --git a/src/plugins/platforms/xcb/qxcbscreen.h b/src/plugins/platforms/xcb/qxcbscreen.h index be6c45e415..ec3b07bfb7 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.h +++ b/src/plugins/platforms/xcb/qxcbscreen.h @@ -208,6 +208,7 @@ public: private: void sendStartupMessage(const QByteArray &message) const; + int forcedDpi() const; QByteArray getOutputProperty(xcb_atom_t atom) const; QByteArray getEdid() const; -- cgit v1.2.3 From 82da8306bc1313b85632eee0faf858239261a092 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 17 Jun 2019 17:47:30 +0200 Subject: Deal with multi-display scenarios when positioning menu popups This is to support rare setups involving an X11 server with multiple independent displays ("zaphod heads" on which the DISPLAY env var is different), possibly with multiple outputs forming a virtual desktop on each display. QMenu::popup() has been assuming that it should show on the screen where the mouse cursor is. That's good most of the time; but with multiple independent screens, QGuiApplication::screenAt(pos) cannot tell us which screen to use (it's ambiguous), but rather will choose the first screen that _could_ contain that position (as documented). In the example in QTBUG-76162, the QMenu has been constructed with a QDesktopScreenWidget as its parent specifically for the purpose of telling it which screen to pop up on; so we need to respect that. But QWidgetPrivate::init() sets the QObject::parent() to null (because the widget isn't actually shown as a child of the QDesktopScreenWidget), and QWidgetPrivate::create_sys() sets initialScreenIndex back to -1 to provide freedom to change the screen later; so QMenu has to remember the screen index for itself. QMenuBarPrivate::popupAction() searches the siblings of the screen on which the menubar is claiming to be (rather than all screens on all displays), to find the screen containing the point at the middle of the bottom edge of the clicked menubar item. It then sets initialScreenIndex so that QMenu::popup() will respect it rather than trying to decide for itself. Fixes: QTBUG-76162 Change-Id: I7a8f8e7aa2e9cf5340d446dc12726369ebe2589a Reviewed-by: Friedemann Kleint --- src/widgets/kernel/qwidget.cpp | 21 +++++++++++++++++---- src/widgets/kernel/qwidget_p.h | 2 ++ src/widgets/widgets/qmenu.cpp | 12 +++++++++++- src/widgets/widgets/qmenu_p.h | 2 ++ src/widgets/widgets/qmenubar.cpp | 15 ++++++++++++--- 5 files changed, 44 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 59053f3539..8b225df8be 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -2589,14 +2589,27 @@ bool QWidgetPrivate::setScreenForPoint(const QPoint &pos) Q_Q(QWidget); if (!q->isWindow()) return false; - // Find the screen for pos and make the widget undertand it is on that screen. + // Find the screen for pos and make the widget understand it is on that screen. + return setScreen(QGuiApplication::screenAt(pos)); +} + +/*! +\internal +Ensures that the widget's QWindow is set to be on the given \a screen. +Returns true if the screen was changed. +*/ + +bool QWidgetPrivate::setScreen(QScreen *screen) +{ + Q_Q(QWidget); + if (!screen || !q->isWindow()) + return false; const QScreen *currentScreen = windowHandle() ? windowHandle()->screen() : nullptr; - QScreen *actualScreen = QGuiApplication::screenAt(pos); - if (actualScreen && currentScreen != actualScreen) { + if (currentScreen != screen) { if (!windowHandle()) // Try to create a window handle if not created. createWinId(); if (windowHandle()) - windowHandle()->setScreen(actualScreen); + windowHandle()->setScreen(screen); return true; } return false; diff --git a/src/widgets/kernel/qwidget_p.h b/src/widgets/kernel/qwidget_p.h index ae50624c04..64bc463ae2 100644 --- a/src/widgets/kernel/qwidget_p.h +++ b/src/widgets/kernel/qwidget_p.h @@ -181,6 +181,7 @@ struct QTLWExtra { QRect frameStrut; QRect normalGeometry; // used by showMin/maximized/FullScreen Qt::WindowFlags savedFlags; // Save widget flags while showing fullscreen + // ### TODO replace initialScreenIndex with QScreen *, in case the screens change at runtime int initialScreenIndex; // Screen number when passing a QDesktop[Screen]Widget as parent. QVector widgetTextures; @@ -356,6 +357,7 @@ public: void createWinId(); bool setScreenForPoint(const QPoint &pos); + bool setScreen(QScreen *screen); void createTLExtra(); void createExtra(); diff --git a/src/widgets/widgets/qmenu.cpp b/src/widgets/widgets/qmenu.cpp index 5b1f609b7e..2d107de268 100644 --- a/src/widgets/widgets/qmenu.cpp +++ b/src/widgets/widgets/qmenu.cpp @@ -2359,8 +2359,18 @@ void QMenu::popup(const QPoint &p, QAction *atAction) d->updateLayoutDirection(); // Ensure that we get correct sizeHints by placing this window on the correct screen. - if (d->setScreenForPoint(p)) + // However if the QMenu was constructed with a QDesktopScreenWidget as its parent, + // then initialScreenIndex was set, so we should respect that for the lifetime of this menu. + // Use d->popupScreen to remember, because initialScreenIndex will be reset after the first showing. + const int screenIndex = d->topData()->initialScreenIndex; + if (screenIndex >= 0) + d->popupScreen = screenIndex; + if (auto s = QGuiApplication::screens().value(d->popupScreen)) { + if (d->setScreen(s)) + d->itemsDirty = true; + } else if (d->setScreenForPoint(p)) { d->itemsDirty = true; + } const bool contextMenu = d->isContextMenu(); if (d->lastContextMenu != contextMenu) { diff --git a/src/widgets/widgets/qmenu_p.h b/src/widgets/widgets/qmenu_p.h index d0fa8ff113..b8976385f4 100644 --- a/src/widgets/widgets/qmenu_p.h +++ b/src/widgets/widgets/qmenu_p.h @@ -509,6 +509,8 @@ public: bool tearoffHighlighted : 1; //menu fading/scrolling effects bool doChildEffects : 1; + + int popupScreen = -1; }; QT_END_NAMESPACE diff --git a/src/widgets/widgets/qmenubar.cpp b/src/widgets/widgets/qmenubar.cpp index e7984078de..33c10f3a9a 100644 --- a/src/widgets/widgets/qmenubar.cpp +++ b/src/widgets/widgets/qmenubar.cpp @@ -68,6 +68,7 @@ #include "qmenu_p.h" #include "qmenubar_p.h" +#include #include "qdebug.h" QT_BEGIN_NAMESPACE @@ -322,11 +323,18 @@ void QMenuBarPrivate::popupAction(QAction *action, bool activateFirst) QRect adjustedActionRect = actionRect(action); QPoint pos(q->mapToGlobal(QPoint(adjustedActionRect.left(), adjustedActionRect.bottom() + 1))); QSize popup_size = activeMenu->sizeHint(); - //we put the popup menu on the screen containing the bottom-center of the action rect - QRect screenRect = QDesktopWidgetPrivate::screenGeometry(pos + QPoint(adjustedActionRect.width() / 2, 0)); + QScreen *popupScreen = q->window()->windowHandle()->screen(); + QPoint bottomMiddlePos = pos + QPoint(adjustedActionRect.width() / 2, 0); + const auto &siblings = popupScreen->virtualSiblings(); + for (QScreen *sibling : siblings) { + if (sibling->geometry().contains(bottomMiddlePos)) { + popupScreen = sibling; + break; + } + } + QRect screenRect = popupScreen->geometry(); pos = QPoint(qMax(pos.x(), screenRect.x()), qMax(pos.y(), screenRect.y())); - const bool fitUp = (pos.y() - popup_size.height() >= screenRect.top()); const bool fitDown = (pos.y() + popup_size.height() <= screenRect.bottom()); const bool rtl = q->isRightToLeft(); @@ -352,6 +360,7 @@ void QMenuBarPrivate::popupAction(QAction *action, bool activateFirst) if(!defaultPopDown || (fitUp && !fitDown)) pos.setY(qMax(screenRect.y(), q->mapToGlobal(QPoint(0, adjustedActionRect.top()-popup_size.height())).y())); + QMenuPrivate::get(activeMenu)->topData()->initialScreenIndex = QGuiApplication::screens().indexOf(popupScreen); activeMenu->popup(pos); if(activateFirst) activeMenu->d_func()->setFirstActionActive(); -- cgit v1.2.3 From cfd2cc91fdae40d8e1eddfc2f9ff2585a0b708e4 Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Thu, 20 Jun 2019 16:53:24 +0200 Subject: Always fill the background of a tabbed QMdiSubWindow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QMacStyle polishes QMdiSubWindows to have autoFillBackground set to false. If the QMdiSubWindow's widget also doesn't fill the background (like QLabel), then nobody fills the window in a tabbed QMdiArea, and the other subwindows show through. This change makes sure that all pixels are painted in that situations, using the styled frame. Fixes: QTBUG-76214 Change-Id: Iae025d15d36a8bc0d0c5838ac43c9d54944bcc83 Reviewed-by: Tor Arne Vestbø --- src/widgets/widgets/qmdisubwindow.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/widgets/widgets/qmdisubwindow.cpp b/src/widgets/widgets/qmdisubwindow.cpp index 794674c427..e9511f944f 100644 --- a/src/widgets/widgets/qmdisubwindow.cpp +++ b/src/widgets/widgets/qmdisubwindow.cpp @@ -3140,8 +3140,6 @@ void QMdiSubWindow::paintEvent(QPaintEvent *paintEvent) } Q_D(QMdiSubWindow); - if (isMaximized() && !d->drawTitleBarWhenMaximized()) - return; if (d->resizeTimerId != -1) { // Only update the style option rect and the window title. @@ -3161,6 +3159,17 @@ void QMdiSubWindow::paintEvent(QPaintEvent *paintEvent) } QStylePainter painter(this); + QStyleOptionFrame frameOptions; + frameOptions.initFrom(this); + frameOptions.state.setFlag(QStyle::State_Active, d->isActive); + if (isMaximized() && !d->drawTitleBarWhenMaximized()) { + if (!autoFillBackground() && (!widget() || !qt_widget_private(widget())->isOpaque)) { + // make sure we paint all pixels of a maximized QMdiSubWindow if no-one else does + painter.drawPrimitive(QStyle::PE_FrameWindow, frameOptions); + } + return; + } + if (!d->windowTitle.isEmpty()) painter.setFont(d->font); painter.drawComplexControl(QStyle::CC_TitleBar, d->cachedStyleOptions); @@ -3168,10 +3177,7 @@ void QMdiSubWindow::paintEvent(QPaintEvent *paintEvent) if (isMinimized() && !d->hasBorder(d->cachedStyleOptions)) return; - QStyleOptionFrame frameOptions; - frameOptions.initFrom(this); frameOptions.lineWidth = style()->pixelMetric(QStyle::PM_MdiSubWindowFrameWidth, 0, this); - frameOptions.state.setFlag(QStyle::State_Active, d->isActive); // ### Ensure that we do not require setting the cliprect for 4.4 if (!isMinimized() && !d->hasBorder(d->cachedStyleOptions)) -- cgit v1.2.3 From d848c3dfea3187b8248fc18a013624946cc84444 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Fri, 21 Jun 2019 12:38:44 +0200 Subject: Remove dead code from QApplicationPrivate::setSystemPalette The code was added in 2005, ifdefed out already, and never used. Change-Id: Ic5d070dd031665a4429739278851b50646694bf9 Reviewed-by: Timur Pocheptsov --- src/widgets/kernel/qapplication.cpp | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index c3e10063e1..6dd0aa2e86 100644 --- a/src/widgets/kernel/qapplication.cpp +++ b/src/widgets/kernel/qapplication.cpp @@ -1463,28 +1463,10 @@ void QApplication::setPalette(const QPalette &palette, const char* className) void QApplicationPrivate::setSystemPalette(const QPalette &pal) { - QPalette adjusted; - -#if 0 - // adjust the system palette to avoid dithering - QColormap cmap = QColormap::instance(); - if (cmap.depths() > 4 && cmap.depths() < 24) { - for (int g = 0; g < QPalette::NColorGroups; g++) - for (int i = 0; i < QPalette::NColorRoles; i++) { - QColor color = pal.color((QPalette::ColorGroup)g, (QPalette::ColorRole)i); - color = cmap.colorAt(cmap.pixel(color)); - adjusted.setColor((QPalette::ColorGroup)g, (QPalette::ColorRole) i, color); - } - } -#else - adjusted = pal; -#endif - if (!sys_pal) - sys_pal = new QPalette(adjusted); + sys_pal = new QPalette(pal); else - *sys_pal = adjusted; - + *sys_pal = pal; if (!QApplicationPrivate::set_pal) QApplication::setPalette(*sys_pal); -- cgit v1.2.3 From cea661d395e75eb931f26cc1d8f5d0ece3e4bc9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Fri, 21 Jun 2019 13:05:23 +0200 Subject: Squash un-needed qt_init function into callsite Only uses a single place and avoids following the init logic through multiple layers when trying to debug it. Change-Id: I8fc119385edf407f69fb5431dc6584288022a7fe Reviewed-by: Timur Pocheptsov --- src/widgets/kernel/qapplication.cpp | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index 6dd0aa2e86..032ed000f4 100644 --- a/src/widgets/kernel/qapplication.cpp +++ b/src/widgets/kernel/qapplication.cpp @@ -381,8 +381,6 @@ QWidget *QApplication::topLevelAt(const QPoint &pos) 0 if there is no such widget. */ -void qt_init(QApplicationPrivate *priv, int type - ); void qt_init_tooltip_palette(); void qt_cleanup(); @@ -571,7 +569,10 @@ void QApplicationPrivate::init() process_cmdline(); // Must be called before initialize() - qt_init(this, application_type); + QColormap::initialize(); + qt_init_tooltip_palette(); + QApplicationPrivate::initializeWidgetFontHash(); + initialize(); eventDispatcher->startingUp(); @@ -586,18 +587,6 @@ void QApplicationPrivate::init() } -void qt_init(QApplicationPrivate *priv, int type) -{ - Q_UNUSED(priv); - Q_UNUSED(type); - - QColormap::initialize(); - - qt_init_tooltip_palette(); - - QApplicationPrivate::initializeWidgetFontHash(); -} - void qt_init_tooltip_palette() { #ifndef QT_NO_TOOLTIP -- cgit v1.2.3 From cbb0295f7b793af44e7560ace23734bb3f31b861 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Fri, 21 Jun 2019 13:17:32 +0200 Subject: Remove duplicate call to QApplicationPrivate::initializeWidgetPaletteHash() initSystemPalette() already calls this when there's a platform theme, which is the only case where initializeWidgetPaletteHash will have an effect. Change-Id: I814ea2bb17ef40aee769f2c36f8ef4296cfca020 Reviewed-by: Timur Pocheptsov --- src/widgets/kernel/qapplication.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index 032ed000f4..d25891a83b 100644 --- a/src/widgets/kernel/qapplication.cpp +++ b/src/widgets/kernel/qapplication.cpp @@ -1155,7 +1155,6 @@ void QApplication::setStyle(QStyle *style) } else if (QApplicationPrivate::sys_pal) { clearSystemPalette(); initSystemPalette(); - QApplicationPrivate::initializeWidgetPaletteHash(); QApplicationPrivate::initializeWidgetFontHash(); QApplicationPrivate::setPalette_helper(*QApplicationPrivate::sys_pal, /*className=*/0, /*clearWidgetPaletteHash=*/false); } else if (!QApplicationPrivate::sys_pal) { -- cgit v1.2.3 From ed00e3093be53923ffaacdec05f3e730d62a8064 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Fri, 21 Jun 2019 13:19:45 +0200 Subject: Remove duplicate call to QApplicationPrivate::setPalette_helper initSystemPalette() already takes care of this. Change-Id: I6521763a74ec3ec629d9fcf05aa2a7cd71a7f26d Reviewed-by: Timur Pocheptsov --- src/widgets/kernel/qapplication.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index d25891a83b..b640808b2f 100644 --- a/src/widgets/kernel/qapplication.cpp +++ b/src/widgets/kernel/qapplication.cpp @@ -1156,7 +1156,6 @@ void QApplication::setStyle(QStyle *style) clearSystemPalette(); initSystemPalette(); QApplicationPrivate::initializeWidgetFontHash(); - QApplicationPrivate::setPalette_helper(*QApplicationPrivate::sys_pal, /*className=*/0, /*clearWidgetPaletteHash=*/false); } else if (!QApplicationPrivate::sys_pal) { // Initialize the sys_pal if it hasn't happened yet... QApplicationPrivate::setSystemPalette(QApplicationPrivate::app_style->standardPalette()); -- cgit v1.2.3 From 674f4e6cfbd119093f5023234372d4c92159ade3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Fri, 21 Jun 2019 13:28:03 +0200 Subject: QApplication: clarify and unify global static usage Explain why there are two accessor functions for the global statics, and use the global statics directly inside qapplication.cpp for consistency. Change-Id: Ibf3952052c1d0e780a8aab220a72f05af0c070a5 Reviewed-by: Timur Pocheptsov --- src/widgets/kernel/qapplication.cpp | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index b640808b2f..8e01cb17c5 100644 --- a/src/widgets/kernel/qapplication.cpp +++ b/src/widgets/kernel/qapplication.cpp @@ -426,16 +426,10 @@ bool Q_WIDGETS_EXPORT qt_tab_all_widgets() // ######## move to QApplicationPrivate // Default application palettes and fonts (per widget type) Q_GLOBAL_STATIC(PaletteHash, app_palettes) -PaletteHash *qt_app_palettes_hash() -{ - return app_palettes(); -} - Q_GLOBAL_STATIC(FontHash, app_fonts) -FontHash *qt_app_fonts_hash() -{ - return app_fonts(); -} +// Exported accessors for use outside of this file +PaletteHash *qt_app_palettes_hash() { return app_palettes(); } +FontHash *qt_app_fonts_hash() { return app_fonts(); } QWidgetList *QApplicationPrivate::popupWidgets = 0; // has keyboard input focus @@ -652,7 +646,7 @@ void QApplicationPrivate::initializeWidgetPaletteHash() QPlatformTheme *platformTheme = QGuiApplicationPrivate::platformTheme(); if (!platformTheme) return; - qt_app_palettes_hash()->clear(); + app_palettes()->clear(); setPossiblePalette(platformTheme->palette(QPlatformTheme::ToolButtonPalette), "QToolButton"); setPossiblePalette(platformTheme->palette(QPlatformTheme::ButtonPalette), "QAbstractButton"); @@ -676,7 +670,7 @@ void QApplicationPrivate::initializeWidgetFontHash() const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme(); if (!theme) return; - FontHash *fontHash = qt_app_fonts_hash(); + FontHash *fontHash = app_fonts(); fontHash->clear(); if (const QFont *font = theme->font(QPlatformTheme::MenuFont)) -- cgit v1.2.3 From 0e1623158b0f4f850b3c1367b78d7831608aa713 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sat, 22 Jun 2019 22:21:50 -0700 Subject: Fix GCC 9's warning about deprecated violation of Rule of 5 error: implicitly-declared 'QDistanceField& QDistanceField::operator=(const QDistanceField&)' is deprecated [-Werror=deprecated-copy] note: because 'QDistanceField' has user-provided 'QDistanceField::QDistanceField(const QDistanceField&)' Change-Id: Ie7ae7616eadf4035bec6fffd15aabc58e99632d9 Reviewed-by: Giuseppe D'Angelo --- src/gui/text/qdistancefield.cpp | 5 ----- src/gui/text/qdistancefield_p.h | 1 - 2 files changed, 6 deletions(-) (limited to 'src') diff --git a/src/gui/text/qdistancefield.cpp b/src/gui/text/qdistancefield.cpp index 82bb617733..75e2e4e745 100644 --- a/src/gui/text/qdistancefield.cpp +++ b/src/gui/text/qdistancefield.cpp @@ -899,11 +899,6 @@ QDistanceField::QDistanceField(int width, int height) { } -QDistanceField::QDistanceField(const QDistanceField &other) -{ - d = other.d; -} - 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 31cdf7edd2..c0873cedab 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(const QDistanceField &other); bool isNull() const; -- cgit v1.2.3 From a7cbb8c639487edbabc08ea99498503b33c9f6d6 Mon Sep 17 00:00:00 2001 From: Christian Ehrlicher Date: Mon, 10 Jun 2019 13:44:42 +0200 Subject: QWidget: fix setTabOrder for compound widgets 81e298a51d08c510457b4a26b37c0d4aac5eba65 fixed a case where the focus chain was screwed up when the order was already correct. This worked correctly in most cases but not when the next focus widget of the first one had Qt::NoFocus. The optimization check if lastFocusChildOfFirst is the same as second is thrown away since it now does not longer screw up the focus chain and the save would only be four pointer assignments. Fixes: QTBUG-75388 Task-number: QTBUG-10907 Task-number: QTBUG-68393 Task-number: QTBUG-69619 Change-Id: I581ed532156c34ea970123afd063194aab016304 Reviewed-by: Richard Moe Gustavsen --- src/widgets/kernel/qwidget.cpp | 60 ++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 8b225df8be..6ef3a4f163 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -1,4 +1,4 @@ -/**************************************************************************** +/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Copyright (C) 2016 Intel Corporation. @@ -7017,37 +7017,41 @@ void QWidget::setTabOrder(QWidget* first, QWidget *second) lastFocusChild = focusNext; } }; + auto setPrev = [](QWidget *w, QWidget *prev) + { + w->d_func()->focus_prev = prev; + }; + auto setNext = [](QWidget *w, QWidget *next) + { + w->d_func()->focus_next = next; + }; - QWidget *lastFocusChildOfFirst, *lastFocusChildOfSecond; - determineLastFocusChild(first, lastFocusChildOfFirst); + // remove the second widget from the chain + QWidget *lastFocusChildOfSecond; determineLastFocusChild(second, lastFocusChildOfSecond); - - // If the tab order is already correct, exit early - if (lastFocusChildOfFirst == second || - lastFocusChildOfFirst->d_func()->focus_next == second) { - return; + { + QWidget *oldPrev = second->d_func()->focus_prev; + QWidget *prevWithFocus = oldPrev; + while (prevWithFocus->focusPolicy() == Qt::NoFocus) + prevWithFocus = prevWithFocus->d_func()->focus_prev; + // only widgets between first and second -> all is fine + if (prevWithFocus == first) + return; + QWidget *oldNext = lastFocusChildOfSecond->d_func()->focus_next; + setPrev(oldNext, oldPrev); + setNext(oldPrev, oldNext); } - // Note that we need to handle two different sections in the tab chain; The section - // that 'first' belongs to (firstSection), where we are about to insert 'second', and - // the section that 'second' used be a part of (secondSection). When we pull 'second' - // out of the second section and insert it into the first, we also need to ensure - // that we leave the second section in a connected state. - QWidget *firstChainOldSecond = lastFocusChildOfFirst->d_func()->focus_next; - QWidget *secondChainNewFirst = second->d_func()->focus_prev; - QWidget *secondChainNewSecond = lastFocusChildOfSecond->d_func()->focus_next; - - // Insert 'second' after 'first' - lastFocusChildOfFirst->d_func()->focus_next = second; - second->d_func()->focus_prev = lastFocusChildOfFirst; - - // The widget that used to be 'second' in the first section, should now become 'third' - lastFocusChildOfSecond->d_func()->focus_next = firstChainOldSecond; - firstChainOldSecond->d_func()->focus_prev = lastFocusChildOfSecond; - - // Repair the second section after we pulled 'second' out of it - secondChainNewFirst->d_func()->focus_next = secondChainNewSecond; - secondChainNewSecond->d_func()->focus_prev = secondChainNewFirst; + // insert the second widget into the chain + QWidget *lastFocusChildOfFirst; + determineLastFocusChild(first, lastFocusChildOfFirst); + { + QWidget *oldNext = lastFocusChildOfFirst->d_func()->focus_next; + setPrev(second, lastFocusChildOfFirst); + setNext(lastFocusChildOfFirst, second); + setPrev(oldNext, lastFocusChildOfSecond); + setNext(lastFocusChildOfSecond, oldNext); + } } /*!\internal -- cgit v1.2.3 From 43c410867b5eccea2e0f7eeceabad76d5f874101 Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Wed, 12 Jun 2019 01:29:19 +0200 Subject: QVector: add a construction from QArrayDataPointerRef To be used to build QVectors out of Q_ARRAY_LITERALs. Change-Id: I6105fd1f2d13f6ce923b79276b4aa7a7f5eff193 Reviewed-by: Thiago Macieira Reviewed-by: Marc Mutz --- src/corelib/tools/qvector.h | 1 + src/corelib/tools/qvector.qdoc | 5 +++++ 2 files changed, 6 insertions(+) (limited to 'src') diff --git a/src/corelib/tools/qvector.h b/src/corelib/tools/qvector.h index 5d68a283bd..65a5174abf 100644 --- a/src/corelib/tools/qvector.h +++ b/src/corelib/tools/qvector.h @@ -80,6 +80,7 @@ public: QVector &operator=(std::initializer_list args); template = true> inline QVector(InputIterator first, InputIterator last); + explicit QVector(QArrayDataPointerRef ref) noexcept : d(ref.ptr) {} bool operator==(const QVector &v) const; inline bool operator!=(const QVector &v) const { return !(*this == v); } diff --git a/src/corelib/tools/qvector.qdoc b/src/corelib/tools/qvector.qdoc index c1b5054f93..4c442511ea 100644 --- a/src/corelib/tools/qvector.qdoc +++ b/src/corelib/tools/qvector.qdoc @@ -251,6 +251,11 @@ The value type of \c InputIterator must be convertible to \c T. */ +/*! + \fn template QVector::QVector(QArrayDataPointerRef ref) + \internal +*/ + /*! \fn template QVector::~QVector() Destroys the vector. -- cgit v1.2.3 From 7eb070b4504e9a3cfac8aee582e84105cd8ebe04 Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Sun, 23 Jun 2019 15:22:28 +0200 Subject: QVarLengthArray: add qHash overload [ChangeLog][QtCore][QVarLengthArray] Added a qHash overload. Change-Id: I771203ae3bb575b49f70e9114287dd2690031b42 Reviewed-by: Marc Mutz --- src/corelib/tools/qvarlengtharray.h | 8 ++++++++ src/corelib/tools/qvarlengtharray.qdoc | 8 ++++++++ 2 files changed, 16 insertions(+) (limited to 'src') diff --git a/src/corelib/tools/qvarlengtharray.h b/src/corelib/tools/qvarlengtharray.h index 01fc63b677..a49e0af687 100644 --- a/src/corelib/tools/qvarlengtharray.h +++ b/src/corelib/tools/qvarlengtharray.h @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -599,6 +600,13 @@ inline bool operator>=(const QVarLengthArray &lhs, const QVarLengt return !(lhs < rhs); } +template +uint qHash(const QVarLengthArray &key, uint seed = 0) + noexcept(noexcept(qHashRange(key.cbegin(), key.cend(), seed))) +{ + return qHashRange(key.cbegin(), key.cend(), seed); +} + QT_END_NAMESPACE #endif // QVARLENGTHARRAY_H diff --git a/src/corelib/tools/qvarlengtharray.qdoc b/src/corelib/tools/qvarlengtharray.qdoc index 80769e3769..e5ba47b8ef 100644 --- a/src/corelib/tools/qvarlengtharray.qdoc +++ b/src/corelib/tools/qvarlengtharray.qdoc @@ -903,3 +903,11 @@ \sa indexOf(), lastIndexOf() */ +/*! + template uint qHash(const QVarLengthArray &key, uint seed = 0) + \relates QVarLengthArray + \since 5.14 + + Returns the hash value for \a key, using \a seed to seed the + calculation. +*/ -- cgit v1.2.3 From 15a1d36b1a209e7f169955d7f8f1b64115d20e6e Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Sun, 23 Jun 2019 15:20:33 +0200 Subject: QRegularExpression docs: streamline the anchoredPattern section Users may get confounded by the "essay" about exact matching. We now have a function to build a pattern to do exact matching, just state that that's the solution, end of the story. Change-Id: I0a72aa2255af50a1991540b834f146b6e6bc912e Reviewed-by: Thiago Macieira Reviewed-by: Samuel Gaist --- .../code/src_corelib_tools_qregularexpression.cpp | 12 +++------ src/corelib/tools/qregularexpression.cpp | 29 +++------------------- 2 files changed, 8 insertions(+), 33 deletions(-) (limited to 'src') diff --git a/src/corelib/doc/snippets/code/src_corelib_tools_qregularexpression.cpp b/src/corelib/doc/snippets/code/src_corelib_tools_qregularexpression.cpp index afdd9c3d25..99cd4ea7a2 100644 --- a/src/corelib/doc/snippets/code/src_corelib_tools_qregularexpression.cpp +++ b/src/corelib/doc/snippets/code/src_corelib_tools_qregularexpression.cpp @@ -286,15 +286,11 @@ if (!invalidRe.isValid()) { { //! [24] -QRegularExpression re("^this pattern must match exactly$"); -//! [24] -} - -{ -//! [25] QString p("a .*|pattern"); -QRegularExpression re("\\A(?:" + p + ")\\z"); // re matches exactly the pattern string p -//! [25] + +// re matches exactly the pattern string p +QRegularExpression re(QRegularExpression::anchoredPattern(p)); +//! [24] } { diff --git a/src/corelib/tools/qregularexpression.cpp b/src/corelib/tools/qregularexpression.cpp index 9c201e770b..17acd476b2 100644 --- a/src/corelib/tools/qregularexpression.cpp +++ b/src/corelib/tools/qregularexpression.cpp @@ -460,34 +460,13 @@ QT_BEGIN_NAMESPACE \row \li \c{"[a-z]+\\d+"} \li \b true \li \b true \endtable - Exact matching is not reflected in QRegularExpression. If you want to be - sure that the subject string matches the regular expression exactly, you can wrap the - pattern between a couple of anchoring expressions. Simply - putting the pattern between the \c{^} and the \c{$} anchors is enough - in most cases: + Exact matching is not reflected in QRegularExpression. If you want + to be sure that the subject string matches the regular expression + exactly, you can wrap the pattern using the anchoredPattern() + function: \snippet code/src_corelib_tools_qregularexpression.cpp 24 - However, remember that the \c{$} anchor not only matches at the end of the - string, but also at a newline character right before the end of the string; - that is, the previous pattern matches against the string "this pattern must - match exactly\\n". Also, the behaviour of both the \c{^} and the \c{$} - anchors changes if the MultiLineOption is set either explicitly (as a - pattern option) or implicitly (as a directive inside the pattern string). - - Therefore, in the most general case, you should wrap the pattern between - the \c{\A} and the \c{\z} anchors: - - \snippet code/src_corelib_tools_qregularexpression.cpp 25 - - Note the usage of the non-capturing group in order to preserve the meaning - of the branch operator inside the pattern. - - The QRegularExpression::anchoredPattern() helper method does exactly that for - you. - - \sa anchoredPattern - \section3 Porting from QRegExp's Partial Matching When using QRegExp::exactMatch(), if an exact match was not found, one -- cgit v1.2.3 From cd401b74a13cd9d9a47d977f195c7985cf725d55 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Fri, 21 Jun 2019 22:53:14 +0200 Subject: Optimize and fix handling of QtMessageHandlers A function may almost always have static storage duration, but that does not necessarily mean that we can store and load pointers to them without memory ordering. Play it safe and use store-release and load-acquire for them (which combines to ordered for the fetchAndSet call in qInstall*Handler(), as we don't know what the caller will do with the returned function pointer). Also change the initial value of the atomic pointer to nullptr. Nullptr already signified the default handler in qInstall*Handler(), so the API doesn't change. But by using nullptr to mean default, we place these variables in the BSS segment instead of TEXT, save dynamic init, or at least a relocation, and we dodge the smelly comparison of function pointers, using comparison against nullptr instead. Also, as a drive-by, put the call to ungrapMessageHandler() in a scope-guard. Both the message handler, as well as the Qt code calling it (toLocal8Bit()!), may throw, and that would stop all further logging. The code still has one problem: When a logging action is underway, and another thread exchanges the message handler, we might still execute code in the old handler. This is probably not a problem in practice, since no-one will use a dynamically-compiled function for logging (right? :), but should probably be documented or fixed. This patch does not address this issue, though. Change-Id: I21aa907288b9c8c6646787b4001002d145b114a5 Reviewed-by: Thiago Macieira --- src/corelib/global/qlogging.cpp | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/corelib/global/qlogging.cpp b/src/corelib/global/qlogging.cpp index 60ed619aae..621b6d7d13 100644 --- a/src/corelib/global/qlogging.cpp +++ b/src/corelib/global/qlogging.cpp @@ -44,6 +44,7 @@ #include "qlogging_p.h" #include "qlist.h" #include "qbytearray.h" +#include "qscopeguard.h" #include "qstring.h" #include "qvarlengtharray.h" #include "qdebug.h" @@ -1499,9 +1500,9 @@ static void qDefaultMsgHandler(QtMsgType type, const char *buf); static void qDefaultMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &buf); // pointer to QtMsgHandler debug handler (without context) -static QBasicAtomicPointer msgHandler = Q_BASIC_ATOMIC_INITIALIZER(qDefaultMsgHandler); +static QBasicAtomicPointer msgHandler = Q_BASIC_ATOMIC_INITIALIZER(nullptr); // pointer to QtMessageHandler debug handler (with context) -static QBasicAtomicPointer messageHandler = Q_BASIC_ATOMIC_INITIALIZER(qDefaultMessageHandler); +static QBasicAtomicPointer messageHandler = Q_BASIC_ATOMIC_INITIALIZER(nullptr); // ------------------------ Alternate logging sinks ------------------------- @@ -1813,14 +1814,15 @@ static void qt_message_print(QtMsgType msgType, const QMessageLogContext &contex // prevent recursion in case the message handler generates messages // itself, e.g. by using Qt API if (grabMessageHandler()) { + const auto ungrab = qScopeGuard([]{ ungrabMessageHandler(); }); + auto oldStyle = msgHandler.loadAcquire(); + auto newStye = messageHandler.loadAcquire(); // prefer new message handler over the old one - if (msgHandler.loadRelaxed() == qDefaultMsgHandler - || messageHandler.loadRelaxed() != qDefaultMessageHandler) { - (*messageHandler.loadRelaxed())(msgType, context, message); + if (newStye || !oldStyle) { + (newStye ? newStye : qDefaultMessageHandler)(msgType, context, message); } else { - (*msgHandler.loadRelaxed())(msgType, message.toLocal8Bit().constData()); + (oldStyle ? oldStyle : qDefaultMsgHandler)(msgType, message.toLocal8Bit().constData()); } - ungrabMessageHandler(); } else { fprintf(stderr, "%s\n", message.toLocal8Bit().constData()); } @@ -2071,18 +2073,20 @@ void qErrnoWarning(int code, const char *msg, ...) QtMessageHandler qInstallMessageHandler(QtMessageHandler h) { - if (!h) - h = qDefaultMessageHandler; - //set 'h' and return old message handler - return messageHandler.fetchAndStoreRelaxed(h); + const auto old = messageHandler.fetchAndStoreOrdered(h); + if (old) + return old; + else + return qDefaultMessageHandler; } QtMsgHandler qInstallMsgHandler(QtMsgHandler h) { - if (!h) - h = qDefaultMsgHandler; - //set 'h' and return old message handler - return msgHandler.fetchAndStoreRelaxed(h); + const auto old = msgHandler.fetchAndStoreOrdered(h); + if (old) + return old; + else + return qDefaultMsgHandler; } void qSetMessagePattern(const QString &pattern) -- cgit v1.2.3 From 79bdc7cf1daec75df59de9236599a9f24077511a Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Mon, 10 Jun 2019 11:40:16 +0200 Subject: Deprecate QAtomic::load() / store() Tell people to move to loadRelaxed() / storeRelaxed(), now that we have them. [ChangeLog][QtCore][QAtomicInteger] The load() / store() functions have been deprecated in favor of loadRelaxed() / storeRelaxed(). [ChangeLog][QtCore][QAtomicPointer] The load() / store() functions have been deprecated in favor of loadRelaxed() / storeRelaxed(). Change-Id: If7a31db2f90fce4a7605a2377067e86990646f48 Reviewed-by: Thiago Macieira --- src/corelib/thread/qbasicatomic.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/corelib/thread/qbasicatomic.h b/src/corelib/thread/qbasicatomic.h index dc976819ef..9804e60119 100644 --- a/src/corelib/thread/qbasicatomic.h +++ b/src/corelib/thread/qbasicatomic.h @@ -99,8 +99,10 @@ public: typename Ops::Type _q_value; // Everything below is either implemented in ../arch/qatomic_XXX.h or (as fallback) in qgenericatomic.h - T load() const noexcept { return loadRelaxed(); } - void store(T newValue) noexcept { storeRelaxed(newValue); } +#if QT_DEPRECATED_SINCE(5, 14) + QT_DEPRECATED_VERSION_X_5_14("Use loadRelaxed") T load() const noexcept { return loadRelaxed(); } + QT_DEPRECATED_VERSION_X_5_14("Use storeRelaxed") void store(T newValue) noexcept { storeRelaxed(newValue); } +#endif T loadRelaxed() const noexcept { return Ops::loadRelaxed(_q_value); } void storeRelaxed(T newValue) noexcept { Ops::storeRelaxed(_q_value, newValue); } @@ -238,8 +240,10 @@ public: AtomicType _q_value; - Type load() const noexcept { return loadRelaxed(); } - void store(Type newValue) noexcept { storeRelaxed(newValue); } +#if QT_DEPRECATED_SINCE(5, 14) + QT_DEPRECATED_VERSION_X_5_14("Use loadRelaxed") Type load() const noexcept { return loadRelaxed(); } + QT_DEPRECATED_VERSION_X_5_14("Use storeRelaxed") void store(Type newValue) noexcept { storeRelaxed(newValue); } +#endif Type loadRelaxed() const noexcept { return Ops::loadRelaxed(_q_value); } void storeRelaxed(Type newValue) noexcept { Ops::storeRelaxed(_q_value, newValue); } -- cgit v1.2.3 From b568e931490a7751120dfed1f0bc53f2a4b4192a Mon Sep 17 00:00:00 2001 From: Christian Ehrlicher Date: Tue, 11 Jun 2019 21:12:47 +0200 Subject: QSS/MenuItem: only draw checkbox if no item is available QTBUG-66380 introduced a behavior change drawing a checkable menu item with an icon. After this path the checkmark and icon was drawn. Revert this regression so the stylesheet style matches the behavior of all other styles. A checkable item should have two different icons (QIcon::On and QIcon::Off). Fixes: QTBUG-74655 Task-number: QTBUG-66380 Change-Id: I32ac2f397087a1c3d5d07400372109703c00c1ba Reviewed-by: Richard Moe Gustavsen --- src/widgets/styles/qstylesheetstyle.cpp | 60 +++++++++++++-------------------- 1 file changed, 23 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/widgets/styles/qstylesheetstyle.cpp b/src/widgets/styles/qstylesheetstyle.cpp index 4518d8c736..28b4c363a6 100644 --- a/src/widgets/styles/qstylesheetstyle.cpp +++ b/src/widgets/styles/qstylesheetstyle.cpp @@ -3703,17 +3703,6 @@ void QStyleSheetStyle::drawControl(ControlElement ce, const QStyleOption *opt, Q bool dis = !(opt->state & QStyle::State_Enabled), act = opt->state & QStyle::State_Selected; - int checkableOffset = 0; - if (checkable) { - QRenderRule subSubRule = renderRule(w, opt, PseudoElement_MenuCheckMark); - QStyleOptionMenuItem newMi = mi; - newMi.rect = positionRect(w, subRule, subSubRule, PseudoElement_MenuCheckMark, opt->rect, opt->direction); - // align with icons if there are some - checkableOffset = std::max(m->maxIconWidth, newMi.rect.width()); - if (subSubRule.hasDrawable() || checked) - drawPrimitive(PE_IndicatorMenuCheckMark, &newMi, p, w); - } - if (!mi.icon.isNull()) { QIcon::Mode mode = dis ? QIcon::Disabled : QIcon::Normal; if (act && !dis) @@ -3730,24 +3719,28 @@ void QStyleSheetStyle::drawControl(ControlElement ce, const QStyleOption *opt, Q } QRect iconRect = positionRect(w, subRule, iconRule, PseudoElement_MenuIcon, opt->rect, opt->direction); if (opt->direction == Qt::LeftToRight) - iconRect.moveLeft(iconRect.left() + checkableOffset); + iconRect.moveLeft(iconRect.left()); else - iconRect.moveRight(iconRect.right() - checkableOffset); + iconRect.moveRight(iconRect.right()); iconRule.drawRule(p, iconRect); QRect pmr(0, 0, pixw, pixh); pmr.moveCenter(iconRect.center()); p->drawPixmap(pmr.topLeft(), pixmap); + } else if (checkable) { + QRenderRule subSubRule = renderRule(w, opt, PseudoElement_MenuCheckMark); + if (subSubRule.hasDrawable() || checked) { + QStyleOptionMenuItem newMi = mi; + if (!dis) + newMi.state |= State_Enabled; + if (act) + newMi.state |= State_On; + newMi.rect = positionRect(w, subRule, subSubRule, PseudoElement_MenuCheckMark, opt->rect, opt->direction); + drawPrimitive(PE_IndicatorMenuCheckMark, &newMi, p, w); + } } - int textOffset = 0; - // padding overrules it all - if (!subRule.hasBox() || subRule.box()->paddings[LeftEdge] == 0) { - textOffset = checkableOffset; - if (!m->icon.isNull() || !checkable) - textOffset += m->maxIconWidth; - } QRect textRect = subRule.contentsRect(opt->rect); - textRect.setLeft(textRect.left() + textOffset); + textRect.setLeft(textRect.left() + m->maxIconWidth); textRect.setWidth(textRect.width() - mi.tabWidth); const QRect vTextRect = visualRect(opt->direction, m->rect, textRect); @@ -5091,27 +5084,20 @@ QSize QStyleSheetStyle::sizeFromContents(ContentsType ct, const QStyleOption *op if ((pe == PseudoElement_MenuSeparator) && subRule.hasContentsSize()) { return QSize(sz.width(), subRule.size().height()); } else if ((pe == PseudoElement_Item) && (subRule.hasBox() || subRule.hasBorder())) { - int width = csz.width(); + QSize sz(csz); if (mi->text.contains(QLatin1Char('\t'))) - width += 12; //as in QCommonStyle + sz.rwidth() += 12; //as in QCommonStyle bool checkable = mi->checkType != QStyleOptionMenuItem::NotCheckable; - int checkableWidth = 0; - if (checkable) { + if (!mi->icon.isNull()) { + const int pmSmall = pixelMetric(PM_SmallIconSize); + const QSize pmSize = mi->icon.actualSize(QSize(pmSmall, pmSmall)); + sz.rwidth() += pmSize.width() + 4; + } else if (checkable) { QRenderRule subSubRule = renderRule(w, opt, PseudoElement_MenuCheckMark); QRect checkmarkRect = positionRect(w, subRule, subSubRule, PseudoElement_MenuCheckMark, opt->rect, opt->direction); - checkableWidth = std::max(mi->maxIconWidth, checkmarkRect.width()); - } - if (!mi->icon.isNull()) { - QPixmap pixmap = mi->icon.pixmap(pixelMetric(PM_SmallIconSize)); - width += pixmap.width(); - } - // padding overrules it all - if (!subRule.hasBox() || subRule.box()->paddings[LeftEdge] == 0) { - width += checkableWidth; - if (!mi->icon.isNull() || !checkable) - width += mi->maxIconWidth; + sz.rwidth() += std::max(mi->maxIconWidth, checkmarkRect.width()) + 4; } - return subRule.boxSize(subRule.adjustSize(QSize(width, csz.height()))); + return subRule.boxSize(subRule.adjustSize(sz)); } } break; -- cgit v1.2.3 From cf052e0737ecf2850b125c1640fe300699303b70 Mon Sep 17 00:00:00 2001 From: Tim Blechmann Date: Thu, 6 Dec 2018 15:39:57 +0800 Subject: Windows: Use UUIDs instead of function pointer to mangle window classes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of using the address of a function pointer, we name-mangle window classes by using an UUID. This fixes a real-world problem with multiple Qt instances where for some reasons the window function appears to be mapped to the same address. Change-Id: Id27e8d7aa17a4db9c14559224395f49d3ecd8d78 Reviewed-by: Tor Arne Vestbø --- src/plugins/platforms/windows/qwindowscontext.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index de533cab08..38b9823d6b 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -77,6 +77,7 @@ #include #include #include +#include #include #include @@ -544,7 +545,7 @@ QString QWindowsContext::registerWindowClass(QString cname, // each one has to have window class names with a unique name // The first instance gets the unmodified name; if the class // has already been registered by another instance of Qt then - // add an instance-specific ID, the address of the window proc. + // add a UUID. static int classExists = -1; const HINSTANCE appInstance = static_cast(GetModuleHandle(nullptr)); @@ -555,7 +556,7 @@ QString QWindowsContext::registerWindowClass(QString cname, } if (classExists) - cname += QString::number(reinterpret_cast(proc)); + cname += QUuid::createUuid().toString(); if (d->m_registeredWindowClassNames.contains(cname)) // already registered in our list return cname; -- cgit v1.2.3 From f344b6357e751f54bcb003bc88c7fe15d616be7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Tue, 18 Jun 2019 14:58:25 +0200 Subject: macOS: Invalidate window shadow after QNSWindowBackingStore resize The window shadow rendered by AppKit is based on the shape/content of the NSWindow surface. If the backingstore is partially transparent, we need to invalidate the window shadow after each resize (and subsequent flush) of the backingstore. Change-Id: I451370af5a8c0c25faea26beb3faa2483a33a5cf Fixes: QTBUG-74560 Reviewed-by: Timur Pocheptsov --- src/plugins/platforms/cocoa/qcocoabackingstore.h | 1 + src/plugins/platforms/cocoa/qcocoabackingstore.mm | 19 +++++++++++++++++++ src/plugins/platforms/cocoa/qcocoawindow.mm | 20 ++++++++++---------- 3 files changed, 30 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.h b/src/plugins/platforms/cocoa/qcocoabackingstore.h index acddc3ecc8..2398e6351e 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.h +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.h @@ -55,6 +55,7 @@ public: QNSWindowBackingStore(QWindow *window); ~QNSWindowBackingStore(); + void resize(const QSize &size, const QRegion &staticContents) override; void flush(QWindow *, const QRegion &, const QPoint &) override; private: diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm index cff1f96615..233ccfab9e 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm @@ -71,6 +71,24 @@ QImage::Format QNSWindowBackingStore::format() const return QRasterBackingStore::format(); } +void QNSWindowBackingStore::resize(const QSize &size, const QRegion &staticContents) +{ + qCDebug(lcQpaBackingStore) << "Resize requested to" << size; + QRasterBackingStore::resize(size, staticContents); + + // The window shadow rendered by AppKit is based on the shape/content of the + // NSWindow surface. Technically any flush of the backingstore can result in + // a potentially new shape of the window, and would need a shadow invalidation, + // but this is likely too expensive to do at every flush for the few cases where + // clients change the shape dynamically. One case where we do know that the shadow + // likely needs invalidation, if the window has partially transparent content, + // is after a resize, where AppKit's default shadow may be based on the previous + // window content. + QCocoaWindow *cocoaWindow = static_cast(window()->handle()); + if (cocoaWindow->isContentView() && !cocoaWindow->isOpaque()) + cocoaWindow->m_needsInvalidateShadow = true; +} + /*! Flushes the given \a region from the specified \a window onto the screen. @@ -217,6 +235,7 @@ void QNSWindowBackingStore::flush(QWindow *window, const QRegion ®ion, const QCocoaWindow *topLevelCocoaWindow = static_cast(topLevelWindow->handle()); if (Q_UNLIKELY(topLevelCocoaWindow->m_needsInvalidateShadow)) { + qCDebug(lcQpaBackingStore) << "Invalidating window shadow for" << topLevelCocoaWindow; [topLevelView.window invalidateShadow]; topLevelCocoaWindow->m_needsInvalidateShadow = false; } diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index d0ad1791c3..dc7319484e 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -1010,16 +1010,16 @@ void QCocoaWindow::setMask(const QRegion ®ion) } else { m_view.layer.mask = nil; } - } - - if (isContentView()) { - // Setting the mask requires invalidating the NSWindow shadow, but that needs - // to happen after the backingstore has been redrawn, so that AppKit can pick - // up the new window shape based on the backingstore content. Doing a display - // directly here is not an option, as the window might not be exposed at this - // time, and so would not result in an updated backingstore. - m_needsInvalidateShadow = true; - [m_view setNeedsDisplay:YES]; + } else { + if (isContentView()) { + // Setting the mask requires invalidating the NSWindow shadow, but that needs + // to happen after the backingstore has been redrawn, so that AppKit can pick + // up the new window shape based on the backingstore content. Doing a display + // directly here is not an option, as the window might not be exposed at this + // time, and so would not result in an updated backingstore. + m_needsInvalidateShadow = true; + [m_view setNeedsDisplay:YES]; + } } } -- cgit v1.2.3 From b68a9df076a5f821b1a1b49207fad9ec9ac8a251 Mon Sep 17 00:00:00 2001 From: Michal Klocek Date: Mon, 3 Jun 2019 13:40:01 +0200 Subject: Fix bogus setAttribute setter warning Since e9e16c74643 running webengine application you can get warning "Attribute Qt::AA_ShareOpenGLContexts must be set before QCoreApplication is created." WebEngine set shared open gl context on qt_call_pre_routines, so when QCoreApplicationPrivate init() runs. Fixes: QTBUG-76391 Change-Id: I5fc146ed70054b0c1597fe06615cea2d7a8969d8 Reviewed-by: Kai Koehne --- src/corelib/kernel/qcoreapplication.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index 8652c45634..db6546028a 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -979,7 +979,11 @@ void QCoreApplication::setAttribute(Qt::ApplicationAttribute attribute, bool on) QCoreApplicationPrivate::attribs |= 1 << attribute; else QCoreApplicationPrivate::attribs &= ~(1 << attribute); +#if defined(QT_NO_QOBJECT) if (Q_UNLIKELY(qApp)) { +#else + if (Q_UNLIKELY(QCoreApplicationPrivate::is_app_running)) { +#endif switch (attribute) { case Qt::AA_EnableHighDpiScaling: case Qt::AA_DisableHighDpiScaling: -- cgit v1.2.3 From 084f84d112dc3dc61de958027ba05508220756d4 Mon Sep 17 00:00:00 2001 From: Richard Moe Gustavsen Date: Wed, 19 Jun 2019 10:25:22 +0200 Subject: QVector3D: fix documentation spelling error Change-Id: Id3b97ea3ce45452e0b59986bc4fd84fd9b0d3708 Reviewed-by: Marc Mutz --- src/gui/math3d/qvector3d.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/gui/math3d/qvector3d.cpp b/src/gui/math3d/qvector3d.cpp index 12a7902272..4f72c1da66 100644 --- a/src/gui/math3d/qvector3d.cpp +++ b/src/gui/math3d/qvector3d.cpp @@ -514,7 +514,7 @@ float QVector3D::distanceToPlane /*! \overload - Returns the distance from this vertex a plane defined by + Returns the distance from this vertex to a plane defined by the vertices \a plane1, \a plane2 and \a plane3. The return value will be negative if the vertex is below the plane, -- cgit v1.2.3 From 1975a98345d96b6155b651bba65eb8ae8ca30d92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Wed, 19 Jun 2019 12:54:46 +0200 Subject: macOS: prevent duplicate backing store scaling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit m_requestedSize is already scaled by the QtGui scale factor (e.g. as set by QT_SCALE_FACTOR). Multiplying by QWindow::devicePixelRatio() then applies this factor again. Use QPlatformWindow::devicePixelRatio() instead, which returns the platform scale factor. Change-Id: I133e99d84f4718215fda9ef0cf81a113b51db2c7 Reviewed-by: Tor Arne Vestbø --- src/plugins/platforms/cocoa/qcocoabackingstore.mm | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm index 233ccfab9e..78aa98094c 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm @@ -413,10 +413,11 @@ void QCALayerBackingStore::ensureBackBuffer() bool QCALayerBackingStore::recreateBackBufferIfNeeded() { - const qreal devicePixelRatio = window()->devicePixelRatio(); + const QCocoaWindow *platformWindow = static_cast(window()->handle()); + const qreal devicePixelRatio = platformWindow->devicePixelRatio(); QSize requestedBufferSize = m_requestedSize * devicePixelRatio; - const NSView *backingStoreView = static_cast(window()->handle())->view(); + const NSView *backingStoreView = platformWindow->view(); Q_UNUSED(backingStoreView); auto bufferSizeMismatch = [&](const QSize requested, const QSize actual) { -- cgit v1.2.3 From a3e69954f57a513b8bfa0a0865753fa6449fe15b Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Wed, 12 Jun 2019 10:21:49 +0200 Subject: Add accessibility notification role On Linux this role is needed to make desktop notifications work. There is no equivalent for Windows, iOS or macOS. On these platforms the role will have no effect. Fixes: QTBUG-76333 Change-Id: I4ef3b3321f7a0e2c09c1ce432a668428d14c52b7 Reviewed-by: Mitch Curtis --- src/gui/accessible/qaccessible.cpp | 1 + src/gui/accessible/qaccessible.h | 1 + src/platformsupport/linuxaccessibility/bridge.cpp | 2 ++ 3 files changed, 4 insertions(+) (limited to 'src') diff --git a/src/gui/accessible/qaccessible.cpp b/src/gui/accessible/qaccessible.cpp index 46dec7f28d..baee4fc252 100644 --- a/src/gui/accessible/qaccessible.cpp +++ b/src/gui/accessible/qaccessible.cpp @@ -350,6 +350,7 @@ Q_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core"); \value MenuItem An item in a menu or menu bar. \value NoRole The object has no role. This usually indicates an invalid object. \value Note A section whose content is parenthetic or ancillary to the main content of the resource. + \value Notification An object that represents a notification (e.g. in the system tray). This role only has an effect on Linux. \value PageTab A page tab that the user can select to switch to a different page in a dialog. \value PageTabList A list of page tabs. \value Paragraph A paragraph of text (usually found in documents). diff --git a/src/gui/accessible/qaccessible.h b/src/gui/accessible/qaccessible.h index 1309f17efd..857b9c5ef5 100644 --- a/src/gui/accessible/qaccessible.h +++ b/src/gui/accessible/qaccessible.h @@ -299,6 +299,7 @@ public: Paragraph = 0x00000083, WebDocument = 0x00000084, Section = 0x00000085, + Notification = 0x00000086, // IAccessible2 roles // IA2_ROLE_CANVAS = 0x401, ### Qt 6 use this one instead of Canvas above diff --git a/src/platformsupport/linuxaccessibility/bridge.cpp b/src/platformsupport/linuxaccessibility/bridge.cpp index a96fe258df..fdc8cd3198 100644 --- a/src/platformsupport/linuxaccessibility/bridge.cpp +++ b/src/platformsupport/linuxaccessibility/bridge.cpp @@ -265,6 +265,8 @@ static RoleMapping map[] = { //: Role of an accessible object { QAccessible::Desktop, ATSPI_ROLE_DESKTOP_FRAME, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "desktop") }, //: Role of an accessible object + { QAccessible::Notification, ATSPI_ROLE_NOTIFICATION, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "notification") }, + //: Role of an accessible object { QAccessible::UserRole, ATSPI_ROLE_UNKNOWN, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "unknown") } }; -- cgit v1.2.3 From 6d61b10f65cd276e009a02cec563cc469245e1f2 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Wed, 19 Jun 2019 08:28:14 +0200 Subject: High DPI: Fix crash in QWindow::mapFromGlobal() With Web Engine, QQuickWidget or similar, the code can hit on the offscreen window, when its handle is null. Add a check. Amends 3af7b279177f7fb092f0e0fb9ffc8e8d846ed774. Fixes: QTBUG-76440 Change-Id: I123633d18386efd3dbfb22aad6072e4f0877a62e Reviewed-by: Allan Sandfeld Jensen --- src/gui/kernel/qhighdpiscaling.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/gui/kernel/qhighdpiscaling.cpp b/src/gui/kernel/qhighdpiscaling.cpp index 4f8e9a3817..93fcb1a216 100644 --- a/src/gui/kernel/qhighdpiscaling.cpp +++ b/src/gui/kernel/qhighdpiscaling.cpp @@ -400,7 +400,7 @@ QPoint QHighDpiScaling::mapPositionToGlobal(const QPoint &pos, const QPoint &win QPoint QHighDpiScaling::mapPositionFromGlobal(const QPoint &pos, const QPoint &windowGlobalPosition, const QWindow *window) { QPoint windowPosCandidate = pos - windowGlobalPosition; - if (QGuiApplicationPrivate::screen_list.size() <= 1) + if (QGuiApplicationPrivate::screen_list.size() <= 1 || window->handle() == nullptr) return windowPosCandidate; // Device independent global (screen) space may discontiguous when high-dpi scaling -- cgit v1.2.3 From d5adaacbb2ae6c1470ebf4b975968534a82225b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Wed, 19 Jun 2019 11:43:14 +0200 Subject: macOS: Make QMacStyle::standardPalette() reflect the platform theme This palette isn't usually used, and the platform theme's palette is preferred, but if a client asks for the standard palette (e.g. as used in the styles example) we should try to report a palette that gives the system look. Change-Id: Ie5e58c890c13c716a9e9b5093b954a737e550dee Reviewed-by: Timur Pocheptsov --- src/plugins/styles/mac/qmacstyle_mac.mm | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/plugins/styles/mac/qmacstyle_mac.mm b/src/plugins/styles/mac/qmacstyle_mac.mm index c1f88149bb..482adf0433 100644 --- a/src/plugins/styles/mac/qmacstyle_mac.mm +++ b/src/plugins/styles/mac/qmacstyle_mac.mm @@ -2555,11 +2555,12 @@ int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QW QPalette QMacStyle::standardPalette() const { - QPalette pal = QCommonStyle::standardPalette(); - pal.setColor(QPalette::Disabled, QPalette::Dark, QColor(191, 191, 191)); - pal.setColor(QPalette::Active, QPalette::Dark, QColor(191, 191, 191)); - pal.setColor(QPalette::Inactive, QPalette::Dark, QColor(191, 191, 191)); - return pal; + auto platformTheme = QGuiApplicationPrivate::platformTheme(); + auto styleNames = platformTheme->themeHint(QPlatformTheme::StyleNames); + if (styleNames.toStringList().contains("macintosh")) + return *platformTheme->palette(); + else + return QStyle::standardPalette(); } int QMacStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget *w, -- cgit v1.2.3 From 39d74b5b3db328fa32f7b831936d3671d29ff0e3 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 17 Jun 2019 09:28:29 +0200 Subject: rhi: Remove reserved and since from the shader classes Private API, do not bother with these yet. Change-Id: I77fb8fadee427425759ed42234944b30155db0f5 Reviewed-by: Lars Knoll --- src/gui/rhi/qshader.cpp | 1 - src/gui/rhi/qshader_p.h | 3 --- src/gui/rhi/qshaderdescription.cpp | 1 - 3 files changed, 5 deletions(-) (limited to 'src') diff --git a/src/gui/rhi/qshader.cpp b/src/gui/rhi/qshader.cpp index 5569aad639..4676ec3f5b 100644 --- a/src/gui/rhi/qshader.cpp +++ b/src/gui/rhi/qshader.cpp @@ -43,7 +43,6 @@ QT_BEGIN_NAMESPACE /*! \class QShader \inmodule QtRhi - \since 5.14 \brief Contains multiple versions of a shader translated to multiple shading languages, together with reflection metadata. diff --git a/src/gui/rhi/qshader_p.h b/src/gui/rhi/qshader_p.h index 12a0a26e12..243842a95a 100644 --- a/src/gui/rhi/qshader_p.h +++ b/src/gui/rhi/qshader_p.h @@ -76,7 +76,6 @@ public: private: int m_version = 100; Flags m_flags; - Q_DECL_UNUSED_MEMBER quint64 m_reserved = 0; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QShaderVersion::Flags) @@ -97,7 +96,6 @@ public: private: QByteArray m_shader; QByteArray m_entryPoint; - Q_DECL_UNUSED_MEMBER quint64 m_reserved = 0; }; Q_DECLARE_TYPEINFO(QShaderCode, Q_MOVABLE_TYPE); @@ -182,7 +180,6 @@ private: QShader::Source m_source = QShader::SpirvShader; QShaderVersion m_sourceVersion; QShader::Variant m_sourceVariant = QShader::StandardShader; - Q_DECL_UNUSED_MEMBER quint64 m_reserved = 0; }; Q_DECLARE_TYPEINFO(QShaderKey, Q_MOVABLE_TYPE); diff --git a/src/gui/rhi/qshaderdescription.cpp b/src/gui/rhi/qshaderdescription.cpp index ed549b083f..c2dbbb38fa 100644 --- a/src/gui/rhi/qshaderdescription.cpp +++ b/src/gui/rhi/qshaderdescription.cpp @@ -44,7 +44,6 @@ QT_BEGIN_NAMESPACE /*! \class QShaderDescription \inmodule QtRhi - \since 5.14 \brief Describes the interface of a shader. -- cgit v1.2.3 From 4c297bdca8da543c582d129f12413d29a2a520eb Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 17 Jun 2019 12:17:19 +0200 Subject: rhi: Make it possible to request making the context current on GL Needed by Qt Quick to handle cases where the application (or other Qt) code contains OpenGL calls, and Qt Quick facilitates this by ensuring the scenegraph's GL context is current. The expectation is that when running with the GL backend of the rhi, all such code remains fully functional. So add a makeCurrent type of call into the QRhi API that is a no-op with anything other than OpenGL. Change-Id: I6f774bf828e31802bdab0c3fef9421cdc0cebe5c Reviewed-by: Lars Knoll --- src/gui/rhi/qrhi.cpp | 14 ++++++++++++++ src/gui/rhi/qrhi_p.h | 1 + src/gui/rhi/qrhi_p_p.h | 1 + src/gui/rhi/qrhid3d11.cpp | 5 +++++ src/gui/rhi/qrhid3d11_p_p.h | 1 + src/gui/rhi/qrhigles2.cpp | 8 ++++++++ src/gui/rhi/qrhigles2_p_p.h | 1 + src/gui/rhi/qrhimetal.mm | 5 +++++ src/gui/rhi/qrhimetal_p_p.h | 1 + src/gui/rhi/qrhinull.cpp | 5 +++++ src/gui/rhi/qrhinull_p_p.h | 1 + src/gui/rhi/qrhivulkan.cpp | 5 +++++ src/gui/rhi/qrhivulkan_p_p.h | 1 + 13 files changed, 49 insertions(+) (limited to 'src') diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp index 8cd4813723..f599f16d21 100644 --- a/src/gui/rhi/qrhi.cpp +++ b/src/gui/rhi/qrhi.cpp @@ -4443,6 +4443,20 @@ const QRhiNativeHandles *QRhi::nativeHandles() return d->nativeHandles(); } +/*! + With OpenGL this makes the OpenGL context current on the current thread. + The function has no effect with other backends. + + Calling this function is relevant typically in Qt framework code, when one + has to ensure external OpenGL code provided by the application can still + run like it did before with direct usage of OpenGL, as long as the QRhi is + using the OpenGL backend. + */ +void QRhi::makeThreadLocalNativeContextCurrent() +{ + d->makeThreadLocalNativeContextCurrent(); +} + /*! \return the associated QRhiProfiler instance. diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h index a0f57819e4..b7515cb17a 100644 --- a/src/gui/rhi/qrhi_p.h +++ b/src/gui/rhi/qrhi_p.h @@ -1352,6 +1352,7 @@ public: int resourceLimit(ResourceLimit limit) const; const QRhiNativeHandles *nativeHandles(); + void makeThreadLocalNativeContextCurrent(); QRhiProfiler *profiler(); diff --git a/src/gui/rhi/qrhi_p_p.h b/src/gui/rhi/qrhi_p_p.h index 7c110431fb..de9bdae992 100644 --- a/src/gui/rhi/qrhi_p_p.h +++ b/src/gui/rhi/qrhi_p_p.h @@ -148,6 +148,7 @@ public: virtual int resourceLimit(QRhi::ResourceLimit limit) const = 0; virtual const QRhiNativeHandles *nativeHandles() = 0; virtual void sendVMemStatsToProfiler() = 0; + virtual void makeThreadLocalNativeContextCurrent() = 0; bool isCompressedFormat(QRhiTexture::Format format) const; void compressedFormatInfo(QRhiTexture::Format format, const QSize &size, diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index 07e67d95d9..6e5b0f751f 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -408,6 +408,11 @@ void QRhiD3D11::sendVMemStatsToProfiler() // nothing to do here } +void QRhiD3D11::makeThreadLocalNativeContextCurrent() +{ + // nothing to do here +} + QRhiRenderBuffer *QRhiD3D11::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize, int sampleCount, QRhiRenderBuffer::Flags flags) { diff --git a/src/gui/rhi/qrhid3d11_p_p.h b/src/gui/rhi/qrhid3d11_p_p.h index fd5247b0b4..3942fa5076 100644 --- a/src/gui/rhi/qrhid3d11_p_p.h +++ b/src/gui/rhi/qrhid3d11_p_p.h @@ -563,6 +563,7 @@ public: int resourceLimit(QRhi::ResourceLimit limit) const override; const QRhiNativeHandles *nativeHandles() override; void sendVMemStatsToProfiler() override; + void makeThreadLocalNativeContextCurrent() override; void flushCommandBuffer(); void enqueueSubresUpload(QD3D11Texture *texD, QD3D11CommandBuffer *cbD, diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp index 0c89a9284f..a6a0bb257f 100644 --- a/src/gui/rhi/qrhigles2.cpp +++ b/src/gui/rhi/qrhigles2.cpp @@ -649,6 +649,14 @@ void QRhiGles2::sendVMemStatsToProfiler() // nothing to do here } +void QRhiGles2::makeThreadLocalNativeContextCurrent() +{ + if (inFrame && !ofr.active) + ensureContext(currentSwapChain->surface); + else + ensureContext(); +} + QRhiRenderBuffer *QRhiGles2::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize, int sampleCount, QRhiRenderBuffer::Flags flags) { diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h index d1a8a1fadf..5254219bd6 100644 --- a/src/gui/rhi/qrhigles2_p_p.h +++ b/src/gui/rhi/qrhigles2_p_p.h @@ -574,6 +574,7 @@ public: int resourceLimit(QRhi::ResourceLimit limit) const override; const QRhiNativeHandles *nativeHandles() override; void sendVMemStatsToProfiler() override; + void makeThreadLocalNativeContextCurrent() override; bool ensureContext(QSurface *surface = nullptr) const; void executeDeferredReleases(); diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm index 8739a42738..6030f55d10 100644 --- a/src/gui/rhi/qrhimetal.mm +++ b/src/gui/rhi/qrhimetal.mm @@ -538,6 +538,11 @@ void QRhiMetal::sendVMemStatsToProfiler() // nothing to do here } +void QRhiMetal::makeThreadLocalNativeContextCurrent() +{ + // nothing to do here +} + QRhiRenderBuffer *QRhiMetal::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize, int sampleCount, QRhiRenderBuffer::Flags flags) { diff --git a/src/gui/rhi/qrhimetal_p_p.h b/src/gui/rhi/qrhimetal_p_p.h index 039a5bdbf6..f9b9d96648 100644 --- a/src/gui/rhi/qrhimetal_p_p.h +++ b/src/gui/rhi/qrhimetal_p_p.h @@ -375,6 +375,7 @@ public: int resourceLimit(QRhi::ResourceLimit limit) const override; const QRhiNativeHandles *nativeHandles() override; void sendVMemStatsToProfiler() override; + void makeThreadLocalNativeContextCurrent() override; void executeDeferredReleases(bool forced = false); void finishActiveReadbacks(bool forced = false); diff --git a/src/gui/rhi/qrhinull.cpp b/src/gui/rhi/qrhinull.cpp index ad7b74a5c3..c764669058 100644 --- a/src/gui/rhi/qrhinull.cpp +++ b/src/gui/rhi/qrhinull.cpp @@ -166,6 +166,11 @@ void QRhiNull::sendVMemStatsToProfiler() // nothing to do here } +void QRhiNull::makeThreadLocalNativeContextCurrent() +{ + // nothing to do here +} + QRhiRenderBuffer *QRhiNull::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize, int sampleCount, QRhiRenderBuffer::Flags flags) { diff --git a/src/gui/rhi/qrhinull_p_p.h b/src/gui/rhi/qrhinull_p_p.h index da48b72656..6f79606486 100644 --- a/src/gui/rhi/qrhinull_p_p.h +++ b/src/gui/rhi/qrhinull_p_p.h @@ -268,6 +268,7 @@ public: int resourceLimit(QRhi::ResourceLimit limit) const override; const QRhiNativeHandles *nativeHandles() override; void sendVMemStatsToProfiler() override; + void makeThreadLocalNativeContextCurrent() override; QRhiNullNativeHandles nativeHandlesStruct; QRhiSwapChain *currentSwapChain = nullptr; diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp index 1f80df6d0d..2d7b7a16f6 100644 --- a/src/gui/rhi/qrhivulkan.cpp +++ b/src/gui/rhi/qrhivulkan.cpp @@ -3457,6 +3457,11 @@ void QRhiVulkan::sendVMemStatsToProfiler() stats.total.usedBytes, stats.total.unusedBytes)); } +void QRhiVulkan::makeThreadLocalNativeContextCurrent() +{ + // nothing to do here +} + QRhiRenderBuffer *QRhiVulkan::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize, int sampleCount, QRhiRenderBuffer::Flags flags) { diff --git a/src/gui/rhi/qrhivulkan_p_p.h b/src/gui/rhi/qrhivulkan_p_p.h index 01acc40d58..afb0cc1d5a 100644 --- a/src/gui/rhi/qrhivulkan_p_p.h +++ b/src/gui/rhi/qrhivulkan_p_p.h @@ -644,6 +644,7 @@ public: int resourceLimit(QRhi::ResourceLimit limit) const override; const QRhiNativeHandles *nativeHandles() override; void sendVMemStatsToProfiler() override; + void makeThreadLocalNativeContextCurrent() override; VkResult createDescriptorPool(VkDescriptorPool *pool); bool allocateDescriptorSet(VkDescriptorSetAllocateInfo *allocInfo, VkDescriptorSet *result, int *resultPoolIndex); -- cgit v1.2.3 From 6f4aa5413183f3f18dd1b15dbc90bcee9ef85bdd Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 12 Jun 2019 14:22:33 +0200 Subject: rhi: Add compute api and implement for Vulkan and Metal D3D11 and GL (4.3+, ES 3.1+) will come separately at a later time. Change-Id: If30f2f3d062fa27e57e9912674669225b82a7b93 Reviewed-by: Lars Knoll --- src/gui/rhi/qrhi.cpp | 555 ++++++++++++++++++++++++++++------ src/gui/rhi/qrhi_p.h | 86 ++++-- src/gui/rhi/qrhi_p_p.h | 43 ++- src/gui/rhi/qrhid3d11.cpp | 132 +++++--- src/gui/rhi/qrhid3d11_p_p.h | 25 +- src/gui/rhi/qrhigles2.cpp | 135 ++++++--- src/gui/rhi/qrhigles2_p_p.h | 24 +- src/gui/rhi/qrhimetal.mm | 533 +++++++++++++++++++++++++------- src/gui/rhi/qrhimetal_p_p.h | 47 ++- src/gui/rhi/qrhinull.cpp | 50 +++ src/gui/rhi/qrhinull_p_p.h | 14 + src/gui/rhi/qrhivulkan.cpp | 571 +++++++++++++++++++++++++++++------ src/gui/rhi/qrhivulkan_p_p.h | 71 ++++- src/gui/rhi/qshaderdescription.cpp | 29 ++ src/gui/rhi/qshaderdescription_p.h | 3 + src/gui/rhi/qshaderdescription_p_p.h | 5 +- 16 files changed, 1902 insertions(+), 421 deletions(-) (limited to 'src') diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp index f599f16d21..dbad63c6d1 100644 --- a/src/gui/rhi/qrhi.cpp +++ b/src/gui/rhi/qrhi.cpp @@ -266,6 +266,18 @@ QT_BEGIN_NAMESPACE transitions. Such synchronization is done implicitly by the backends, where applicable (for example, Vulkan), by tracking resource usage as necessary. + \note Resources within a render or compute pass are expected to be bound to + a single usage during that pass. For example, a buffer can be used as + vertex, index, uniform, or storage buffer, but not a combination of them + within a single pass. However, it is perfectly fine to use a buffer as a + storage buffer in a compute pass, and then as a vertex buffer in a render + pass, for example, assuming the buffer declared both usages upon creation. + + \note Textures have this rule relaxed in certain cases, because using two + subresources (typically two different mip levels) of the same texture for + different access (one for load, one for store) is supported even within the + same pass. + \section3 Resource reuse From the user's point of view a QRhiResource is reusable immediately after @@ -481,6 +493,8 @@ QT_BEGIN_NAMESPACE when running on plain OpenGL ES 2.0 implementations without the necessary extension. When false, only 16-bit unsigned elements are supported in the index buffer. + + \value Compute Indicates that compute shaders are supported. */ /*! @@ -1131,21 +1145,22 @@ QDebug operator<<(QDebug dbg, const QRhiVertexInputLayout &v) #endif /*! - \class QRhiGraphicsShaderStage + \class QRhiShaderStage \inmodule QtRhi - \brief Specifies the type and the shader code for a shader stage in the graphics pipeline. + \brief Specifies the type and the shader code for a shader stage in the pipeline. */ /*! - \enum QRhiGraphicsShaderStage::Type + \enum QRhiShaderStage::Type Specifies the type of the shader stage. \value Vertex Vertex stage \value Fragment Fragment (pixel) stage + \value Compute Compute stage (this may not always be supported at run time) */ /*! - \fn QRhiGraphicsShaderStage::QRhiGraphicsShaderStage() + \fn QRhiShaderStage::QRhiShaderStage() Constructs a shader stage description for the vertex stage with an empty QShader. @@ -1160,7 +1175,7 @@ QDebug operator<<(QDebug dbg, const QRhiVertexInputLayout &v) In addition, it can also contain variants of the shader with slightly modified code. \a v can then be used to select the desired variant. */ -QRhiGraphicsShaderStage::QRhiGraphicsShaderStage(Type type, const QShader &shader, QShader::Variant v) +QRhiShaderStage::QRhiShaderStage(Type type, const QShader &shader, QShader::Variant v) : m_type(type), m_shader(shader), m_shaderVariant(v) @@ -1168,12 +1183,12 @@ QRhiGraphicsShaderStage::QRhiGraphicsShaderStage(Type type, const QShader &shade } /*! - \return \c true if the values in the two QRhiGraphicsShaderStage objects + \return \c true if the values in the two QRhiShaderStage objects \a a and \a b are equal. - \relates QRhiGraphicsShaderStage + \relates QRhiShaderStage */ -bool operator==(const QRhiGraphicsShaderStage &a, const QRhiGraphicsShaderStage &b) Q_DECL_NOTHROW +bool operator==(const QRhiShaderStage &a, const QRhiShaderStage &b) Q_DECL_NOTHROW { return a.type() == b.type() && a.shader() == b.shader() @@ -1181,12 +1196,12 @@ bool operator==(const QRhiGraphicsShaderStage &a, const QRhiGraphicsShaderStage } /*! - \return \c false if the values in the two QRhiGraphicsShaderStage + \return \c false if the values in the two QRhiShaderStage objects \a a and \a b are equal; otherwise returns \c true. - \relates QRhiGraphicsShaderStage + \relates QRhiShaderStage */ -bool operator!=(const QRhiGraphicsShaderStage &a, const QRhiGraphicsShaderStage &b) Q_DECL_NOTHROW +bool operator!=(const QRhiShaderStage &a, const QRhiShaderStage &b) Q_DECL_NOTHROW { return !(a == b); } @@ -1194,18 +1209,18 @@ bool operator!=(const QRhiGraphicsShaderStage &a, const QRhiGraphicsShaderStage /*! \return the hash value for \a v, using \a seed to seed the calculation. - \relates QRhiGraphicsShaderStage + \relates QRhiShaderStage */ -uint qHash(const QRhiGraphicsShaderStage &v, uint seed) Q_DECL_NOTHROW +uint qHash(const QRhiShaderStage &v, uint seed) Q_DECL_NOTHROW { return v.type() + qHash(v.shader(), seed) + v.shaderVariant(); } #ifndef QT_NO_DEBUG_STREAM -QDebug operator<<(QDebug dbg, const QRhiGraphicsShaderStage &s) +QDebug operator<<(QDebug dbg, const QRhiShaderStage &s) { QDebugStateSaver saver(dbg); - dbg.nospace() << "QRhiGraphicsShaderStage(type=" << s.type() + dbg.nospace() << "QRhiShaderStage(type=" << s.type() << " shader=" << s.shader() << " variant=" << s.shaderVariant() << ')'; @@ -1781,9 +1796,25 @@ quint64 QRhiResource::globalResourceId() const \enum QRhiBuffer::UsageFlag Flag values to specify how the buffer is going to be used. - \value VertexBuffer Vertex buffer - \value IndexBuffer Index buffer - \value UniformBuffer Uniform (constant) buffer + \value VertexBuffer Vertex buffer. This allows the QRhiBuffer to be used in + \l{setVertexInput()}{QRhiCommandBuffer::setVertexInput()}. + + \value IndexBuffer Index buffer. This allows the QRhiBuffer to be used in + \l{setVertexInput()}{QRhiCommandBuffer::setVertexInput()}. + + \value UniformBuffer Uniform buffer (also called constant buffer). This + allows the QRhiBuffer to be used in combination with + \l{UniformBuffer}{QRhiShaderResourceBinding::UniformBuffer}. When + \l{QRhi::NonDynamicUniformBuffers}{NonDynamicUniformBuffers} is reported as + not supported, this usage can only be combined with the type Dynamic. + + \value StorageBuffer Storage buffer. This allows the QRhiBuffer to be used + in combination with \l{BufferLoad}{QRhiShaderResourceBinding::BufferLoad}, + \l{BufferStore}{QRhiShaderResourceBinding::BufferStore}, or + \l{BufferLoadStore}{QRhiShaderResourceBinding::BufferLoadStore}. This usage + can only be combined with the types Immutable or Static, and is only + available when the \l{QRhi::Compute}{Compute feature} is reported as + supported. */ /*! @@ -1941,6 +1972,9 @@ QRhiResource::Type QRhiRenderBuffer::resourceType() const \value UsedWithGenerateMips The texture is going to be used with QRhiResourceUpdateBatch::generateMips(). + + \value UsedWithLoadStore The texture is going to be used with image + load/store operations, for example, in a compute shader. */ /*! @@ -2438,7 +2472,26 @@ bool QRhiShaderResourceBindings::isLayoutCompatible(const QRhiShaderResourceBind Specifies type of the shader resource bound to a binding point \value UniformBuffer Uniform buffer + \value SampledTexture Combined image sampler + + \value ImageLoad Image load (with GLSL this maps to doing imageLoad() on a + single level - and either one or all layers - of a texture exposed to the + shader as an image object) + + \value ImageStore Image store (with GLSL this maps to doing imageStore() or + imageAtomic*() on a single level - and either one or all layers - of a + texture exposed to the shader as an image object) + + \value ImageLoadStore Image load and store + + \value BufferLoad Storage buffer store (with GLSL this maps to reading from + a shader storage buffer) + + \value BufferStore Storage buffer store (with GLSL this maps to writing to + a shader storage buffer) + + \value BufferLoadStore Storage buffer load and store */ /*! @@ -2447,6 +2500,7 @@ bool QRhiShaderResourceBindings::isLayoutCompatible(const QRhiShaderResourceBind \value VertexStage Vertex stage \value FragmentStage Fragment (pixel) stage + \value ComputeStage Compute stage */ /*! @@ -2513,6 +2567,8 @@ bool QRhiShaderResourceBinding::isLayoutCompatible(const QRhiShaderResourceBindi /*! \return a shader resource binding for the given binding number, pipeline stages, and buffer specified by \a binding, \a stage, and \a buf. + + \note \a buf must have been created with QRhiBuffer::UniformBuffer. */ QRhiShaderResourceBinding QRhiShaderResourceBinding::uniformBuffer( int binding, StageFlags stage, QRhiBuffer *buf) @@ -2539,21 +2595,17 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::uniformBuffer( QRhi::ubufAlignment(). \note \a size must be greater than 0. + + \note \a buf must have been created with QRhiBuffer::UniformBuffer. */ QRhiShaderResourceBinding QRhiShaderResourceBinding::uniformBuffer( int binding, StageFlags stage, QRhiBuffer *buf, int offset, int size) { Q_ASSERT(size > 0); - QRhiShaderResourceBinding b; + QRhiShaderResourceBinding b = uniformBuffer(binding, stage, buf); QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b); - Q_ASSERT(d->ref.load() == 1); - d->binding = binding; - d->stage = stage; - d->type = UniformBuffer; - d->u.ubuf.buf = buf; d->u.ubuf.offset = offset; d->u.ubuf.maybeSize = size; - d->u.ubuf.hasDynamicOffset = false; return b; } @@ -2565,19 +2617,14 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::uniformBuffer( varying offset values without creating new bindings for the buffer. The size of the bound region is specified by \a size. Like with non-dynamic offsets, \c{offset + size} cannot exceed the size of \a buf. + + \note \a buf must have been created with QRhiBuffer::UniformBuffer. */ QRhiShaderResourceBinding QRhiShaderResourceBinding::uniformBufferWithDynamicOffset( int binding, StageFlags stage, QRhiBuffer *buf, int size) { - QRhiShaderResourceBinding b; + QRhiShaderResourceBinding b = uniformBuffer(binding, stage, buf, 0, size); QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b); - Q_ASSERT(d->ref.load() == 1); - d->binding = binding; - d->stage = stage; - d->type = UniformBuffer; - d->u.ubuf.buf = buf; - d->u.ubuf.offset = 0; - d->u.ubuf.maybeSize = size; d->u.ubuf.hasDynamicOffset = true; return b; } @@ -2601,6 +2648,167 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::sampledTexture( return b; } +/*! + \return a shader resource binding for a read-only storage image with the + given \a binding number and pipeline \a stage. The image load operations + will have access to all layers of the specified \a level. (so if the texture + is a cubemap, the shader must use imageCube instead of image2D) + + \note \a tex must have been created with QRhiTexture::UsedWithLoadStore. + */ +QRhiShaderResourceBinding QRhiShaderResourceBinding::imageLoad( + int binding, StageFlags stage, QRhiTexture *tex, int level) +{ + QRhiShaderResourceBinding b; + QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b); + Q_ASSERT(d->ref.load() == 1); + d->binding = binding; + d->stage = stage; + d->type = ImageLoad; + d->u.simage.tex = tex; + d->u.simage.level = level; + return b; +} + +/*! + \return a shader resource binding for a write-only storage image with the + given \a binding number and pipeline \a stage. The image store operations + will have access to all layers of the specified \a level. (so if the texture + is a cubemap, the shader must use imageCube instead of image2D) + + \note \a tex must have been created with QRhiTexture::UsedWithLoadStore. + */ +QRhiShaderResourceBinding QRhiShaderResourceBinding::imageStore( + int binding, StageFlags stage, QRhiTexture *tex, int level) +{ + QRhiShaderResourceBinding b = imageLoad(binding, stage, tex, level); + QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b); + d->type = ImageStore; + return b; +} + +/*! + \return a shader resource binding for a read/write storage image with the + given \a binding number and pipeline \a stage. The image load/store operations + will have access to all layers of the specified \a level. (so if the texture + is a cubemap, the shader must use imageCube instead of image2D) + + \note \a tex must have been created with QRhiTexture::UsedWithLoadStore. + */ +QRhiShaderResourceBinding QRhiShaderResourceBinding::imageLoadStore( + int binding, StageFlags stage, QRhiTexture *tex, int level) +{ + QRhiShaderResourceBinding b = imageLoad(binding, stage, tex, level); + QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b); + d->type = ImageLoadStore; + return b; +} + +/*! + \return a shader resource binding for a read-only storage buffer with the + given \a binding number and pipeline \a stage. + + \note \a buf must have been created with QRhiBuffer::StorageBuffer. + */ +QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferLoad( + int binding, StageFlags stage, QRhiBuffer *buf) +{ + QRhiShaderResourceBinding b; + QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b); + Q_ASSERT(d->ref.load() == 1); + d->binding = binding; + d->stage = stage; + d->type = BufferLoad; + d->u.sbuf.buf = buf; + d->u.sbuf.offset = 0; + d->u.sbuf.maybeSize = 0; // entire buffer + return b; +} + +/*! + \return a shader resource binding for a read-only storage buffer with the + given \a binding number and pipeline \a stage. This overload binds a region + only, as specified by \a offset and \a size. + + \note \a buf must have been created with QRhiBuffer::StorageBuffer. + */ +QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferLoad( + int binding, StageFlags stage, QRhiBuffer *buf, int offset, int size) +{ + Q_ASSERT(size > 0); + QRhiShaderResourceBinding b = bufferLoad(binding, stage, buf); + QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b); + d->u.sbuf.offset = offset; + d->u.sbuf.maybeSize = size; + return b; +} + +/*! + \return a shader resource binding for a write-only storage buffer with the + given \a binding number and pipeline \a stage. + + \note \a buf must have been created with QRhiBuffer::StorageBuffer. + */ +QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferStore( + int binding, StageFlags stage, QRhiBuffer *buf) +{ + QRhiShaderResourceBinding b = bufferLoad(binding, stage, buf); + QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b); + d->type = BufferStore; + return b; +} + +/*! + \return a shader resource binding for a write-only storage buffer with the + given \a binding number and pipeline \a stage. This overload binds a region + only, as specified by \a offset and \a size. + + \note \a buf must have been created with QRhiBuffer::StorageBuffer. + */ +QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferStore( + int binding, StageFlags stage, QRhiBuffer *buf, int offset, int size) +{ + Q_ASSERT(size > 0); + QRhiShaderResourceBinding b = bufferStore(binding, stage, buf); + QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b); + d->u.sbuf.offset = offset; + d->u.sbuf.maybeSize = size; + return b; +} + +/*! + \return a shader resource binding for a read-write storage buffer with the + given \a binding number and pipeline \a stage. + + \note \a buf must have been created with QRhiBuffer::StorageBuffer. + */ +QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferLoadStore( + int binding, StageFlags stage, QRhiBuffer *buf) +{ + QRhiShaderResourceBinding b = bufferLoad(binding, stage, buf); + QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b); + d->type = BufferLoadStore; + return b; +} + +/*! + \return a shader resource binding for a read-write storage buffer with the + given \a binding number and pipeline \a stage. This overload binds a region + only, as specified by \a offset and \a size. + + \note \a buf must have been created with QRhiBuffer::StorageBuffer. + */ +QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferLoadStore( + int binding, StageFlags stage, QRhiBuffer *buf, int offset, int size) +{ + Q_ASSERT(size > 0); + QRhiShaderResourceBinding b = bufferLoadStore(binding, stage, buf); + QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b); + d->u.sbuf.offset = offset; + d->u.sbuf.maybeSize = size; + return b; +} + /*! \return \c true if the contents of the two QRhiShaderResourceBinding objects \a a and \a b are equal. This includes the resources (buffer, @@ -2639,6 +2847,29 @@ bool operator==(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBind return false; } break; + case QRhiShaderResourceBinding::ImageLoad: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::ImageStore: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::ImageLoadStore: + if (a.d->u.simage.tex != b.d->u.simage.tex + || a.d->u.simage.level != b.d->u.simage.level) + { + return false; + } + break; + case QRhiShaderResourceBinding::BufferLoad: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::BufferStore: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::BufferLoadStore: + if (a.d->u.sbuf.buf != b.d->u.sbuf.buf + || a.d->u.sbuf.offset != b.d->u.sbuf.offset + || a.d->u.sbuf.maybeSize != b.d->u.sbuf.maybeSize) + { + return false; + } + break; default: Q_UNREACHABLE(); return false; @@ -2693,6 +2924,45 @@ QDebug operator<<(QDebug dbg, const QRhiShaderResourceBinding &b) << " sampler=" << d->u.stex.sampler << ')'; break; + case QRhiShaderResourceBinding::ImageLoad: + dbg.nospace() << " ImageLoad(" + << "texture=" << d->u.simage.tex + << " level=" << d->u.simage.level + << ')'; + break; + case QRhiShaderResourceBinding::ImageStore: + dbg.nospace() << " ImageStore(" + << "texture=" << d->u.simage.tex + << " level=" << d->u.simage.level + << ')'; + break; + case QRhiShaderResourceBinding::ImageLoadStore: + dbg.nospace() << " ImageLoadStore(" + << "texture=" << d->u.simage.tex + << " level=" << d->u.simage.level + << ')'; + break; + case QRhiShaderResourceBinding::BufferLoad: + dbg.nospace() << " BufferLoad(" + << "buffer=" << d->u.sbuf.buf + << " offset=" << d->u.sbuf.offset + << " maybeSize=" << d->u.sbuf.maybeSize + << ')'; + break; + case QRhiShaderResourceBinding::BufferStore: + dbg.nospace() << " BufferStore(" + << "buffer=" << d->u.sbuf.buf + << " offset=" << d->u.sbuf.offset + << " maybeSize=" << d->u.sbuf.maybeSize + << ')'; + break; + case QRhiShaderResourceBinding::BufferLoadStore: + dbg.nospace() << " BufferLoadStore(" + << "buffer=" << d->u.sbuf.buf + << " offset=" << d->u.sbuf.offset + << " maybeSize=" << d->u.sbuf.maybeSize + << ')'; + break; default: Q_UNREACHABLE(); break; @@ -3195,6 +3465,34 @@ QRhiResource::Type QRhiSwapChain::resourceType() const Regardless of the return value, calling release() is always safe. */ +/*! + \class QRhiComputePipeline + \inmodule QtRhi + \brief Compute pipeline state resource. + + \note Setting the shader resource bindings is mandatory. The referenced + QRhiShaderResourceBindings must already be built by the time build() is + called. + + \note Setting the shader is mandatory. + */ + +/*! + \return the resource type. + */ +QRhiResource::Type QRhiComputePipeline::resourceType() const +{ + return ComputePipeline; +} + +/*! + \internal + */ +QRhiComputePipeline::QRhiComputePipeline(QRhiImplementation *rhi) + : QRhiResource(rhi) +{ +} + /*! \class QRhiCommandBuffer \inmodule QtRhi @@ -3982,8 +4280,8 @@ void QRhiCommandBuffer::endPass(QRhiResourceUpdateBatch *resourceUpdates) therefore overoptimizing to avoid calls to this function is not necessary on the applications' side. - \note This function can only be called inside a pass, meaning between a - beginPass() end endPass() call. + \note This function can only be called inside a render pass, meaning + between a beginPass() and endPass() call. */ void QRhiCommandBuffer::setGraphicsPipeline(QRhiGraphicsPipeline *ps) { @@ -3994,14 +4292,13 @@ void QRhiCommandBuffer::setGraphicsPipeline(QRhiGraphicsPipeline *ps) Records binding a set of shader resources, such as, uniform buffers or textures, that are made visible to one or more shader stages. - \a srb can be null in which case the current graphics pipeline's associated - QRhiGraphicsPipeline::shaderResourceBindings() is used. When \a srb is - non-null, it must be + \a srb can be null in which case the current graphics or compute pipeline's + associated QRhiShaderResourceBindings is used. When \a srb is non-null, it + must be \l{QRhiShaderResourceBindings::isLayoutCompatible()}{layout-compatible}, meaning the layout (number of bindings, the type and binding number of each binding) must fully match the QRhiShaderResourceBindings that was - associated with the pipeline at the time of calling - QRhiGraphicsPipeline::build(). + associated with the pipeline at the time of calling the pipeline's build(). There are cases when a seemingly unnecessary setShaderResources() call is mandatory: when rebuilding a resource referenced from \a srb, for example @@ -4029,8 +4326,9 @@ void QRhiCommandBuffer::setGraphicsPipeline(QRhiGraphicsPipeline *ps) the conditions described above into account), so therefore overoptimizing to avoid calls to this function is not necessary on the applications' side. - \note This function can only be called inside a pass, meaning between a - beginPass() end endPass() call. + \note This function can only be called inside a render or compute pass, + meaning between a beginPass() and endPass(), or beginComputePass() and + endComputePass(). */ void QRhiCommandBuffer::setShaderResources(QRhiShaderResourceBindings *srb, int dynamicOffsetCount, @@ -4056,8 +4354,8 @@ void QRhiCommandBuffer::setShaderResources(QRhiShaderResourceBindings *srb, automatically with most backends and therefore applications do not need to overoptimize to avoid calls to this function. - \note This function can only be called inside a pass, meaning between a - beginPass() end endPass() call. + \note This function can only be called inside a render pass, meaning + between a beginPass() and endPass() call. As a simple example, take a vertex shader with two inputs: @@ -4110,8 +4408,8 @@ void QRhiCommandBuffer::setVertexInput(int startBinding, int bindingCount, const \note QRhi assumes OpenGL-style viewport coordinates, meaning x and y are bottom-left. - \note This function can only be called inside a pass, meaning between a - beginPass() end endPass() call. + \note This function can only be called inside a render pass, meaning + between a beginPass() and endPass() call. */ void QRhiCommandBuffer::setViewport(const QRhiViewport &viewport) { @@ -4129,8 +4427,8 @@ void QRhiCommandBuffer::setViewport(const QRhiViewport &viewport) \note QRhi assumes OpenGL-style viewport coordinates, meaning x and y are bottom-left. - \note This function can only be called inside a pass, meaning between a - beginPass() end endPass() call. + \note This function can only be called inside a render pass, meaning + between a beginPass() and endPass() call. */ void QRhiCommandBuffer::setScissor(const QRhiScissor &scissor) { @@ -4143,8 +4441,8 @@ void QRhiCommandBuffer::setScissor(const QRhiScissor &scissor) This can only be called when the bound pipeline has QRhiGraphicsPipeline::UsesBlendConstants set. - \note This function can only be called inside a pass, meaning between a - beginPass() end endPass() call. + \note This function can only be called inside a render pass, meaning + between a beginPass() and endPass() call. */ void QRhiCommandBuffer::setBlendConstants(const QColor &c) { @@ -4157,8 +4455,8 @@ void QRhiCommandBuffer::setBlendConstants(const QColor &c) This can only be called when the bound pipeline has QRhiGraphicsPipeline::UsesStencilRef set. - \note This function can only be called inside a pass, meaning between a - beginPass() end endPass() call. + \note This function can only be called inside a render pass, meaning between + a beginPass() and endPass() call. */ void QRhiCommandBuffer::setStencilRef(quint32 refValue) { @@ -4173,8 +4471,8 @@ void QRhiCommandBuffer::setStencilRef(quint32 refValue) the index of the first vertex to draw. \a firstInstance is the instance ID of the first instance to draw. - \note This function can only be called inside a pass, meaning between a - beginPass() end endPass() call. + \note This function can only be called inside a render pass, meaning + between a beginPass() and endPass() call. */ void QRhiCommandBuffer::draw(quint32 vertexCount, quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) @@ -4200,8 +4498,8 @@ void QRhiCommandBuffer::draw(quint32 vertexCount, \a vertexOffset is added to the vertex index. - \note This function can only be called inside a pass, meaning between a - beginPass() end endPass() call. + \note This function can only be called inside a render pass, meaning + between a beginPass() and endPass() call. */ void QRhiCommandBuffer::drawIndexed(quint32 indexCount, quint32 instanceCount, quint32 firstIndex, @@ -4254,6 +4552,69 @@ void QRhiCommandBuffer::debugMarkMsg(const QByteArray &msg) m_rhi->debugMarkMsg(this, msg); } +/*! + Records starting a new compute pass. + + \a resourceUpdates, when not null, specifies a resource update batch that + is to be committed and then released. + + \note Do not assume that any state or resource bindings persist between + passes. + + \note A compute pass can record setComputePipeline(), setShaderResources(), + and dispatch() calls, not graphics ones. General functionality, such as, + debug markers and beginExternal() is available both in render and compute + passes. + + \note Compute is only available when the \l{QRhi::Compute}{Compute} feature + is reported as supported. + */ +void QRhiCommandBuffer::beginComputePass(QRhiResourceUpdateBatch *resourceUpdates) +{ + m_rhi->beginComputePass(this, resourceUpdates); +} + +/*! + Records ending the current compute pass. + + \a resourceUpdates, when not null, specifies a resource update batch that + is to be committed and then released. + */ +void QRhiCommandBuffer::endComputePass(QRhiResourceUpdateBatch *resourceUpdates) +{ + m_rhi->endComputePass(this, resourceUpdates); +} + +/*! + Records setting a new compute pipeline \a ps. + + \note This function must be called before recording setShaderResources() or + dispatch() commands on the command buffer. + + \note QRhi will optimize out unnecessary invocations within a pass, so + therefore overoptimizing to avoid calls to this function is not necessary + on the applications' side. + + \note This function can only be called inside a compute pass, meaning + between a beginComputePass() and endComputePass() call. + */ +void QRhiCommandBuffer::setComputePipeline(QRhiComputePipeline *ps) +{ + m_rhi->setComputePipeline(this, ps); +} + +/*! + Records dispatching compute work items, with \a x, \a y, and \a z + specifying the number of local workgroups in the corresponding dimension. + + \note This function can only be called inside a compute pass, meaning + between a beginComputePass() and endComputePass() call. + */ +void QRhiCommandBuffer::dispatch(int x, int y, int z) +{ + m_rhi->dispatch(this, x, y, z); +} + /*! \return a pointer to a backend-specific QRhiNativeHandles subclass, such as QRhiVulkanCommandBufferNativeHandles. The returned value is null when @@ -4479,6 +4840,19 @@ QRhiGraphicsPipeline *QRhi::newGraphicsPipeline() return d->createGraphicsPipeline(); } +/*! + \return a new compute pipeline resource. + + \note Compute is only available when the \l{QRhi::Compute}{Compute} feature + is reported as supported. + + \sa QRhiResource::release() + */ +QRhiComputePipeline *QRhi::newComputePipeline() +{ + return d->createComputePipeline(); +} + /*! \return a new shader resource binding collection resource. @@ -4493,7 +4867,8 @@ QRhiShaderResourceBindings *QRhi::newShaderResourceBindings() \return a new buffer with the specified \a type, \a usage, and \a size. \note Some \a usage and \a type combinations may not be supported by all - backends. See \l{QRhi::NonDynamicUniformBuffers}{the feature flags}. + backends. See \l{QRhiBuffer::UsageFlag}{UsageFlags} and + \l{QRhi::NonDynamicUniformBuffers}{the feature flags}. \sa QRhiResource::release() */ @@ -4840,32 +5215,30 @@ static inline QRhiPassResourceTracker::BufferStage earlierStage(QRhiPassResource return QRhiPassResourceTracker::BufferStage(qMin(int(a), int(b))); } -void QRhiPassResourceTracker::registerBufferOnce(QRhiBuffer *buf, int slot, BufferAccess access, BufferStage stage, - const UsageState &stateAtPassBegin) +void QRhiPassResourceTracker::registerBuffer(QRhiBuffer *buf, int slot, BufferAccess *access, BufferStage *stage, + const UsageState &state) { auto it = std::find_if(m_buffers.begin(), m_buffers.end(), [buf](const Buffer &b) { return b.buf == buf; }); if (it != m_buffers.end()) { - if (it->access != access) { + if (it->access != *access) { const QByteArray name = buf->name(); qWarning("Buffer %p (%s) used with different accesses within the same pass, this is not allowed.", buf, name.constData()); return; } - if (it->stage != stage) - it->stage = earlierStage(it->stage, stage); - // Multiple registrations of the same buffer is fine as long is it is - // a compatible usage. stateAtPassBegin is not actually the state at - // pass begin in the second, third, etc. invocation but that's fine - // since we'll just return here. + if (it->stage != *stage) { + it->stage = earlierStage(it->stage, *stage); + *stage = it->stage; + } return; } Buffer b; b.buf = buf; b.slot = slot; - b.access = access; - b.stage = stage; - b.stateAtPassBegin = stateAtPassBegin; + b.access = *access; + b.stage = *stage; + b.stateAtPassBegin = state; // first use -> initial state m_buffers.append(b); } @@ -4875,30 +5248,44 @@ static inline QRhiPassResourceTracker::TextureStage earlierStage(QRhiPassResourc return QRhiPassResourceTracker::TextureStage(qMin(int(a), int(b))); } -void QRhiPassResourceTracker::registerTextureOnce(QRhiTexture *tex, TextureAccess access, TextureStage stage, - const UsageState &stateAtPassBegin) +static inline bool isImageLoadStore(QRhiPassResourceTracker::TextureAccess access) +{ + return access == QRhiPassResourceTracker::TexStorageLoad + || access == QRhiPassResourceTracker::TexStorageStore + || access == QRhiPassResourceTracker::TexStorageLoadStore; +} + +void QRhiPassResourceTracker::registerTexture(QRhiTexture *tex, TextureAccess *access, TextureStage *stage, + const UsageState &state) { auto it = std::find_if(m_textures.begin(), m_textures.end(), [tex](const Texture &t) { return t.tex == tex; }); if (it != m_textures.end()) { - if (it->access != access) { - const QByteArray name = tex->name(); - qWarning("Texture %p (%s) used with different accesses within the same pass, this is not allowed.", - tex, name.constData()); + if (it->access != *access) { + // Different subresources of a texture may be used for both load + // and store in the same pass. (think reading from one mip level + // and writing to another one in a compute shader) This we can + // handle by treating the entire resource as read-write. + if (isImageLoadStore(it->access) && isImageLoadStore(*access)) { + it->access = QRhiPassResourceTracker::TexStorageLoadStore; + *access = it->access; + } else { + const QByteArray name = tex->name(); + qWarning("Texture %p (%s) used with different accesses within the same pass, this is not allowed.", + tex, name.constData()); + } + } + if (it->stage != *stage) { + it->stage = earlierStage(it->stage, *stage); + *stage = it->stage; } - if (it->stage != stage) - it->stage = earlierStage(it->stage, stage); - // Multiple registrations of the same texture is fine as long is it is - // a compatible usage. stateAtPassBegin is not actually the state at - // pass begin in the second, third, etc. invocation but that's fine - // since we'll just return here. return; } Texture t; t.tex = tex; - t.access = access; - t.stage = stage; - t.stateAtPassBegin = stateAtPassBegin; + t.access = *access; + t.stage = *stage; + t.stateAtPassBegin = state; // first use -> initial state m_textures.append(t); } diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h index b7515cb17a..0d296d370c 100644 --- a/src/gui/rhi/qrhi_p.h +++ b/src/gui/rhi/qrhi_p.h @@ -259,17 +259,18 @@ Q_GUI_EXPORT uint qHash(const QRhiVertexInputLayout &v, uint seed = 0) Q_DECL_NO Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiVertexInputLayout &); #endif -class Q_GUI_EXPORT QRhiGraphicsShaderStage +class Q_GUI_EXPORT QRhiShaderStage { public: enum Type { Vertex, - Fragment + Fragment, + Compute }; - QRhiGraphicsShaderStage() = default; - QRhiGraphicsShaderStage(Type type, const QShader &shader, - QShader::Variant v = QShader::StandardShader); + QRhiShaderStage() = default; + QRhiShaderStage(Type type, const QShader &shader, + QShader::Variant v = QShader::StandardShader); Type type() const { return m_type; } void setType(Type t) { m_type = t; } @@ -286,26 +287,35 @@ private: QShader::Variant m_shaderVariant = QShader::StandardShader; }; -Q_DECLARE_TYPEINFO(QRhiGraphicsShaderStage, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(QRhiShaderStage, Q_MOVABLE_TYPE); -Q_GUI_EXPORT bool operator==(const QRhiGraphicsShaderStage &a, const QRhiGraphicsShaderStage &b) Q_DECL_NOTHROW; -Q_GUI_EXPORT bool operator!=(const QRhiGraphicsShaderStage &a, const QRhiGraphicsShaderStage &b) Q_DECL_NOTHROW; -Q_GUI_EXPORT uint qHash(const QRhiGraphicsShaderStage &s, uint seed = 0) Q_DECL_NOTHROW; +Q_GUI_EXPORT bool operator==(const QRhiShaderStage &a, const QRhiShaderStage &b) Q_DECL_NOTHROW; +Q_GUI_EXPORT bool operator!=(const QRhiShaderStage &a, const QRhiShaderStage &b) Q_DECL_NOTHROW; +Q_GUI_EXPORT uint qHash(const QRhiShaderStage &s, uint seed = 0) Q_DECL_NOTHROW; #ifndef QT_NO_DEBUG_STREAM -Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiGraphicsShaderStage &); +Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiShaderStage &); #endif +using QRhiGraphicsShaderStage = QRhiShaderStage; + class Q_GUI_EXPORT QRhiShaderResourceBinding { public: enum Type { UniformBuffer, - SampledTexture + SampledTexture, + ImageLoad, + ImageStore, + ImageLoadStore, + BufferLoad, + BufferStore, + BufferLoadStore }; enum StageFlag { VertexStage = 1 << 0, - FragmentStage = 1 << 1 + FragmentStage = 1 << 1, + ComputeStage = 1 << 2 }; Q_DECLARE_FLAGS(StageFlags, StageFlag) @@ -320,8 +330,20 @@ public: static QRhiShaderResourceBinding uniformBuffer(int binding, StageFlags stage, QRhiBuffer *buf); static QRhiShaderResourceBinding uniformBuffer(int binding, StageFlags stage, QRhiBuffer *buf, int offset, int size); static QRhiShaderResourceBinding uniformBufferWithDynamicOffset(int binding, StageFlags stage, QRhiBuffer *buf, int size); + static QRhiShaderResourceBinding sampledTexture(int binding, StageFlags stage, QRhiTexture *tex, QRhiSampler *sampler); + static QRhiShaderResourceBinding imageLoad(int binding, StageFlags stage, QRhiTexture *tex, int level); + static QRhiShaderResourceBinding imageStore(int binding, StageFlags stage, QRhiTexture *tex, int level); + static QRhiShaderResourceBinding imageLoadStore(int binding, StageFlags stage, QRhiTexture *tex, int level); + + static QRhiShaderResourceBinding bufferLoad(int binding, StageFlags stage, QRhiBuffer *buf); + static QRhiShaderResourceBinding bufferLoad(int binding, StageFlags stage, QRhiBuffer *buf, int offset, int size); + static QRhiShaderResourceBinding bufferStore(int binding, StageFlags stage, QRhiBuffer *buf); + static QRhiShaderResourceBinding bufferStore(int binding, StageFlags stage, QRhiBuffer *buf, int offset, int size); + static QRhiShaderResourceBinding bufferLoadStore(int binding, StageFlags stage, QRhiBuffer *buf); + static QRhiShaderResourceBinding bufferLoadStore(int binding, StageFlags stage, QRhiBuffer *buf, int offset, int size); + private: QRhiShaderResourceBindingPrivate *d; friend class QRhiShaderResourceBindingPrivate; @@ -558,6 +580,7 @@ public: ShaderResourceBindings, GraphicsPipeline, SwapChain, + ComputePipeline, CommandBuffer }; @@ -594,7 +617,8 @@ public: enum UsageFlag { VertexBuffer = 1 << 0, IndexBuffer = 1 << 1, - UniformBuffer = 1 << 2 + UniformBuffer = 1 << 2, + StorageBuffer = 1 << 3 }; Q_DECLARE_FLAGS(UsageFlags, UsageFlag) @@ -629,7 +653,8 @@ public: MipMapped = 1 << 3, sRGB = 1 << 4, UsedAsTransferSource = 1 << 5, - UsedWithGenerateMips = 1 << 6 + UsedWithGenerateMips = 1 << 6, + UsedWithLoadStore = 1 << 7 }; Q_DECLARE_FLAGS(Flags, Flag) @@ -1043,8 +1068,8 @@ public: int sampleCount() const { return m_sampleCount; } void setSampleCount(int s) { m_sampleCount = s; } - QVector shaderStages() const { return m_shaderStages; } - void setShaderStages(const QVector &stages) { m_shaderStages = stages; } + QVector shaderStages() const { return m_shaderStages; } + void setShaderStages(const QVector &stages) { m_shaderStages = stages; } QRhiVertexInputLayout vertexInputLayout() const { return m_vertexInputLayout; } void setVertexInputLayout(const QRhiVertexInputLayout &layout) { m_vertexInputLayout = layout; } @@ -1073,7 +1098,7 @@ protected: quint32 m_stencilReadMask = 0xFF; quint32 m_stencilWriteMask = 0xFF; int m_sampleCount = 1; - QVector m_shaderStages; + QVector m_shaderStages; QRhiVertexInputLayout m_vertexInputLayout; QRhiShaderResourceBindings *m_shaderResourceBindings = nullptr; QRhiRenderPassDescriptor *m_renderPassDesc = nullptr; @@ -1133,6 +1158,24 @@ protected: Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiSwapChain::Flags) +class Q_GUI_EXPORT QRhiComputePipeline : public QRhiResource +{ +public: + QRhiResource::Type resourceType() const override; + virtual bool build() = 0; + + QRhiShaderStage shaderStage() const { return m_shaderStage; } + void setShaderStage(const QRhiShaderStage &stage) { m_shaderStage = stage; } + + QRhiShaderResourceBindings *shaderResourceBindings() const { return m_shaderResourceBindings; } + void setShaderResourceBindings(QRhiShaderResourceBindings *srb) { m_shaderResourceBindings = srb; } + +protected: + QRhiComputePipeline(QRhiImplementation *rhi); + QRhiShaderStage m_shaderStage; + QRhiShaderResourceBindings *m_shaderResourceBindings = nullptr; +}; + class Q_GUI_EXPORT QRhiCommandBuffer : public QRhiResource { public: @@ -1181,6 +1224,11 @@ public: void debugMarkEnd(); void debugMarkMsg(const QByteArray &msg); + void beginComputePass(QRhiResourceUpdateBatch *resourceUpdates = nullptr); + void endComputePass(QRhiResourceUpdateBatch *resourceUpdates = nullptr); + void setComputePipeline(QRhiComputePipeline *ps); + void dispatch(int x, int y, int z); + const QRhiNativeHandles *nativeHandles(); void beginExternal(); void endExternal(); @@ -1263,7 +1311,8 @@ public: NonFourAlignedEffectiveIndexBufferOffset, NPOTTextureRepeat, RedOrAlpha8IsRed, - ElementIndexUint + ElementIndexUint, + Compute }; enum BeginFrameFlag { @@ -1297,6 +1346,7 @@ public: void runCleanup(); QRhiGraphicsPipeline *newGraphicsPipeline(); + QRhiComputePipeline *newComputePipeline(); QRhiShaderResourceBindings *newShaderResourceBindings(); QRhiBuffer *newBuffer(QRhiBuffer::Type type, diff --git a/src/gui/rhi/qrhi_p_p.h b/src/gui/rhi/qrhi_p_p.h index de9bdae992..4fd01d3ef2 100644 --- a/src/gui/rhi/qrhi_p_p.h +++ b/src/gui/rhi/qrhi_p_p.h @@ -70,6 +70,7 @@ public: virtual void destroy() = 0; virtual QRhiGraphicsPipeline *createGraphicsPipeline() = 0; + virtual QRhiComputePipeline *createComputePipeline() = 0; virtual QRhiShaderResourceBindings *createShaderResourceBindings() = 0; virtual QRhiBuffer *createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, @@ -133,6 +134,11 @@ public: virtual void debugMarkEnd(QRhiCommandBuffer *cb) = 0; virtual void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) = 0; + virtual void beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) = 0; + virtual void endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) = 0; + virtual void setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps) = 0; + virtual void dispatch(QRhiCommandBuffer *cb, int x, int y, int z) = 0; + virtual const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) = 0; virtual void beginExternal(QRhiCommandBuffer *cb) = 0; virtual void endExternal(QRhiCommandBuffer *cb) = 0; @@ -200,6 +206,7 @@ public: protected: bool debugMarkers = false; int currentFrameSlot = 0; // for vk, mtl, and similar. unused by gl and d3d11. + bool inFrame = false; private: QRhi::Implementation implType; @@ -210,7 +217,6 @@ private: QSet resources; QSet pendingReleaseAndDestroyResources; QVector cleanupCallbacks; - bool inFrame = false; friend class QRhi; friend class QRhiResourceUpdateBatchPrivate; @@ -393,9 +399,20 @@ public: QRhiTexture *tex; QRhiSampler *sampler; }; + struct StorageImageData { + QRhiTexture *tex; + int level; + }; + struct StorageBufferData { + QRhiBuffer *buf; + int offset; + int maybeSize; + }; union { UniformBufferData ubuf; SampledTextureData stex; + StorageImageData simage; + StorageBufferData sbuf; } u; }; @@ -487,33 +504,41 @@ public: enum BufferStage { BufVertexInputStage, BufVertexStage, - BufFragmentStage + BufFragmentStage, + BufComputeStage }; enum BufferAccess { BufVertexInput, BufIndexRead, - BufUniformRead + BufUniformRead, + BufStorageLoad, + BufStorageStore, + BufStorageLoadStore }; - void registerBufferOnce(QRhiBuffer *buf, int slot, BufferAccess access, BufferStage stage, - const UsageState &stateAtPassBegin); + void registerBuffer(QRhiBuffer *buf, int slot, BufferAccess *access, BufferStage *stage, + const UsageState &state); enum TextureStage { TexVertexStage, TexFragmentStage, TexColorOutputStage, - TexDepthOutputStage + TexDepthOutputStage, + TexComputeStage }; enum TextureAccess { TexSample, TexColorOutput, - TexDepthOutput + TexDepthOutput, + TexStorageLoad, + TexStorageStore, + TexStorageLoadStore }; - void registerTextureOnce(QRhiTexture *tex, TextureAccess access, TextureStage stage, - const UsageState &stateAtPassBegin); + void registerTexture(QRhiTexture *tex, TextureAccess *access, TextureStage *stage, + const UsageState &state); struct Buffer { QRhiBuffer *buf; diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index 6e5b0f751f..ce1617045b 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -375,6 +375,8 @@ bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const return true; case QRhi::ElementIndexUint: return true; + case QRhi::Compute: + return false; default: Q_UNREACHABLE(); return false; @@ -443,6 +445,11 @@ QRhiGraphicsPipeline *QRhiD3D11::createGraphicsPipeline() return new QD3D11GraphicsPipeline(this); } +QRhiComputePipeline *QRhiD3D11::createComputePipeline() +{ + return new QD3D11ComputePipeline(this); +} + QRhiShaderResourceBindings *QRhiD3D11::createShaderResourceBindings() { return new QD3D11ShaderResourceBindings(this); @@ -450,9 +457,8 @@ QRhiShaderResourceBindings *QRhiD3D11::createShaderResourceBindings() void QRhiD3D11::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps) { - Q_ASSERT(inPass); - QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::RenderPass); QD3D11GraphicsPipeline *psD = QRHI_RES(QD3D11GraphicsPipeline, ps); const bool pipelineChanged = cbD->currentPipeline != ps || cbD->currentPipelineGeneration != psD->generation; @@ -471,9 +477,8 @@ void QRhiD3D11::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind int dynamicOffsetCount, const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) { - Q_ASSERT(inPass); - QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::RenderPass); Q_ASSERT(cbD->currentPipeline); if (!srb) @@ -568,8 +573,8 @@ void QRhiD3D11::setVertexInput(QRhiCommandBuffer *cb, int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings, QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat) { - Q_ASSERT(inPass); QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::RenderPass); bool needsBindVBuf = false; for (int i = 0; i < bindingCount; ++i) { @@ -632,8 +637,8 @@ void QRhiD3D11::setVertexInput(QRhiCommandBuffer *cb, void QRhiD3D11::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) { - Q_ASSERT(inPass); QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::RenderPass); Q_ASSERT(cbD->currentTarget); const QSize outputSize = cbD->currentTarget->pixelSize(); @@ -656,8 +661,8 @@ void QRhiD3D11::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) void QRhiD3D11::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) { - Q_ASSERT(inPass); QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::RenderPass); Q_ASSERT(cbD->currentTarget); const QSize outputSize = cbD->currentTarget->pixelSize(); @@ -678,8 +683,9 @@ void QRhiD3D11::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) void QRhiD3D11::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) { - Q_ASSERT(inPass); QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::RenderPass); + QD3D11CommandBuffer::Command cmd; cmd.cmd = QD3D11CommandBuffer::Command::BlendConstants; cmd.args.blendConstants.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentPipeline); @@ -692,8 +698,9 @@ void QRhiD3D11::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) void QRhiD3D11::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) { - Q_ASSERT(inPass); QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::RenderPass); + QD3D11CommandBuffer::Command cmd; cmd.cmd = QD3D11CommandBuffer::Command::StencilRef; cmd.args.stencilRef.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentPipeline); @@ -704,8 +711,9 @@ void QRhiD3D11::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) void QRhiD3D11::draw(QRhiCommandBuffer *cb, quint32 vertexCount, quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) { - Q_ASSERT(inPass); QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::RenderPass); + QD3D11CommandBuffer::Command cmd; cmd.cmd = QD3D11CommandBuffer::Command::Draw; cmd.args.draw.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentPipeline); @@ -719,8 +727,9 @@ void QRhiD3D11::draw(QRhiCommandBuffer *cb, quint32 vertexCount, void QRhiD3D11::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance) { - Q_ASSERT(inPass); QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::RenderPass); + QD3D11CommandBuffer::Command cmd; cmd.cmd = QD3D11CommandBuffer::Command::DrawIndexed; cmd.args.drawIndexed.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentPipeline); @@ -777,28 +786,23 @@ const QRhiNativeHandles *QRhiD3D11::nativeHandles(QRhiCommandBuffer *cb) void QRhiD3D11::beginExternal(QRhiCommandBuffer *cb) { - Q_ASSERT(inPass); Q_UNUSED(cb); flushCommandBuffer(); } void QRhiD3D11::endExternal(QRhiCommandBuffer *cb) { - Q_ASSERT(inPass); QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); - Q_ASSERT(cbD->currentTarget); Q_ASSERT(cbD->commands.isEmpty()); cbD->resetCachedState(); - enqueueSetRenderTarget(cbD, cbD->currentTarget); + if (cbD->currentTarget) // could be compute, no rendertarget then + enqueueSetRenderTarget(cbD, cbD->currentTarget); } QRhi::FrameOpResult QRhiD3D11::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) { Q_UNUSED(flags); - Q_ASSERT(!inFrame); - inFrame = true; - QD3D11SwapChain *swapChainD = QRHI_RES(QD3D11SwapChain, swapChain); contextState.currentSwapChain = swapChainD; const int currentFrameSlot = swapChainD->currentFrameSlot; @@ -844,9 +848,6 @@ QRhi::FrameOpResult QRhiD3D11::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginF QRhi::FrameOpResult QRhiD3D11::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) { - Q_ASSERT(inFrame); - inFrame = false; - QD3D11SwapChain *swapChainD = QRHI_RES(QD3D11SwapChain, swapChain); Q_ASSERT(contextState.currentSwapChain = swapChainD); const int currentFrameSlot = swapChainD->currentFrameSlot; @@ -899,8 +900,6 @@ QRhi::FrameOpResult QRhiD3D11::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame QRhi::FrameOpResult QRhiD3D11::beginOffscreenFrame(QRhiCommandBuffer **cb) { - Q_ASSERT(!inFrame); - inFrame = true; ofr.active = true; ofr.cbWrapper.resetState(); @@ -911,8 +910,6 @@ QRhi::FrameOpResult QRhiD3D11::beginOffscreenFrame(QRhiCommandBuffer **cb) QRhi::FrameOpResult QRhiD3D11::endOffscreenFrame() { - Q_ASSERT(inFrame && ofr.active); - inFrame = false; ofr.active = false; executeCommandBuffer(&ofr.cbWrapper); @@ -1047,8 +1044,6 @@ static inline bool isDepthTextureFormat(QRhiTexture::Format format) QRhi::FrameOpResult QRhiD3D11::finish() { - Q_ASSERT(!inPass); - if (inFrame) flushCommandBuffer(); @@ -1379,7 +1374,7 @@ static inline QD3D11RenderTargetData *rtData(QRhiRenderTarget *rt) void QRhiD3D11::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) { - Q_ASSERT(inFrame && !inPass); + Q_ASSERT(QRHI_RES(QD3D11CommandBuffer, cb)->recordingPass == QD3D11CommandBuffer::NoPass); enqueueResourceUpdates(cb, resourceUpdates); } @@ -1398,12 +1393,12 @@ void QRhiD3D11::beginPass(QRhiCommandBuffer *cb, const QRhiDepthStencilClearValue &depthStencilClearValue, QRhiResourceUpdateBatch *resourceUpdates) { - Q_ASSERT(inFrame && !inPass); + QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::NoPass); if (resourceUpdates) enqueueResourceUpdates(cb, resourceUpdates); - QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); bool wantsColorClear = true; bool wantsDsClear = true; QD3D11RenderTargetData *rtD = rtData(rt); @@ -1431,17 +1426,15 @@ void QRhiD3D11::beginPass(QRhiCommandBuffer *cb, clearCmd.args.clear.s = depthStencilClearValue.stencilClearValue(); cbD->commands.append(clearCmd); + cbD->recordingPass = QD3D11CommandBuffer::RenderPass; cbD->currentTarget = rt; - - inPass = true; } void QRhiD3D11::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) { - Q_ASSERT(inPass); - inPass = false; - QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::RenderPass); + if (cbD->currentTarget->resourceType() == QRhiResource::TextureRenderTarget) { QD3D11TextureRenderTarget *rtTex = QRHI_RES(QD3D11TextureRenderTarget, cbD->currentTarget); const QVector colorAttachments = rtTex->m_desc.colorAttachments(); @@ -1491,12 +1484,49 @@ void QRhiD3D11::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resource } } + cbD->recordingPass = QD3D11CommandBuffer::NoPass; cbD->currentTarget = nullptr; if (resourceUpdates) enqueueResourceUpdates(cb, resourceUpdates); } +void QRhiD3D11::beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) +{ + QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::NoPass); + + if (resourceUpdates) + enqueueResourceUpdates(cb, resourceUpdates); + + cbD->recordingPass = QD3D11CommandBuffer::ComputePass; +} + +void QRhiD3D11::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) +{ + QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::ComputePass); + + cbD->recordingPass = QD3D11CommandBuffer::NoPass; + + if (resourceUpdates) + enqueueResourceUpdates(cb, resourceUpdates); +} + +void QRhiD3D11::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps) +{ + Q_UNUSED(cb); + Q_UNUSED(ps); +} + +void QRhiD3D11::dispatch(QRhiCommandBuffer *cb, int x, int y, int z) +{ + Q_UNUSED(cb); + Q_UNUSED(x); + Q_UNUSED(y); + Q_UNUSED(z); +} + void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD) { srbD->vsubufs.clear(); @@ -1709,6 +1739,8 @@ void QRhiD3D11::setRenderTarget(QRhiRenderTarget *rt) void QRhiD3D11::executeCommandBuffer(QD3D11CommandBuffer *cbD, QD3D11SwapChain *timestampSwapChain) { + Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::NoPass); + quint32 stencilRef = 0; float blendConstants[] = { 1, 1, 1, 1 }; @@ -1911,6 +1943,11 @@ bool QD3D11Buffer::build() if (buffer) release(); + if (m_usage.testFlag(QRhiBuffer::UniformBuffer) && m_type != Dynamic) { + qWarning("UniformBuffer must always be combined with Dynamic on D3D11"); + return false; + } + const int nonZeroSize = m_size <= 0 ? 256 : m_size; const int roundedSize = m_usage.testFlag(QRhiBuffer::UniformBuffer) ? aligned(nonZeroSize, 256) : nonZeroSize; @@ -3008,7 +3045,7 @@ bool QD3D11GraphicsPipeline::build() } QByteArray vsByteCode; - for (const QRhiGraphicsShaderStage &shaderStage : qAsConst(m_shaderStages)) { + for (const QRhiShaderStage &shaderStage : qAsConst(m_shaderStages)) { QString error; QByteArray bytecode = compileHlslShaderSource(shaderStage.shader(), shaderStage.shaderVariant(), &error); if (bytecode.isEmpty()) { @@ -3016,7 +3053,7 @@ bool QD3D11GraphicsPipeline::build() return false; } switch (shaderStage.type()) { - case QRhiGraphicsShaderStage::Vertex: + case QRhiShaderStage::Vertex: hr = rhiD->dev->CreateVertexShader(bytecode.constData(), bytecode.size(), nullptr, &vs); if (FAILED(hr)) { qWarning("Failed to create vertex shader: %s", qPrintable(comErrorMessage(hr))); @@ -3024,7 +3061,7 @@ bool QD3D11GraphicsPipeline::build() } vsByteCode = bytecode; break; - case QRhiGraphicsShaderStage::Fragment: + case QRhiShaderStage::Fragment: hr = rhiD->dev->CreatePixelShader(bytecode.constData(), bytecode.size(), nullptr, &fs); if (FAILED(hr)) { qWarning("Failed to create pixel shader: %s", qPrintable(comErrorMessage(hr))); @@ -3072,6 +3109,25 @@ bool QD3D11GraphicsPipeline::build() return true; } +QD3D11ComputePipeline::QD3D11ComputePipeline(QRhiImplementation *rhi) + : QRhiComputePipeline(rhi) +{ +} + +QD3D11ComputePipeline::~QD3D11ComputePipeline() +{ + release(); +} + +void QD3D11ComputePipeline::release() +{ +} + +bool QD3D11ComputePipeline::build() +{ + return false; +} + QD3D11CommandBuffer::QD3D11CommandBuffer(QRhiImplementation *rhi) : QRhiCommandBuffer(rhi) { diff --git a/src/gui/rhi/qrhid3d11_p_p.h b/src/gui/rhi/qrhid3d11_p_p.h index 3942fa5076..775f256cb7 100644 --- a/src/gui/rhi/qrhid3d11_p_p.h +++ b/src/gui/rhi/qrhid3d11_p_p.h @@ -254,6 +254,14 @@ struct QD3D11GraphicsPipeline : public QRhiGraphicsPipeline friend class QRhiD3D11; }; +struct QD3D11ComputePipeline : public QRhiComputePipeline +{ + QD3D11ComputePipeline(QRhiImplementation *rhi); + ~QD3D11ComputePipeline(); + void release() override; + bool build() override; +}; + struct QD3D11SwapChain; struct QD3D11CommandBuffer : public QRhiCommandBuffer @@ -387,7 +395,14 @@ struct QD3D11CommandBuffer : public QRhiCommandBuffer } args; }; + enum PassType { + NoPass, + RenderPass, + ComputePass + }; + QVector commands; + PassType recordingPass; QRhiRenderTarget *currentTarget; QRhiGraphicsPipeline *currentPipeline; uint currentPipelineGeneration; @@ -418,6 +433,7 @@ struct QD3D11CommandBuffer : public QRhiCommandBuffer } void resetState() { resetCommands(); + recordingPass = NoPass; currentTarget = nullptr; resetCachedState(); } @@ -484,6 +500,7 @@ public: void destroy() override; QRhiGraphicsPipeline *createGraphicsPipeline() override; + QRhiComputePipeline *createComputePipeline() override; QRhiShaderResourceBindings *createShaderResourceBindings() override; QRhiBuffer *createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, @@ -548,6 +565,11 @@ public: void debugMarkEnd(QRhiCommandBuffer *cb) override; void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) override; + void beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override; + void endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override; + void setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps) override; + void dispatch(QRhiCommandBuffer *cb, int x, int y, int z) override; + const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) override; void beginExternal(QRhiCommandBuffer *cb) override; void endExternal(QRhiCommandBuffer *cb) override; @@ -591,9 +613,6 @@ public: bool hasDxgi2 = false; QRhiD3D11NativeHandles nativeHandlesStruct; - bool inFrame = false; - bool inPass = false; - struct { int vsHighestActiveSrvBinding = -1; int fsHighestActiveSrvBinding = -1; diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp index a6a0bb257f..32a25dd615 100644 --- a/src/gui/rhi/qrhigles2.cpp +++ b/src/gui/rhi/qrhigles2.cpp @@ -616,6 +616,8 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const return caps.coreProfile; case QRhi::ElementIndexUint: return caps.elementIndexUint; + case QRhi::Compute: + return false; default: Q_UNREACHABLE(); return false; @@ -692,11 +694,15 @@ QRhiShaderResourceBindings *QRhiGles2::createShaderResourceBindings() return new QGles2ShaderResourceBindings(this); } -void QRhiGles2::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps) +QRhiComputePipeline *QRhiGles2::createComputePipeline() { - Q_ASSERT(inPass); + return new QGles2ComputePipeline(this); +} +void QRhiGles2::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps) +{ 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; @@ -715,9 +721,8 @@ void QRhiGles2::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind int dynamicOffsetCount, const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) { - Q_ASSERT(inPass); - QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass); Q_ASSERT(cbD->currentPipeline); if (!srb) @@ -770,8 +775,8 @@ void QRhiGles2::setVertexInput(QRhiCommandBuffer *cb, int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings, QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat) { - Q_ASSERT(inPass); QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass); for (int i = 0; i < bindingCount; ++i) { QRhiBuffer *buf = bindings[i].first; @@ -801,7 +806,9 @@ void QRhiGles2::setVertexInput(QRhiCommandBuffer *cb, void QRhiGles2::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) { - Q_ASSERT(inPass); + QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass); + QGles2CommandBuffer::Command cmd; cmd.cmd = QGles2CommandBuffer::Command::Viewport; const std::array r = viewport.viewport(); @@ -811,12 +818,14 @@ void QRhiGles2::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) cmd.args.viewport.h = r[3]; cmd.args.viewport.d0 = viewport.minDepth(); cmd.args.viewport.d1 = viewport.maxDepth(); - QRHI_RES(QGles2CommandBuffer, cb)->commands.append(cmd); + cbD->commands.append(cmd); } void QRhiGles2::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) { - Q_ASSERT(inPass); + QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass); + QGles2CommandBuffer::Command cmd; cmd.cmd = QGles2CommandBuffer::Command::Scissor; const std::array r = scissor.scissor(); @@ -824,25 +833,27 @@ void QRhiGles2::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) cmd.args.scissor.y = qMax(0, r[1]); cmd.args.scissor.w = r[2]; cmd.args.scissor.h = r[3]; - QRHI_RES(QGles2CommandBuffer, cb)->commands.append(cmd); + cbD->commands.append(cmd); } void QRhiGles2::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) { - Q_ASSERT(inPass); + QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass); + QGles2CommandBuffer::Command cmd; cmd.cmd = QGles2CommandBuffer::Command::BlendConstants; cmd.args.blendConstants.r = c.redF(); cmd.args.blendConstants.g = c.greenF(); cmd.args.blendConstants.b = c.blueF(); cmd.args.blendConstants.a = c.alphaF(); - QRHI_RES(QGles2CommandBuffer, cb)->commands.append(cmd); + cbD->commands.append(cmd); } void QRhiGles2::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) { - Q_ASSERT(inPass); QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass); QGles2CommandBuffer::Command cmd; cmd.cmd = QGles2CommandBuffer::Command::StencilRef; @@ -854,10 +865,10 @@ void QRhiGles2::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) void QRhiGles2::draw(QRhiCommandBuffer *cb, quint32 vertexCount, quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) { - Q_ASSERT(inPass); Q_UNUSED(instanceCount); // no instancing Q_UNUSED(firstInstance); QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass); QGles2CommandBuffer::Command cmd; cmd.cmd = QGles2CommandBuffer::Command::Draw; @@ -870,11 +881,11 @@ void QRhiGles2::draw(QRhiCommandBuffer *cb, quint32 vertexCount, void QRhiGles2::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance) { - Q_ASSERT(inPass); Q_UNUSED(instanceCount); // no instancing Q_UNUSED(firstInstance); Q_UNUSED(vertexOffset); // no glDrawElementsBaseVertex QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass); QGles2CommandBuffer::Command cmd; cmd.cmd = QGles2CommandBuffer::Command::DrawIndexed; @@ -918,19 +929,17 @@ const QRhiNativeHandles *QRhiGles2::nativeHandles(QRhiCommandBuffer *cb) void QRhiGles2::beginExternal(QRhiCommandBuffer *cb) { - Q_ASSERT(inPass); Q_UNUSED(cb); flushCommandBuffer(); // also ensures the context is current } void QRhiGles2::endExternal(QRhiCommandBuffer *cb) { - Q_ASSERT(inPass); QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); - Q_ASSERT(cbD->currentTarget); Q_ASSERT(cbD->commands.isEmpty()); cbD->resetCachedState(); - enqueueBindFramebuffer(cbD->currentTarget, cbD); + if (cbD->currentTarget) + enqueueBindFramebuffer(cbD->currentTarget, cbD); } static void addBoundaryCommand(QGles2CommandBuffer *cb, QGles2CommandBuffer::Command::Cmd type) @@ -943,13 +952,11 @@ static void addBoundaryCommand(QGles2CommandBuffer *cb, QGles2CommandBuffer::Com QRhi::FrameOpResult QRhiGles2::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) { Q_UNUSED(flags); - Q_ASSERT(!inFrame); QGles2SwapChain *swapChainD = QRHI_RES(QGles2SwapChain, swapChain); if (!ensureContext(swapChainD->surface)) return QRhi::FrameOpError; - inFrame = true; currentSwapChain = swapChainD; QRhiProfilerPrivate *rhiP = profilerPrivateOrNull(); @@ -965,9 +972,6 @@ QRhi::FrameOpResult QRhiGles2::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginF QRhi::FrameOpResult QRhiGles2::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) { - Q_ASSERT(inFrame); - inFrame = false; - QGles2SwapChain *swapChainD = QRHI_RES(QGles2SwapChain, swapChain); Q_ASSERT(currentSwapChain == swapChainD); @@ -996,12 +1000,9 @@ QRhi::FrameOpResult QRhiGles2::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame QRhi::FrameOpResult QRhiGles2::beginOffscreenFrame(QRhiCommandBuffer **cb) { - Q_ASSERT(!inFrame); - if (!ensureContext()) return QRhi::FrameOpError; - inFrame = true; ofr.active = true; executeDeferredReleases(); @@ -1015,8 +1016,7 @@ QRhi::FrameOpResult QRhiGles2::beginOffscreenFrame(QRhiCommandBuffer **cb) QRhi::FrameOpResult QRhiGles2::endOffscreenFrame() { - Q_ASSERT(inFrame && ofr.active); - inFrame = false; + Q_ASSERT(ofr.active); ofr.active = false; addBoundaryCommand(&ofr.cbWrapper, QGles2CommandBuffer::Command::EndFrame); @@ -1031,7 +1031,6 @@ QRhi::FrameOpResult QRhiGles2::endOffscreenFrame() QRhi::FrameOpResult QRhiGles2::finish() { - Q_ASSERT(!inPass); // because that's what the QRhi docs say, even though not required by this backend return inFrame ? flushCommandBuffer() : QRhi::FrameOpSuccess; } @@ -1501,6 +1500,8 @@ static inline GLenum toGlTextureCompareFunc(QRhiSampler::CompareOp op) void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb) { QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::NoPass); + GLenum indexType = GL_UNSIGNED_SHORT; quint32 indexStride = sizeof(quint16); quint32 indexOffset = 0; @@ -1970,7 +1971,7 @@ void QRhiGles2::bindShaderResources(QRhiGraphicsPipeline *ps, QRhiShaderResource void QRhiGles2::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) { - Q_ASSERT(inFrame && !inPass); + Q_ASSERT(QRHI_RES(QGles2CommandBuffer, cb)->recordingPass == QGles2CommandBuffer::NoPass); enqueueResourceUpdates(cb, resourceUpdates); } @@ -2018,12 +2019,12 @@ void QRhiGles2::beginPass(QRhiCommandBuffer *cb, const QRhiDepthStencilClearValue &depthStencilClearValue, QRhiResourceUpdateBatch *resourceUpdates) { - Q_ASSERT(inFrame && !inPass); + QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::NoPass); if (resourceUpdates) enqueueResourceUpdates(cb, resourceUpdates); - QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); bool wantsColorClear, wantsDsClear; QGles2RenderTargetData *rtD = enqueueBindFramebuffer(rt, cbD, &wantsColorClear, &wantsDsClear); @@ -2042,17 +2043,15 @@ void QRhiGles2::beginPass(QRhiCommandBuffer *cb, clearCmd.args.clear.s = depthStencilClearValue.stencilClearValue(); cbD->commands.append(clearCmd); + cbD->recordingPass = QGles2CommandBuffer::RenderPass; cbD->currentTarget = rt; - - inPass = true; } void QRhiGles2::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) { - Q_ASSERT(inPass); - inPass = false; - QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass); + if (cbD->currentTarget->resourceType() == QRhiResource::TextureRenderTarget) { QGles2TextureRenderTarget *rtTex = QRHI_RES(QGles2TextureRenderTarget, cbD->currentTarget); const QVector colorAttachments = rtTex->m_desc.colorAttachments(); @@ -2083,12 +2082,49 @@ void QRhiGles2::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resource } } + cbD->recordingPass = QGles2CommandBuffer::NoPass; cbD->currentTarget = nullptr; if (resourceUpdates) enqueueResourceUpdates(cb, resourceUpdates); } +void QRhiGles2::beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) +{ + QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::NoPass); + + if (resourceUpdates) + enqueueResourceUpdates(cb, resourceUpdates); + + cbD->recordingPass = QGles2CommandBuffer::ComputePass; +} + +void QRhiGles2::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) +{ + QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::ComputePass); + + cbD->recordingPass = QGles2CommandBuffer::NoPass; + + if (resourceUpdates) + enqueueResourceUpdates(cb, resourceUpdates); +} + +void QRhiGles2::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps) +{ + Q_UNUSED(cb); + Q_UNUSED(ps); +} + +void QRhiGles2::dispatch(QRhiCommandBuffer *cb, int x, int y, int z) +{ + Q_UNUSED(cb); + Q_UNUSED(x); + Q_UNUSED(y); + Q_UNUSED(z); +} + QGles2Buffer::QGles2Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size) : QRhiBuffer(rhi, type, usage, size) { @@ -2742,9 +2778,9 @@ bool QGles2GraphicsPipeline::build() program = rhiD->f->glCreateProgram(); int sourceVer = 0; - for (const QRhiGraphicsShaderStage &shaderStage : qAsConst(m_shaderStages)) { - const bool isVertex = shaderStage.type() == QRhiGraphicsShaderStage::Vertex; - const bool isFragment = shaderStage.type() == QRhiGraphicsShaderStage::Fragment; + 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; @@ -2902,6 +2938,25 @@ bool QGles2GraphicsPipeline::build() return true; } +QGles2ComputePipeline::QGles2ComputePipeline(QRhiImplementation *rhi) + : QRhiComputePipeline(rhi) +{ +} + +QGles2ComputePipeline::~QGles2ComputePipeline() +{ + release(); +} + +void QGles2ComputePipeline::release() +{ +} + +bool QGles2ComputePipeline::build() +{ + return false; +} + QGles2CommandBuffer::QGles2CommandBuffer(QRhiImplementation *rhi) : QRhiCommandBuffer(rhi) { diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h index 5254219bd6..fe74e2e75b 100644 --- a/src/gui/rhi/qrhigles2_p_p.h +++ b/src/gui/rhi/qrhigles2_p_p.h @@ -248,6 +248,14 @@ struct QGles2GraphicsPipeline : public QRhiGraphicsPipeline 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; +}; + struct QGles2CommandBuffer : public QRhiCommandBuffer { QGles2CommandBuffer(QRhiImplementation *rhi); @@ -426,7 +434,14 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer } args; }; + enum PassType { + NoPass, + RenderPass, + ComputePass + }; + QVector commands; + PassType recordingPass; QRhiRenderTarget *currentTarget; QRhiGraphicsPipeline *currentPipeline; uint currentPipelineGeneration; @@ -452,6 +467,7 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer } void resetState() { resetCommands(); + recordingPass = NoPass; currentTarget = nullptr; resetCachedState(); } @@ -495,6 +511,7 @@ public: void destroy() override; QRhiGraphicsPipeline *createGraphicsPipeline() override; + QRhiComputePipeline *createComputePipeline() override; QRhiShaderResourceBindings *createShaderResourceBindings() override; QRhiBuffer *createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, @@ -559,6 +576,11 @@ public: void debugMarkEnd(QRhiCommandBuffer *cb) override; void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) override; + void beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override; + void endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override; + void setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps) override; + void dispatch(QRhiCommandBuffer *cb, int x, int y, int z) override; + const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) override; void beginExternal(QRhiCommandBuffer *cb) override; void endExternal(QRhiCommandBuffer *cb) override; @@ -645,8 +667,6 @@ public: uint uniformBuffers : 1; uint elementIndexUint : 1; } caps; - bool inFrame = false; - bool inPass = false; QGles2SwapChain *currentSwapChain = nullptr; QVector supportedCompressedFormats; mutable QVector supportedSampleCountList; diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm index 6030f55d10..22d4e4e6d2 100644 --- a/src/gui/rhi/qrhimetal.mm +++ b/src/gui/rhi/qrhimetal.mm @@ -36,6 +36,7 @@ #include "qrhimetal_p_p.h" #include "qshader_p.h" +#include "qshaderdescription_p.h" #include #include #include @@ -51,14 +52,12 @@ QT_BEGIN_NAMESPACE /* Metal backend. Double buffers and throttles to vsync. "Dynamic" buffers are - Shared (host visible) and duplicated (due to 2 frames in flight), "static" - are Managed on macOS and Shared on iOS/tvOS, and still duplicated. - "Immutable" is like "static" but with only one native buffer underneath. + Shared (host visible) and duplicated (to help having 2 frames in flight), + "static" and "immutable" are Managed on macOS and Shared on iOS/tvOS. 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 (hence uses commandBufferWithUnretainedReferences), but - does rely on automatic dependency tracking between encoders (hence no - MTLResourceHazardTrackingModeUntracked atm). + command buffers but does rely on the automatic resource tracking of the + command encoders. */ #if __has_feature(objc_arc) @@ -173,6 +172,7 @@ struct QRhiMetalData struct { id texture; id stagingBuffers[QMTL_FRAMES_IN_FLIGHT]; + id views[QRhi::MAX_LEVELS]; } texture; struct { id samplerState; @@ -213,6 +213,7 @@ Q_DECLARE_TYPEINFO(QRhiMetalData::ActiveReadback, Q_MOVABLE_TYPE); struct QMetalBufferData { bool managed; + bool slotted; id buf[QMTL_FRAMES_IN_FLIGHT]; QVector pendingUpdates[QMTL_FRAMES_IN_FLIGHT]; }; @@ -225,10 +226,16 @@ struct QMetalRenderBufferData struct QMetalTextureData { + QMetalTextureData(QMetalTexture *t) : q(t) { } + + QMetalTexture *q; MTLPixelFormat format; id tex = nil; id stagingBuf[QMTL_FRAMES_IN_FLIGHT]; bool owns = true; + id perLevelViews[QRhi::MAX_LEVELS]; + + id viewForLevel(int level); }; struct QMetalSamplerData @@ -239,7 +246,8 @@ struct QMetalSamplerData struct QMetalCommandBufferData { id cb; - id currentPassEncoder; + id currentRenderPassEncoder; + id currentComputePassEncoder; MTLRenderPassDescriptor *currentPassRpDesc; int currentFirstVertexBinding; QRhiBatchedBindings > currentVertexInputsBuffers; @@ -286,6 +294,14 @@ struct QMetalGraphicsPipelineData id fsFunc = nil; }; +struct QMetalComputePipelineData +{ + id ps = nil; + id csLib = nil; + id csFunc = nil; + MTLSize localSize; +}; + struct QMetalSwapChainData { CAMetalLayer *layer = nullptr; @@ -505,6 +521,8 @@ bool QRhiMetal::isFeatureSupported(QRhi::Feature feature) const return true; case QRhi::ElementIndexUint: return true; + case QRhi::Compute: + return true; default: Q_UNREACHABLE(); return false; @@ -573,6 +591,11 @@ QRhiGraphicsPipeline *QRhiMetal::createGraphicsPipeline() return new QMetalGraphicsPipeline(this); } +QRhiComputePipeline *QRhiMetal::createComputePipeline() +{ + return new QMetalComputePipeline(this); +} + QRhiShaderResourceBindings *QRhiMetal::createShaderResourceBindings() { return new QMetalShaderResourceBindings(this); @@ -583,7 +606,7 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD const QRhiCommandBuffer::DynamicOffset *dynamicOffsets, bool offsetOnlyChange) { - static const int KNOWN_STAGES = 2; + static const int KNOWN_STAGES = 3; struct { QRhiBatchedBindings > buffers; QRhiBatchedBindings bufferOffsets; @@ -597,7 +620,7 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD case QRhiShaderResourceBinding::UniformBuffer: { QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, b->u.ubuf.buf); - id mtlbuf = bufD->d->buf[bufD->m_type == QRhiBuffer::Immutable ? 0 : currentFrameSlot]; + id mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0]; uint offset = b->u.ubuf.offset; for (int i = 0; i < dynamicOffsetCount; ++i) { const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]); @@ -614,6 +637,10 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD res[1].buffers.feed(b->binding, mtlbuf); res[1].bufferOffsets.feed(b->binding, offset); } + if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) { + res[2].buffers.feed(b->binding, mtlbuf); + res[2].bufferOffsets.feed(b->binding, offset); + } } break; case QRhiShaderResourceBinding::SampledTexture: @@ -628,6 +655,49 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD res[1].textures.feed(b->binding, texD->d->tex); res[1].samplers.feed(b->binding, samplerD->d->samplerState); } + if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) { + res[2].textures.feed(b->binding, texD->d->tex); + res[2].samplers.feed(b->binding, samplerD->d->samplerState); + } + } + break; + case QRhiShaderResourceBinding::ImageLoad: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::ImageStore: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::ImageLoadStore: + { + QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.simage.tex); + id t = texD->d->viewForLevel(b->u.simage.level); + if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) + res[0].textures.feed(b->binding, t); + if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) + res[1].textures.feed(b->binding, t); + if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) + res[2].textures.feed(b->binding, t); + } + break; + case QRhiShaderResourceBinding::BufferLoad: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::BufferStore: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::BufferLoadStore: + { + QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, b->u.sbuf.buf); + id mtlbuf = bufD->d->buf[0]; + uint offset = b->u.sbuf.offset; + if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) { + res[0].buffers.feed(b->binding, mtlbuf); + res[0].bufferOffsets.feed(b->binding, offset); + } + if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) { + res[1].buffers.feed(b->binding, mtlbuf); + res[1].bufferOffsets.feed(b->binding, offset); + } + if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) { + res[2].buffers.feed(b->binding, mtlbuf); + res[2].bufferOffsets.feed(b->binding, offset); + } } break; default: @@ -645,12 +715,17 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD const auto &offsetBatch(res[idx].bufferOffsets.batches[i]); switch (idx) { case 0: - [cbD->d->currentPassEncoder setVertexBuffers: bufferBatch.resources.constData() + [cbD->d->currentRenderPassEncoder setVertexBuffers: bufferBatch.resources.constData() offsets: offsetBatch.resources.constData() withRange: NSMakeRange(bufferBatch.startBinding, bufferBatch.resources.count())]; break; case 1: - [cbD->d->currentPassEncoder setFragmentBuffers: bufferBatch.resources.constData() + [cbD->d->currentRenderPassEncoder setFragmentBuffers: bufferBatch.resources.constData() + offsets: offsetBatch.resources.constData() + withRange: NSMakeRange(bufferBatch.startBinding, bufferBatch.resources.count())]; + break; + case 2: + [cbD->d->currentComputePassEncoder setBuffers: bufferBatch.resources.constData() offsets: offsetBatch.resources.constData() withRange: NSMakeRange(bufferBatch.startBinding, bufferBatch.resources.count())]; break; @@ -670,11 +745,15 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD const auto &batch(res[idx].textures.batches[i]); switch (idx) { case 0: - [cbD->d->currentPassEncoder setVertexTextures: batch.resources.constData() + [cbD->d->currentRenderPassEncoder setVertexTextures: batch.resources.constData() withRange: NSMakeRange(batch.startBinding, batch.resources.count())]; break; case 1: - [cbD->d->currentPassEncoder setFragmentTextures: batch.resources.constData() + [cbD->d->currentRenderPassEncoder setFragmentTextures: batch.resources.constData() + withRange: NSMakeRange(batch.startBinding, batch.resources.count())]; + break; + case 2: + [cbD->d->currentComputePassEncoder setTextures: batch.resources.constData() withRange: NSMakeRange(batch.startBinding, batch.resources.count())]; break; default: @@ -686,11 +765,15 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD const auto &batch(res[idx].samplers.batches[i]); switch (idx) { case 0: - [cbD->d->currentPassEncoder setVertexSamplerStates: batch.resources.constData() + [cbD->d->currentRenderPassEncoder setVertexSamplerStates: batch.resources.constData() withRange: NSMakeRange(batch.startBinding, batch.resources.count())]; break; case 1: - [cbD->d->currentPassEncoder setFragmentSamplerStates: batch.resources.constData() + [cbD->d->currentRenderPassEncoder setFragmentSamplerStates: batch.resources.constData() + withRange: NSMakeRange(batch.startBinding, batch.resources.count())]; + break; + case 2: + [cbD->d->currentComputePassEncoder setSamplerStates: batch.resources.constData() withRange: NSMakeRange(batch.startBinding, batch.resources.count())]; break; default: @@ -703,19 +786,19 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD void QRhiMetal::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps) { - Q_ASSERT(inPass); - QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::RenderPass); QMetalGraphicsPipeline *psD = QRHI_RES(QMetalGraphicsPipeline, ps); - if (cbD->currentPipeline != ps || cbD->currentPipelineGeneration != psD->generation) { - cbD->currentPipeline = ps; + if (cbD->currentGraphicsPipeline != ps || cbD->currentPipelineGeneration != psD->generation) { + cbD->currentGraphicsPipeline = ps; + cbD->currentComputePipeline = nullptr; cbD->currentPipelineGeneration = psD->generation; - [cbD->d->currentPassEncoder setRenderPipelineState: psD->d->ps]; - [cbD->d->currentPassEncoder setDepthStencilState: psD->d->ds]; - [cbD->d->currentPassEncoder setCullMode: psD->d->cullMode]; - [cbD->d->currentPassEncoder setFrontFacingWinding: psD->d->winding]; + [cbD->d->currentRenderPassEncoder setRenderPipelineState: psD->d->ps]; + [cbD->d->currentRenderPassEncoder setDepthStencilState: psD->d->ds]; + [cbD->d->currentRenderPassEncoder setCullMode: psD->d->cullMode]; + [cbD->d->currentRenderPassEncoder setFrontFacingWinding: psD->d->winding]; } psD->lastActiveFrameSlot = currentFrameSlot; @@ -725,12 +808,17 @@ void QRhiMetal::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind int dynamicOffsetCount, const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) { - Q_ASSERT(inPass); - QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); - Q_ASSERT(cbD->currentPipeline); - if (!srb) - srb = QRHI_RES(QMetalGraphicsPipeline, cbD->currentPipeline)->m_shaderResourceBindings; + Q_ASSERT(cbD->recordingPass != QMetalCommandBuffer::NoPass); + QMetalGraphicsPipeline *gfxPsD = QRHI_RES(QMetalGraphicsPipeline, cbD->currentGraphicsPipeline); + QMetalComputePipeline *compPsD = QRHI_RES(QMetalComputePipeline, cbD->currentComputePipeline); + + if (!srb) { + if (gfxPsD) + srb = gfxPsD->m_shaderResourceBindings; + else + srb = compPsD->m_shaderResourceBindings; + } QMetalShaderResourceBindings *srbD = QRHI_RES(QMetalShaderResourceBindings, srb); bool hasSlottedResourceInSrb = false; @@ -747,7 +835,7 @@ void QRhiMetal::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, b->u.ubuf.buf); Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)); executeBufferHostWritesForCurrentFrame(bufD); - if (bufD->m_type != QRhiBuffer::Immutable) + if (bufD->d->slotted) hasSlottedResourceInSrb = true; if (b->u.ubuf.hasDynamicOffset) hasDynamicOffsetInSrb = true; @@ -778,6 +866,38 @@ void QRhiMetal::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind samplerD->lastActiveFrameSlot = currentFrameSlot; } break; + case QRhiShaderResourceBinding::ImageLoad: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::ImageStore: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::ImageLoadStore: + { + QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.simage.tex); + if (texD->generation != bd.simage.generation || texD->m_id != bd.simage.id) { + resNeedsRebind = true; + bd.simage.id = texD->m_id; + bd.simage.generation = texD->generation; + } + texD->lastActiveFrameSlot = currentFrameSlot; + } + break; + case QRhiShaderResourceBinding::BufferLoad: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::BufferStore: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::BufferLoadStore: + { + QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, b->u.sbuf.buf); + Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::StorageBuffer)); + executeBufferHostWritesForCurrentFrame(bufD); + if (bufD->generation != bd.sbuf.generation || bufD->m_id != bd.sbuf.id) { + resNeedsRebind = true; + bd.sbuf.id = bufD->m_id; + bd.sbuf.generation = bufD->generation; + } + bufD->lastActiveFrameSlot = currentFrameSlot; + } + break; default: Q_UNREACHABLE(); break; @@ -789,15 +909,22 @@ void QRhiMetal::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind if (hasSlottedResourceInSrb && cbD->currentResSlot != resSlot) resNeedsRebind = true; - const bool srbChange = cbD->currentSrb != srb || cbD->currentSrbGeneration != srbD->generation; + const bool srbChanged = gfxPsD ? (cbD->currentGraphicsSrb != srb) : (cbD->currentComputeSrb != srb); + const bool srbRebuilt = cbD->currentSrbGeneration != srbD->generation; // dynamic uniform buffer offsets always trigger a rebind - if (hasDynamicOffsetInSrb || resNeedsRebind || srbChange) { - cbD->currentSrb = srb; + if (hasDynamicOffsetInSrb || resNeedsRebind || srbChanged || srbRebuilt) { + if (gfxPsD) { + cbD->currentGraphicsSrb = srb; + cbD->currentComputeSrb = nullptr; + } else { + cbD->currentGraphicsSrb = nullptr; + cbD->currentComputeSrb = srb; + } cbD->currentSrbGeneration = srbD->generation; cbD->currentResSlot = resSlot; - const bool offsetOnlyChange = hasDynamicOffsetInSrb && !resNeedsRebind && !srbChange; + const bool offsetOnlyChange = hasDynamicOffsetInSrb && !resNeedsRebind && !srbChanged && !srbRebuilt; enqueueShaderResourceBindings(srbD, cbD, dynamicOffsetCount, dynamicOffsets, offsetOnlyChange); } } @@ -806,9 +933,8 @@ void QRhiMetal::setVertexInput(QRhiCommandBuffer *cb, int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings, QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat) { - Q_ASSERT(inPass); QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); - Q_ASSERT(cbD->currentPipeline); + Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::RenderPass); QRhiBatchedBindings > buffers; QRhiBatchedBindings offsets; @@ -816,7 +942,7 @@ void QRhiMetal::setVertexInput(QRhiCommandBuffer *cb, QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, bindings[i].first); executeBufferHostWritesForCurrentFrame(bufD); bufD->lastActiveFrameSlot = currentFrameSlot; - id mtlbuf = bufD->d->buf[bufD->m_type == QRhiBuffer::Immutable ? 0 : currentFrameSlot]; + id mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0]; buffers.feed(startBinding + i, mtlbuf); offsets.feed(startBinding + i, bindings[i].second); } @@ -824,12 +950,12 @@ void QRhiMetal::setVertexInput(QRhiCommandBuffer *cb, offsets.finish(); // same binding space for vertex and constant buffers - work it around - QRhiShaderResourceBindings *srb = cbD->currentSrb; + QRhiShaderResourceBindings *srb = cbD->currentGraphicsSrb; // There's nothing guaranteeing setShaderResources() was called before // setVertexInput()... but whatever srb will get bound will have to be // layout-compatible anyways so maxBinding is the same. if (!srb) - srb = cbD->currentPipeline->shaderResourceBindings(); + srb = cbD->currentGraphicsPipeline->shaderResourceBindings(); const int firstVertexBinding = QRHI_RES(QMetalShaderResourceBindings, srb)->maxBinding + 1; if (firstVertexBinding != cbD->d->currentFirstVertexBinding @@ -843,7 +969,7 @@ void QRhiMetal::setVertexInput(QRhiCommandBuffer *cb, for (int i = 0, ie = buffers.batches.count(); i != ie; ++i) { const auto &bufferBatch(buffers.batches[i]); const auto &offsetBatch(offsets.batches[i]); - [cbD->d->currentPassEncoder setVertexBuffers: + [cbD->d->currentRenderPassEncoder setVertexBuffers: bufferBatch.resources.constData() offsets: offsetBatch.resources.constData() withRange: NSMakeRange(firstVertexBinding + bufferBatch.startBinding, bufferBatch.resources.count())]; @@ -864,9 +990,8 @@ void QRhiMetal::setVertexInput(QRhiCommandBuffer *cb, void QRhiMetal::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) { - Q_ASSERT(inPass); QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); - Q_ASSERT(cbD->currentPipeline && cbD->currentTarget); + Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::RenderPass); const QSize outputSize = cbD->currentTarget->pixelSize(); // x,y is top-left in MTLViewportRect but bottom-left in QRhiViewport @@ -882,24 +1007,23 @@ void QRhiMetal::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) vp.znear = viewport.minDepth(); vp.zfar = viewport.maxDepth(); - [cbD->d->currentPassEncoder setViewport: vp]; + [cbD->d->currentRenderPassEncoder setViewport: vp]; - if (!QRHI_RES(QMetalGraphicsPipeline, cbD->currentPipeline)->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)) { + if (!QRHI_RES(QMetalGraphicsPipeline, cbD->currentGraphicsPipeline)->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)) { MTLScissorRect s; s.x = x; s.y = y; s.width = w; s.height = h; - [cbD->d->currentPassEncoder setScissorRect: s]; + [cbD->d->currentRenderPassEncoder setScissorRect: s]; } } void QRhiMetal::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) { - Q_ASSERT(inPass); QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); - Q_ASSERT(cbD->currentPipeline && cbD->currentTarget); - Q_ASSERT(QRHI_RES(QMetalGraphicsPipeline, cbD->currentPipeline)->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)); + Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::RenderPass); + Q_ASSERT(QRHI_RES(QMetalGraphicsPipeline, cbD->currentGraphicsPipeline)->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)); const QSize outputSize = cbD->currentTarget->pixelSize(); // x,y is top-left in MTLScissorRect but bottom-left in QRhiScissor @@ -913,38 +1037,42 @@ void QRhiMetal::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) s.width = w; s.height = h; - [cbD->d->currentPassEncoder setScissorRect: s]; + [cbD->d->currentRenderPassEncoder setScissorRect: s]; } void QRhiMetal::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) { - Q_ASSERT(inPass); QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); - [cbD->d->currentPassEncoder setBlendColorRed: c.redF() green: c.greenF() blue: c.blueF() alpha: c.alphaF()]; + Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::RenderPass); + + [cbD->d->currentRenderPassEncoder setBlendColorRed: c.redF() green: c.greenF() blue: c.blueF() alpha: c.alphaF()]; } void QRhiMetal::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) { - Q_ASSERT(inPass); QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); - [cbD->d->currentPassEncoder setStencilReferenceValue: refValue]; + Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::RenderPass); + + [cbD->d->currentRenderPassEncoder setStencilReferenceValue: refValue]; } void QRhiMetal::draw(QRhiCommandBuffer *cb, quint32 vertexCount, quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) { - Q_ASSERT(inPass); QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); - [cbD->d->currentPassEncoder drawPrimitives: - QRHI_RES(QMetalGraphicsPipeline, cbD->currentPipeline)->d->primitiveType + Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::RenderPass); + + [cbD->d->currentRenderPassEncoder drawPrimitives: + QRHI_RES(QMetalGraphicsPipeline, cbD->currentGraphicsPipeline)->d->primitiveType vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount baseInstance: firstInstance]; } void QRhiMetal::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance) { - Q_ASSERT(inPass); QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::RenderPass); + if (!cbD->currentIndexBuffer) return; @@ -952,9 +1080,9 @@ void QRhiMetal::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, Q_ASSERT(indexOffset == aligned(indexOffset, 4)); QMetalBuffer *ibufD = QRHI_RES(QMetalBuffer, cbD->currentIndexBuffer); - id mtlbuf = ibufD->d->buf[ibufD->m_type == QRhiBuffer::Immutable ? 0 : currentFrameSlot]; + id mtlbuf = ibufD->d->buf[ibufD->d->slotted ? currentFrameSlot : 0]; - [cbD->d->currentPassEncoder drawIndexedPrimitives: QRHI_RES(QMetalGraphicsPipeline, cbD->currentPipeline)->d->primitiveType + [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: QRHI_RES(QMetalGraphicsPipeline, cbD->currentGraphicsPipeline)->d->primitiveType indexCount: indexCount indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32 indexBuffer: mtlbuf @@ -971,8 +1099,8 @@ void QRhiMetal::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) NSString *str = [NSString stringWithUTF8String: name.constData()]; QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); - if (inPass) { - [cbD->d->currentPassEncoder pushDebugGroup: str]; + if (cbD->recordingPass != QMetalCommandBuffer::NoPass) { + [cbD->d->currentRenderPassEncoder pushDebugGroup: str]; } else { if (@available(macOS 10.13, iOS 11.0, *)) [cbD->d->cb pushDebugGroup: str]; @@ -985,8 +1113,8 @@ void QRhiMetal::debugMarkEnd(QRhiCommandBuffer *cb) return; QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); - if (inPass) { - [cbD->d->currentPassEncoder popDebugGroup]; + if (cbD->recordingPass != QMetalCommandBuffer::NoPass) { + [cbD->d->currentRenderPassEncoder popDebugGroup]; } else { if (@available(macOS 10.13, iOS 11.0, *)) [cbD->d->cb popDebugGroup]; @@ -998,10 +1126,9 @@ void QRhiMetal::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) if (!debugMarkers) return; - if (inPass) { - QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); - [cbD->d->currentPassEncoder insertDebugSignpost: [NSString stringWithUTF8String: msg.constData()]]; - } + QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); + if (cbD->recordingPass != QMetalCommandBuffer::NoPass) + [cbD->d->currentRenderPassEncoder insertDebugSignpost: [NSString stringWithUTF8String: msg.constData()]]; } const QRhiNativeHandles *QRhiMetal::nativeHandles(QRhiCommandBuffer *cb) @@ -1023,8 +1150,6 @@ void QRhiMetal::endExternal(QRhiCommandBuffer *cb) QRhi::FrameOpResult QRhiMetal::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) { Q_UNUSED(flags); - Q_ASSERT(!inFrame); - inFrame = true; QMetalSwapChain *swapChainD = QRHI_RES(QMetalSwapChain, swapChain); @@ -1077,9 +1202,6 @@ QRhi::FrameOpResult QRhiMetal::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginF QRhi::FrameOpResult QRhiMetal::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) { - Q_ASSERT(inFrame); - inFrame = false; - QMetalSwapChain *swapChainD = QRHI_RES(QMetalSwapChain, swapChain); Q_ASSERT(currentSwapChain == swapChainD); @@ -1110,9 +1232,6 @@ QRhi::FrameOpResult QRhiMetal::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame QRhi::FrameOpResult QRhiMetal::beginOffscreenFrame(QRhiCommandBuffer **cb) { - Q_ASSERT(!inFrame); - inFrame = true; - currentFrameSlot = (currentFrameSlot + 1) % QMTL_FRAMES_IN_FLIGHT; if (swapchains.count() > 1) { for (QMetalSwapChain *sc : qAsConst(swapchains)) { @@ -1140,8 +1259,6 @@ QRhi::FrameOpResult QRhiMetal::endOffscreenFrame() { Q_ASSERT(d->ofr.active); d->ofr.active = false; - Q_ASSERT(inFrame); - inFrame = false; [d->ofr.cbWrapper.d->cb commit]; @@ -1155,17 +1272,17 @@ QRhi::FrameOpResult QRhiMetal::endOffscreenFrame() QRhi::FrameOpResult QRhiMetal::finish() { - Q_ASSERT(!inPass); - id cb = nil; QMetalSwapChain *swapChainD = nullptr; if (inFrame) { if (d->ofr.active) { Q_ASSERT(!currentSwapChain); + Q_ASSERT(d->ofr.cbWrapper.recordingPass == QMetalCommandBuffer::NoPass); cb = d->ofr.cbWrapper.d->cb; } else { Q_ASSERT(currentSwapChain); swapChainD = currentSwapChain; + Q_ASSERT(swapChainD->cbWrapper.recordingPass == QMetalCommandBuffer::NoPass); cb = swapChainD->cbWrapper.d->cb; } } @@ -1373,11 +1490,13 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate bufD->d->pendingUpdates[i].append(u); } + // Due to the Metal API the handling of static and dynamic buffers is + // basically the same. So go through the same pendingUpdates machinery. for (const QRhiResourceUpdateBatchPrivate::StaticBufferUpload &u : ud->staticBufferUploads) { QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, u.buf); Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic); Q_ASSERT(u.offset + u.data.size() <= bufD->m_size); - for (int i = 0, ie = bufD->m_type == QRhiBuffer::Immutable ? 1 : QMTL_FRAMES_IN_FLIGHT; i != ie; ++i) + for (int i = 0, ie = bufD->d->slotted ? QMTL_FRAMES_IN_FLIGHT : 1; i != ie; ++i) bufD->d->pendingUpdates[i].append({ u.buf, u.offset, u.data.size(), u.data.constData() }); } @@ -1516,9 +1635,10 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate ud->free(); } +// this handles all types of buffers, not just Dynamic void QRhiMetal::executeBufferHostWritesForCurrentFrame(QMetalBuffer *bufD) { - const int idx = bufD->m_type == QRhiBuffer::Immutable ? 0 : currentFrameSlot; + const int idx = bufD->d->slotted ? currentFrameSlot : 0; QVector &updates(bufD->d->pendingUpdates[idx]); if (updates.isEmpty()) return; @@ -1542,7 +1662,7 @@ void QRhiMetal::executeBufferHostWritesForCurrentFrame(QMetalBuffer *bufD) void QRhiMetal::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) { - Q_ASSERT(inFrame && !inPass); + Q_ASSERT(QRHI_RES(QMetalCommandBuffer, cb)->recordingPass == QMetalCommandBuffer::NoPass); enqueueResourceUpdates(cb, resourceUpdates); } @@ -1553,13 +1673,12 @@ void QRhiMetal::beginPass(QRhiCommandBuffer *cb, const QRhiDepthStencilClearValue &depthStencilClearValue, QRhiResourceUpdateBatch *resourceUpdates) { - Q_ASSERT(inFrame && !inPass); + QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::NoPass); if (resourceUpdates) enqueueResourceUpdates(cb, resourceUpdates); - QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); - QMetalRenderTargetData *rtD = nullptr; switch (rt->resourceType()) { case QRhiResource::RenderTarget: @@ -1639,28 +1758,80 @@ void QRhiMetal::beginPass(QRhiCommandBuffer *cb, cbD->d->currentPassRpDesc.depthAttachment.storeAction = MTLStoreActionStore; } - cbD->d->currentPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc]; + cbD->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc]; cbD->resetPerPassState(); + cbD->recordingPass = QMetalCommandBuffer::RenderPass; cbD->currentTarget = rt; - inPass = true; } void QRhiMetal::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) { - Q_ASSERT(inPass); - inPass = false; - QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); - [cbD->d->currentPassEncoder endEncoding]; + Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::RenderPass); + [cbD->d->currentRenderPassEncoder endEncoding]; + + cbD->recordingPass = QMetalCommandBuffer::NoPass; cbD->currentTarget = nullptr; if (resourceUpdates) enqueueResourceUpdates(cb, resourceUpdates); } +void QRhiMetal::beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) +{ + QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::NoPass); + + if (resourceUpdates) + enqueueResourceUpdates(cb, resourceUpdates); + + cbD->d->currentComputePassEncoder = [cbD->d->cb computeCommandEncoder]; + cbD->resetPerPassState(); + cbD->recordingPass = QMetalCommandBuffer::ComputePass; +} + +void QRhiMetal::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) +{ + QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::ComputePass); + + [cbD->d->currentComputePassEncoder endEncoding]; + cbD->recordingPass = QMetalCommandBuffer::NoPass; + + if (resourceUpdates) + enqueueResourceUpdates(cb, resourceUpdates); +} + +void QRhiMetal::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps) +{ + QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::ComputePass); + QMetalComputePipeline *psD = QRHI_RES(QMetalComputePipeline, ps); + + if (cbD->currentComputePipeline != ps || cbD->currentPipelineGeneration != psD->generation) { + cbD->currentGraphicsPipeline = nullptr; + cbD->currentComputePipeline = ps; + cbD->currentPipelineGeneration = psD->generation; + + [cbD->d->currentComputePassEncoder setComputePipelineState: psD->d->ps]; + } + + psD->lastActiveFrameSlot = currentFrameSlot; +} + +void QRhiMetal::dispatch(QRhiCommandBuffer *cb, int x, int y, int z) +{ + QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::ComputePass); + QMetalComputePipeline *psD = QRHI_RES(QMetalComputePipeline, cbD->currentComputePipeline); + + [cbD->d->currentComputePassEncoder dispatchThreadgroups: MTLSizeMake(x, y, z) + threadsPerThreadgroup: psD->d->localSize]; +} + static void qrhimtl_releaseBuffer(const QRhiMetalData::DeferredReleaseEntry &e) { for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) @@ -1677,6 +1848,8 @@ static void qrhimtl_releaseTexture(const QRhiMetalData::DeferredReleaseEntry &e) [e.texture.texture release]; for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) [e.texture.stagingBuffers[i] release]; + for (int i = 0; i < QRhi::MAX_LEVELS; ++i) + [e.texture.views[i] release]; } static void qrhimtl_releaseSampler(const QRhiMetalData::DeferredReleaseEntry &e) @@ -1782,6 +1955,11 @@ bool QMetalBuffer::build() if (d->buf[0]) release(); + if (m_usage.testFlag(QRhiBuffer::StorageBuffer) && m_type == Dynamic) { + qWarning("StorageBuffer cannot be combined with Dynamic"); + return false; + } + const int nonZeroSize = m_size <= 0 ? 256 : m_size; const int roundedSize = m_usage.testFlag(QRhiBuffer::UniformBuffer) ? aligned(nonZeroSize, 256) : nonZeroSize; @@ -1794,15 +1972,17 @@ bool QMetalBuffer::build() } #endif + // Immutable and Static only has buf[0] and pendingUpdates[0] in use. + // Dynamic uses all. + d->slotted = m_type == Dynamic; + QRHI_RES_RHI(QRhiMetal); for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) { - // Immutable only has buf[0] and pendingUpdates[0] in use. - // Static and Dynamic use all. - if (i == 0 || m_type != Immutable) { + if (i == 0 || d->slotted) { d->buf[i] = [rhiD->d->dev newBufferWithLength: roundedSize options: opts]; d->pendingUpdates[i].reserve(16); if (!m_objectName.isEmpty()) { - if (m_type == Immutable) { + if (!d->slotted) { d->buf[i].label = [NSString stringWithUTF8String: m_objectName.constData()]; } else { const QByteArray name = m_objectName + '/' + QByteArray::number(i); @@ -1813,7 +1993,7 @@ bool QMetalBuffer::build() } QRHI_PROF; - QRHI_PROF_F(newBuffer(this, roundedSize, m_type == Immutable ? 1 : QMTL_FRAMES_IN_FLIGHT, 0)); + QRHI_PROF_F(newBuffer(this, roundedSize, d->slotted ? QMTL_FRAMES_IN_FLIGHT : 1, 0)); lastActiveFrameSlot = -1; generation += 1; @@ -1919,10 +2099,13 @@ QRhiTexture::Format QMetalRenderBuffer::backingFormat() const QMetalTexture::QMetalTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int sampleCount, Flags flags) : QRhiTexture(rhi, format, pixelSize, sampleCount, flags), - d(new QMetalTextureData) + d(new QMetalTextureData(this)) { for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) d->stagingBuf[i] = nil; + + for (int i = 0; i < QRhi::MAX_LEVELS; ++i) + d->perLevelViews[i] = nil; } QMetalTexture::~QMetalTexture() @@ -1949,6 +2132,11 @@ void QMetalTexture::release() d->stagingBuf[i] = nil; } + for (int i = 0; i < QRhi::MAX_LEVELS; ++i) { + e.texture.views[i] = d->perLevelViews[i]; + d->perLevelViews[i] = nil; + } + QRHI_RES_RHI(QRhiMetal); rhiD->d->releaseQueue.append(e); QRHI_PROF; @@ -2138,6 +2326,8 @@ bool QMetalTexture::build() desc.usage = MTLTextureUsageShaderRead; if (m_flags.testFlag(RenderTarget)) desc.usage |= MTLTextureUsageRenderTarget; + if (m_flags.testFlag(UsedWithLoadStore)) + desc.usage |= MTLTextureUsageShaderWrite; QRHI_RES_RHI(QRhiMetal); d->tex = [rhiD->d->dev newTextureWithDescriptor: desc]; @@ -2187,6 +2377,21 @@ const QRhiNativeHandles *QMetalTexture::nativeHandles() return &nativeHandlesStruct; } +id QMetalTextureData::viewForLevel(int level) +{ + Q_ASSERT(level >= 0 && level < int(q->mipLevelCount)); + if (perLevelViews[level]) + return perLevelViews[level]; + + const MTLTextureType type = [tex textureType]; + const bool isCube = q->m_flags.testFlag(QRhiTexture::CubeMap); + id view = [tex newTextureViewWithPixelFormat: format textureType: type + levels: NSMakeRange(level, 1) slices: NSMakeRange(0, isCube ? 6 : 1)]; + + perLevelViews[level] = view; + return view; +} + QMetalSampler::QMetalSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode, AddressMode u, AddressMode v) : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v), @@ -2538,6 +2743,28 @@ bool QMetalShaderResourceBindings::build() bd.stex.samplerGeneration = samplerD->generation; } break; + case QRhiShaderResourceBinding::ImageLoad: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::ImageStore: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::ImageLoadStore: + { + QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.simage.tex); + bd.simage.id = texD->m_id; + bd.simage.generation = texD->generation; + } + break; + case QRhiShaderResourceBinding::BufferLoad: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::BufferStore: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::BufferLoadStore: + { + QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, b->u.sbuf.buf); + bd.sbuf.id = bufD->m_id; + bd.sbuf.generation = bufD->generation; + } + break; default: Q_UNREACHABLE(); break; @@ -2874,21 +3101,12 @@ bool QMetalGraphicsPipeline::build() rpDesc.vertexDescriptor = inputLayout; - if (@available(macOS 10.13, iOS 11.0, *)) { - // Everything is immutable because we can guarantee that "neither the - // CPU nor the GPU will modify a buffer's contents between the time the - // buffer is set in a function's argument table and the time its - // associated command buffer completes execution" (as that's the point - // of our Vulkan-style buffer juggling in the first place). - const int vertexBufferCount = firstVertexBinding + bindings.count(); // cbuf + vbuf - const int fragmentBufferCount = firstVertexBinding; // cbuf - for (int i = 0; i < vertexBufferCount; ++i) - rpDesc.vertexBuffers[i].mutability = MTLMutabilityImmutable; - for (int i = 0; i < fragmentBufferCount; ++i) - rpDesc.fragmentBuffers[i].mutability = MTLMutabilityImmutable; - } - - for (const QRhiGraphicsShaderStage &shaderStage : qAsConst(m_shaderStages)) { + // mutability cannot be determined (slotted buffers could be set as + // MTLMutabilityImmutable, but then we potentially need a different + // descriptor for each buffer combination as this depends on the actual + // buffers not just the resource binding layout) so leave it at the default + + for (const QRhiShaderStage &shaderStage : qAsConst(m_shaderStages)) { QString error; QByteArray entryPoint; id lib = rhiD->d->createMetalLib(shaderStage.shader(), shaderStage.shaderVariant(), &error, &entryPoint); @@ -2903,12 +3121,12 @@ bool QMetalGraphicsPipeline::build() return false; } switch (shaderStage.type()) { - case QRhiGraphicsShaderStage::Vertex: + case QRhiShaderStage::Vertex: rpDesc.vertexFunction = func; d->vsLib = lib; d->vsFunc = func; break; - case QRhiGraphicsShaderStage::Fragment: + case QRhiShaderStage::Fragment: rpDesc.fragmentFunction = func; d->fsLib = lib; d->fsFunc = func; @@ -3000,6 +3218,83 @@ bool QMetalGraphicsPipeline::build() return true; } +QMetalComputePipeline::QMetalComputePipeline(QRhiImplementation *rhi) + : QRhiComputePipeline(rhi), + d(new QMetalComputePipelineData) +{ +} + +QMetalComputePipeline::~QMetalComputePipeline() +{ + release(); + delete d; +} + +void QMetalComputePipeline::release() +{ + QRHI_RES_RHI(QRhiMetal); + + if (d->csFunc) { + [d->csFunc release]; + d->csFunc = nil; + } + if (d->csLib) { + [d->csLib release]; + d->csLib = nil; + } + + if (!d->ps) + return; + + if (d->ps) { + [d->ps release]; + d->ps = nil; + } + + rhiD->unregisterResource(this); +} + +bool QMetalComputePipeline::build() +{ + if (d->ps) + release(); + + QRHI_RES_RHI(QRhiMetal); + + const QShader shader = m_shaderStage.shader(); + QString error; + QByteArray entryPoint; + id lib = rhiD->d->createMetalLib(shader, m_shaderStage.shaderVariant(), + &error, &entryPoint); + if (!lib) { + qWarning("MSL shader compilation failed: %s", qPrintable(error)); + return false; + } + id func = rhiD->d->createMSLShaderFunction(lib, entryPoint); + if (!func) { + qWarning("MSL function for entry point %s not found", entryPoint.constData()); + [lib release]; + return false; + } + d->csLib = lib; + d->csFunc = func; + std::array localSize = shader.description().computeShaderLocalSize(); + d->localSize = MTLSizeMake(localSize[0], localSize[1], localSize[2]); + + NSError *err = nil; + d->ps = [rhiD->d->dev newComputePipelineStateWithFunction: d->csFunc error: &err]; + if (!d->ps) { + const QString msg = QString::fromNSString(err.localizedDescription); + qWarning("Failed to create render pipeline state: %s", qPrintable(msg)); + return false; + } + + lastActiveFrameSlot = -1; + generation += 1; + rhiD->registerResource(this); + return true; +} + QMetalCommandBuffer::QMetalCommandBuffer(QRhiImplementation *rhi) : QRhiCommandBuffer(rhi), d(new QMetalCommandBufferData) @@ -3021,28 +3316,32 @@ void QMetalCommandBuffer::release() const QRhiNativeHandles *QMetalCommandBuffer::nativeHandles() { nativeHandlesStruct.commandBuffer = d->cb; - nativeHandlesStruct.encoder = d->currentPassEncoder; + nativeHandlesStruct.encoder = d->currentRenderPassEncoder; return &nativeHandlesStruct; } void QMetalCommandBuffer::resetState() { - d->currentPassEncoder = nil; + d->currentRenderPassEncoder = nil; + d->currentComputePassEncoder = nil; d->currentPassRpDesc = nil; resetPerPassState(); } void QMetalCommandBuffer::resetPerPassState() { + recordingPass = NoPass; currentTarget = nullptr; resetPerPassCachedState(); } void QMetalCommandBuffer::resetPerPassCachedState() { - currentPipeline = nullptr; + currentGraphicsPipeline = nullptr; + currentComputePipeline = nullptr; currentPipelineGeneration = 0; - currentSrb = nullptr; + currentGraphicsSrb = nullptr; + currentComputeSrb = nullptr; currentSrbGeneration = 0; currentResSlot = -1; currentIndexBuffer = nullptr; diff --git a/src/gui/rhi/qrhimetal_p_p.h b/src/gui/rhi/qrhimetal_p_p.h index f9b9d96648..8b0256991d 100644 --- a/src/gui/rhi/qrhimetal_p_p.h +++ b/src/gui/rhi/qrhimetal_p_p.h @@ -113,6 +113,7 @@ struct QMetalTexture : public QRhiTexture int lastActiveFrameSlot = -1; friend class QRhiMetal; friend struct QMetalShaderResourceBindings; + friend struct QMetalTextureData; }; struct QMetalSamplerData; @@ -200,10 +201,20 @@ struct QMetalShaderResourceBindings : public QRhiShaderResourceBindings quint64 samplerId; uint samplerGeneration; }; + struct BoundStorageImageData { + quint64 id; + uint generation; + }; + struct BoundStorageBufferData { + quint64 id; + uint generation; + }; struct BoundResourceData { union { BoundUniformBufferData ubuf; BoundSampledTextureData stex; + BoundStorageImageData simage; + BoundStorageBufferData sbuf; }; }; QVector boundResourceData; @@ -227,6 +238,21 @@ struct QMetalGraphicsPipeline : public QRhiGraphicsPipeline friend class QRhiMetal; }; +struct QMetalComputePipelineData; + +struct QMetalComputePipeline : public QRhiComputePipeline +{ + QMetalComputePipeline(QRhiImplementation *rhi); + ~QMetalComputePipeline(); + void release() override; + bool build() override; + + QMetalComputePipelineData *d; + uint generation = 0; + int lastActiveFrameSlot = -1; + friend class QRhiMetal; +}; + struct QMetalCommandBufferData; struct QMetalSwapChain; @@ -239,10 +265,19 @@ struct QMetalCommandBuffer : public QRhiCommandBuffer QMetalCommandBufferData *d = nullptr; QRhiMetalCommandBufferNativeHandles nativeHandlesStruct; + enum PassType { + NoPass, + RenderPass, + ComputePass + }; + + PassType recordingPass; QRhiRenderTarget *currentTarget; - QRhiGraphicsPipeline *currentPipeline; + QRhiGraphicsPipeline *currentGraphicsPipeline; + QRhiComputePipeline *currentComputePipeline; uint currentPipelineGeneration; - QRhiShaderResourceBindings *currentSrb; + QRhiShaderResourceBindings *currentGraphicsSrb; + QRhiShaderResourceBindings *currentComputeSrb; uint currentSrbGeneration; int currentResSlot; QRhiBuffer *currentIndexBuffer; @@ -296,6 +331,7 @@ public: void destroy() override; QRhiGraphicsPipeline *createGraphicsPipeline() override; + QRhiComputePipeline *createComputePipeline() override; QRhiShaderResourceBindings *createShaderResourceBindings() override; QRhiBuffer *createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, @@ -360,6 +396,11 @@ public: void debugMarkEnd(QRhiCommandBuffer *cb) override; void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) override; + void beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override; + void endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override; + void setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps) override; + void dispatch(QRhiCommandBuffer *cb, int x, int y, int z) override; + const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) override; void beginExternal(QRhiCommandBuffer *cb) override; void endExternal(QRhiCommandBuffer *cb) override; @@ -393,8 +434,6 @@ public: bool importedDevice = false; bool importedCmdQueue = false; - bool inFrame = false; - bool inPass = false; QMetalSwapChain *currentSwapChain = nullptr; QSet swapchains; QRhiMetalNativeHandles nativeHandlesStruct; diff --git a/src/gui/rhi/qrhinull.cpp b/src/gui/rhi/qrhinull.cpp index c764669058..1314e53893 100644 --- a/src/gui/rhi/qrhinull.cpp +++ b/src/gui/rhi/qrhinull.cpp @@ -201,6 +201,11 @@ QRhiGraphicsPipeline *QRhiNull::createGraphicsPipeline() return new QNullGraphicsPipeline(this); } +QRhiComputePipeline *QRhiNull::createComputePipeline() +{ + return new QNullComputePipeline(this); +} + QRhiShaderResourceBindings *QRhiNull::createShaderResourceBindings() { return new QNullShaderResourceBindings(this); @@ -297,6 +302,20 @@ void QRhiNull::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) Q_UNUSED(msg); } +void QRhiNull::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps) +{ + Q_UNUSED(cb); + Q_UNUSED(ps); +} + +void QRhiNull::dispatch(QRhiCommandBuffer *cb, int x, int y, int z) +{ + Q_UNUSED(cb); + Q_UNUSED(x); + Q_UNUSED(y); + Q_UNUSED(z); +} + const QRhiNativeHandles *QRhiNull::nativeHandles(QRhiCommandBuffer *cb) { Q_UNUSED(cb); @@ -395,6 +414,18 @@ void QRhiNull::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceU resourceUpdate(cb, resourceUpdates); } +void QRhiNull::beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) +{ + if (resourceUpdates) + resourceUpdate(cb, resourceUpdates); +} + +void QRhiNull::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) +{ + if (resourceUpdates) + resourceUpdate(cb, resourceUpdates); +} + QNullBuffer::QNullBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size) : QRhiBuffer(rhi, type, usage, size) { @@ -647,6 +678,25 @@ bool QNullGraphicsPipeline::build() return true; } +QNullComputePipeline::QNullComputePipeline(QRhiImplementation *rhi) + : QRhiComputePipeline(rhi) +{ +} + +QNullComputePipeline::~QNullComputePipeline() +{ + release(); +} + +void QNullComputePipeline::release() +{ +} + +bool QNullComputePipeline::build() +{ + return true; +} + QNullCommandBuffer::QNullCommandBuffer(QRhiImplementation *rhi) : QRhiCommandBuffer(rhi) { diff --git a/src/gui/rhi/qrhinull_p_p.h b/src/gui/rhi/qrhinull_p_p.h index 6f79606486..b0227bc110 100644 --- a/src/gui/rhi/qrhinull_p_p.h +++ b/src/gui/rhi/qrhinull_p_p.h @@ -154,6 +154,14 @@ struct QNullGraphicsPipeline : public QRhiGraphicsPipeline bool build() override; }; +struct QNullComputePipeline : public QRhiComputePipeline +{ + QNullComputePipeline(QRhiImplementation *rhi); + ~QNullComputePipeline(); + void release() override; + bool build() override; +}; + struct QNullCommandBuffer : public QRhiCommandBuffer { QNullCommandBuffer(QRhiImplementation *rhi); @@ -189,6 +197,7 @@ public: void destroy() override; QRhiGraphicsPipeline *createGraphicsPipeline() override; + QRhiComputePipeline *createComputePipeline() override; QRhiShaderResourceBindings *createShaderResourceBindings() override; QRhiBuffer *createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, @@ -253,6 +262,11 @@ public: void debugMarkEnd(QRhiCommandBuffer *cb) override; void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) override; + void beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override; + void endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override; + void setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps) override; + void dispatch(QRhiCommandBuffer *cb, int x, int y, int z) override; + const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) override; void beginExternal(QRhiCommandBuffer *cb) override; void endExternal(QRhiCommandBuffer *cb) override; diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp index 2d7b7a16f6..f6ecd7c00e 100644 --- a/src/gui/rhi/qrhivulkan.cpp +++ b/src/gui/rhi/qrhivulkan.cpp @@ -379,30 +379,31 @@ bool QRhiVulkan::create(QRhi::Flags flags) queryQueueFamilyProps(); gfxQueue = VK_NULL_HANDLE; + + // We only support combined graphics+present queues. When it comes to + // compute, only combined graphics+compute queue is used, compute gets + // disabled otherwise. gfxQueueFamilyIdx = -1; - int presQueueFamilyIdx = -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); if (gfxQueueFamilyIdx == -1 && (queueFamilyProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) && (!maybeWindow || inst->supportsPresent(physDev, i, maybeWindow))) { - gfxQueueFamilyIdx = i; + if (queueFamilyProps[i].queueFlags & VK_QUEUE_COMPUTE_BIT) + gfxQueueFamilyIdx = i; + else if (computelessGfxQueueCandidateIdx == -1) + computelessGfxQueueCandidateIdx = i; } } - if (gfxQueueFamilyIdx != -1) { - presQueueFamilyIdx = gfxQueueFamilyIdx; - } else { - // ### - qWarning("No graphics queue that can present. This is not supported atm."); - } if (gfxQueueFamilyIdx == -1) { - qWarning("No graphics queue family found"); - return false; - } - if (presQueueFamilyIdx == -1) { - qWarning("No present queue family found"); - return false; + if (computelessGfxQueueCandidateIdx != -1) { + gfxQueueFamilyIdx = computelessGfxQueueCandidateIdx; + } else { + qWarning("No graphics (or no graphics+present) queue family found"); + return false; + } } VkDeviceQueueCreateInfo queueInfo[2]; @@ -412,12 +413,6 @@ bool QRhiVulkan::create(QRhi::Flags flags) queueInfo[0].queueFamilyIndex = gfxQueueFamilyIdx; queueInfo[0].queueCount = 1; queueInfo[0].pQueuePriorities = prio; - if (gfxQueueFamilyIdx != presQueueFamilyIdx) { - queueInfo[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queueInfo[1].queueFamilyIndex = presQueueFamilyIdx; - queueInfo[1].queueCount = 1; - queueInfo[1].pQueuePriorities = prio; - } QVector devLayers; if (inst->layers().contains("VK_LAYER_LUNARG_standard_validation")) @@ -449,7 +444,7 @@ bool QRhiVulkan::create(QRhi::Flags flags) VkDeviceCreateInfo devInfo; memset(&devInfo, 0, sizeof(devInfo)); devInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - devInfo.queueCreateInfoCount = gfxQueueFamilyIdx == presQueueFamilyIdx ? 1 : 2; + devInfo.queueCreateInfoCount = 1; devInfo.pQueueCreateInfos = queueInfo; devInfo.enabledLayerCount = devLayers.count(); devInfo.ppEnabledLayerNames = devLayers.constData(); @@ -478,18 +473,13 @@ bool QRhiVulkan::create(QRhi::Flags flags) } if (gfxQueueFamilyIdx != -1) { - // Will use one queue always, including when multiple QRhis use the - // same device. This has significant consequences, and cannot easily be - // changed (e.g. think pipeline barriers which create a dependency - // between commands submitted to a queue - with multiple queues - // additional synchronization would be needed) - if (!gfxQueue) df->vkGetDeviceQueue(dev, gfxQueueFamilyIdx, 0, &gfxQueue); if (queueFamilyProps.isEmpty()) queryQueueFamilyProps(); + hasCompute = (queueFamilyProps[gfxQueueFamilyIdx].queueFlags & VK_QUEUE_COMPUTE_BIT) != 0; timestampValidBits = queueFamilyProps[gfxQueueFamilyIdx].timestampValidBits; } @@ -631,7 +621,9 @@ VkResult QRhiVulkan::createDescriptorPool(VkDescriptorPool *pool) VkDescriptorPoolSize descPoolSizes[] = { { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, QVK_UNIFORM_BUFFERS_PER_POOL }, { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, QVK_UNIFORM_BUFFERS_PER_POOL }, - { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, QVK_COMBINED_IMAGE_SAMPLERS_PER_POOL } + { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, QVK_COMBINED_IMAGE_SAMPLERS_PER_POOL }, + { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, QVK_STORAGE_BUFFERS_PER_POOL }, + { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, QVK_STORAGE_IMAGES_PER_POOL } }; VkDescriptorPoolCreateInfo descPoolInfo; memset(&descPoolInfo, 0, sizeof(descPoolInfo)); @@ -1353,6 +1345,8 @@ bool QRhiVulkan::recreateSwapChain(QRhiSwapChain *swapChain) qWarning("Failed to create swapchain image view %d: %d", i, err); return false; } + + image.lastUse = QVkSwapChain::ImageResources::ScImageUseNone; } swapChainD->currentImageIndex = 0; @@ -1579,9 +1573,6 @@ QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::Begin QRhi::FrameOpResult QRhiVulkan::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) { - Q_ASSERT(inFrame); - inFrame = false; - QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain); Q_ASSERT(currentSwapChain == swapChainD); @@ -1590,23 +1581,34 @@ QRhi::FrameOpResult QRhiVulkan::endFrame(QRhiSwapChain *swapChain, QRhi::EndFram QVkSwapChain::FrameResources &frame(swapChainD->frameRes[swapChainD->currentFrameSlot]); QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]); - if (image.transferSource) { - // was used in a readback as transfer source, go back to presentable layout + if (image.lastUse != QVkSwapChain::ImageResources::ScImageUseRender) { VkImageMemoryBarrier presTrans; memset(&presTrans, 0, sizeof(presTrans)); presTrans.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - presTrans.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; presTrans.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - presTrans.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; presTrans.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; presTrans.image = image.image; presTrans.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; presTrans.subresourceRange.levelCount = presTrans.subresourceRange.layerCount = 1; - df->vkCmdPipelineBarrier(frame.cmdBuf, - VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - 0, 0, nullptr, 0, nullptr, - 1, &presTrans); - image.transferSource = false; + + if (image.lastUse == QVkSwapChain::ImageResources::ScImageUseNone) { + // was not used at all (no render pass), just transition from undefined to presentable + presTrans.srcAccessMask = 0; + presTrans.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + df->vkCmdPipelineBarrier(frame.cmdBuf, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + 0, 0, nullptr, 0, nullptr, + 1, &presTrans); + } else if (image.lastUse == QVkSwapChain::ImageResources::ScImageUseTransferSource) { + // was used in a readback as transfer source, go back to presentable layout + presTrans.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + presTrans.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + df->vkCmdPipelineBarrier(frame.cmdBuf, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + 0, 0, nullptr, 0, nullptr, + 1, &presTrans); + } + image.lastUse = QVkSwapChain::ImageResources::ScImageUseRender; } // record another timestamp, when enabled @@ -1669,9 +1671,6 @@ QRhi::FrameOpResult QRhiVulkan::endFrame(QRhiSwapChain *swapChain, QRhi::EndFram void QRhiVulkan::prepareNewFrame(QRhiCommandBuffer *cb) { - Q_ASSERT(!inFrame); - inFrame = true; - // Now is the time to do things for frame N-F, where N is the current one, // F is QVK_FRAMES_IN_FLIGHT, because only here it is guaranteed that that // frame has completed on the GPU (due to the fence wait in beginFrame). To @@ -1810,8 +1809,6 @@ QRhi::FrameOpResult QRhiVulkan::beginOffscreenFrame(QRhiCommandBuffer **cb) QRhi::FrameOpResult QRhiVulkan::endOffscreenFrame() { - Q_ASSERT(inFrame); - inFrame = false; Q_ASSERT(ofr.active); ofr.active = false; @@ -1845,8 +1842,6 @@ QRhi::FrameOpResult QRhiVulkan::endOffscreenFrame() QRhi::FrameOpResult QRhiVulkan::finish() { - Q_ASSERT(!inPass); - QVkSwapChain *swapChainD = nullptr; if (inFrame) { // There is either a swapchain or an offscreen frame on-going. @@ -1942,9 +1937,10 @@ void QRhiVulkan::activateTextureRenderTarget(QVkCommandBuffer *cbD, QVkTextureRe void QRhiVulkan::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) { - Q_ASSERT(inFrame && !inPass); + QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::NoPass); - enqueueResourceUpdates(QRHI_RES(QVkCommandBuffer, cb), resourceUpdates); + enqueueResourceUpdates(cbD, resourceUpdates); } void QRhiVulkan::beginPass(QRhiCommandBuffer *cb, @@ -1953,8 +1949,8 @@ void QRhiVulkan::beginPass(QRhiCommandBuffer *cb, const QRhiDepthStencilClearValue &depthStencilClearValue, QRhiResourceUpdateBatch *resourceUpdates) { - Q_ASSERT(inFrame && !inPass); QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::NoPass); if (resourceUpdates) enqueueResourceUpdates(cbD, resourceUpdates); @@ -1970,6 +1966,9 @@ void QRhiVulkan::beginPass(QRhiCommandBuffer *cb, case QRhiResource::RenderTarget: rtD = &QRHI_RES(QVkReferenceRenderTarget, rt)->d; rtD->rp->lastActiveFrameSlot = currentFrameSlot; + Q_ASSERT(currentSwapChain); + currentSwapChain->imageRes[currentSwapChain->currentImageIndex].lastUse = + QVkSwapChain::ImageResources::ScImageUseRender; break; case QRhiResource::TextureRenderTarget: { @@ -1983,6 +1982,7 @@ void QRhiVulkan::beginPass(QRhiCommandBuffer *cb, break; } + cbD->recordingPass = QVkCommandBuffer::RenderPass; cbD->currentTarget = rt; // No copy operations or image layout transitions allowed after this point @@ -2022,26 +2022,83 @@ void QRhiVulkan::beginPass(QRhiCommandBuffer *cb, cmd.args.beginRenderPass.clearValueIndex = cbD->pools.clearValue.count(); cbD->pools.clearValue.append(cvs.constData(), cvs.count()); cbD->commands.append(cmd); - - inPass = true; } void QRhiVulkan::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) { - Q_ASSERT(inPass); QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass); QVkCommandBuffer::Command cmd; cmd.cmd = QVkCommandBuffer::Command::EndRenderPass; cbD->commands.append(cmd); - inPass = false; + cbD->recordingPass = QVkCommandBuffer::NoPass; cbD->currentTarget = nullptr; if (resourceUpdates) enqueueResourceUpdates(cbD, resourceUpdates); } +void QRhiVulkan::beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) +{ + QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::NoPass); + + if (resourceUpdates) + enqueueResourceUpdates(cbD, resourceUpdates); + + enqueueTransitionPassResources(cbD); + + cbD->recordingPass = QVkCommandBuffer::ComputePass; +} + +void QRhiVulkan::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) +{ + QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::ComputePass); + + cbD->recordingPass = QVkCommandBuffer::NoPass; + + if (resourceUpdates) + enqueueResourceUpdates(cbD, resourceUpdates); +} + +void QRhiVulkan::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps) +{ + QVkComputePipeline *psD = QRHI_RES(QVkComputePipeline, ps); + Q_ASSERT(psD->pipeline); + QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::ComputePass); + + if (cbD->currentComputePipeline != ps || cbD->currentPipelineGeneration != psD->generation) { + QVkCommandBuffer::Command cmd; + cmd.cmd = QVkCommandBuffer::Command::BindPipeline; + cmd.args.bindPipeline.bindPoint = VK_PIPELINE_BIND_POINT_COMPUTE; + cmd.args.bindPipeline.pipeline = psD->pipeline; + cbD->commands.append(cmd); + + cbD->currentGraphicsPipeline = nullptr; + cbD->currentComputePipeline = ps; + cbD->currentPipelineGeneration = psD->generation; + } + + psD->lastActiveFrameSlot = currentFrameSlot; +} + +void QRhiVulkan::dispatch(QRhiCommandBuffer *cb, int x, int y, int z) +{ + QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::ComputePass); + + QVkCommandBuffer::Command cmd; + cmd.cmd = QVkCommandBuffer::Command::Dispatch; + cmd.args.dispatch.x = x; + cmd.args.dispatch.y = y; + cmd.args.dispatch.z = z; + cbD->commands.append(cmd); +} + VkShaderModule QRhiVulkan::createShader(const QByteArray &spirv) { VkShaderModuleCreateInfo shaderInfo; @@ -2133,6 +2190,45 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i writeInfo.pImageInfo = &imageInfos.last(); } break; + case QRhiShaderResourceBinding::ImageLoad: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::ImageStore: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::ImageLoadStore: + { + QVkTexture *texD = QRHI_RES(QVkTexture, b->u.simage.tex); + VkImageView view = texD->imageViewForLevel(b->u.simage.level); + if (view) { + writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + bd.simage.id = texD->m_id; + bd.simage.generation = texD->generation; + VkDescriptorImageInfo imageInfo; + imageInfo.sampler = VK_NULL_HANDLE; + imageInfo.imageView = view; + imageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL; + imageInfos.append(imageInfo); + writeInfo.pImageInfo = &imageInfos.last(); + } + } + break; + case QRhiShaderResourceBinding::BufferLoad: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::BufferStore: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::BufferLoadStore: + { + QVkBuffer *bufD = QRHI_RES(QVkBuffer, b->u.sbuf.buf); + writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + bd.sbuf.id = bufD->m_id; + bd.sbuf.generation = bufD->generation; + VkDescriptorBufferInfo bufInfo; + bufInfo.buffer = bufD->m_type == QRhiBuffer::Dynamic ? bufD->buffers[frameSlot] : bufD->buffers[0]; + bufInfo.offset = b->u.ubuf.offset; + bufInfo.range = b->u.ubuf.maybeSize ? b->u.ubuf.maybeSize : bufD->m_size; + bufferInfos.append(bufInfo); + writeInfo.pBufferInfo = &bufferInfos.last(); + } + break; default: continue; } @@ -2158,7 +2254,7 @@ static inline bool accessIsWrite(VkAccessFlags access) void QRhiVulkan::trackedBufferBarrier(QVkCommandBuffer *cbD, QVkBuffer *bufD, int slot, VkAccessFlags access, VkPipelineStageFlags stage) { - Q_ASSERT(!inPass); + Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::NoPass); Q_ASSERT(access && stage); QVkBuffer::UsageState &s(bufD->usageState[slot]); if (!s.stage) { @@ -2198,7 +2294,7 @@ void QRhiVulkan::trackedBufferBarrier(QVkCommandBuffer *cbD, QVkBuffer *bufD, in void QRhiVulkan::trackedImageBarrier(QVkCommandBuffer *cbD, QVkTexture *texD, VkImageLayout layout, VkAccessFlags access, VkPipelineStageFlags stage) { - Q_ASSERT(!inPass); + Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::NoPass); Q_ASSERT(layout && access && stage); QVkTexture::UsageState &s(texD->usageState); if (s.access == access && s.stage == stage && s.layout == layout) { @@ -2245,7 +2341,7 @@ void QRhiVulkan::subresourceBarrier(QVkCommandBuffer *cbD, VkImage image, int startLayer, int layerCount, int startLevel, int levelCount) { - Q_ASSERT(!inPass); + Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::NoPass); VkImageMemoryBarrier barrier; memset(&barrier, 0, sizeof(barrier)); barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; @@ -2678,15 +2774,20 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat cbD->commands.append(cmd); } else { // use the swapchain image - VkImage image = swapChainD->imageRes[swapChainD->currentImageIndex].image; - if (!swapChainD->imageRes[swapChainD->currentImageIndex].transferSource) { + QVkSwapChain::ImageResources &imageRes(swapChainD->imageRes[swapChainD->currentImageIndex]); + VkImage image = imageRes.image; + if (imageRes.lastUse != QVkSwapChain::ImageResources::ScImageUseTransferSource) { + if (imageRes.lastUse != QVkSwapChain::ImageResources::ScImageUseRender) { + qWarning("Attempted to read back undefined swapchain image content, " + "results are undefined. (do a render pass first)"); + } subresourceBarrier(cbD, image, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_MEMORY_READ_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 1, 0, 1); - swapChainD->imageRes[swapChainD->currentImageIndex].transferSource = true; + imageRes.lastUse = QVkSwapChain::ImageResources::ScImageUseTransferSource; } QVkCommandBuffer::Command cmd; @@ -2847,6 +2948,10 @@ static void qrhivk_releaseTexture(const QRhiVulkan::DeferredReleaseEntry &e, VkD vmaDestroyImage(toVmaAllocator(allocator), e.texture.image, toVmaAllocation(e.texture.allocation)); for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) vmaDestroyBuffer(toVmaAllocator(allocator), e.texture.stagingBuffers[i], toVmaAllocation(e.texture.stagingAllocations[i])); + for (int i = 0; i < QRhi::MAX_LEVELS; ++i) { + if (e.texture.extraImageViews[i]) + df->vkDestroyImageView(dev, e.texture.extraImageViews[i], nullptr); + } } static void qrhivk_releaseSampler(const QRhiVulkan::DeferredReleaseEntry &e, VkDevice dev, QVulkanDeviceFunctions *df) @@ -3005,6 +3110,8 @@ void QRhiVulkan::enqueueTransitionPassResources(QVkCommandBuffer *cbD) void QRhiVulkan::recordCommandBuffer(QVkCommandBuffer *cbD) { + Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::NoPass); + for (QVkCommandBuffer::Command &cmd : cbD->commands) { switch (cmd.cmd) { case QVkCommandBuffer::Command::CopyBuffer: @@ -3111,6 +3218,9 @@ void QRhiVulkan::recordCommandBuffer(QVkCommandBuffer *cbD) case QVkCommandBuffer::Command::TransitionPassResources: recordTransitionPassResources(cbD, cbD->passResTrackers[cmd.args.transitionResources.trackerIndex]); break; + case QVkCommandBuffer::Command::Dispatch: + df->vkCmdDispatch(cbD->cb, cmd.args.dispatch.x, cmd.args.dispatch.y, cmd.args.dispatch.z); + break; default: break; } @@ -3128,6 +3238,12 @@ static inline VkAccessFlags toVkAccess(QRhiPassResourceTracker::BufferAccess acc return VK_ACCESS_INDEX_READ_BIT; case QRhiPassResourceTracker::BufUniformRead: return VK_ACCESS_UNIFORM_READ_BIT; + case QRhiPassResourceTracker::BufStorageLoad: + return VK_ACCESS_SHADER_READ_BIT; + case QRhiPassResourceTracker::BufStorageStore: + return VK_ACCESS_SHADER_WRITE_BIT; + case QRhiPassResourceTracker::BufStorageLoadStore: + return VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; default: Q_UNREACHABLE(); break; @@ -3144,6 +3260,8 @@ static inline VkPipelineStageFlags toVkPipelineStage(QRhiPassResourceTracker::Bu return VK_PIPELINE_STAGE_VERTEX_SHADER_BIT; case QRhiPassResourceTracker::BufFragmentStage: return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + case QRhiPassResourceTracker::BufComputeStage: + return VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; default: Q_UNREACHABLE(); break; @@ -3168,6 +3286,12 @@ static inline VkImageLayout toVkLayout(QRhiPassResourceTracker::TextureAccess ac return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; case QRhiPassResourceTracker::TexDepthOutput: return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + case QRhiPassResourceTracker::TexStorageLoad: + Q_FALLTHROUGH(); + case QRhiPassResourceTracker::TexStorageStore: + Q_FALLTHROUGH(); + case QRhiPassResourceTracker::TexStorageLoadStore: + return VK_IMAGE_LAYOUT_GENERAL; default: Q_UNREACHABLE(); break; @@ -3184,6 +3308,12 @@ static inline VkAccessFlags toVkAccess(QRhiPassResourceTracker::TextureAccess ac return VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; case QRhiPassResourceTracker::TexDepthOutput: return VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + case QRhiPassResourceTracker::TexStorageLoad: + return VK_ACCESS_SHADER_READ_BIT; + case QRhiPassResourceTracker::TexStorageStore: + return VK_ACCESS_SHADER_WRITE_BIT; + case QRhiPassResourceTracker::TexStorageLoadStore: + return VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; default: Q_UNREACHABLE(); break; @@ -3202,6 +3332,8 @@ static inline VkPipelineStageFlags toVkPipelineStage(QRhiPassResourceTracker::Te return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; case QRhiPassResourceTracker::TexDepthOutputStage: return VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + case QRhiPassResourceTracker::TexComputeStage: + return VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; default: Q_UNREACHABLE(); break; @@ -3225,10 +3357,7 @@ void QRhiVulkan::trackedRegisterBuffer(QRhiPassResourceTracker *passResTracker, QRhiPassResourceTracker::BufferStage stage) { QVkBuffer::UsageState &u(bufD->usageState[slot]); - // The last arg will get ignored if this buffer was already used in the - // same pass; that's good because u is not the state at pass start anymore - // at that point. - passResTracker->registerBufferOnce(bufD, slot, access, stage, toPassTrackerUsageState(u)); + passResTracker->registerBuffer(bufD, slot, &access, &stage, toPassTrackerUsageState(u)); u.access = toVkAccess(access); u.stage = toVkPipelineStage(stage); } @@ -3239,10 +3368,7 @@ void QRhiVulkan::trackedRegisterTexture(QRhiPassResourceTracker *passResTracker, QRhiPassResourceTracker::TextureStage stage) { QVkTexture::UsageState &u(texD->usageState); - // The last arg will get ignored if this buffer was already used in the - // same pass; that's good because u is not the state at pass start anymore - // at that point. - passResTracker->registerTextureOnce(texD, access, stage, toPassTrackerUsageState(u)); + passResTracker->registerTexture(texD, &access, &stage, toPassTrackerUsageState(u)); u.layout = toVkLayout(access); u.access = toVkAccess(access); u.stage = toVkPipelineStage(stage); @@ -3417,6 +3543,8 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const return true; case QRhi::ElementIndexUint: return true; + case QRhi::Compute: + return hasCompute; default: Q_UNREACHABLE(); return false; @@ -3492,6 +3620,11 @@ QRhiGraphicsPipeline *QRhiVulkan::createGraphicsPipeline() return new QVkGraphicsPipeline(this); } +QRhiComputePipeline *QRhiVulkan::createComputePipeline() +{ + return new QVkComputePipeline(this); +} + QRhiShaderResourceBindings *QRhiVulkan::createShaderResourceBindings() { return new QVkShaderResourceBindings(this); @@ -3499,19 +3632,20 @@ QRhiShaderResourceBindings *QRhiVulkan::createShaderResourceBindings() void QRhiVulkan::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps) { - Q_ASSERT(inPass); QVkGraphicsPipeline *psD = QRHI_RES(QVkGraphicsPipeline, ps); Q_ASSERT(psD->pipeline); QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass); - if (cbD->currentPipeline != ps || cbD->currentPipelineGeneration != psD->generation) { + if (cbD->currentGraphicsPipeline != ps || cbD->currentPipelineGeneration != psD->generation) { QVkCommandBuffer::Command cmd; cmd.cmd = QVkCommandBuffer::Command::BindPipeline; cmd.args.bindPipeline.bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; cmd.args.bindPipeline.pipeline = psD->pipeline; cbD->commands.append(cmd); - cbD->currentPipeline = ps; + cbD->currentGraphicsPipeline = ps; + cbD->currentComputePipeline = nullptr; cbD->currentPipelineGeneration = psD->generation; } @@ -3525,6 +3659,8 @@ QRhiPassResourceTracker::BufferStage toPassTrackerBufferStage(QRhiShaderResource return QRhiPassResourceTracker::BufVertexStage; if (stages.testFlag(QRhiShaderResourceBinding::FragmentStage)) return QRhiPassResourceTracker::BufFragmentStage; + if (stages.testFlag(QRhiShaderResourceBinding::ComputeStage)) + return QRhiPassResourceTracker::BufComputeStage; Q_UNREACHABLE(); return QRhiPassResourceTracker::BufVertexStage; @@ -3537,6 +3673,8 @@ QRhiPassResourceTracker::TextureStage toPassTrackerTextureStage(QRhiShaderResour return QRhiPassResourceTracker::TexVertexStage; if (stages.testFlag(QRhiShaderResourceBinding::FragmentStage)) return QRhiPassResourceTracker::TexFragmentStage; + if (stages.testFlag(QRhiShaderResourceBinding::ComputeStage)) + return QRhiPassResourceTracker::TexComputeStage; Q_UNREACHABLE(); return QRhiPassResourceTracker::TexVertexStage; @@ -3546,14 +3684,17 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin int dynamicOffsetCount, const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) { - Q_ASSERT(inPass); - QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); - Q_ASSERT(cbD->currentPipeline); - QVkGraphicsPipeline *psD = QRHI_RES(QVkGraphicsPipeline, cbD->currentPipeline); + Q_ASSERT(cbD->recordingPass != QVkCommandBuffer::NoPass); + QVkGraphicsPipeline *gfxPsD = QRHI_RES(QVkGraphicsPipeline, cbD->currentGraphicsPipeline); + QVkComputePipeline *compPsD = QRHI_RES(QVkComputePipeline, cbD->currentComputePipeline); - if (!srb) - srb = psD->m_shaderResourceBindings; + if (!srb) { + if (gfxPsD) + srb = gfxPsD->m_shaderResourceBindings; + else + srb = compPsD->m_shaderResourceBindings; + } QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, srb); bool hasSlottedResourceInSrb = false; @@ -3592,7 +3733,7 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin executeBufferHostWritesForCurrentFrame(bufD); bufD->lastActiveFrameSlot = currentFrameSlot; - trackedRegisterBuffer(&passResTracker, bufD, currentFrameSlot, + trackedRegisterBuffer(&passResTracker, bufD, bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0, QRhiPassResourceTracker::BufUniformRead, toPassTrackerBufferStage(b->stage)); @@ -3630,6 +3771,64 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin } } break; + case QRhiShaderResourceBinding::ImageLoad: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::ImageStore: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::ImageLoadStore: + { + QVkTexture *texD = QRHI_RES(QVkTexture, b->u.simage.tex); + Q_ASSERT(texD->m_flags.testFlag(QRhiTexture::UsedWithLoadStore)); + texD->lastActiveFrameSlot = currentFrameSlot; + 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, + toPassTrackerTextureStage(b->stage)); + + if (texD->generation != bd.simage.generation || texD->m_id != bd.simage.id) { + rewriteDescSet = true; + bd.simage.id = texD->m_id; + bd.simage.generation = texD->generation; + } + } + break; + case QRhiShaderResourceBinding::BufferLoad: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::BufferStore: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::BufferLoadStore: + { + QVkBuffer *bufD = QRHI_RES(QVkBuffer, b->u.sbuf.buf); + Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::StorageBuffer)); + + if (bufD->m_type == QRhiBuffer::Dynamic) + executeBufferHostWritesForCurrentFrame(bufD); + + bufD->lastActiveFrameSlot = currentFrameSlot; + 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, bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0, + access, + toPassTrackerBufferStage(b->stage)); + + if (bufD->generation != bd.sbuf.generation || bufD->m_id != bd.sbuf.id) { + rewriteDescSet = true; + bd.sbuf.id = bufD->m_id; + bd.sbuf.generation = bufD->generation; + } + } + break; default: Q_UNREACHABLE(); break; @@ -3644,7 +3843,9 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin // also, dynamic offsets always need a bind. const bool forceRebind = (hasSlottedResourceInSrb && cbD->currentDescSetSlot != descSetIdx) || hasDynamicOffsetInSrb; - if (forceRebind || rewriteDescSet || cbD->currentSrb != srb || cbD->currentSrbGeneration != srbD->generation) { + const bool srbChanged = gfxPsD ? (cbD->currentGraphicsSrb != srb) : (cbD->currentComputeSrb != srb); + + if (forceRebind || rewriteDescSet || srbChanged || cbD->currentSrbGeneration != srbD->generation) { QVarLengthArray dynOfs; if (hasDynamicOffsetInSrb) { // Filling out dynOfs based on the sorted bindings is important @@ -3669,15 +3870,22 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin QVkCommandBuffer::Command cmd; cmd.cmd = QVkCommandBuffer::Command::BindDescriptorSet; - cmd.args.bindDescriptorSet.bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - cmd.args.bindDescriptorSet.pipelineLayout = psD->layout; + cmd.args.bindDescriptorSet.bindPoint = gfxPsD ? VK_PIPELINE_BIND_POINT_GRAPHICS + : VK_PIPELINE_BIND_POINT_COMPUTE; + cmd.args.bindDescriptorSet.pipelineLayout = gfxPsD ? gfxPsD->layout : compPsD->layout; cmd.args.bindDescriptorSet.descSet = srbD->descSets[descSetIdx]; cmd.args.bindDescriptorSet.dynamicOffsetCount = dynOfs.count(); cmd.args.bindDescriptorSet.dynamicOffsetIndex = cbD->pools.dynamicOffset.count(); cbD->pools.dynamicOffset.append(dynOfs.constData(), dynOfs.count()); cbD->commands.append(cmd); - cbD->currentSrb = srb; + if (gfxPsD) { + cbD->currentGraphicsSrb = srb; + cbD->currentComputeSrb = nullptr; + } else { + cbD->currentGraphicsSrb = nullptr; + cbD->currentComputeSrb = srb; + } cbD->currentSrbGeneration = srbD->generation; cbD->currentDescSetSlot = descSetIdx; } @@ -3689,8 +3897,8 @@ void QRhiVulkan::setVertexInput(QRhiCommandBuffer *cb, int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings, QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat) { - Q_ASSERT(inPass); QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass); QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]); bool needsBindVBuf = false; @@ -3772,9 +3980,8 @@ void QRhiVulkan::setVertexInput(QRhiCommandBuffer *cb, void QRhiVulkan::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) { - Q_ASSERT(inPass); QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); - Q_ASSERT(cbD->currentPipeline && cbD->currentTarget); + Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass); const QSize outputSize = cbD->currentTarget->pixelSize(); // x,y is top-left in VkViewport but bottom-left in QRhiViewport @@ -3793,7 +4000,7 @@ void QRhiVulkan::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport vp->maxDepth = viewport.maxDepth(); cbD->commands.append(cmd); - if (!QRHI_RES(QVkGraphicsPipeline, cbD->currentPipeline)->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)) { + if (!QRHI_RES(QVkGraphicsPipeline, cbD->currentGraphicsPipeline)->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)) { cmd.cmd = QVkCommandBuffer::Command::SetScissor; VkRect2D *s = &cmd.args.setScissor.scissor; s->offset.x = x; @@ -3806,10 +4013,9 @@ void QRhiVulkan::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport void QRhiVulkan::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) { - Q_ASSERT(inPass); QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); - Q_ASSERT(cbD->currentPipeline && cbD->currentTarget); - Q_ASSERT(QRHI_RES(QVkGraphicsPipeline, cbD->currentPipeline)->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)); + Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass); + Q_ASSERT(QRHI_RES(QVkGraphicsPipeline, cbD->currentGraphicsPipeline)->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)); const QSize outputSize = cbD->currentTarget->pixelSize(); // x,y is top-left in VkRect2D but bottom-left in QRhiScissor @@ -3829,8 +4035,9 @@ void QRhiVulkan::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) void QRhiVulkan::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) { - Q_ASSERT(inPass); QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass); + QVkCommandBuffer::Command cmd; cmd.cmd = QVkCommandBuffer::Command::SetBlendConstants; cmd.args.setBlendConstants.c[0] = c.redF(); @@ -3842,8 +4049,9 @@ void QRhiVulkan::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) void QRhiVulkan::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) { - Q_ASSERT(inPass); QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass); + QVkCommandBuffer::Command cmd; cmd.cmd = QVkCommandBuffer::Command::SetStencilRef; cmd.args.setStencilRef.ref = refValue; @@ -3853,8 +4061,9 @@ void QRhiVulkan::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) void QRhiVulkan::draw(QRhiCommandBuffer *cb, quint32 vertexCount, quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) { - Q_ASSERT(inPass); QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass); + QVkCommandBuffer::Command cmd; cmd.cmd = QVkCommandBuffer::Command::Draw; cmd.args.draw.vertexCount = vertexCount; @@ -3867,8 +4076,9 @@ void QRhiVulkan::draw(QRhiCommandBuffer *cb, quint32 vertexCount, void QRhiVulkan::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance) { - Q_ASSERT(inPass); QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass); + QVkCommandBuffer::Command cmd; cmd.cmd = QVkCommandBuffer::Command::DrawIndexed; cmd.args.drawIndexed.indexCount = indexCount; @@ -3969,6 +4179,8 @@ static inline VkBufferUsageFlagBits toVkBufferUsage(QRhiBuffer::UsageFlags usage u |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT; if (usage.testFlag(QRhiBuffer::UniformBuffer)) u |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; + if (usage.testFlag(QRhiBuffer::StorageBuffer)) + u |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; return VkBufferUsageFlagBits(u); } @@ -4019,13 +4231,15 @@ static inline VkSamplerAddressMode toVkAddressMode(QRhiSampler::AddressMode m) } } -static inline VkShaderStageFlagBits toVkShaderStage(QRhiGraphicsShaderStage::Type type) +static inline VkShaderStageFlagBits toVkShaderStage(QRhiShaderStage::Type type) { switch (type) { - case QRhiGraphicsShaderStage::Vertex: + case QRhiShaderStage::Vertex: return VK_SHADER_STAGE_VERTEX_BIT; - case QRhiGraphicsShaderStage::Fragment: + case QRhiShaderStage::Fragment: return VK_SHADER_STAGE_FRAGMENT_BIT; + case QRhiShaderStage::Compute: + return VK_SHADER_STAGE_COMPUTE_BIT; default: Q_UNREACHABLE(); return VK_SHADER_STAGE_VERTEX_BIT; @@ -4246,8 +4460,24 @@ static inline VkDescriptorType toVkDescriptorType(const QRhiShaderResourceBindin case QRhiShaderResourceBinding::UniformBuffer: return b->u.ubuf.hasDynamicOffset ? VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + case QRhiShaderResourceBinding::SampledTexture: return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + + case QRhiShaderResourceBinding::ImageLoad: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::ImageStore: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::ImageLoadStore: + return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + + case QRhiShaderResourceBinding::BufferLoad: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::BufferStore: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::BufferLoadStore: + return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + default: Q_UNREACHABLE(); return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; @@ -4261,6 +4491,8 @@ static inline VkShaderStageFlags toVkShaderStageFlags(QRhiShaderResourceBinding: s |= VK_SHADER_STAGE_VERTEX_BIT; if (stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) s |= VK_SHADER_STAGE_FRAGMENT_BIT; + if (stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) + s |= VK_SHADER_STAGE_COMPUTE_BIT; return VkShaderStageFlags(s); } @@ -4339,6 +4571,11 @@ bool QVkBuffer::build() if (buffers[0]) release(); + if (m_usage.testFlag(QRhiBuffer::StorageBuffer) && m_type == Dynamic) { + qWarning("StorageBuffer cannot be combined with Dynamic"); + return false; + } + const int nonZeroSize = m_size <= 0 ? 256 : m_size; VkBufferCreateInfo bufferInfo; @@ -4514,6 +4751,8 @@ QVkTexture::QVkTexture(QRhiImplementation *rhi, Format format, const QSize &pixe stagingBuffers[i] = VK_NULL_HANDLE; stagingAllocations[i] = nullptr; } + for (int i = 0; i < QRhi::MAX_LEVELS; ++i) + perLevelImageViews[i] = VK_NULL_HANDLE; } QVkTexture::~QVkTexture() @@ -4542,6 +4781,11 @@ void QVkTexture::release() stagingAllocations[i] = nullptr; } + for (int i = 0; i < QRhi::MAX_LEVELS; ++i) { + e.texture.extraImageViews[i] = perLevelImageViews[i]; + perLevelImageViews[i] = VK_NULL_HANDLE; + } + image = VK_NULL_HANDLE; imageView = VK_NULL_HANDLE; imageAlloc = nullptr; @@ -4674,6 +4918,8 @@ bool QVkTexture::build() imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; if (m_flags.testFlag(QRhiTexture::UsedWithGenerateMips)) imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + if (m_flags.testFlag(QRhiTexture::UsedWithLoadStore)) + imageInfo.usage |= VK_IMAGE_USAGE_STORAGE_BIT; VmaAllocationCreateInfo allocInfo; memset(&allocInfo, 0, sizeof(allocInfo)); @@ -4732,6 +4978,43 @@ const QRhiNativeHandles *QVkTexture::nativeHandles() return &nativeHandlesStruct; } +VkImageView QVkTexture::imageViewForLevel(int level) +{ + Q_ASSERT(level >= 0 && level < int(mipLevelCount)); + if (perLevelImageViews[level] != VK_NULL_HANDLE) + return perLevelImageViews[level]; + + const bool isDepth = isDepthTextureFormat(m_format); + const bool isCube = m_flags.testFlag(CubeMap); + + VkImageViewCreateInfo viewInfo; + memset(&viewInfo, 0, sizeof(viewInfo)); + viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewInfo.image = image; + viewInfo.viewType = isCube ? VK_IMAGE_VIEW_TYPE_CUBE : VK_IMAGE_VIEW_TYPE_2D; + viewInfo.format = vkformat; + viewInfo.components.r = VK_COMPONENT_SWIZZLE_R; + viewInfo.components.g = VK_COMPONENT_SWIZZLE_G; + viewInfo.components.b = VK_COMPONENT_SWIZZLE_B; + viewInfo.components.a = VK_COMPONENT_SWIZZLE_A; + viewInfo.subresourceRange.aspectMask = isDepth ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT; + viewInfo.subresourceRange.baseMipLevel = level; + viewInfo.subresourceRange.levelCount = 1; + viewInfo.subresourceRange.baseArrayLayer = 0; + viewInfo.subresourceRange.layerCount = isCube ? 6 : 1; + + VkImageView v = VK_NULL_HANDLE; + QRHI_RES_RHI(QRhiVulkan); + VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &v); + if (err != VK_SUCCESS) { + qWarning("Failed to create image view: %d", err); + return VK_NULL_HANDLE; + } + + perLevelImageViews[level] = v; + return v; +} + QVkSampler::QVkSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode, AddressMode u, AddressMode v) : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v) @@ -5222,7 +5505,7 @@ bool QVkGraphicsPipeline::build() QVarLengthArray shaders; QVarLengthArray shaderStageCreateInfos; - for (const QRhiGraphicsShaderStage &shaderStage : m_shaderStages) { + for (const QRhiShaderStage &shaderStage : m_shaderStages) { const QShader bakedShader = shaderStage.shader(); const QShaderCode spirv = bakedShader.shader({ QShader::SpirvShader, 100, shaderStage.shaderVariant() }); if (spirv.shader().isEmpty()) { @@ -5404,6 +5687,100 @@ bool QVkGraphicsPipeline::build() return true; } +QVkComputePipeline::QVkComputePipeline(QRhiImplementation *rhi) + : QRhiComputePipeline(rhi) +{ +} + +QVkComputePipeline::~QVkComputePipeline() +{ + release(); +} + +void QVkComputePipeline::release() +{ + if (!pipeline && !layout) + return; + + QRhiVulkan::DeferredReleaseEntry e; + e.type = QRhiVulkan::DeferredReleaseEntry::Pipeline; + e.lastActiveFrameSlot = lastActiveFrameSlot; + + e.pipelineState.pipeline = pipeline; + e.pipelineState.layout = layout; + + pipeline = VK_NULL_HANDLE; + layout = VK_NULL_HANDLE; + + QRHI_RES_RHI(QRhiVulkan); + rhiD->releaseQueue.append(e); + + rhiD->unregisterResource(this); +} + +bool QVkComputePipeline::build() +{ + if (pipeline) + release(); + + QRHI_RES_RHI(QRhiVulkan); + if (!rhiD->ensurePipelineCache()) + return false; + + VkPipelineLayoutCreateInfo pipelineLayoutInfo; + memset(&pipelineLayoutInfo, 0, sizeof(pipelineLayoutInfo)); + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = 1; + QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, m_shaderResourceBindings); + Q_ASSERT(m_shaderResourceBindings && srbD->layout); + pipelineLayoutInfo.pSetLayouts = &srbD->layout; + VkResult err = rhiD->df->vkCreatePipelineLayout(rhiD->dev, &pipelineLayoutInfo, nullptr, &layout); + if (err != VK_SUCCESS) { + qWarning("Failed to create pipeline layout: %d", err); + return false; + } + + VkComputePipelineCreateInfo pipelineInfo; + memset(&pipelineInfo, 0, sizeof(pipelineInfo)); + pipelineInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; + pipelineInfo.layout = layout; + + if (m_shaderStage.type() != QRhiShaderStage::Compute) { + qWarning("Compute pipeline requires a compute shader stage"); + return false; + } + const QShader bakedShader = m_shaderStage.shader(); + const QShaderCode spirv = bakedShader.shader({ QShader::SpirvShader, 100, m_shaderStage.shaderVariant() }); + if (spirv.shader().isEmpty()) { + qWarning() << "No SPIR-V 1.0 shader code found in baked shader" << bakedShader; + return false; + } + if (bakedShader.stage() != QShader::ComputeStage) { + qWarning() << bakedShader << "is not a compute shader"; + return false; + } + VkShaderModule shader = rhiD->createShader(spirv.shader()); + VkPipelineShaderStageCreateInfo shaderInfo; + memset(&shaderInfo, 0, sizeof(shaderInfo)); + shaderInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shaderInfo.stage = VK_SHADER_STAGE_COMPUTE_BIT; + shaderInfo.module = shader; + shaderInfo.pName = spirv.entryPoint().constData(); + pipelineInfo.stage = shaderInfo; + + err = rhiD->df->vkCreateComputePipelines(rhiD->dev, rhiD->pipelineCache, 1, &pipelineInfo, nullptr, &pipeline); + rhiD->df->vkDestroyShaderModule(rhiD->dev, shader, nullptr); + if (err != VK_SUCCESS) { + qWarning("Failed to create graphics pipeline: %d", err); + return false; + } + + lastActiveFrameSlot = -1; + generation += 1; + rhiD->registerResource(this); + return true; +} + QVkCommandBuffer::QVkCommandBuffer(QRhiImplementation *rhi) : QRhiCommandBuffer(rhi) { diff --git a/src/gui/rhi/qrhivulkan_p_p.h b/src/gui/rhi/qrhivulkan_p_p.h index afb0cc1d5a..cec9016603 100644 --- a/src/gui/rhi/qrhivulkan_p_p.h +++ b/src/gui/rhi/qrhivulkan_p_p.h @@ -61,6 +61,8 @@ static const int QVK_FRAMES_IN_FLIGHT = 2; static const int QVK_DESC_SETS_PER_POOL = 128; static const int QVK_UNIFORM_BUFFERS_PER_POOL = 256; static const int QVK_COMBINED_IMAGE_SAMPLERS_PER_POOL = 256; +static const int QVK_STORAGE_BUFFERS_PER_POOL = 128; +static const int QVK_STORAGE_IMAGES_PER_POOL = 128; static const int QVK_MAX_ACTIVE_TIMESTAMP_PAIRS = 16; @@ -123,12 +125,14 @@ struct QVkTexture : public QRhiTexture bool prepareBuild(QSize *adjustedSize = nullptr); bool finishBuild(); + VkImageView imageViewForLevel(int level); VkImage image = VK_NULL_HANDLE; VkImageView imageView = VK_NULL_HANDLE; QVkAlloc imageAlloc = nullptr; VkBuffer stagingBuffers[QVK_FRAMES_IN_FLIGHT]; QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT]; + VkImageView perLevelImageViews[QRhi::MAX_LEVELS]; bool owns = true; QRhiVulkanTextureNativeHandles nativeHandlesStruct; struct UsageState { @@ -246,10 +250,20 @@ struct QVkShaderResourceBindings : public QRhiShaderResourceBindings quint64 samplerId; uint samplerGeneration; }; + struct BoundStorageImageData { + quint64 id; + uint generation; + }; + struct BoundStorageBufferData { + quint64 id; + uint generation; + }; struct BoundResourceData { union { BoundUniformBufferData ubuf; BoundSampledTextureData stex; + BoundStorageImageData simage; + BoundStorageBufferData sbuf; }; }; QVector boundResourceData[QVK_FRAMES_IN_FLIGHT]; @@ -273,6 +287,20 @@ struct QVkGraphicsPipeline : public QRhiGraphicsPipeline friend class QRhiVulkan; }; +struct QVkComputePipeline : public QRhiComputePipeline +{ + QVkComputePipeline(QRhiImplementation *rhi); + ~QVkComputePipeline(); + void release() override; + bool build() override; + + VkPipelineLayout layout = VK_NULL_HANDLE; + VkPipeline pipeline = VK_NULL_HANDLE; + int lastActiveFrameSlot = -1; + uint generation = 0; + friend class QRhiVulkan; +}; + struct QVkCommandBuffer : public QRhiCommandBuffer { QVkCommandBuffer(QRhiImplementation *rhi); @@ -287,16 +315,25 @@ struct QVkCommandBuffer : public QRhiCommandBuffer return &nativeHandlesStruct; } + enum PassType { + NoPass, + RenderPass, + ComputePass + }; + void resetState() { resetCommands(); + recordingPass = NoPass; currentTarget = nullptr; resetCachedState(); } void resetCachedState() { - currentPipeline = nullptr; + currentGraphicsPipeline = nullptr; + currentComputePipeline = nullptr; currentPipelineGeneration = 0; - currentSrb = nullptr; + currentGraphicsSrb = nullptr; + currentComputeSrb = nullptr; currentSrbGeneration = 0; currentDescSetSlot = -1; currentIndexBuffer = VK_NULL_HANDLE; @@ -306,10 +343,13 @@ struct QVkCommandBuffer : public QRhiCommandBuffer memset(currentVertexOffsets, 0, sizeof(currentVertexOffsets)); } + PassType recordingPass; QRhiRenderTarget *currentTarget; - QRhiGraphicsPipeline *currentPipeline; + QRhiGraphicsPipeline *currentGraphicsPipeline; + QRhiComputePipeline *currentComputePipeline; uint currentPipelineGeneration; - QRhiShaderResourceBindings *currentSrb; + QRhiShaderResourceBindings *currentGraphicsSrb; + QRhiShaderResourceBindings *currentComputeSrb; uint currentSrbGeneration; int currentDescSetSlot; VkBuffer currentIndexBuffer; @@ -343,7 +383,8 @@ struct QVkCommandBuffer : public QRhiCommandBuffer DebugMarkerBegin, DebugMarkerEnd, DebugMarkerInsert, - TransitionPassResources + TransitionPassResources, + Dispatch }; Cmd cmd; @@ -456,6 +497,9 @@ struct QVkCommandBuffer : public QRhiCommandBuffer struct { int trackerIndex; } transitionResources; + struct { + int x, y, z; + } dispatch; } args; }; QVector commands; @@ -532,7 +576,12 @@ struct QVkSwapChain : public QRhiSwapChain VkFramebuffer fb = VK_NULL_HANDLE; VkImage msaaImage = VK_NULL_HANDLE; VkImageView msaaImageView = VK_NULL_HANDLE; - bool transferSource = false; + enum LastUse { + ScImageUseNone, + ScImageUseRender, + ScImageUseTransferSource + }; + LastUse lastUse = ScImageUseNone; } imageRes[MAX_BUFFER_COUNT]; struct FrameResources { @@ -565,6 +614,7 @@ public: void destroy() override; QRhiGraphicsPipeline *createGraphicsPipeline() override; + QRhiComputePipeline *createComputePipeline() override; QRhiShaderResourceBindings *createShaderResourceBindings() override; QRhiBuffer *createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, @@ -629,6 +679,11 @@ public: void debugMarkEnd(QRhiCommandBuffer *cb) override; void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) override; + void beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override; + void endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override; + void setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps) override; + void dispatch(QRhiCommandBuffer *cb, int x, int y, int z) override; + const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) override; void beginExternal(QRhiCommandBuffer *cb) override; void endExternal(QRhiCommandBuffer *cb) override; @@ -722,6 +777,7 @@ public: VkCommandPool cmdPool = VK_NULL_HANDLE; int gfxQueueFamilyIdx = -1; VkQueue gfxQueue = VK_NULL_HANDLE; + bool hasCompute = false; quint32 timestampValidBits = 0; bool importedAllocator = false; QVkAllocator allocator = nullptr; @@ -765,8 +821,6 @@ public: VkFormat optimalDsFormat = VK_FORMAT_UNDEFINED; QMatrix4x4 clipCorrectMatrix; - bool inFrame = false; - bool inPass = false; QVkSwapChain *currentSwapChain = nullptr; QSet swapchains; QRhiVulkanNativeHandles nativeHandlesStruct; @@ -830,6 +884,7 @@ public: QVkAlloc allocation; VkBuffer stagingBuffers[QVK_FRAMES_IN_FLIGHT]; QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT]; + VkImageView extraImageViews[QRhi::MAX_LEVELS]; } texture; struct { VkSampler sampler; diff --git a/src/gui/rhi/qshaderdescription.cpp b/src/gui/rhi/qshaderdescription.cpp index c2dbbb38fa..77aceaddba 100644 --- a/src/gui/rhi/qshaderdescription.cpp +++ b/src/gui/rhi/qshaderdescription.cpp @@ -522,6 +522,21 @@ QVector QShaderDescription::storageImages() c return d->storageImages; } +/*! + Returns the local size of a compute shader. + + For example, for a compute shader with the following declaration the + function returns { 256, 16, 1}. + + \badcode + layout(local_size_x = 256, local_size_y = 16, local_size_z = 1) in; + \endcode + */ +std::array QShaderDescription::computeShaderLocalSize() const +{ + return d->localSize; +} + static struct TypeTab { QString k; QShaderDescription::VariableType v; @@ -799,6 +814,7 @@ static const QString pushConstantBlocksKey = QLatin1String("pushConstantBlocks") static const QString storageBlocksKey = QLatin1String("storageBlocks"); static const QString combinedImageSamplersKey = QLatin1String("combinedImageSamplers"); static const QString storageImagesKey = QLatin1String("storageImages"); +static const QString localSizeKey = QLatin1String("localSize"); static void addDeco(QJsonObject *obj, const QShaderDescription::InOutVariable &v) { @@ -941,6 +957,11 @@ QJsonDocument QShaderDescriptionPrivate::makeDoc() if (!jstorageImages.isEmpty()) root[storageImagesKey] = jstorageImages; + QJsonArray jlocalSize; + for (int i = 0; i < 3; ++i) + jlocalSize.append(QJsonValue(int(localSize[i]))); + root[localSizeKey] = jlocalSize; + return QJsonDocument(root); } @@ -1082,6 +1103,14 @@ void QShaderDescriptionPrivate::loadDoc(const QJsonDocument &doc) for (int i = 0; i < images.count(); ++i) storageImages.append(inOutVar(images[i].toObject())); } + + if (root.contains(localSizeKey)) { + QJsonArray localSizeArr = root[localSizeKey].toArray(); + if (localSizeArr.count() == 3) { + for (int i = 0; i < 3; ++i) + localSize[i] = localSizeArr[i].toInt(); + } + } } QT_END_NAMESPACE diff --git a/src/gui/rhi/qshaderdescription_p.h b/src/gui/rhi/qshaderdescription_p.h index 43d4256a63..5a63b998cd 100644 --- a/src/gui/rhi/qshaderdescription_p.h +++ b/src/gui/rhi/qshaderdescription_p.h @@ -51,6 +51,7 @@ #include #include #include +#include QT_BEGIN_NAMESPACE @@ -254,6 +255,8 @@ public: QVector combinedImageSamplers() const; QVector storageImages() const; + std::array computeShaderLocalSize() const; + private: QShaderDescriptionPrivate *d; friend struct QShaderDescriptionPrivate; diff --git a/src/gui/rhi/qshaderdescription_p_p.h b/src/gui/rhi/qshaderdescription_p_p.h index dbe68d1060..1caee24984 100644 --- a/src/gui/rhi/qshaderdescription_p_p.h +++ b/src/gui/rhi/qshaderdescription_p_p.h @@ -60,6 +60,7 @@ struct Q_GUI_EXPORT QShaderDescriptionPrivate QShaderDescriptionPrivate() : ref(1) { + localSize[0] = localSize[1] = localSize[2] = 0; } QShaderDescriptionPrivate(const QShaderDescriptionPrivate *other) @@ -70,7 +71,8 @@ struct Q_GUI_EXPORT QShaderDescriptionPrivate pushConstantBlocks(other->pushConstantBlocks), storageBlocks(other->storageBlocks), combinedImageSamplers(other->combinedImageSamplers), - storageImages(other->storageImages) + storageImages(other->storageImages), + localSize(other->localSize) { } @@ -88,6 +90,7 @@ struct Q_GUI_EXPORT QShaderDescriptionPrivate QVector storageBlocks; QVector combinedImageSamplers; QVector storageImages; + std::array localSize; }; QT_END_NAMESPACE -- cgit v1.2.3 From cdfe8c76af1518ccdd497c20ecc189365c733879 Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Mon, 17 Jun 2019 14:25:09 +0200 Subject: checkOcspResponse - remove unneeded locking and also plain wrong comments: since we don't set verification callback on a store - we don't have to lock (our q_X509Callback never gets called). This change should simplify the merge with change I have in 5.12 (where I completely got rid of locking). Since I don't care about exact errors found (relying on the fact it's the same chain of certs we check in SSL_connect/SSL_accept), for now we don't try to extract them from OCSP_basic_verify. In fufure, if these chains are different, we can create a temporary store (see how it's done in 'verify', for example) and set a VF callback on this store. Change-Id: I4a36e19836d19c2ea95c869dcfe85f49fe723ff0 Reviewed-by: Timur Pocheptsov --- src/network/ssl/qsslsocket_openssl.cpp | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index 977d8a6742..7c04feb5f8 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -1575,27 +1575,15 @@ bool QSslSocketBackendPrivate::checkOcspStatus() // 3) It checks CertID in response. // 4) Ensures the responder is authorized to sign the status respond. // - // Here it's important to notice that it calls X509_cert_verify and - // as a result, possibly, our verification callback. Given this callback - // at the moment uses a global variable, we have to lock. This will change - // as soon as we fix our verification procedure. - // Also note, OpenSSL prior to 1.0.2b would only use bs->certs to + // Note, OpenSSL prior to 1.0.2b would only use bs->certs to // verify the responder's chain (see their commit 4ba9a4265bd). // Working this around - is too much fuss for ancient versions we // are dropping quite soon anyway. { const unsigned long verificationFlags = 0; - const QMutexLocker locker(&_q_sslErrorList()->mutex); - // Before unlocking the mutex, startHandshake() stores errors (found in SSL_connect() - // or SSL_accept()) into the local variable, so it's safe to clear it here - as soon - // as we managed to lock, whoever had the lock before, already stored their own copy - // of errors. - _q_sslErrorList()->errors.clear(); const int success = q_OCSP_basic_verify(basicResponse, peerChain, store, verificationFlags); - if (success <= 0 || _q_sslErrorList()->errors.size()) { - _q_sslErrorList()->errors.clear(); + if (success <= 0) ocspErrors.push_back(QSslError::OcspResponseCannotBeTrusted); - } } if (q_OCSP_resp_count(basicResponse) != 1) { -- cgit v1.2.3 From 6275e037bfe532d12ecd76913d5d36a91c1d5034 Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Tue, 18 Jun 2019 23:03:01 +0200 Subject: Android: Make sure that the root file system is in the list of volumes On Android (at least in the emulator), the root file system is reported to be of type rootfs, which we usually ignore on Linux as legacy. Also, it's a read-only file system with bytesTotal reported as zero. We usually ignore such volumes, but as the root file system is expected to be in the list, we should never ignore it. This fixes the failing QStorageInfo test. Change-Id: I778ee9e76e385649e58d5e5ac7e0ae2d8e0ba92b Fixes: QTBUG-73563 Reviewed-by: Thiago Macieira Reviewed-by: BogDan Vatra --- src/corelib/io/qstorageinfo_unix.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/corelib/io/qstorageinfo_unix.cpp b/src/corelib/io/qstorageinfo_unix.cpp index b7621b5d2f..6b821b0fe9 100644 --- a/src/corelib/io/qstorageinfo_unix.cpp +++ b/src/corelib/io/qstorageinfo_unix.cpp @@ -220,7 +220,7 @@ static bool shouldIncludeFs(const QStorageIterator &it) return false; } -#ifdef Q_OS_LINUX +#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) if (it.fileSystemType() == "rootfs") return false; #endif @@ -846,7 +846,7 @@ QList QStorageInfoPrivate::mountedVolumes() const QString mountDir = it.rootPath(); QStorageInfo info(mountDir); - if (info.bytesTotal() == 0) + if (info.bytesTotal() == 0 && info != root()) continue; volumes.append(info); } -- cgit v1.2.3 From 2ce4a9f48705095669cb74c8de9d8a72f9d49b0e Mon Sep 17 00:00:00 2001 From: Eirik Aavitsland Date: Wed, 19 Jun 2019 16:35:28 +0200 Subject: Fix crash in QPainterPath::reserve() Function did not handle default-constructed (null d_ptr) path correctly. Fixes: QTBUG-76516 Change-Id: I2925d4306f7fce34ece6739b18a8e275e7970837 Reviewed-by: Allan Sandfeld Jensen --- src/gui/painting/qpainterpath.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/gui/painting/qpainterpath.cpp b/src/gui/painting/qpainterpath.cpp index cbe34c2857..d20faf89a2 100644 --- a/src/gui/painting/qpainterpath.cpp +++ b/src/gui/painting/qpainterpath.cpp @@ -675,8 +675,9 @@ void QPainterPath::reserve(int size) { Q_D(QPainterPath); if ((!d && size > 0) || (d && d->elements.capacity() < size)) { + ensureData(); detach(); - d->elements.reserve(size); + d_func()->elements.reserve(size); } } -- cgit v1.2.3 From cbdc9a7786278c31fdb6eb465f32d0374349c1ef Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 20 Jun 2019 09:36:46 -0700 Subject: RCC: Change the default compression to Zlib Codebases using QResource::isCompressed() and assuming it is Zlib break if zstd is enabled. So change the default back to Zlib until Qt 6.0. [ChangeLog][Important Behavior Changes] RCC's default compression algorithm was changed back to Zlib, as it was in all previous releases until 5.13.0. The default will remain Zlib for all Qt 5.x releases but will change in Qt 6.0. To activate Zstd compression for your resources, either pass the --compress-algo=zstd option to the rcc tool or add the XML attribute compression-algorithm="zstd" to the tags in the .qrc file. Task-number: QTBUG-76521 Change-Id: Ief874765cd7b43798de3fffd15a9f56fd9ad1ad4 Reviewed-by: Simon Hausmann --- src/tools/rcc/rcc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/tools/rcc/rcc.cpp b/src/tools/rcc/rcc.cpp index 862e574f2d..011a7db810 100644 --- a/src/tools/rcc/rcc.cpp +++ b/src/tools/rcc/rcc.cpp @@ -58,7 +58,7 @@ enum { CONSTANT_COMPRESSTHRESHOLD_DEFAULT = 70 }; -#if QT_CONFIG(zstd) +#if QT_CONFIG(zstd) && QT_VERSION >= QT_VERSION_CHECK(6,0,0) # define CONSTANT_COMPRESSALGO_DEFAULT RCCResourceLibrary::CompressionAlgorithm::Zstd #elif !defined(QT_NO_COMPRESS) # define CONSTANT_COMPRESSALGO_DEFAULT RCCResourceLibrary::CompressionAlgorithm::Zlib -- cgit v1.2.3 From 0f3f143f6deee3b31a7dfaba07dfd517f0aee442 Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Sun, 23 Jun 2019 23:26:28 +0200 Subject: Fix window shortcuts when a completer popup has focus The completer popup has focus, making QShortcut direct to it's window rather than to the window the completer belongs to. As QShortcut handles the case for Tool windows that have a parent, but doens't do the same for popups. And they shouldn't be treated the same way, as a context menu popup for a e.g. text edit should in fact block the text edit's shortcuts while open. However, the completer popup is special, in that it explicitly makes the widget completes for its focusProxy, which is what we can use to fix this issue. Change-Id: Ie7177d39668b3af14a1d9e0ee5d93eca9c67c8af Fixes: QTBUG-4485 Reviewed-by: Richard Moe Gustavsen --- src/widgets/kernel/qshortcut.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/widgets/kernel/qshortcut.cpp b/src/widgets/kernel/qshortcut.cpp index a680ff7913..b7857e2b74 100644 --- a/src/widgets/kernel/qshortcut.cpp +++ b/src/widgets/kernel/qshortcut.cpp @@ -189,10 +189,15 @@ static bool correctWidgetContext(Qt::ShortcutContext context, QWidget *w, QWidge } #endif - /* if a floating tool window is active, keep shortcuts on the - * parent working */ - if (active_window != tlw && active_window && active_window->windowType() == Qt::Tool && active_window->parentWidget()) { - active_window = active_window->parentWidget()->window(); + if (active_window && active_window != tlw) { + /* if a floating tool window is active, keep shortcuts on the parent working. + * and if a popup window is active (f.ex a completer), keep shortcuts on the + * focus proxy working */ + if (active_window->windowType() == Qt::Tool && active_window->parentWidget()) { + active_window = active_window->parentWidget()->window(); + } else if (active_window->windowType() == Qt::Popup && active_window->focusProxy()) { + active_window = active_window->focusProxy()->window(); + } } if (active_window != tlw) { -- cgit v1.2.3 From 9be66cb282dee1ce4380602a2f3caf5abfd144cf Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Wed, 24 Apr 2019 15:40:29 +0200 Subject: Android: Use Android button layout for the DialogButtonBoxLayout hint Fixes: QTBUG-70045 Change-Id: I9c51e9a769f510a6f14f6e9d78583caf3df15031 Reviewed-by: BogDan Vatra --- src/plugins/platforms/android/qandroidplatformtheme.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/plugins/platforms/android/qandroidplatformtheme.cpp b/src/plugins/platforms/android/qandroidplatformtheme.cpp index b891407c44..94405738f3 100644 --- a/src/plugins/platforms/android/qandroidplatformtheme.cpp +++ b/src/plugins/platforms/android/qandroidplatformtheme.cpp @@ -488,6 +488,8 @@ QVariant QAndroidPlatformTheme::themeHint(ThemeHint hint) const Q_FALLTHROUGH(); } + case DialogButtonBoxLayout: + return QVariant(QPlatformDialogHelper::AndroidLayout); default: return QPlatformTheme::themeHint(hint); } -- cgit v1.2.3 From 33f16af30764d82af3f6c196618165b7b5f5afee Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 18 Jun 2019 16:18:05 +0200 Subject: rhi: metal: Reuse drawable after a frame without a present The source of this is likely a "window" grab from Qt Quick. Requesting a new drawable is an error in this case since we do not enqueue a present for the current one. Change-Id: I64bab03ff46743ce1f270b251229be126f9ad9fb Reviewed-by: Lars Knoll --- src/gui/rhi/qrhimetal.mm | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm index 22d4e4e6d2..214374e0c6 100644 --- a/src/gui/rhi/qrhimetal.mm +++ b/src/gui/rhi/qrhimetal.mm @@ -1206,8 +1206,10 @@ 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; + } __block int thisFrameSlot = currentFrameSlot; [swapChainD->cbWrapper.d->cb addCompletedHandler: ^(id) { @@ -1689,7 +1691,8 @@ void QRhiMetal::beginPass(QRhiCommandBuffer *cb, if (color0.needsDrawableForTex || color0.needsDrawableForResolveTex) { Q_ASSERT(currentSwapChain); QMetalSwapChain *swapChainD = QRHI_RES(QMetalSwapChain, currentSwapChain); - swapChainD->d->curDrawable = [swapChainD->d->layer nextDrawable]; + if (!swapChainD->d->curDrawable) + swapChainD->d->curDrawable = [swapChainD->d->layer nextDrawable]; if (!swapChainD->d->curDrawable) { qWarning("No drawable"); return; @@ -3497,6 +3500,8 @@ bool QMetalSwapChain::buildOrResize() [d->layer setDevice: rhiD->d->dev]; + d->curDrawable = nil; + for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) { if (!d->sem[i]) d->sem[i] = dispatch_semaphore_create(QMTL_FRAMES_IN_FLIGHT - 1); -- cgit v1.2.3 From 6f2eabb54e3f827ebd2b90f68144b86342933869 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Wed, 19 Jun 2019 12:48:04 +0200 Subject: Remove binary compat qimage methods Change-Id: Ic8f1bd6c13468e9a19ff11a23d4b0c29afbcded4 Reviewed-by: Alexandru Croitor --- src/gui/image/image.pri | 3 -- src/gui/image/qimage.h | 9 ------ src/gui/image/qimage_compat.cpp | 66 ----------------------------------------- 3 files changed, 78 deletions(-) delete mode 100644 src/gui/image/qimage_compat.cpp (limited to 'src') diff --git a/src/gui/image/image.pri b/src/gui/image/image.pri index 70fccbc378..b482a6e142 100644 --- a/src/gui/image/image.pri +++ b/src/gui/image/image.pri @@ -59,9 +59,6 @@ win32:!winrt: SOURCES += image/qpixmap_win.cpp darwin: OBJECTIVE_SOURCES += image/qimage_darwin.mm -NO_PCH_SOURCES += image/qimage_compat.cpp -false: SOURCES += $$NO_PCH_SOURCES # Hack for QtCreator - # Built-in image format support HEADERS += \ image/qbmphandler_p.h \ diff --git a/src/gui/image/qimage.h b/src/gui/image/qimage.h index 7c68168be8..3f0d900a70 100644 --- a/src/gui/image/qimage.h +++ b/src/gui/image/qimage.h @@ -178,7 +178,6 @@ public: Format format() const; -#if defined(Q_COMPILER_REF_QUALIFIERS) && !defined(QT_COMPILING_QIMAGE_COMPAT_CPP) Q_REQUIRED_RESULT Q_ALWAYS_INLINE QImage convertToFormat(Format f, Qt::ImageConversionFlags flags = Qt::AutoColor) const & { return convertToFormat_helper(f, flags); } Q_REQUIRED_RESULT Q_ALWAYS_INLINE QImage convertToFormat(Format f, Qt::ImageConversionFlags flags = Qt::AutoColor) && @@ -188,9 +187,6 @@ public: else return convertToFormat_helper(f, flags); } -#else - Q_REQUIRED_RESULT QImage convertToFormat(Format f, Qt::ImageConversionFlags flags = Qt::AutoColor) const; -#endif Q_REQUIRED_RESULT QImage convertToFormat(Format f, const QVector &colorTable, Qt::ImageConversionFlags flags = Qt::AutoColor) const; bool reinterpretAsFormat(Format f); @@ -283,7 +279,6 @@ public: static QMatrix trueMatrix(const QMatrix &, int w, int h); QImage transformed(const QTransform &matrix, Qt::TransformationMode mode = Qt::FastTransformation) const; static QTransform trueMatrix(const QTransform &, int w, int h); -#if defined(Q_COMPILER_REF_QUALIFIERS) && !defined(QT_COMPILING_QIMAGE_COMPAT_CPP) QImage mirrored(bool horizontally = false, bool vertically = true) const & { return mirrored_helper(horizontally, vertically); } QImage &&mirrored(bool horizontally = false, bool vertically = true) && @@ -292,10 +287,6 @@ public: { return rgbSwapped_helper(); } QImage &&rgbSwapped() && { rgbSwapped_inplace(); return std::move(*this); } -#else - QImage mirrored(bool horizontally = false, bool vertically = true) const; - QImage rgbSwapped() const; -#endif void invertPixels(InvertMode = InvertRgb); QColorSpace colorSpace() const; diff --git a/src/gui/image/qimage_compat.cpp b/src/gui/image/qimage_compat.cpp deleted file mode 100644 index ba31a9ac9b..0000000000 --- a/src/gui/image/qimage_compat.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifdef QIMAGE_H -# error "This file cannot be used with precompiled headers" -#endif -#define QT_COMPILING_QIMAGE_COMPAT_CPP - -#include "qimage.h" - -QT_BEGIN_NAMESPACE - -// These implementations must be the same as the inline versions in qimage.h - -QImage QImage::convertToFormat(Format f, Qt::ImageConversionFlags flags) const -{ - return convertToFormat_helper(f, flags); -} - -QImage QImage::mirrored(bool horizontally, bool vertically) const -{ - return mirrored_helper(horizontally, vertically); -} - -QImage QImage::rgbSwapped() const -{ - return rgbSwapped_helper(); -} - -QT_END_NAMESPACE -- cgit v1.2.3 From b6ded193ee64ffe67df6d22e7a23aa1ea9e02ec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Tue, 5 Mar 2019 11:14:21 +0100 Subject: QHighDpi::fromNativePixels: use correct screen Calls like QHighDpi::fromNativePixels(point, window) would return device independent coordinates outside any screen in cases where the window is spanning multiple screens and the native point was not on the main screen. Correct this by looking up the correct screen and use its scale factor and origin when scaling coordinates. Task-number: QTBUG-73231 Change-Id: I01a3a42f42121b8d9f4ced2bb0fb023d6ae6bfe7 Reviewed-by: Friedemann Kleint --- src/gui/kernel/qhighdpiscaling.cpp | 50 +++++++++++--------------------------- src/gui/kernel/qhighdpiscaling_p.h | 37 +++++++++++++++++++++------- 2 files changed, 42 insertions(+), 45 deletions(-) (limited to 'src') diff --git a/src/gui/kernel/qhighdpiscaling.cpp b/src/gui/kernel/qhighdpiscaling.cpp index 93fcb1a216..0fea416404 100644 --- a/src/gui/kernel/qhighdpiscaling.cpp +++ b/src/gui/kernel/qhighdpiscaling.cpp @@ -452,52 +452,30 @@ QDpi QHighDpiScaling::logicalDpi() return m_logicalDpi; } -qreal QHighDpiScaling::factor(const QScreen *screen) +QHighDpiScaling::ScaleAndOrigin QHighDpiScaling::scaleAndOrigin(const QPlatformScreen *platformScreen, QPoint *nativePosition) { - // Fast path for when scaling in Qt is not used at all. if (!m_active) - return qreal(1.0); - - // The effective factor for a given screen is the product of the - // screen and global sub-factors - qreal factor = m_factor; - if (screen) - factor *= screenSubfactor(screen->handle()); - return factor; + return { qreal(1), QPoint() }; + const QPlatformScreen *actualScreen = nativePosition ? + platformScreen->screenForPosition(*nativePosition) : platformScreen; + return { m_factor * screenSubfactor(actualScreen), actualScreen->geometry().topLeft() }; } -qreal QHighDpiScaling::factor(const QPlatformScreen *platformScreen) +QHighDpiScaling::ScaleAndOrigin QHighDpiScaling::scaleAndOrigin(const QScreen *screen, QPoint *nativePosition) { if (!m_active) - return qreal(1.0); - - return m_factor * screenSubfactor(platformScreen); + return { qreal(1), QPoint() }; + if (!screen) + return { m_factor, QPoint() }; // the global factor + return scaleAndOrigin(screen->handle(), nativePosition); } -qreal QHighDpiScaling::factor(const QWindow *window) +QHighDpiScaling::ScaleAndOrigin QHighDpiScaling::scaleAndOrigin(const QWindow *window, QPoint *nativePosition) { if (!m_active) - return qreal(1.0); - - return factor(window ? window->screen() : QGuiApplication::primaryScreen()); -} - -QPoint QHighDpiScaling::origin(const QScreen *screen) -{ - return screen->geometry().topLeft(); -} - -QPoint QHighDpiScaling::origin(const QPlatformScreen *platformScreen) -{ - return platformScreen->geometry().topLeft(); -} - -QPoint QHighDpiScaling::origin(const QWindow *window) -{ - if (window && window->isTopLevel() && window->screen()) - return window->screen()->geometry().topLeft(); - - return QPoint(0, 0); + return { qreal(1), QPoint() }; + QScreen *screen = window ? window->screen() : QGuiApplication::primaryScreen(); + return scaleAndOrigin(screen, nativePosition); } #endif //QT_NO_HIGHDPISCALING diff --git a/src/gui/kernel/qhighdpiscaling_p.h b/src/gui/kernel/qhighdpiscaling_p.h index 525e3fe78e..3410c1d345 100644 --- a/src/gui/kernel/qhighdpiscaling_p.h +++ b/src/gui/kernel/qhighdpiscaling_p.h @@ -78,14 +78,23 @@ public: static void setScreenFactor(QScreen *window, qreal factor); static bool isActive() { return m_active; } - static qreal factor(const QWindow *window); - static qreal factor(const QScreen *screen); - static qreal factor(const QPlatformScreen *platformScreen); - static QPoint origin(const QScreen *screen); - static QPoint origin(const QPlatformScreen *platformScreen); - static QPoint origin(const QWindow *window); - static QPoint mapPositionToNative(const QPoint &pos, const QPlatformScreen *platformScreen); + + struct ScaleAndOrigin + { + qreal factor; + QPoint origin; + }; + static ScaleAndOrigin scaleAndOrigin(const QPlatformScreen *platformScreen, QPoint *nativePosition = nullptr); + static ScaleAndOrigin scaleAndOrigin(const QScreen *screen, QPoint *nativePosition = nullptr); + static ScaleAndOrigin scaleAndOrigin(const QWindow *platformScreen, QPoint *nativePosition = nullptr); + + template + static qreal factor(C *context, QPoint *nativePosition = nullptr) { + return scaleAndOrigin(context, nativePosition).factor; + } + static QPoint mapPositionFromNative(const QPoint &pos, const QPlatformScreen *platformScreen); + static QPoint mapPositionToNative(const QPoint &pos, const QPlatformScreen *platformScreen); static QPoint mapPositionToGlobal(const QPoint &pos, const QPoint &windowGlobalPosition, const QWindow *window); static QPoint mapPositionFromGlobal(const QPoint &pos, const QPoint &windowGlobalPosition, const QWindow *window); static QDpi logicalDpi(); @@ -166,16 +175,26 @@ inline QRegion scale(const QRegion ®ion, qreal scaleFactor, QPoint origin = Q return scaled; } +template +inline QPoint position(T) { return QPoint(); } +inline QPoint position(QPoint point) { return point; } +inline QPoint position(QPointF point) { return point.toPoint(); } +inline QPoint position(QRect rect) { return rect.center(); } +inline QPoint position(QRectF rect) { return rect.center().toPoint(); } + template T fromNativePixels(const T &value, const C *context) { - return scale(value, qreal(1) / QHighDpiScaling::factor(context), QHighDpiScaling::origin(context)); + QPoint nativePosition = position(value); + QHighDpiScaling::ScaleAndOrigin so = QHighDpiScaling::scaleAndOrigin(context, &nativePosition); + return scale(value, qreal(1) / so.factor, so.origin); } template T toNativePixels(const T &value, const C *context) { - return scale(value, QHighDpiScaling::factor(context), QHighDpiScaling::origin(context)); + QHighDpiScaling::ScaleAndOrigin so = QHighDpiScaling::scaleAndOrigin(context); + return scale(value, so.factor, so.origin); } template -- cgit v1.2.3 From 5b5e8f78fecbe2bd9279bfa9ef10015cef8b1bc7 Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Fri, 21 Jun 2019 13:37:40 +0200 Subject: NSMenuItem/NSMenu - set the submenu properly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ... in case the submenu is set from a slot, attached to the aboutToShow() signal. Normally, with a 'statically' pre-populated menu, we set 'submenu' property on a menu item from 'updateItem' callback in our menu delegate. After that, AppKit calls our delegate's willOpen call back and this is where we emit 'aboutToShow'. Unfortunately, if an application tries to create a nested menu 'dynamically' at this point, it never becomes 'submenu' of the item, since 'updateItem' was already handled at this point. We catch this case in QCocoaMenuItem and call setAttachedItem if needed. Fixes: QTBUG-76060 Change-Id: I676bf1d8529b9ddbfc90e4dff422b39668b7a5fa Reviewed-by: Tor Arne Vestbø --- src/plugins/platforms/cocoa/qcocoamenu.h | 4 ++++ src/plugins/platforms/cocoa/qcocoamenu.mm | 10 ++++++++++ src/plugins/platforms/cocoa/qcocoamenuitem.mm | 8 ++++++++ src/plugins/platforms/cocoa/qcocoansmenu.mm | 2 ++ 4 files changed, 24 insertions(+) (limited to 'src') diff --git a/src/plugins/platforms/cocoa/qcocoamenu.h b/src/plugins/platforms/cocoa/qcocoamenu.h index 34d8428188..a957710a88 100644 --- a/src/plugins/platforms/cocoa/qcocoamenu.h +++ b/src/plugins/platforms/cocoa/qcocoamenu.h @@ -92,6 +92,9 @@ public: bool isOpen() const; void setIsOpen(bool isOpen); + bool isAboutToShow() const; + void setIsAboutToShow(bool isAbout); + void timerEvent(QTimerEvent *e) override; void syncMenuItem_helper(QPlatformMenuItem *menuItem, bool menubarUpdate); @@ -111,6 +114,7 @@ private: bool m_parentEnabled:1; bool m_visible:1; bool m_isOpen:1; + bool m_isAboutToShow:1; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoamenu.mm b/src/plugins/platforms/cocoa/qcocoamenu.mm index f34988721d..8c4fca0d29 100644 --- a/src/plugins/platforms/cocoa/qcocoamenu.mm +++ b/src/plugins/platforms/cocoa/qcocoamenu.mm @@ -178,6 +178,16 @@ void QCocoaMenu::setIsOpen(bool isOpen) m_isOpen = isOpen; } +bool QCocoaMenu::isAboutToShow() const +{ + return m_isAboutToShow; +} + +void QCocoaMenu::setIsAboutToShow(bool isAbout) +{ + m_isAboutToShow = isAbout; +} + void QCocoaMenu::removeMenuItem(QPlatformMenuItem *menuItem) { QMacAutoReleasePool pool; diff --git a/src/plugins/platforms/cocoa/qcocoamenuitem.mm b/src/plugins/platforms/cocoa/qcocoamenuitem.mm index e54b6284e5..ef9b2659d2 100644 --- a/src/plugins/platforms/cocoa/qcocoamenuitem.mm +++ b/src/plugins/platforms/cocoa/qcocoamenuitem.mm @@ -140,6 +140,12 @@ void QCocoaMenuItem::setMenu(QPlatformMenu *menu) if (menu == m_menu) return; + bool setAttached = false; + if ([m_native.menu isKindOfClass:[QCocoaNSMenu class]]) { + auto parentMenu = static_cast(m_native.menu); + setAttached = parentMenu.platformMenu && parentMenu.platformMenu->isAboutToShow(); + } + if (m_menu && m_menu->menuParent() == this) { m_menu->setMenuParent(nullptr); // Free the menu from its parent's influence @@ -153,6 +159,8 @@ void QCocoaMenuItem::setMenu(QPlatformMenu *menu) if (m_menu) { m_menu->setMenuParent(this); m_menu->propagateEnabledState(isEnabled()); + if (setAttached) + m_menu->setAttachedItem(m_native); } else { // we previously had a menu, but no longer // clear out our item so the nexy sync() call builds a new one diff --git a/src/plugins/platforms/cocoa/qcocoansmenu.mm b/src/plugins/platforms/cocoa/qcocoansmenu.mm index 65b0832d9f..c51460282a 100644 --- a/src/plugins/platforms/cocoa/qcocoansmenu.mm +++ b/src/plugins/platforms/cocoa/qcocoansmenu.mm @@ -195,7 +195,9 @@ static NSString *qt_mac_removePrivateUnicode(NSString *string) return; platformMenu->setIsOpen(true); + platformMenu->setIsAboutToShow(true); emit platformMenu->aboutToShow(); + platformMenu->setIsAboutToShow(false); } - (void)menuDidClose:(NSMenu *)menu -- cgit v1.2.3 From 64033c35927efed044bac2eebd903304452abaa0 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Fri, 21 Jun 2019 13:29:50 +0200 Subject: Output all font families in HTML output Fix our generation of font-family CSS so it contains the full list of families. Change-Id: I37d5efa64faeb4b6aeb7e2c5d6a54ff07febe9cc Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/gui/text/qtextdocument.cpp | 33 +++++++++++++++++++++++++++++++-- src/gui/text/qtextdocument_p.h | 1 + 2 files changed, 32 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp index 4e5ba8e038..2c677dffe0 100644 --- a/src/gui/text/qtextdocument.cpp +++ b/src/gui/text/qtextdocument.cpp @@ -2299,7 +2299,11 @@ QString QTextHtmlExporter::toHtml(const QByteArray &encoding, ExportMode mode) if (mode == ExportEntireDocument) { html += QLatin1String(" style=\""); - emitFontFamily(defaultCharFormat.fontFamily()); + QStringList fontFamilies = defaultCharFormat.fontFamilies().toStringList(); + if (!fontFamilies.isEmpty()) + emitFontFamily(fontFamilies); + else + emitFontFamily(defaultCharFormat.fontFamily()); if (defaultCharFormat.hasProperty(QTextFormat::FontPointSize)) { html += QLatin1String(" font-size:"); @@ -2361,8 +2365,12 @@ bool QTextHtmlExporter::emitCharFormatStyle(const QTextCharFormat &format) bool attributesEmitted = false; { + const QStringList families = format.fontFamilies().toStringList(); const QString family = format.fontFamily(); - if (!family.isEmpty() && family != defaultCharFormat.fontFamily()) { + if (!families.isEmpty() && families != defaultCharFormat.fontFamilies().toStringList()) { + emitFontFamily(families); + attributesEmitted = true; + } else if (!family.isEmpty() && family != defaultCharFormat.fontFamily()) { emitFontFamily(family); attributesEmitted = true; } @@ -2639,6 +2647,27 @@ void QTextHtmlExporter::emitFontFamily(const QString &family) html += QLatin1Char(';'); } +void QTextHtmlExporter::emitFontFamily(const QStringList &families) +{ + html += QLatin1String(" font-family:"); + + bool first = true; + for (const QString &family : families) { + QLatin1String quote("\'"); + if (family.contains(QLatin1Char('\''))) + quote = QLatin1String("""); + + if (!first) + html += QLatin1String(","); + else + first = false; + html += quote; + html += family.toHtmlEscaped(); + html += quote; + } + html += QLatin1Char(';'); +} + void QTextHtmlExporter::emitMargins(const QString &top, const QString &bottom, const QString &left, const QString &right) { html += QLatin1String(" margin-top:"); diff --git a/src/gui/text/qtextdocument_p.h b/src/gui/text/qtextdocument_p.h index cad9131fbf..d668066091 100644 --- a/src/gui/text/qtextdocument_p.h +++ b/src/gui/text/qtextdocument_p.h @@ -396,6 +396,7 @@ private: void emitPageBreakPolicy(QTextFormat::PageBreakFlags policy); void emitFontFamily(const QString &family); + void emitFontFamily(const QStringList &families); void emitBackgroundAttribute(const QTextFormat &format); QString findUrlForImage(const QTextDocument *doc, qint64 cacheKey, bool isPixmap); -- cgit v1.2.3 From 084e17c4e113f72dc43180bae2cb4e8c68b2a0f6 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 19 Jun 2019 13:42:01 +0200 Subject: Avoid undefined behavior in qjsonwriter.cpp See comment in qnumeric_p.h:convertDoubleTo for details. Change-Id: Ifcd13f7f67995af6a60e50ccabe843a855be04ae Reviewed-by: Thiago Macieira --- src/corelib/serialization/qjsonwriter.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/corelib/serialization/qjsonwriter.cpp b/src/corelib/serialization/qjsonwriter.cpp index 12ce20ef09..32feb41183 100644 --- a/src/corelib/serialization/qjsonwriter.cpp +++ b/src/corelib/serialization/qjsonwriter.cpp @@ -43,6 +43,7 @@ #include "qjsonwriter_p.h" #include "qjson_p.h" #include "private/qutfcodec_p.h" +#include QT_BEGIN_NAMESPACE @@ -131,8 +132,9 @@ static void valueToJson(const QJsonPrivate::Base *b, const QJsonPrivate::Value & case QJsonValue::Double: { const double d = v.toDouble(b); if (qIsFinite(d)) { // +2 to format to ensure the expected precision - const double abs = std::abs(d); - json += QByteArray::number(d, abs == static_cast(abs) ? 'f' : 'g', QLocale::FloatingPointShortest); + quint64 absInt; + json += QByteArray::number(d, convertDoubleTo(std::abs(d), &absInt) ? 'f' : 'g', + QLocale::FloatingPointShortest); } else { json += "null"; // +INF || -INF || NaN (see RFC4627#section2.4) } -- cgit v1.2.3 From 53da86fe8c2a39bebe665231480cad3cc08b6cb3 Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Tue, 16 Apr 2019 09:58:23 +0200 Subject: Add a QVkConvenience class with vkFormatFromGlFormat Converts from OpenGL formats to Vulkan formats. There are commented out lines for the formats in QOpenGLTexture::TextureFormat for which it was hard to find an unambiguous mapping to vkFormat. Task-number: QTBUG-75108 Change-Id: I06a7fd8df7d98cef314410ffd79ca9cff6599357 Reviewed-by: Laszlo Agocs (cherry picked from commit b21b07877a96c175ee51e83e1b41425c2e67beb3) Reviewed-by: Lars Knoll Reviewed-by: Johan Helsing --- .../vkconvenience/qvkconvenience.cpp | 215 +++++++++++++++++++++ .../vkconvenience/qvkconvenience_p.h | 67 +++++++ .../vkconvenience/vkconvenience.pro | 2 + 3 files changed, 284 insertions(+) create mode 100644 src/platformsupport/vkconvenience/qvkconvenience.cpp create mode 100644 src/platformsupport/vkconvenience/qvkconvenience_p.h (limited to 'src') diff --git a/src/platformsupport/vkconvenience/qvkconvenience.cpp b/src/platformsupport/vkconvenience/qvkconvenience.cpp new file mode 100644 index 0000000000..462cdc9e0d --- /dev/null +++ b/src/platformsupport/vkconvenience/qvkconvenience.cpp @@ -0,0 +1,215 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qvkconvenience_p.h" + +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QVkConvenience + \brief A collection of static helper functions for Vulkan support + \since 5.14 + \internal + \ingroup qpa + */ + +VkFormat QVkConvenience::vkFormatFromGlFormat(uint glFormat) +{ + using GlFormat = QOpenGLTexture::TextureFormat; + switch (glFormat) { + case GlFormat::NoFormat: return VK_FORMAT_UNDEFINED; // GL_NONE + + // Unsigned normalized formats + case GlFormat::R8_UNorm: return VK_FORMAT_R8_UNORM; // GL_R8 + case GlFormat::RG8_UNorm: return VK_FORMAT_R8G8_UNORM; // GL_RG8 + case GlFormat::RGB8_UNorm: return VK_FORMAT_R8G8B8_UNORM; // GL_RGB8 + case GlFormat::RGBA8_UNorm: return VK_FORMAT_R8G8B8A8_UNORM; // GL_RGBA8 + + case GlFormat::R16_UNorm: return VK_FORMAT_R16_UNORM; // GL_R16 + case GlFormat::RG16_UNorm: return VK_FORMAT_R16G16_UNORM; // GL_RG16 + case GlFormat::RGB16_UNorm: return VK_FORMAT_R16G16B16_UNORM; // GL_RGB16 + case GlFormat::RGBA16_UNorm: return VK_FORMAT_R16G16B16A16_UNORM; // GL_RGBA16 + + // Signed normalized formats + case GlFormat::R8_SNorm: return VK_FORMAT_R8_SNORM; // GL_R8_SNORM + case GlFormat::RG8_SNorm: return VK_FORMAT_R8G8_SNORM; // GL_RG8_SNORM + case GlFormat::RGB8_SNorm: return VK_FORMAT_R8G8B8_SNORM; // GL_RGB8_SNORM + case GlFormat::RGBA8_SNorm: return VK_FORMAT_R8G8B8A8_SNORM; // GL_RGBA8_SNORM + + case GlFormat::R16_SNorm: return VK_FORMAT_R16_SNORM; // GL_R16_SNORM + case GlFormat::RG16_SNorm: return VK_FORMAT_R16G16_SNORM; // GL_RG16_SNORM + case GlFormat::RGB16_SNorm: return VK_FORMAT_R16G16B16_SNORM; // GL_RGB16_SNORM + case GlFormat::RGBA16_SNorm: return VK_FORMAT_R16G16B16A16_SNORM; // GL_RGBA16_SNORM + + // Unsigned integer formats + case GlFormat::R8U: return VK_FORMAT_R8_UINT; // GL_R8UI + case GlFormat::RG8U: return VK_FORMAT_R8G8_UINT; // GL_RG8UI + case GlFormat::RGB8U: return VK_FORMAT_R8G8B8_UINT; // GL_RGB8UI + case GlFormat::RGBA8U: return VK_FORMAT_R8G8B8A8_UINT; // GL_RGBA8UI + + case GlFormat::R16U: return VK_FORMAT_R16_UINT; // GL_R16UI + case GlFormat::RG16U: return VK_FORMAT_R16G16_UINT; // GL_RG16UI + case GlFormat::RGB16U: return VK_FORMAT_R16G16B16_UINT; // GL_RGB16UI + case GlFormat::RGBA16U: return VK_FORMAT_R16G16B16A16_UINT; // GL_RGBA16UI + + case GlFormat::R32U: return VK_FORMAT_R32_UINT; // GL_R32UI + case GlFormat::RG32U: return VK_FORMAT_R32G32_UINT; // GL_RG32UI + case GlFormat::RGB32U: return VK_FORMAT_R32G32B32_UINT; // GL_RGB32UI + case GlFormat::RGBA32U: return VK_FORMAT_R32G32B32A32_UINT; // GL_RGBA32UI + + // Signed integer formats + case GlFormat::R8I: return VK_FORMAT_R8_SINT; // GL_R8I + case GlFormat::RG8I: return VK_FORMAT_R8G8_SINT; // GL_RG8I + case GlFormat::RGB8I: return VK_FORMAT_R8G8B8_SINT; // GL_RGB8I + case GlFormat::RGBA8I: return VK_FORMAT_R8G8B8A8_SINT; // GL_RGBA8I + + case GlFormat::R16I: return VK_FORMAT_R16_SINT; // GL_R16I + case GlFormat::RG16I: return VK_FORMAT_R16G16_SINT; // GL_RG16I + case GlFormat::RGB16I: return VK_FORMAT_R16G16B16_SINT; // GL_RGB16I + case GlFormat::RGBA16I: return VK_FORMAT_R16G16B16A16_SINT; // GL_RGBA16I + + case GlFormat::R32I: return VK_FORMAT_R32_SINT; // GL_R32I + case GlFormat::RG32I: return VK_FORMAT_R32G32_SINT; // GL_RG32I + case GlFormat::RGB32I: return VK_FORMAT_R32G32B32_SINT; // GL_RGB32I + case GlFormat::RGBA32I: return VK_FORMAT_R32G32B32A32_SINT; // GL_RGBA32I + + // Floating point formats + case GlFormat::R16F: return VK_FORMAT_R16_SFLOAT; // GL_R16F + case GlFormat::RG16F: return VK_FORMAT_R16G16_SFLOAT; // GL_RG16F + case GlFormat::RGB16F: return VK_FORMAT_R16G16B16_SFLOAT; // GL_RGB16F + case GlFormat::RGBA16F: return VK_FORMAT_R16G16B16A16_SFLOAT; // GL_RGBA16F + + case GlFormat::R32F: return VK_FORMAT_R32_SFLOAT; // GL_R32F + case GlFormat::RG32F: return VK_FORMAT_R32G32_SFLOAT; // GL_RG32F + case GlFormat::RGB32F: return VK_FORMAT_R32G32B32_SFLOAT; // GL_RGB32F + case GlFormat::RGBA32F: return VK_FORMAT_R32G32B32A32_SFLOAT; // GL_RGBA32F + + // Packed formats + case GlFormat::RGB9E5: return VK_FORMAT_E5B9G9R9_UFLOAT_PACK32; // GL_RGB9_E5 + case GlFormat::RG11B10F: return VK_FORMAT_B10G11R11_UFLOAT_PACK32; // GL_R11F_G11F_B10F +// case GlFormat::RG3B2: return VK_FORMAT_R3_G3_B2; // GL_R3_G3_B2 + case GlFormat::R5G6B5: return VK_FORMAT_R5G6B5_UNORM_PACK16; // GL_RGB565 + case GlFormat::RGB5A1: return VK_FORMAT_R5G5B5A1_UNORM_PACK16; // GL_RGB5_A1 + case GlFormat::RGBA4: return VK_FORMAT_R4G4B4A4_UNORM_PACK16; // GL_RGBA4 + case GlFormat::RGB10A2: return VK_FORMAT_A2R10G10B10_UINT_PACK32; // GL_RGB10_A2UI + + // Depth formats +// case Format::D16: return VK_FORMAT_DEPTH_COMPONENT16; // GL_DEPTH_COMPONENT16 +// case Format::D24: return VK_FORMAT_DEPTH_COMPONENT24; // GL_DEPTH_COMPONENT24 +// case Format::D24S8: return VK_FORMAT_DEPTH24_STENCIL8; // GL_DEPTH24_STENCIL8 +// case Format::D32: return VK_FORMAT_DEPTH_COMPONENT32; // GL_DEPTH_COMPONENT32 +// case Format::D32F: return VK_FORMAT_DEPTH_COMPONENT32F; // GL_DEPTH_COMPONENT32F +// case Format::D32FS8X24: return VK_FORMAT_DEPTH32F_STENCIL8; // GL_DEPTH32F_STENCIL8 +// case Format::S8: return VK_FORMAT_STENCIL_INDEX8; // GL_STENCIL_INDEX8 + + // Compressed formats + case GlFormat::RGB_DXT1: return VK_FORMAT_BC1_RGB_UNORM_BLOCK; // GL_COMPRESSED_RGB_S3TC_DXT1_EXT + case GlFormat::RGBA_DXT1: return VK_FORMAT_BC1_RGBA_UNORM_BLOCK; // GL_COMPRESSED_RGBA_S3TC_DXT1_EXT + case GlFormat::RGBA_DXT3: return VK_FORMAT_BC2_UNORM_BLOCK; // GL_COMPRESSED_RGBA_S3TC_DXT3_EXT + case GlFormat::RGBA_DXT5: return VK_FORMAT_BC3_UNORM_BLOCK; // GL_COMPRESSED_RGBA_S3TC_DXT5_EXT + case GlFormat::R_ATI1N_UNorm: return VK_FORMAT_BC4_UNORM_BLOCK; // GL_COMPRESSED_RED_RGTC1 + case GlFormat::R_ATI1N_SNorm: return VK_FORMAT_BC4_SNORM_BLOCK; // GL_COMPRESSED_SIGNED_RED_RGTC1 + case GlFormat::RG_ATI2N_UNorm: return VK_FORMAT_BC5_UNORM_BLOCK; // GL_COMPRESSED_RG_RGTC2 + case GlFormat::RG_ATI2N_SNorm: return VK_FORMAT_BC5_SNORM_BLOCK; // GL_COMPRESSED_SIGNED_RG_RGTC2 + case GlFormat::RGB_BP_UNSIGNED_FLOAT: return VK_FORMAT_BC6H_UFLOAT_BLOCK; // GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB + case GlFormat::RGB_BP_SIGNED_FLOAT: return VK_FORMAT_BC6H_SFLOAT_BLOCK; // GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB + case GlFormat::RGB_BP_UNorm: return VK_FORMAT_BC7_UNORM_BLOCK; // GL_COMPRESSED_RGBA_BPTC_UNORM_ARB + case GlFormat::R11_EAC_UNorm: return VK_FORMAT_EAC_R11_UNORM_BLOCK; // GL_COMPRESSED_R11_EAC + case GlFormat::R11_EAC_SNorm: return VK_FORMAT_EAC_R11_SNORM_BLOCK; // GL_COMPRESSED_SIGNED_R11_EAC + case GlFormat::RG11_EAC_UNorm: return VK_FORMAT_EAC_R11G11_UNORM_BLOCK; // GL_COMPRESSED_RG11_EAC + case GlFormat::RG11_EAC_SNorm: return VK_FORMAT_EAC_R11G11_SNORM_BLOCK; // GL_COMPRESSED_SIGNED_RG11_EAC + case GlFormat::RGB8_ETC2: return VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK; // GL_COMPRESSED_RGB8_ETC2 + case GlFormat::SRGB8_ETC2: return VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK; // GL_COMPRESSED_SRGB8_ETC2 + case GlFormat::RGB8_PunchThrough_Alpha1_ETC2: return VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK; // GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 + case GlFormat::SRGB8_PunchThrough_Alpha1_ETC2: return VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK; // GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 + case GlFormat::RGBA8_ETC2_EAC: return VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK; // GL_COMPRESSED_RGBA8_ETC2_EAC + case GlFormat::SRGB8_Alpha8_ETC2_EAC: return VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK; // GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC +// case GlFormat::RGB8_ETC1: return VK_FORMAT_ETC1_RGB8_OES; // GL_ETC1_RGB8_OES + case GlFormat::RGBA_ASTC_4x4: return VK_FORMAT_ASTC_4x4_UNORM_BLOCK; // GL_COMPRESSED_RGBA_ASTC_4x4_KHR + case GlFormat::RGBA_ASTC_5x4: return VK_FORMAT_ASTC_5x4_UNORM_BLOCK; // GL_COMPRESSED_RGBA_ASTC_5x4_KHR + case GlFormat::RGBA_ASTC_5x5: return VK_FORMAT_ASTC_5x5_UNORM_BLOCK; // GL_COMPRESSED_RGBA_ASTC_5x5_KHR + case GlFormat::RGBA_ASTC_6x5: return VK_FORMAT_ASTC_6x5_UNORM_BLOCK; // GL_COMPRESSED_RGBA_ASTC_6x5_KHR + case GlFormat::RGBA_ASTC_6x6: return VK_FORMAT_ASTC_6x6_UNORM_BLOCK; // GL_COMPRESSED_RGBA_ASTC_6x6_KHR + case GlFormat::RGBA_ASTC_8x5: return VK_FORMAT_ASTC_8x5_UNORM_BLOCK; // GL_COMPRESSED_RGBA_ASTC_8x5_KHR + case GlFormat::RGBA_ASTC_8x6: return VK_FORMAT_ASTC_8x6_UNORM_BLOCK; // GL_COMPRESSED_RGBA_ASTC_8x6_KHR + case GlFormat::RGBA_ASTC_8x8: return VK_FORMAT_ASTC_8x8_UNORM_BLOCK; // GL_COMPRESSED_RGBA_ASTC_8x8_KHR + case GlFormat::RGBA_ASTC_10x5: return VK_FORMAT_ASTC_10x5_UNORM_BLOCK; // GL_COMPRESSED_RGBA_ASTC_10x5_KHR + case GlFormat::RGBA_ASTC_10x6: return VK_FORMAT_ASTC_10x6_UNORM_BLOCK; // GL_COMPRESSED_RGBA_ASTC_10x6_KHR + case GlFormat::RGBA_ASTC_10x8: return VK_FORMAT_ASTC_10x8_UNORM_BLOCK; // GL_COMPRESSED_RGBA_ASTC_10x8_KHR + case GlFormat::RGBA_ASTC_10x10: return VK_FORMAT_ASTC_10x10_UNORM_BLOCK; // GL_COMPRESSED_RGBA_ASTC_10x10_KHR + case GlFormat::RGBA_ASTC_12x10: return VK_FORMAT_ASTC_12x10_UNORM_BLOCK; // GL_COMPRESSED_RGBA_ASTC_12x10_KHR + case GlFormat::RGBA_ASTC_12x12: return VK_FORMAT_ASTC_12x12_UNORM_BLOCK; // GL_COMPRESSED_RGBA_ASTC_12x12_KHR + case GlFormat::SRGB8_Alpha8_ASTC_4x4: return VK_FORMAT_ASTC_4x4_SRGB_BLOCK; // GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR + case GlFormat::SRGB8_Alpha8_ASTC_5x4: return VK_FORMAT_ASTC_5x4_SRGB_BLOCK; // GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR + case GlFormat::SRGB8_Alpha8_ASTC_5x5: return VK_FORMAT_ASTC_5x5_SRGB_BLOCK; // GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR + case GlFormat::SRGB8_Alpha8_ASTC_6x5: return VK_FORMAT_ASTC_6x5_SRGB_BLOCK; // GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR + case GlFormat::SRGB8_Alpha8_ASTC_6x6: return VK_FORMAT_ASTC_6x6_SRGB_BLOCK; // GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR + case GlFormat::SRGB8_Alpha8_ASTC_8x5: return VK_FORMAT_ASTC_8x5_SRGB_BLOCK; // GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR + case GlFormat::SRGB8_Alpha8_ASTC_8x6: return VK_FORMAT_ASTC_8x6_SRGB_BLOCK; // GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR + case GlFormat::SRGB8_Alpha8_ASTC_8x8: return VK_FORMAT_ASTC_8x8_SRGB_BLOCK; // GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR + case GlFormat::SRGB8_Alpha8_ASTC_10x5: return VK_FORMAT_ASTC_10x5_SRGB_BLOCK; // GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR + case GlFormat::SRGB8_Alpha8_ASTC_10x6: return VK_FORMAT_ASTC_10x6_SRGB_BLOCK; // GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR + case GlFormat::SRGB8_Alpha8_ASTC_10x8: return VK_FORMAT_ASTC_10x8_SRGB_BLOCK; // GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR + case GlFormat::SRGB8_Alpha8_ASTC_10x10: return VK_FORMAT_ASTC_10x10_SRGB_BLOCK; // GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR + case GlFormat::SRGB8_Alpha8_ASTC_12x10: return VK_FORMAT_ASTC_12x10_SRGB_BLOCK; // GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR + case GlFormat::SRGB8_Alpha8_ASTC_12x12: return VK_FORMAT_ASTC_12x12_SRGB_BLOCK; // GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR + + // sRGB formats + case GlFormat::SRGB8: return VK_FORMAT_R8G8B8_SRGB; // GL_SRGB8 + case GlFormat::SRGB8_Alpha8: return VK_FORMAT_R8G8B8A8_SRGB; // GL_SRGB8_ALPHA8 + case GlFormat::SRGB_DXT1: return VK_FORMAT_BC1_RGB_SRGB_BLOCK; // GL_COMPRESSED_SRGB_S3TC_DXT1_EXT + case GlFormat::SRGB_Alpha_DXT1: return VK_FORMAT_BC1_RGBA_SRGB_BLOCK; // GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT + case GlFormat::SRGB_Alpha_DXT3: return VK_FORMAT_BC2_SRGB_BLOCK; // GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT + case GlFormat::SRGB_Alpha_DXT5: return VK_FORMAT_BC3_SRGB_BLOCK; // GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT + case GlFormat::SRGB_BP_UNorm: return VK_FORMAT_BC7_SRGB_BLOCK; // GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB + + // ES 2 formats +// case GlFormat::DepthFormat: return VK_FORMAT_DEPTH_COMPONENT; // GL_DEPTH_COMPONENT +// case GlFormat::AlphaFormat: return VK_FORMAT_ALPHA; // GL_ALPHA +// case GlFormat::RGBFormat: return VK_FORMAT_RGB; // GL_RGB +// case GlFormat::RGBAFormat: return VK_FORMAT_RGBA; // GL_RGBA +// case GlFormat::LuminanceFormat: return VK_FORMAT_LUMINANCE; // GL_LUMINANCE +// case GlFormat::LuminanceAlphaFormat: return VK_FORMAT; + default: return VK_FORMAT_UNDEFINED; + } +} + +QT_END_NAMESPACE diff --git a/src/platformsupport/vkconvenience/qvkconvenience_p.h b/src/platformsupport/vkconvenience/qvkconvenience_p.h new file mode 100644 index 0000000000..1dd1dfc4a7 --- /dev/null +++ b/src/platformsupport/vkconvenience/qvkconvenience_p.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QVKCONVENIENCE_P_H +#define QVKCONVENIENCE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +class QVkConvenience +{ +public: + static VkFormat vkFormatFromGlFormat(uint glFormat); +}; + +QT_END_NAMESPACE + +#endif // QVKCONVENIENCE_P_H diff --git a/src/platformsupport/vkconvenience/vkconvenience.pro b/src/platformsupport/vkconvenience/vkconvenience.pro index 7a4cdb041d..ee540024cf 100644 --- a/src/platformsupport/vkconvenience/vkconvenience.pro +++ b/src/platformsupport/vkconvenience/vkconvenience.pro @@ -8,9 +8,11 @@ DEFINES += QT_NO_CAST_FROM_ASCII PRECOMPILED_HEADER = ../../corelib/global/qt_pch.h SOURCES += \ + qvkconvenience.cpp \ qbasicvulkanplatforminstance.cpp HEADERS += \ + qvkconvenience_p.h \ qbasicvulkanplatforminstance_p.h load(qt_module) -- cgit v1.2.3 From f6ed6037596783ef1562f586eff69186610ba620 Mon Sep 17 00:00:00 2001 From: Val Doroshchuk Date: Fri, 21 Jun 2019 11:24:06 +0200 Subject: ANGLE: Invalidate client window area when resizing swap chain Inspired by: https://codereview.appspot.com/6812076/ Resizing a window larger results in the newly exposed region being invalidated but the old region is treated as valid. This can result in the old region no longer updating. Was added to D3D9. Improving a fix from Filippo Cucchetto: https://codereview.qt-project.org/c/qt/qtbase/+/195336 and pushing to D3D11. ifndef protects against compilation error for WinRT. Invalidate() should be used only for desktop apps. Task-number: QTBUG-46074 Change-Id: Ie24b8dffe130b970f2362337ac4f9bee666f82b2 Reviewed-by: Oliver Wolff --- .../libANGLE/renderer/d3d/d3d11/SwapChain11.cpp | 4 +++ ...idate-client-window-area-when-resizing-sw.patch | 37 ++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 src/angle/patches/0015-ANGLE-Invalidate-client-window-area-when-resizing-sw.patch (limited to 'src') diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp index dcfd06484d..e8f13b388f 100644 --- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp +++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp @@ -504,6 +504,10 @@ EGLint SwapChain11::resize(const gl::Context *context, ASSERT(SUCCEEDED(result)); if (SUCCEEDED(result)) { +#ifndef ANGLE_ENABLE_WINDOWS_STORE + if (mNativeWindow->getNativeWindow()) + InvalidateRect(mNativeWindow->getNativeWindow(), nullptr, FALSE); +#endif const auto &format = d3d11::Format::Get(mOffscreenRenderTargetFormat, mRenderer->getRenderer11DeviceCaps()); mBackBufferTexture.set(backbufferTexture, format); diff --git a/src/angle/patches/0015-ANGLE-Invalidate-client-window-area-when-resizing-sw.patch b/src/angle/patches/0015-ANGLE-Invalidate-client-window-area-when-resizing-sw.patch new file mode 100644 index 0000000000..9380437761 --- /dev/null +++ b/src/angle/patches/0015-ANGLE-Invalidate-client-window-area-when-resizing-sw.patch @@ -0,0 +1,37 @@ +From 7d300c6e7d05f4e31c966f1298d11da3eae9d679 Mon Sep 17 00:00:00 2001 +From: Val Doroshchuk +Date: Fri, 21 Jun 2019 11:24:06 +0200 +Subject: [PATCH] ANGLE: Invalidate client window area when resizing swap chain + +Inspired by: +https://codereview.appspot.com/6812076/ +Resizing a window larger results in the newly exposed region being invalidated +but the old region is treated as valid. +This can result in the old region no longer updating. +Was added to D3D9. + +Improving a fix from Filippo Cucchetto: +https://codereview.qt-project.org/c/qt/qtbase/+/195336 +and pushing to D3D11. + +ifndef protects against compilation error for WinRT. +Invalidate() should be used only for desktop apps. + +diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp +index dcfd06484d..e8f13b388f 100644 +--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp ++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp +@@ -504,6 +504,10 @@ EGLint SwapChain11::resize(const gl::Context *context, + ASSERT(SUCCEEDED(result)); + if (SUCCEEDED(result)) + { ++#ifndef ANGLE_ENABLE_WINDOWS_STORE ++ if (mNativeWindow->getNativeWindow()) ++ InvalidateRect(mNativeWindow->getNativeWindow(), nullptr, FALSE); ++#endif + const auto &format = + d3d11::Format::Get(mOffscreenRenderTargetFormat, mRenderer->getRenderer11DeviceCaps()); + mBackBufferTexture.set(backbufferTexture, format); +-- +2.14.2.windows.1 + -- cgit v1.2.3 From 484fc8545fba6cb7516185e58db8075874d241b7 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 19 Jun 2019 16:23:54 +0200 Subject: rhi: d3d11: Add compute and load/store res support Also revise how we reset shader input/outputs at the beginning of a pass. Change-Id: I6d4057f32318ca09b12e16c602bb1033a3ec8e3c Reviewed-by: Lars Knoll --- src/gui/rhi/qrhid3d11.cpp | 470 +++++++++++++++++++++++++++++++++++++------- src/gui/rhi/qrhid3d11_p_p.h | 60 +++++- 2 files changed, 450 insertions(+), 80 deletions(-) (limited to 'src') diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index ce1617045b..6d5caec57a 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -376,7 +376,7 @@ bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const case QRhi::ElementIndexUint: return true; case QRhi::Compute: - return false; + return true; default: Q_UNREACHABLE(); return false; @@ -460,10 +460,11 @@ void QRhiD3D11::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::RenderPass); QD3D11GraphicsPipeline *psD = QRHI_RES(QD3D11GraphicsPipeline, 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; QD3D11CommandBuffer::Command cmd; @@ -478,11 +479,16 @@ void QRhiD3D11::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) { QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); - Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::RenderPass); - Q_ASSERT(cbD->currentPipeline); + Q_ASSERT(cbD->recordingPass != QD3D11CommandBuffer::NoPass); + QD3D11GraphicsPipeline *gfxPsD = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentGraphicsPipeline); + QD3D11ComputePipeline *compPsD = QRHI_RES(QD3D11ComputePipeline, cbD->currentComputePipeline); - if (!srb) - srb = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentPipeline)->m_shaderResourceBindings; + if (!srb) { + if (gfxPsD) + srb = gfxPsD->m_shaderResourceBindings; + else + srb = compPsD->m_shaderResourceBindings; + } QD3D11ShaderResourceBindings *srbD = QRHI_RES(QD3D11ShaderResourceBindings, srb); @@ -525,6 +531,34 @@ void QRhiD3D11::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind } } break; + case QRhiShaderResourceBinding::ImageLoad: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::ImageStore: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::ImageLoadStore: + { + QD3D11Texture *texD = QRHI_RES(QD3D11Texture, b->u.simage.tex); + if (texD->generation != bd.simage.generation || texD->m_id != bd.simage.id) { + srbUpdate = true; + bd.simage.id = texD->m_id; + bd.simage.generation = texD->generation; + } + } + break; + case QRhiShaderResourceBinding::BufferLoad: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::BufferStore: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::BufferLoadStore: + { + QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, b->u.sbuf.buf); + if (bufD->generation != bd.sbuf.generation || bufD->m_id != bd.sbuf.id) { + srbUpdate = true; + bd.sbuf.id = bufD->m_id; + bd.sbuf.generation = bufD->generation; + } + } + break; default: Q_UNREACHABLE(); break; @@ -534,10 +568,17 @@ void QRhiD3D11::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind if (srbUpdate) updateShaderResourceBindings(srbD); - 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 || srbUpdate || hasDynamicOffsetInSrb) { - cbD->currentSrb = srb; + if (srbChanged || srbRebuilt || srbUpdate || hasDynamicOffsetInSrb) { + if (gfxPsD) { + cbD->currentGraphicsSrb = srb; + cbD->currentComputeSrb = nullptr; + } else { + cbD->currentGraphicsSrb = nullptr; + cbD->currentComputeSrb = srb; + } cbD->currentSrbGeneration = srbD->generation; QD3D11CommandBuffer::Command cmd; @@ -545,7 +586,7 @@ void QRhiD3D11::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind cmd.args.bindShaderResources.srb = srbD; // dynamic offsets have to be applied at the time of executing the bind // operations, not here - cmd.args.bindShaderResources.offsetOnlyChange = !srbChanged && !srbUpdate && hasDynamicOffsetInSrb; + cmd.args.bindShaderResources.offsetOnlyChange = !srbChanged && !srbRebuilt && !srbUpdate && hasDynamicOffsetInSrb; cmd.args.bindShaderResources.dynamicOffsetCount = 0; if (hasDynamicOffsetInSrb) { if (dynamicOffsetCount < QD3D11CommandBuffer::Command::MAX_UBUF_BINDINGS) { @@ -599,7 +640,7 @@ void QRhiD3D11::setVertexInput(QRhiCommandBuffer *cb, cmd.args.bindVertexBuffers.startSlot = startBinding; cmd.args.bindVertexBuffers.slotCount = bindingCount; const QVector inputBindings = - QRHI_RES(QD3D11GraphicsPipeline, cbD->currentPipeline)->m_vertexInputLayout.bindings(); + QRHI_RES(QD3D11GraphicsPipeline, cbD->currentGraphicsPipeline)->m_vertexInputLayout.bindings(); for (int i = 0, ie = qMin(bindingCount, inputBindings.count()); i != ie; ++i) { QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, bindings[i].first); cmd.args.bindVertexBuffers.buffers[i] = bufD->buffer; @@ -688,7 +729,7 @@ void QRhiD3D11::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) QD3D11CommandBuffer::Command cmd; cmd.cmd = QD3D11CommandBuffer::Command::BlendConstants; - cmd.args.blendConstants.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentPipeline); + cmd.args.blendConstants.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentGraphicsPipeline); cmd.args.blendConstants.c[0] = c.redF(); cmd.args.blendConstants.c[1] = c.greenF(); cmd.args.blendConstants.c[2] = c.blueF(); @@ -703,7 +744,7 @@ void QRhiD3D11::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) QD3D11CommandBuffer::Command cmd; cmd.cmd = QD3D11CommandBuffer::Command::StencilRef; - cmd.args.stencilRef.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentPipeline); + cmd.args.stencilRef.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentGraphicsPipeline); cmd.args.stencilRef.ref = refValue; cbD->commands.append(cmd); } @@ -716,7 +757,7 @@ void QRhiD3D11::draw(QRhiCommandBuffer *cb, quint32 vertexCount, QD3D11CommandBuffer::Command cmd; cmd.cmd = QD3D11CommandBuffer::Command::Draw; - cmd.args.draw.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentPipeline); + cmd.args.draw.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentGraphicsPipeline); cmd.args.draw.vertexCount = vertexCount; cmd.args.draw.instanceCount = instanceCount; cmd.args.draw.firstVertex = firstVertex; @@ -732,7 +773,7 @@ void QRhiD3D11::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, QD3D11CommandBuffer::Command cmd; cmd.cmd = QD3D11CommandBuffer::Command::DrawIndexed; - cmd.args.drawIndexed.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentPipeline); + cmd.args.drawIndexed.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentGraphicsPipeline); cmd.args.drawIndexed.indexCount = indexCount; cmd.args.drawIndexed.instanceCount = instanceCount; cmd.args.drawIndexed.firstIndex = firstIndex; @@ -795,8 +836,12 @@ void QRhiD3D11::endExternal(QRhiCommandBuffer *cb) QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); Q_ASSERT(cbD->commands.isEmpty()); cbD->resetCachedState(); - if (cbD->currentTarget) // could be compute, no rendertarget then - enqueueSetRenderTarget(cbD, cbD->currentTarget); + if (cbD->currentTarget) { // could be compute, no rendertarget then + QD3D11CommandBuffer::Command fbCmd; + fbCmd.cmd = QD3D11CommandBuffer::Command::SetRenderTarget; + fbCmd.args.setRenderTarget.rt = cbD->currentTarget; + cbD->commands.append(fbCmd); + } } QRhi::FrameOpResult QRhiD3D11::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) @@ -1379,14 +1424,6 @@ void QRhiD3D11::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *r enqueueResourceUpdates(cb, resourceUpdates); } -void QRhiD3D11::enqueueSetRenderTarget(QD3D11CommandBuffer *cbD, QRhiRenderTarget *rt) -{ - QD3D11CommandBuffer::Command fbCmd; - fbCmd.cmd = QD3D11CommandBuffer::Command::SetRenderTarget; - fbCmd.args.setRenderTarget.rt = rt; - cbD->commands.append(fbCmd); -} - void QRhiD3D11::beginPass(QRhiCommandBuffer *cb, QRhiRenderTarget *rt, const QColor &colorClearValue, @@ -1407,7 +1444,13 @@ void QRhiD3D11::beginPass(QRhiCommandBuffer *cb, wantsColorClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents); wantsDsClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents); } - enqueueSetRenderTarget(cbD, rt); + + QD3D11CommandBuffer::Command fbCmd; + fbCmd.cmd = QD3D11CommandBuffer::Command::ResetShaderResources; + cbD->commands.append(fbCmd); + fbCmd.cmd = QD3D11CommandBuffer::Command::SetRenderTarget; + fbCmd.args.setRenderTarget.rt = rt; + cbD->commands.append(fbCmd); QD3D11CommandBuffer::Command clearCmd; clearCmd.cmd = QD3D11CommandBuffer::Command::Clear; @@ -1499,6 +1542,10 @@ void QRhiD3D11::beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch if (resourceUpdates) enqueueResourceUpdates(cb, resourceUpdates); + QD3D11CommandBuffer::Command cmd; + cmd.cmd = QD3D11CommandBuffer::Command::ResetShaderResources; + cbD->commands.append(cmd); + cbD->recordingPass = QD3D11CommandBuffer::ComputePass; } @@ -1515,16 +1562,34 @@ void QRhiD3D11::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *r void QRhiD3D11::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps) { - Q_UNUSED(cb); - Q_UNUSED(ps); + QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::ComputePass); + QD3D11ComputePipeline *psD = QRHI_RES(QD3D11ComputePipeline, ps); + const bool pipelineChanged = cbD->currentComputePipeline != ps || cbD->currentPipelineGeneration != psD->generation; + + if (pipelineChanged) { + cbD->currentGraphicsPipeline = nullptr; + cbD->currentComputePipeline = psD; + cbD->currentPipelineGeneration = psD->generation; + + QD3D11CommandBuffer::Command cmd; + cmd.cmd = QD3D11CommandBuffer::Command::BindComputePipeline; + cmd.args.bindComputePipeline.ps = psD; + cbD->commands.append(cmd); + } } void QRhiD3D11::dispatch(QRhiCommandBuffer *cb, int x, int y, int z) { - Q_UNUSED(cb); - Q_UNUSED(x); - Q_UNUSED(y); - Q_UNUSED(z); + QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); + Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::ComputePass); + + QD3D11CommandBuffer::Command cmd; + cmd.cmd = QD3D11CommandBuffer::Command::Dispatch; + cmd.args.dispatch.x = x; + cmd.args.dispatch.y = y; + cmd.args.dispatch.z = z; + cbD->commands.append(cmd); } void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD) @@ -1537,12 +1602,21 @@ void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD) srbD->fsubufoffsets.clear(); srbD->fsubufsizes.clear(); + srbD->csubufs.clear(); + srbD->csubufoffsets.clear(); + srbD->csubufsizes.clear(); + srbD->vssamplers.clear(); srbD->vsshaderresources.clear(); srbD->fssamplers.clear(); srbD->fsshaderresources.clear(); + srbD->cssamplers.clear(); + srbD->csshaderresources.clear(); + + srbD->csUAVs.clear(); + for (int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) { const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&srbD->sortedBindings[i]); QD3D11ShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[i]); @@ -1571,6 +1645,11 @@ void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD) srbD->fsubufoffsets.feed(b->binding, offsetInConstants); srbD->fsubufsizes.feed(b->binding, sizeInConstants); } + if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) { + srbD->csubufs.feed(b->binding, bufD->buffer); + srbD->csubufoffsets.feed(b->binding, offsetInConstants); + srbD->csubufsizes.feed(b->binding, sizeInConstants); + } } break; case QRhiShaderResourceBinding::SampledTexture: @@ -1591,6 +1670,46 @@ void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD) srbD->fssamplers.feed(b->binding, samplerD->samplerState); srbD->fsshaderresources.feed(b->binding, texD->srv); } + if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) { + srbD->cssamplers.feed(b->binding, samplerD->samplerState); + srbD->csshaderresources.feed(b->binding, texD->srv); + } + } + break; + case QRhiShaderResourceBinding::ImageLoad: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::ImageStore: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::ImageLoadStore: + { + QD3D11Texture *texD = QRHI_RES(QD3D11Texture, b->u.simage.tex); + bd.simage.id = texD->m_id; + bd.simage.generation = texD->generation; + if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) { + ID3D11UnorderedAccessView *uav = texD->unorderedAccessViewForLevel(b->u.simage.level); + if (uav) + srbD->csUAVs.feed(b->binding, uav); + } else { + qWarning("Unordered access only supported at compute stage"); + } + } + break; + case QRhiShaderResourceBinding::BufferLoad: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::BufferStore: + Q_FALLTHROUGH(); + case QRhiShaderResourceBinding::BufferLoadStore: + { + QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, b->u.sbuf.buf); + bd.sbuf.id = bufD->m_id; + bd.sbuf.generation = bufD->generation; + if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) { + ID3D11UnorderedAccessView *uav = bufD->unorderedAccessView(); + if (uav) + srbD->csUAVs.feed(b->binding, uav); + } else { + qWarning("Unordered access only supported at compute stage"); + } } break; default: @@ -1607,11 +1726,20 @@ void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD) srbD->fsubufoffsets.finish(); srbD->fsubufsizes.finish(); + srbD->csubufs.finish(); + srbD->csubufoffsets.finish(); + srbD->csubufsizes.finish(); + srbD->vssamplers.finish(); srbD->vsshaderresources.finish(); srbD->fssamplers.finish(); srbD->fsshaderresources.finish(); + + srbD->cssamplers.finish(); + srbD->csshaderresources.finish(); + + srbD->csUAVs.finish(); } void QRhiD3D11::executeBufferHostWritesForCurrentFrame(QD3D11Buffer *bufD) @@ -1631,6 +1759,27 @@ void QRhiD3D11::executeBufferHostWritesForCurrentFrame(QD3D11Buffer *bufD) } } +static void applyDynamicOffsets(QVarLengthArray *offsets, + int batchIndex, + QRhiBatchedBindings *ubufs, + QRhiBatchedBindings *ubufoffsets, + const uint *dynOfsPairs, int dynOfsPairCount) +{ + const UINT count = ubufs->batches[batchIndex].resources.count(); + const UINT startBinding = ubufs->batches[batchIndex].startBinding; + *offsets = ubufoffsets->batches[batchIndex].resources; + for (UINT b = 0; b < count; ++b) { + for (int di = 0; di < dynOfsPairCount; ++di) { + const uint binding = dynOfsPairs[2 * di]; + if (binding == startBinding + b) { + const uint offsetInConstants = dynOfsPairs[2 * di + 1]; + (*offsets)[b] = offsetInConstants; + break; + } + } + } +} + void QRhiD3D11::bindShaderResources(QD3D11ShaderResourceBindings *srbD, const uint *dynOfsPairs, int dynOfsPairCount, bool offsetOnlyChange) @@ -1653,6 +1802,15 @@ void QRhiD3D11::bindShaderResources(QD3D11ShaderResourceBindings *srbD, contextState.fsHighestActiveSrvBinding = qMax(contextState.fsHighestActiveSrvBinding, batch.startBinding + batch.resources.count() - 1); } + + for (const auto &batch : srbD->cssamplers.batches) + context->CSSetSamplers(batch.startBinding, batch.resources.count(), batch.resources.constData()); + + for (const auto &batch : srbD->csshaderresources.batches) { + context->CSSetShaderResources(batch.startBinding, batch.resources.count(), batch.resources.constData()); + contextState.csHighestActiveSrvBinding = qMax(contextState.csHighestActiveSrvBinding, + batch.startBinding + batch.resources.count() - 1); + } } for (int i = 0, ie = srbD->vsubufs.batches.count(); i != ie; ++i) { @@ -1663,21 +1821,10 @@ void QRhiD3D11::bindShaderResources(QD3D11ShaderResourceBindings *srbD, srbD->vsubufoffsets.batches[i].resources.constData(), srbD->vsubufsizes.batches[i].resources.constData()); } else { - const UINT count = srbD->vsubufs.batches[i].resources.count(); - const UINT startBinding = srbD->vsubufs.batches[i].startBinding; - QVarLengthArray offsets = srbD->vsubufoffsets.batches[i].resources; - for (UINT b = 0; b < count; ++b) { - for (int di = 0; di < dynOfsPairCount; ++di) { - const uint binding = dynOfsPairs[2 * di]; - if (binding == startBinding + b) { - const uint offsetInConstants = dynOfsPairs[2 * di + 1]; - offsets[b] = offsetInConstants; - break; - } - } - } - context->VSSetConstantBuffers1(startBinding, - count, + QVarLengthArray offsets; + applyDynamicOffsets(&offsets, i, &srbD->vsubufs, &srbD->vsubufoffsets, dynOfsPairs, dynOfsPairCount); + context->VSSetConstantBuffers1(srbD->vsubufs.batches[i].startBinding, + srbD->vsubufs.batches[i].resources.count(), srbD->vsubufs.batches[i].resources.constData(), offsets.constData(), srbD->vsubufsizes.batches[i].resources.constData()); @@ -1692,33 +1839,73 @@ void QRhiD3D11::bindShaderResources(QD3D11ShaderResourceBindings *srbD, srbD->fsubufoffsets.batches[i].resources.constData(), srbD->fsubufsizes.batches[i].resources.constData()); } else { - const UINT count = srbD->fsubufs.batches[i].resources.count(); - const UINT startBinding = srbD->fsubufs.batches[i].startBinding; - QVarLengthArray offsets = srbD->fsubufoffsets.batches[i].resources; - for (UINT b = 0; b < count; ++b) { - for (int di = 0; di < dynOfsPairCount; ++di) { - const uint binding = dynOfsPairs[2 * di]; - if (binding == startBinding + b) { - const uint offsetInConstants = dynOfsPairs[2 * di + 1]; - offsets[b] = offsetInConstants; - break; - } - } - } - context->PSSetConstantBuffers1(startBinding, - count, + QVarLengthArray offsets; + applyDynamicOffsets(&offsets, i, &srbD->fsubufs, &srbD->fsubufoffsets, dynOfsPairs, dynOfsPairCount); + context->PSSetConstantBuffers1(srbD->fsubufs.batches[i].startBinding, + srbD->fsubufs.batches[i].resources.count(), srbD->fsubufs.batches[i].resources.constData(), offsets.constData(), srbD->fsubufsizes.batches[i].resources.constData()); } } + + for (int i = 0, ie = srbD->csubufs.batches.count(); i != ie; ++i) { + if (!dynOfsPairCount) { + context->CSSetConstantBuffers1(srbD->csubufs.batches[i].startBinding, + srbD->csubufs.batches[i].resources.count(), + srbD->csubufs.batches[i].resources.constData(), + srbD->csubufoffsets.batches[i].resources.constData(), + srbD->csubufsizes.batches[i].resources.constData()); + } else { + QVarLengthArray offsets; + applyDynamicOffsets(&offsets, i, &srbD->csubufs, &srbD->csubufoffsets, dynOfsPairs, dynOfsPairCount); + context->CSSetConstantBuffers1(srbD->csubufs.batches[i].startBinding, + srbD->csubufs.batches[i].resources.count(), + srbD->csubufs.batches[i].resources.constData(), + offsets.constData(), + srbD->csubufsizes.batches[i].resources.constData()); + } + } + + for (int i = 0, ie = srbD->csUAVs.batches.count(); i != ie; ++i) { + const uint startBinding = srbD->csUAVs.batches[i].startBinding; + const uint count = srbD->csUAVs.batches[i].resources.count(); + context->CSSetUnorderedAccessViews(startBinding, + count, + srbD->csUAVs.batches[i].resources.constData(), + nullptr); + contextState.csHighestActiveUavBinding = qMax(contextState.csHighestActiveUavBinding, + startBinding + count - 1); + } } -void QRhiD3D11::setRenderTarget(QRhiRenderTarget *rt) +void QRhiD3D11::resetShaderResources() { - // The new output cannot be bound as input from the previous frame, - // otherwise the debug layer complains. Avoid this. - const int nullsrvCount = qMax(contextState.vsHighestActiveSrvBinding, contextState.fsHighestActiveSrvBinding) + 1; + // Output cannot be bound on input etc. + + if (contextState.vsHasIndexBufferBound) { + context->IASetIndexBuffer(nullptr, DXGI_FORMAT_R16_UINT, 0); + contextState.vsHasIndexBufferBound = false; + } + + if (contextState.vsHighestActiveVertexBufferBinding >= 0) { + const int count = contextState.vsHighestActiveVertexBufferBinding + 1; + QVarLengthArray nullbufs(count); + for (int i = 0; i < count; ++i) + nullbufs[i] = nullptr; + QVarLengthArray nullstrides(count); + for (int i = 0; i < count; ++i) + nullstrides[i] = 0; + QVarLengthArray nulloffsets(count); + for (int i = 0; i < count; ++i) + nulloffsets[i] = 0; + context->IASetVertexBuffers(0, count, nullbufs.constData(), nullstrides.constData(), nulloffsets.constData()); + contextState.vsHighestActiveVertexBufferBinding = -1; + } + + int nullsrvCount = qMax(contextState.vsHighestActiveSrvBinding, contextState.fsHighestActiveSrvBinding); + nullsrvCount = qMax(nullsrvCount, contextState.csHighestActiveSrvBinding); + nullsrvCount += 1; if (nullsrvCount > 0) { QVarLengthArray nullsrvs(nullsrvCount); @@ -1732,9 +1919,21 @@ void QRhiD3D11::setRenderTarget(QRhiRenderTarget *rt) context->PSSetShaderResources(0, contextState.fsHighestActiveSrvBinding + 1, nullsrvs.constData()); contextState.fsHighestActiveSrvBinding = -1; } + if (contextState.csHighestActiveSrvBinding >= 0) { + context->CSSetShaderResources(0, contextState.csHighestActiveSrvBinding + 1, nullsrvs.constData()); + contextState.csHighestActiveSrvBinding = -1; + } + } + + if (contextState.csHighestActiveUavBinding >= 0) { + const int nulluavCount = contextState.csHighestActiveUavBinding + 1; + QVarLengthArray nulluavs(nulluavCount); + for (int i = 0; i < nulluavCount; ++i) + nulluavs[i] = nullptr; + context->CSSetUnorderedAccessViews(0, nulluavCount, nulluavs.constData(), nullptr); + contextState.csHighestActiveUavBinding = -1; } - QD3D11RenderTargetData *rtD = rtData(rt); - context->OMSetRenderTargets(rtD->colorAttCount, rtD->colorAttCount ? rtD->rtv : nullptr, rtD->dsv); } void QRhiD3D11::executeCommandBuffer(QD3D11CommandBuffer *cbD, QD3D11SwapChain *timestampSwapChain) @@ -1755,15 +1954,22 @@ void QRhiD3D11::executeCommandBuffer(QD3D11CommandBuffer *cbD, QD3D11SwapChain * // it around by issuing a semi-fake OMSetRenderTargets early and // writing the first timestamp only afterwards. context->Begin(tsDisjoint); - setRenderTarget(×tampSwapChain->rt); + QD3D11RenderTargetData *rtD = rtData(×tampSwapChain->rt); + context->OMSetRenderTargets(rtD->colorAttCount, rtD->colorAttCount ? rtD->rtv : nullptr, rtD->dsv); context->End(tsStart); // just record a timestamp, no Begin needed } } for (const QD3D11CommandBuffer::Command &cmd : qAsConst(cbD->commands)) { switch (cmd.cmd) { + case QD3D11CommandBuffer::Command::ResetShaderResources: + resetShaderResources(); + break; case QD3D11CommandBuffer::Command::SetRenderTarget: - setRenderTarget(cmd.args.setRenderTarget.rt); + { + QD3D11RenderTargetData *rtD = rtData(cmd.args.setRenderTarget.rt); + context->OMSetRenderTargets(rtD->colorAttCount, rtD->colorAttCount ? rtD->rtv : nullptr, rtD->dsv); + } break; case QD3D11CommandBuffer::Command::Clear: { @@ -1805,6 +2011,9 @@ void QRhiD3D11::executeCommandBuffer(QD3D11CommandBuffer *cbD, QD3D11SwapChain * } break; case QD3D11CommandBuffer::Command::BindVertexBuffers: + contextState.vsHighestActiveVertexBufferBinding = qMax( + contextState.vsHighestActiveVertexBufferBinding, + cmd.args.bindVertexBuffers.startSlot + cmd.args.bindVertexBuffers.slotCount - 1); context->IASetVertexBuffers(cmd.args.bindVertexBuffers.startSlot, cmd.args.bindVertexBuffers.slotCount, cmd.args.bindVertexBuffers.buffers, @@ -1812,6 +2021,7 @@ void QRhiD3D11::executeCommandBuffer(QD3D11CommandBuffer *cbD, QD3D11SwapChain * cmd.args.bindVertexBuffers.offsets); break; case QD3D11CommandBuffer::Command::BindIndexBuffer: + contextState.vsHasIndexBufferBound = true; context->IASetIndexBuffer(cmd.args.bindIndexBuffer.buffer, cmd.args.bindIndexBuffer.format, cmd.args.bindIndexBuffer.offset); @@ -1894,6 +2104,12 @@ void QRhiD3D11::executeCommandBuffer(QD3D11CommandBuffer *cbD, QD3D11SwapChain * case QD3D11CommandBuffer::Command::DebugMarkMsg: annotations->SetMarker(reinterpret_cast(QString::fromLatin1(cmd.args.debugMark.s).utf16())); break; + case QD3D11CommandBuffer::Command::BindComputePipeline: + context->CSSetShader(cmd.args.bindComputePipeline.ps->cs, nullptr, 0); + break; + case QD3D11CommandBuffer::Command::Dispatch: + context->Dispatch(cmd.args.dispatch.x, cmd.args.dispatch.y, cmd.args.dispatch.z); + break; default: break; } @@ -1920,6 +2136,11 @@ void QD3D11Buffer::release() buffer->Release(); buffer = nullptr; + if (uav) { + uav->Release(); + uav = nullptr; + } + QRHI_RES_RHI(QRhiD3D11); QRHI_PROF; QRHI_PROF_F(releaseBuffer(this)); @@ -1935,6 +2156,8 @@ static inline uint toD3DBufferUsage(QRhiBuffer::UsageFlags usage) u |= D3D11_BIND_INDEX_BUFFER; if (usage.testFlag(QRhiBuffer::UniformBuffer)) u |= D3D11_BIND_CONSTANT_BUFFER; + if (usage.testFlag(QRhiBuffer::StorageBuffer)) + u |= D3D11_BIND_UNORDERED_ACCESS; return u; } @@ -1948,8 +2171,13 @@ bool QD3D11Buffer::build() return false; } + if (m_usage.testFlag(QRhiBuffer::StorageBuffer) && m_type == Dynamic) { + qWarning("StorageBuffer cannot be combined with Dynamic"); + return false; + } + const int nonZeroSize = m_size <= 0 ? 256 : m_size; - const int roundedSize = m_usage.testFlag(QRhiBuffer::UniformBuffer) ? aligned(nonZeroSize, 256) : nonZeroSize; + const int roundedSize = aligned(nonZeroSize, m_usage.testFlag(QRhiBuffer::UniformBuffer) ? 256 : 4); D3D11_BUFFER_DESC desc; memset(&desc, 0, sizeof(desc)); @@ -1957,6 +2185,7 @@ bool QD3D11Buffer::build() desc.Usage = m_type == Dynamic ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT; desc.BindFlags = toD3DBufferUsage(m_usage); desc.CPUAccessFlags = m_type == Dynamic ? D3D11_CPU_ACCESS_WRITE : 0; + desc.MiscFlags = m_usage.testFlag(QRhiBuffer::StorageBuffer) ? D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS : 0; QRHI_RES_RHI(QRhiD3D11); HRESULT hr = rhiD->dev->CreateBuffer(&desc, nullptr, &buffer); @@ -1981,6 +2210,30 @@ bool QD3D11Buffer::build() return true; } +ID3D11UnorderedAccessView *QD3D11Buffer::unorderedAccessView() +{ + if (uav) + return uav; + + // SPIRV-Cross generated HLSL uses RWByteAddressBuffer + D3D11_UNORDERED_ACCESS_VIEW_DESC desc; + memset(&desc, 0, sizeof(desc)); + desc.Format = DXGI_FORMAT_R32_TYPELESS; + desc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER; + desc.Buffer.FirstElement = 0; + desc.Buffer.NumElements = aligned(m_size, 4) / 4; + desc.Buffer.Flags = D3D11_BUFFER_UAV_FLAG_RAW; + + QRHI_RES_RHI(QRhiD3D11); + HRESULT hr = rhiD->dev->CreateUnorderedAccessView(buffer, &desc, &uav); + if (FAILED(hr)) { + qWarning("Failed to create UAV: %s", qPrintable(comErrorMessage(hr))); + return nullptr; + } + + return uav; +} + QD3D11RenderBuffer::QD3D11RenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize, int sampleCount, QRhiRenderBuffer::Flags flags) : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags) @@ -2097,6 +2350,8 @@ QD3D11Texture::QD3D11Texture(QRhiImplementation *rhi, Format format, const QSize int sampleCount, Flags flags) : QRhiTexture(rhi, format, pixelSize, sampleCount, flags) { + for (int i = 0; i < QRhi::MAX_LEVELS; ++i) + perLevelViews[i] = nullptr; } QD3D11Texture::~QD3D11Texture() @@ -2114,6 +2369,13 @@ void QD3D11Texture::release() srv = nullptr; } + for (int i = 0; i < QRhi::MAX_LEVELS; ++i) { + if (perLevelViews[i]) { + perLevelViews[i]->Release(); + perLevelViews[i] = nullptr; + } + } + if (owns) tex->Release(); @@ -2244,6 +2506,8 @@ bool QD3D11Texture::build() bindFlags |= D3D11_BIND_RENDER_TARGET; miscFlags |= D3D11_RESOURCE_MISC_GENERATE_MIPS; } + if (m_flags.testFlag(UsedWithLoadStore)) + bindFlags |= D3D11_BIND_UNORDERED_ACCESS; D3D11_TEXTURE2D_DESC desc; memset(&desc, 0, sizeof(desc)); @@ -2306,6 +2570,37 @@ const QRhiNativeHandles *QD3D11Texture::nativeHandles() return &nativeHandlesStruct; } +ID3D11UnorderedAccessView *QD3D11Texture::unorderedAccessViewForLevel(int level) +{ + if (perLevelViews[level]) + return perLevelViews[level]; + + const bool isCube = m_flags.testFlag(CubeMap); + D3D11_UNORDERED_ACCESS_VIEW_DESC desc; + memset(&desc, 0, sizeof(desc)); + desc.Format = dxgiFormat; + if (isCube) { + desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2DARRAY; + desc.Texture2DArray.MipSlice = level; + desc.Texture2DArray.FirstArraySlice = 0; + desc.Texture2DArray.ArraySize = 6; + } else { + desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D; + desc.Texture2D.MipSlice = level; + } + + QRHI_RES_RHI(QRhiD3D11); + ID3D11UnorderedAccessView *uav = nullptr; + HRESULT hr = rhiD->dev->CreateUnorderedAccessView(tex, &desc, &uav); + if (FAILED(hr)) { + qWarning("Failed to create UAV: %s", qPrintable(comErrorMessage(hr))); + return nullptr; + } + + perLevelViews[level] = uav; + return uav; +} + QD3D11Sampler::QD3D11Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode, AddressMode u, AddressMode v) : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v) @@ -3121,11 +3416,40 @@ QD3D11ComputePipeline::~QD3D11ComputePipeline() void QD3D11ComputePipeline::release() { + QRHI_RES_RHI(QRhiD3D11); + + if (!cs) + return; + + cs->Release(); + cs = nullptr; + + rhiD->unregisterResource(this); } bool QD3D11ComputePipeline::build() { - return false; + if (cs) + release(); + + QRHI_RES_RHI(QRhiD3D11); + + QString error; + QByteArray bytecode = compileHlslShaderSource(m_shaderStage.shader(), m_shaderStage.shaderVariant(), &error); + if (bytecode.isEmpty()) { + qWarning("HLSL compute shader compilation failed: %s", qPrintable(error)); + return false; + } + + HRESULT hr = rhiD->dev->CreateComputeShader(bytecode.constData(), bytecode.size(), nullptr, &cs); + if (FAILED(hr)) { + qWarning("Failed to create compute shader: %s", qPrintable(comErrorMessage(hr))); + return false; + } + + generation += 1; + rhiD->registerResource(this); + return true; } QD3D11CommandBuffer::QD3D11CommandBuffer(QRhiImplementation *rhi) diff --git a/src/gui/rhi/qrhid3d11_p_p.h b/src/gui/rhi/qrhid3d11_p_p.h index 775f256cb7..688f79b3b7 100644 --- a/src/gui/rhi/qrhid3d11_p_p.h +++ b/src/gui/rhi/qrhid3d11_p_p.h @@ -65,9 +65,12 @@ struct QD3D11Buffer : public QRhiBuffer void release() override; bool build() override; + ID3D11UnorderedAccessView *unorderedAccessView(); + ID3D11Buffer *buffer = nullptr; QByteArray dynBuf; bool hasPendingDynamicUpdates = false; + ID3D11UnorderedAccessView *uav = nullptr; uint generation = 0; friend class QRhiD3D11; }; @@ -101,6 +104,7 @@ struct QD3D11Texture : public QRhiTexture bool prepareBuild(QSize *adjustedSize = nullptr); bool finishBuild(); + ID3D11UnorderedAccessView *unorderedAccessViewForLevel(int level); ID3D11Texture2D *tex = nullptr; bool owns = true; @@ -109,6 +113,7 @@ struct QD3D11Texture : public QRhiTexture uint mipLevelCount = 0; DXGI_SAMPLE_DESC sampleDesc; QRhiD3D11TextureNativeHandles nativeHandlesStruct; + ID3D11UnorderedAccessView *perLevelViews[QRhi::MAX_LEVELS]; uint generation = 0; friend class QRhiD3D11; }; @@ -209,10 +214,20 @@ struct QD3D11ShaderResourceBindings : public QRhiShaderResourceBindings quint64 samplerId; uint samplerGeneration; }; + struct BoundStorageImageData { + quint64 id; + uint generation; + }; + struct BoundStorageBufferData { + quint64 id; + uint generation; + }; struct BoundResourceData { union { BoundUniformBufferData ubuf; BoundSampledTextureData stex; + BoundStorageImageData simage; + BoundStorageBufferData sbuf; }; }; QVector boundResourceData; @@ -225,12 +240,21 @@ struct QD3D11ShaderResourceBindings : public QRhiShaderResourceBindings QRhiBatchedBindings fsubufoffsets; QRhiBatchedBindings fsubufsizes; + QRhiBatchedBindings csubufs; + QRhiBatchedBindings csubufoffsets; + QRhiBatchedBindings csubufsizes; + QRhiBatchedBindings vssamplers; QRhiBatchedBindings vsshaderresources; QRhiBatchedBindings fssamplers; QRhiBatchedBindings fsshaderresources; + QRhiBatchedBindings cssamplers; + QRhiBatchedBindings csshaderresources; + + QRhiBatchedBindings csUAVs; + friend class QRhiD3D11; }; @@ -260,6 +284,10 @@ struct QD3D11ComputePipeline : public QRhiComputePipeline ~QD3D11ComputePipeline(); void release() override; bool build() override; + + ID3D11ComputeShader *cs = nullptr; + uint generation = 0; + friend class QRhiD3D11; }; struct QD3D11SwapChain; @@ -272,6 +300,7 @@ struct QD3D11CommandBuffer : public QRhiCommandBuffer struct Command { enum Cmd { + ResetShaderResources, SetRenderTarget, Clear, Viewport, @@ -290,7 +319,9 @@ struct QD3D11CommandBuffer : public QRhiCommandBuffer GenMip, DebugMarkBegin, DebugMarkEnd, - DebugMarkMsg + DebugMarkMsg, + BindComputePipeline, + Dispatch }; enum ClearFlag { Color = 1, Depth = 2, Stencil = 4 }; Cmd cmd; @@ -392,6 +423,14 @@ struct QD3D11CommandBuffer : public QRhiCommandBuffer struct { char s[64]; } debugMark; + struct { + QD3D11ComputePipeline *ps; + } bindComputePipeline; + struct { + UINT x; + UINT y; + UINT z; + } dispatch; } args; }; @@ -404,9 +443,11 @@ struct QD3D11CommandBuffer : public QRhiCommandBuffer QVector commands; PassType recordingPass; QRhiRenderTarget *currentTarget; - QRhiGraphicsPipeline *currentPipeline; + QRhiGraphicsPipeline *currentGraphicsPipeline; + QRhiComputePipeline *currentComputePipeline; uint currentPipelineGeneration; - QRhiShaderResourceBindings *currentSrb; + QRhiShaderResourceBindings *currentGraphicsSrb; + QRhiShaderResourceBindings *currentComputeSrb; uint currentSrbGeneration; ID3D11Buffer *currentIndexBuffer; quint32 currentIndexOffset; @@ -438,9 +479,11 @@ struct QD3D11CommandBuffer : public QRhiCommandBuffer resetCachedState(); } void resetCachedState() { - currentPipeline = nullptr; + currentGraphicsPipeline = nullptr; + currentComputePipeline = nullptr; currentPipelineGeneration = 0; - currentSrb = nullptr; + currentGraphicsSrb = nullptr; + currentComputeSrb = nullptr; currentSrbGeneration = 0; currentIndexBuffer = nullptr; currentIndexOffset = 0; @@ -596,11 +639,10 @@ public: void bindShaderResources(QD3D11ShaderResourceBindings *srbD, const uint *dynOfsPairs, int dynOfsPairCount, bool offsetOnlyChange); - void setRenderTarget(QRhiRenderTarget *rt); + void resetShaderResources(); void executeCommandBuffer(QD3D11CommandBuffer *cbD, QD3D11SwapChain *timestampSwapChain = nullptr); DXGI_SAMPLE_DESC effectiveSampleCount(int sampleCount) const; void finishActiveReadbacks(); - void enqueueSetRenderTarget(QD3D11CommandBuffer *cbD, QRhiRenderTarget *rt); void reportLiveObjects(ID3D11Device *device); bool debugLayer = false; @@ -614,8 +656,12 @@ public: QRhiD3D11NativeHandles nativeHandlesStruct; struct { + int vsHighestActiveVertexBufferBinding = -1; + bool vsHasIndexBufferBound = false; int vsHighestActiveSrvBinding = -1; int fsHighestActiveSrvBinding = -1; + int csHighestActiveSrvBinding = -1; + int csHighestActiveUavBinding = -1; QD3D11SwapChain *currentSwapChain = nullptr; } contextState; -- cgit v1.2.3 From 88625709058b386ee74cca536c6c5556159c99fa Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 21 Jun 2019 15:04:13 +0200 Subject: rhi: d3d: gl: Remove incorrect exec.Cmd.Buf. assumption May very well be called within an active pass when going through beginExternal() (think examples like d3d11underqml) Change-Id: Ie98e72609308f47497d83fbe10c19ad1ae8eade3 Reviewed-by: Lars Knoll --- src/gui/rhi/qrhid3d11.cpp | 2 -- src/gui/rhi/qrhigles2.cpp | 2 -- 2 files changed, 4 deletions(-) (limited to 'src') diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index 6d5caec57a..7d9c934c18 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -1938,8 +1938,6 @@ void QRhiD3D11::resetShaderResources() void QRhiD3D11::executeCommandBuffer(QD3D11CommandBuffer *cbD, QD3D11SwapChain *timestampSwapChain) { - Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::NoPass); - quint32 stencilRef = 0; float blendConstants[] = { 1, 1, 1, 1 }; diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp index 32a25dd615..7c40a36701 100644 --- a/src/gui/rhi/qrhigles2.cpp +++ b/src/gui/rhi/qrhigles2.cpp @@ -1500,8 +1500,6 @@ static inline GLenum toGlTextureCompareFunc(QRhiSampler::CompareOp op) void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb) { QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); - Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::NoPass); - GLenum indexType = GL_UNSIGNED_SHORT; quint32 indexStride = sizeof(quint16); quint32 indexOffset = 0; -- cgit v1.2.3 From 44d9e9b096738a79b6bceffcdad20da42f2e3713 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Tue, 25 Jun 2019 18:31:58 +0200 Subject: Fix crash when setting QGuiApplication palette before app is available Change-Id: Ia154f66a27cba970d179f100e66aa2daab01c9fa Reviewed-by: Timur Pocheptsov --- src/gui/kernel/qguiapplication.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index a67214bd9a..5014878bd2 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -3233,9 +3233,12 @@ void QGuiApplication::setPalette(const QPalette &pal) QGuiApplicationPrivate::app_pal = new QPalette(pal); else *QGuiApplicationPrivate::app_pal = pal; + applicationResourceFlags |= ApplicationPaletteExplicitlySet; QCoreApplication::setAttribute(Qt::AA_SetPalette); - emit qGuiApp->paletteChanged(*QGuiApplicationPrivate::app_pal); + + if (qGuiApp) + emit qGuiApp->paletteChanged(*QGuiApplicationPrivate::app_pal); } void QGuiApplicationPrivate::applyWindowGeometrySpecificationTo(QWindow *window) -- cgit v1.2.3 From e21169796297f752a62db25c722ff8e2ff66d8dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Nordheim?= Date: Fri, 21 Jun 2019 09:29:22 +0200 Subject: QHttpNetworkConnectionChannel: don't close if we're already closing In some scenarios with QNAM we call socket->close, leading to a flush, leading to an error, leading to another error emission... To work around this scenario we stop trying to close the socket if the network channel is already closing. Change-Id: Id15504f476484ce61f11ba83a5755ceb5f581f9b Fixes: QTBUG-76567 Reviewed-by: Timur Pocheptsov --- src/network/access/qhttpnetworkconnectionchannel.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index d5f63af745..074c389689 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -207,6 +207,9 @@ void QHttpNetworkConnectionChannel::init() void QHttpNetworkConnectionChannel::close() { + if (state == QHttpNetworkConnectionChannel::ClosingState) + return; + if (!socket) state = QHttpNetworkConnectionChannel::IdleState; else if (socket->state() == QAbstractSocket::UnconnectedState) -- cgit v1.2.3 From 0d6b4b519272915c2690ee12d1834823747233ab Mon Sep 17 00:00:00 2001 From: Heikki Halmet Date: Mon, 17 Jun 2019 12:26:54 +0300 Subject: Add keyword msvc-2019 to testlib blacklisting Change-Id: Ibb1d225909e6c0c8b35f3b41aeb619784012d43b Reviewed-by: Frederik Gladhorn --- src/testlib/qtestblacklist.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/testlib/qtestblacklist.cpp b/src/testlib/qtestblacklist.cpp index 886f1f75b9..6642699758 100644 --- a/src/testlib/qtestblacklist.cpp +++ b/src/testlib/qtestblacklist.cpp @@ -150,8 +150,10 @@ static QSet keywords() << "msvc-2013" # elif _MSC_VER <= 1900 << "msvc-2015" -# else +# elif _MSC_VER <= 1916 << "msvc-2017" +# else + << "msvc-2019" # endif #endif -- cgit v1.2.3 From 89655525ae56dd9662f1873efdce72cbff7bb932 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Nordheim?= Date: Fri, 21 Jun 2019 09:20:39 +0200 Subject: QHttpNetworkConnection: Change assert to early return and handle Http2 When using a http proxy (and presumably other proxies) we might have failed/aborted (aka "finished") the request and _then_ receive a "proxy authentication required" message from the proxy. In this case there is no spdy/http2 reply in the queue, so asserting is wrong. Change-Id: Id9b76b580299f6a6cd6efad62d6aaf63183816fb Fixes: QTBUG-76426 Reviewed-by: Timur Pocheptsov --- src/network/access/qhttpnetworkconnection.cpp | 12 +++++++----- src/network/access/qhttpnetworkconnectionchannel.cpp | 4 +++- 2 files changed, 10 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index c58fd24a44..10c8541c5f 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -1528,19 +1528,21 @@ void QHttpNetworkConnectionPrivate::emitProxyAuthenticationRequired(const QHttpN // dialog is displaying pauseConnection(); QHttpNetworkReply *reply; -#ifndef QT_NO_SSL - if (connectionType == QHttpNetworkConnection::ConnectionTypeSPDY) { + if (connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2 + || connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2Direct +#if QT_CONFIG(ssl) + || connectionType == QHttpNetworkConnection::ConnectionTypeSPDY +#endif + ) { + // we choose the reply to emit the proxyAuth signal from somewhat arbitrarily, // but that does not matter because the signal will ultimately be emitted // by the QNetworkAccessManager. Q_ASSERT(chan->spdyRequestsToSend.count() > 0); reply = chan->spdyRequestsToSend.cbegin().value().second; } else { // HTTP -#endif // QT_NO_SSL reply = chan->reply; -#ifndef QT_NO_SSL } -#endif // QT_NO_SSL Q_ASSERT(reply); emit reply->proxyAuthenticationRequired(proxy, auth); diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index 074c389689..38adca2633 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -1115,11 +1115,13 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket void QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator* auth) { if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2 + || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct #ifndef QT_NO_SSL || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY #endif ) { - connection->d_func()->emitProxyAuthenticationRequired(this, proxy, auth); + if (spdyRequestsToSend.count() > 0) + connection->d_func()->emitProxyAuthenticationRequired(this, proxy, auth); } else { // HTTP // Need to dequeue the request before we can emit the error. if (!reply) -- cgit v1.2.3 From 5859f7d0d9440f82086486639a707f3935696cf4 Mon Sep 17 00:00:00 2001 From: Liang Qi Date: Wed, 26 Jun 2019 10:35:53 +0000 Subject: Revert "Deprecate QAtomic::load() / store()" This reverts commit 79bdc7cf1daec75df59de9236599a9f24077511a. We haven't ported every usages to loadRelaxed() / storeRelaxed() yet. And warning as error is enabled in dev. We will revert this change when new qt5 baseline got integrated. Task-number: QTBUG-76611 Change-Id: I5b1f608fefbaca481311f376f22718f2c5047106 Reviewed-by: Friedemann Kleint Reviewed-by: Frederik Gladhorn --- src/corelib/thread/qbasicatomic.h | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/corelib/thread/qbasicatomic.h b/src/corelib/thread/qbasicatomic.h index 9804e60119..dc976819ef 100644 --- a/src/corelib/thread/qbasicatomic.h +++ b/src/corelib/thread/qbasicatomic.h @@ -99,10 +99,8 @@ public: typename Ops::Type _q_value; // Everything below is either implemented in ../arch/qatomic_XXX.h or (as fallback) in qgenericatomic.h -#if QT_DEPRECATED_SINCE(5, 14) - QT_DEPRECATED_VERSION_X_5_14("Use loadRelaxed") T load() const noexcept { return loadRelaxed(); } - QT_DEPRECATED_VERSION_X_5_14("Use storeRelaxed") void store(T newValue) noexcept { storeRelaxed(newValue); } -#endif + T load() const noexcept { return loadRelaxed(); } + void store(T newValue) noexcept { storeRelaxed(newValue); } T loadRelaxed() const noexcept { return Ops::loadRelaxed(_q_value); } void storeRelaxed(T newValue) noexcept { Ops::storeRelaxed(_q_value, newValue); } @@ -240,10 +238,8 @@ public: AtomicType _q_value; -#if QT_DEPRECATED_SINCE(5, 14) - QT_DEPRECATED_VERSION_X_5_14("Use loadRelaxed") Type load() const noexcept { return loadRelaxed(); } - QT_DEPRECATED_VERSION_X_5_14("Use storeRelaxed") void store(Type newValue) noexcept { storeRelaxed(newValue); } -#endif + Type load() const noexcept { return loadRelaxed(); } + void store(Type newValue) noexcept { storeRelaxed(newValue); } Type loadRelaxed() const noexcept { return Ops::loadRelaxed(_q_value); } void storeRelaxed(Type newValue) noexcept { Ops::storeRelaxed(_q_value, newValue); } -- cgit v1.2.3 From cc13b99781b0bbf5f9d09b2c5b081a085762cd0c Mon Sep 17 00:00:00 2001 From: Aleix Pol Date: Mon, 24 Jun 2019 17:43:16 +0200 Subject: Do not downscale png when the size is exactly right Don't go through the doScaledRead path (i.e. calling read_image_scaled) when reading an image that the target size is the image of the file we are opening. This makes the loading of the file much faster while keeping the output correct. [ChangeLog][QtGui][QImage] Improve loading time when loading png files that have the same size as the target. Change-Id: I2a33c49fe1ce52ec296c2175ee542b5bcdec2c4b Reviewed-by: Eirik Aavitsland --- src/gui/image/qpnghandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/gui/image/qpnghandler.cpp b/src/gui/image/qpnghandler.cpp index 801b30881d..e7e998efd1 100644 --- a/src/gui/image/qpnghandler.cpp +++ b/src/gui/image/qpnghandler.cpp @@ -394,7 +394,7 @@ void setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr, QSize scal } QSize outSize(width,height); if (!scaledSize.isEmpty() && quint32(scaledSize.width()) <= width && - quint32(scaledSize.height()) <= height && interlace_method == PNG_INTERLACE_NONE) { + quint32(scaledSize.height()) <= height && scaledSize != outSize && interlace_method == PNG_INTERLACE_NONE) { // Do inline downscaling outSize = scaledSize; if (doScaledRead) -- cgit v1.2.3 From 2a756e294e2efa4bcb2b51afd1c20822df251f29 Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Mon, 24 Jun 2019 22:42:28 +0200 Subject: macOS: lower the splash screen when a modal dialog blocks it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes the usability issue of a modal dialog showing up behind a splash screen, not visible to the user, but blocking user input and the application startup sequence until discarded. [ChangeLog][QtWidgets][QSlashScreen] On macOS, lower the splash screen when a modal dialog is shown to make sure the user sees the dialog. Change-Id: Ibae768f76909d930cb25dcf5cee31edc5f15c29a Fixes: QTBUG-49576 Reviewed-by: Tor Arne Vestbø --- src/plugins/platforms/cocoa/qcocoaintegration.mm | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'src') diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index 232c74b1e9..b5d63f8331 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -217,6 +217,25 @@ QCocoaIntegration::QCocoaIntegration(const QStringList ¶mList) connect(qGuiApp, &QGuiApplication::focusWindowChanged, this, &QCocoaIntegration::focusWindowChanged); + + static auto splashScreenHider = QMacKeyValueObserver(NSApp, @"modalWindow", []{ + const QWindowList allWindows = QGuiApplication::topLevelWindows(); + for (QWindow *window : allWindows) { + if ((window->flags() & Qt::SplashScreen) == Qt::SplashScreen) { + QCocoaWindow *platformWindow = static_cast(window->handle()); + NSWindow *splashWindow = platformWindow->view().window; + if (!splashWindow) + continue; + if (NSApp.modalWindow) { + NSInteger originalLevel = splashWindow.level; + splashWindow.level = NSNormalWindowLevel; + window->setProperty("_q_levelBeforeModalSession", (qlonglong)originalLevel); + } else if (NSInteger originalLevel = window->property("_q_levelBeforeModalSession").toLongLong()) { + splashWindow.level = originalLevel; + } + } + } + }); } QCocoaIntegration::~QCocoaIntegration() -- cgit v1.2.3 From 72e3d3633e4aca6742a124fb29584d46b63f40ff Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Mon, 24 Jun 2019 12:23:41 +0200 Subject: 3rd-Party/Double conversion: Fix developer build with MSVC Suppress warnings: C4244: 'argument': conversion from 'const uc16' to 'char', possible loss of data caused by various character handling routines. Amends 327bfdb671e0e263c6fb027133a54985a65194c4. Change-Id: I3818c5d0aecb9b6cee174f866b5e7e77aa96d877 Reviewed-by: Allan Sandfeld Jensen --- src/3rdparty/double-conversion/double-conversion.cc | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/3rdparty/double-conversion/double-conversion.cc b/src/3rdparty/double-conversion/double-conversion.cc index ecd1a5ef3f..881ca0adbc 100644 --- a/src/3rdparty/double-conversion/double-conversion.cc +++ b/src/3rdparty/double-conversion/double-conversion.cc @@ -38,6 +38,11 @@ #include #include +// Fix warning C4244: 'argument': conversion from 'const uc16' to 'char', possible loss of data +#ifdef _MSC_VER + __pragma(warning(disable: 4244)) +#endif + namespace double_conversion { const DoubleToStringConverter& DoubleToStringConverter::EcmaScriptConverter() { -- cgit v1.2.3 From fc940b24dbe8a118e92e335c0d9c7d4194bf3d4f Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Tue, 18 Jun 2019 17:19:08 +0300 Subject: QString: fix comments for qsizetype Change-Id: I445ba61513fbafd24834fa48ade849feae4f1324 Reviewed-by: Thiago Macieira --- src/corelib/tools/qregexp.cpp | 2 +- src/corelib/tools/qstring.cpp | 44 +++++++++++++++++++++---------------------- src/corelib/tools/qstring.h | 20 ++++++++++---------- 3 files changed, 33 insertions(+), 33 deletions(-) (limited to 'src') diff --git a/src/corelib/tools/qregexp.cpp b/src/corelib/tools/qregexp.cpp index 128df84053..dd38ba0360 100644 --- a/src/corelib/tools/qregexp.cpp +++ b/src/corelib/tools/qregexp.cpp @@ -1420,7 +1420,7 @@ void QRegExpMatchState::match(const QChar *str0, int len0, int pos0, #ifndef QT_NO_REGEXP_OPTIM if (eng->trivial && !oneTest) { - // ### Qt6: qsize + // ### Qt6: qsizetype pos = int(QtPrivate::findString(QStringView(str0, len0), pos0, QStringView(eng->goodStr.unicode(), eng->goodStr.length()), eng->cs)); matchLen = eng->goodStr.length(); matched = (pos != -1); diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index f9a9fcfe91..319f82af53 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -3701,7 +3701,7 @@ bool QString::operator>(QLatin1String other) const noexcept */ int QString::indexOf(const QString &str, int from, Qt::CaseSensitivity cs) const { - // ### Qt6: qsize + // ### Qt6: qsizetype return int(QtPrivate::findString(QStringView(unicode(), length()), from, QStringView(str.unicode(), str.length()), cs)); } #endif // QT_STRINGVIEW_LEVEL < 2 @@ -3745,7 +3745,7 @@ int QString::indexOf(const QString &str, int from, Qt::CaseSensitivity cs) const int QString::indexOf(QLatin1String str, int from, Qt::CaseSensitivity cs) const { - // ### Qt6: qsize + // ### Qt6: qsizetype return int(QtPrivate::findString(QStringView(unicode(), size()), from, str, cs)); } @@ -3758,7 +3758,7 @@ int QString::indexOf(QLatin1String str, int from, Qt::CaseSensitivity cs) const */ int QString::indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const { - // ### Qt6: qsize + // ### Qt6: qsizetype return int(qFindChar(QStringView(unicode(), length()), ch, from, cs)); } @@ -3777,7 +3777,7 @@ int QString::indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const */ int QString::indexOf(const QStringRef &str, int from, Qt::CaseSensitivity cs) const { - // ### Qt6: qsize + // ### Qt6: qsizetype return int(QtPrivate::findString(QStringView(unicode(), length()), from, QStringView(str.unicode(), str.length()), cs)); } @@ -3799,7 +3799,7 @@ int QString::indexOf(const QStringRef &str, int from, Qt::CaseSensitivity cs) co */ int QString::lastIndexOf(const QString &str, int from, Qt::CaseSensitivity cs) const { - // ### Qt6: qsize + // ### Qt6: qsizetype return int(QtPrivate::lastIndexOf(*this, from, str, cs)); } @@ -3826,7 +3826,7 @@ int QString::lastIndexOf(const QString &str, int from, Qt::CaseSensitivity cs) c */ int QString::lastIndexOf(QLatin1String str, int from, Qt::CaseSensitivity cs) const { - // ### Qt6: qsize + // ### Qt6: qsizetype return int(QtPrivate::lastIndexOf(*this, from, str, cs)); } @@ -3838,7 +3838,7 @@ int QString::lastIndexOf(QLatin1String str, int from, Qt::CaseSensitivity cs) co */ int QString::lastIndexOf(QChar ch, int from, Qt::CaseSensitivity cs) const { - // ### Qt6: qsize + // ### Qt6: qsizetype return int(qLastIndexOf(*this, ch, from, cs)); } @@ -3860,7 +3860,7 @@ int QString::lastIndexOf(QChar ch, int from, Qt::CaseSensitivity cs) const */ int QString::lastIndexOf(const QStringRef &str, int from, Qt::CaseSensitivity cs) const { - // ### Qt6: qsize + // ### Qt6: qsizetype return int(QtPrivate::lastIndexOf(*this, from, str, cs)); } #endif // QT_STRINGVIEW_LEVEL < 2 @@ -4192,7 +4192,7 @@ QString &QString::replace(const QRegularExpression &re, const QString &after) int QString::count(const QString &str, Qt::CaseSensitivity cs) const { - // ### Qt6: qsize + // ### Qt6: qsizetype return int(qt_string_count(QStringView(unicode(), size()), QStringView(str.unicode(), str.size()), cs)); } @@ -4209,7 +4209,7 @@ int QString::count(const QString &str, Qt::CaseSensitivity cs) const int QString::count(QChar ch, Qt::CaseSensitivity cs) const { - // ### Qt6: qsize + // ### Qt6: qsizetype return int(qt_string_count(QStringView(unicode(), size()), ch, cs)); } @@ -4226,7 +4226,7 @@ int QString::count(QChar ch, Qt::CaseSensitivity cs) const */ int QString::count(const QStringRef &str, Qt::CaseSensitivity cs) const { - // ### Qt6: qsize + // ### Qt6: qsizetype return int(qt_string_count(QStringView(unicode(), size()), QStringView(str.unicode(), str.size()), cs)); } @@ -11172,7 +11172,7 @@ QStringRef QString::midRef(int position, int n) const */ int QStringRef::indexOf(const QString &str, int from, Qt::CaseSensitivity cs) const { - // ### Qt6: qsize + // ### Qt6: qsizetype return int(QtPrivate::findString(QStringView(unicode(), length()), from, QStringView(str.unicode(), str.length()), cs)); } #endif // QT_STRINGVIEW_LEVEL < 2 @@ -11207,7 +11207,7 @@ int QStringRef::indexOf(const QString &str, int from, Qt::CaseSensitivity cs) co */ int QStringRef::indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const { - // ### Qt6: qsize + // ### Qt6: qsizetype return int(qFindChar(QStringView(unicode(), length()), ch, from, cs)); } @@ -11228,7 +11228,7 @@ int QStringRef::indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const */ int QStringRef::indexOf(QLatin1String str, int from, Qt::CaseSensitivity cs) const { - // ### Qt6: qsize + // ### Qt6: qsizetype return int(QtPrivate::findString(QStringView(unicode(), size()), from, str, cs)); } @@ -11249,7 +11249,7 @@ int QStringRef::indexOf(QLatin1String str, int from, Qt::CaseSensitivity cs) con */ int QStringRef::indexOf(const QStringRef &str, int from, Qt::CaseSensitivity cs) const { - // ### Qt6: qsize + // ### Qt6: qsizetype return int(QtPrivate::findString(QStringView(unicode(), size()), from, QStringView(str.unicode(), str.size()), cs)); } #endif // QT_STRINGVIEW_LEVEL < 2 @@ -11270,7 +11270,7 @@ int QStringRef::indexOf(const QStringRef &str, int from, Qt::CaseSensitivity cs) */ int QStringRef::lastIndexOf(const QString &str, int from, Qt::CaseSensitivity cs) const { - // ### Qt6: qsize + // ### Qt6: qsizetype return int(QtPrivate::lastIndexOf(*this, from, str, cs)); } @@ -11285,7 +11285,7 @@ int QStringRef::lastIndexOf(const QString &str, int from, Qt::CaseSensitivity cs */ int QStringRef::lastIndexOf(QChar ch, int from, Qt::CaseSensitivity cs) const { - // ### Qt6: qsize + // ### Qt6: qsizetype return int(qLastIndexOf(*this, ch, from, cs)); } @@ -11306,7 +11306,7 @@ int QStringRef::lastIndexOf(QChar ch, int from, Qt::CaseSensitivity cs) const */ int QStringRef::lastIndexOf(QLatin1String str, int from, Qt::CaseSensitivity cs) const { - // ### Qt6: qsize + // ### Qt6: qsizetype return int(QtPrivate::lastIndexOf(*this, from, str, cs)); } @@ -11327,7 +11327,7 @@ int QStringRef::lastIndexOf(QLatin1String str, int from, Qt::CaseSensitivity cs) */ int QStringRef::lastIndexOf(const QStringRef &str, int from, Qt::CaseSensitivity cs) const { - // ### Qt6: qsize + // ### Qt6: qsizetype return int(QtPrivate::lastIndexOf(*this, from, str, cs)); } @@ -11360,7 +11360,7 @@ int QStringRef::lastIndexOf(const QStringRef &str, int from, Qt::CaseSensitivity */ int QStringRef::count(const QString &str, Qt::CaseSensitivity cs) const { - // ### Qt6: qsize + // ### Qt6: qsizetype return int(qt_string_count(QStringView(unicode(), size()), QStringView(str.unicode(), str.size()), cs)); } @@ -11378,7 +11378,7 @@ int QStringRef::count(const QString &str, Qt::CaseSensitivity cs) const */ int QStringRef::count(QChar ch, Qt::CaseSensitivity cs) const { - // ### Qt6: qsize + // ### Qt6: qsizetype return int(qt_string_count(QStringView(unicode(), size()), ch, cs)); } @@ -11396,7 +11396,7 @@ int QStringRef::count(QChar ch, Qt::CaseSensitivity cs) const */ int QStringRef::count(const QStringRef &str, Qt::CaseSensitivity cs) const { - // ### Qt6: qsize + // ### Qt6: qsizetype return int(qt_string_count(QStringView(unicode(), size()), QStringView(str.unicode(), str.size()), cs)); } diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index b56b37edf3..37bc8d91c9 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -135,11 +135,11 @@ public: { return QtPrivate::endsWith(*this, QStringView(&c, 1), cs); } Q_REQUIRED_RESULT int indexOf(QStringView s, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept - { return int(QtPrivate::findString(*this, from, s, cs)); } // ### Qt6: qsize + { return int(QtPrivate::findString(*this, from, s, cs)); } // ### Qt6: qsizetype Q_REQUIRED_RESULT int indexOf(QLatin1String s, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept - { return int(QtPrivate::findString(*this, from, s, cs)); } // ### Qt6: qsize + { return int(QtPrivate::findString(*this, from, s, cs)); } // ### Qt6: qsizetype Q_REQUIRED_RESULT inline int indexOf(QChar c, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept - { return int(QtPrivate::findString(*this, from, QStringView(&c, 1), cs)); } // ### Qt6: qsize + { return int(QtPrivate::findString(*this, from, QStringView(&c, 1), cs)); } // ### Qt6: qsizetype Q_REQUIRED_RESULT bool contains(QStringView s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept { return indexOf(s, 0, cs) != -1; } @@ -149,11 +149,11 @@ public: { return indexOf(QStringView(&c, 1), 0, cs) != -1; } Q_REQUIRED_RESULT int lastIndexOf(QStringView s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept - { return int(QtPrivate::lastIndexOf(*this, from, s, cs)); } // ### Qt6: qsize + { return int(QtPrivate::lastIndexOf(*this, from, s, cs)); } // ### Qt6: qsizetype Q_REQUIRED_RESULT int lastIndexOf(QLatin1String s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept - { return int(QtPrivate::lastIndexOf(*this, from, s, cs)); } // ### Qt6: qsize + { return int(QtPrivate::lastIndexOf(*this, from, s, cs)); } // ### Qt6: qsizetype Q_REQUIRED_RESULT inline int lastIndexOf(QChar c, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept - { return int(QtPrivate::lastIndexOf(*this, from, QStringView(&c, 1), cs)); } // ### Qt6: qsize + { return int(QtPrivate::lastIndexOf(*this, from, QStringView(&c, 1), cs)); } // ### Qt6: qsizetype using value_type = const char; using reference = value_type&; @@ -367,7 +367,7 @@ public: int indexOf(const QStringRef &s, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; #endif Q_REQUIRED_RESULT int indexOf(QStringView s, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept - { return int(QtPrivate::findString(*this, from, s, cs)); } // ### Qt6: qsize + { return int(QtPrivate::findString(*this, from, s, cs)); } // ### Qt6: qsizetype int lastIndexOf(QChar c, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; int lastIndexOf(QLatin1String s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; #if QT_STRINGVIEW_LEVEL < 2 @@ -376,7 +376,7 @@ public: #endif Q_REQUIRED_RESULT int lastIndexOf(QStringView s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept - { return int(QtPrivate::lastIndexOf(*this, from, s, cs)); } // ### Qt6: qsize + { return int(QtPrivate::lastIndexOf(*this, from, s, cs)); } // ### Qt6: qsizetype inline bool contains(QChar c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; #if QT_STRINGVIEW_LEVEL < 2 @@ -1555,7 +1555,7 @@ public: int indexOf(const QStringRef &str, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; #endif Q_REQUIRED_RESULT int indexOf(QStringView s, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept - { return int(QtPrivate::findString(*this, from, s, cs)); } // ### Qt6: qsize + { return int(QtPrivate::findString(*this, from, s, cs)); } // ### Qt6: qsizetype int indexOf(QChar ch, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; int indexOf(QLatin1String str, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; #if QT_STRINGVIEW_LEVEL < 2 @@ -1565,7 +1565,7 @@ public: int lastIndexOf(QChar ch, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; int lastIndexOf(QLatin1String str, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; Q_REQUIRED_RESULT int lastIndexOf(QStringView s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept - { return int(QtPrivate::lastIndexOf(*this, from, s, cs)); } // ### Qt6: qsize + { return int(QtPrivate::lastIndexOf(*this, from, s, cs)); } // ### Qt6: qsizetype #if QT_STRINGVIEW_LEVEL < 2 inline bool contains(const QString &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; -- cgit v1.2.3 From 6fa34930c23c7494a3f2703777f46794ff091e2b Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Wed, 26 Jun 2019 07:24:18 +0200 Subject: QFreeList: fix memory order on block deletion Blocks are likely to have been created in a differnt thread from the one performing their deletion, so we need an acquire fence. The rest of the atomics use in the class looks ok, but nevertheless warrants a deeper analysis. Change-Id: I1571ded3a06695b0d58b5bf1d80d6283ac21f959 Reviewed-by: Thiago Macieira --- src/corelib/tools/qfreelist_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/corelib/tools/qfreelist_p.h b/src/corelib/tools/qfreelist_p.h index dcaf5688dc..5ba23b344b 100644 --- a/src/corelib/tools/qfreelist_p.h +++ b/src/corelib/tools/qfreelist_p.h @@ -218,7 +218,7 @@ template inline QFreeList::~QFreeList() { for (int i = 0; i < ConstantsType::BlockCount; ++i) - delete [] _v[i].loadRelaxed(); + delete [] _v[i].loadAcquire(); } template -- cgit v1.2.3 From 8da3eea4fb702c2dc369c1628e91a034569aa9f0 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Tue, 25 Jun 2019 22:21:44 +0200 Subject: Optimize some atomic counters Define the static QAtomic at file scope to avoid GCC's pessimisation with function-static QAtomic (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79561), and make sure the initial value is 0, so it ends up in BSS, not TEXT. In QRhi..., don't create a static instance of the wrapper class, use a file- static atomic, too. This turns the class into a glorified namespace. Change-Id: I707f628e2b434330028077223071716d5704ba32 Reviewed-by: Thiago Macieira --- src/gui/rhi/qrhi.cpp | 5 +++-- src/gui/rhi/qrhi_p_p.h | 4 ---- src/gui/text/qfont.cpp | 4 ++-- src/platformsupport/fbconvenience/qfbwindow.cpp | 5 +++-- src/plugins/platforms/android/qandroidplatformwindow.cpp | 5 +++-- 5 files changed, 11 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp index dbad63c6d1..927de859dd 100644 --- a/src/gui/rhi/qrhi.cpp +++ b/src/gui/rhi/qrhi.cpp @@ -5192,10 +5192,11 @@ int QRhi::ubufAlignment() const return d->ubufAlignment(); } +static QBasicAtomicInteger counter = Q_BASIC_ATOMIC_INITIALIZER(0); + QRhiGlobalObjectIdGenerator::Type QRhiGlobalObjectIdGenerator::newId() { - static QRhiGlobalObjectIdGenerator inst; - return ++inst.counter; + return counter.fetchAndAddRelaxed(1) + 1; } bool QRhiPassResourceTracker::isEmpty() const diff --git a/src/gui/rhi/qrhi_p_p.h b/src/gui/rhi/qrhi_p_p.h index 4fd01d3ef2..83d521f441 100644 --- a/src/gui/rhi/qrhi_p_p.h +++ b/src/gui/rhi/qrhi_p_p.h @@ -52,7 +52,6 @@ #include "qrhiprofiler_p_p.h" #include #include -#include QT_BEGIN_NAMESPACE @@ -484,9 +483,6 @@ public: using Type = quint32; #endif static Type newId(); - -private: - QAtomicInteger counter; }; class QRhiPassResourceTracker diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp index 5555422b82..2a1d207702 100644 --- a/src/gui/text/qfont.cpp +++ b/src/gui/text/qfont.cpp @@ -2799,12 +2799,12 @@ void QFontCache::cleanup() cache->setLocalData(0); } -QBasicAtomicInt font_cache_id = Q_BASIC_ATOMIC_INITIALIZER(1); +static QBasicAtomicInt font_cache_id = Q_BASIC_ATOMIC_INITIALIZER(0); QFontCache::QFontCache() : QObject(), total_cost(0), max_cost(min_cost), current_timestamp(0), fast(false), timer_id(-1), - m_id(font_cache_id.fetchAndAddRelaxed(1)) + m_id(font_cache_id.fetchAndAddRelaxed(1) + 1) { } diff --git a/src/platformsupport/fbconvenience/qfbwindow.cpp b/src/platformsupport/fbconvenience/qfbwindow.cpp index 36f92b8cea..9f5f87d9d6 100644 --- a/src/platformsupport/fbconvenience/qfbwindow.cpp +++ b/src/platformsupport/fbconvenience/qfbwindow.cpp @@ -45,11 +45,12 @@ QT_BEGIN_NAMESPACE +static QBasicAtomicInt winIdGenerator = Q_BASIC_ATOMIC_INITIALIZER(0); + QFbWindow::QFbWindow(QWindow *window) : QPlatformWindow(window), mBackingStore(0), mWindowState(Qt::WindowNoState) { - static QAtomicInt winIdGenerator(1); - mWindowId = winIdGenerator.fetchAndAddRelaxed(1); + mWindowId = winIdGenerator.fetchAndAddRelaxed(1) + 1; } QFbWindow::~QFbWindow() diff --git a/src/plugins/platforms/android/qandroidplatformwindow.cpp b/src/plugins/platforms/android/qandroidplatformwindow.cpp index c095f51fa3..4f691ce112 100644 --- a/src/plugins/platforms/android/qandroidplatformwindow.cpp +++ b/src/plugins/platforms/android/qandroidplatformwindow.cpp @@ -49,13 +49,14 @@ QT_BEGIN_NAMESPACE +static QBasicAtomicInt winIdGenerator = Q_BASIC_ATOMIC_INITIALIZER(0); + QAndroidPlatformWindow::QAndroidPlatformWindow(QWindow *window) : QPlatformWindow(window) { m_windowFlags = Qt::Widget; m_windowState = Qt::WindowNoState; - static QAtomicInt winIdGenerator(1); - m_windowId = winIdGenerator.fetchAndAddRelaxed(1); + m_windowId = winIdGenerator.fetchAndAddRelaxed(1) + 1; setWindowState(window->windowStates()); } -- cgit v1.2.3 From e36e32c21312eee42194815da82203648023a67b Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Sat, 15 Jun 2019 17:49:21 +0200 Subject: QBearerEngine: fix const-correctness of a loop with const-propagating QESDP The loop iterates over a collection of QExplicitSharedDataPointer, which traditionally doesn't propagate const. In Qt 6, it will, so prepare the code for this change, by taking the loop variable by non-const reference. Since the loop is followed by container.clear(), make it a consume-loop by looping over qExchange(container, {}). Change-Id: If12ab005544183598fd76a0c486b2df1582710d5 Reviewed-by: Giuseppe D'Angelo --- src/network/bearer/qbearerengine.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/network/bearer/qbearerengine.cpp b/src/network/bearer/qbearerengine.cpp index c06adb202f..dbcf1e530e 100644 --- a/src/network/bearer/qbearerengine.cpp +++ b/src/network/bearer/qbearerengine.cpp @@ -46,11 +46,10 @@ QT_BEGIN_NAMESPACE static void cleanUpConfigurations(QHash &configurations) { - for (const auto &ptr : qAsConst(configurations)) { + for (auto &ptr : qExchange(configurations, {})) { ptr->isValid = false; ptr->id.clear(); } - configurations.clear(); } static bool hasUsedConfiguration(const QHash &configurations) -- cgit v1.2.3 From 5220b40edd52ea70ed3a09f9a28fa7fe50ce71e6 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Wed, 26 Jun 2019 07:56:49 +0200 Subject: Port from implicit to explicit atomic integer operations The old code used the implicit conversions from QAtomicInteger to T and vice versa. The semantics of these differ from the ones std::atomic uses, so we're going to deprecate these, like we did for load() and store(), too. This patch fixex some users of these APIs before we deprecate them. Change-Id: I4877276581757cd57e042efea8296fe535a493d1 Reviewed-by: Thiago Macieira --- src/3rdparty/forkfd/forkfd.c | 2 +- src/corelib/global/qrandom.cpp | 10 +++++----- src/corelib/global/qrandom_p.h | 4 +++- src/corelib/kernel/qobject.cpp | 2 +- src/corelib/kernel/qobject_p.h | 2 +- 5 files changed, 11 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/3rdparty/forkfd/forkfd.c b/src/3rdparty/forkfd/forkfd.c index bef109e401..1cbe0da771 100644 --- a/src/3rdparty/forkfd/forkfd.c +++ b/src/3rdparty/forkfd/forkfd.c @@ -267,7 +267,7 @@ static int tryReaping(pid_t pid, struct pipe_payload *payload) static void freeInfo(Header *header, ProcessInfo *entry) { entry->deathPipe = -1; - entry->pid = 0; + ffd_atomic_store(&entry->pid, 0, FFD_ATOMIC_RELEASE); (void)ffd_atomic_add_fetch(&header->busyCount, -1, FFD_ATOMIC_RELEASE); assert(header->busyCount >= 0); diff --git a/src/corelib/global/qrandom.cpp b/src/corelib/global/qrandom.cpp index 84cf960f2d..379e37ac07 100644 --- a/src/corelib/global/qrandom.cpp +++ b/src/corelib/global/qrandom.cpp @@ -359,17 +359,17 @@ Q_NEVER_INLINE void QRandomGenerator::SystemGenerator::generate(quint32 *begin, quint32 *buffer = begin; qsizetype count = end - begin; - if (Q_UNLIKELY(uint(qt_randomdevice_control) & SetRandomData)) { - uint value = uint(qt_randomdevice_control) & RandomDataMask; + if (Q_UNLIKELY(uint(qt_randomdevice_control.loadAcquire()) & SetRandomData)) { + uint value = uint(qt_randomdevice_control.loadAcquire()) & RandomDataMask; std::fill_n(buffer, count, value); return; } qsizetype filled = 0; - if (qt_has_hwrng() && (uint(qt_randomdevice_control) & SkipHWRNG) == 0) + if (qt_has_hwrng() && (uint(qt_randomdevice_control.loadAcquire()) & SkipHWRNG) == 0) filled += qt_random_cpu(buffer, count); - if (filled != count && (uint(qt_randomdevice_control) & SkipSystemRNG) == 0) { + if (filled != count && (uint(qt_randomdevice_control.loadAcquire()) & SkipSystemRNG) == 0) { qsizetype bytesFilled = fillBuffer(buffer + filled, (count - filled) * qsizetype(sizeof(*buffer))); filled += bytesFilled / qsizetype(sizeof(*buffer)); @@ -1222,7 +1222,7 @@ void QRandomGenerator::_fillRange(void *buffer, void *bufferEnd) quint32 *begin = static_cast(buffer); quint32 *end = static_cast(bufferEnd); - if (type == SystemRNG || Q_UNLIKELY(uint(qt_randomdevice_control) & (UseSystemRNG|SetRandomData))) + if (type == SystemRNG || Q_UNLIKELY(uint(qt_randomdevice_control.loadAcquire()) & (UseSystemRNG|SetRandomData))) return SystemGenerator::self().generate(begin, end); SystemAndGlobalGenerators::PRNGLocker lock(this); diff --git a/src/corelib/global/qrandom_p.h b/src/corelib/global/qrandom_p.h index 917a91098e..167f4cc57d 100644 --- a/src/corelib/global/qrandom_p.h +++ b/src/corelib/global/qrandom_p.h @@ -76,7 +76,9 @@ Q_CORE_EXPORT QBasicAtomicInteger qt_randomdevice_control = Q_BASIC_ATOMIC #elif defined(QT_BUILD_INTERNAL) extern Q_CORE_EXPORT QBasicAtomicInteger qt_randomdevice_control; #else -enum { qt_randomdevice_control = 0 }; +static const struct { + uint loadAcquire() const { return 0; } +} qt_randomdevice_control; #endif inline bool qt_has_hwrng() diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 8f80be30bd..e3b25f8bf7 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -421,7 +421,7 @@ void QObjectPrivate::ConnectionData::cleanOrphanedConnectionsImpl(QObject *sende ConnectionOrSignalVector *c = nullptr; { QBasicMutexLocker l(signalSlotLock(sender)); - if (ref > 1) + if (ref.loadAcquire() > 1) return; // Since ref == 1, no activate() is in process since we locked the mutex. That implies, diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h index 1953aea21e..247c7b1501 100644 --- a/src/corelib/kernel/qobject_p.h +++ b/src/corelib/kernel/qobject_p.h @@ -279,7 +279,7 @@ public: void removeConnection(Connection *c); void cleanOrphanedConnections(QObject *sender) { - if (orphaned.loadRelaxed() && ref == 1) + if (orphaned.loadRelaxed() && ref.loadAcquire() == 1) cleanOrphanedConnectionsImpl(sender); } void cleanOrphanedConnectionsImpl(QObject *sender); -- cgit v1.2.3 From 3043c7b95e139426d07889bf8dd46f0552874297 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Wed, 26 Jun 2019 14:50:27 +0200 Subject: Fix qdoc warnings in 5.14 Fix warning: src/corelib/global/qnamespace.qdoc:2371: (qdoc) warning: Cannot find 'Qt::SplitBehavior' specified with '\enum' in any header file and qdoc getting confused by a link spanning lines: src/corelib/time/qdatetime.cpp:3324: (qdoc) warning: Unknown macro 'note' src/corelib/time/qdatetime.cpp:3326: (qdoc) warning: Unknown macro 'section1' src/corelib/time/qdatetime.cpp:3328: (qdoc) warning: Unknown macro 'section2' src/corelib/time/qdatetime.cpp:3334: (qdoc) warning: Unknown macro 'section2' src/corelib/time/qdatetime.cpp:3344: (qdoc) warning: Unknown macro 'section2' src/corelib/time/qdatetime.cpp:3361: (qdoc) warning: Unknown macro 'section2' src/corelib/time/qdatetime.cpp:3373: (qdoc) warning: Unknown macro 'e' src/corelib/time/qdatetime.cpp:3378: (qdoc) warning: Unknown macro 'section2' src/corelib/time/qdatetime.cpp:3386: (qdoc) warning: Unknown macro 'sa' Change-Id: I314da69078eb07f66e9d389eecdf0195569ea2ce Reviewed-by: Leena Miettinen --- src/corelib/global/qnamespace.qdoc | 2 +- src/corelib/time/qdatetime.cpp | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/corelib/global/qnamespace.qdoc b/src/corelib/global/qnamespace.qdoc index 45d79902c7..0ff6be2049 100644 --- a/src/corelib/global/qnamespace.qdoc +++ b/src/corelib/global/qnamespace.qdoc @@ -2369,7 +2369,7 @@ */ /*! - \enum Qt::SplitBehavior + \enum Qt::SplitBehaviorFlags \since 5.14 This enum specifies how the split() functions should behave with diff --git a/src/corelib/time/qdatetime.cpp b/src/corelib/time/qdatetime.cpp index e34ce71212..3c7484dca5 100644 --- a/src/corelib/time/qdatetime.cpp +++ b/src/corelib/time/qdatetime.cpp @@ -3272,9 +3272,10 @@ inline qint64 QDateTimePrivate::zoneMSecsToEpochMSecs(qint64 zoneMSecs, const QT provides functions for comparing datetimes and for manipulating a datetime by adding a number of seconds, days, months, or years. - QDateTime can describe datetimes with respect to \l{Qt::LocalTime}{local - time}, to \l{Qt::UTC}{UTC}, to a specified \l{Qt::OffsetFromUTC}{offset - from UTC} or to a specified \l{{Qt::TimeZone}{time zone}, in conjunction + QDateTime can describe datetimes with respect to + \l{Qt::LocalTime}{local time}, to \l{Qt::UTC}{UTC}, to a specified + \l{Qt::OffsetFromUTC}{offset from UTC} or to a specified + \l{Qt::TimeZone}{time zone}, in conjunction with the QTimeZone class. For example, a time zone of "Europe/Berlin" will apply the daylight-saving rules as used in Germany since 1970. In contrast, an offset from UTC of +3600 seconds is one hour ahead of UTC (usually -- cgit v1.2.3 From fb244259ae749f33cb4d870552c41c886fbe3f2b Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Wed, 26 Jun 2019 10:37:57 +0200 Subject: RHI: Fix compilation with MinGW developer builds Make aligned a template, fixing: rhi\qrhid3d11.cpp: In member function 'void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings*)': rhi\qrhid3d11.cpp:1627:53: error: comparison between signed and unsigned integer expressions [-Werror=sign-compare] Q_ASSERT(aligned(b->u.ubuf.offset, 256) == b->u.ubuf.offset); Change-Id: I6b747ebaf78e5accb9b7ed145df71a80d0a15762 Reviewed-by: Laszlo Agocs --- src/gui/rhi/qrhid3d11.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index 7d9c934c18..6b05d68fb5 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -150,7 +150,8 @@ static QString comErrorMessage(HRESULT hr) return result; } -static inline uint aligned(uint v, uint byteAlign) +template +static inline Int aligned(Int v, Int byteAlign) { return (v + byteAlign - 1) & ~(byteAlign - 1); } @@ -595,7 +596,7 @@ void QRhiD3D11::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind for (int i = 0; i < dynamicOffsetCount; ++i) { const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]); const uint binding = dynOfs.first; - Q_ASSERT(aligned(dynOfs.second, 256) == dynOfs.second); + Q_ASSERT(aligned(dynOfs.second, quint32(256)) == dynOfs.second); const uint offsetInConstants = dynOfs.second / 16; *p++ = binding; *p++ = offsetInConstants; -- cgit v1.2.3 From 6007120aa4391fa53acd774a451530c6561a1658 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Thu, 20 Jun 2019 09:47:38 +0200 Subject: QtWidgets: Fix static method invocations Apply fixits by Creator and results of manual search focusing on QCore/Gui/Applicaton(Private) methods and variables to prepare for splitting out some classes. Task-number: QTBUG-69478 Task-number: QTBUG-76497 Task-number: QTBUG-76493 Change-Id: Iaf468166793e0cabb514b51c827b30317bf45a2d Reviewed-by: Oliver Wolff --- src/widgets/dialogs/qdialog.cpp | 6 +- src/widgets/dialogs/qerrormessage.cpp | 2 +- src/widgets/dialogs/qfilesystemmodel.cpp | 2 +- src/widgets/dialogs/qfontdialog.cpp | 4 +- src/widgets/dialogs/qmessagebox.cpp | 2 +- src/widgets/dialogs/qprogressdialog.cpp | 4 +- src/widgets/dialogs/qwizard.cpp | 2 +- src/widgets/graphicsview/qgraphicslayout.cpp | 2 +- src/widgets/graphicsview/qgraphicslayout_p.cpp | 2 +- src/widgets/graphicsview/qgraphicsproxywidget.cpp | 32 ++--- src/widgets/graphicsview/qgraphicsscene.cpp | 24 ++-- src/widgets/graphicsview/qgraphicsview.cpp | 56 ++++---- src/widgets/graphicsview/qgraphicswidget.cpp | 46 +++--- src/widgets/graphicsview/qgraphicswidget_p.cpp | 10 +- src/widgets/itemviews/qabstractitemview.cpp | 18 +-- src/widgets/itemviews/qdirmodel.cpp | 4 +- src/widgets/itemviews/qheaderview.cpp | 2 +- src/widgets/itemviews/qlistview.cpp | 6 +- src/widgets/kernel/qaction.cpp | 44 +++--- src/widgets/kernel/qapplication.cpp | 50 +++---- src/widgets/kernel/qboxlayout.cpp | 2 +- src/widgets/kernel/qformlayout.cpp | 2 +- src/widgets/kernel/qgesturemanager.cpp | 4 +- src/widgets/kernel/qlayout.cpp | 4 +- src/widgets/kernel/qopenglwidget.cpp | 4 +- src/widgets/kernel/qshortcut.cpp | 14 +- src/widgets/kernel/qwhatsthis.cpp | 14 +- src/widgets/kernel/qwidget.cpp | 162 +++++++++++----------- src/widgets/kernel/qwidgetbackingstore.cpp | 6 +- src/widgets/kernel/qwidgetwindow.cpp | 12 +- src/widgets/kernel/qwindowcontainer.cpp | 2 +- src/widgets/styles/qcommonstyle.cpp | 20 +-- src/widgets/styles/qstyle.cpp | 4 +- src/widgets/styles/qstyleoption.cpp | 2 +- src/widgets/styles/qstylesheetstyle.cpp | 6 +- src/widgets/styles/qwindowsstyle.cpp | 11 +- src/widgets/util/qcompleter.cpp | 2 +- src/widgets/util/qflickgesture.cpp | 8 +- src/widgets/util/qsystemtrayicon_x11.cpp | 4 +- src/widgets/widgets/qabstractscrollarea.cpp | 6 +- src/widgets/widgets/qabstractspinbox.cpp | 6 +- src/widgets/widgets/qcombobox.cpp | 4 +- src/widgets/widgets/qdatetimeedit.cpp | 8 +- src/widgets/widgets/qdockwidget.cpp | 2 +- src/widgets/widgets/qeffects.cpp | 8 +- src/widgets/widgets/qlineedit.cpp | 19 +-- src/widgets/widgets/qmdiarea.cpp | 2 +- src/widgets/widgets/qmdisubwindow.cpp | 6 +- src/widgets/widgets/qmenu.cpp | 30 ++-- src/widgets/widgets/qmenubar.cpp | 4 +- src/widgets/widgets/qplaintextedit.cpp | 4 +- src/widgets/widgets/qspinbox.cpp | 8 +- src/widgets/widgets/qsplashscreen.cpp | 6 +- src/widgets/widgets/qtabbar.cpp | 2 +- src/widgets/widgets/qtextedit.cpp | 2 +- src/widgets/widgets/qtoolbar.cpp | 2 +- src/widgets/widgets/qwidgetlinecontrol.cpp | 10 +- src/widgets/widgets/qwidgettextcontrol.cpp | 20 +-- 58 files changed, 376 insertions(+), 374 deletions(-) (limited to 'src') diff --git a/src/widgets/dialogs/qdialog.cpp b/src/widgets/dialogs/qdialog.cpp index 5692a16bce..3cdd9a5f04 100644 --- a/src/widgets/dialogs/qdialog.cpp +++ b/src/widgets/dialogs/qdialog.cpp @@ -282,7 +282,7 @@ void QDialogPrivate::deletePlatformHelper() progress dialogs, where the user must have the ability to interact with the dialog, e.g. to cancel a long running operation. If you use show() and setModal(true) together to perform a long operation, - you must call QApplication::processEvents() periodically during + you must call QCoreApplication::processEvents() periodically during processing to enable the user to interact with the dialog. (See QProgressDialog.) @@ -691,7 +691,7 @@ void QDialog::contextMenuEvent(QContextMenuEvent *e) if (p.data()->exec(e->globalPos()) == wt) { QHelpEvent e(QEvent::WhatsThis, w->rect().center(), w->mapToGlobal(w->rect().center())); - QApplication::sendEvent(w, &e); + QCoreApplication::sendEvent(w, &e); } delete p.data(); } @@ -826,7 +826,7 @@ QT_WARNING_POP #endif if (fw && !fw->hasFocus()) { QFocusEvent e(QEvent::FocusIn, Qt::TabFocusReason); - QApplication::sendEvent(fw, &e); + QCoreApplication::sendEvent(fw, &e); } #ifndef QT_NO_ACCESSIBILITY diff --git a/src/widgets/dialogs/qerrormessage.cpp b/src/widgets/dialogs/qerrormessage.cpp index 51a3e080bf..f0ec2c0102 100644 --- a/src/widgets/dialogs/qerrormessage.cpp +++ b/src/widgets/dialogs/qerrormessage.cpp @@ -295,7 +295,7 @@ QErrorMessage * QErrorMessage::qtHandler() if (!qtMessageHandler) { qtMessageHandler = new QErrorMessage(0); qAddPostRoutine(deleteStaticcQErrorMessage); // clean up - qtMessageHandler->setWindowTitle(QApplication::applicationName()); + qtMessageHandler->setWindowTitle(QCoreApplication::applicationName()); qInstallMessageHandler(jump); } return qtMessageHandler; diff --git a/src/widgets/dialogs/qfilesystemmodel.cpp b/src/widgets/dialogs/qfilesystemmodel.cpp index a778fd3a45..b521f50f40 100644 --- a/src/widgets/dialogs/qfilesystemmodel.cpp +++ b/src/widgets/dialogs/qfilesystemmodel.cpp @@ -1390,7 +1390,7 @@ QModelIndex QFileSystemModel::setRootPath(const QString &newPath) if (d->rootDir.path() == longNewPath) return d->index(rootPath()); - bool showDrives = (longNewPath.isEmpty() || longNewPath == d->myComputer()); + bool showDrives = (longNewPath.isEmpty() || longNewPath == QFileSystemModelPrivate::myComputer()); if (!showDrives && !newPathDir.exists()) return d->index(rootPath()); diff --git a/src/widgets/dialogs/qfontdialog.cpp b/src/widgets/dialogs/qfontdialog.cpp index 2b81180ecb..cd296ca020 100644 --- a/src/widgets/dialogs/qfontdialog.cpp +++ b/src/widgets/dialogs/qfontdialog.cpp @@ -432,7 +432,7 @@ bool QFontDialog::eventFilter(QObject *o , QEvent *e) k->key() == Qt::Key_PageDown)) { int ci = d->sizeList->currentItem(); - (void)QApplication::sendEvent(d->sizeList, k); + QCoreApplication::sendEvent(d->sizeList, k); if (ci != d->sizeList->currentItem() && style()->styleHint(QStyle::SH_FontDialog_SelectAssociatedText, 0, this)) @@ -680,7 +680,7 @@ void QFontDialogPrivate::updateSampleFont(const QFont &newFont) void QFontDialogPrivate::_q_writingSystemHighlighted(int index) { writingSystem = QFontDatabase::WritingSystem(index); - sampleEdit->setText(fdb.writingSystemSample(writingSystem)); + sampleEdit->setText(QFontDatabase::writingSystemSample(writingSystem)); updateFamilies(); } diff --git a/src/widgets/dialogs/qmessagebox.cpp b/src/widgets/dialogs/qmessagebox.cpp index 9bfea06a54..8dad212692 100644 --- a/src/widgets/dialogs/qmessagebox.cpp +++ b/src/widgets/dialogs/qmessagebox.cpp @@ -1508,7 +1508,7 @@ void QMessageBox::keyPressEvent(QKeyEvent *e) if (d->detailsText) textToCopy += d->detailsText->text() + QLatin1Char('\n') + separator; #endif - QApplication::clipboard()->setText(textToCopy); + QGuiApplication::clipboard()->setText(textToCopy); return; } #endif // Q_OS_WIN diff --git a/src/widgets/dialogs/qprogressdialog.cpp b/src/widgets/dialogs/qprogressdialog.cpp index 078dd6463b..e1a6bce5b1 100644 --- a/src/widgets/dialogs/qprogressdialog.cpp +++ b/src/widgets/dialogs/qprogressdialog.cpp @@ -643,7 +643,7 @@ int QProgressDialog::value() const \warning If the progress dialog is modal (see QProgressDialog::QProgressDialog()), - setValue() calls QApplication::processEvents(), so take care that + setValue() calls QCoreApplication::processEvents(), so take care that this does not cause undesirable re-entrancy in your code. For example, don't use a QProgressDialog inside a paintEvent()! @@ -659,7 +659,7 @@ void QProgressDialog::setValue(int progress) if (d->shown_once) { if (isModal()) - QApplication::processEvents(); + QCoreApplication::processEvents(); } else { if ((!d->setValue_called && progress == 0 /* for compat with Qt < 5.4 */) || progress == minimum()) { d->starttime.start(); diff --git a/src/widgets/dialogs/qwizard.cpp b/src/widgets/dialogs/qwizard.cpp index f428114bbe..4d28dda7c7 100644 --- a/src/widgets/dialogs/qwizard.cpp +++ b/src/widgets/dialogs/qwizard.cpp @@ -2577,7 +2577,7 @@ void QWizard::setWizardStyle(WizardStyle style) //Send a resizeevent since the antiflicker widget probably needs a new size //because of the backbutton in the window title QResizeEvent ev(geometry().size(), geometry().size()); - QApplication::sendEvent(this, &ev); + QCoreApplication::sendEvent(this, &ev); } #endif d->updateLayout(); diff --git a/src/widgets/graphicsview/qgraphicslayout.cpp b/src/widgets/graphicsview/qgraphicslayout.cpp index ca0aef9e27..8b52b57580 100644 --- a/src/widgets/graphicsview/qgraphicslayout.cpp +++ b/src/widgets/graphicsview/qgraphicslayout.cpp @@ -338,7 +338,7 @@ void QGraphicsLayout::invalidate() } if (layoutItem && !layoutItem->isLayout()) { // If a layout has a parent that is not a layout it must be a QGraphicsWidget. - QApplication::postEvent(static_cast(layoutItem), new QEvent(QEvent::LayoutRequest)); + QCoreApplication::postEvent(static_cast(layoutItem), new QEvent(QEvent::LayoutRequest)); } } } diff --git a/src/widgets/graphicsview/qgraphicslayout_p.cpp b/src/widgets/graphicsview/qgraphicslayout_p.cpp index ae1eeffa2b..59ed7acd72 100644 --- a/src/widgets/graphicsview/qgraphicslayout_p.cpp +++ b/src/widgets/graphicsview/qgraphicslayout_p.cpp @@ -108,7 +108,7 @@ Qt::LayoutDirection QGraphicsLayoutPrivate::visualDirection() const if (maybeWidget->isWidget()) return static_cast(maybeWidget)->layoutDirection(); } - return QApplication::layoutDirection(); + return QGuiApplication::layoutDirection(); } static bool removeLayoutItemFromLayout(QGraphicsLayout *lay, QGraphicsLayoutItem *layoutItem) diff --git a/src/widgets/graphicsview/qgraphicsproxywidget.cpp b/src/widgets/graphicsview/qgraphicsproxywidget.cpp index e9f092020f..f1b01fbb4d 100644 --- a/src/widgets/graphicsview/qgraphicsproxywidget.cpp +++ b/src/widgets/graphicsview/qgraphicsproxywidget.cpp @@ -342,7 +342,7 @@ void QGraphicsProxyWidgetPrivate::sendWidgetKeyEvent(QKeyEvent *event) Q_ASSERT(receiver); do { - bool res = QApplication::sendEvent(receiver, event); + bool res = QCoreApplication::sendEvent(receiver, event); if ((res && event->isAccepted()) || (q->isWindow() && receiver == widget)) break; receiver = receiver->parentWidget(); @@ -356,9 +356,9 @@ void QGraphicsProxyWidgetPrivate::removeSubFocusHelper(QWidget *widget, Qt::Focu { QFocusEvent event(QEvent::FocusOut, reason); QPointer widgetGuard = widget; - QApplication::sendEvent(widget, &event); + QCoreApplication::sendEvent(widget, &event); if (widgetGuard && event.isAccepted()) - QApplication::sendEvent(widget->style(), &event); + QCoreApplication::sendEvent(widget->style(), &event); } /*! @@ -865,7 +865,7 @@ bool QGraphicsProxyWidget::event(QEvent *event) case QEvent::ShortcutOverride: { QWidget *focusWidget = d->widget->focusWidget(); while (focusWidget) { - QApplication::sendEvent(focusWidget, event); + QCoreApplication::sendEvent(focusWidget, event); if (event->isAccepted()) return true; focusWidget = focusWidget->parentWidget(); @@ -878,7 +878,7 @@ bool QGraphicsProxyWidget::event(QEvent *event) if (!(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) { //### Add MetaModifier? QWidget *focusWidget = d->widget->focusWidget(); while (focusWidget) { - bool res = QApplication::sendEvent(focusWidget, event); + const bool res = QCoreApplication::sendEvent(focusWidget, event); if ((res && event->isAccepted()) || (isWindow() && focusWidget == d->widget)) { event->accept(); break; @@ -897,7 +897,7 @@ bool QGraphicsProxyWidget::event(QEvent *event) QGraphicsSceneHelpEvent *he = static_cast(event); QPoint pos = d->mapToReceiver(mapFromScene(he->scenePos()), d->lastWidgetUnderMouse).toPoint(); QHelpEvent e(QEvent::ToolTip, pos, he->screenPos()); - QApplication::sendEvent(d->lastWidgetUnderMouse, &e); + QCoreApplication::sendEvent(d->lastWidgetUnderMouse, &e); event->setAccepted(e.isAccepted()); return e.isAccepted(); } @@ -919,7 +919,7 @@ bool QGraphicsProxyWidget::event(QEvent *event) if (event->spontaneous()) qt_sendSpontaneousEvent(d->widget, event); else - QApplication::sendEvent(d->widget, event); + QCoreApplication::sendEvent(d->widget, event); if (event->isAccepted()) return true; @@ -1044,7 +1044,7 @@ void QGraphicsProxyWidget::contextMenuEvent(QGraphicsSceneContextMenuEvent *even // Send mouse event. ### Doesn't propagate the event. QContextMenuEvent contextMenuEvent(QContextMenuEvent::Reason(event->reason()), pos.toPoint(), globalPos, event->modifiers()); - QApplication::sendEvent(receiver, &contextMenuEvent); + QCoreApplication::sendEvent(receiver, &contextMenuEvent); event->setAccepted(contextMenuEvent.isAccepted()); } @@ -1065,7 +1065,7 @@ void QGraphicsProxyWidget::dragEnterEvent(QGraphicsSceneDragDropEvent *event) QDragEnterEvent proxyDragEnter(event->pos().toPoint(), event->dropAction(), event->mimeData(), event->buttons(), event->modifiers()); proxyDragEnter.setAccepted(event->isAccepted()); - QApplication::sendEvent(d->widget, &proxyDragEnter); + QCoreApplication::sendEvent(d->widget, &proxyDragEnter); event->setAccepted(proxyDragEnter.isAccepted()); if (proxyDragEnter.isAccepted()) // we discard answerRect event->setDropAction(proxyDragEnter.dropAction()); @@ -1082,7 +1082,7 @@ void QGraphicsProxyWidget::dragLeaveEvent(QGraphicsSceneDragDropEvent *event) if (!d->widget || !d->dragDropWidget) return; QDragLeaveEvent proxyDragLeave; - QApplication::sendEvent(d->dragDropWidget, &proxyDragLeave); + QCoreApplication::sendEvent(d->dragDropWidget, &proxyDragLeave); d->dragDropWidget = 0; #endif } @@ -1112,7 +1112,7 @@ void QGraphicsProxyWidget::dragMoveEvent(QGraphicsSceneDragDropEvent *event) // Try to enter before we leave QDragEnterEvent dragEnter(receiverPos, event->possibleActions(), event->mimeData(), event->buttons(), event->modifiers()); dragEnter.setDropAction(event->proposedAction()); - QApplication::sendEvent(receiver, &dragEnter); + QCoreApplication::sendEvent(receiver, &dragEnter); event->setAccepted(dragEnter.isAccepted()); event->setDropAction(dragEnter.dropAction()); if (!event->isAccepted()) { @@ -1124,14 +1124,14 @@ void QGraphicsProxyWidget::dragMoveEvent(QGraphicsSceneDragDropEvent *event) if (d->dragDropWidget) { QDragLeaveEvent dragLeave; - QApplication::sendEvent(d->dragDropWidget, &dragLeave); + QCoreApplication::sendEvent(d->dragDropWidget, &dragLeave); } d->dragDropWidget = receiver; } QDragMoveEvent dragMove(receiverPos, event->possibleActions(), event->mimeData(), event->buttons(), event->modifiers()); event->setDropAction(d->lastDropAction); - QApplication::sendEvent(receiver, &dragMove); + QCoreApplication::sendEvent(receiver, &dragMove); event->setAccepted(dragMove.isAccepted()); event->setDropAction(dragMove.dropAction()); if (event->isAccepted()) @@ -1144,7 +1144,7 @@ void QGraphicsProxyWidget::dragMoveEvent(QGraphicsSceneDragDropEvent *event) if (d->dragDropWidget) { // Leave the last drag drop item QDragLeaveEvent dragLeave; - QApplication::sendEvent(d->dragDropWidget, &dragLeave); + QCoreApplication::sendEvent(d->dragDropWidget, &dragLeave); d->dragDropWidget = 0; } // Propagate @@ -1165,7 +1165,7 @@ void QGraphicsProxyWidget::dropEvent(QGraphicsSceneDragDropEvent *event) if (d->widget && d->dragDropWidget) { QPoint widgetPos = d->mapToReceiver(event->pos(), d->dragDropWidget).toPoint(); QDropEvent dropEvent(widgetPos, event->possibleActions(), event->mimeData(), event->buttons(), event->modifiers()); - QApplication::sendEvent(d->dragDropWidget, &dropEvent); + QCoreApplication::sendEvent(d->dragDropWidget, &dropEvent); event->setAccepted(dropEvent.isAccepted()); d->dragDropWidget = 0; } @@ -1464,7 +1464,7 @@ void QGraphicsProxyWidget::inputMethodEvent(QInputMethodEvent *event) Q_D(const QGraphicsProxyWidget); QWidget *focusWidget = d->widget->focusWidget(); if (focusWidget && focusWidget->testAttribute(Qt::WA_InputMethodEnabled)) - QApplication::sendEvent(focusWidget, event); + QCoreApplication::sendEvent(focusWidget, event); } /*! diff --git a/src/widgets/graphicsview/qgraphicsscene.cpp b/src/widgets/graphicsview/qgraphicsscene.cpp index e5bd65d61e..2cdbdc2072 100644 --- a/src/widgets/graphicsview/qgraphicsscene.cpp +++ b/src/widgets/graphicsview/qgraphicsscene.cpp @@ -114,7 +114,7 @@ Another responsibility that QGraphicsScene has, is to propagate events from QGraphicsView. To send an event to a scene, you construct an event that inherits QEvent, and then send it using, for example, - QApplication::sendEvent(). event() is responsible for dispatching + QCoreApplication::sendEvent(). event() is responsible for dispatching the event to the individual items. Some common events are handled by convenience event handlers. For example, key press events are handled by keyPressEvent(), and mouse press events are handled by mousePressEvent(). @@ -451,7 +451,7 @@ void QGraphicsScenePrivate::_q_polishItems() } if (itemd->isWidget) { QEvent event(QEvent::Polish); - QApplication::sendEvent((QGraphicsWidget *)item, &event); + QCoreApplication::sendEvent((QGraphicsWidget *)item, &event); } } @@ -774,7 +774,7 @@ void QGraphicsScenePrivate::setActivePanelHelper(QGraphicsItem *item, bool durin // Update activate state. activePanel = panel; QEvent event(QEvent::ActivationChange); - QApplication::sendEvent(q, &event); + QCoreApplication::sendEvent(q, &event); // Activate if (panel) { @@ -1243,7 +1243,7 @@ bool QGraphicsScenePrivate::sendEvent(QGraphicsItem *item, QEvent *event) return false; if (QGraphicsObject *o = item->toGraphicsObject()) { bool spont = event->spontaneous(); - if (spont ? qt_sendSpontaneousEvent(o, event) : QApplication::sendEvent(o, event)) + if (spont ? qt_sendSpontaneousEvent(o, event) : QCoreApplication::sendEvent(o, event)) return true; event->spont = spont; } @@ -1569,7 +1569,7 @@ void QGraphicsScenePrivate::updateFont(const QFont &font) // Send the scene a FontChange event. QEvent event(QEvent::FontChange); - QApplication::sendEvent(q, &event); + QCoreApplication::sendEvent(q, &event); } /*! @@ -1593,7 +1593,7 @@ void QGraphicsScenePrivate::setPalette_helper(const QPalette &palette) */ void QGraphicsScenePrivate::resolvePalette() { - QPalette naturalPalette = QApplication::palette(); + QPalette naturalPalette = QGuiApplication::palette(); naturalPalette.resolve(0); QPalette resolvedPalette = palette.resolve(naturalPalette); updatePalette(resolvedPalette); @@ -1626,7 +1626,7 @@ void QGraphicsScenePrivate::updatePalette(const QPalette &palette) // Send the scene a PaletteChange event. QEvent event(QEvent::PaletteChange); - QApplication::sendEvent(q, &event); + QCoreApplication::sendEvent(q, &event); } /*! @@ -3548,10 +3548,10 @@ bool QGraphicsScene::eventFilter(QObject *watched, QEvent *event) switch (event->type()) { case QEvent::ApplicationPaletteChange: - QApplication::postEvent(this, new QEvent(QEvent::ApplicationPaletteChange)); + QCoreApplication::postEvent(this, new QEvent(QEvent::ApplicationPaletteChange)); break; case QEvent::ApplicationFontChange: - QApplication::postEvent(this, new QEvent(QEvent::ApplicationFontChange)); + QCoreApplication::postEvent(this, new QEvent(QEvent::ApplicationFontChange)); break; default: break; @@ -5605,7 +5605,7 @@ void QGraphicsScene::setStyle(QStyle *style) // Notify the scene. QEvent event(QEvent::StyleChange); - QApplication::sendEvent(this, &event); + QCoreApplication::sendEvent(this, &event); // Notify all widgets that don't have a style explicitly set. const auto items_ = items(); @@ -5613,7 +5613,7 @@ void QGraphicsScene::setStyle(QStyle *style) if (item->isWidget()) { QGraphicsWidget *widget = static_cast(item); if (!widget->testAttribute(Qt::WA_SetStyle)) - QApplication::sendEvent(widget, &event); + QCoreApplication::sendEvent(widget, &event); } } } @@ -5686,7 +5686,7 @@ QPalette QGraphicsScene::palette() const void QGraphicsScene::setPalette(const QPalette &palette) { Q_D(QGraphicsScene); - QPalette naturalPalette = QApplication::palette(); + QPalette naturalPalette = QGuiApplication::palette(); naturalPalette.resolve(0); QPalette resolvedPalette = palette.resolve(naturalPalette); d->setPalette_helper(resolvedPalette); diff --git a/src/widgets/graphicsview/qgraphicsview.cpp b/src/widgets/graphicsview/qgraphicsview.cpp index 5fe520132f..3ec9668cde 100644 --- a/src/widgets/graphicsview/qgraphicsview.cpp +++ b/src/widgets/graphicsview/qgraphicsview.cpp @@ -667,7 +667,7 @@ void QGraphicsViewPrivate::mouseMoveEventHandler(QMouseEvent *event) if (event->spontaneous()) qt_sendSpontaneousEvent(scene, &mouseEvent); else - QApplication::sendEvent(scene, &mouseEvent); + QCoreApplication::sendEvent(scene, &mouseEvent); // Remember whether the last event was accepted or not. lastMouseEvent.setAccepted(mouseEvent.isAccepted()); @@ -1716,7 +1716,7 @@ void QGraphicsView::setScene(QGraphicsScene *scene) if (isActiveWindow() && isVisible()) { QEvent windowDeactivate(QEvent::WindowDeactivate); - QApplication::sendEvent(d->scene, &windowDeactivate); + QCoreApplication::sendEvent(d->scene, &windowDeactivate); } if(hasFocus()) d->scene->clearFocus(); @@ -1744,7 +1744,7 @@ void QGraphicsView::setScene(QGraphicsScene *scene) if (isActiveWindow() && isVisible()) { QEvent windowActivate(QEvent::WindowActivate); - QApplication::sendEvent(d->scene, &windowActivate); + QCoreApplication::sendEvent(d->scene, &windowActivate); } } else { d->recalculateContentSize(); @@ -2809,7 +2809,7 @@ bool QGraphicsView::event(QEvent *event) switch (event->type()) { case QEvent::ShortcutOverride: if (d->scene) - return QApplication::sendEvent(d->scene, event); + return QCoreApplication::sendEvent(d->scene, event); break; case QEvent::KeyPress: if (d->scene) { @@ -2821,7 +2821,7 @@ bool QGraphicsView::event(QEvent *event) // and the base implementation will call QGraphicsView's // focusNextPrevChild() function. If the event is ignored, // we fall back to standard tab focus handling. - QApplication::sendEvent(d->scene, event); + QCoreApplication::sendEvent(d->scene, event); if (event->isAccepted()) return true; // Ensure the event doesn't propagate just because the @@ -2850,10 +2850,10 @@ bool QGraphicsView::viewportEvent(QEvent *event) switch (event->type()) { case QEvent::Enter: - QApplication::sendEvent(d->scene, event); + QCoreApplication::sendEvent(d->scene, event); break; case QEvent::WindowActivate: - QApplication::sendEvent(d->scene, event); + QCoreApplication::sendEvent(d->scene, event); break; case QEvent::WindowDeactivate: // ### This is a temporary fix for until we get proper mouse @@ -2862,19 +2862,19 @@ bool QGraphicsView::viewportEvent(QEvent *event) // Remove all popups when the scene loses focus. if (!d->scene->d_func()->popupWidgets.isEmpty()) d->scene->d_func()->removePopup(d->scene->d_func()->popupWidgets.constFirst()); - QApplication::sendEvent(d->scene, event); + QCoreApplication::sendEvent(d->scene, event); break; case QEvent::Show: if (d->scene && isActiveWindow()) { QEvent windowActivate(QEvent::WindowActivate); - QApplication::sendEvent(d->scene, &windowActivate); + QCoreApplication::sendEvent(d->scene, &windowActivate); } break; case QEvent::Hide: // spontaneous event will generate a WindowDeactivate. if (!event->spontaneous() && d->scene && isActiveWindow()) { QEvent windowDeactivate(QEvent::WindowDeactivate); - QApplication::sendEvent(d->scene, &windowDeactivate); + QCoreApplication::sendEvent(d->scene, &windowDeactivate); } break; case QEvent::Leave: { @@ -2892,7 +2892,7 @@ bool QGraphicsView::viewportEvent(QEvent *event) Q_ASSERT(event->d == 0); QScopedValueRollback rb(event->d); event->d = reinterpret_cast(viewport()); - QApplication::sendEvent(d->scene, event); + QCoreApplication::sendEvent(d->scene, event); break; } #ifndef QT_NO_TOOLTIP @@ -2902,7 +2902,7 @@ bool QGraphicsView::viewportEvent(QEvent *event) helpEvent.setWidget(viewport()); helpEvent.setScreenPos(toolTip->globalPos()); helpEvent.setScenePos(mapToScene(toolTip->pos())); - QApplication::sendEvent(d->scene, &helpEvent); + QCoreApplication::sendEvent(d->scene, &helpEvent); toolTip->setAccepted(helpEvent.isAccepted()); return true; } @@ -2940,7 +2940,7 @@ bool QGraphicsView::viewportEvent(QEvent *event) QTouchEvent *touchEvent = static_cast(event); touchEvent->setTarget(viewport()); QGraphicsViewPrivate::translateTouchEvent(d, touchEvent); - (void) QApplication::sendEvent(d->scene, touchEvent); + QCoreApplication::sendEvent(d->scene, touchEvent); } else { event->ignore(); } @@ -2957,7 +2957,7 @@ bool QGraphicsView::viewportEvent(QEvent *event) if (d->scene && d->sceneInteractionAllowed) { QGestureEvent *gestureEvent = static_cast(event); gestureEvent->setWidget(viewport()); - (void) QApplication::sendEvent(d->scene, gestureEvent); + QCoreApplication::sendEvent(d->scene, gestureEvent); } return true; } @@ -2992,7 +2992,7 @@ void QGraphicsView::contextMenuEvent(QContextMenuEvent *event) contextEvent.setModifiers(event->modifiers()); contextEvent.setReason((QGraphicsSceneContextMenuEvent::Reason)(event->reason())); contextEvent.setAccepted(event->isAccepted()); - QApplication::sendEvent(d->scene, &contextEvent); + QCoreApplication::sendEvent(d->scene, &contextEvent); event->setAccepted(contextEvent.isAccepted()); } #endif // QT_NO_CONTEXTMENU @@ -3012,7 +3012,7 @@ void QGraphicsView::dropEvent(QDropEvent *event) d->populateSceneDragDropEvent(&sceneEvent, event); // Send it to the scene. - QApplication::sendEvent(d->scene, &sceneEvent); + QCoreApplication::sendEvent(d->scene, &sceneEvent); // Accept the originating event if the scene accepted the scene event. event->setAccepted(sceneEvent.isAccepted()); @@ -3043,7 +3043,7 @@ void QGraphicsView::dragEnterEvent(QDragEnterEvent *event) d->storeDragDropEvent(&sceneEvent); // Send it to the scene. - QApplication::sendEvent(d->scene, &sceneEvent); + QCoreApplication::sendEvent(d->scene, &sceneEvent); // Accept the originating event if the scene accepted the scene event. if (sceneEvent.isAccepted()) { @@ -3081,7 +3081,7 @@ void QGraphicsView::dragLeaveEvent(QDragLeaveEvent *event) d->lastDragDropEvent = 0; // Send it to the scene. - QApplication::sendEvent(d->scene, &sceneEvent); + QCoreApplication::sendEvent(d->scene, &sceneEvent); // Accept the originating event if the scene accepted the scene event. if (sceneEvent.isAccepted()) @@ -3105,7 +3105,7 @@ void QGraphicsView::dragMoveEvent(QDragMoveEvent *event) d->storeDragDropEvent(&sceneEvent); // Send it to the scene. - QApplication::sendEvent(d->scene, &sceneEvent); + QCoreApplication::sendEvent(d->scene, &sceneEvent); // Ignore the originating event if the scene ignored the scene event. event->setAccepted(sceneEvent.isAccepted()); @@ -3123,7 +3123,7 @@ void QGraphicsView::focusInEvent(QFocusEvent *event) d->updateInputMethodSensitivity(); QAbstractScrollArea::focusInEvent(event); if (d->scene) - QApplication::sendEvent(d->scene, event); + QCoreApplication::sendEvent(d->scene, event); // Pass focus on if the scene cannot accept focus. if (!d->scene || !event->isAccepted()) QAbstractScrollArea::focusInEvent(event); @@ -3145,7 +3145,7 @@ void QGraphicsView::focusOutEvent(QFocusEvent *event) Q_D(QGraphicsView); QAbstractScrollArea::focusOutEvent(event); if (d->scene) - QApplication::sendEvent(d->scene, event); + QCoreApplication::sendEvent(d->scene, event); } /*! @@ -3158,7 +3158,7 @@ void QGraphicsView::keyPressEvent(QKeyEvent *event) QAbstractScrollArea::keyPressEvent(event); return; } - QApplication::sendEvent(d->scene, event); + QCoreApplication::sendEvent(d->scene, event); if (!event->isAccepted()) QAbstractScrollArea::keyPressEvent(event); } @@ -3171,7 +3171,7 @@ void QGraphicsView::keyReleaseEvent(QKeyEvent *event) Q_D(QGraphicsView); if (!d->scene || !d->sceneInteractionAllowed) return; - QApplication::sendEvent(d->scene, event); + QCoreApplication::sendEvent(d->scene, event); if (!event->isAccepted()) QAbstractScrollArea::keyReleaseEvent(event); } @@ -3210,7 +3210,7 @@ void QGraphicsView::mouseDoubleClickEvent(QMouseEvent *event) if (event->spontaneous()) qt_sendSpontaneousEvent(d->scene, &mouseEvent); else - QApplication::sendEvent(d->scene, &mouseEvent); + QCoreApplication::sendEvent(d->scene, &mouseEvent); // Update the original mouse event accepted state. const bool isAccepted = mouseEvent.isAccepted(); @@ -3261,7 +3261,7 @@ void QGraphicsView::mousePressEvent(QMouseEvent *event) if (event->spontaneous()) qt_sendSpontaneousEvent(d->scene, &mouseEvent); else - QApplication::sendEvent(d->scene, &mouseEvent); + QCoreApplication::sendEvent(d->scene, &mouseEvent); // Update the original mouse event accepted state. bool isAccepted = mouseEvent.isAccepted(); @@ -3397,7 +3397,7 @@ void QGraphicsView::mouseReleaseEvent(QMouseEvent *event) if (event->spontaneous()) qt_sendSpontaneousEvent(d->scene, &mouseEvent); else - QApplication::sendEvent(d->scene, &mouseEvent); + QCoreApplication::sendEvent(d->scene, &mouseEvent); // Update the last mouse event selected state. d->lastMouseEvent.setAccepted(mouseEvent.isAccepted()); @@ -3433,7 +3433,7 @@ void QGraphicsView::wheelEvent(QWheelEvent *event) wheelEvent.setDelta(event->delta()); wheelEvent.setOrientation(event->orientation()); wheelEvent.setAccepted(false); - QApplication::sendEvent(d->scene, &wheelEvent); + QCoreApplication::sendEvent(d->scene, &wheelEvent); event->setAccepted(wheelEvent.isAccepted()); if (!event->isAccepted()) QAbstractScrollArea::wheelEvent(event); @@ -3720,7 +3720,7 @@ void QGraphicsView::inputMethodEvent(QInputMethodEvent *event) { Q_D(QGraphicsView); if (d->scene) - QApplication::sendEvent(d->scene, event); + QCoreApplication::sendEvent(d->scene, event); } /*! diff --git a/src/widgets/graphicsview/qgraphicswidget.cpp b/src/widgets/graphicsview/qgraphicswidget.cpp index cd647a5db1..643f073085 100644 --- a/src/widgets/graphicsview/qgraphicswidget.cpp +++ b/src/widgets/graphicsview/qgraphicswidget.cpp @@ -390,7 +390,7 @@ void QGraphicsWidget::setGeometry(const QRectF &rect) QGraphicsSceneMoveEvent event; event.setOldPos(oldPos); event.setNewPos(pos()); - QApplication::sendEvent(this, &event); + QCoreApplication::sendEvent(this, &event); if (wd->inSetPos) { //set the new pos d->geom.moveTopLeft(pos()); @@ -413,10 +413,10 @@ void QGraphicsWidget::setGeometry(const QRectF &rect) QGraphicsLayout *lay = wd->layout; if (QGraphicsLayout::instantInvalidatePropagation()) { if (!lay || lay->isActivated()) { - QApplication::sendEvent(this, &re); + QCoreApplication::sendEvent(this, &re); } } else { - QApplication::sendEvent(this, &re); + QCoreApplication::sendEvent(this, &re); } } } @@ -427,7 +427,7 @@ relayoutChildrenAndReturn: if (QGraphicsLayout *lay = wd->layout) { if (!lay->isActivated()) { QEvent layoutRequest(QEvent::LayoutRequest); - QApplication::sendEvent(this, &layoutRequest); + QCoreApplication::sendEvent(this, &layoutRequest); } } } @@ -507,7 +507,7 @@ void QGraphicsWidget::setContentsMargins(QMarginsF margins) updateGeometry(); QEvent e(QEvent::ContentsRectChange); - QApplication::sendEvent(this, &e); + QCoreApplication::sendEvent(this, &e); } /*! @@ -963,7 +963,7 @@ void QGraphicsWidget::setStyle(QStyle *style) // Deliver StyleChange to the widget itself (doesn't propagate). QEvent event(QEvent::StyleChange); - QApplication::sendEvent(this, &event); + QCoreApplication::sendEvent(this, &event); } /*! @@ -1028,7 +1028,7 @@ void QGraphicsWidget::setFont(const QFont &font) By default, this property contains the application's default palette. - \sa QApplication::palette(), QGraphicsScene::palette, QPalette::resolve() + \sa QGuiApplication::palette(), QGraphicsScene::palette, QPalette::resolve() */ QPalette QGraphicsWidget::palette() const { @@ -1100,7 +1100,7 @@ void QGraphicsWidget::updateGeometry() // This is for custom layouting QGraphicsWidget *parentWid = parentWidget(); //### if (parentWid->isVisible()) - QApplication::postEvent(parentWid, new QEvent(QEvent::LayoutRequest)); + QCoreApplication::postEvent(parentWid, new QEvent(QEvent::LayoutRequest)); } else { /** * If this is the topmost widget, post a LayoutRequest event to the widget. @@ -1108,7 +1108,7 @@ void QGraphicsWidget::updateGeometry() * widgets in one go. This will make a relayout flicker-free. */ if (QGraphicsLayout::instantInvalidatePropagation()) - QApplication::postEvent(static_cast(this), new QEvent(QEvent::LayoutRequest)); + QCoreApplication::postEvent(static_cast(this), new QEvent(QEvent::LayoutRequest)); } if (!QGraphicsLayout::instantInvalidatePropagation()) { bool wasResized = testAttribute(Qt::WA_Resized); @@ -1145,14 +1145,14 @@ QVariant QGraphicsWidget::itemChange(GraphicsItemChange change, const QVariant & case ItemEnabledHasChanged: { // Send EnabledChange after the enabled state has changed. QEvent event(QEvent::EnabledChange); - QApplication::sendEvent(this, &event); + QCoreApplication::sendEvent(this, &event); break; } case ItemVisibleChange: if (value.toBool()) { // Send Show event before the item has been shown. QShowEvent event; - QApplication::sendEvent(this, &event); + QCoreApplication::sendEvent(this, &event); bool resized = testAttribute(Qt::WA_Resized); if (!resized) { adjustSize(); @@ -1168,7 +1168,7 @@ QVariant QGraphicsWidget::itemChange(GraphicsItemChange change, const QVariant & if (!value.toBool()) { // Send Hide event after the item has been hidden. QHideEvent event; - QApplication::sendEvent(this, &event); + QCoreApplication::sendEvent(this, &event); } break; case ItemPositionHasChanged: @@ -1177,25 +1177,25 @@ QVariant QGraphicsWidget::itemChange(GraphicsItemChange change, const QVariant & case ItemParentChange: { // Deliver ParentAboutToChange. QEvent event(QEvent::ParentAboutToChange); - QApplication::sendEvent(this, &event); + QCoreApplication::sendEvent(this, &event); break; } case ItemParentHasChanged: { // Deliver ParentChange. QEvent event(QEvent::ParentChange); - QApplication::sendEvent(this, &event); + QCoreApplication::sendEvent(this, &event); break; } case ItemCursorHasChanged: { // Deliver CursorChange. QEvent event(QEvent::CursorChange); - QApplication::sendEvent(this, &event); + QCoreApplication::sendEvent(this, &event); break; } case ItemToolTipHasChanged: { // Deliver ToolTipChange. QEvent event(QEvent::ToolTipChange); - QApplication::sendEvent(this, &event); + QCoreApplication::sendEvent(this, &event); break; } default: @@ -1930,7 +1930,7 @@ int QGraphicsWidget::grabShortcut(const QKeySequence &sequence, Qt::ShortcutCont if (sequence.isEmpty()) return 0; // ### setAttribute(Qt::WA_GrabbedShortcut); - return qApp->d_func()->shortcutMap.addShortcut(this, sequence, context, qWidgetShortcutContextMatcher); + return QGuiApplicationPrivate::instance()->shortcutMap.addShortcut(this, sequence, context, qWidgetShortcutContextMatcher); } /*! @@ -1954,7 +1954,7 @@ void QGraphicsWidget::releaseShortcut(int id) { Q_ASSERT(qApp); if (id) - qApp->d_func()->shortcutMap.removeShortcut(id, this, 0); + QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(id, this, 0); } /*! @@ -1975,7 +1975,7 @@ void QGraphicsWidget::setShortcutEnabled(int id, bool enabled) { Q_ASSERT(qApp); if (id) - qApp->d_func()->shortcutMap.setShortcutEnabled(enabled, id, this, 0); + QGuiApplicationPrivate::instance()->shortcutMap.setShortcutEnabled(enabled, id, this, 0); } /*! @@ -1990,7 +1990,7 @@ void QGraphicsWidget::setShortcutAutoRepeat(int id, bool enabled) { Q_ASSERT(qApp); if (id) - qApp->d_func()->shortcutMap.setShortcutAutoRepeat(enabled, id, this, 0); + QGuiApplicationPrivate::instance()->shortcutMap.setShortcutAutoRepeat(enabled, id, this, 0); } #endif @@ -2068,7 +2068,7 @@ void QGraphicsWidget::insertAction(QAction *before, QAction *action) } QActionEvent e(QEvent::ActionAdded, action, before); - QApplication::sendEvent(this, &e); + QCoreApplication::sendEvent(this, &e); } /*! @@ -2111,7 +2111,7 @@ void QGraphicsWidget::removeAction(QAction *action) if (d->actions.removeAll(action)) { QActionEvent e(QEvent::ActionRemoved, action); - QApplication::sendEvent(this, &e); + QCoreApplication::sendEvent(this, &e); } } @@ -2404,7 +2404,7 @@ QPainterPath QGraphicsWidget::shape() const bool QGraphicsWidget::close() { QCloseEvent closeEvent; - QApplication::sendEvent(this, &closeEvent); + QCoreApplication::sendEvent(this, &closeEvent); if (!closeEvent.isAccepted()) { return false; } diff --git a/src/widgets/graphicsview/qgraphicswidget_p.cpp b/src/widgets/graphicsview/qgraphicswidget_p.cpp index ce027c1319..cc08b1c566 100644 --- a/src/widgets/graphicsview/qgraphicswidget_p.cpp +++ b/src/widgets/graphicsview/qgraphicswidget_p.cpp @@ -190,7 +190,7 @@ void QGraphicsWidgetPrivate::updatePalette(const QPalette &palette) // Notify change. QEvent event(QEvent::PaletteChange); - QApplication::sendEvent(q, &event); + QCoreApplication::sendEvent(q, &event); } void QGraphicsWidgetPrivate::setLayoutDirection_helper(Qt::LayoutDirection direction) @@ -212,7 +212,7 @@ void QGraphicsWidgetPrivate::setLayoutDirection_helper(Qt::LayoutDirection direc // Send the notification event to this widget item. QEvent e(QEvent::LayoutDirectionChange); - QApplication::sendEvent(q, &e); + QCoreApplication::sendEvent(q, &e); } void QGraphicsWidgetPrivate::resolveLayoutDirection() @@ -226,9 +226,9 @@ void QGraphicsWidgetPrivate::resolveLayoutDirection() } else if (scene) { // ### shouldn't the scene have a layoutdirection really? how does // ### QGraphicsWidget get changes from QApplication::layoutDirection? - setLayoutDirection_helper(QApplication::layoutDirection()); + setLayoutDirection_helper(QGuiApplication::layoutDirection()); } else { - setLayoutDirection_helper(QApplication::layoutDirection()); + setLayoutDirection_helper(QGuiApplication::layoutDirection()); } } @@ -290,7 +290,7 @@ void QGraphicsWidgetPrivate::updateFont(const QFont &font) return; // Notify change. QEvent event(QEvent::FontChange); - QApplication::sendEvent(q, &event); + QCoreApplication::sendEvent(q, &event); } QFont QGraphicsWidgetPrivate::naturalWidgetFont() const diff --git a/src/widgets/itemviews/qabstractitemview.cpp b/src/widgets/itemviews/qabstractitemview.cpp index 1d1c144bb8..6ff0fe8132 100644 --- a/src/widgets/itemviews/qabstractitemview.cpp +++ b/src/widgets/itemviews/qabstractitemview.cpp @@ -180,7 +180,7 @@ void QAbstractItemViewPrivate::checkMouseMove(const QPersistentModelIndex &index QString statustip = model->data(index, Qt::StatusTipRole).toString(); if (parent && (shouldClearStatusTip || !statustip.isEmpty())) { QStatusTipEvent tip(statustip); - QApplication::sendEvent(parent, &tip); + QCoreApplication::sendEvent(parent, &tip); shouldClearStatusTip = !statustip.isEmpty(); } #endif @@ -189,7 +189,7 @@ void QAbstractItemViewPrivate::checkMouseMove(const QPersistentModelIndex &index if (parent && shouldClearStatusTip) { QString emptyString; QStatusTipEvent tip( emptyString ); - QApplication::sendEvent(parent, &tip); + QCoreApplication::sendEvent(parent, &tip); } #endif emit q->viewportEntered(); @@ -521,7 +521,7 @@ void QAbstractItemViewPrivate::_q_scrollerStateChanged() the mouse was pressed on is specified by \a index. The signal is only emitted when the index is valid. - Use the QApplication::mouseButtons() function to get the state + Use the QGuiApplication::mouseButtons() function to get the state of the mouse buttons. \sa activated(), clicked(), doubleClicked(), entered() @@ -1713,7 +1713,7 @@ bool QAbstractItemView::viewportEvent(QEvent *event) if (d->shouldClearStatusTip && d->parent) { QString empty; QStatusTipEvent tip(empty); - QApplication::sendEvent(d->parent, &tip); + QCoreApplication::sendEvent(d->parent, &tip); d->shouldClearStatusTip = false; } #endif @@ -2338,7 +2338,7 @@ void QAbstractItemView::keyPressEvent(QKeyEvent *event) if (d->model) variant = d->model->data(currentIndex(), Qt::DisplayRole); if (variant.type() == QVariant::String) - QApplication::clipboard()->setText(variant.toString()); + QGuiApplication::clipboard()->setText(variant.toString()); event->accept(); } #endif @@ -2845,7 +2845,7 @@ void QAbstractItemView::closeEditor(QWidget *editor, QAbstractItemDelegate::EndE } QPointer ed = editor; - QApplication::sendPostedEvents(editor, 0); + QCoreApplication::sendPostedEvents(editor, 0); editor = ed; if (!isPersistent && editor) @@ -3957,7 +3957,7 @@ QItemSelectionModel::SelectionFlags QAbstractItemView::selectionCommand(const QM keyModifiers = (static_cast(event))->modifiers(); break; default: - keyModifiers = QApplication::keyboardModifiers(); + keyModifiers = QGuiApplication::keyboardModifiers(); } } switch (d->selectionMode) { @@ -4015,7 +4015,7 @@ QItemSelectionModel::SelectionFlags QAbstractItemViewPrivate::multiSelectionComm QItemSelectionModel::SelectionFlags QAbstractItemViewPrivate::extendedSelectionCommand( const QModelIndex &index, const QEvent *event) const { - Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers(); + Qt::KeyboardModifiers modifiers = QGuiApplication::keyboardModifiers(); if (event) { switch (event->type()) { case QEvent::MouseMove: { @@ -4425,7 +4425,7 @@ bool QAbstractItemViewPrivate::openEditor(const QModelIndex &index, QEvent *even w->setFocus(); if (event) - QApplication::sendEvent(w->focusProxy() ? w->focusProxy() : w, event); + QCoreApplication::sendEvent(w->focusProxy() ? w->focusProxy() : w, event); return true; } diff --git a/src/widgets/itemviews/qdirmodel.cpp b/src/widgets/itemviews/qdirmodel.cpp index b94c31fb42..13a1bbd8eb 100644 --- a/src/widgets/itemviews/qdirmodel.cpp +++ b/src/widgets/itemviews/qdirmodel.cpp @@ -1086,7 +1086,7 @@ QString QDirModel::filePath(const QModelIndex &index) const if (d->indexValid(index)) { QFileInfo fi = fileInfo(index); if (d->resolveSymlinks && fi.isSymLink()) - fi = d->resolvedInfo(fi); + fi = QDirModelPrivate::resolvedInfo(fi); return QDir::cleanPath(fi.absoluteFilePath()); } return QString(); // root path @@ -1108,7 +1108,7 @@ QString QDirModel::fileName(const QModelIndex &index) const if (QFileSystemEntry::isRootPath(path)) return path; if (d->resolveSymlinks && info.isSymLink()) - info = d->resolvedInfo(info); + info = QDirModelPrivate::resolvedInfo(info); return info.fileName(); } diff --git a/src/widgets/itemviews/qheaderview.cpp b/src/widgets/itemviews/qheaderview.cpp index 1fcd5bdef2..be80843b07 100644 --- a/src/widgets/itemviews/qheaderview.cpp +++ b/src/widgets/itemviews/qheaderview.cpp @@ -2896,7 +2896,7 @@ bool QHeaderView::viewportEvent(QEvent *e) case QEvent::Wheel: { QAbstractScrollArea *asa = qobject_cast(parentWidget()); if (asa) - return QApplication::sendEvent(asa->viewport(), e); + return QCoreApplication::sendEvent(asa->viewport(), e); break; } default: break; diff --git a/src/widgets/itemviews/qlistview.cpp b/src/widgets/itemviews/qlistview.cpp index 641b15f85b..25facd1484 100644 --- a/src/widgets/itemviews/qlistview.cpp +++ b/src/widgets/itemviews/qlistview.cpp @@ -821,13 +821,13 @@ void QListView::wheelEvent(QWheelEvent *e) if (e->spontaneous()) qt_sendSpontaneousEvent(d->hbar, &hwe); else - QApplication::sendEvent(d->hbar, &hwe); + QCoreApplication::sendEvent(d->hbar, &hwe); e->setAccepted(hwe.isAccepted()); } else { - QApplication::sendEvent(d->vbar, e); + QCoreApplication::sendEvent(d->vbar, e); } } else { - QApplication::sendEvent(d->hbar, e); + QCoreApplication::sendEvent(d->hbar, e); } } #endif // QT_CONFIG(wheelevent) diff --git a/src/widgets/kernel/qaction.cpp b/src/widgets/kernel/qaction.cpp index f6631199d6..47e91bf8f9 100644 --- a/src/widgets/kernel/qaction.cpp +++ b/src/widgets/kernel/qaction.cpp @@ -47,15 +47,15 @@ #include "qlist.h" #include "qstylehints.h" #include -#include +#include #if QT_CONFIG(menu) #include #endif #include #define QAPP_CHECK(functionName) \ - if (Q_UNLIKELY(!qApp)) { \ - qWarning("QAction: Initialize QApplication before calling '" functionName "'."); \ + if (Q_UNLIKELY(!QCoreApplication::instance())) { \ + qWarning("QAction: Initialize Q(Gui)Application before calling '" functionName "'."); \ return; \ } @@ -101,7 +101,7 @@ bool QActionPrivate::showStatusText(QWidget *widget, const QString &str) #else if(QObject *object = widget ? widget : parent) { QStatusTipEvent tip(str); - QApplication::sendEvent(object, &tip); + QCoreApplication::sendEvent(object, &tip); return true; } #endif @@ -114,15 +114,15 @@ void QActionPrivate::sendDataChanged() QActionEvent e(QEvent::ActionChanged, q); for (int i = 0; i < widgets.size(); ++i) { QWidget *w = widgets.at(i); - QApplication::sendEvent(w, &e); + QCoreApplication::sendEvent(w, &e); } #if QT_CONFIG(graphicsview) for (int i = 0; i < graphicsWidgets.size(); ++i) { QGraphicsWidget *w = graphicsWidgets.at(i); - QApplication::sendEvent(w, &e); + QCoreApplication::sendEvent(w, &e); } #endif - QApplication::sendEvent(q, &e); + QCoreApplication::sendEvent(q, &e); emit q->changed(); } @@ -391,7 +391,7 @@ void QAction::setShortcut(const QKeySequence &shortcut) return; d->shortcut = shortcut; - d->redoGrab(qApp->d_func()->shortcutMap); + d->redoGrab(QGuiApplicationPrivate::instance()->shortcutMap); d->sendDataChanged(); } @@ -420,8 +420,8 @@ void QAction::setShortcuts(const QList &shortcuts) d->shortcut = primary; d->alternateShortcuts = listCopy; - d->redoGrab(qApp->d_func()->shortcutMap); - d->redoGrabAlternate(qApp->d_func()->shortcutMap); + d->redoGrab(QGuiApplicationPrivate::instance()->shortcutMap); + d->redoGrabAlternate(QGuiApplicationPrivate::instance()->shortcutMap); d->sendDataChanged(); } @@ -485,8 +485,8 @@ void QAction::setShortcutContext(Qt::ShortcutContext context) return; QAPP_CHECK("setShortcutContext"); d->shortcutContext = context; - d->redoGrab(qApp->d_func()->shortcutMap); - d->redoGrabAlternate(qApp->d_func()->shortcutMap); + d->redoGrab(QGuiApplicationPrivate::instance()->shortcutMap); + d->redoGrabAlternate(QGuiApplicationPrivate::instance()->shortcutMap); d->sendDataChanged(); } @@ -513,8 +513,8 @@ void QAction::setAutoRepeat(bool on) return; QAPP_CHECK("setAutoRepeat"); d->autorepeat = on; - d->redoGrab(qApp->d_func()->shortcutMap); - d->redoGrabAlternate(qApp->d_func()->shortcutMap); + d->redoGrab(QGuiApplicationPrivate::instance()->shortcutMap); + d->redoGrabAlternate(QGuiApplicationPrivate::instance()->shortcutMap); d->sendDataChanged(); } @@ -575,10 +575,10 @@ QAction::~QAction() d->group->removeAction(this); #ifndef QT_NO_SHORTCUT if (d->shortcutId && qApp) { - qApp->d_func()->shortcutMap.removeShortcut(d->shortcutId, this); + QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(d->shortcutId, this); for(int i = 0; i < d->alternateShortcutIds.count(); ++i) { const int id = d->alternateShortcutIds.at(i); - qApp->d_func()->shortcutMap.removeShortcut(id, this); + QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(id, this); } } #endif @@ -1028,7 +1028,7 @@ void QAction::setEnabled(bool b) QAPP_CHECK("setEnabled"); d->enabled = b; #ifndef QT_NO_SHORTCUT - d->setShortcutEnabled(b, qApp->d_func()->shortcutMap); + d->setShortcutEnabled(b, QGuiApplicationPrivate::instance()->shortcutMap); #endif d->sendDataChanged(); } @@ -1061,8 +1061,8 @@ void QAction::setVisible(bool b) d->forceInvisible = !b; d->visible = b; d->enabled = b && !d->forceDisabled && (!d->group || d->group->isEnabled()) ; -#ifndef QT_NO_SHORTCUT - d->setShortcutEnabled(d->enabled, qApp->d_func()->shortcutMap); +#if QT_CONFIG(shortcut) + d->setShortcutEnabled(d->enabled, QGuiApplicationPrivate::instance()->shortcutMap); #endif d->sendDataChanged(); } @@ -1285,7 +1285,7 @@ void QAction::setIconVisibleInMenu(bool visible) d->iconVisibleInMenu = visible; // Only send data changed if we really need to. if (oldValue != -1 - || visible == !QApplication::instance()->testAttribute(Qt::AA_DontShowIconsInMenus)) { + || visible == !QCoreApplication::testAttribute(Qt::AA_DontShowIconsInMenus)) { d->sendDataChanged(); } } @@ -1295,7 +1295,7 @@ bool QAction::isIconVisibleInMenu() const { Q_D(const QAction); if (d->iconVisibleInMenu == -1) { - return !QApplication::instance()->testAttribute(Qt::AA_DontShowIconsInMenus); + return !QCoreApplication::testAttribute(Qt::AA_DontShowIconsInMenus); } return d->iconVisibleInMenu; } @@ -1323,7 +1323,7 @@ void QAction::setShortcutVisibleInContextMenu(bool visible) d->shortcutVisibleInContextMenu = visible; // Only send data changed if we really need to. if (oldValue != -1 - || visible == !QApplication::instance()->testAttribute(Qt::AA_DontShowShortcutsInContextMenus)) { + || visible == !QCoreApplication::testAttribute(Qt::AA_DontShowShortcutsInContextMenus)) { d->sendDataChanged(); } } diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index 526bf0a0ff..22ebeaae51 100644 --- a/src/widgets/kernel/qapplication.cpp +++ b/src/widgets/kernel/qapplication.cpp @@ -622,7 +622,7 @@ void QApplicationPrivate::initialize() if (qt_is_gui_used) initializeMultitouch(); - if (QApplication::desktopSettingsAware()) + if (QGuiApplication::desktopSettingsAware()) if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) { QApplicationPrivate::enabledAnimations = theme->themeHint(QPlatformTheme::UiEffects).toInt(); } @@ -1172,7 +1172,7 @@ void QApplication::setStyle(QStyle *style) QWidget *w = *it; if (w->windowType() != Qt::Desktop && !w->testAttribute(Qt::WA_SetStyle)) { QEvent e(QEvent::StyleChange); - QApplication::sendEvent(w, &e); + QCoreApplication::sendEvent(w, &e); w->update(); } } @@ -1189,7 +1189,7 @@ void QApplication::setStyle(QStyle *style) if (QApplicationPrivate::focus_widget) { QFocusEvent in(QEvent::FocusIn, Qt::OtherFocusReason); - QApplication::sendEvent(QApplicationPrivate::focus_widget->style(), &in); + QCoreApplication::sendEvent(QApplicationPrivate::focus_widget->style(), &in); QApplicationPrivate::focus_widget->update(); } } @@ -1551,7 +1551,7 @@ void QApplication::setFont(const QFont &font, const char *className) if (QApplicationPrivate::is_app_running && !QApplicationPrivate::is_app_closing) { // Send ApplicationFontChange to qApp itself, and to the widgets. QEvent e(QEvent::ApplicationFontChange); - QApplication::sendEvent(QApplication::instance(), &e); + QCoreApplication::sendEvent(QApplication::instance(), &e); QWidgetList wids = QApplication::allWidgets(); for (QWidgetList::ConstIterator it = wids.constBegin(), cend = wids.constEnd(); it != cend; ++it) { @@ -1565,7 +1565,7 @@ void QApplication::setFont(const QFont &font, const char *className) QList &scenes = qApp->d_func()->scene_list; for (QList::ConstIterator it = scenes.constBegin(); it != scenes.constEnd(); ++it) { - QApplication::sendEvent(*it, &e); + QCoreApplication::sendEvent(*it, &e); } #endif // QT_CONFIG(graphicsview) } @@ -1739,16 +1739,16 @@ void QApplicationPrivate::setFocusWidget(QWidget *focus, Qt::FocusReason reason) #endif QFocusEvent out(QEvent::FocusOut, reason); QPointer that = prev; - QApplication::sendEvent(prev, &out); + QCoreApplication::sendEvent(prev, &out); if (that) - QApplication::sendEvent(that->style(), &out); + QCoreApplication::sendEvent(that->style(), &out); } if(focus && QApplicationPrivate::focus_widget == focus) { QFocusEvent in(QEvent::FocusIn, reason); QPointer that = focus; - QApplication::sendEvent(focus, &in); + QCoreApplication::sendEvent(focus, &in); if (that) - QApplication::sendEvent(that->style(), &in); + QCoreApplication::sendEvent(that->style(), &in); } emit qApp->focusChanged(prev, focus_widget); } @@ -1924,7 +1924,7 @@ bool QApplication::event(QEvent *e) } if (showToolTip) { QHelpEvent e(QEvent::ToolTip, d->toolTipPos, d->toolTipGlobalPos); - QApplication::sendEvent(d->toolTipWidget, &e); + QCoreApplication::sendEvent(d->toolTipWidget, &e); if (e.isAccepted()) { QStyle *s = d->toolTipWidget->style(); int sleepDelay = s->styleHint(QStyle::SH_ToolTip_FallAsleepDelay, 0, d->toolTipWidget, 0); @@ -2036,7 +2036,7 @@ void QApplication::setActiveWindow(QWidget* act) QGuiApplication::inputMethod()->commit(); QFocusEvent focusAboutToChange(QEvent::FocusAboutToChange, Qt::ActiveWindowFocusReason); - QApplication::sendEvent(QApplicationPrivate::focus_widget, &focusAboutToChange); + QCoreApplication::sendEvent(QApplicationPrivate::focus_widget, &focusAboutToChange); } QApplicationPrivate::active_window = window; @@ -2111,7 +2111,7 @@ QWidget *qt_tlw_for_window(QWindow *wnd) wnd = wnd->parent(); } if (wnd) { - const auto tlws = qApp->topLevelWidgets(); + const auto tlws = QApplication::topLevelWidgets(); for (QWidget *tlw : tlws) { if (tlw->windowHandle() == wnd) return tlw; @@ -2205,12 +2205,12 @@ void QApplicationPrivate::dispatchEnterLeave(QWidget* enter, QWidget* leave, con #if 0 if (leave) { QEvent e(QEvent::Leave); - QApplication::sendEvent(leave, & e); + QCoreApplication::sendEvent(leave, & e); } if (enter) { const QPoint windowPos = enter->window()->mapFromGlobal(globalPos); QEnterEvent e(enter->mapFromGlobal(globalPos), windowPos, globalPos); - QApplication::sendEvent(enter, & e); + QCoreApplication::sendEvent(enter, & e); } return; #endif @@ -2271,12 +2271,12 @@ void QApplicationPrivate::dispatchEnterLeave(QWidget* enter, QWidget* leave, con for (int i = 0; i < leaveList.size(); ++i) { auto *w = leaveList.at(i); if (!QApplication::activeModalWidget() || QApplicationPrivate::tryModalHelper(w, 0)) { - QApplication::sendEvent(w, &leaveEvent); + QCoreApplication::sendEvent(w, &leaveEvent); if (w->testAttribute(Qt::WA_Hover) && (!QApplication::activePopupWidget() || QApplication::activePopupWidget() == w->window())) { Q_ASSERT(instance()); QHoverEvent he(QEvent::HoverLeave, QPoint(-1, -1), w->mapFromGlobal(QApplicationPrivate::instance()->hoverGlobalPos), - QApplication::keyboardModifiers()); + QGuiApplication::keyboardModifiers()); qApp->d_func()->notify_helper(w, &he); } } @@ -2292,11 +2292,11 @@ void QApplicationPrivate::dispatchEnterLeave(QWidget* enter, QWidget* leave, con if (!QApplication::activeModalWidget() || QApplicationPrivate::tryModalHelper(w, 0)) { const QPointF localPos = w->mapFromGlobal(globalPos); QEnterEvent enterEvent(localPos, windowPos, globalPosF); - QApplication::sendEvent(w, &enterEvent); + QCoreApplication::sendEvent(w, &enterEvent); if (w->testAttribute(Qt::WA_Hover) && (!QApplication::activePopupWidget() || QApplication::activePopupWidget() == w->window())) { QHoverEvent he(QEvent::HoverEnter, localPos, QPoint(-1, -1), - QApplication::keyboardModifiers()); + QGuiApplication::keyboardModifiers()); qApp->d_func()->notify_helper(w, &he); } } @@ -2644,7 +2644,7 @@ bool QApplicationPrivate::sendMouseEvent(QWidget *receiver, QMouseEvent *event, if (spontaneous) result = QApplication::sendSpontaneousEvent(receiver, event); else - result = QApplication::sendEvent(receiver, event); + result = QCoreApplication::sendEvent(receiver, event); } if (!graphicsWidget && leaveAfterRelease && event->type() == QEvent::MouseButtonRelease @@ -2898,7 +2898,7 @@ bool QApplication::notify(QObject *receiver, QEvent *e) } #ifndef QT_NO_DEBUG - d->checkReceiverThread(receiver); + QCoreApplicationPrivate::checkReceiverThread(receiver); #endif if (receiver->isWindowType()) { @@ -3818,7 +3818,7 @@ void QApplicationPrivate::openPopup(QWidget *popup) } else if (popupWidgets->count() == 1) { // this was the first popup if (QWidget *fw = QApplication::focusWidget()) { QFocusEvent e(QEvent::FocusOut, Qt::PopupFocusReason); - QApplication::sendEvent(fw, &e); + QCoreApplication::sendEvent(fw, &e); } } } @@ -4383,7 +4383,7 @@ bool QApplicationPrivate::translateRawTouchEvent(QWidget *window, QTouchEvent touchEvent(eventType, device, - QApplication::keyboardModifiers(), + QGuiApplication::keyboardModifiers(), it.value().first, it.value().second); bool containsPress = updateTouchPointsForWidget(widget, &touchEvent); @@ -4426,7 +4426,7 @@ bool QApplicationPrivate::translateRawTouchEvent(QWidget *window, void QApplicationPrivate::translateTouchCancel(QTouchDevice *device, ulong timestamp) { - QTouchEvent touchEvent(QEvent::TouchCancel, device, QApplication::keyboardModifiers()); + QTouchEvent touchEvent(QEvent::TouchCancel, device, QGuiApplication::keyboardModifiers()); touchEvent.setTimestamp(timestamp); QHash::const_iterator it = self->activeTouchPoints.constBegin(), ite = self->activeTouchPoints.constEnd(); @@ -4462,12 +4462,12 @@ void QApplicationPrivate::sendApplicationPaletteChange(bool toAllWidgets, const const QWidgetList widgets = QApplication::allWidgets(); for (auto widget : widgets) { if (toAllWidgets || (!className && widget->isWindow()) || (className && widget->inherits(className))) - QApplication::sendEvent(widget, &event); + QCoreApplication::sendEvent(widget, &event); } #if QT_CONFIG(graphicsview) for (auto scene : qAsConst(scene_list)) - QApplication::sendEvent(scene, &event); + QCoreApplication::sendEvent(scene, &event); #endif // QT_CONFIG(graphicsview) } diff --git a/src/widgets/kernel/qboxlayout.cpp b/src/widgets/kernel/qboxlayout.cpp index a368f379ad..76d8533271 100644 --- a/src/widgets/kernel/qboxlayout.cpp +++ b/src/widgets/kernel/qboxlayout.cpp @@ -172,7 +172,7 @@ void QBoxLayoutPrivate::effectiveMargins(int *left, int *top, int *right, int *b rightDelta = w->geometry().right() - itm->geometry().right(); } QWidget *w = q->parentWidget(); - Qt::LayoutDirection layoutDirection = w ? w->layoutDirection() : QApplication::layoutDirection(); + Qt::LayoutDirection layoutDirection = w ? w->layoutDirection() : QGuiApplication::layoutDirection(); if (layoutDirection == Qt::RightToLeft) qSwap(leftDelta, rightDelta); diff --git a/src/widgets/kernel/qformlayout.cpp b/src/widgets/kernel/qformlayout.cpp index 9146ba84c8..6f7527c013 100644 --- a/src/widgets/kernel/qformlayout.cpp +++ b/src/widgets/kernel/qformlayout.cpp @@ -2178,7 +2178,7 @@ void QFormLayoutPrivate::arrangeWidgets(const QVector& layouts, Q int i; const int rr = m_matrix.rowCount(); QWidget *w = q->parentWidget(); - Qt::LayoutDirection layoutDirection = w ? w->layoutDirection() : QApplication::layoutDirection(); + Qt::LayoutDirection layoutDirection = w ? w->layoutDirection() : QGuiApplication::layoutDirection(); Qt::Alignment formAlignment = fixedAlignment(q->formAlignment(), layoutDirection); int leftOffset = 0; diff --git a/src/widgets/kernel/qgesturemanager.cpp b/src/widgets/kernel/qgesturemanager.cpp index ff7bc1eccf..1555c2a73a 100644 --- a/src/widgets/kernel/qgesturemanager.cpp +++ b/src/widgets/kernel/qgesturemanager.cpp @@ -707,7 +707,7 @@ void QGestureManager::deliverEvents(const QSet &gestures, foreach(QGesture *g, gestures) event.setAccepted(g, false); - QApplication::sendEvent(receiver, &event); + QCoreApplication::sendEvent(receiver, &event); bool eventAccepted = event.isAccepted(); const auto eventGestures = event.gestures(); for (QGesture *gesture : eventGestures) { @@ -734,7 +734,7 @@ void QGestureManager::deliverEvents(const QSet &gestures, qCDebug(lcGestureManager) << "QGestureManager::deliverEvents: sending to" << it.key() << "gestures:" << it.value(); QGestureEvent event(it.value()); - QApplication::sendEvent(it.key(), &event); + QCoreApplication::sendEvent(it.key(), &event); bool eventAccepted = event.isAccepted(); const auto eventGestures = event.gestures(); for (QGesture *gesture : eventGestures) { diff --git a/src/widgets/kernel/qlayout.cpp b/src/widgets/kernel/qlayout.cpp index f71d038a5f..3ce81a390b 100644 --- a/src/widgets/kernel/qlayout.cpp +++ b/src/widgets/kernel/qlayout.cpp @@ -1040,7 +1040,7 @@ void QLayout::update() if (layout->d_func()->topLevel) { Q_ASSERT(layout->parent()->isWidgetType()); QWidget *mw = static_cast(layout->parent()); - QApplication::postEvent(mw, new QEvent(QEvent::LayoutRequest)); + QCoreApplication::postEvent(mw, new QEvent(QEvent::LayoutRequest)); break; } layout = static_cast(layout->parent()); @@ -1366,7 +1366,7 @@ QRect QLayout::alignmentRect(const QRect &r) const y += (r.height() - s.height()) / 2; QWidget *parent = parentWidget(); - a = QStyle::visualAlignment(parent ? parent->layoutDirection() : QApplication::layoutDirection(), a); + a = QStyle::visualAlignment(parent ? parent->layoutDirection() : QGuiApplication::layoutDirection(), a); if (a & Qt::AlignRight) x += (r.width() - s.width()); else if (!(a & Qt::AlignLeft)) diff --git a/src/widgets/kernel/qopenglwidget.cpp b/src/widgets/kernel/qopenglwidget.cpp index ae7729b49b..451b18d8d3 100644 --- a/src/widgets/kernel/qopenglwidget.cpp +++ b/src/widgets/kernel/qopenglwidget.cpp @@ -1432,7 +1432,7 @@ bool QOpenGLWidget::event(QEvent *e) Q_D(QOpenGLWidget); switch (e->type()) { case QEvent::WindowChangeInternal: - if (qGuiApp->testAttribute(Qt::AA_ShareOpenGLContexts)) + if (QCoreApplication::testAttribute(Qt::AA_ShareOpenGLContexts)) break; if (d->initialized) d->reset(); @@ -1445,7 +1445,7 @@ bool QOpenGLWidget::event(QEvent *e) { // Special case: did grabFramebuffer() for a hidden widget that then became visible. // Recreate all resources since the context now needs to share with the TLW's. - if (!qGuiApp->testAttribute(Qt::AA_ShareOpenGLContexts)) + if (!QCoreApplication::testAttribute(Qt::AA_ShareOpenGLContexts)) d->reset(); } if (!d->initialized && !size().isEmpty() && window()->windowHandle()) { diff --git a/src/widgets/kernel/qshortcut.cpp b/src/widgets/kernel/qshortcut.cpp index a680ff7913..c7b9f3ec7a 100644 --- a/src/widgets/kernel/qshortcut.cpp +++ b/src/widgets/kernel/qshortcut.cpp @@ -228,7 +228,7 @@ static bool correctGraphicsWidgetContext(Qt::ShortcutContext context, QGraphicsW { bool visible = w->isVisible(); #if defined(Q_OS_DARWIN) && QT_CONFIG(menubar) - if (!qApp->testAttribute(Qt::AA_DontUseNativeMenuBar) && qobject_cast(w)) + if (!QCoreApplication::testAttribute(Qt::AA_DontUseNativeMenuBar) && qobject_cast(w)) visible = true; #endif @@ -488,7 +488,7 @@ QShortcut::QShortcut(const QKeySequence &key, QWidget *parent, Q_D(QShortcut); d->sc_context = context; d->sc_sequence = key; - d->redoGrab(qApp->d_func()->shortcutMap); + d->redoGrab(QGuiApplicationPrivate::instance()->shortcutMap); if (member) connect(this, SIGNAL(activated()), parent, member); if (ambiguousMember) @@ -502,7 +502,7 @@ QShortcut::~QShortcut() { Q_D(QShortcut); if (qApp) - qApp->d_func()->shortcutMap.removeShortcut(d->sc_id, this); + QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(d->sc_id, this); } /*! @@ -523,7 +523,7 @@ void QShortcut::setKey(const QKeySequence &key) return; QAPP_CHECK("setKey"); d->sc_sequence = key; - d->redoGrab(qApp->d_func()->shortcutMap); + d->redoGrab(QGuiApplicationPrivate::instance()->shortcutMap); } QKeySequence QShortcut::key() const @@ -554,7 +554,7 @@ void QShortcut::setEnabled(bool enable) return; QAPP_CHECK("setEnabled"); d->sc_enabled = enable; - qApp->d_func()->shortcutMap.setShortcutEnabled(enable, d->sc_id, this); + QGuiApplicationPrivate::instance()->shortcutMap.setShortcutEnabled(enable, d->sc_id, this); } bool QShortcut::isEnabled() const @@ -582,7 +582,7 @@ void QShortcut::setContext(Qt::ShortcutContext context) return; QAPP_CHECK("setContext"); d->sc_context = context; - d->redoGrab(qApp->d_func()->shortcutMap); + d->redoGrab(QGuiApplicationPrivate::instance()->shortcutMap); } Qt::ShortcutContext QShortcut::context() const @@ -634,7 +634,7 @@ void QShortcut::setAutoRepeat(bool on) return; QAPP_CHECK("setAutoRepeat"); d->sc_autorepeat = on; - qApp->d_func()->shortcutMap.setShortcutAutoRepeat(on, d->sc_id, this); + QGuiApplicationPrivate::instance()->shortcutMap.setShortcutAutoRepeat(on, d->sc_id, this); } bool QShortcut::autoRepeat() const diff --git a/src/widgets/kernel/qwhatsthis.cpp b/src/widgets/kernel/qwhatsthis.cpp index 4a798a7490..a48eae6628 100644 --- a/src/widgets/kernel/qwhatsthis.cpp +++ b/src/widgets/kernel/qwhatsthis.cpp @@ -263,7 +263,7 @@ void QWhatsThat::mouseReleaseEvent(QMouseEvent* e) anchor.clear(); if (!href.isEmpty()) { QWhatsThisClickedEvent e(href); - if (QApplication::sendEvent(widget, &e)) + if (QCoreApplication::sendEvent(widget, &e)) return; } } @@ -380,7 +380,7 @@ void QWhatsThisPrivate::notifyToplevels(QEvent *e) { const QWidgetList toplevels = QApplication::topLevelWidgets(); for (auto *w : toplevels) - QApplication::sendEvent(w, e); + QCoreApplication::sendEvent(w, e); } QWhatsThisPrivate *QWhatsThisPrivate::instance = 0; @@ -394,7 +394,7 @@ QWhatsThisPrivate::QWhatsThisPrivate() QPoint pos = QCursor::pos(); if (QWidget *w = QApplication::widgetAt(pos)) { QHelpEvent e(QEvent::QueryWhatsThis, w->mapFromGlobal(pos), pos); - bool sentEvent = QApplication::sendEvent(w, &e); + const bool sentEvent = QCoreApplication::sendEvent(w, &e); #ifdef QT_NO_CURSOR Q_UNUSED(sentEvent); #else @@ -439,7 +439,7 @@ bool QWhatsThisPrivate::eventFilter(QObject *o, QEvent *e) if (me->button() == Qt::RightButton || customWhatsThis) return false; QHelpEvent e(QEvent::WhatsThis, me->pos(), me->globalPos()); - if (!QApplication::sendEvent(w, &e) || !e.isAccepted()) + if (!QCoreApplication::sendEvent(w, &e) || !e.isAccepted()) leaveOnMouseRelease = true; } break; @@ -448,12 +448,12 @@ bool QWhatsThisPrivate::eventFilter(QObject *o, QEvent *e) { QMouseEvent *me = static_cast(e); QHelpEvent e(QEvent::QueryWhatsThis, me->pos(), me->globalPos()); - bool sentEvent = QApplication::sendEvent(w, &e); + const bool sentEvent = QCoreApplication::sendEvent(w, &e); #ifdef QT_NO_CURSOR Q_UNUSED(sentEvent); #else - QApplication::changeOverrideCursor((!sentEvent || !e.isAccepted())? - Qt::ForbiddenCursor:Qt::WhatsThisCursor); + QGuiApplication::changeOverrideCursor((!sentEvent || !e.isAccepted())? + Qt::ForbiddenCursor:Qt::WhatsThisCursor); #endif Q_FALLTHROUGH(); } diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 2176c612d0..f686341f6e 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -359,7 +359,7 @@ void QWidgetPrivate::scrollChildren(int dx, int dy) w->d_func()->setWSGeometry(); w->d_func()->setDirtyOpaqueRegion(); QMoveEvent e(r.topLeft(), oldp); - QApplication::sendEvent(w, &e); + QCoreApplication::sendEvent(w, &e); } } } @@ -435,13 +435,13 @@ void QWidget::setEditFocus(bool on) if (!on && QWidgetPrivate::editingWidget == f) { QWidgetPrivate::editingWidget = 0; QEvent event(QEvent::LeaveEditFocus); - QApplication::sendEvent(f, &event); - QApplication::sendEvent(f->style(), &event); + QCoreApplication::sendEvent(f, &event); + QCoreApplication::sendEvent(f->style(), &event); } else if (on) { QWidgetPrivate::editingWidget = f; QEvent event(QEvent::EnterEditFocus); - QApplication::sendEvent(f, &event); - QApplication::sendEvent(f->style(), &event); + QCoreApplication::sendEvent(f, &event); + QCoreApplication::sendEvent(f->style(), &event); } } #endif @@ -1223,8 +1223,8 @@ void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f) q->create(); QEvent e(QEvent::Create); - QApplication::sendEvent(q, &e); - QApplication::postEvent(q, new QEvent(QEvent::PolishRequest)); + QCoreApplication::sendEvent(q, &e); + QCoreApplication::postEvent(q, new QEvent(QEvent::PolishRequest)); extraPaintEngine = 0; @@ -1624,7 +1624,7 @@ QWidget::~QWidget() // Remove all shortcuts grabbed by this // widget, unless application is closing if (!QApplicationPrivate::is_app_closing && testAttribute(Qt::WA_GrabbedShortcut)) - qApp->d_func()->shortcutMap.removeShortcut(0, this, QKeySequence()); + QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(0, this, QKeySequence()); #endif // delete layout while we still are a valid widget @@ -1732,7 +1732,7 @@ QWidget::~QWidget() if (!d->children.isEmpty()) d->deleteChildren(); - QApplication::removePostedEvents(this); + QCoreApplication::removePostedEvents(this); QT_TRY { destroy(); // platform-dependent cleanup @@ -2044,7 +2044,7 @@ void QWidgetPrivate::propagatePaletteChange() QCoreApplication::testAttribute(Qt::AA_UseStyleSheetPropagationInWidgetStyles); QEvent pc(QEvent::PaletteChange); - QApplication::sendEvent(q, &pc); + QCoreApplication::sendEvent(q, &pc); for (int i = 0; i < children.size(); ++i) { QWidget *w = qobject_cast(children.at(i)); if (w && (!w->testAttribute(Qt::WA_StyleSheet) || useStyleSheetPropagationInWidgetStyles) @@ -2848,7 +2848,7 @@ void QWidgetPrivate::setStyle_helper(QStyle *newStyle, bool propagate) #endif QEvent e(QEvent::StyleChange); - QApplication::sendEvent(q, &e); + QCoreApplication::sendEvent(q, &e); #ifndef QT_NO_STYLE_STYLESHEET // dereference the old stylesheet style @@ -3066,7 +3066,7 @@ void QWidget::overrideWindowState(Qt::WindowStates newstate) { QWindowStateChangeEvent e(Qt::WindowStates(data->window_state), true); data->window_state = newstate; - QApplication::sendEvent(this, &e); + QCoreApplication::sendEvent(this, &e); } /*! @@ -3128,7 +3128,7 @@ void QWidget::setWindowState(Qt::WindowStates newstate) activateWindow(); QWindowStateChangeEvent e(oldstate); - QApplication::sendEvent(this, &e); + QCoreApplication::sendEvent(this, &e); } /*! @@ -3354,7 +3354,7 @@ void QWidget::insertAction(QAction *before, QAction *action) apriv->widgets.append(this); QActionEvent e(QEvent::ActionAdded, action, before); - QApplication::sendEvent(this, &e); + QCoreApplication::sendEvent(this, &e); } /*! @@ -3392,7 +3392,7 @@ void QWidget::removeAction(QAction *action) if (d->actions.removeAll(action)) { QActionEvent e(QEvent::ActionRemoved, action); - QApplication::sendEvent(this, &e); + QCoreApplication::sendEvent(this, &e); } } @@ -3499,7 +3499,7 @@ void QWidgetPrivate::setEnabled_helper(bool enable) } #endif //QT_NO_IM QEvent e(QEvent::EnabledChange); - QApplication::sendEvent(q, &e); + QCoreApplication::sendEvent(q, &e); } /*! @@ -4566,7 +4566,7 @@ void QWidget::setForegroundRole(QPalette::ColorRole role) the "color", "background-color", "selection-color", "selection-background-color" and "alternate-background-color". - \sa QApplication::palette(), QWidget::font(), {Qt Style Sheets} + \sa QGuiApplication::palette(), QWidget::font(), {Qt Style Sheets} */ const QPalette &QWidget::palette() const { @@ -4629,7 +4629,7 @@ QPalette QWidgetPrivate::naturalWidgetPalette(uint inheritedMask) const )) { if (QWidget *p = q->parentWidget()) { if (!p->testAttribute(Qt::WA_StyleSheet) || useStyleSheetPropagationInWidgetStyles) { - if (!naturalPalette.isCopyOf(QApplication::palette())) { + if (!naturalPalette.isCopyOf(QGuiApplication::palette())) { QPalette inheritedPalette = p->palette(); inheritedPalette.resolve(inheritedMask); naturalPalette = inheritedPalette.resolve(naturalPalette); @@ -4895,7 +4895,7 @@ void QWidgetPrivate::updateFont(const QFont &font) #endif QEvent e(QEvent::FontChange); - QApplication::sendEvent(q, &e); + QCoreApplication::sendEvent(q, &e); } void QWidgetPrivate::setLayoutDirection_helper(Qt::LayoutDirection direction) @@ -4913,14 +4913,14 @@ void QWidgetPrivate::setLayoutDirection_helper(Qt::LayoutDirection direction) } } QEvent e(QEvent::LayoutDirectionChange); - QApplication::sendEvent(q, &e); + QCoreApplication::sendEvent(q, &e); } void QWidgetPrivate::resolveLayoutDirection() { Q_Q(const QWidget); if (!q->testAttribute(Qt::WA_SetLayoutDirection)) - setLayoutDirection_helper(q->isWindow() ? QApplication::layoutDirection() : q->parentWidget()->layoutDirection()); + setLayoutDirection_helper(q->isWindow() ? QGuiApplication::layoutDirection() : q->parentWidget()->layoutDirection()); } /*! @@ -5040,7 +5040,7 @@ void QWidget::setCursor(const QCursor &cursor) d->setCursor_sys(cursor); QEvent event(QEvent::CursorChange); - QApplication::sendEvent(this, &event); + QCoreApplication::sendEvent(this, &event); } void QWidgetPrivate::setCursor_sys(const QCursor &cursor) @@ -5062,7 +5062,7 @@ void QWidget::unsetCursor() d->unsetCursor_sys(); QEvent event(QEvent::CursorChange); - QApplication::sendEvent(this, &event); + QCoreApplication::sendEvent(this, &event); } void QWidgetPrivate::unsetCursor_sys() @@ -5260,7 +5260,7 @@ void QWidget::render(QPainter *painter, const QPoint &targetOffset, static void sendResizeEvents(QWidget *target) { QResizeEvent e(target->size(), QSize()); - QApplication::sendEvent(target, &e); + QCoreApplication::sendEvent(target, &e); const QObjectList children = target->children(); for (int i = 0; i < children.size(); ++i) { @@ -6014,7 +6014,7 @@ void QWidgetPrivate::setLocale_helper(const QLocale &loc, bool forceUpdate) } } QEvent e(QEvent::LocaleChange); - QApplication::sendEvent(q, &e); + QCoreApplication::sendEvent(q, &e); } void QWidget::setLocale(const QLocale &locale) @@ -6189,7 +6189,7 @@ void QWidget::setWindowIconText(const QString &iconText) d->setWindowIconText_helper(iconText); QEvent e(QEvent::IconTextChange); - QApplication::sendEvent(this, &e); + QCoreApplication::sendEvent(this, &e); emit windowIconTextChanged(iconText); } @@ -6213,7 +6213,7 @@ void QWidget::setWindowTitle(const QString &title) d->setWindowTitle_helper(title); QEvent e(QEvent::WindowTitleChange); - QApplication::sendEvent(this, &e); + QCoreApplication::sendEvent(this, &e); emit windowTitleChanged(title); } @@ -6251,11 +6251,11 @@ void QWidgetPrivate::setWindowIcon_helper() // QWidgetWindow to the top level QWidget ensures that the event reaches // the top level anyhow if (!q->windowHandle()) - QApplication::sendEvent(q, &e); + QCoreApplication::sendEvent(q, &e); for (int i = 0; i < children.size(); ++i) { QWidget *w = qobject_cast(children.at(i)); if (w && !w->isWindow()) - QApplication::sendEvent(w, &e); + QCoreApplication::sendEvent(w, &e); } } @@ -6598,7 +6598,7 @@ void QWidget::setFocus(Qt::FocusReason reason) if (reason != Qt::NoFocusReason) { QFocusEvent focusAboutToChange(QEvent::FocusAboutToChange, reason); - QApplication::sendEvent(prev, &focusAboutToChange); + QCoreApplication::sendEvent(prev, &focusAboutToChange); } } @@ -6625,9 +6625,9 @@ void QWidget::setFocus(Qt::FocusReason reason) // Send event to self QFocusEvent event(QEvent::FocusOut, reason); QPointer that = previousProxyFocus; - QApplication::sendEvent(previousProxyFocus, &event); + QCoreApplication::sendEvent(previousProxyFocus, &event); if (that) - QApplication::sendEvent(that->style(), &event); + QCoreApplication::sendEvent(that->style(), &event); } if (!isHidden()) { #if QT_CONFIG(graphicsview) @@ -6639,9 +6639,9 @@ void QWidget::setFocus(Qt::FocusReason reason) // Send event to self QFocusEvent event(QEvent::FocusIn, reason); QPointer that = f; - QApplication::sendEvent(f, &event); + QCoreApplication::sendEvent(f, &event); if (that) - QApplication::sendEvent(that->style(), &event); + QCoreApplication::sendEvent(that->style(), &event); } } } @@ -6748,7 +6748,7 @@ void QWidget::clearFocus() QGuiApplication::inputMethod()->commit(); QFocusEvent focusAboutToChange(QEvent::FocusAboutToChange); - QApplication::sendEvent(this, &focusAboutToChange); + QCoreApplication::sendEvent(this, &focusAboutToChange); } QWidget *w = this; @@ -7395,11 +7395,11 @@ void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove) if (isMove) { QMoveEvent e(q->pos(), oldPos); - QApplication::sendEvent(q, &e); + QCoreApplication::sendEvent(q, &e); } if (isResize) { QResizeEvent e(r.size(), olds); - QApplication::sendEvent(q, &e); + QCoreApplication::sendEvent(q, &e); if (q->windowHandle()) q->update(); } @@ -7697,13 +7697,13 @@ void QWidgetPrivate::updateContentsRect() if (q->isVisible()) { q->update(); QResizeEvent e(q->data->crect.size(), q->data->crect.size()); - QApplication::sendEvent(q, &e); + QCoreApplication::sendEvent(q, &e); } else { q->setAttribute(Qt::WA_PendingResizeEvent, true); } QEvent e(QEvent::ContentsRectChange); - QApplication::sendEvent(q, &e); + QCoreApplication::sendEvent(q, &e); } /*! @@ -7999,13 +7999,13 @@ void QWidgetPrivate::sendPendingMoveAndResizeEvents(bool recursive, bool disable if (q->testAttribute(Qt::WA_PendingMoveEvent)) { QMoveEvent e(data.crect.topLeft(), data.crect.topLeft()); - QApplication::sendEvent(q, &e); + QCoreApplication::sendEvent(q, &e); q->setAttribute(Qt::WA_PendingMoveEvent, false); } if (q->testAttribute(Qt::WA_PendingResizeEvent)) { QResizeEvent e(data.crect.size(), QSize()); - QApplication::sendEvent(q, &e); + QCoreApplication::sendEvent(q, &e); q->setAttribute(Qt::WA_PendingResizeEvent, false); } @@ -8115,7 +8115,7 @@ void QWidgetPrivate::show_helper() // send the show event before showing the window QShowEvent showEvent; - QApplication::sendEvent(q, &showEvent); + QCoreApplication::sendEvent(q, &showEvent); show_sys(); @@ -8138,7 +8138,7 @@ void QWidgetPrivate::show_helper() // is spinnning; otherwise it might not show up on particular platforms. // This makes QSplashScreen behave the same on all platforms. if (!qApp->d_func()->in_exec && q->windowType() == Qt::SplashScreen) - QApplication::processEvents(); + QCoreApplication::processEvents(); data.in_show = false; // reset qws optimization } @@ -8164,9 +8164,9 @@ void QWidgetPrivate::show_sys() } if (renderToTexture && !q->isWindow()) - QApplication::postEvent(q->parentWidget(), new QUpdateLaterEvent(q->geometry())); + QCoreApplication::postEvent(q->parentWidget(), new QUpdateLaterEvent(q->geometry())); else - QApplication::postEvent(q, new QUpdateLaterEvent(q->rect())); + QCoreApplication::postEvent(q, new QUpdateLaterEvent(q->rect())); if ((!q->isWindow() && !q->testAttribute(Qt::WA_NativeWindow)) || q->testAttribute(Qt::WA_OutsideWSRange)) { @@ -8254,7 +8254,7 @@ void QWidgetPrivate::hide_helper() } QHideEvent hideEvent; - QApplication::sendEvent(q, &hideEvent); + QCoreApplication::sendEvent(q, &hideEvent); hideChildren(false); // next bit tries to move the focus if the focus widget is now @@ -8425,7 +8425,7 @@ void QWidgetPrivate::setVisible(bool visible) } QEvent showToParentEvent(QEvent::ShowToParent); - QApplication::sendEvent(q, &showToParentEvent); + QCoreApplication::sendEvent(q, &showToParentEvent); } else { // hide #if 0 // Used to be included in Qt4 for Q_WS_WIN // reset WS_DISABLED style in a Blocked window @@ -8456,11 +8456,11 @@ void QWidgetPrivate::setVisible(bool visible) if (q->parentWidget()->d_func()->layout) q->parentWidget()->d_func()->layout->invalidate(); else if (q->parentWidget()->isVisible()) - QApplication::postEvent(q->parentWidget(), new QEvent(QEvent::LayoutRequest)); + QCoreApplication::postEvent(q->parentWidget(), new QEvent(QEvent::LayoutRequest)); } QEvent hideToParentEvent(QEvent::HideToParent); - QApplication::sendEvent(q, &hideToParentEvent); + QCoreApplication::sendEvent(q, &hideToParentEvent); } } @@ -8535,7 +8535,7 @@ void QWidgetPrivate::hideChildren(bool spontaneous) if (spontaneous) { QApplication::sendSpontaneousEvent(widget, &e); } else { - QApplication::sendEvent(widget, &e); + QCoreApplication::sendEvent(widget, &e); if (widget->internalWinId() && widget->testAttribute(Qt::WA_DontCreateNativeAncestors)) { // hide_sys() on an ancestor won't have any affect on this @@ -8570,7 +8570,7 @@ bool QWidgetPrivate::close_helper(CloseMode mode) if (mode == CloseWithSpontaneousEvent) QApplication::sendSpontaneousEvent(q, &e); else - QApplication::sendEvent(q, &e); + QCoreApplication::sendEvent(q, &e); if (!that.isNull() && !e.isAccepted()) { data.is_closing = 0; return false; @@ -9085,7 +9085,7 @@ bool QWidget::event(QEvent *event) setAttribute(Qt::WA_WState_Polished); if (!QApplication::font(this).isCopyOf(QApplication::font())) d->resolveFont(); - if (!QApplication::palette(this).isCopyOf(QApplication::palette())) + if (!QApplication::palette(this).isCopyOf(QGuiApplication::palette())) d->resolvePalette(); } break; @@ -9109,7 +9109,7 @@ bool QWidget::event(QEvent *event) #if QT_CONFIG(statustip) if (d->statusTip.size()) { QStatusTipEvent tip(d->statusTip); - QApplication::sendEvent(const_cast(this), &tip); + QCoreApplication::sendEvent(const_cast(this), &tip); } #endif enterEvent(event); @@ -9120,7 +9120,7 @@ bool QWidget::event(QEvent *event) if (d->statusTip.size()) { QString empty; QStatusTipEvent tip(empty); - QApplication::sendEvent(const_cast(this), &tip); + QCoreApplication::sendEvent(const_cast(this), &tip); } #endif leaveEvent(event); @@ -9271,7 +9271,7 @@ bool QWidget::event(QEvent *event) for (int i = 0; i < childList.size(); ++i) { QWidget *w = qobject_cast(childList.at(i)); if (w && w->isVisible() && !w->isWindow()) - QApplication::sendEvent(w, event); + QCoreApplication::sendEvent(w, event); } break; } @@ -9282,7 +9282,7 @@ bool QWidget::event(QEvent *event) for (int i = 0; i < childList.size(); ++i) { QObject *o = childList.at(i); if (o) - QApplication::sendEvent(o, event); + QCoreApplication::sendEvent(o, event); } } update(); @@ -9321,7 +9321,7 @@ bool QWidget::event(QEvent *event) QWidget *w = static_cast(o); // do not forward the event to child windows; QApplication does this for us if (!w->isWindow()) - QApplication::sendEvent(w, event); + QCoreApplication::sendEvent(w, event); } } } @@ -9373,7 +9373,7 @@ bool QWidget::event(QEvent *event) for (int i = 0; i < childList.size(); ++i) { QWidget *w = qobject_cast(childList.at(i)); if (w && w->isVisible() && !w->isWindow()) - QApplication::sendEvent(w, event); + QCoreApplication::sendEvent(w, event); } break; } @@ -9482,8 +9482,8 @@ void QWidget::changeEvent(QEvent * event) break; case QEvent::ThemeChange: - if (QApplication::desktopSettingsAware() && windowType() != Qt::Desktop - && qApp && !QApplication::closingDown()) { + if (QGuiApplication::desktopSettingsAware() && windowType() != Qt::Desktop + && qApp && !QCoreApplication::closingDown()) { if (testAttribute(Qt::WA_WState_Polished)) QApplication::style()->unpolish(this); if (testAttribute(Qt::WA_WState_Polished)) @@ -10550,7 +10550,7 @@ void QWidgetPrivate::updateGeometry_helper(bool forceUpdate) if (parent->d_func()->layout) parent->d_func()->layout->invalidate(); else if (parent->isVisible()) - QApplication::postEvent(parent, new QEvent(QEvent::LayoutRequest)); + QCoreApplication::postEvent(parent, new QEvent(QEvent::LayoutRequest)); } } } @@ -10704,7 +10704,7 @@ static void sendWindowChangeToTextureChildrenRecursively(QWidget *widget) QWidgetPrivate *d = QWidgetPrivate::get(widget); if (d->renderToTexture) { QEvent e(QEvent::WindowChangeInternal); - QApplication::sendEvent(widget, &e); + QCoreApplication::sendEvent(widget, &e); } for (int i = 0; i < d->children.size(); ++i) { @@ -10753,7 +10753,7 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f) bool newParent = (parent != parentWidget()) || !wasCreated || desktopWidget; if (newParent && parent && !desktopWidget) { - if (testAttribute(Qt::WA_NativeWindow) && !qApp->testAttribute(Qt::AA_DontCreateNativeWidgetSiblings)) + if (testAttribute(Qt::WA_NativeWindow) && !QCoreApplication::testAttribute(Qt::AA_DontCreateNativeWidgetSiblings)) parent->d_func()->enforceNativeChildren(); else if (parent->d_func()->nativeChildrenForced() || parent->testAttribute(Qt::WA_PaintOnScreen)) setAttribute(Qt::WA_NativeWindow); @@ -10766,7 +10766,7 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f) } if (newParent) { QEvent e(QEvent::ParentAboutToChange); - QApplication::sendEvent(this, &e); + QCoreApplication::sendEvent(this, &e); } } if (newParent && isAncestorOf(focusWidget())) @@ -10839,7 +10839,7 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f) // send and post remaining QObject events if (parent && d->sendChildEvents) { QChildEvent e(QEvent::ChildAdded, this); - QApplication::sendEvent(parent, &e); + QCoreApplication::sendEvent(parent, &e); } //### already hidden above ---> must probably do something smart on the mac @@ -10857,7 +10857,7 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f) } QEvent e(QEvent::ParentChange); - QApplication::sendEvent(this, &e); + QCoreApplication::sendEvent(this, &e); } #ifndef QT_NO_OPENGL //renderToTexture widgets also need to know when their top-level window changes @@ -11220,7 +11220,7 @@ void QWidgetPrivate::update(T r) return; if (q->testAttribute(Qt::WA_WState_InPaintEvent)) { - QApplication::postEvent(q, new QUpdateLaterEvent(clipped)); + QCoreApplication::postEvent(q, new QUpdateLaterEvent(clipped)); return; } @@ -11257,7 +11257,7 @@ void QWidgetPrivate::macUpdateSizeAttribute() { Q_Q(QWidget); QEvent event(QEvent::MacSizeChange); - QApplication::sendEvent(q, &event); + QCoreApplication::sendEvent(q, &event); for (int i = 0; i < children.size(); ++i) { QWidget *w = qobject_cast(children.at(i)); if (w && (!w->isWindow() || w->testAttribute(Qt::WA_WindowPropagation)) @@ -11313,7 +11313,7 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) else if (!on && (isWindow() || !parentWidget() || !parentWidget()->testAttribute(Qt::WA_DropSiteRegistered))) setAttribute(Qt::WA_DropSiteRegistered, false); QEvent e(QEvent::AcceptDropsChange); - QApplication::sendEvent(this, &e); + QCoreApplication::sendEvent(this, &e); break; } case Qt::WA_DropSiteRegistered: { @@ -11396,11 +11396,11 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) break; case Qt::WA_MouseTracking: { QEvent e(QEvent::MouseTrackingChange); - QApplication::sendEvent(this, &e); + QCoreApplication::sendEvent(this, &e); break; } case Qt::WA_TabletTracking: { QEvent e(QEvent::TabletTrackingChange); - QApplication::sendEvent(this, &e); + QCoreApplication::sendEvent(this, &e); break; } case Qt::WA_NativeWindow: { d->createTLExtra(); @@ -11413,7 +11413,7 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) QGuiApplication::inputMethod()->commit(); QGuiApplication::inputMethod()->update(Qt::ImEnabled); } - if (!qApp->testAttribute(Qt::AA_DontCreateNativeWidgetSiblings) && parentWidget()) + if (!QCoreApplication::testAttribute(Qt::AA_DontCreateNativeWidgetSiblings) && parentWidget()) parentWidget()->d_func()->enforceNativeChildren(); if (on && !internalWinId() && testAttribute(Qt::WA_WState_Created)) d->createWinId(); @@ -11649,7 +11649,7 @@ void QWidget::setWindowModified(bool mod) d->setWindowModified_helper(); QEvent e(QEvent::ModifiedChange); - QApplication::sendEvent(this, &e); + QCoreApplication::sendEvent(this, &e); } void QWidgetPrivate::setWindowModified_helper() @@ -11695,7 +11695,7 @@ void QWidget::setToolTip(const QString &s) d->toolTip = s; QEvent event(QEvent::ToolTipChange); - QApplication::sendEvent(this, &event); + QCoreApplication::sendEvent(this, &event); } QString QWidget::toolTip() const @@ -11872,7 +11872,7 @@ int QWidget::grabShortcut(const QKeySequence &key, Qt::ShortcutContext context) if (key.isEmpty()) return 0; setAttribute(Qt::WA_GrabbedShortcut); - return qApp->d_func()->shortcutMap.addShortcut(this, key, context, qWidgetShortcutContextMatcher); + return QGuiApplicationPrivate::instance()->shortcutMap.addShortcut(this, key, context, qWidgetShortcutContextMatcher); } /*! @@ -11894,7 +11894,7 @@ void QWidget::releaseShortcut(int id) { Q_ASSERT(qApp); if (id) - qApp->d_func()->shortcutMap.removeShortcut(id, this, 0); + QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(id, this, 0); } /*! @@ -11913,7 +11913,7 @@ void QWidget::setShortcutEnabled(int id, bool enable) { Q_ASSERT(qApp); if (id) - qApp->d_func()->shortcutMap.setShortcutEnabled(enable, id, this, 0); + QGuiApplicationPrivate::instance()->shortcutMap.setShortcutEnabled(enable, id, this, 0); } /*! @@ -11928,7 +11928,7 @@ void QWidget::setShortcutAutoRepeat(int id, bool enable) { Q_ASSERT(qApp); if (id) - qApp->d_func()->shortcutMap.setShortcutAutoRepeat(enable, id, this, 0); + QGuiApplicationPrivate::instance()->shortcutMap.setShortcutAutoRepeat(enable, id, this, 0); } #endif // QT_NO_SHORTCUT @@ -11983,7 +11983,7 @@ void QWidget::raise() QWindowContainer::parentWasRaised(this); QEvent e(QEvent::ZOrderChange); - QApplication::sendEvent(this, &e); + QCoreApplication::sendEvent(this, &e); } void QWidgetPrivate::raise_sys() @@ -12033,7 +12033,7 @@ void QWidget::lower() QWindowContainer::parentWasLowered(this); QEvent e(QEvent::ZOrderChange); - QApplication::sendEvent(this, &e); + QCoreApplication::sendEvent(this, &e); } void QWidgetPrivate::lower_sys() @@ -12080,7 +12080,7 @@ void QWidget::stackUnder(QWidget* w) d->stackUnder_sys(w); QEvent e(QEvent::ZOrderChange); - QApplication::sendEvent(this, &e); + QCoreApplication::sendEvent(this, &e); } void QWidgetPrivate::stackUnder_sys(QWidget*) diff --git a/src/widgets/kernel/qwidgetbackingstore.cpp b/src/widgets/kernel/qwidgetbackingstore.cpp index 595beeaf47..ab33649b3e 100644 --- a/src/widgets/kernel/qwidgetbackingstore.cpp +++ b/src/widgets/kernel/qwidgetbackingstore.cpp @@ -491,11 +491,11 @@ void QWidgetBackingStore::sendUpdateRequest(QWidget *widget, UpdateTime updateTi switch (updateTime) { case UpdateLater: updateRequestSent = true; - QApplication::postEvent(widget, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority); + QCoreApplication::postEvent(widget, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority); break; case UpdateNow: { QEvent event(QEvent::UpdateRequest); - QApplication::sendEvent(widget, &event); + QCoreApplication::sendEvent(widget, &event); break; } } @@ -1507,7 +1507,7 @@ void QWidgetPrivate::invalidateBackingStore(const T &r) if (r.isEmpty()) return; - if (QApplication::closingDown()) + if (QCoreApplication::closingDown()) return; Q_Q(QWidget); diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp index a5d9eee49d..ba10083829 100644 --- a/src/widgets/kernel/qwidgetwindow.cpp +++ b/src/widgets/kernel/qwidgetwindow.cpp @@ -155,7 +155,7 @@ QWidgetWindow::QWidgetWindow(QWidget *widget) // Enable QOpenGLWidget/QQuickWidget children if the platform plugin supports it, // and the application developer has not explicitly disabled it. if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::RasterGLSurface) - && !QApplication::testAttribute(Qt::AA_ForceRasterWidgets)) { + && !QCoreApplication::testAttribute(Qt::AA_ForceRasterWidgets)) { setSurfaceType(QSurface::RasterGLSurface); } connect(widget, &QObject::objectNameChanged, this, &QWidgetWindow::updateObjectName); @@ -496,8 +496,8 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event) static const QEvent::Type contextMenuTrigger = QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::ContextMenuOnMouseRelease).toBool() ? QEvent::MouseButtonRelease : QEvent::MouseButtonPress; - if (qApp->d_func()->inPopupMode()) { - QWidget *activePopupWidget = qApp->activePopupWidget(); + if (QApplicationPrivate::inPopupMode()) { + QWidget *activePopupWidget = QApplication::activePopupWidget(); QPoint mapped = event->pos(); if (activePopupWidget != m_widget) mapped = activePopupWidget->mapFromGlobal(event->globalPos()); @@ -577,7 +577,7 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event) } } - if (qApp->activePopupWidget() != activePopupWidget + if (QApplication::activePopupWidget() != activePopupWidget && qt_replay_popup_mouse_event && QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::ReplayMousePressOutsidePopup).toBool()) { if (m_widget->windowType() != Qt::Popup) @@ -680,7 +680,7 @@ void QWidgetWindow::handleTouchEvent(QTouchEvent *event) if (event->type() == QEvent::TouchCancel) { QApplicationPrivate::translateTouchCancel(event->device(), event->timestamp()); event->accept(); - } else if (qApp->d_func()->inPopupMode()) { + } else if (QApplicationPrivate::inPopupMode()) { // Ignore touch events for popups. This will cause QGuiApplication to synthesise mouse // events instead, which QWidgetWindow::handleMouseEvent will forward correctly: event->ignore(); @@ -744,7 +744,7 @@ void QWidgetWindow::updateMargins() static void sendScreenChangeRecursively(QWidget *widget) { QEvent e(QEvent::ScreenChangeInternal); - QApplication::sendEvent(widget, &e); + QCoreApplication::sendEvent(widget, &e); QWidgetPrivate *d = QWidgetPrivate::get(widget); for (int i = 0; i < d->children.size(); ++i) { QWidget *w = qobject_cast(d->children.at(i)); diff --git a/src/widgets/kernel/qwindowcontainer.cpp b/src/widgets/kernel/qwindowcontainer.cpp index 4b289d2d33..fd8581edbb 100644 --- a/src/widgets/kernel/qwindowcontainer.cpp +++ b/src/widgets/kernel/qwindowcontainer.cpp @@ -223,7 +223,7 @@ QWindowContainer::QWindowContainer(QWindow *embeddedWindow, QWidget *parent, Qt: // Otherwise we may end up with BadMatch failures on X11. if (embeddedWindow->surfaceType() == QSurface::RasterSurface && QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::RasterGLSurface) - && !QApplication::testAttribute(Qt::AA_ForceRasterWidgets)) + && !QCoreApplication::testAttribute(Qt::AA_ForceRasterWidgets)) embeddedWindow->setSurfaceType(QSurface::RasterGLSurface); d->window = embeddedWindow; diff --git a/src/widgets/styles/qcommonstyle.cpp b/src/widgets/styles/qcommonstyle.cpp index 9e701995a4..7b53f5272c 100644 --- a/src/widgets/styles/qcommonstyle.cpp +++ b/src/widgets/styles/qcommonstyle.cpp @@ -1253,12 +1253,12 @@ void QCommonStylePrivate::tabLayout(const QStyleOptionTab *opt, const QWidget *w *iconRect = QRect(tr.left() + offsetX, tr.center().y() - tabIconSize.height() / 2, tabIconSize.width(), tabIconSize.height()); if (!verticalTabs) - *iconRect = proxyStyle->visualRect(opt->direction, opt->rect, *iconRect); + *iconRect = QStyle::visualRect(opt->direction, opt->rect, *iconRect); tr.setLeft(tr.left() + tabIconSize.width() + 4); } if (!verticalTabs) - tr = proxyStyle->visualRect(opt->direction, opt->rect, tr); + tr = QStyle::visualRect(opt->direction, opt->rect, tr); *textRect = tr; } @@ -4835,7 +4835,7 @@ int QCommonStyle::pixelMetric(PixelMetric m, const QStyleOption *opt, const QWid break; case PM_MessageBoxIconSize: #ifdef Q_OS_MAC - if (QApplication::desktopSettingsAware()) { + if (QGuiApplication::desktopSettingsAware()) { ret = 64; // No DPI scaling, it's handled elsewhere. } else #endif @@ -5460,14 +5460,14 @@ static QIcon clearTextIcon(bool rtl) QPixmap QCommonStyle::standardPixmap(StandardPixmap sp, const QStyleOption *option, const QWidget *widget) const { - const bool rtl = (option && option->direction == Qt::RightToLeft) || (!option && QApplication::isRightToLeft()); + const bool rtl = (option && option->direction == Qt::RightToLeft) || (!option && QGuiApplication::isRightToLeft()); #ifdef QT_NO_IMAGEFORMAT_PNG Q_UNUSED(widget); Q_UNUSED(sp); #else QPixmap pixmap; - if (QApplication::desktopSettingsAware() && !QIcon::themeName().isEmpty()) { + if (QGuiApplication::desktopSettingsAware() && !QIcon::themeName().isEmpty()) { switch (sp) { case SP_DialogYesButton: case SP_DialogOkButton: @@ -5829,7 +5829,7 @@ QIcon QCommonStyle::standardIcon(StandardPixmap standardIcon, const QStyleOption const QWidget *widget) const { QIcon icon; - const bool rtl = (option && option->direction == Qt::RightToLeft) || (!option && QApplication::isRightToLeft()); + const bool rtl = (option && option->direction == Qt::RightToLeft) || (!option && QGuiApplication::isRightToLeft()); #ifdef Q_OS_WIN switch (standardIcon) { @@ -5881,7 +5881,7 @@ QIcon QCommonStyle::standardIcon(StandardPixmap standardIcon, const QStyleOption #endif - if (QApplication::desktopSettingsAware() && !QIcon::themeName().isEmpty()) { + if (QGuiApplication::desktopSettingsAware() && !QIcon::themeName().isEmpty()) { switch (standardIcon) { case SP_DirHomeIcon: icon = QIcon::fromTheme(QLatin1String("user-home")); @@ -6057,13 +6057,13 @@ QIcon QCommonStyle::standardIcon(StandardPixmap standardIcon, const QStyleOption default: break; } - } // if (QApplication::desktopSettingsAware() && !QIcon::themeName().isEmpty()) + } // if (QGuiApplication::desktopSettingsAware() && !QIcon::themeName().isEmpty()) if (!icon.isNull()) return icon; #if defined(Q_OS_MAC) - if (QApplication::desktopSettingsAware()) { + if (QGuiApplication::desktopSettingsAware()) { switch (standardIcon) { case SP_DirIcon: { // A rather special case @@ -6129,7 +6129,7 @@ QIcon QCommonStyle::standardIcon(StandardPixmap standardIcon, const QStyleOption default: break; } - } // if (QApplication::desktopSettingsAware()) + } // if (QGuiApplication::desktopSettingsAware()) #endif // Q_OS_MAC switch (standardIcon) { diff --git a/src/widgets/styles/qstyle.cpp b/src/widgets/styles/qstyle.cpp index 2c1132da19..6cbed34c3a 100644 --- a/src/widgets/styles/qstyle.cpp +++ b/src/widgets/styles/qstyle.cpp @@ -560,7 +560,7 @@ QRect QStyle::itemPixmapRect(const QRect &rect, int alignment, const QPixmap &pi x += w - pixmapWidth; else if ((alignment & Qt::AlignHCenter) == Qt::AlignHCenter) x += w/2 - pixmapWidth/2; - else if ((alignment & Qt::AlignLeft) != Qt::AlignLeft && QApplication::isRightToLeft()) + else if ((alignment & Qt::AlignLeft) != Qt::AlignLeft && QGuiApplication::isRightToLeft()) x += w - pixmapWidth; result = QRect(x, y, pixmapWidth, pixmapHeight); return result; @@ -624,7 +624,7 @@ void QStyle::drawItemPixmap(QPainter *painter, const QRect &rect, int alignment, const QPixmap &pixmap) const { qreal scale = pixmap.devicePixelRatio(); - QRect aligned = alignedRect(QApplication::layoutDirection(), QFlag(alignment), pixmap.size() / scale, rect); + QRect aligned = alignedRect(QGuiApplication::layoutDirection(), QFlag(alignment), pixmap.size() / scale, rect); QRect inter = aligned.intersected(rect); painter->drawPixmap(inter.x(), inter.y(), pixmap, inter.x() - aligned.x(), inter.y() - aligned.y(), inter.width() * scale, inter.height() *scale); diff --git a/src/widgets/styles/qstyleoption.cpp b/src/widgets/styles/qstyleoption.cpp index 88031a9f1e..4faf98a0a3 100644 --- a/src/widgets/styles/qstyleoption.cpp +++ b/src/widgets/styles/qstyleoption.cpp @@ -151,7 +151,7 @@ QT_BEGIN_NAMESPACE QStyleOption::QStyleOption(int version, int type) : version(version), type(type), state(QStyle::State_None), - direction(QApplication::layoutDirection()), fontMetrics(QFont()), styleObject(0) + direction(QGuiApplication::layoutDirection()), fontMetrics(QFont()), styleObject(0) { } diff --git a/src/widgets/styles/qstylesheetstyle.cpp b/src/widgets/styles/qstylesheetstyle.cpp index c1f498cd0c..ea653459d3 100644 --- a/src/widgets/styles/qstylesheetstyle.cpp +++ b/src/widgets/styles/qstylesheetstyle.cpp @@ -909,7 +909,7 @@ static QStyle::StandardPixmap subControlIcon(int pe) QRenderRule::QRenderRule(const QVector &declarations, const QObject *object) : features(0), hasFont(false), pal(0), b(0), bg(0), bd(0), ou(0), geo(0), p(0), img(0), clipset(0) { - QPalette palette = QApplication::palette(); // ###: ideally widget's palette + QPalette palette = QGuiApplication::palette(); // ###: ideally widget's palette ValueExtractor v(declarations, palette); features = v.extractStyleFeatures(); @@ -2734,7 +2734,7 @@ static void updateObjects(const QList& objects) for (const QObject *object : objects) { if (auto widget = qobject_cast(const_cast(object))) { widget->style()->polish(widget); - QApplication::sendEvent(widget, &event); + QCoreApplication::sendEvent(widget, &event); } } } @@ -6073,7 +6073,7 @@ void QStyleSheetStyle::updateStyleSheetFont(QWidget* w) const w->d_func()->directFontResolveMask = font.resolve(); QEvent e(QEvent::FontChange); - QApplication::sendEvent(w, &e); + QCoreApplication::sendEvent(w, &e); } } diff --git a/src/widgets/styles/qwindowsstyle.cpp b/src/widgets/styles/qwindowsstyle.cpp index 3c0478b84e..1d5934e3f7 100644 --- a/src/widgets/styles/qwindowsstyle.cpp +++ b/src/widgets/styles/qwindowsstyle.cpp @@ -244,11 +244,12 @@ void QWindowsStyle::polish(QApplication *app) if (!proxy()->styleHint(SH_UnderlineShortcut, 0) && app) app->installEventFilter(this); - d->activeCaptionColor = app->palette().highlight().color(); - d->activeGradientCaptionColor = app->palette().highlight() .color(); - d->inactiveCaptionColor = app->palette().dark().color(); - d->inactiveGradientCaptionColor = app->palette().dark().color(); - d->inactiveCaptionText = app->palette().window().color(); + const auto &palette = QGuiApplication::palette(); + d->activeGradientCaptionColor = palette.highlight().color(); + d->activeCaptionColor = d->activeGradientCaptionColor; + d->inactiveGradientCaptionColor = palette.dark().color(); + d->inactiveCaptionColor = d->inactiveGradientCaptionColor; + d->inactiveCaptionText = palette.window().color(); #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) //fetch native title bar colors if(app->desktopSettingsAware()){ diff --git a/src/widgets/util/qcompleter.cpp b/src/widgets/util/qcompleter.cpp index e41f7e7573..04407cdb1d 100644 --- a/src/widgets/util/qcompleter.cpp +++ b/src/widgets/util/qcompleter.cpp @@ -1504,7 +1504,7 @@ bool QCompleter::eventFilter(QObject *o, QEvent *e) case QEvent::InputMethod: case QEvent::ShortcutOverride: - QApplication::sendEvent(d->widget, e); + QCoreApplication::sendEvent(d->widget, e); break; default: diff --git a/src/widgets/util/qflickgesture.cpp b/src/widgets/util/qflickgesture.cpp index a8b2a00a80..55ec93c7d3 100644 --- a/src/widgets/util/qflickgesture.cpp +++ b/src/widgets/util/qflickgesture.cpp @@ -228,7 +228,7 @@ public: // we did send a press, so we need to fake a release now // release all pressed mouse buttons - /* Qt::MouseButtons mouseButtons = QApplication::mouseButtons(); + /* Qt::MouseButtons mouseButtons = QGuiApplication::mouseButtons(); for (int i = 0; i < 32; ++i) { if (mouseButtons & (1 << i)) { Qt::MouseButton b = static_cast(1 << i); @@ -237,7 +237,7 @@ public: qFGDebug() << "QFG: sending a fake mouse release at far-far-away to " << mouseTarget; QMouseEvent re(QEvent::MouseButtonRelease, QPoint(), farFarAway, - b, mouseButtons, QApplication::keyboardModifiers()); + b, mouseButtons, QGuiApplication::keyboardModifiers()); sendMouseEvent(&re); } }*/ @@ -246,8 +246,8 @@ public: qFGDebug() << "QFG: sending a fake mouse release at far-far-away to " << mouseTarget; QMouseEvent re(QEvent::MouseButtonRelease, QPoint(), farFarAway, farFarAway, - mouseButton, QApplication::mouseButtons() & ~mouseButton, - QApplication::keyboardModifiers(), mouseEventSource); + mouseButton, QGuiApplication::mouseButtons() & ~mouseButton, + QGuiApplication::keyboardModifiers(), mouseEventSource); sendMouseEvent(&re, RegrabMouseAfterwards); // don't clear the mouseTarget just yet, since we need to explicitly ungrab the mouse on release! } diff --git a/src/widgets/util/qsystemtrayicon_x11.cpp b/src/widgets/util/qsystemtrayicon_x11.cpp index 70e5f3678e..0c7bb94a91 100644 --- a/src/widgets/util/qsystemtrayicon_x11.cpp +++ b/src/widgets/util/qsystemtrayicon_x11.cpp @@ -151,11 +151,11 @@ bool QSystemTrayIconSys::event(QEvent *e) { switch (e->type()) { case QEvent::ToolTip: - QApplication::sendEvent(q, e); + QCoreApplication::sendEvent(q, e); break; #if QT_CONFIG(wheelevent) case QEvent::Wheel: - return QApplication::sendEvent(q, e); + return QCoreApplication::sendEvent(q, e); #endif default: break; diff --git a/src/widgets/widgets/qabstractscrollarea.cpp b/src/widgets/widgets/qabstractscrollarea.cpp index 5ea8330db2..b9e0d3280f 100644 --- a/src/widgets/widgets/qabstractscrollarea.cpp +++ b/src/widgets/widgets/qabstractscrollarea.cpp @@ -1086,7 +1086,7 @@ bool QAbstractScrollArea::event(QEvent *e) QScrollBar *vBar = verticalScrollBar(); QPointF delta = g->delta(); if (!delta.isNull()) { - if (QApplication::isRightToLeft()) + if (QGuiApplication::isRightToLeft()) delta.rx() *= -1; int newX = hBar->value() - delta.x(); int newY = vBar->value() - delta.y(); @@ -1325,9 +1325,9 @@ void QAbstractScrollArea::wheelEvent(QWheelEvent *e) { Q_D(QAbstractScrollArea); if (e->orientation() == Qt::Horizontal) - QApplication::sendEvent(d->hbar, e); + QCoreApplication::sendEvent(d->hbar, e); else - QApplication::sendEvent(d->vbar, e); + QCoreApplication::sendEvent(d->vbar, e); } #endif diff --git a/src/widgets/widgets/qabstractspinbox.cpp b/src/widgets/widgets/qabstractspinbox.cpp index 04276aa400..e6e9939a10 100644 --- a/src/widgets/widgets/qabstractspinbox.cpp +++ b/src/widgets/widgets/qabstractspinbox.cpp @@ -333,7 +333,7 @@ void QAbstractSpinBox::setReadOnly(bool enable) d->readOnly = enable; d->edit->setReadOnly(enable); QEvent event(QEvent::ReadOnlyChange); - QApplication::sendEvent(this, &event); + QCoreApplication::sendEvent(this, &event); update(); } @@ -574,10 +574,10 @@ QAbstractSpinBox::StepEnabled QAbstractSpinBox::stepEnabled() const if (d->wrapping) return StepEnabled(StepUpEnabled | StepDownEnabled); StepEnabled ret = StepNone; - if (d->variantCompare(d->value, d->maximum) < 0) { + if (QAbstractSpinBoxPrivate::variantCompare(d->value, d->maximum) < 0) { ret |= StepUpEnabled; } - if (d->variantCompare(d->value, d->minimum) > 0) { + if (QAbstractSpinBoxPrivate::variantCompare(d->value, d->minimum) > 0) { ret |= StepDownEnabled; } return ret; diff --git a/src/widgets/widgets/qcombobox.cpp b/src/widgets/widgets/qcombobox.cpp index a052f2df79..0b52747b6a 100644 --- a/src/widgets/widgets/qcombobox.cpp +++ b/src/widgets/widgets/qcombobox.cpp @@ -2618,7 +2618,7 @@ bool QComboBoxPrivate::showNativePopup() // We need to fake one here to un-press the button. QMouseEvent mouseReleased(QEvent::MouseButtonRelease, q->pos(), Qt::LeftButton, Qt::MouseButtons(Qt::LeftButton), Qt::KeyboardModifiers()); - qApp->sendEvent(q, &mouseReleased); + QCoreApplication::sendEvent(q, &mouseReleased); #endif return true; @@ -2915,7 +2915,7 @@ void QComboBox::hidePopup() bool didFade = false; if (needFade) { #if defined(Q_OS_MAC) - QPlatformNativeInterface *platformNativeInterface = qApp->platformNativeInterface(); + QPlatformNativeInterface *platformNativeInterface = QGuiApplication::platformNativeInterface(); int at = platformNativeInterface->metaObject()->indexOfMethod("fadeWindow()"); if (at != -1) { QMetaMethod windowFade = platformNativeInterface->metaObject()->method(at); diff --git a/src/widgets/widgets/qdatetimeedit.cpp b/src/widgets/widgets/qdatetimeedit.cpp index 1dac496bef..45c72e24d4 100644 --- a/src/widgets/widgets/qdatetimeedit.cpp +++ b/src/widgets/widgets/qdatetimeedit.cpp @@ -645,7 +645,7 @@ QDateTimeEdit::Section QDateTimeEdit::currentSection() const if (QApplication::keypadNavigationEnabled() && d->focusOnButton) return NoSection; #endif - return d->convertToPublic(d->sectionType(d->currentSectionIndex)); + return QDateTimeEditPrivate::convertToPublic(d->sectionType(d->currentSectionIndex)); } void QDateTimeEdit::setCurrentSection(Section section) @@ -659,7 +659,7 @@ void QDateTimeEdit::setCurrentSection(Section section) int index = d->currentSectionIndex + 1; for (int i=0; i<2; ++i) { while (index < size) { - if (d->convertToPublic(d->sectionType(index)) == section) { + if (QDateTimeEditPrivate::convertToPublic(d->sectionType(index)) == section) { d->edit->setCursorPosition(d->sectionPos(index)); QDTEDEBUG << d->sectionPos(index); return; @@ -685,7 +685,7 @@ QDateTimeEdit::Section QDateTimeEdit::sectionAt(int index) const Q_D(const QDateTimeEdit); if (index < 0 || index >= d->sectionNodes.size()) return NoSection; - return d->convertToPublic(d->sectionType(index)); + return QDateTimeEditPrivate::convertToPublic(d->sectionType(index)); } /*! @@ -879,7 +879,7 @@ void QDateTimeEdit::setDisplayFormat(const QString &format) } d->formatExplicitlySet = true; - d->sections = d->convertSections(d->display); + d->sections = QDateTimeEditPrivate::convertSections(d->display); d->clearCache(); d->currentSectionIndex = qMin(d->currentSectionIndex, d->sectionNodes.size() - 1); diff --git a/src/widgets/widgets/qdockwidget.cpp b/src/widgets/widgets/qdockwidget.cpp index b8b6c12bf3..dc3b77b7bc 100644 --- a/src/widgets/widgets/qdockwidget.cpp +++ b/src/widgets/widgets/qdockwidget.cpp @@ -1590,7 +1590,7 @@ bool QDockWidget::event(QEvent *event) // This is a workaround for loosing the mouse on Vista. QPoint pos = QCursor::pos(); QMouseEvent fake(QEvent::MouseMove, mapFromGlobal(pos), pos, Qt::NoButton, - QApplication::mouseButtons(), QApplication::keyboardModifiers()); + QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers()); d->mouseMoveEvent(&fake); } break; diff --git a/src/widgets/widgets/qeffects.cpp b/src/widgets/widgets/qeffects.cpp index bcc8d7815d..9463641369 100644 --- a/src/widgets/widgets/qeffects.cpp +++ b/src/widgets/widgets/qeffects.cpp @@ -569,8 +569,8 @@ void qScrollEffect(QWidget* w, QEffects::DirFlags orient, int time) if (!w) return; - QApplication::sendPostedEvents(w, QEvent::Move); - QApplication::sendPostedEvents(w, QEvent::Resize); + QCoreApplication::sendPostedEvents(w, QEvent::Move); + QCoreApplication::sendPostedEvents(w, QEvent::Resize); Qt::WindowFlags flags = Qt::ToolTip; // those can be popups - they would steal the focus, but are disabled @@ -591,8 +591,8 @@ void qFadeEffect(QWidget* w, int time) if (!w) return; - QApplication::sendPostedEvents(w, QEvent::Move); - QApplication::sendPostedEvents(w, QEvent::Resize); + QCoreApplication::sendPostedEvents(w, QEvent::Move); + QCoreApplication::sendPostedEvents(w, QEvent::Resize); Qt::WindowFlags flags = Qt::ToolTip; diff --git a/src/widgets/widgets/qlineedit.cpp b/src/widgets/widgets/qlineedit.cpp index 09b7687d8e..68e3de05bc 100644 --- a/src/widgets/widgets/qlineedit.cpp +++ b/src/widgets/widgets/qlineedit.cpp @@ -84,7 +84,7 @@ #include "qkeysequence.h" #define ACCEL_KEY(k) ((!QCoreApplication::testAttribute(Qt::AA_DontShowIconsInMenus) \ && QGuiApplication::styleHints()->showShortcutsInContextMenus()) \ - && !qApp->d_func()->shortcutMap.hasShortcutForKeySequence(k) ? \ + && !QGuiApplicationPrivate::instance()->shortcutMap.hasShortcutForKeySequence(k) ? \ QLatin1Char('\t') + QKeySequence(k).toString(QKeySequence::NativeText) : QString()) #else #define ACCEL_KEY(k) QString() @@ -684,10 +684,10 @@ QSize QLineEdit::sizeHint() const ensurePolished(); QFontMetrics fm(font()); const int iconSize = style()->pixelMetric(QStyle::PM_SmallIconSize, 0, this); - int h = qMax(fm.height(), iconSize - 2) + 2*d->verticalMargin + int h = qMax(fm.height(), iconSize - 2) + 2 * QLineEditPrivate::verticalMargin + d->topTextMargin + d->bottomTextMargin + d->topmargin + d->bottommargin; - int w = fm.horizontalAdvance(QLatin1Char('x')) * 17 + 2*d->horizontalMargin + int w = fm.horizontalAdvance(QLatin1Char('x')) * 17 + 2 * QLineEditPrivate::horizontalMargin + d->effectiveLeftTextMargin() + d->effectiveRightTextMargin() + d->leftmargin + d->rightmargin; // "some" QStyleOptionFrame opt; @@ -708,7 +708,7 @@ QSize QLineEdit::minimumSizeHint() const Q_D(const QLineEdit); ensurePolished(); QFontMetrics fm = fontMetrics(); - int h = fm.height() + qMax(2*d->verticalMargin, fm.leading()) + int h = fm.height() + qMax(2 * QLineEditPrivate::verticalMargin, fm.leading()) + d->topTextMargin + d->bottomTextMargin + d->topmargin + d->bottommargin; int w = fm.maxWidth() @@ -1606,7 +1606,7 @@ void QLineEdit::mouseReleaseEvent(QMouseEvent* e) } #endif #ifndef QT_NO_CLIPBOARD - if (QApplication::clipboard()->supportsSelection()) { + if (QGuiApplication::clipboard()->supportsSelection()) { if (e->button() == Qt::LeftButton) { d->control->copy(QClipboard::Selection); } else if (!d->control->isReadOnly() && e->button() == Qt::MidButton) { @@ -1960,17 +1960,18 @@ void QLineEdit::paintEvent(QPaintEvent *) Qt::Alignment va = QStyle::visualAlignment(d->control->layoutDirection(), QFlag(d->alignment)); switch (va & Qt::AlignVertical_Mask) { case Qt::AlignBottom: - d->vscroll = r.y() + r.height() - fm.height() - d->verticalMargin; + d->vscroll = r.y() + r.height() - fm.height() - QLineEditPrivate::verticalMargin; break; case Qt::AlignTop: - d->vscroll = r.y() + d->verticalMargin; + d->vscroll = r.y() + QLineEditPrivate::verticalMargin; break; default: //center d->vscroll = r.y() + (r.height() - fm.height() + 1) / 2; break; } - QRect lineRect(r.x() + d->horizontalMargin, d->vscroll, r.width() - 2*d->horizontalMargin, fm.height()); + QRect lineRect(r.x() + QLineEditPrivate::horizontalMargin, d->vscroll, + r.width() - 2 * QLineEditPrivate::horizontalMargin, fm.height()); if (d->shouldShowPlaceholderText()) { if (!d->placeholderText.isEmpty()) { @@ -2206,7 +2207,7 @@ QMenu *QLineEdit::createStandardContextMenu() if (!isReadOnly()) { action = popup->addAction(QLineEdit::tr("&Paste") + ACCEL_KEY(QKeySequence::Paste)); - action->setEnabled(!d->control->isReadOnly() && !QApplication::clipboard()->text().isEmpty()); + action->setEnabled(!d->control->isReadOnly() && !QGuiApplication::clipboard()->text().isEmpty()); setActionIcon(action, QStringLiteral("edit-paste")); connect(action, SIGNAL(triggered()), SLOT(paste())); } diff --git a/src/widgets/widgets/qmdiarea.cpp b/src/widgets/widgets/qmdiarea.cpp index fe3d1663a8..c7b7e5bf97 100644 --- a/src/widgets/widgets/qmdiarea.cpp +++ b/src/widgets/widgets/qmdiarea.cpp @@ -2525,7 +2525,7 @@ bool QMdiArea::event(QEvent *event) case QEvent::WindowIconChange: foreach (QMdiSubWindow *window, d->childWindows) { if (sanityCheck(window, "QMdiArea::WindowIconChange")) - QApplication::sendEvent(window, event); + QCoreApplication::sendEvent(window, event); } break; case QEvent::Hide: diff --git a/src/widgets/widgets/qmdisubwindow.cpp b/src/widgets/widgets/qmdisubwindow.cpp index 685c5e159e..77692930fc 100644 --- a/src/widgets/widgets/qmdisubwindow.cpp +++ b/src/widgets/widgets/qmdisubwindow.cpp @@ -1826,7 +1826,7 @@ void QMdiSubWindowPrivate::showButtonsInMenuBar(QMenuBar *menuBar) // Make sure topLevelWindow->contentsRect returns correct geometry. // topLevelWidget->updateGeoemtry will not do the trick here since it will post the event. QEvent event(QEvent::LayoutRequest); - QApplication::sendEvent(topLevelWindow, &event); + QCoreApplication::sendEvent(topLevelWindow, &event); } } @@ -1936,7 +1936,7 @@ QPalette QMdiSubWindowPrivate::desktopPalette() const #ifndef COLOR_GRADIENTINACTIVECAPTION #define COLOR_GRADIENTINACTIVECAPTION 28 #endif - if (QApplication::desktopSettingsAware()) { + if (QGuiApplication::desktopSettingsAware()) { newPalette.setColor(QPalette::Active, QPalette::Highlight, colorref2qrgb(GetSysColor(COLOR_ACTIVECAPTION))); newPalette.setColor(QPalette::Inactive, QPalette::Highlight, @@ -3050,7 +3050,7 @@ void QMdiSubWindow::closeEvent(QCloseEvent *closeEvent) d->setActive(false); if (parentWidget() && testAttribute(Qt::WA_DeleteOnClose)) { QChildEvent childRemoved(QEvent::ChildRemoved, this); - QApplication::sendEvent(parentWidget(), &childRemoved); + QCoreApplication::sendEvent(parentWidget(), &childRemoved); } closeEvent->accept(); } diff --git a/src/widgets/widgets/qmenu.cpp b/src/widgets/widgets/qmenu.cpp index 7b6a1b6da8..72653b377d 100644 --- a/src/widgets/widgets/qmenu.cpp +++ b/src/widgets/widgets/qmenu.cpp @@ -264,7 +264,7 @@ void QMenuPrivate::copyActionToPlatformItem(const QAction *action, QPlatformMenu item->setIconSize(w->style()->pixelMetric(QStyle::PM_SmallIconSize, &opt, w)); } else { QStyleOption opt; - item->setIconSize(qApp->style()->pixelMetric(QStyle::PM_SmallIconSize, &opt, 0)); + item->setIconSize(QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize, &opt, 0)); } } else { item->setIcon(QIcon()); @@ -907,7 +907,7 @@ void QMenuPrivate::updateLayoutDirection() else if (QWidget *w = q->parentWidget()) setLayoutDirection_helper(w->layoutDirection()); else - setLayoutDirection_helper(QApplication::layoutDirection()); + setLayoutDirection_helper(QGuiApplication::layoutDirection()); } } @@ -1335,7 +1335,7 @@ bool QMenuPrivate::mouseEventTaken(QMouseEvent *e) if (e->type() != QEvent::MouseButtonRelease || mouseDown == caused) { QMouseEvent new_e(e->type(), cpos, caused->mapTo(caused->topLevelWidget(), cpos), e->screenPos(), e->button(), e->buttons(), e->modifiers(), e->source()); - QApplication::sendEvent(caused, &new_e); + QCoreApplication::sendEvent(caused, &new_e); return true; } } @@ -1543,7 +1543,7 @@ void QMenu::initStyleOption(QStyleOptionMenuItem *option, const QAction *action) if (d->currentAction && d->currentAction == action && !d->currentAction->isSeparator()) { option->state |= QStyle::State_Selected - | (d->mouseDown ? QStyle::State_Sunken : QStyle::State_None); + | (QMenuPrivate::mouseDown ? QStyle::State_Sunken : QStyle::State_None); } option->menuHasCheckableItems = d->hasCheckableItems; @@ -2362,7 +2362,7 @@ void QMenu::popup(const QPoint &p, QAction *atAction) QRect screen; #if QT_CONFIG(graphicsview) - bool isEmbedded = !bypassGraphicsProxyWidget(this) && d->nearestGraphicsProxyWidget(this); + bool isEmbedded = !bypassGraphicsProxyWidget(this) && QMenuPrivate::nearestGraphicsProxyWidget(this); if (isEmbedded) screen = d->popupGeometry(); else @@ -2698,8 +2698,8 @@ void QMenu::hideEvent(QHideEvent *) if (QMenuBar *mb = qobject_cast(d->causedPopup.widget)) mb->d_func()->setCurrentAction(0); #endif - if (d->mouseDown == this) - d->mouseDown = 0; + if (QMenuPrivate::mouseDown == this) + QMenuPrivate::mouseDown = nullptr; d->hasHadMouse = false; if (d->activeMenu) d->hideMenu(d->activeMenu); @@ -2874,7 +2874,7 @@ void QMenu::mousePressEvent(QMouseEvent *e) d->hideUpToMenuBar(); return; } - d->mouseDown = this; + QMenuPrivate::mouseDown = this; QAction *action = d->actionAt(e->pos()); d->setCurrentAction(action, 20); @@ -2889,12 +2889,12 @@ void QMenu::mouseReleaseEvent(QMouseEvent *e) Q_D(QMenu); if (d->aboutToHide || d->mouseEventTaken(e)) return; - if(d->mouseDown != this) { - d->mouseDown = 0; + if (QMenuPrivate::mouseDown != this) { + QMenuPrivate::mouseDown = nullptr; return; } - d->mouseDown = 0; + QMenuPrivate::mouseDown = nullptr; d->setSyncAction(); QAction *action = d->actionAt(e->pos()); @@ -2995,7 +2995,7 @@ QMenu::event(QEvent *e) d->updateActionRects(); break; } case QEvent::Show: - d->mouseDown = 0; + QMenuPrivate::mouseDown = nullptr; d->updateActionRects(); d->sloppyState.reset(); if (d->currentAction) @@ -3385,7 +3385,7 @@ void QMenu::keyPressEvent(QKeyEvent *e) #if QT_CONFIG(menubar) if (QMenuBar *mb = qobject_cast(d->topCausedWidget())) { QAction *oldAct = mb->d_func()->currentAction; - QApplication::sendEvent(mb, e); + QCoreApplication::sendEvent(mb, e); if (mb->d_func()->currentAction != oldAct) key_consumed = true; } @@ -3428,7 +3428,7 @@ void QMenu::mouseMoveEvent(QMouseEvent *e) } if (e->buttons()) - d->mouseDown = this; + QMenuPrivate::mouseDown = this; if (d->activeMenu) d->activeMenu->d_func()->setCurrentAction(0); @@ -3593,7 +3593,7 @@ void QMenu::internalDelayedPopup() QRect screen; #if QT_CONFIG(graphicsview) - bool isEmbedded = !bypassGraphicsProxyWidget(this) && d->nearestGraphicsProxyWidget(this); + bool isEmbedded = !bypassGraphicsProxyWidget(this) && QMenuPrivate::nearestGraphicsProxyWidget(this); if (isEmbedded) screen = d->popupGeometry(); else diff --git a/src/widgets/widgets/qmenubar.cpp b/src/widgets/widgets/qmenubar.cpp index 9a60f1477d..3d31a3b73a 100644 --- a/src/widgets/widgets/qmenubar.cpp +++ b/src/widgets/widgets/qmenubar.cpp @@ -404,7 +404,7 @@ void QMenuBarPrivate::setCurrentAction(QAction *action, bool popup, bool activat } else if (previousAction) { QString empty; QStatusTipEvent tip(empty); - QApplication::sendEvent(q, &tip); + QCoreApplication::sendEvent(q, &tip); #endif } if (fw) @@ -701,7 +701,7 @@ void QMenuBarPrivate::init() q->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); q->setAttribute(Qt::WA_CustomWhatsThis); - if (!QApplication::instance()->testAttribute(Qt::AA_DontUseNativeMenuBar)) + if (!QCoreApplication::testAttribute(Qt::AA_DontUseNativeMenuBar)) platformMenuBar = QGuiApplicationPrivate::platformTheme()->createPlatformMenuBar(); if (platformMenuBar) diff --git a/src/widgets/widgets/qplaintextedit.cpp b/src/widgets/widgets/qplaintextedit.cpp index 4a875975a4..397304ec44 100644 --- a/src/widgets/widgets/qplaintextedit.cpp +++ b/src/widgets/widgets/qplaintextedit.cpp @@ -1588,7 +1588,7 @@ bool QPlainTextEdit::event(QEvent *e) d->originalOffsetY = vBar->value(); QPointF offset = g->offset(); if (!offset.isNull()) { - if (QApplication::isRightToLeft()) + if (QGuiApplication::isRightToLeft()) offset.rx() *= -1; // QPlainTextEdit scrolls by lines only in vertical direction QFontMetrics fm(document()->defaultFont()); @@ -2649,7 +2649,7 @@ void QPlainTextEdit::setReadOnly(bool ro) d->control->setTextInteractionFlags(flags); setAttribute(Qt::WA_InputMethodEnabled, shouldEnableInputMethod(this)); QEvent event(QEvent::ReadOnlyChange); - QApplication::sendEvent(this, &event); + QCoreApplication::sendEvent(this, &event); } /*! diff --git a/src/widgets/widgets/qspinbox.cpp b/src/widgets/widgets/qspinbox.cpp index 97a3a12336..61ea81c892 100644 --- a/src/widgets/widgets/qspinbox.cpp +++ b/src/widgets/widgets/qspinbox.cpp @@ -384,7 +384,7 @@ void QSpinBox::setMinimum(int minimum) { Q_D(QSpinBox); const QVariant m(minimum); - d->setRange(m, (d->variantCompare(d->maximum, m) > 0 ? d->maximum : m)); + d->setRange(m, (QSpinBoxPrivate::variantCompare(d->maximum, m) > 0 ? d->maximum : m)); } /*! @@ -412,7 +412,7 @@ void QSpinBox::setMaximum(int maximum) { Q_D(QSpinBox); const QVariant m(maximum); - d->setRange((d->variantCompare(d->minimum, m) < 0 ? d->minimum : m), m); + d->setRange((QSpinBoxPrivate::variantCompare(d->minimum, m) < 0 ? d->minimum : m), m); } /*! @@ -864,7 +864,7 @@ void QDoubleSpinBox::setMinimum(double minimum) Q_D(QDoubleSpinBox); d->actualMin = minimum; const QVariant m(d->round(minimum)); - d->setRange(m, (d->variantCompare(d->maximum, m) > 0 ? d->maximum : m)); + d->setRange(m, (QDoubleSpinBoxPrivate::variantCompare(d->maximum, m) > 0 ? d->maximum : m)); } /*! @@ -895,7 +895,7 @@ void QDoubleSpinBox::setMaximum(double maximum) Q_D(QDoubleSpinBox); d->actualMax = maximum; const QVariant m(d->round(maximum)); - d->setRange((d->variantCompare(d->minimum, m) < 0 ? d->minimum : m), m); + d->setRange((QDoubleSpinBoxPrivate::variantCompare(d->minimum, m) < 0 ? d->minimum : m), m); } /*! diff --git a/src/widgets/widgets/qsplashscreen.cpp b/src/widgets/widgets/qsplashscreen.cpp index bf6bf1c7c9..e39ef6d1cd 100644 --- a/src/widgets/widgets/qsplashscreen.cpp +++ b/src/widgets/widgets/qsplashscreen.cpp @@ -111,7 +111,7 @@ public: The user can hide the splash screen by clicking on it with the mouse. Since the splash screen is typically displayed before the event loop has started running, it is necessary to periodically - call QApplication::processEvents() to receive the mouse clicks. + call QCoreApplication::processEvents() to receive the mouse clicks. It is sometimes useful to update the splash screen with messages, for example, announcing connections established or modules loaded @@ -170,13 +170,13 @@ void QSplashScreen::mousePressEvent(QMouseEvent *) /*! This overrides QWidget::repaint(). It differs from the standard repaint - function in that it also calls QApplication::processEvents() to ensure + function in that it also calls QCoreApplication::processEvents() to ensure the updates are displayed, even when there is no event loop present. */ void QSplashScreen::repaint() { QWidget::repaint(); - QApplication::processEvents(); + QCoreApplication::processEvents(); } /*! diff --git a/src/widgets/widgets/qtabbar.cpp b/src/widgets/widgets/qtabbar.cpp index b13f4da9d2..f6f56c12d1 100644 --- a/src/widgets/widgets/qtabbar.cpp +++ b/src/widgets/widgets/qtabbar.cpp @@ -843,7 +843,7 @@ void QTabBarPrivate::refresh() // be safe in case a subclass is also handling move with the tabs if (pressedIndex != -1 && movable - && QApplication::mouseButtons() == Qt::NoButton) { + && QGuiApplication::mouseButtons() == Qt::NoButton) { moveTabFinished(pressedIndex); if (!validIndex(pressedIndex)) pressedIndex = -1; diff --git a/src/widgets/widgets/qtextedit.cpp b/src/widgets/widgets/qtextedit.cpp index 01f7c34f93..8c1d7e7a03 100644 --- a/src/widgets/widgets/qtextedit.cpp +++ b/src/widgets/widgets/qtextedit.cpp @@ -2260,7 +2260,7 @@ void QTextEdit::setReadOnly(bool ro) d->control->setTextInteractionFlags(flags); setAttribute(Qt::WA_InputMethodEnabled, shouldEnableInputMethod(this)); QEvent event(QEvent::ReadOnlyChange); - QApplication::sendEvent(this, &event); + QCoreApplication::sendEvent(this, &event); } /*! diff --git a/src/widgets/widgets/qtoolbar.cpp b/src/widgets/widgets/qtoolbar.cpp index bcf5a40ae3..fcaafbc581 100644 --- a/src/widgets/widgets/qtoolbar.cpp +++ b/src/widgets/widgets/qtoolbar.cpp @@ -1172,7 +1172,7 @@ bool QToolBar::event(QEvent *event) // This is a workaround for loosing the mouse on Vista. QPoint pos = QCursor::pos(); QMouseEvent fake(QEvent::MouseMove, mapFromGlobal(pos), pos, Qt::NoButton, - QApplication::mouseButtons(), QApplication::keyboardModifiers()); + QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers()); d->mouseMoveEvent(&fake); #endif } else { diff --git a/src/widgets/widgets/qwidgetlinecontrol.cpp b/src/widgets/widgets/qwidgetlinecontrol.cpp index cf2d885b52..56dba1dd32 100644 --- a/src/widgets/widgets/qwidgetlinecontrol.cpp +++ b/src/widgets/widgets/qwidgetlinecontrol.cpp @@ -158,7 +158,7 @@ void QWidgetLineControl::copy(QClipboard::Mode mode) const { QString t = selectedText(); if (!t.isEmpty() && m_echoMode == QLineEdit::Normal) { - QApplication::clipboard()->setText(t, mode); + QGuiApplication::clipboard()->setText(t, mode); } } @@ -172,7 +172,7 @@ void QWidgetLineControl::copy(QClipboard::Mode mode) const */ void QWidgetLineControl::paste(QClipboard::Mode clipboardMode) { - QString clip = QApplication::clipboard()->text(clipboardMode); + QString clip = QGuiApplication::clipboard()->text(clipboardMode); if (!clip.isEmpty() || hasSelectedText()) { separate(); //make it a separate undo/redo command insert(clip); @@ -1524,9 +1524,9 @@ void QWidgetLineControl::setBlinkingCursorEnabled(bool enable) m_blinkEnabled = enable; if (enable) - connect(qApp->styleHints(), &QStyleHints::cursorFlashTimeChanged, this, &QWidgetLineControl::updateCursorBlinking); + connect(QGuiApplication::styleHints(), &QStyleHints::cursorFlashTimeChanged, this, &QWidgetLineControl::updateCursorBlinking); else - disconnect(qApp->styleHints(), &QStyleHints::cursorFlashTimeChanged, this, &QWidgetLineControl::updateCursorBlinking); + disconnect(QGuiApplication::styleHints(), &QStyleHints::cursorFlashTimeChanged, this, &QWidgetLineControl::updateCursorBlinking); updateCursorBlinking(); } @@ -1680,7 +1680,7 @@ void QWidgetLineControl::processKeyEvent(QKeyEvent* event) if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) { if (hasAcceptableInput() || fixup()) { - QInputMethod *inputMethod = QApplication::inputMethod(); + QInputMethod *inputMethod = QGuiApplication::inputMethod(); inputMethod->commit(); QWidget *lineEdit = qobject_cast(parent()); if (!(lineEdit && lineEdit->inputMethodHints() & Qt::ImhMultiLine)) diff --git a/src/widgets/widgets/qwidgettextcontrol.cpp b/src/widgets/widgets/qwidgettextcontrol.cpp index cad3a64749..ccf02da219 100644 --- a/src/widgets/widgets/qwidgettextcontrol.cpp +++ b/src/widgets/widgets/qwidgettextcontrol.cpp @@ -97,7 +97,7 @@ #include #define ACCEL_KEY(k) ((!QCoreApplication::testAttribute(Qt::AA_DontShowShortcutsInContextMenus) \ && QGuiApplication::styleHints()->showShortcutsInContextMenus()) \ - && !qApp->d_func()->shortcutMap.hasShortcutForKeySequence(k) ? \ + && !QGuiApplicationPrivate::instance()->shortcutMap.hasShortcutForKeySequence(k) ? \ QLatin1Char('\t') + QKeySequence(k).toString(QKeySequence::NativeText) : QString()) #else @@ -648,7 +648,7 @@ void QWidgetTextControlPrivate::_q_updateCurrentCharFormatAndSelection() #ifndef QT_NO_CLIPBOARD void QWidgetTextControlPrivate::setClipboardSelection() { - QClipboard *clipboard = QApplication::clipboard(); + QClipboard *clipboard = QGuiApplication::clipboard(); if (!cursor.hasSelection() || !clipboard->supportsSelection()) return; Q_Q(QWidgetTextControl); @@ -719,9 +719,9 @@ void QWidgetTextControlPrivate::setCursorVisible(bool visible) updateCursorBlinking(); if (cursorVisible) - connect(qApp->styleHints(), &QStyleHints::cursorFlashTimeChanged, this, &QWidgetTextControlPrivate::updateCursorBlinking); + connect(QGuiApplication::styleHints(), &QStyleHints::cursorFlashTimeChanged, this, &QWidgetTextControlPrivate::updateCursorBlinking); else - disconnect(qApp->styleHints(), &QStyleHints::cursorFlashTimeChanged, this, &QWidgetTextControlPrivate::updateCursorBlinking); + disconnect(QGuiApplication::styleHints(), &QStyleHints::cursorFlashTimeChanged, this, &QWidgetTextControlPrivate::updateCursorBlinking); } void QWidgetTextControlPrivate::updateCursorBlinking() @@ -959,12 +959,12 @@ void QWidgetTextControl::copy() if (!d->cursor.hasSelection()) return; QMimeData *data = createMimeDataFromSelection(); - QApplication::clipboard()->setMimeData(data); + QGuiApplication::clipboard()->setMimeData(data); } void QWidgetTextControl::paste(QClipboard::Mode mode) { - const QMimeData *md = QApplication::clipboard()->mimeData(mode); + const QMimeData *md = QGuiApplication::clipboard()->mimeData(mode); if (md) insertFromMimeData(md); } @@ -1787,9 +1787,9 @@ void QWidgetTextControlPrivate::mouseReleaseEvent(QEvent *e, Qt::MouseButton but selectionChanged(true); } else if (button == Qt::MidButton && (interactionFlags & Qt::TextEditable) - && QApplication::clipboard()->supportsSelection()) { + && QGuiApplication::clipboard()->supportsSelection()) { setCursorPosition(pos); - const QMimeData *md = QApplication::clipboard()->mimeData(QClipboard::Selection); + const QMimeData *md = QGuiApplication::clipboard()->mimeData(QClipboard::Selection); if (md) q->insertFromMimeData(md); #endif @@ -2598,7 +2598,7 @@ bool QWidgetTextControl::canPaste() const #ifndef QT_NO_CLIPBOARD Q_D(const QWidgetTextControl); if (d->interactionFlags & Qt::TextEditable) { - const QMimeData *md = QApplication::clipboard()->mimeData(); + const QMimeData *md = QGuiApplication::clipboard()->mimeData(); return md && canInsertFromMimeData(md); } #endif @@ -3331,7 +3331,7 @@ void QWidgetTextControlPrivate::_q_copyLink() #ifndef QT_NO_CLIPBOARD QMimeData *md = new QMimeData; md->setText(linkToCopy); - QApplication::clipboard()->setMimeData(md); + QGuiApplication::clipboard()->setMimeData(md); #endif } -- cgit v1.2.3 From 168b18de2d1429dc4f8c8db538c0a2cf3141c694 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Thu, 20 Jun 2019 12:52:15 +0200 Subject: QtWidgets: Preparatory change for moving out QAction (or parts of it) - Fix some spelling - Use QT_CONFIG for shortcuts - Quick C++ brush up, use member initialization Preemptively fix sanity bot warnings about missing space after flow control keyword, introducing range-based for on this occasion - Remove unused member variable Task-number: QTBUG-69478 Change-Id: I6af21886d5a0b48f4b2d11082991a877bd8d817d Reviewed-by: Oliver Wolff --- src/widgets/kernel/qaction.cpp | 36 +++++++++++++++--------------------- src/widgets/kernel/qaction.h | 4 ++-- src/widgets/kernel/qaction_p.h | 18 ++++++++---------- src/widgets/kernel/qactiongroup.cpp | 23 +++++++++++------------ 4 files changed, 36 insertions(+), 45 deletions(-) (limited to 'src') diff --git a/src/widgets/kernel/qaction.cpp b/src/widgets/kernel/qaction.cpp index 47e91bf8f9..19ad65692b 100644 --- a/src/widgets/kernel/qaction.cpp +++ b/src/widgets/kernel/qaction.cpp @@ -75,24 +75,18 @@ static QString qt_strippedText(QString s) } -QActionPrivate::QActionPrivate() : group(0), enabled(1), forceDisabled(0), - visible(1), forceInvisible(0), checkable(0), checked(0), separator(0), fontSet(false), - iconVisibleInMenu(-1), - shortcutVisibleInContextMenu(-1), - menuRole(QAction::TextHeuristicRole), - priority(QAction::NormalPriority) -{ -#ifndef QT_NO_SHORTCUT - shortcutId = 0; - shortcutContext = Qt::WindowShortcut; - autorepeat = true; +QActionPrivate::QActionPrivate() : +#if QT_CONFIG(shortcut) + autorepeat(1), #endif -} - -QActionPrivate::~QActionPrivate() + enabled(1), forceDisabled(0), visible(1), forceInvisible(0), checkable(0), + checked(0), separator(0), fontSet(false), + iconVisibleInMenu(-1), shortcutVisibleInContextMenu(-1) { } +QActionPrivate::~QActionPrivate() = default; + bool QActionPrivate::showStatusText(QWidget *widget, const QString &str) { #if !QT_CONFIG(statustip) @@ -127,7 +121,7 @@ void QActionPrivate::sendDataChanged() emit q->changed(); } -#ifndef QT_NO_SHORTCUT +#if QT_CONFIG(shortcut) void QActionPrivate::redoGrab(QShortcutMap &map) { Q_Q(QAction); @@ -345,7 +339,7 @@ QWidget *QAction::parentWidget() const QObject *ret = parent(); while (ret && !ret->isWidgetType()) ret = ret->parent(); - return (QWidget*)ret; + return static_cast(ret); } /*! @@ -374,7 +368,7 @@ QList QAction::associatedGraphicsWidgets() const } #endif -#ifndef QT_NO_SHORTCUT +#if QT_CONFIG(shortcut) /*! \property QAction::shortcut \brief the action's primary shortcut key @@ -573,7 +567,7 @@ QAction::~QAction() #endif if (d->group) d->group->removeAction(this); -#ifndef QT_NO_SHORTCUT +#if QT_CONFIG(shortcut) if (d->shortcutId && qApp) { QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(d->shortcutId, this); for(int i = 0; i < d->alternateShortcutIds.count(); ++i) { @@ -1027,7 +1021,7 @@ void QAction::setEnabled(bool b) return; QAPP_CHECK("setEnabled"); d->enabled = b; -#ifndef QT_NO_SHORTCUT +#if QT_CONFIG(shortcut) d->setShortcutEnabled(b, QGuiApplicationPrivate::instance()->shortcutMap); #endif d->sendDataChanged(); @@ -1080,7 +1074,7 @@ bool QAction::isVisible() const bool QAction::event(QEvent *e) { -#ifndef QT_NO_SHORTCUT +#if QT_CONFIG(shortcut) if (e->type() == QEvent::Shortcut) { QShortcutEvent *se = static_cast(e); Q_ASSERT_X(se->key() == d_func()->shortcut || d_func()->alternateShortcuts.contains(se->key()), @@ -1351,7 +1345,7 @@ Q_WIDGETS_EXPORT QDebug operator<<(QDebug d, const QAction *action) d << " toolTip=" << action->toolTip(); if (action->isCheckable()) d << " checked=" << action->isChecked(); -#ifndef QT_NO_SHORTCUT +#if QT_CONFIG(shortcut) if (!action->shortcut().isEmpty()) d << " shortcut=" << action->shortcut(); #endif diff --git a/src/widgets/kernel/qaction.h b/src/widgets/kernel/qaction.h index 84bf92d2ac..f7693f4dde 100644 --- a/src/widgets/kernel/qaction.h +++ b/src/widgets/kernel/qaction.h @@ -72,7 +72,7 @@ class Q_WIDGETS_EXPORT QAction : public QObject Q_PROPERTY(QString statusTip READ statusTip WRITE setStatusTip NOTIFY changed) Q_PROPERTY(QString whatsThis READ whatsThis WRITE setWhatsThis NOTIFY changed) Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY changed) -#ifndef QT_NO_SHORTCUT +#if QT_CONFIG(shortcut) Q_PROPERTY(QKeySequence shortcut READ shortcut WRITE setShortcut NOTIFY changed) Q_PROPERTY(Qt::ShortcutContext shortcutContext READ shortcutContext WRITE setShortcutContext NOTIFY changed) Q_PROPERTY(bool autoRepeat READ autoRepeat WRITE setAutoRepeat NOTIFY changed) @@ -129,7 +129,7 @@ public: void setSeparator(bool b); bool isSeparator() const; -#ifndef QT_NO_SHORTCUT +#if QT_CONFIG(shortcut) void setShortcut(const QKeySequence &shortcut); QKeySequence shortcut() const; diff --git a/src/widgets/kernel/qaction_p.h b/src/widgets/kernel/qaction_p.h index 19ae47c7b9..6b6ca8076f 100644 --- a/src/widgets/kernel/qaction_p.h +++ b/src/widgets/kernel/qaction_p.h @@ -89,15 +89,15 @@ public: QString tooltip; QString statustip; QString whatsthis; -#ifndef QT_NO_SHORTCUT +#if QT_CONFIG(shortcut) QKeySequence shortcut; QList alternateShortcuts; #endif QVariant userData; -#ifndef QT_NO_SHORTCUT - int shortcutId; +#if QT_CONFIG(shortcut) + int shortcutId = 0; QVector alternateShortcutIds; - Qt::ShortcutContext shortcutContext; + Qt::ShortcutContext shortcutContext = Qt::WindowShortcut; uint autorepeat : 1; #endif QFont font; @@ -112,19 +112,17 @@ public: int iconVisibleInMenu : 2; // Only has values -1, 0, and 1 int shortcutVisibleInContextMenu : 2; // Only has values -1, 0, and 1 - QAction::MenuRole menuRole; - QAction::Priority priority; + QAction::MenuRole menuRole = QAction::TextHeuristicRole; + QAction::Priority priority = QAction::NormalPriority; - QList widgets; + QWidgetList widgets; #if QT_CONFIG(graphicsview) QList graphicsWidgets; #endif -#ifndef QT_NO_SHORTCUT +#if QT_CONFIG(shortcut) void redoGrab(QShortcutMap &map); void redoGrabAlternate(QShortcutMap &map); void setShortcutEnabled(bool enable, QShortcutMap &map); - - static QShortcutMap *globalMap; #endif // QT_NO_SHORTCUT void sendDataChanged(); diff --git a/src/widgets/kernel/qactiongroup.cpp b/src/widgets/kernel/qactiongroup.cpp index ab42b1c7aa..1d9213de0c 100644 --- a/src/widgets/kernel/qactiongroup.cpp +++ b/src/widgets/kernel/qactiongroup.cpp @@ -42,7 +42,6 @@ #ifndef QT_NO_ACTION #include "qaction_p.h" -#include "qapplication.h" #include "qevent.h" #include "qlist.h" @@ -73,7 +72,7 @@ void QActionGroupPrivate::_q_actionChanged() { Q_Q(QActionGroup); QAction *action = qobject_cast(q->sender()); - Q_ASSERT_X(action != 0, "QWidgetGroup::_q_actionChanged", "internal error"); + Q_ASSERT_X(action != nullptr, "QActionGroup::_q_actionChanged", "internal error"); if (exclusionPolicy != QActionGroup::ExclusionPolicy::None) { if (action->isChecked()) { if (action != current) { @@ -91,7 +90,7 @@ void QActionGroupPrivate::_q_actionTriggered() { Q_Q(QActionGroup); QAction *action = qobject_cast(q->sender()); - Q_ASSERT_X(action != 0, "QWidgetGroup::_q_actionTriggered", "internal error"); + Q_ASSERT_X(action != nullptr, "QActionGroup::_q_actionTriggered", "internal error"); emit q->triggered(action); } @@ -99,7 +98,7 @@ void QActionGroupPrivate::_q_actionHovered() { Q_Q(QActionGroup); QAction *action = qobject_cast(q->sender()); - Q_ASSERT_X(action != 0, "QWidgetGroup::_q_actionHovered", "internal error"); + Q_ASSERT_X(action != nullptr, "QActionGroup::_q_actionHovered", "internal error"); emit q->hovered(action); } @@ -363,10 +362,10 @@ void QActionGroup::setEnabled(bool b) { Q_D(QActionGroup); d->enabled = b; - for(QList::const_iterator it = d->actions.constBegin(); it != d->actions.constEnd(); ++it) { - if(!(*it)->d_func()->forceDisabled) { - (*it)->setEnabled(b); - (*it)->d_func()->forceDisabled = false; + for (auto action : qAsConst(d->actions)) { + if (!action->d_func()->forceDisabled) { + action->setEnabled(b); + action->d_func()->forceDisabled = false; } } } @@ -400,10 +399,10 @@ void QActionGroup::setVisible(bool b) { Q_D(QActionGroup); d->visible = b; - for(QList::Iterator it = d->actions.begin(); it != d->actions.end(); ++it) { - if(!(*it)->d_func()->forceInvisible) { - (*it)->setVisible(b); - (*it)->d_func()->forceInvisible = false; + for (auto action : qAsConst(d->actions)) { + if (!action->d_func()->forceInvisible) { + action->setVisible(b); + action->d_func()->forceInvisible = false; } } } -- cgit v1.2.3 From 3ccbf19761ec203a88480d25f147cac047a58ee3 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Mon, 24 Jun 2019 13:38:54 +0200 Subject: QSocks5SocketEnginePrivate::sendRequestMethod(): Fix out of bounds string access Fix warnings showing in network tests: tst_QNetworkReply::headFromHttp(rfc+socks): Using QCharRef with an index pointing outside the valid range of a QString. The corresponding behavior is deprecated, and will be changed in a future version of Qt. introduced by qtbase/c2d2757bccc68e1b981df059786c2e76f2969530 (5.14). Replace index access by QByteArray::append(). Change-Id: I0b4aed563d076237d5f9cc6aa438c7502eb3568c Reviewed-by: Giuseppe D'Angelo Reviewed-by: Thiago Macieira Reviewed-by: Timur Pocheptsov --- src/network/socket/qsocks5socketengine.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp index 23aec12390..47b500227f 100644 --- a/src/network/socket/qsocks5socketengine.cpp +++ b/src/network/socket/qsocks5socketengine.cpp @@ -801,9 +801,9 @@ void QSocks5SocketEnginePrivate::sendRequestMethod() QByteArray buf; buf.reserve(270); // big enough for domain name; - buf[0] = S5_VERSION_5; - buf[1] = command; - buf[2] = 0x00; + buf.append(char(S5_VERSION_5)); + buf.append(command); + buf.append('\0'); if (peerName.isEmpty() && !qt_socks5_set_host_address_and_port(address, port, &buf)) { QSOCKS5_DEBUG << "error setting address" << address << " : " << port; //### set error code .... -- cgit v1.2.3 From e65ea360187bbbf50f844a965faddc392322a0e7 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Wed, 26 Jun 2019 10:36:46 +0200 Subject: RHI: Fix deprecation warnings about QAtomicInteger::load() Replace by loadRelaxed(), fixing: rhi\qrhi.cpp: In static member function 'static QRhiShaderResourceBinding QRhiShaderResourceBinding::uniformBuffer(int, QRhiShaderResourceBinding::StageFlags, QRhiBuffer*)': rhi\qrhi.cpp:2578:26: warning: 'T QBasicAtomicInteger::load() const [with T = int]' is deprecated: Use loadRelaxed [-Wdeprecated-declarations] Q_ASSERT(d->ref.load() == 1); Change-Id: Iebe9a62d20498e67bde34b2f0cab8cc38682154f Reviewed-by: Laszlo Agocs Reviewed-by: Giuseppe D'Angelo --- src/gui/rhi/qrhi.cpp | 8 ++++---- src/gui/rhi/qshader.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp index 927de859dd..334fa1064a 100644 --- a/src/gui/rhi/qrhi.cpp +++ b/src/gui/rhi/qrhi.cpp @@ -2575,7 +2575,7 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::uniformBuffer( { QRhiShaderResourceBinding b; QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b); - Q_ASSERT(d->ref.load() == 1); + Q_ASSERT(d->ref.loadRelaxed() == 1); d->binding = binding; d->stage = stage; d->type = UniformBuffer; @@ -2639,7 +2639,7 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::sampledTexture( { QRhiShaderResourceBinding b; QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b); - Q_ASSERT(d->ref.load() == 1); + Q_ASSERT(d->ref.loadRelaxed() == 1); d->binding = binding; d->stage = stage; d->type = SampledTexture; @@ -2661,7 +2661,7 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::imageLoad( { QRhiShaderResourceBinding b; QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b); - Q_ASSERT(d->ref.load() == 1); + Q_ASSERT(d->ref.loadRelaxed() == 1); d->binding = binding; d->stage = stage; d->type = ImageLoad; @@ -2715,7 +2715,7 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferLoad( { QRhiShaderResourceBinding b; QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b); - Q_ASSERT(d->ref.load() == 1); + Q_ASSERT(d->ref.loadRelaxed() == 1); d->binding = binding; d->stage = stage; d->type = BufferLoad; diff --git a/src/gui/rhi/qshader.cpp b/src/gui/rhi/qshader.cpp index 4676ec3f5b..9098180f69 100644 --- a/src/gui/rhi/qshader.cpp +++ b/src/gui/rhi/qshader.cpp @@ -389,7 +389,7 @@ QShader QShader::fromSerialized(const QByteArray &data) QShader bs; QShaderPrivate *d = QShaderPrivate::get(&bs); - Q_ASSERT(d->ref.load() == 1); // must be detached + Q_ASSERT(d->ref.loadRelaxed() == 1); // must be detached int intVal; ds >> intVal; if (intVal != QSB_VERSION) -- cgit v1.2.3 From 5fb5ec93d69e6bfa918b643f2291503e57d13ad0 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Tue, 25 Jun 2019 11:20:55 +0200 Subject: QImageWriter/XPM: Fix out of bounds string access Fix warnings: QWWARN : tst_QImageReader::readFromFileAfterJunk(xpm) Using QCharRef with an index pointing outside the valid range of a QString. The corresponding behavior is deprecated, and will be changed in a future version of Qt. QWARN : tst_QImageReader::readFromFileAfterJunk(xpm) Using QCharRef with an index pointing outside the valid range of a QString. The corresponding behavior is deprecated, and will be changed in a future version of Qt. QWARN : tst_QImageReader::readFromFileAfterJunk(xpm) Using QCharRef with an index pointing outside the valid range of a QString. The corresponding behavior is deprecated, and will be changed in a future version of Qt. introduced by qtbase/c2d2757bccc68e1b981df059786c2e76f2969530 (5.14). Refactor write_xpm_image() to use a QByteArray and append(). Change-Id: I25e6270e2e5fcb868d4ee38e3b294afc7ee27dcc Reviewed-by: Eirik Aavitsland --- src/gui/image/qxpmhandler.cpp | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/gui/image/qxpmhandler.cpp b/src/gui/image/qxpmhandler.cpp index deff56aa58..cf105b250a 100644 --- a/src/gui/image/qxpmhandler.cpp +++ b/src/gui/image/qxpmhandler.cpp @@ -1125,8 +1125,6 @@ static bool write_xpm_image(const QImage &sourceImage, QIODevice *device, const break; } - QString line; - // write header QTextStream s(device); s << "/* XPM */" << Qt::endl @@ -1137,35 +1135,29 @@ static bool write_xpm_image(const QImage &sourceImage, QIODevice *device, const QMap::Iterator c = colorMap.begin(); while (c != colorMap.end()) { QRgb color = c.key(); - if (image.format() != QImage::Format_RGB32 && !qAlpha(color)) - line = QString::asprintf("\"%s c None\"", - xpm_color_name(cpp, *c)); - else - line = QString::asprintf("\"%s c #%02x%02x%02x\"", - xpm_color_name(cpp, *c), - qRed(color), - qGreen(color), - qBlue(color)); + const QString line = image.format() != QImage::Format_RGB32 && !qAlpha(color) + ? QString::asprintf("\"%s c None\"", xpm_color_name(cpp, *c)) + : QString::asprintf("\"%s c #%02x%02x%02x\"", xpm_color_name(cpp, *c), + qRed(color), qGreen(color), qBlue(color)); ++c; s << ',' << Qt::endl << line; } // write pixels, limit to 4 characters per pixel - line.truncate(cpp*w); + QByteArray line; for(y=0; y(image.constScanLine(y)); - int cc = 0; for(x=0; x 1) { - line[cc++] = QLatin1Char(chars[1]); + line.append(chars[1]); if (cpp > 2) { - line[cc++] = QLatin1Char(chars[2]); - if (cpp > 3) { - line[cc++] = QLatin1Char(chars[3]); - } + line.append(chars[2]); + if (cpp > 3) + line.append(chars[3]); } } } -- cgit v1.2.3 From 6301d5e51b0c56d09b0bd1cb63f4908c3f8e2c70 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Thu, 27 Jun 2019 14:58:41 +0200 Subject: uic: Add option to disable the call to QObject::connectSlotsByName() The code was actually there, but not connected to the command line parser. Task-number: QTBUG-76375 Change-Id: I801cf2bbd2f207a6ce1dabd1ee1dfbd892089bbc Reviewed-by: Jarek Kobus --- src/tools/uic/main.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/tools/uic/main.cpp b/src/tools/uic/main.cpp index 439789d221..9cf22d502d 100644 --- a/src/tools/uic/main.cpp +++ b/src/tools/uic/main.cpp @@ -68,6 +68,10 @@ int runUic(int argc, char *argv[]) outputOption.setValueName(QStringLiteral("file")); parser.addOption(outputOption); + QCommandLineOption noAutoConnectionOption(QStringList() << QStringLiteral("a") << QStringLiteral("no-autoconnection")); + noAutoConnectionOption.setDescription(QStringLiteral("Do not generate a call to QObject::connectSlotsByName().")); + parser.addOption(noAutoConnectionOption); + QCommandLineOption noProtOption(QStringList() << QStringLiteral("p") << QStringLiteral("no-protection")); noProtOption.setDescription(QStringLiteral("Disable header protection.")); parser.addOption(noProtOption); @@ -110,6 +114,7 @@ int runUic(int argc, char *argv[]) driver.option().dependencies = parser.isSet(dependenciesOption); driver.option().outputFile = parser.value(outputOption); + driver.option().autoConnection = !parser.isSet(noAutoConnectionOption); driver.option().headerProtection = !parser.isSet(noProtOption); driver.option().implicitIncludes = !parser.isSet(noImplicitIncludesOption); driver.option().idBased = parser.isSet(idBasedOption); -- cgit v1.2.3 From 94bee657c1d6d1207c82cdb5eddef8945f262618 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Thu, 27 Jun 2019 15:17:29 +0200 Subject: uic: Fix remaining clang warnings Fix remaining nullptr and member initialization issues which Qt Creator did not catch. Change-Id: If5492259aea9849c790f00809a27f4c78b446b9b Reviewed-by: Jarek Kobus --- src/tools/uic/cpp/cppwriteincludes.cpp | 2 +- src/tools/uic/cpp/cppwriteincludes.h | 2 +- src/tools/uic/cpp/cppwriteinitialization.cpp | 13 ++++--------- src/tools/uic/cpp/cppwriteinitialization.h | 10 +++++----- src/tools/uic/customwidgetsinfo.cpp | 4 ++-- 5 files changed, 13 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/tools/uic/cpp/cppwriteincludes.cpp b/src/tools/uic/cpp/cppwriteincludes.cpp index 0ba49627c0..71e02ae9c9 100644 --- a/src/tools/uic/cpp/cppwriteincludes.cpp +++ b/src/tools/uic/cpp/cppwriteincludes.cpp @@ -69,7 +69,7 @@ static inline QString moduleHeader(const QString &module, const QString &header) namespace CPP { WriteIncludes::WriteIncludes(Uic *uic) - : m_uic(uic), m_output(uic->output()), m_laidOut(false) + : m_uic(uic), m_output(uic->output()) { // When possible (no namespace) use the "QtModule/QClass" convention // and create a re-mapping of the old header "qclass.h" to it. Do not do this diff --git a/src/tools/uic/cpp/cppwriteincludes.h b/src/tools/uic/cpp/cppwriteincludes.h index aadc6f54fc..9b9ac283fe 100644 --- a/src/tools/uic/cpp/cppwriteincludes.h +++ b/src/tools/uic/cpp/cppwriteincludes.h @@ -101,7 +101,7 @@ private: StringMap m_classToHeader; StringMap m_oldHeaderToNewHeader; - bool m_laidOut; + bool m_laidOut = false; }; } // namespace CPP diff --git a/src/tools/uic/cpp/cppwriteinitialization.cpp b/src/tools/uic/cpp/cppwriteinitialization.cpp index 85b9a9f60b..4185d3ba70 100644 --- a/src/tools/uic/cpp/cppwriteinitialization.cpp +++ b/src/tools/uic/cpp/cppwriteinitialization.cpp @@ -455,22 +455,17 @@ WriteInitialization::WriteInitialization(Uic *uic) : m_driver(uic->driver()), m_output(uic->output()), m_option(uic->option()), m_indent(m_option.indent + m_option.indent), m_dindent(m_indent + m_option.indent), - m_stdsetdef(true), - m_layoutMarginType(TopLevelMargin), - m_mainFormUsedInRetranslateUi(false), m_delayedOut(&m_delayedInitialization, QIODevice::WriteOnly), m_refreshOut(&m_refreshInitialization, QIODevice::WriteOnly), - m_actionOut(&m_delayedActionInitialization, QIODevice::WriteOnly), - m_layoutWidget(false), - m_firstThemeIcon(true) + m_actionOut(&m_delayedActionInitialization, QIODevice::WriteOnly) { } void WriteInitialization::acceptUI(DomUI *node) { - m_actionGroupChain.push(0); - m_widgetChain.push(0); - m_layoutChain.push(0); + m_actionGroupChain.push(nullptr); + m_widgetChain.push(nullptr); + m_layoutChain.push(nullptr); acceptLayoutDefault(node->elementLayoutDefault()); acceptLayoutFunction(node->elementLayoutFunction()); diff --git a/src/tools/uic/cpp/cppwriteinitialization.h b/src/tools/uic/cpp/cppwriteinitialization.h index 3cd0efeaac..a28dfc1b25 100644 --- a/src/tools/uic/cpp/cppwriteinitialization.h +++ b/src/tools/uic/cpp/cppwriteinitialization.h @@ -244,7 +244,7 @@ private: const Option &m_option; QString m_indent; QString m_dindent; - bool m_stdsetdef; + bool m_stdsetdef = true; struct Buddy { @@ -294,11 +294,11 @@ private: // layout defaults LayoutDefaultHandler m_LayoutDefaultHandler; - int m_layoutMarginType; + int m_layoutMarginType = TopLevelMargin; QString m_generatedClass; QString m_mainFormVarName; - bool m_mainFormUsedInRetranslateUi; + bool m_mainFormUsedInRetranslateUi = false; QString m_delayedInitialization; QTextStream m_delayedOut; @@ -309,8 +309,8 @@ private: QString m_delayedActionInitialization; QTextStream m_actionOut; - bool m_layoutWidget; - bool m_firstThemeIcon; + bool m_layoutWidget = false; + bool m_firstThemeIcon = true; }; } // namespace CPP diff --git a/src/tools/uic/customwidgetsinfo.cpp b/src/tools/uic/customwidgetsinfo.cpp index d6a409152b..c838feaf73 100644 --- a/src/tools/uic/customwidgetsinfo.cpp +++ b/src/tools/uic/customwidgetsinfo.cpp @@ -95,7 +95,7 @@ bool CustomWidgetsInfo::extendsOneOf(const QString &classNameIn, bool CustomWidgetsInfo::isCustomWidgetContainer(const QString &className) const { - if (const DomCustomWidget *dcw = m_customWidgets.value(className, 0)) + if (const DomCustomWidget *dcw = m_customWidgets.value(className, nullptr)) if (dcw->hasElementContainer()) return dcw->elementContainer() != 0; return false; @@ -111,7 +111,7 @@ QString CustomWidgetsInfo::realClassName(const QString &className) const QString CustomWidgetsInfo::customWidgetAddPageMethod(const QString &name) const { - if (DomCustomWidget *dcw = m_customWidgets.value(name, 0)) + if (DomCustomWidget *dcw = m_customWidgets.value(name, nullptr)) return dcw->elementAddPageMethod(); return QString(); } -- cgit v1.2.3 From df7c97f8eed00a1dcd24a815d89d2842f7d6d116 Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Thu, 27 Jun 2019 16:14:30 +0200 Subject: qsslsocket_openssl.cpp - restructure the code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit not to resolve merge conflicts on every 5.13->dev merge. Change-Id: Id41a7efff52148fe46bedcde828646694fd1764d Reviewed-by: Mårten Nordheim --- src/network/ssl/qsslsocket_openssl.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index 4bd2f4c99a..c0035d23a8 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -1572,12 +1572,10 @@ bool QSslSocketBackendPrivate::checkOcspStatus() // verify the responder's chain (see their commit 4ba9a4265bd). // Working this around - is too much fuss for ancient versions we // are dropping quite soon anyway. - { - const unsigned long verificationFlags = 0; - const int success = q_OCSP_basic_verify(basicResponse, peerChain, store, verificationFlags); - if (success <= 0) - ocspErrors.push_back(QSslError::OcspResponseCannotBeTrusted); - } + const unsigned long verificationFlags = 0; + const int success = q_OCSP_basic_verify(basicResponse, peerChain, store, verificationFlags); + if (success <= 0) + ocspErrors.push_back(QSslError::OcspResponseCannotBeTrusted); if (q_OCSP_resp_count(basicResponse) != 1) { ocspErrors.push_back(QSslError::OcspMalformedResponse); -- cgit v1.2.3 From f66c1db16c050c9d685a44a38ad7c5cf9f6fcc96 Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Tue, 30 Apr 2019 09:39:21 +0200 Subject: Introduce Q_NAMESPACE_EXPORT A recurring problem with the Q_NAMESPACE macro is that it declares an object (staticMetaObject) in the surrounding namespace. That object lacks any export/import qualification to make it usable with shared libraries. Introduce therefore another macro to work around this issue, allowing the user to prefix the object with an exporting macro, f.i. like this: Q_NAMESPACE_EXPORT(Q_CORE_EXPORT) The old macro can simply then be rewritten in terms of this new one, supplying an empty export macro. Note that NOT passing an argument to a macro expecting one is well defined behavior in C99 -- the macro will expand an empty token. Of course, MSVC doesn't like this and emits warnings. As a workaround, use a variadic macro. [ChangeLog][QtCore] Added the new Q_NAMESPACE_EXPORT macro. It can be used just like Q_NAMESPACE to add meta-object information to a namespace; however it also supports exporting of such information from shared libraries. [ChangeLog][Potentially Source-Incompatible Changes] Prefixing Q_NAMESPACE with an export macro may no longer work. Use the new Q_NAMESPACE_EXPORT macro for that use case. Fixes: QTBUG-68014 Change-Id: Ib044a555ace1f77ae8e0244d824ec473550f3d8e Reviewed-by: Edward Welbourne Reviewed-by: Qt CI Bot Reviewed-by: BogDan Vatra --- src/corelib/kernel/qobject.cpp | 18 ++ src/corelib/kernel/qobjectdefs.h | 10 +- src/gui/util/qshaderlanguage_p.h | 2 +- src/tools/moc/keywords.cpp | 357 ++++++++++++++++--------------- src/tools/moc/moc.cpp | 7 + src/tools/moc/token.h | 1 + src/tools/moc/util/generate_keywords.cpp | 1 + 7 files changed, 217 insertions(+), 179 deletions(-) (limited to 'src') diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index e3b25f8bf7..1540ebfe12 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -4526,6 +4526,24 @@ QDebug operator<<(QDebug dbg, const QObject *o) Q_NAMESPACE makes an external variable, \c{staticMetaObject}, available. \c{staticMetaObject} is of type QMetaObject and provides access to the enums declared with Q_ENUM_NS/Q_FLAG_NS. + + \sa Q_NAMESPACE_EXPORT +*/ + +/*! + \macro Q_NAMESPACE_EXPORT(EXPORT_MACRO) + \relates QObject + \since 5.14 + + The Q_NAMESPACE_EXPORT macro can be used to add QMetaObject capabilities + to a namespace. + + It works exactly like the Q_NAMESPACE macro. However, the external + \c{staticMetaObject} variable that gets defined in the namespace + is declared with the supplied \c{EXPORT_MACRO} qualifier. This is + useful f.i. if the object needs to be exported from a dynamic library. + + \sa Q_NAMESPACE, {Creating Shared Libraries} */ /*! diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h index 4d5ac4dcb2..ef22b6e67f 100644 --- a/src/corelib/kernel/qobjectdefs.h +++ b/src/corelib/kernel/qobjectdefs.h @@ -201,12 +201,16 @@ private: \ QT_ANNOTATE_CLASS(qt_qgadget, "") \ /*end*/ -/* qmake ignore Q_NAMESPACE */ -#define Q_NAMESPACE \ - extern const QMetaObject staticMetaObject; \ +/* qmake ignore Q_NAMESPACE_EXPORT */ +#define Q_NAMESPACE_EXPORT(...) \ + extern __VA_ARGS__ const QMetaObject staticMetaObject; \ QT_ANNOTATE_CLASS(qt_qnamespace, "") \ /*end*/ +/* qmake ignore Q_NAMESPACE */ +#define Q_NAMESPACE Q_NAMESPACE_EXPORT() \ + /*end*/ + #endif // QT_NO_META_MACROS #else // Q_MOC_RUN diff --git a/src/gui/util/qshaderlanguage_p.h b/src/gui/util/qshaderlanguage_p.h index 3af967b8c6..193f797cc3 100644 --- a/src/gui/util/qshaderlanguage_p.h +++ b/src/gui/util/qshaderlanguage_p.h @@ -59,7 +59,7 @@ QT_BEGIN_NAMESPACE namespace QShaderLanguage { - Q_GUI_EXPORT Q_NAMESPACE + Q_NAMESPACE_EXPORT(Q_GUI_EXPORT) enum StorageQualifier : char { Const = 1, diff --git a/src/tools/moc/keywords.cpp b/src/tools/moc/keywords.cpp index 07c59d155f..7da8d94efc 100644 --- a/src/tools/moc/keywords.cpp +++ b/src/tools/moc/keywords.cpp @@ -30,12 +30,12 @@ // DO NOT EDIT. static const short keyword_trans[][128] = { - {0,0,0,0,0,0,0,0,0,561,558,0,0,0,0,0, + {0,0,0,0,0,0,0,0,0,568,565,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 561,252,559,562,8,38,239,560,25,26,236,234,30,235,27,237, + 568,252,566,569,8,38,239,567,25,26,236,234,30,235,27,237, 22,22,22,22,22,22,22,22,22,22,34,41,23,39,24,43, 0,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,21,8,8,8,8,8,8,8,8,8,31,564,32,238,8, + 8,21,8,8,8,8,8,8,8,8,8,31,571,32,238,8, 0,1,2,3,4,5,6,7,8,9,8,8,10,11,12,13, 14,8,15,16,17,18,19,20,8,8,8,36,245,37,248,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -116,7 +116,7 @@ static const short keyword_trans[][128] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,290,222,0,0,490,0,0,0, + 0,0,0,0,0,0,0,0,290,222,0,0,497,0,0,0, 0,0,0,0,55,0,0,330,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -155,7 +155,7 @@ static const short keyword_trans[][128] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,514,0,0,0,0,0,0,0,0,0,0,357, + 0,0,0,0,521,0,0,0,0,0,0,0,0,0,0,357, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -177,7 +177,7 @@ static const short keyword_trans[][128] = { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,42,0,0,0,28,0, - 567,567,567,567,567,567,567,567,567,567,0,0,0,0,0,0, + 574,574,574,574,574,574,574,574,574,574,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -336,7 +336,7 @@ static const short keyword_trans[][128] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,566,0,0,0,0,565, + 0,0,0,0,0,0,0,0,0,0,573,0,0,0,0,572, 0,0,0,0,0,0,0,0,0,0,0,0,0,258,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -372,29 +372,29 @@ static const short keyword_trans[][128] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,487,0,0,0,300,0,0,0,0,0,0,0,0,0,0, + 0,494,0,0,0,300,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,468,417,401,409,373,0,477,0,0,0,0,364,358, - 379,0,550,465,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,475,424,408,416,380,0,484,0,0,0,0,364,358, + 386,0,557,472,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,387,0,0,0, - 0,0,380,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,394,0,0,0, + 0,0,387,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,504,0,0,0,0,0,381, + 0,0,0,0,0,0,0,0,0,511,0,0,0,0,0,388, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, @@ -403,7 +403,7 @@ static const short keyword_trans[][128] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,405,0,0,0,0,0,0,0,0,0,0,0,406, + 0,0,0,412,0,0,0,0,0,0,0,0,0,0,0,413, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -411,14 +411,14 @@ static const short keyword_trans[][128] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,413,0,0,0,0,0,0,0,0,0,0,0,414, + 0,0,0,420,0,0,0,0,0,0,0,0,0,0,0,421, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,447,425,0,0,430,0,0,0,439,0,0, + 0,0,0,0,0,454,432,0,0,437,0,0,0,446,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, @@ -426,7 +426,7 @@ static const short keyword_trans[][128] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,533,0,466,0,0,0,494,0,0,500,0,0,0, + 0,0,0,540,0,473,0,0,0,501,0,0,507,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, @@ -435,7 +435,7 @@ static const short keyword_trans[][128] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,479,0,526,0,0,0,0,0,0,0,0,0, + 0,0,0,0,486,0,533,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -443,7 +443,7 @@ static const short keyword_trans[][128] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 542,0,0,510,0,0,0,0,0,0,0,0,0,0,0,0, + 549,0,0,517,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} }; @@ -828,197 +828,204 @@ static const struct {CHARACTER, 0, 65, 370, CHARACTER}, {CHARACTER, 0, 67, 371, CHARACTER}, {CHARACTER, 0, 69, 372, CHARACTER}, - {Q_NAMESPACE_TOKEN, 0, 0, 0, CHARACTER}, - {CHARACTER, 0, 65, 374, CHARACTER}, - {CHARACTER, 0, 68, 375, CHARACTER}, - {CHARACTER, 0, 71, 376, CHARACTER}, - {CHARACTER, 0, 69, 377, CHARACTER}, - {CHARACTER, 0, 84, 378, CHARACTER}, + {Q_NAMESPACE_TOKEN, 0, 95, 373, CHARACTER}, + {CHARACTER, 0, 69, 374, CHARACTER}, + {CHARACTER, 0, 88, 375, CHARACTER}, + {CHARACTER, 0, 80, 376, CHARACTER}, + {CHARACTER, 0, 79, 377, CHARACTER}, + {CHARACTER, 0, 82, 378, CHARACTER}, + {CHARACTER, 0, 84, 379, CHARACTER}, + {Q_NAMESPACE_EXPORT_TOKEN, 0, 0, 0, CHARACTER}, + {CHARACTER, 0, 65, 381, CHARACTER}, + {CHARACTER, 0, 68, 382, CHARACTER}, + {CHARACTER, 0, 71, 383, CHARACTER}, + {CHARACTER, 0, 69, 384, CHARACTER}, + {CHARACTER, 0, 84, 385, CHARACTER}, {Q_GADGET_TOKEN, 0, 0, 0, CHARACTER}, {CHARACTER, 44, 0, 0, CHARACTER}, {CHARACTER, 45, 0, 0, CHARACTER}, - {CHARACTER, 0, 80, 382, CHARACTER}, - {CHARACTER, 0, 69, 383, CHARACTER}, - {CHARACTER, 0, 82, 384, CHARACTER}, - {CHARACTER, 0, 84, 385, CHARACTER}, - {CHARACTER, 0, 89, 386, CHARACTER}, + {CHARACTER, 0, 80, 389, CHARACTER}, + {CHARACTER, 0, 69, 390, CHARACTER}, + {CHARACTER, 0, 82, 391, CHARACTER}, + {CHARACTER, 0, 84, 392, CHARACTER}, + {CHARACTER, 0, 89, 393, CHARACTER}, {Q_PROPERTY_TOKEN, 0, 0, 0, CHARACTER}, - {CHARACTER, 0, 85, 388, CHARACTER}, - {CHARACTER, 0, 71, 389, CHARACTER}, - {CHARACTER, 0, 73, 390, CHARACTER}, - {CHARACTER, 0, 78, 391, CHARACTER}, - {CHARACTER, 0, 95, 392, CHARACTER}, - {CHARACTER, 0, 77, 393, CHARACTER}, - {CHARACTER, 0, 69, 394, CHARACTER}, - {CHARACTER, 0, 84, 395, CHARACTER}, - {CHARACTER, 0, 65, 396, CHARACTER}, - {CHARACTER, 0, 68, 397, CHARACTER}, - {CHARACTER, 0, 65, 398, CHARACTER}, - {CHARACTER, 0, 84, 399, CHARACTER}, - {CHARACTER, 0, 65, 400, CHARACTER}, + {CHARACTER, 0, 85, 395, CHARACTER}, + {CHARACTER, 0, 71, 396, CHARACTER}, + {CHARACTER, 0, 73, 397, CHARACTER}, + {CHARACTER, 0, 78, 398, CHARACTER}, + {CHARACTER, 0, 95, 399, CHARACTER}, + {CHARACTER, 0, 77, 400, CHARACTER}, + {CHARACTER, 0, 69, 401, CHARACTER}, + {CHARACTER, 0, 84, 402, CHARACTER}, + {CHARACTER, 0, 65, 403, CHARACTER}, + {CHARACTER, 0, 68, 404, CHARACTER}, + {CHARACTER, 0, 65, 405, CHARACTER}, + {CHARACTER, 0, 84, 406, CHARACTER}, + {CHARACTER, 0, 65, 407, CHARACTER}, {Q_PLUGIN_METADATA_TOKEN, 0, 0, 0, CHARACTER}, - {CHARACTER, 0, 78, 402, CHARACTER}, - {CHARACTER, 0, 85, 403, CHARACTER}, - {CHARACTER, 0, 77, 404, CHARACTER}, + {CHARACTER, 0, 78, 409, CHARACTER}, + {CHARACTER, 0, 85, 410, CHARACTER}, + {CHARACTER, 0, 77, 411, CHARACTER}, {Q_ENUM_TOKEN, 46, 0, 0, CHARACTER}, {Q_ENUMS_TOKEN, 0, 0, 0, CHARACTER}, - {CHARACTER, 0, 78, 407, CHARACTER}, - {CHARACTER, 0, 83, 408, CHARACTER}, + {CHARACTER, 0, 78, 414, CHARACTER}, + {CHARACTER, 0, 83, 415, CHARACTER}, {Q_ENUM_NS_TOKEN, 0, 0, 0, CHARACTER}, - {CHARACTER, 0, 76, 410, CHARACTER}, - {CHARACTER, 0, 65, 411, CHARACTER}, - {CHARACTER, 0, 71, 412, CHARACTER}, + {CHARACTER, 0, 76, 417, CHARACTER}, + {CHARACTER, 0, 65, 418, CHARACTER}, + {CHARACTER, 0, 71, 419, CHARACTER}, {Q_FLAG_TOKEN, 47, 0, 0, CHARACTER}, {Q_FLAGS_TOKEN, 0, 0, 0, CHARACTER}, - {CHARACTER, 0, 78, 415, CHARACTER}, - {CHARACTER, 0, 83, 416, CHARACTER}, + {CHARACTER, 0, 78, 422, CHARACTER}, + {CHARACTER, 0, 83, 423, CHARACTER}, {Q_FLAG_NS_TOKEN, 0, 0, 0, CHARACTER}, - {CHARACTER, 0, 69, 418, CHARACTER}, - {CHARACTER, 0, 67, 419, CHARACTER}, - {CHARACTER, 0, 76, 420, CHARACTER}, - {CHARACTER, 0, 65, 421, CHARACTER}, - {CHARACTER, 0, 82, 422, CHARACTER}, - {CHARACTER, 0, 69, 423, CHARACTER}, - {CHARACTER, 0, 95, 424, CHARACTER}, + {CHARACTER, 0, 69, 425, CHARACTER}, + {CHARACTER, 0, 67, 426, CHARACTER}, + {CHARACTER, 0, 76, 427, CHARACTER}, + {CHARACTER, 0, 65, 428, CHARACTER}, + {CHARACTER, 0, 82, 429, CHARACTER}, + {CHARACTER, 0, 69, 430, CHARACTER}, + {CHARACTER, 0, 95, 431, CHARACTER}, {CHARACTER, 48, 0, 0, CHARACTER}, - {CHARACTER, 0, 76, 426, CHARACTER}, - {CHARACTER, 0, 65, 427, CHARACTER}, - {CHARACTER, 0, 71, 428, CHARACTER}, - {CHARACTER, 0, 83, 429, CHARACTER}, + {CHARACTER, 0, 76, 433, CHARACTER}, + {CHARACTER, 0, 65, 434, CHARACTER}, + {CHARACTER, 0, 71, 435, CHARACTER}, + {CHARACTER, 0, 83, 436, CHARACTER}, {Q_DECLARE_FLAGS_TOKEN, 0, 0, 0, CHARACTER}, - {CHARACTER, 0, 78, 431, CHARACTER}, - {CHARACTER, 0, 84, 432, CHARACTER}, - {CHARACTER, 0, 69, 433, CHARACTER}, - {CHARACTER, 0, 82, 434, CHARACTER}, - {CHARACTER, 0, 70, 435, CHARACTER}, - {CHARACTER, 0, 65, 436, CHARACTER}, - {CHARACTER, 0, 67, 437, CHARACTER}, - {CHARACTER, 0, 69, 438, CHARACTER}, - {Q_DECLARE_INTERFACE_TOKEN, 0, 0, 0, CHARACTER}, + {CHARACTER, 0, 78, 438, CHARACTER}, + {CHARACTER, 0, 84, 439, CHARACTER}, {CHARACTER, 0, 69, 440, CHARACTER}, - {CHARACTER, 0, 84, 441, CHARACTER}, - {CHARACTER, 0, 65, 442, CHARACTER}, - {CHARACTER, 0, 84, 443, CHARACTER}, - {CHARACTER, 0, 89, 444, CHARACTER}, - {CHARACTER, 0, 80, 445, CHARACTER}, - {CHARACTER, 0, 69, 446, CHARACTER}, + {CHARACTER, 0, 82, 441, CHARACTER}, + {CHARACTER, 0, 70, 442, CHARACTER}, + {CHARACTER, 0, 65, 443, CHARACTER}, + {CHARACTER, 0, 67, 444, CHARACTER}, + {CHARACTER, 0, 69, 445, CHARACTER}, + {Q_DECLARE_INTERFACE_TOKEN, 0, 0, 0, CHARACTER}, + {CHARACTER, 0, 69, 447, CHARACTER}, + {CHARACTER, 0, 84, 448, CHARACTER}, + {CHARACTER, 0, 65, 449, CHARACTER}, + {CHARACTER, 0, 84, 450, CHARACTER}, + {CHARACTER, 0, 89, 451, CHARACTER}, + {CHARACTER, 0, 80, 452, CHARACTER}, + {CHARACTER, 0, 69, 453, CHARACTER}, {Q_DECLARE_METATYPE_TOKEN, 0, 0, 0, CHARACTER}, - {CHARACTER, 0, 88, 448, CHARACTER}, - {CHARACTER, 0, 84, 449, CHARACTER}, - {CHARACTER, 0, 69, 450, CHARACTER}, - {CHARACTER, 0, 78, 451, CHARACTER}, - {CHARACTER, 0, 83, 452, CHARACTER}, - {CHARACTER, 0, 73, 453, CHARACTER}, - {CHARACTER, 0, 79, 454, CHARACTER}, - {CHARACTER, 0, 78, 455, CHARACTER}, - {CHARACTER, 0, 95, 456, CHARACTER}, - {CHARACTER, 0, 73, 457, CHARACTER}, + {CHARACTER, 0, 88, 455, CHARACTER}, + {CHARACTER, 0, 84, 456, CHARACTER}, + {CHARACTER, 0, 69, 457, CHARACTER}, {CHARACTER, 0, 78, 458, CHARACTER}, - {CHARACTER, 0, 84, 459, CHARACTER}, - {CHARACTER, 0, 69, 460, CHARACTER}, - {CHARACTER, 0, 82, 461, CHARACTER}, - {CHARACTER, 0, 70, 462, CHARACTER}, - {CHARACTER, 0, 65, 463, CHARACTER}, - {CHARACTER, 0, 67, 464, CHARACTER}, - {CHARACTER, 0, 69, 438, CHARACTER}, - {CHARACTER, 49, 0, 0, CHARACTER}, - {CHARACTER, 0, 84, 467, CHARACTER}, - {CHARACTER, 0, 83, 413, CHARACTER}, - {CHARACTER, 0, 76, 469, CHARACTER}, + {CHARACTER, 0, 83, 459, CHARACTER}, + {CHARACTER, 0, 73, 460, CHARACTER}, + {CHARACTER, 0, 79, 461, CHARACTER}, + {CHARACTER, 0, 78, 462, CHARACTER}, + {CHARACTER, 0, 95, 463, CHARACTER}, + {CHARACTER, 0, 73, 464, CHARACTER}, + {CHARACTER, 0, 78, 465, CHARACTER}, + {CHARACTER, 0, 84, 466, CHARACTER}, + {CHARACTER, 0, 69, 467, CHARACTER}, + {CHARACTER, 0, 82, 468, CHARACTER}, + {CHARACTER, 0, 70, 469, CHARACTER}, {CHARACTER, 0, 65, 470, CHARACTER}, - {CHARACTER, 0, 83, 471, CHARACTER}, - {CHARACTER, 0, 83, 472, CHARACTER}, - {CHARACTER, 0, 73, 473, CHARACTER}, - {CHARACTER, 0, 78, 474, CHARACTER}, - {CHARACTER, 0, 70, 475, CHARACTER}, - {CHARACTER, 0, 79, 476, CHARACTER}, + {CHARACTER, 0, 67, 471, CHARACTER}, + {CHARACTER, 0, 69, 445, CHARACTER}, + {CHARACTER, 49, 0, 0, CHARACTER}, + {CHARACTER, 0, 84, 474, CHARACTER}, + {CHARACTER, 0, 83, 420, CHARACTER}, + {CHARACTER, 0, 76, 476, CHARACTER}, + {CHARACTER, 0, 65, 477, CHARACTER}, + {CHARACTER, 0, 83, 478, CHARACTER}, + {CHARACTER, 0, 83, 479, CHARACTER}, + {CHARACTER, 0, 73, 480, CHARACTER}, + {CHARACTER, 0, 78, 481, CHARACTER}, + {CHARACTER, 0, 70, 482, CHARACTER}, + {CHARACTER, 0, 79, 483, CHARACTER}, {Q_CLASSINFO_TOKEN, 0, 0, 0, CHARACTER}, - {CHARACTER, 0, 78, 478, CHARACTER}, + {CHARACTER, 0, 78, 485, CHARACTER}, {CHARACTER, 50, 0, 0, CHARACTER}, - {CHARACTER, 0, 69, 480, CHARACTER}, - {CHARACTER, 0, 82, 481, CHARACTER}, - {CHARACTER, 0, 70, 482, CHARACTER}, - {CHARACTER, 0, 65, 483, CHARACTER}, - {CHARACTER, 0, 67, 484, CHARACTER}, - {CHARACTER, 0, 69, 485, CHARACTER}, - {CHARACTER, 0, 83, 486, CHARACTER}, + {CHARACTER, 0, 69, 487, CHARACTER}, + {CHARACTER, 0, 82, 488, CHARACTER}, + {CHARACTER, 0, 70, 489, CHARACTER}, + {CHARACTER, 0, 65, 490, CHARACTER}, + {CHARACTER, 0, 67, 491, CHARACTER}, + {CHARACTER, 0, 69, 492, CHARACTER}, + {CHARACTER, 0, 83, 493, CHARACTER}, {Q_INTERFACES_TOKEN, 0, 0, 0, CHARACTER}, - {CHARACTER, 0, 108, 488, CHARACTER}, - {CHARACTER, 0, 115, 489, CHARACTER}, + {CHARACTER, 0, 108, 495, CHARACTER}, + {CHARACTER, 0, 115, 496, CHARACTER}, {SIGNALS, 0, 0, 0, CHARACTER}, - {CHARACTER, 0, 111, 491, CHARACTER}, - {CHARACTER, 0, 116, 492, CHARACTER}, - {CHARACTER, 0, 115, 493, CHARACTER}, + {CHARACTER, 0, 111, 498, CHARACTER}, + {CHARACTER, 0, 116, 499, CHARACTER}, + {CHARACTER, 0, 115, 500, CHARACTER}, {SLOTS, 0, 0, 0, CHARACTER}, - {CHARACTER, 0, 71, 495, CHARACTER}, - {CHARACTER, 0, 78, 496, CHARACTER}, - {CHARACTER, 0, 65, 497, CHARACTER}, - {CHARACTER, 0, 76, 498, CHARACTER}, - {Q_SIGNAL_TOKEN, 0, 83, 499, CHARACTER}, + {CHARACTER, 0, 71, 502, CHARACTER}, + {CHARACTER, 0, 78, 503, CHARACTER}, + {CHARACTER, 0, 65, 504, CHARACTER}, + {CHARACTER, 0, 76, 505, CHARACTER}, + {Q_SIGNAL_TOKEN, 0, 83, 506, CHARACTER}, {Q_SIGNALS_TOKEN, 0, 0, 0, CHARACTER}, - {CHARACTER, 0, 79, 501, CHARACTER}, - {CHARACTER, 0, 84, 502, CHARACTER}, - {Q_SLOT_TOKEN, 0, 83, 503, CHARACTER}, + {CHARACTER, 0, 79, 508, CHARACTER}, + {CHARACTER, 0, 84, 509, CHARACTER}, + {Q_SLOT_TOKEN, 0, 83, 510, CHARACTER}, {Q_SLOTS_TOKEN, 0, 0, 0, CHARACTER}, - {CHARACTER, 0, 86, 505, CHARACTER}, - {CHARACTER, 0, 65, 506, CHARACTER}, - {CHARACTER, 0, 84, 507, CHARACTER}, - {CHARACTER, 0, 69, 508, CHARACTER}, - {CHARACTER, 0, 95, 509, CHARACTER}, + {CHARACTER, 0, 86, 512, CHARACTER}, + {CHARACTER, 0, 65, 513, CHARACTER}, + {CHARACTER, 0, 84, 514, CHARACTER}, + {CHARACTER, 0, 69, 515, CHARACTER}, + {CHARACTER, 0, 95, 516, CHARACTER}, {CHARACTER, 51, 0, 0, CHARACTER}, - {CHARACTER, 0, 76, 511, CHARACTER}, - {CHARACTER, 0, 79, 512, CHARACTER}, - {CHARACTER, 0, 84, 513, CHARACTER}, + {CHARACTER, 0, 76, 518, CHARACTER}, + {CHARACTER, 0, 79, 519, CHARACTER}, + {CHARACTER, 0, 84, 520, CHARACTER}, {Q_PRIVATE_SLOT_TOKEN, 0, 0, 0, CHARACTER}, - {CHARACTER, 0, 95, 515, CHARACTER}, - {CHARACTER, 0, 77, 516, CHARACTER}, - {CHARACTER, 0, 79, 517, CHARACTER}, - {CHARACTER, 0, 67, 518, CHARACTER}, - {CHARACTER, 0, 95, 519, CHARACTER}, - {CHARACTER, 0, 67, 520, CHARACTER}, - {CHARACTER, 0, 79, 521, CHARACTER}, - {CHARACTER, 0, 77, 522, CHARACTER}, - {CHARACTER, 0, 80, 523, CHARACTER}, - {CHARACTER, 0, 65, 524, CHARACTER}, - {CHARACTER, 0, 84, 525, CHARACTER}, + {CHARACTER, 0, 95, 522, CHARACTER}, + {CHARACTER, 0, 77, 523, CHARACTER}, + {CHARACTER, 0, 79, 524, CHARACTER}, + {CHARACTER, 0, 67, 525, CHARACTER}, + {CHARACTER, 0, 95, 526, CHARACTER}, + {CHARACTER, 0, 67, 527, CHARACTER}, + {CHARACTER, 0, 79, 528, CHARACTER}, + {CHARACTER, 0, 77, 529, CHARACTER}, + {CHARACTER, 0, 80, 530, CHARACTER}, + {CHARACTER, 0, 65, 531, CHARACTER}, + {CHARACTER, 0, 84, 532, CHARACTER}, {Q_MOC_COMPAT_TOKEN, 0, 0, 0, CHARACTER}, - {CHARACTER, 0, 79, 527, CHARACTER}, - {CHARACTER, 0, 75, 528, CHARACTER}, - {CHARACTER, 0, 65, 529, CHARACTER}, - {CHARACTER, 0, 66, 530, CHARACTER}, - {CHARACTER, 0, 76, 531, CHARACTER}, - {CHARACTER, 0, 69, 532, CHARACTER}, + {CHARACTER, 0, 79, 534, CHARACTER}, + {CHARACTER, 0, 75, 535, CHARACTER}, + {CHARACTER, 0, 65, 536, CHARACTER}, + {CHARACTER, 0, 66, 537, CHARACTER}, + {CHARACTER, 0, 76, 538, CHARACTER}, + {CHARACTER, 0, 69, 539, CHARACTER}, {Q_INVOKABLE_TOKEN, 0, 0, 0, CHARACTER}, - {CHARACTER, 0, 82, 534, CHARACTER}, - {CHARACTER, 0, 73, 535, CHARACTER}, - {CHARACTER, 0, 80, 536, CHARACTER}, - {CHARACTER, 0, 84, 537, CHARACTER}, - {CHARACTER, 0, 65, 538, CHARACTER}, - {CHARACTER, 0, 66, 539, CHARACTER}, - {CHARACTER, 0, 76, 540, CHARACTER}, - {CHARACTER, 0, 69, 541, CHARACTER}, + {CHARACTER, 0, 82, 541, CHARACTER}, + {CHARACTER, 0, 73, 542, CHARACTER}, + {CHARACTER, 0, 80, 543, CHARACTER}, + {CHARACTER, 0, 84, 544, CHARACTER}, + {CHARACTER, 0, 65, 545, CHARACTER}, + {CHARACTER, 0, 66, 546, CHARACTER}, + {CHARACTER, 0, 76, 547, CHARACTER}, + {CHARACTER, 0, 69, 548, CHARACTER}, {Q_SCRIPTABLE_TOKEN, 0, 0, 0, CHARACTER}, - {CHARACTER, 0, 82, 543, CHARACTER}, - {CHARACTER, 0, 79, 544, CHARACTER}, - {CHARACTER, 0, 80, 545, CHARACTER}, - {CHARACTER, 0, 69, 546, CHARACTER}, - {CHARACTER, 0, 82, 547, CHARACTER}, - {CHARACTER, 0, 84, 548, CHARACTER}, - {CHARACTER, 0, 89, 549, CHARACTER}, + {CHARACTER, 0, 82, 550, CHARACTER}, + {CHARACTER, 0, 79, 551, CHARACTER}, + {CHARACTER, 0, 80, 552, CHARACTER}, + {CHARACTER, 0, 69, 553, CHARACTER}, + {CHARACTER, 0, 82, 554, CHARACTER}, + {CHARACTER, 0, 84, 555, CHARACTER}, + {CHARACTER, 0, 89, 556, CHARACTER}, {Q_PRIVATE_PROPERTY_TOKEN, 0, 0, 0, CHARACTER}, - {CHARACTER, 0, 69, 551, CHARACTER}, - {CHARACTER, 0, 86, 552, CHARACTER}, - {CHARACTER, 0, 73, 553, CHARACTER}, - {CHARACTER, 0, 83, 554, CHARACTER}, - {CHARACTER, 0, 73, 555, CHARACTER}, - {CHARACTER, 0, 79, 556, CHARACTER}, - {CHARACTER, 0, 78, 557, CHARACTER}, + {CHARACTER, 0, 69, 558, CHARACTER}, + {CHARACTER, 0, 86, 559, CHARACTER}, + {CHARACTER, 0, 73, 560, CHARACTER}, + {CHARACTER, 0, 83, 561, CHARACTER}, + {CHARACTER, 0, 73, 562, CHARACTER}, + {CHARACTER, 0, 79, 563, CHARACTER}, + {CHARACTER, 0, 78, 564, CHARACTER}, {Q_REVISION_TOKEN, 0, 0, 0, CHARACTER}, {NEWLINE, 0, 0, 0, NOTOKEN}, {QUOTE, 0, 0, 0, NOTOKEN}, {SINGLEQUOTE, 0, 0, 0, NOTOKEN}, {WHITESPACE, 0, 0, 0, NOTOKEN}, - {HASH, 0, 35, 563, HASH}, + {HASH, 0, 35, 570, HASH}, {PP_HASHHASH, 0, 0, 0, NOTOKEN}, {BACKSLASH, 0, 0, 0, NOTOKEN}, {CPP_COMMENT, 0, 0, 0, NOTOKEN}, diff --git a/src/tools/moc/moc.cpp b/src/tools/moc/moc.cpp index 2f0ea633fa..50946443be 100644 --- a/src/tools/moc/moc.cpp +++ b/src/tools/moc/moc.cpp @@ -620,6 +620,13 @@ void Moc::parse() case Q_NAMESPACE_TOKEN: def.hasQNamespace = true; break; + case Q_NAMESPACE_EXPORT_TOKEN: + next(LPAREN); + while (test(IDENTIFIER)) + {} + next(RPAREN); + def.hasQNamespace = true; + break; case Q_ENUMS_TOKEN: case Q_ENUM_NS_TOKEN: parseEnumOrFlag(&def, false); diff --git a/src/tools/moc/token.h b/src/tools/moc/token.h index db9d319b78..0cc163f9e4 100644 --- a/src/tools/moc/token.h +++ b/src/tools/moc/token.h @@ -155,6 +155,7 @@ QT_BEGIN_NAMESPACE F(Q_OBJECT_TOKEN) \ F(Q_GADGET_TOKEN) \ F(Q_NAMESPACE_TOKEN) \ + F(Q_NAMESPACE_EXPORT_TOKEN) \ F(Q_PROPERTY_TOKEN) \ F(Q_PLUGIN_METADATA_TOKEN) \ F(Q_ENUMS_TOKEN) \ diff --git a/src/tools/moc/util/generate_keywords.cpp b/src/tools/moc/util/generate_keywords.cpp index df850c1bdc..9248e9e2e7 100644 --- a/src/tools/moc/util/generate_keywords.cpp +++ b/src/tools/moc/util/generate_keywords.cpp @@ -214,6 +214,7 @@ static const Keyword keywords[] = { { "return", "RETURN" }, { "Q_OBJECT", "Q_OBJECT_TOKEN" }, { "Q_NAMESPACE", "Q_NAMESPACE_TOKEN" }, + { "Q_NAMESPACE_EXPORT", "Q_NAMESPACE_EXPORT_TOKEN" }, { "Q_GADGET", "Q_GADGET_TOKEN" }, { "Q_PROPERTY", "Q_PROPERTY_TOKEN" }, { "Q_PLUGIN_METADATA", "Q_PLUGIN_METADATA_TOKEN" }, -- cgit v1.2.3 From 1e4dec12d5c0152d5c3eee3b612a0af4bf389a37 Mon Sep 17 00:00:00 2001 From: BogDan Vatra Date: Thu, 27 Jun 2019 11:51:26 +0300 Subject: Android: Stick with buildToolsVersion 28.0.3 On Windows buildToolsVersion 29.0.0 have problems, therefore, it's better to use a version that we know it works on all platofrms. Change-Id: I25cdea4b8101bfe5f022025fcd7cc4cbf358fa03 Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/android/templates/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/android/templates/build.gradle b/src/android/templates/build.gradle index 989d0792cf..ed704c4957 100644 --- a/src/android/templates/build.gradle +++ b/src/android/templates/build.gradle @@ -36,7 +36,7 @@ android { compileSdkVersion androidCompileSdkVersion.toInteger() - buildToolsVersion androidBuildToolsVersion + buildToolsVersion '28.0.3' sourceSets { main { -- cgit v1.2.3 From d1141b6c90e53554f69fd6a7a272988211f5bd34 Mon Sep 17 00:00:00 2001 From: Joni Poikelin Date: Thu, 28 Feb 2019 09:54:20 +0200 Subject: Fix crash with drag cursor handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 7a7c722782a435f7c01b94f48df7a5f4ff4d599e caused a regresssion in some cases. Change-Id: I1089a79534d811b195de663ff664d9ba5a6ac6c5 Fixes: QTBUG-74110 Reviewed-by: Tor Arne Vestbø --- src/gui/kernel/qsimpledrag.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/gui/kernel/qsimpledrag.cpp b/src/gui/kernel/qsimpledrag.cpp index 9aab332ef5..bd409c124f 100644 --- a/src/gui/kernel/qsimpledrag.cpp +++ b/src/gui/kernel/qsimpledrag.cpp @@ -310,11 +310,15 @@ void QBasicDrag::updateCursor(Qt::DropAction action) m_dndHasSetOverrideCursor = true; } else { QCursor *cursor = QGuiApplication::overrideCursor(); - if (!pixmap.isNull()) { - if (cursor->pixmap().cacheKey() != pixmap.cacheKey()) - QGuiApplication::changeOverrideCursor(QCursor(pixmap)); - } else if (cursorShape != cursor->shape()) { - QGuiApplication::changeOverrideCursor(QCursor(cursorShape)); + if (!cursor) { + QGuiApplication::changeOverrideCursor(pixmap.isNull() ? QCursor(cursorShape) : QCursor(pixmap)); + } else { + if (!pixmap.isNull()) { + if (cursor->pixmap().cacheKey() != pixmap.cacheKey()) + QGuiApplication::changeOverrideCursor(QCursor(pixmap)); + } else if (cursorShape != cursor->shape()) { + QGuiApplication::changeOverrideCursor(QCursor(cursorShape)); + } } } #endif -- cgit v1.2.3 From e19d93b212d71521b7edd3a7e45e4b9319cd5c72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Nordheim?= Date: Thu, 27 Jun 2019 11:38:43 +0200 Subject: QSocks5SocketEngine: avoid dereferencing null-pointer The header argument is optional Change-Id: I035e11db5ee70183274afb48ba67c4d3ed2f615d Reviewed-by: Timur Pocheptsov --- src/network/socket/qsocks5socketengine.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp index dd2bc90855..6791b85273 100644 --- a/src/network/socket/qsocks5socketengine.cpp +++ b/src/network/socket/qsocks5socketengine.cpp @@ -1611,8 +1611,10 @@ qint64 QSocks5SocketEngine::readDatagram(char *data, qint64 maxlen, QIpPacketHea QSocks5RevivedDatagram datagram = d->udpData->pendingDatagrams.dequeue(); int copyLen = qMin(maxlen, datagram.data.size()); memcpy(data, datagram.data.constData(), copyLen); - header->senderAddress = datagram.address; - header->senderPort = datagram.port; + if (header) { + header->senderAddress = datagram.address; + header->senderPort = datagram.port; + } return copyLen; #else Q_UNUSED(data) -- cgit v1.2.3 From 399bf445771936648691c48bebca500b36ae69dd Mon Sep 17 00:00:00 2001 From: Vova Mshanetskiy Date: Fri, 31 May 2019 18:51:43 +0300 Subject: QAndroidInputContext: Consider preedit text in getCursorCapsMode() Fixes auto-capitalization in AnySoftKeyboard. It was typing the whole first word in a sentence in upper case. Change-Id: I605a1aee39d432a3474c0bf706445d354562285f Reviewed-by: BogDan Vatra --- src/plugins/platforms/android/qandroidinputcontext.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/android/qandroidinputcontext.cpp b/src/plugins/platforms/android/qandroidinputcontext.cpp index fa07af8c46..bab71751f8 100644 --- a/src/plugins/platforms/android/qandroidinputcontext.cpp +++ b/src/plugins/platforms/android/qandroidinputcontext.cpp @@ -1200,13 +1200,18 @@ jint QAndroidInputContext::getCursorCapsMode(jint /*reqModes*/) const uint qtInputMethodHints = query->value(Qt::ImHints).toUInt(); const int localPos = query->value(Qt::ImCursorPosition).toInt(); - bool atWordBoundary = (localPos == 0); + bool atWordBoundary = + localPos == 0 + && (!focusObjectIsComposing() || m_composingCursor == m_composingTextStart); + if (!atWordBoundary) { QString surroundingText = query->value(Qt::ImSurroundingText).toString(); surroundingText.truncate(localPos); + if (focusObjectIsComposing()) + surroundingText += m_composingText.leftRef(m_composingCursor - m_composingTextStart); // Add a character to see if it is at the end of the sentence or not QTextBoundaryFinder finder(QTextBoundaryFinder::Sentence, surroundingText + QLatin1Char('A')); - finder.setPosition(localPos); + finder.setPosition(surroundingText.length()); if (finder.isAtBoundary()) atWordBoundary = finder.isAtBoundary(); } -- cgit v1.2.3 From 2c61b4e0f08e55d5478e710fe66eb12594ae17cb Mon Sep 17 00:00:00 2001 From: Vova Mshanetskiy Date: Fri, 31 May 2019 19:42:57 +0300 Subject: QAndroidInputContext: Do not stop composing when user taps the cursor There is no need to tell the editor to stop composing if user taps so close to the cursor position that the cursor will not move anyway. If we do stop composing in such case, then since there will be no cursor position change notification, we will never start composing again (before the cursor is actually moved), and the current composing region will remain being displayed as normal text instead of being displayed as composing text. Change-Id: I4ebe6442e1ba8c365d6754c1a8487235d177c732 Reviewed-by: BogDan Vatra --- .../platforms/android/qandroidinputcontext.cpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/plugins/platforms/android/qandroidinputcontext.cpp b/src/plugins/platforms/android/qandroidinputcontext.cpp index bab71751f8..69b100fa4e 100644 --- a/src/plugins/platforms/android/qandroidinputcontext.cpp +++ b/src/plugins/platforms/android/qandroidinputcontext.cpp @@ -792,7 +792,25 @@ void QAndroidInputContext::touchDown(int x, int y) m_handleMode = ShowCursor; // The VK will appear in a moment, stop the timer m_hideCursorHandleTimer.stop(); - focusObjectStopComposing(); + + if (focusObjectIsComposing()) { + const double pixelDensity = + QGuiApplication::focusWindow() + ? QHighDpiScaling::factor(QGuiApplication::focusWindow()) + : QHighDpiScaling::factor(QtAndroid::androidPlatformIntegration()->screen()); + + const QPointF touchPointLocal = + QGuiApplication::inputMethod()->inputItemTransform().inverted().map( + QPointF(x / pixelDensity, y / pixelDensity)); + + const int curBlockPos = getBlockPosition( + focusObjectInputMethodQuery(Qt::ImCursorPosition | Qt::ImAbsolutePosition)); + const int touchPosition = curBlockPos + + QInputMethod::queryFocusObject(Qt::ImCursorPosition, touchPointLocal).toInt(); + if (touchPosition != m_composingCursor) + focusObjectStopComposing(); + } + updateSelectionHandles(); } } -- cgit v1.2.3 From 346f1999f579c76c98eaa53c5bec2510ae596c22 Mon Sep 17 00:00:00 2001 From: Vova Mshanetskiy Date: Wed, 5 Jun 2019 14:59:18 +0300 Subject: QAndroidInputContext: Generate a QInputMethodEvent in reset() Although QPlatformInputContext::reset() documentation says that reset() should not send any QInputMethodEvents, implementations on Windows, macOS and iOS do send a QInputMethodEvent which clears preedit text in their reimplementations of reset(). Text editing controls depend on that and may not clear preedit text if such event is not sent. Change-Id: I75ab73946cb06e93e5fc5e98e0cc503a7de5c2e0 Reviewed-by: BogDan Vatra --- src/plugins/platforms/android/qandroidinputcontext.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/plugins/platforms/android/qandroidinputcontext.cpp b/src/plugins/platforms/android/qandroidinputcontext.cpp index 69b100fa4e..5614d3b04f 100644 --- a/src/plugins/platforms/android/qandroidinputcontext.cpp +++ b/src/plugins/platforms/android/qandroidinputcontext.cpp @@ -558,6 +558,7 @@ static inline int getBlockPosition(const QSharedPointer void QAndroidInputContext::reset() { + focusObjectStopComposing(); clear(); m_batchEditNestingLevel = 0; m_handleMode = Hidden; -- cgit v1.2.3 From 1ce832648e05ad6226071ec38a5da0ef08d3ab8e Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Tue, 25 Jun 2019 00:56:31 +0200 Subject: forkfd: port to loadRelaxed / storeRelaxed The usages were hidden behind clever macros. Change-Id: I594814cd45b19841880e9a88f40af8805c97fe79 Reviewed-by: Thiago Macieira --- src/corelib/io/forkfd_qt.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'src') diff --git a/src/corelib/io/forkfd_qt.cpp b/src/corelib/io/forkfd_qt.cpp index dce0ebb4da..80d1cd54d7 100644 --- a/src/corelib/io/forkfd_qt.cpp +++ b/src/corelib/io/forkfd_qt.cpp @@ -56,8 +56,6 @@ QT_BEGIN_NAMESPACE #define FFD_ATOMIC_RELAXED Relaxed #define FFD_ATOMIC_ACQUIRE Acquire #define FFD_ATOMIC_RELEASE Release -#define loadRelaxed load -#define storeRelaxed store #define FFD_CONCAT(x, y) x ## y -- cgit v1.2.3 From 9520ba5a73cd9d6cb8ef47f10837b92494c8221e Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Mon, 17 Jun 2019 12:41:59 +0200 Subject: Improve performance when loading application fonts Since it was added in Qt 4.2, addAppFont() has been written to first register the font, then immediately invalidate the font database and later reload the font again the next time the db is used. This caused all application fonts to be reloaded *at least* once, an operation which can be quite heavy for large fonts, such as CJK fonts. If an application loaded multiple fonts at different stages of execution, you could end up loading the same fonts a large number of times, since all application fonts would be reregistered every time a new one was added. When calling removeApplicationFont(), this is okay-ish, since we need to remove all traces of the font from the platform database and clearing the whole thing is a convenient way of making sure there is nothing left. There might be more efficient ways of doing this, but unloading fonts is not a common operation, so it is fine to keep this behavior there. This change removes the invalidation of the font database from addAppFont(), since this should not be necessary as long as we are adding fonts. It also removes the reregisterAppFonts flag, which was a bit of a convoluted way of saying that the database had been invalidated and needed repopulating. Instead, we use the same mechanism as for repopulating the system database: We just check if it is currently empty, which means it has been invalidated and the application fonts have to be reregistered. It does not touch the logic in qt_cleanupFontDatabase(), which is kind of broken (it will leave application fonts in memory and reregister them if the application continues to run). But this is only actually called during shutdown (from application destructor). [ChangeLog][QtGui][Text] Fixed an issue where application fonts would be parsed multiple times, causing some unnecessary overhead when during application startup. Task-number: QTBUG-76239 Change-Id: Idfb62f73133b55e0909bb398631c8e762442e95b Reviewed-by: Allan Sandfeld Jensen --- src/gui/text/qfontdatabase.cpp | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/gui/text/qfontdatabase.cpp b/src/gui/text/qfontdatabase.cpp index 5350a9c5ec..562ee3e2b1 100644 --- a/src/gui/text/qfontdatabase.cpp +++ b/src/gui/text/qfontdatabase.cpp @@ -451,8 +451,7 @@ class QFontDatabasePrivate public: QFontDatabasePrivate() : count(0), families(0), - fallbacksCache(64), - reregisterAppFonts(false) + fallbacksCache(64) { } ~QFontDatabasePrivate() { @@ -488,7 +487,6 @@ public: }; QVector applicationFonts; int addAppFont(const QByteArray &fontData, const QString &fileName); - bool reregisterAppFonts; bool isApplicationFont(const QString &fileName); void invalidate(); @@ -897,15 +895,12 @@ static void initializeDb() QFontDatabasePrivate *db = privateDb(); // init by asking for the platformfontdb for the first time or after invalidation - if (!db->count) + if (!db->count) { QGuiApplicationPrivate::platformIntegration()->fontDatabase()->populateFontDatabase(); - - if (db->reregisterAppFonts) { for (int i = 0; i < db->applicationFonts.count(); i++) { if (!db->applicationFonts.at(i).families.isEmpty()) registerFont(&db->applicationFonts[i]); } - db->reregisterAppFonts = false; } } @@ -1033,11 +1028,7 @@ QFontEngine *loadEngine(int script, const QFontDef &request, static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt) { - QFontDatabasePrivate *db = privateDb(); - fnt->families = QGuiApplicationPrivate::platformIntegration()->fontDatabase()->addApplicationFont(fnt->data,fnt->fileName); - - db->reregisterAppFonts = true; } static QtFontStyle *bestStyle(QtFontFoundry *foundry, const QtFontStyle::Key &styleKey, @@ -2451,13 +2442,18 @@ int QFontDatabasePrivate::addAppFont(const QByteArray &fontData, const QString & if (font.fileName.isEmpty() && !fontData.isEmpty()) font.fileName = QLatin1String(":qmemoryfonts/") + QString::number(i); + bool wasEmpty = privateDb()->count == 0; registerFont(&font); if (font.families.isEmpty()) return -1; applicationFonts[i] = font; - invalidate(); + // If the cache has not yet been populated, we need to reload the application font later + if (wasEmpty) + invalidate(); + else + emit qApp->fontDatabaseChanged(); return i; } @@ -2598,7 +2594,6 @@ bool QFontDatabase::removeApplicationFont(int handle) db->applicationFonts[handle] = QFontDatabasePrivate::ApplicationFont(); - db->reregisterAppFonts = true; db->invalidate(); return true; } -- cgit v1.2.3 From a03270f8917a5e5e6fd0c3ffcf3fb4f705e8cffa Mon Sep 17 00:00:00 2001 From: Marvin Scholz Date: Sun, 28 Oct 2018 01:52:15 +0200 Subject: rcc: Add -d option to output a Makefile-syntax depfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The -d option makes rcc output a dependency file with the specified file name. The resulting dependency file is useful for make or ninja based build systems. [ChangeLog][Tools][rcc] Added -d option to generate a dependency file. Fixes: QTBUG-45460 Change-Id: I495ade50f8d9865d4c00dce9373b2b6d1a6c8f2f Reviewed-by: Jörg Bornemann --- src/tools/rcc/main.cpp | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) (limited to 'src') diff --git a/src/tools/rcc/main.cpp b/src/tools/rcc/main.cpp index 0eb6766b5a..ac87e48e39 100644 --- a/src/tools/rcc/main.cpp +++ b/src/tools/rcc/main.cpp @@ -99,6 +99,37 @@ int createProject(const QString &outFileName) return 0; } +// Escapes a path for use in a Depfile (Makefile syntax) +QString makefileEscape(const QString &filepath) +{ + // Always use forward slashes + QString result = QDir::cleanPath(filepath); + // Spaces are escaped with a backslash + result.replace(QLatin1Char(' '), QLatin1String("\\ ")); + // Pipes are escaped with a backslash + result.replace(QLatin1Char('|'), QLatin1String("\\|")); + // Dollars are escaped with a dollar + result.replace(QLatin1Char('$'), QLatin1String("$$")); + + return result; +} + +void writeDepFile(QIODevice &iodev, const QStringList &depsList, const QString &targetName) +{ + QTextStream out(&iodev); + out << qPrintable(makefileEscape(targetName)); + out << QLatin1Char(':'); + + // Write depfile + for (int i = 0; i < depsList.size(); ++i) { + out << QLatin1Char(' '); + + out << qPrintable(makefileEscape(depsList.at(i))); + } + + out << QLatin1Char('\n'); +} + int runRcc(int argc, char *argv[]) { QCoreApplication app(argc, argv); @@ -176,6 +207,10 @@ int runRcc(int argc, char *argv[]) QStringLiteral("Only output a mapping of resource paths to file system paths defined in the .qrc file, do not generate code.")); parser.addOption(mapOption); + QCommandLineOption depFileOption(QStringList{QStringLiteral("d"), QStringLiteral("depfile")}, + QStringLiteral("Write a depfile with the .qrc dependencies to ."), QStringLiteral("file")); + parser.addOption(depFileOption); + QCommandLineOption projectOption(QStringLiteral("project"), QStringLiteral("Output a resource file containing all files from the current directory.")); parser.addOption(projectOption); @@ -266,6 +301,7 @@ int runRcc(int argc, char *argv[]) QString outFilename = parser.value(outputOption); QString tempFilename = parser.value(tempOption); + QString depFilename = parser.value(depFileOption); if (projectRequested) { return createProject(outFilename); @@ -352,6 +388,28 @@ int runRcc(int argc, char *argv[]) return 0; } + // Write depfile + if (!depFilename.isEmpty()) { + QFile depout; + depout.setFileName(depFilename); + + if (outFilename.isEmpty() || outFilename == QLatin1String("-")) { + const QString msg = QString::fromUtf8("Unable to write depfile when outputting to stdout!\n"); + errorDevice.write(msg.toUtf8()); + return 1; + } + + if (!depout.open(QIODevice::WriteOnly | QIODevice::Text)) { + const QString msg = QString::fromUtf8("Unable to open depfile %1 for writing: %2\n") + .arg(depout.fileName(), depout.errorString()); + errorDevice.write(msg.toUtf8()); + return 1; + } + + writeDepFile(depout, library.dataFiles(), outFilename); + depout.close(); + } + QFile temp; if (!tempFilename.isEmpty()) { temp.setFileName(tempFilename); -- cgit v1.2.3 From 282c030785498d94c3ccee59a1a201f090e69248 Mon Sep 17 00:00:00 2001 From: Tasuku Suzuki Date: Mon, 13 May 2019 16:13:08 +0900 Subject: Remove property usage in QHttp2ProtocolHandler This helps to fix build without feature.properties Change-Id: Ia1fd2a1ca88105048e75694874058bb1292899a0 Reviewed-by: Timur Pocheptsov --- src/network/access/qhttp2protocolhandler.cpp | 24 ++++++++++++++++++------ src/network/access/qhttp2protocolhandler_p.h | 2 ++ 2 files changed, 20 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/network/access/qhttp2protocolhandler.cpp b/src/network/access/qhttp2protocolhandler.cpp index 87a70d8a55..93afcf0ee1 100644 --- a/src/network/access/qhttp2protocolhandler.cpp +++ b/src/network/access/qhttp2protocolhandler.cpp @@ -219,7 +219,8 @@ void QHttp2ProtocolHandler::_q_uploadDataReadyRead() auto data = qobject_cast(sender()); Q_ASSERT(data); - const qint32 streamID = data->property("HTTP2StreamID").toInt(); + const qint32 streamID = streamIDs.value(data); + Q_ASSERT(streamID != 0); Q_ASSERT(activeStreams.contains(streamID)); auto &stream = activeStreams[streamID]; @@ -234,7 +235,7 @@ void QHttp2ProtocolHandler::_q_uploadDataReadyRead() void QHttp2ProtocolHandler::_q_replyDestroyed(QObject *reply) { - const quint32 streamID = reply->property("HTTP2StreamID").toInt(); + const quint32 streamID = streamIDs.take(reply); if (activeStreams.contains(streamID)) { sendRST_STREAM(streamID, CANCEL); markAsReset(streamID); @@ -242,6 +243,11 @@ void QHttp2ProtocolHandler::_q_replyDestroyed(QObject *reply) } } +void QHttp2ProtocolHandler::_q_uploadDataDestroyed(QObject *uploadData) +{ + streamIDs.remove(uploadData); +} + void QHttp2ProtocolHandler::_q_readyRead() { _q_receiveReply(); @@ -1249,7 +1255,7 @@ quint32 QHttp2ProtocolHandler::createNewStream(const HttpMessagePair &message, b replyPrivate->connection = m_connection; replyPrivate->connectionChannel = m_channel; reply->setSpdyWasUsed(true); - reply->setProperty("HTTP2StreamID", newStreamID); + streamIDs.insert(reply, newStreamID); connect(reply, SIGNAL(destroyed(QObject*)), this, SLOT(_q_replyDestroyed(QObject*))); @@ -1261,7 +1267,9 @@ quint32 QHttp2ProtocolHandler::createNewStream(const HttpMessagePair &message, b if (auto src = newStream.data()) { connect(src, SIGNAL(readyRead()), this, SLOT(_q_uploadDataReadyRead()), Qt::QueuedConnection); - src->setProperty("HTTP2StreamID", newStreamID); + connect(src, &QHttp2ProtocolHandler::destroyed, + this, &QHttp2ProtocolHandler::_q_uploadDataDestroyed); + streamIDs.insert(src, newStreamID); } } @@ -1343,10 +1351,14 @@ void QHttp2ProtocolHandler::deleteActiveStream(quint32 streamID) { if (activeStreams.contains(streamID)) { auto &stream = activeStreams[streamID]; - if (stream.reply()) + if (stream.reply()) { stream.reply()->disconnect(this); - if (stream.data()) + streamIDs.remove(stream.reply()); + } + if (stream.data()) { stream.data()->disconnect(this); + streamIDs.remove(stream.data()); + } activeStreams.remove(streamID); } diff --git a/src/network/access/qhttp2protocolhandler_p.h b/src/network/access/qhttp2protocolhandler_p.h index 9165808302..b582123961 100644 --- a/src/network/access/qhttp2protocolhandler_p.h +++ b/src/network/access/qhttp2protocolhandler_p.h @@ -93,6 +93,7 @@ public: private slots: void _q_uploadDataReadyRead(); void _q_replyDestroyed(QObject* reply); + void _q_uploadDataDestroyed(QObject* uploadData); private: using Stream = Http2::Stream; @@ -156,6 +157,7 @@ private: HPack::Decoder decoder; HPack::Encoder encoder; + QHash streamIDs; QHash activeStreams; std::deque suspendedStreams[3]; // 3 for priorities: High, Normal, Low. static const std::deque::size_type maxRecycledStreams; -- cgit v1.2.3 From 7ffb5b2c8c1d374d8181ebcd288b869c43fe5b3a Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Wed, 22 May 2019 12:25:51 -0700 Subject: Fix Clang warning about applying operator|| to non-boolean qmetatype_p.h:111:412: error: use of logical '||' with constant operand [-Werror,-Wconstant-logical-operand] Change-Id: Iac6ae11e29bd4169bae9fffd15a117d576a95adb Reviewed-by: Ville Voutilainen --- src/corelib/kernel/qmetatype_p.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/corelib/kernel/qmetatype_p.h b/src/corelib/kernel/qmetatype_p.h index 94e9228778..eba0207ea7 100644 --- a/src/corelib/kernel/qmetatype_p.h +++ b/src/corelib/kernel/qmetatype_p.h @@ -74,7 +74,7 @@ template class QTypeModuleInfo { public: - enum Module { + enum Module : bool { IsCore = false, IsWidget = false, IsGui = false, @@ -87,7 +87,7 @@ template<> \ class QTypeModuleInfo \ { \ public: \ - enum Module { \ + enum Module : bool { \ IsCore = (((MODULE) == (QModulesPrivate::Core))), \ IsWidget = (((MODULE) == (QModulesPrivate::Widgets))), \ IsGui = (((MODULE) == (QModulesPrivate::Gui))), \ -- cgit v1.2.3 From 89d28ebe86e3e3912b4811f344c728e053bab122 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 27 Jun 2019 19:20:04 -0700 Subject: QTransposeProxyModel: include the moc in the .cpp All the other QtCore files do. Change-Id: Ie7ae7616eadf4035bec6fffd15ac3b5479716ff3 Reviewed-by: Marc Mutz --- src/corelib/itemmodels/qtransposeproxymodel.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/corelib/itemmodels/qtransposeproxymodel.cpp b/src/corelib/itemmodels/qtransposeproxymodel.cpp index d4f379bc64..4853f90632 100644 --- a/src/corelib/itemmodels/qtransposeproxymodel.cpp +++ b/src/corelib/itemmodels/qtransposeproxymodel.cpp @@ -445,3 +445,5 @@ void QTransposeProxyModel::sort(int column, Qt::SortOrder order) } QT_END_NAMESPACE + +#include "moc_qtransposeproxymodel.cpp" -- cgit v1.2.3 From d62ee142839ff78134d6d4a48acfdd11040ceeae Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Wed, 15 May 2019 07:48:49 -0700 Subject: Work around Apple Clang's -Wshadow warning Well, yeah, it technically does... qcborstream.h:245:15: warning: declaration shadows a typedef in the global namespace [-Wshadow] /usr/include/libkern/OSTypes.h:36:26: note: previous declaration is here Fixes: QTBUG-75825 Change-Id: Idce141629dd34287808bfffd159ee2a75428bf12 Reviewed-by: Marc Mutz --- src/corelib/serialization/qcborstream.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/corelib/serialization/qcborstream.h b/src/corelib/serialization/qcborstream.h index 3b13a309ab..7a451e63ac 100644 --- a/src/corelib/serialization/qcborstream.h +++ b/src/corelib/serialization/qcborstream.h @@ -242,8 +242,8 @@ private: template FP _toFloatingPoint() const noexcept { - using UInt = typename QIntegerForSizeof::Unsigned; - UInt u = UInt(value64); + using UIntFP = typename QIntegerForSizeof::Unsigned; + UIntFP u = UIntFP(value64); FP f; memcpy(static_cast(&f), &u, sizeof(f)); return f; -- cgit v1.2.3 From b321559a22a3ee6490695ecb9d95d3c9fbf7ee1c Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 29 Apr 2019 11:13:04 -0700 Subject: Document OOM conditions in the QArrayData-based containers The other containers probably don't handle as well, so I just documented the ones that I know how they work. Fixes: QTBUG-75470 Change-Id: I95ecabe2f50e450c991afffd159a0483aac35a79 Reviewed-by: Allan Sandfeld Jensen Reviewed-by: Albert Astals Cid Reviewed-by: Thiago Macieira --- src/corelib/tools/qbytearray.cpp | 17 +++++++++++++++++ src/corelib/tools/qstring.cpp | 18 ++++++++++++++++++ src/corelib/tools/qvector.qdoc | 18 ++++++++++++++++++ 3 files changed, 53 insertions(+) (limited to 'src') diff --git a/src/corelib/tools/qbytearray.cpp b/src/corelib/tools/qbytearray.cpp index 6cc41a8956..c2c2b9de28 100644 --- a/src/corelib/tools/qbytearray.cpp +++ b/src/corelib/tools/qbytearray.cpp @@ -1043,6 +1043,23 @@ QByteArray qUncompress(const uchar* data, int nbytes) and QByteArray() compares equal to QByteArray(""). We recommend that you always use isEmpty() and avoid isNull(). + \section1 Maximum size and out-of-memory conditions + + The current version of QByteArray is limited to just under 2 GB (2^31 + bytes) in size. The exact value is architecture-dependent, since it depends + on the overhead required for managing the data block, but is no more than + 32 bytes. Raw data blocks are also limited by the use of \c int type in the + current version to 2 GB minus 1 byte. + + In case memory allocation fails, QByteArray will throw a \c std::bad_alloc + exception. Out of memory conditions in the Qt containers are the only case + where Qt will throw exceptions. + + Note that the operating system may impose further limits on applications + holding a lot of allocated memory, especially large, contiguous blocks. + Such considerations, the configuration of such behavior or any mitigation + are outside the scope of the QByteArray API. + \section1 Notes on Locale \section2 Number-String Conversions diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index ee9d486eb8..a0e33d4e45 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -1777,6 +1777,24 @@ const QString::Null QString::null = { }; and the \c{'+'} will automatically be performed as the \c{QStringBuilder} \c{'%'} everywhere. + \section1 Maximum size and out-of-memory conditions + + The current version of QString is limited to just under 2 GB (2^31 bytes) + in size. The exact value is architecture-dependent, since it depends on the + overhead required for managing the data block, but is no more than 32 + bytes. Raw data blocks are also limited by the use of \c int type in the + current version to 2 GB minus 1 byte. Since QString uses two bytes per + character, that translates to just under 2^30 characters in one QString. + + In case memory allocation fails, QString will throw a \c std::bad_alloc + exception. Out of memory conditions in the Qt containers are the only case + where Qt will throw exceptions. + + Note that the operating system may impose further limits on applications + holding a lot of allocated memory, especially large, contiguous blocks. + Such considerations, the configuration of such behavior or any mitigation + are outside the scope of the Qt API. + \sa fromRawData(), QChar, QLatin1String, QByteArray, QStringRef */ diff --git a/src/corelib/tools/qvector.qdoc b/src/corelib/tools/qvector.qdoc index 75b17a4207..ad5821e73b 100644 --- a/src/corelib/tools/qvector.qdoc +++ b/src/corelib/tools/qvector.qdoc @@ -173,6 +173,24 @@ For a detailed discussion comparing Qt containers with each other and with STL containers, see \l {Understand the Qt Containers}. + \section1 Maximum size and out-of-memory conditions + + The current version of QVector is limited to just under 2 GB (2^31 bytes) + in size. The exact value is architecture-dependent, since it depends on the + overhead required for managing the data block, but is no more than 32 + bytes. The number of elements that can be stored in a QVector is that size + divided by the size of each element. + + In case memory allocation fails, QVector will use the \l Q_CHECK_PTR macro, + which will throw a \c std::bad_alloc exception if the application is being + compiled with exception support. If exceptions are disabled, then running + out of memory is undefined behavior. + + Note that the operating system may impose further limits on applications + holding a lot of allocated memory, especially large, contiguous blocks. + Such considerations, the configuration of such behavior or any mitigation + are outside the scope of the Qt API. + \sa QVectorIterator, QMutableVectorIterator, QList, QLinkedList */ -- cgit v1.2.3 From 227c1a56ecb8b99cbd64bd6b8335b3cc3c8a21f1 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sun, 14 Apr 2019 08:20:31 -0700 Subject: Move the Item typedef to public in the associative Java iterators The documentation talks about them. They're just iterators. Fixes: QTBUG-75123 Change-Id: I194d3f37471a49788a7bfffd15956064b42383b7 Reviewed-by: Lars Knoll --- src/corelib/tools/qiterator.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/corelib/tools/qiterator.h b/src/corelib/tools/qiterator.h index 586d26cbad..177a4f1a4c 100644 --- a/src/corelib/tools/qiterator.h +++ b/src/corelib/tools/qiterator.h @@ -115,11 +115,11 @@ template \ class Q##C##Iterator \ { \ typedef typename Q##C::const_iterator const_iterator; \ - typedef const_iterator Item; \ Q##C c; \ const_iterator i, n; \ inline bool item_exists() const { return n != c.constEnd(); } \ public: \ + typedef const_iterator Item; \ inline Q##C##Iterator(const Q##C &container) \ : c(container), i(c.constBegin()), n(c.constEnd()) {} \ inline Q##C##Iterator &operator=(const Q##C &container) \ @@ -148,11 +148,11 @@ class QMutable##C##Iterator \ { \ typedef typename Q##C::iterator iterator; \ typedef typename Q##C::const_iterator const_iterator; \ - typedef iterator Item; \ Q##C *c; \ iterator i, n; \ inline bool item_exists() const { return const_iterator(n) != c->constEnd(); } \ public: \ + typedef iterator Item; \ inline QMutable##C##Iterator(Q##C &container) \ : c(&container) \ { i = c->begin(); n = c->end(); } \ -- cgit v1.2.3 From a39b19b0c7419021b3c22dc4d4bced0995f3a29f Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Wed, 27 Mar 2019 11:18:37 -0700 Subject: QTranslator: simplify QString byte-swapping code No need to check QSysInfo, just use qFromBigEndian. On big-endian systems, it does the memcpy for us. Change-Id: I1004b4b819774c4c9296fffd158fe3aa5ff0a287 Reviewed-by: Oswald Buddenhagen Reviewed-by: Thiago Macieira --- src/corelib/kernel/qtranslator.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/corelib/kernel/qtranslator.cpp b/src/corelib/kernel/qtranslator.cpp index dc0ab9f08a..637ef84d21 100644 --- a/src/corelib/kernel/qtranslator.cpp +++ b/src/corelib/kernel/qtranslator.cpp @@ -948,11 +948,8 @@ static QString getMessage(const uchar *m, const uchar *end, const char *context, end: if (!tn) return QString(); - QString str = QString((const QChar *)tn, tn_length/2); - if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) { - QChar *data = str.data(); - qbswap(data, str.length(), data); - } + QString str(tn_length / 2, Qt::Uninitialized); + qFromBigEndian(tn, str.length(), str.data()); return str; } -- cgit v1.2.3 From 5d7e221bbf9e27e90448243909abc76d81733381 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 29 Mar 2019 12:49:47 -0700 Subject: QRandomGenerator: assert that bounded() calls have correct arguments Otherwise, the math will fail badly. Documentation improved to reflect reality. Change-Id: I9e3d261ad9bf41cfb2b6fffd159085cd38e3c388 Reviewed-by: Thiago Macieira --- src/corelib/global/qrandom.cpp | 16 ++++++++++++---- src/corelib/global/qrandom.h | 10 ++++++---- 2 files changed, 18 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/corelib/global/qrandom.cpp b/src/corelib/global/qrandom.cpp index 90df8653a7..bf01b7ae2a 100644 --- a/src/corelib/global/qrandom.cpp +++ b/src/corelib/global/qrandom.cpp @@ -903,6 +903,10 @@ inline QRandomGenerator::SystemGenerator &QRandomGenerator::SystemGenerator::sel \snippet code/src_corelib_global_qrandom.cpp 12 + If the \a highest parameter is negative, the result will be negative too; + if it is infinite or NaN, the result will be infinite or NaN too (that is, + not random). + \sa generateDouble(), bounded() */ @@ -934,7 +938,7 @@ inline QRandomGenerator::SystemGenerator &QRandomGenerator::SystemGenerator::sel \overload Generates one random 32-bit quantity in the range between 0 (inclusive) and - \a highest (exclusive). \a highest must not be negative. + \a highest (exclusive). \a highest must be positive. Note that this function cannot be used to obtain values in the full 32-bit range of int. Instead, use generate() and cast to int. @@ -946,8 +950,11 @@ inline QRandomGenerator::SystemGenerator &QRandomGenerator::SystemGenerator::sel \fn quint32 QRandomGenerator::bounded(quint32 lowest, quint32 highest) \overload - Generates one random 32-bit quantity in the range between \a lowest (inclusive) - and \a highest (exclusive). The same result may also be obtained by using + Generates one random 32-bit quantity in the range between \a lowest + (inclusive) and \a highest (exclusive). The \a highest parameter must be + greater than \a lowest. + + The same result may also be obtained by using \l{http://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution}{\c std::uniform_int_distribution} with parameters \a lowest and \c{\a highest - 1}. That class can also be used to obtain quantities larger than 32 bits. @@ -968,7 +975,8 @@ inline QRandomGenerator::SystemGenerator &QRandomGenerator::SystemGenerator::sel \overload Generates one random 32-bit quantity in the range between \a lowest - (inclusive) and \a highest (exclusive), both of which may be negative. + (inclusive) and \a highest (exclusive), both of which may be negative, but + \a highest must be greater than \a lowest. Note that this function cannot be used to obtain values in the full 32-bit range of int. Instead, use generate() and cast to int. diff --git a/src/corelib/global/qrandom.h b/src/corelib/global/qrandom.h index 46d3e0e152..260fc1501a 100644 --- a/src/corelib/global/qrandom.h +++ b/src/corelib/global/qrandom.h @@ -122,14 +122,16 @@ public: return quint32(value); } - int bounded(int highest) + quint32 bounded(quint32 lowest, quint32 highest) { - return int(bounded(quint32(highest))); + Q_ASSERT(highest > lowest); + return bounded(highest - lowest) + lowest; } - quint32 bounded(quint32 lowest, quint32 highest) + int bounded(int highest) { - return bounded(highest - lowest) + lowest; + Q_ASSERT(highest > 0); + return int(bounded(0U, quint32(highest))); } int bounded(int lowest, int highest) -- cgit v1.2.3 From 66223727c708e48009cbf297721aa2d0134b48d2 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Wed, 26 Jun 2019 07:56:49 +0200 Subject: Port from implicit to explicit atomic pointer operations The old code used the implicit conversions from QAtomicPointer to T* and vice versa. The semantics of these differ from the ones std::atomic uses, so we're going to deprecate these, like we did for load() and store(), too. This patch fixex some users of these APIs before we deprecate them. Change-Id: I0a88bb1c359392538bb64b511bfc62381a56a468 Reviewed-by: Thiago Macieira --- src/3rdparty/forkfd/forkfd.c | 4 ++-- src/corelib/io/qprocess_unix.cpp | 2 +- src/corelib/kernel/qcoreapplication.cpp | 18 +++++++++--------- src/corelib/kernel/qeventloop.cpp | 2 +- src/corelib/kernel/qobject.cpp | 26 +++++++++++++------------- src/corelib/kernel/qobject_p.h | 2 +- src/corelib/thread/qthread.cpp | 12 ++++++------ src/corelib/thread/qthread_unix.cpp | 8 ++++---- src/widgets/kernel/qapplication.cpp | 2 +- 9 files changed, 38 insertions(+), 38 deletions(-) (limited to 'src') diff --git a/src/3rdparty/forkfd/forkfd.c b/src/3rdparty/forkfd/forkfd.c index 1cbe0da771..12537b6199 100644 --- a/src/3rdparty/forkfd/forkfd.c +++ b/src/3rdparty/forkfd/forkfd.c @@ -519,9 +519,9 @@ static void cleanup() ffd_atomic_store(&forkfd_status, 0, FFD_ATOMIC_RELAXED); /* free any arrays we might have */ - array = children.header.nextArray; + array = ffd_atomic_load(&children.header.nextArray, FFD_ATOMIC_ACQUIRE); while (array != NULL) { - BigArray *next = array->header.nextArray; + BigArray *next = ffd_atomic_load(&array->header.nextArray, FFD_ATOMIC_ACQUIRE); free(array); array = next; } diff --git a/src/corelib/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp index 3a29a0d842..1d5b76a8a4 100644 --- a/src/corelib/io/qprocess_unix.cpp +++ b/src/corelib/io/qprocess_unix.cpp @@ -517,7 +517,7 @@ void QProcessPrivate::startProcess() if (stderrChannel.pipe[0] != -1) ::fcntl(stderrChannel.pipe[0], F_SETFL, ::fcntl(stderrChannel.pipe[0], F_GETFL) | O_NONBLOCK); - if (threadData->eventDispatcher) { + if (threadData->eventDispatcher.loadAcquire()) { deathNotifier = new QSocketNotifier(forkfd, QSocketNotifier::Read, q); QObject::connect(deathNotifier, SIGNAL(activated(int)), q, SLOT(_q_processDied())); diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index 5d300a0488..e5b88c3c26 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -385,8 +385,8 @@ struct QCoreApplicationData { ~QCoreApplicationData() { #ifndef QT_NO_QOBJECT // cleanup the QAdoptedThread created for the main() thread - if (QCoreApplicationPrivate::theMainThread) { - QThreadData *data = QThreadData::get2(QCoreApplicationPrivate::theMainThread); + if (auto *t = QCoreApplicationPrivate::theMainThread.loadAcquire()) { + QThreadData *data = QThreadData::get2(t); data->deref(); // deletes the data and the adopted thread } #endif @@ -486,7 +486,7 @@ QCoreApplicationPrivate::QCoreApplicationPrivate(int &aargc, char **aargv, uint #endif QThread *cur = QThread::currentThread(); // note: this may end up setting theMainThread! - if (cur != theMainThread) + if (cur != theMainThread.loadAcquire()) qWarning("WARNING: QApplication was not created in the main() thread."); #endif } @@ -862,7 +862,7 @@ void QCoreApplicationPrivate::init() Q_ASSERT(eventDispatcher); if (!eventDispatcher->parent()) { - eventDispatcher->moveToThread(threadData->thread); + eventDispatcher->moveToThread(threadData->thread.loadAcquire()); eventDispatcher->setParent(q); } @@ -1181,7 +1181,7 @@ static bool doNotify(QObject *receiver, QEvent *event) bool QCoreApplicationPrivate::sendThroughApplicationEventFilters(QObject *receiver, QEvent *event) { // We can't access the application event filters outside of the main thread (race conditions) - Q_ASSERT(receiver->d_func()->threadData->thread == mainThread()); + Q_ASSERT(receiver->d_func()->threadData->thread.loadAcquire() == mainThread()); if (extraData) { // application event filters are only called for objects in the GUI thread @@ -1234,7 +1234,7 @@ bool QCoreApplicationPrivate::notify_helper(QObject *receiver, QEvent * event) // send to all application event filters (only does anything in the main thread) if (QCoreApplication::self - && receiver->d_func()->threadData->thread == mainThread() + && receiver->d_func()->threadData->thread.loadAcquire() == mainThread() && QCoreApplication::self->d_func()->sendThroughApplicationEventFilters(receiver, event)) { filtered = true; return filtered; @@ -2905,7 +2905,7 @@ void QCoreApplication::installNativeEventFilter(QAbstractNativeEventFilter *filt return; } - QAbstractEventDispatcher *eventDispatcher = QAbstractEventDispatcher::instance(QCoreApplicationPrivate::theMainThread); + QAbstractEventDispatcher *eventDispatcher = QAbstractEventDispatcher::instance(QCoreApplicationPrivate::theMainThread.loadAcquire()); if (!filterObj || !eventDispatcher) return; eventDispatcher->installNativeEventFilter(filterObj); @@ -2961,7 +2961,7 @@ bool QCoreApplication::hasPendingEvents() */ QAbstractEventDispatcher *QCoreApplication::eventDispatcher() { - if (QCoreApplicationPrivate::theMainThread) + if (QCoreApplicationPrivate::theMainThread.loadAcquire()) return QCoreApplicationPrivate::theMainThread.loadRelaxed()->eventDispatcher(); return 0; } @@ -2974,7 +2974,7 @@ QAbstractEventDispatcher *QCoreApplication::eventDispatcher() */ void QCoreApplication::setEventDispatcher(QAbstractEventDispatcher *eventDispatcher) { - QThread *mainThread = QCoreApplicationPrivate::theMainThread; + QThread *mainThread = QCoreApplicationPrivate::theMainThread.loadAcquire(); if (!mainThread) mainThread = QThread::currentThread(); // will also setup theMainThread mainThread->setEventDispatcher(eventDispatcher); diff --git a/src/corelib/kernel/qeventloop.cpp b/src/corelib/kernel/qeventloop.cpp index 2104b22095..eacd0c4e73 100644 --- a/src/corelib/kernel/qeventloop.cpp +++ b/src/corelib/kernel/qeventloop.cpp @@ -165,7 +165,7 @@ int QEventLoop::exec(ProcessEventsFlags flags) { Q_D(QEventLoop); //we need to protect from race condition with QThread::exit - QMutexLocker locker(&static_cast(QObjectPrivate::get(d->threadData->thread))->mutex); + QMutexLocker locker(&static_cast(QObjectPrivate::get(d->threadData->thread.loadAcquire()))->mutex); if (d->threadData->quitNow) return -1; diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 1540ebfe12..9a2ae36177 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -219,7 +219,7 @@ QObjectPrivate::QObjectPrivate(int version) QObjectPrivate::~QObjectPrivate() { if (extraData && !extraData->runningTimers.isEmpty()) { - if (Q_LIKELY(threadData->thread == QThread::currentThread())) { + if (Q_LIKELY(threadData->thread.loadAcquire() == QThread::currentThread())) { // unregister pending timers if (threadData->hasEventDispatcher()) threadData->eventDispatcher.loadRelaxed()->unregisterTimers(q_ptr); @@ -493,11 +493,11 @@ bool QObjectPrivate::maybeSignalConnected(uint signalIndex) const if (!signalVector) return false; - if (signalVector->at(-1).first) + if (signalVector->at(-1).first.loadAcquire()) return true; if (signalIndex < uint(cd->signalVectorCount())) { - const QObjectPrivate::Connection *c = signalVector->at(signalIndex).first; + const QObjectPrivate::Connection *c = signalVector->at(signalIndex).first.loadAcquire(); return c != nullptr; } return false; @@ -819,8 +819,8 @@ static bool check_parent_thread(QObject *parent, QThreadData *currentThreadData) { if (parent && parentThreadData != currentThreadData) { - QThread *parentThread = parentThreadData->thread; - QThread *currentThread = currentThreadData->thread; + QThread *parentThread = parentThreadData->thread.loadAcquire(); + QThread *currentThread = currentThreadData->thread.loadAcquire(); qWarning("QObject: Cannot create children for a parent that is in a different thread.\n" "(Parent is %s(%p), parent's thread is %s(%p), current thread is %s(%p)", parent->metaObject()->className(), @@ -987,11 +987,11 @@ QObject::~QObject() QObjectPrivate::ConnectionList &connectionList = cd->connectionsForSignal(signal); while (QObjectPrivate::Connection *c = connectionList.first.loadRelaxed()) { - Q_ASSERT(c->receiver); + Q_ASSERT(c->receiver.loadAcquire()); QBasicMutex *m = signalSlotLock(c->receiver.loadRelaxed()); bool needToUnlock = QOrderedMutexLocker::relock(signalSlotMutex, m); - if (c->receiver) { + if (c->receiver.loadAcquire()) { cd->removeConnection(c); Q_ASSERT(connectionList.first.loadRelaxed() != c); } @@ -1003,7 +1003,7 @@ QObject::~QObject() /* Disconnect all senders: */ while (QObjectPrivate::Connection *node = cd->senders) { - Q_ASSERT(node->receiver); + Q_ASSERT(node->receiver.loadAcquire()); QObject *sender = node->sender; // Send disconnectNotify before removing the connection from sender's connection list. // This ensures any eventual destructor of sender will block on getting receiver's lock @@ -1453,7 +1453,7 @@ bool QObject::blockSignals(bool block) noexcept */ QThread *QObject::thread() const { - return d_func()->threadData->thread; + return d_func()->threadData->thread.loadAcquire(); } /*! @@ -1500,7 +1500,7 @@ void QObject::moveToThread(QThread *targetThread) { Q_D(QObject); - if (d->threadData->thread == targetThread) { + if (d->threadData->thread.loadAcquire() == targetThread) { // object is already in this thread return; } @@ -1516,7 +1516,7 @@ void QObject::moveToThread(QThread *targetThread) QThreadData *currentData = QThreadData::current(); QThreadData *targetData = targetThread ? QThreadData::get2(targetThread) : nullptr; - if (d->threadData->thread == 0 && currentData == targetData) { + if (d->threadData->thread.loadAcquire() == 0 && currentData == targetData) { // one exception to the rule: we allow moving objects with no thread affinity to the current thread currentData = d->threadData; } else if (d->threadData != currentData) { @@ -3716,7 +3716,7 @@ void doActivate(QObject *sender, int signal_index, void **argv) bool senderDeleted = false; { - Q_ASSERT(sp->connections); + Q_ASSERT(sp->connections.loadAcquire()); QObjectPrivate::ConnectionDataPointer connections(sp->connections.loadRelaxed()); QObjectPrivate::SignalVector *signalVector = connections->signalVector.loadRelaxed(); @@ -3773,7 +3773,7 @@ void doActivate(QObject *sender, int signal_index, void **argv) QSemaphore semaphore; { QBasicMutexLocker locker(signalSlotLock(sender)); - if (!c->receiver) + if (!c->receiver.loadAcquire()) continue; QMetaCallEvent *ev = c->isSlotObject ? new QMetaCallEvent(c->slotObj, sender, signal_index, 0, 0, argv, &semaphore) : diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h index 247c7b1501..9cf1bfed08 100644 --- a/src/corelib/kernel/qobject_p.h +++ b/src/corelib/kernel/qobject_p.h @@ -312,7 +312,7 @@ public: } } int signalVectorCount() const { - return signalVector ? signalVector.loadRelaxed()->count() : -1; + return signalVector.loadAcquire() ? signalVector.loadRelaxed()->count() : -1; } static void deleteOrphaned(ConnectionOrSignalVector *c); diff --git a/src/corelib/thread/qthread.cpp b/src/corelib/thread/qthread.cpp index 1eac7db11d..fe555afca7 100644 --- a/src/corelib/thread/qthread.cpp +++ b/src/corelib/thread/qthread.cpp @@ -73,8 +73,8 @@ QThreadData::~QThreadData() // crashing during QCoreApplicationData's global static cleanup we need to // safeguard the main thread here.. This fix is a bit crude, but it solves // the problem... - if (this->thread == QCoreApplicationPrivate::theMainThread) { - QCoreApplicationPrivate::theMainThread = 0; + if (this->thread.loadAcquire() == QCoreApplicationPrivate::theMainThread.loadAcquire()) { + QCoreApplicationPrivate::theMainThread.storeRelease(nullptr); QThreadData::clearCurrentThreadData(); } @@ -85,8 +85,8 @@ QThreadData::~QThreadData() // because this destructor is still running (the _ref sub-object has not // been destroyed) and there's no reentrancy. The refcount will become // negative, but that's acceptable. - QThread *t = thread; - thread = 0; + QThread *t = thread.loadAcquire(); + thread.storeRelease(nullptr); delete t; for (int i = 0; i < postEventList.size(); ++i) { @@ -393,7 +393,7 @@ QThread *QThread::currentThread() { QThreadData *data = QThreadData::current(); Q_ASSERT(data != 0); - return data->thread; + return data->thread.loadAcquire(); } /*! @@ -980,7 +980,7 @@ bool QThread::event(QEvent *event) void QThread::requestInterruption() { - if (this == QCoreApplicationPrivate::theMainThread) { + if (this == QCoreApplicationPrivate::theMainThread.loadAcquire()) { qWarning("QThread::requestInterruption has no effect on the main thread"); return; } diff --git a/src/corelib/thread/qthread_unix.cpp b/src/corelib/thread/qthread_unix.cpp index 89efbff6aa..38e9c1c3ec 100644 --- a/src/corelib/thread/qthread_unix.cpp +++ b/src/corelib/thread/qthread_unix.cpp @@ -141,7 +141,7 @@ static void destroy_current_thread_data(void *p) pthread_setspecific(current_thread_data_key, p); QThreadData *data = static_cast(p); if (data->isAdopted) { - QThread *thread = data->thread; + QThread *thread = data->thread.loadAcquire(); Q_ASSERT(thread); QThreadPrivate *thread_p = static_cast(QObjectPrivate::get(thread)); Q_ASSERT(!thread_p->finished); @@ -253,8 +253,8 @@ QThreadData *QThreadData::current(bool createIfNecessary) data->deref(); data->isAdopted = true; data->threadId.storeRelaxed(to_HANDLE(pthread_self())); - if (!QCoreApplicationPrivate::theMainThread) - QCoreApplicationPrivate::theMainThread = data->thread.loadRelaxed(); + if (!QCoreApplicationPrivate::theMainThread.loadAcquire()) + QCoreApplicationPrivate::theMainThread.storeRelease(data->thread.loadRelaxed()); } return data; } @@ -285,7 +285,7 @@ QAbstractEventDispatcher *QThreadPrivate::createEventDispatcher(QThreadData *dat else return new QEventDispatcherUNIX; #elif !defined(QT_NO_GLIB) - const bool isQtMainThread = data->thread == QCoreApplicationPrivate::mainThread(); + const bool isQtMainThread = data->thread.loadAcquire() == QCoreApplicationPrivate::mainThread(); if (qEnvironmentVariableIsEmpty("QT_NO_GLIB") && (isQtMainThread || qEnvironmentVariableIsEmpty("QT_NO_THREADED_GLIB")) && QEventDispatcherGlib::versionSupported()) diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index 22ebeaae51..58f63c0fed 100644 --- a/src/widgets/kernel/qapplication.cpp +++ b/src/widgets/kernel/qapplication.cpp @@ -3666,7 +3666,7 @@ bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e) // send to all application event filters if (threadRequiresCoreApplication() - && receiver->d_func()->threadData->thread == mainThread() + && receiver->d_func()->threadData->thread.loadAcquire() == mainThread() && sendThroughApplicationEventFilters(receiver, e)) { filtered = true; return filtered; -- cgit v1.2.3 From 56962604254a31d5d54ea41044a5c251ff27fa0c Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Wed, 26 Jun 2019 17:41:59 +0200 Subject: QObject: fix allocation of IDs for the undocumented userData() feature This appears to have come to some fame on the internet, so better fix the implementation before we remove it in Qt 6. Change-Id: Ia37ca89105b13bea1ffcdce8b2e8cd957b7bd108 Reviewed-by: Thiago Macieira --- src/corelib/kernel/qobject.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 9a2ae36177..69556ba9e3 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -4187,13 +4187,14 @@ void QObject::dumpObjectInfo() const } #ifndef QT_NO_USERDATA +static QBasicAtomicInteger user_data_registration = Q_BASIC_ATOMIC_INITIALIZER(0); + /*! \internal */ uint QObject::registerUserData() { - static int user_data_registration = 0; - return user_data_registration++; + return user_data_registration.fetchAndAddRelaxed(1); } /*! -- cgit v1.2.3 From a27e7e815175bc9417aaf7377b349109afd235e4 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Wed, 26 Jun 2019 17:41:59 +0200 Subject: QObject: deprecate the undocumented userData() feature ... and schedule it for removal in Qt 6. This appears to have come to some fame on the internet, so better add a deprecation warning before we remove it in Qt 6. Change-Id: I42d91d933f47dfd2d8d54c92358e9e46ced6bf21 Reviewed-by: Thiago Macieira Reviewed-by: Lars Knoll --- src/corelib/kernel/qobject.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src') diff --git a/src/corelib/kernel/qobject.h b/src/corelib/kernel/qobject.h index 7f72b69c1a..240ace1e27 100644 --- a/src/corelib/kernel/qobject.h +++ b/src/corelib/kernel/qobject.h @@ -77,6 +77,9 @@ class QRegExp; #if QT_CONFIG(regularexpression) class QRegularExpression; #endif +#if !QT_DEPRECATED_SINCE(5, 14) || QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +# define QT_NO_USERDATA +#endif #ifndef QT_NO_USERDATA class QObjectUserData; #endif @@ -405,8 +408,11 @@ public: #endif // QT_NO_PROPERTIES #ifndef QT_NO_USERDATA + QT_DEPRECATED_VERSION_5_14 static uint registerUserData(); + QT_DEPRECATED_VERSION_X_5_14("Use setProperty()") void setUserData(uint id, QObjectUserData* data); + QT_DEPRECATED_VERSION_X_5_14("Use property()") QObjectUserData* userData(uint id) const; #endif // QT_NO_USERDATA -- cgit v1.2.3 From 840f55f0ae9f0911e963c492d12944c5b4c12c60 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Sat, 29 Jun 2019 09:08:34 +0200 Subject: Remove unused qmutexpool_p.h includes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ic5f56b12953f2e42d94f6e6c14fac166c0c75539 Reviewed-by: Mårten Nordheim --- src/corelib/kernel/qeventdispatcher_win.cpp | 1 - src/network/kernel/qauthenticator.cpp | 1 - src/network/kernel/qdnslookup_win.cpp | 1 - 3 files changed, 3 deletions(-) (limited to 'src') diff --git a/src/corelib/kernel/qeventdispatcher_win.cpp b/src/corelib/kernel/qeventdispatcher_win.cpp index c2e57a7924..dea6cf6389 100644 --- a/src/corelib/kernel/qeventdispatcher_win.cpp +++ b/src/corelib/kernel/qeventdispatcher_win.cpp @@ -52,7 +52,6 @@ #include "qelapsedtimer.h" #include "qcoreapplication_p.h" #include -#include #include QT_BEGIN_NAMESPACE diff --git a/src/network/kernel/qauthenticator.cpp b/src/network/kernel/qauthenticator.cpp index 3ca8806c2b..4100dfd784 100644 --- a/src/network/kernel/qauthenticator.cpp +++ b/src/network/kernel/qauthenticator.cpp @@ -52,7 +52,6 @@ #ifdef Q_OS_WIN #include -#include #include #endif diff --git a/src/network/kernel/qdnslookup_win.cpp b/src/network/kernel/qdnslookup_win.cpp index cfdb9ca633..262893179c 100644 --- a/src/network/kernel/qdnslookup_win.cpp +++ b/src/network/kernel/qdnslookup_win.cpp @@ -41,7 +41,6 @@ #include "qdnslookup_p.h" #include -#include #include #include -- cgit v1.2.3 From 65b8f59e045bb41fef99b1a44f462115de65064a Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Mon, 24 Jun 2019 09:41:07 +0200 Subject: QMutexPool: fix memory order of atomic operations The array of QAtomicPointer can be initialized using relaxed stores of nullptr, since nullptr is the whole data. But once we store an actual QMutex pointer in the array, we need to publish the indirect data thus created. We did this, with testAndSetRelease(); what was missing was a corresponding acquire fence on load, without which there is no happens-before relationship between the writes performed by the QMutex ctor and the reads performed by a subsequent mutex.lock(), say, on the same data. Fix by adding acquire fences to all loads. That includes the dtor, since mutexes may have been created in different threads, and never been imported into this_thread before the dtor is running. As a drive-by, return a new'ed QMutex that was successfully installed directly to the caller, without again going through a load-acquire. Change-Id: Ia25d205b1127c8c4de0979cef997d1a88123c5c3 Reviewed-by: David Faure Reviewed-by: Giuseppe D'Angelo Reviewed-by: Thiago Macieira --- src/corelib/thread/qmutexpool.cpp | 9 ++++++--- src/corelib/thread/qmutexpool_p.h | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/corelib/thread/qmutexpool.cpp b/src/corelib/thread/qmutexpool.cpp index 2a02197859..3ece30c01c 100644 --- a/src/corelib/thread/qmutexpool.cpp +++ b/src/corelib/thread/qmutexpool.cpp @@ -104,7 +104,7 @@ QMutexPool::QMutexPool(QMutex::RecursionMode recursionMode, int size) QMutexPool::~QMutexPool() { for (int index = 0; index < mutexes.count(); ++index) - delete mutexes[index].loadRelaxed(); + delete mutexes[index].loadAcquire(); } /*! @@ -129,9 +129,12 @@ QMutex *QMutexPool::createMutex(int index) { // mutex not created, create one QMutex *newMutex = new QMutex(recursionMode); - if (!mutexes[index].testAndSetRelease(0, newMutex)) + if (!mutexes[index].testAndSetRelease(nullptr, newMutex)) { delete newMutex; - return mutexes[index].loadRelaxed(); + return mutexes[index].loadAcquire(); + } else { + return newMutex; + } } /*! diff --git a/src/corelib/thread/qmutexpool_p.h b/src/corelib/thread/qmutexpool_p.h index 1a47231abc..00710199b8 100644 --- a/src/corelib/thread/qmutexpool_p.h +++ b/src/corelib/thread/qmutexpool_p.h @@ -68,7 +68,7 @@ public: inline QMutex *get(const void *address) { int index = uint(quintptr(address)) % mutexes.count(); - QMutex *m = mutexes[index].loadRelaxed(); + QMutex *m = mutexes[index].loadAcquire(); if (m) return m; else -- cgit v1.2.3 From e7b1c500d45c8a96fb09b0b425472eb1cdb10943 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Mon, 27 May 2019 17:32:37 +0200 Subject: Mark qHash(QOcspResponse) as noexcept MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because it is. Change-Id: I8d5204c30884b2c8656615a7d82428c539672d28 Reviewed-by: Giuseppe D'Angelo Reviewed-by: Mårten Nordheim Reviewed-by: Ville Voutilainen --- src/network/ssl/qocspresponse.cpp | 2 +- src/network/ssl/qocspresponse.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/network/ssl/qocspresponse.cpp b/src/network/ssl/qocspresponse.cpp index 79f0cfd1d4..bf27bb768b 100644 --- a/src/network/ssl/qocspresponse.cpp +++ b/src/network/ssl/qocspresponse.cpp @@ -239,7 +239,7 @@ Q_NETWORK_EXPORT bool operator==(const QOcspResponse &lhs, const QOcspResponse & \since 5.13 \relates QHash */ -uint qHash(const QOcspResponse &response, uint seed) +uint qHash(const QOcspResponse &response, uint seed) noexcept { const QOcspResponsePrivate *d = response.d.data(); Q_ASSERT(d); diff --git a/src/network/ssl/qocspresponse.h b/src/network/ssl/qocspresponse.h index 0e134d236b..cf6be5a369 100644 --- a/src/network/ssl/qocspresponse.h +++ b/src/network/ssl/qocspresponse.h @@ -73,7 +73,7 @@ enum class QOcspRevocationReason }; class QOcspResponse; -Q_NETWORK_EXPORT uint qHash(const QOcspResponse &response, uint seed = 0); +Q_NETWORK_EXPORT uint qHash(const QOcspResponse &response, uint seed = 0) noexcept; class QOcspResponsePrivate; class Q_NETWORK_EXPORT QOcspResponse @@ -100,7 +100,7 @@ private: friend class QSslSocketBackendPrivate; friend Q_NETWORK_EXPORT bool operator==(const QOcspResponse &lhs, const QOcspResponse &rhs); - friend Q_NETWORK_EXPORT uint qHash(const QOcspResponse &response, uint seed); + friend Q_NETWORK_EXPORT uint qHash(const QOcspResponse &response, uint seed) noexcept; QSharedDataPointer d; }; -- cgit v1.2.3 From fabf9239e0e6231f09d4a324bfe85ffcc529da3d Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Thu, 13 Jun 2019 11:54:29 +0200 Subject: QTestLib: Replace typedef by 'using' Apply Fixits by Qt Creator with some amendments. Task-number: QTBUG-69413 Change-Id: I366cca6e5755719e8241e76774af6be2b5312627 Reviewed-by: Lars Knoll --- src/testlib/qtestaccessible.h | 2 +- src/testlib/qtestcase.cpp | 2 +- src/testlib/qtestcase.h | 2 +- src/testlib/qtesttable.cpp | 10 ++++------ 4 files changed, 7 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/testlib/qtestaccessible.h b/src/testlib/qtestaccessible.h index d14dcec031..bd77ee77a1 100644 --- a/src/testlib/qtestaccessible.h +++ b/src/testlib/qtestaccessible.h @@ -65,7 +65,7 @@ QT_BEGIN_NAMESPACE class QObject; // Use pointers since we subclass QAccessibleEvent -typedef QList EventList; +using EventList = QList; bool operator==(const QAccessibleEvent &l, const QAccessibleEvent &r) { diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index ac309374e3..10477238cb 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -301,7 +301,7 @@ class TestMethods { public: Q_DISABLE_COPY_MOVE(TestMethods) - typedef std::vector MetaMethods; + using MetaMethods = std::vector; explicit TestMethods(const QObject *o, const MetaMethods &m = MetaMethods()); diff --git a/src/testlib/qtestcase.h b/src/testlib/qtestcase.h index 794283ff78..af7c0f43b9 100644 --- a/src/testlib/qtestcase.h +++ b/src/testlib/qtestcase.h @@ -336,7 +336,7 @@ namespace QTest template inline void addColumn(const char *name, T * = nullptr) { - typedef std::is_same QIsSameTConstChar; + using QIsSameTConstChar = std::is_same; Q_STATIC_ASSERT_X(!QIsSameTConstChar::value, "const char* is not allowed as a test data format."); addColumnInternal(qMetaTypeId(), name); } diff --git a/src/testlib/qtesttable.cpp b/src/testlib/qtesttable.cpp index 8d42668a5b..20e0702d44 100644 --- a/src/testlib/qtesttable.cpp +++ b/src/testlib/qtesttable.cpp @@ -65,10 +65,10 @@ public: int type; }; - typedef std::vector ElementList; + using ElementList = std::vector; ElementList elementList; - typedef std::vector DataList; + using DataList = std::vector; DataList dataList; void addColumn(int elemType, const char *elemName) { elementList.push_back(Element(elemName, elemType)); } @@ -152,14 +152,12 @@ private: int QTestTable::indexOf(const char *elementName) const { - typedef QTestTablePrivate::ElementList::const_iterator It; - QTEST_ASSERT(elementName); const QTestTablePrivate::ElementList &elementList = d->elementList; - const It it = std::find_if(elementList.begin(), elementList.end(), - NamePredicate(elementName)); + const auto it = std::find_if(elementList.begin(), elementList.end(), + NamePredicate(elementName)); return it != elementList.end() ? int(it - elementList.begin()) : -1; } -- cgit v1.2.3 From ff2b2032a089d74975da4a3fac7c7c90989e6dc5 Mon Sep 17 00:00:00 2001 From: Sona Kurazyan Date: Thu, 20 Jun 2019 17:40:45 +0200 Subject: Remove usages of deprecated APIs from QtAlgorithms Task-number: QTBUG-76491 Change-Id: I9dab736a0cbd2e86588919640c26e8ce6b3674d0 Reviewed-by: Alex Blasche Reviewed-by: Leena Miettinen --- src/corelib/doc/snippets/code/doc_src_qset.cpp | 6 +++--- src/corelib/doc/snippets/code/src_corelib_tools_qlinkedlist.cpp | 8 ++++---- src/corelib/doc/snippets/code/src_corelib_tools_qlistdata.cpp | 2 +- src/corelib/doc/src/containers.qdoc | 2 +- src/corelib/tools/qlinkedlist.cpp | 6 ++---- src/widgets/doc/src/model-view-programming.qdoc | 2 +- 6 files changed, 12 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/corelib/doc/snippets/code/doc_src_qset.cpp b/src/corelib/doc/snippets/code/doc_src_qset.cpp index 7f7cec8b45..4248c49642 100644 --- a/src/corelib/doc/snippets/code/doc_src_qset.cpp +++ b/src/corelib/doc/snippets/code/doc_src_qset.cpp @@ -131,7 +131,7 @@ while (i != set.end()) { //! [10] QSet set; ... -QSet::iterator it = qFind(set.begin(), set.end(), "Jeanette"); +QSet::iterator it = std::find(set.begin(), set.end(), "Jeanette"); if (it != set.end()) cout << "Found Jeanette" << Qt::endl; //! [10] @@ -150,7 +150,7 @@ for (i = set.begin(); i != set.end(); ++i) //! [12] QSet set; ... -QSet::iterator it = qFind(set.begin(), set.end(), "Jeanette"); +QSet::iterator it = std::find(set.begin(), set.end(), "Jeanette"); if (it != set.constEnd()) cout << "Found Jeanette" << Qt::endl; //! [12] @@ -161,7 +161,7 @@ QSet set; set << "red" << "green" << "blue" << ... << "black"; QList list = set.toList(); -qSort(list); +std::sort(list.begin(), list.end()); //! [13] diff --git a/src/corelib/doc/snippets/code/src_corelib_tools_qlinkedlist.cpp b/src/corelib/doc/snippets/code/src_corelib_tools_qlinkedlist.cpp index e0d3c71dc5..e8754a5f34 100644 --- a/src/corelib/doc/snippets/code/src_corelib_tools_qlinkedlist.cpp +++ b/src/corelib/doc/snippets/code/src_corelib_tools_qlinkedlist.cpp @@ -119,8 +119,8 @@ for (i = list.begin(); i != list.end(); ++i) //! [8] QLinkedList list; ... -QLinkedList::iterator it = qFind(list.begin(), - list.end(), "Joel"); +QLinkedList::iterator it = std::find(list.begin(), + list.end(), "Joel"); if (it != list.end()) cout << "Found Joel" << Qt::endl; //! [8] @@ -189,8 +189,8 @@ for (i = list.constBegin(); i != list.constEnd(); ++i) //! [15] QLinkedList list; ... -QLinkedList::iterator it = qFind(list.constBegin(), - list.constEnd(), "Joel"); +QLinkedList::const_iterator it = std::find(list.constBegin(), + list.constEnd(), "Joel"); if (it != list.constEnd()) cout << "Found Joel" << Qt::endl; //! [15] diff --git a/src/corelib/doc/snippets/code/src_corelib_tools_qlistdata.cpp b/src/corelib/doc/snippets/code/src_corelib_tools_qlistdata.cpp index a24e599f2f..38fa526ef4 100644 --- a/src/corelib/doc/snippets/code/src_corelib_tools_qlistdata.cpp +++ b/src/corelib/doc/snippets/code/src_corelib_tools_qlistdata.cpp @@ -247,7 +247,7 @@ QSet set; set << 20 << 30 << 40 << ... << 70; QList list = QList::fromSet(set); -qSort(list); +std::sort(list.begin(), list.end()); //! [23] diff --git a/src/corelib/doc/src/containers.qdoc b/src/corelib/doc/src/containers.qdoc index e6c95129db..919533f651 100644 --- a/src/corelib/doc/src/containers.qdoc +++ b/src/corelib/doc/src/containers.qdoc @@ -666,7 +666,7 @@ \li \b{Logarithmic time:} O(log \e n). A function that runs in logarithmic time is a function whose running time is proportional to the logarithm of the number of items in the - container. One example is qBinaryFind(). + container. One example is the binary search algorithm. \li \b{Linear time:} O(\e n). A function that runs in linear time will execute in a time directly proportional to the number of diff --git a/src/corelib/tools/qlinkedlist.cpp b/src/corelib/tools/qlinkedlist.cpp index c0450f5cd8..d239fe0ef4 100644 --- a/src/corelib/tools/qlinkedlist.cpp +++ b/src/corelib/tools/qlinkedlist.cpp @@ -742,8 +742,7 @@ const QLinkedListData QLinkedListData::shared_null = { \snippet code/src_corelib_tools_qlinkedlist.cpp 7 STL-style iterators can be used as arguments to \l{generic - algorithms}. For example, here's how to find an item in the list - using the qFind() algorithm: + algorithms}. For example, here's how to find an item in the list: \snippet code/src_corelib_tools_qlinkedlist.cpp 8 @@ -987,8 +986,7 @@ const QLinkedListData QLinkedListData::shared_null = { \snippet code/src_corelib_tools_qlinkedlist.cpp 14 STL-style iterators can be used as arguments to \l{generic - algorithms}. For example, here's how to find an item in the list - using the qFind() algorithm: + algorithms}. For example, here's how to find an item in the list: \snippet code/src_corelib_tools_qlinkedlist.cpp 15 diff --git a/src/widgets/doc/src/model-view-programming.qdoc b/src/widgets/doc/src/model-view-programming.qdoc index 9335ff78c9..236582ef3f 100644 --- a/src/widgets/doc/src/model-view-programming.qdoc +++ b/src/widgets/doc/src/model-view-programming.qdoc @@ -1912,7 +1912,7 @@ \section3 Custom sorting models - QSortFilterProxyModel instances use Qt's built-in qStableSort() function to set up + QSortFilterProxyModel instances use std::stable_sort() function to set up mappings between items in the source model and those in the proxy model, allowing a sorted hierarchy of items to be exposed to views without modifying the structure of the source model. To provide custom sorting behavior, reimplement the -- cgit v1.2.3 From e85d1963b58da8b17fb740a2a9c4b4bef7ee69cd Mon Sep 17 00:00:00 2001 From: Tasuku Suzuki Date: Tue, 28 May 2019 01:53:06 +0900 Subject: Fix build without feature.stringlistmodel Change-Id: Ia7a24ef1b0beea7519403000ba20dc78a3c20a21 Reviewed-by: Mikhail Svetkin --- src/sql/models/qsqltablemodel.cpp | 1 - src/widgets/util/qcompleter.cpp | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/sql/models/qsqltablemodel.cpp b/src/sql/models/qsqltablemodel.cpp index 11d30ab2e8..4bc9a8c2f8 100644 --- a/src/sql/models/qsqltablemodel.cpp +++ b/src/sql/models/qsqltablemodel.cpp @@ -50,7 +50,6 @@ #include "qsqltablemodel_p.h" #include -#include QT_BEGIN_NAMESPACE diff --git a/src/widgets/util/qcompleter.cpp b/src/widgets/util/qcompleter.cpp index 04407cdb1d..ee0418b5b8 100644 --- a/src/widgets/util/qcompleter.cpp +++ b/src/widgets/util/qcompleter.cpp @@ -145,7 +145,9 @@ #include "QtWidgets/qscrollbar.h" #include "QtCore/qdir.h" +#if QT_CONFIG(stringlistmodel) #include "QtCore/qstringlistmodel.h" +#endif #if QT_CONFIG(dirmodel) #include "QtWidgets/qdirmodel.h" #endif -- cgit v1.2.3 From 98f7df211464c79266e643a552d17067dd2b6c56 Mon Sep 17 00:00:00 2001 From: Mikhail Svetkin Date: Fri, 14 Jun 2019 15:27:02 +0200 Subject: rtems: Fix build qdatetime.cpp RTEMS does not have "timezone" global variable. Change-Id: Ifa4c6f8939270a83fb7b5ba623daafa1e9f81003 Reviewed-by: Volker Hilsheimer --- src/corelib/time/qdatetime.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/corelib/time/qdatetime.cpp b/src/corelib/time/qdatetime.cpp index 3c7484dca5..b3e747c588 100644 --- a/src/corelib/time/qdatetime.cpp +++ b/src/corelib/time/qdatetime.cpp @@ -2464,7 +2464,7 @@ static int qt_timezone() // - It also takes DST into account, so we need to adjust it to always // get the Standard Time offset. return -t.tm_gmtoff + (t.tm_isdst ? (long)SECS_PER_HOUR : 0L); -#elif defined(Q_OS_INTEGRITY) +#elif defined(Q_OS_INTEGRITY) || defined(Q_OS_RTEMS) return 0; #else return timezone; -- cgit v1.2.3 From 889b44043ee92f77c6bb21abff556688354193d2 Mon Sep 17 00:00:00 2001 From: Mikhail Svetkin Date: Tue, 11 Jun 2019 15:06:26 +0200 Subject: rtems: Fix build 3rdparty/sha3 RTEMS does not have byteswap.h Change-Id: I37222ba4edb7338600f7c902819440feadc03a63 Reviewed-by: Ryan Chu --- src/3rdparty/sha3/brg_endian.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/3rdparty/sha3/brg_endian.h b/src/3rdparty/sha3/brg_endian.h index 09d2a8b6a9..9bb306e678 100644 --- a/src/3rdparty/sha3/brg_endian.h +++ b/src/3rdparty/sha3/brg_endian.h @@ -42,7 +42,7 @@ Changes for ARM 9/9/2010 [Downstream relative to Gladman's GitHub, upstream to Q #elif defined( __linux__ ) || defined( __GNUC__ ) || defined( __GNU_LIBRARY__ ) # if !defined( __MINGW32__ ) && !defined( _AIX ) && !defined(Q_OS_QNX) # include -# if !defined( __BEOS__ ) +# if !defined( __BEOS__ ) && !defined(Q_OS_RTEMS) # include # endif # endif -- cgit v1.2.3 From 2a99f60cfbe8a10c8b64b2178573dc8da3d27abe Mon Sep 17 00:00:00 2001 From: Mat Sutcliffe Date: Sun, 23 Jun 2019 23:24:32 +0100 Subject: QStringList: add QStringView overloads of join, filter, replaceInStrings [ChangeLog][QtCore][QStringList] Added QStringView overloads of join(), filter(), and replaceInStrings(). Change-Id: I9636e21e2e43ed46cce0aa7fa23ab0710aa641ba Reviewed-by: Marc Mutz --- src/corelib/tools/qstringlist.cpp | 64 +++++++++++++++++++++++++++++++++++++++ src/corelib/tools/qstringlist.h | 52 +++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) (limited to 'src') diff --git a/src/corelib/tools/qstringlist.cpp b/src/corelib/tools/qstringlist.cpp index f6da7b1428..3bb0e3c886 100644 --- a/src/corelib/tools/qstringlist.cpp +++ b/src/corelib/tools/qstringlist.cpp @@ -283,6 +283,7 @@ void QtPrivate::QStringList_sort(QStringList *that, Qt::CaseSensitivity cs) } +#if QT_STRINGVIEW_LEVEL < 2 /*! \fn QStringList QStringList::filter(const QString &str, Qt::CaseSensitivity cs) const @@ -302,6 +303,26 @@ void QtPrivate::QStringList_sort(QStringList *that, Qt::CaseSensitivity cs) \sa contains() */ +#endif + +/*! + \fn QStringList QStringList::filter(QStringView str, Qt::CaseSensitivity cs) const + \overload + \since 5.14 +*/ +QStringList QtPrivate::QStringList_filter(const QStringList *that, QStringView str, + Qt::CaseSensitivity cs) +{ + QStringMatcher matcher(str.data(), str.length(), cs); + QStringList res; + for (int i = 0; i < that->size(); ++i) + if (matcher.indexIn(that->at(i)) != -1) + res << that->at(i); + return res; +} + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +/// Not really needed anymore, but kept for binary compatibility QStringList QtPrivate::QStringList_filter(const QStringList *that, const QString &str, Qt::CaseSensitivity cs) { @@ -312,6 +333,7 @@ QStringList QtPrivate::QStringList_filter(const QStringList *that, const QString res << that->at(i); return res; } +#endif template static bool stringList_contains(const QStringList &stringList, const T &str, Qt::CaseSensitivity cs) @@ -466,6 +488,7 @@ QStringList QtPrivate::QStringList_filter(const QStringList *that, const QRegula } #endif // QT_CONFIG(regularexpression) +#if QT_STRINGVIEW_LEVEL < 2 /*! \fn QStringList &QStringList::replaceInStrings(const QString &before, const QString &after, Qt::CaseSensitivity cs) @@ -481,12 +504,41 @@ QStringList QtPrivate::QStringList_filter(const QStringList *that, const QRegula \sa QString::replace() */ + +/*! + \fn QStringList &QStringList::replaceInStrings(QStringView before, const QString &after, Qt::CaseSensitivity cs) + \overload + \since 5.14 +*/ + +/*! + \fn QStringList &QStringList::replaceInStrings(const QString &before, QStringView after, Qt::CaseSensitivity cs) + \overload + \since 5.14 +*/ +#endif + +/*! + \fn QStringList &QStringList::replaceInStrings(QStringView before, QStringView after, Qt::CaseSensitivity cs) + \overload + \since 5.14 +*/ +void QtPrivate::QStringList_replaceInStrings(QStringList *that, QStringView before, + QStringView after, Qt::CaseSensitivity cs) +{ + for (int i = 0; i < that->size(); ++i) + (*that)[i].replace(before.data(), before.length(), after.data(), after.length(), cs); +} + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +/// Not really needed anymore, but kept for binary compatibility void QtPrivate::QStringList_replaceInStrings(QStringList *that, const QString &before, const QString &after, Qt::CaseSensitivity cs) { for (int i = 0; i < that->size(); ++i) (*that)[i].replace(before, after, cs); } +#endif #ifndef QT_NO_REGEXP @@ -561,6 +613,7 @@ static int accumulatedSize(const QStringList &list, int seplen) return result; } +#if QT_STRINGVIEW_LEVEL < 2 /*! \fn QString QStringList::join(const QString &separator) const @@ -570,6 +623,7 @@ static int accumulatedSize(const QStringList &list, int seplen) \sa QString::split() */ +#endif /*! \fn QString QStringList::join(QChar separator) const @@ -614,6 +668,16 @@ QString QtPrivate::QStringList_join(const QStringList &list, QLatin1String sep) return result; } +/*! + \fn QString QStringList::join(QStringView separator) const + \overload + \since 5.14 +*/ +QString QtPrivate::QStringList_join(const QStringList *that, QStringView sep) +{ + return QStringList_join(that, sep.data(), sep.length()); +} + /*! \fn QStringList QStringList::operator+(const QStringList &other) const diff --git a/src/corelib/tools/qstringlist.h b/src/corelib/tools/qstringlist.h index 3bb657069b..45f51bfcc6 100644 --- a/src/corelib/tools/qstringlist.h +++ b/src/corelib/tools/qstringlist.h @@ -73,12 +73,21 @@ public: inline void sort(Qt::CaseSensitivity cs = Qt::CaseSensitive); inline int removeDuplicates(); +#if QT_STRINGVIEW_LEVEL < 2 inline QString join(const QString &sep) const; +#endif + inline QString join(QStringView sep) const; inline QString join(QLatin1String sep) const; inline QString join(QChar sep) const; + inline QStringList filter(QStringView str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + inline QStringList &replaceInStrings(QStringView before, QStringView after, Qt::CaseSensitivity cs = Qt::CaseSensitive); +#if QT_STRINGVIEW_LEVEL < 2 inline QStringList filter(const QString &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; inline QStringList &replaceInStrings(const QString &before, const QString &after, Qt::CaseSensitivity cs = Qt::CaseSensitive); + inline QStringList &replaceInStrings(const QString &before, QStringView after, Qt::CaseSensitivity cs = Qt::CaseSensitive); + inline QStringList &replaceInStrings(QStringView before, const QString &after, Qt::CaseSensitivity cs = Qt::CaseSensitive); +#endif #ifndef QT_NO_REGEXP inline QStringList filter(const QRegExp &rx) const; @@ -163,18 +172,27 @@ inline const QStringList *QListSpecialMethods::self() const namespace QtPrivate { void Q_CORE_EXPORT QStringList_sort(QStringList *that, Qt::CaseSensitivity cs); int Q_CORE_EXPORT QStringList_removeDuplicates(QStringList *that); + QString Q_CORE_EXPORT QStringList_join(const QStringList *that, QStringView sep); QString Q_CORE_EXPORT QStringList_join(const QStringList *that, const QChar *sep, int seplen); Q_CORE_EXPORT QString QStringList_join(const QStringList &list, QLatin1String sep); + QStringList Q_CORE_EXPORT QStringList_filter(const QStringList *that, QStringView str, + Qt::CaseSensitivity cs); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) QStringList Q_CORE_EXPORT QStringList_filter(const QStringList *that, const QString &str, Qt::CaseSensitivity cs); +#endif #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) bool Q_CORE_EXPORT QStringList_contains(const QStringList *that, const QString &str, Qt::CaseSensitivity cs); #endif bool Q_CORE_EXPORT QStringList_contains(const QStringList *that, QStringView str, Qt::CaseSensitivity cs); bool Q_CORE_EXPORT QStringList_contains(const QStringList *that, QLatin1String str, Qt::CaseSensitivity cs); + void Q_CORE_EXPORT QStringList_replaceInStrings(QStringList *that, QStringView before, QStringView after, + Qt::CaseSensitivity cs); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) void Q_CORE_EXPORT QStringList_replaceInStrings(QStringList *that, const QString &before, const QString &after, Qt::CaseSensitivity cs); +#endif #ifndef QT_NO_REGEXP void Q_CORE_EXPORT QStringList_replaceInStrings(QStringList *that, const QRegExp &rx, const QString &after); @@ -203,10 +221,17 @@ inline int QListSpecialMethods::removeDuplicates() return QtPrivate::QStringList_removeDuplicates(self()); } +#if QT_STRINGVIEW_LEVEL < 2 inline QString QListSpecialMethods::join(const QString &sep) const { return QtPrivate::QStringList_join(self(), sep.constData(), sep.length()); } +#endif + +inline QString QListSpecialMethods::join(QStringView sep) const +{ + return QtPrivate::QStringList_join(self(), sep); +} QString QListSpecialMethods::join(QLatin1String sep) const { @@ -218,10 +243,17 @@ inline QString QListSpecialMethods::join(QChar sep) const return QtPrivate::QStringList_join(self(), &sep, 1); } +inline QStringList QListSpecialMethods::filter(QStringView str, Qt::CaseSensitivity cs) const +{ + return QtPrivate::QStringList_filter(self(), str, cs); +} + +#if QT_STRINGVIEW_LEVEL < 2 inline QStringList QListSpecialMethods::filter(const QString &str, Qt::CaseSensitivity cs) const { return QtPrivate::QStringList_filter(self(), str, cs); } +#endif #if QT_STRINGVIEW_LEVEL < 2 inline bool QStringList::contains(const QString &str, Qt::CaseSensitivity cs) const @@ -240,12 +272,32 @@ inline bool QStringList::contains(QStringView str, Qt::CaseSensitivity cs) const return QtPrivate::QStringList_contains(this, str, cs); } +inline QStringList &QListSpecialMethods::replaceInStrings(QStringView before, QStringView after, Qt::CaseSensitivity cs) +{ + QtPrivate::QStringList_replaceInStrings(self(), before, after, cs); + return *self(); +} + +#if QT_STRINGVIEW_LEVEL < 2 inline QStringList &QListSpecialMethods::replaceInStrings(const QString &before, const QString &after, Qt::CaseSensitivity cs) { QtPrivate::QStringList_replaceInStrings(self(), before, after, cs); return *self(); } +inline QStringList &QListSpecialMethods::replaceInStrings(QStringView before, const QString &after, Qt::CaseSensitivity cs) +{ + QtPrivate::QStringList_replaceInStrings(self(), before, qToStringViewIgnoringNull(after), cs); + return *self(); +} + +inline QStringList &QListSpecialMethods::replaceInStrings(const QString &before, QStringView after, Qt::CaseSensitivity cs) +{ + QtPrivate::QStringList_replaceInStrings(self(), QStringView(before), after, cs); + return *self(); +} +#endif + inline QStringList operator+(const QList &one, const QStringList &other) { QStringList n = one; -- cgit v1.2.3 From 248e81a28028b1b0570ffe0ed2e0edbf63a0a21c Mon Sep 17 00:00:00 2001 From: Mikhail Svetkin Date: Thu, 6 Jun 2019 10:42:22 +0200 Subject: rtems: Add environment variable for configure default thread stack size The default stack size is too small on RTEMS. Qt uses threads internally and there is no way to change their stack size. Change-Id: I94a42c7a70c745f0b50d7051d9320edfabd1e09e Reviewed-by: Volker Hilsheimer --- src/corelib/thread/qthread.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/corelib/thread/qthread.cpp b/src/corelib/thread/qthread.cpp index fe555afca7..280c785049 100644 --- a/src/corelib/thread/qthread.cpp +++ b/src/corelib/thread/qthread.cpp @@ -171,6 +171,11 @@ QThreadPrivate::QThreadPrivate(QThreadData *d) // to 128K. #ifdef Q_OS_INTEGRITY stackSize = 128 * 1024; +#elif defined(Q_OS_RTEMS) + static bool envStackSizeOk = false; + static const int envStackSize = qEnvironmentVariableIntValue("QT_DEFAULT_THREAD_STACK_SIZE", &envStackSizeOk); + if (envStackSizeOk) + stackSize = envStackSize; #endif #if defined (Q_OS_WIN) -- cgit v1.2.3 From 222b81f5dac2a5d32027b15d3a784819291431f6 Mon Sep 17 00:00:00 2001 From: Mikhail Svetkin Date: Tue, 20 Mar 2018 14:32:54 +0100 Subject: rtems: Fix missing some ernno defines By default RTEMS does not support linux extensions for errno. Enable it for build. Change-Id: I43b346794b99ac0ed339bfbe6e39684071615503 Reviewed-by: Volker Hilsheimer Reviewed-by: Thiago Macieira Reviewed-by: Ryan Chu --- src/corelib/kernel/qcore_unix_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/corelib/kernel/qcore_unix_p.h b/src/corelib/kernel/qcore_unix_p.h index b56c2b9732..6ec7d28318 100644 --- a/src/corelib/kernel/qcore_unix_p.h +++ b/src/corelib/kernel/qcore_unix_p.h @@ -52,8 +52,8 @@ // We mean it. // -#include #include "qplatformdefs.h" +#include #include "qatomic.h" #include "qbytearray.h" -- cgit v1.2.3 From 56fb2266f2d224ba58d262640f318a0439a9fa32 Mon Sep 17 00:00:00 2001 From: Yulong Bai Date: Mon, 24 Jun 2019 15:22:01 +0200 Subject: QTypeModuleInfo: fix clang '-wconstant-logical-operand' warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ia73e4ffbabd5db67063584a7e53966da53133689 Reviewed-by: Jędrzej Nowacki --- src/corelib/kernel/qmetatype_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/corelib/kernel/qmetatype_p.h b/src/corelib/kernel/qmetatype_p.h index 97d6001937..fa7208369a 100644 --- a/src/corelib/kernel/qmetatype_p.h +++ b/src/corelib/kernel/qmetatype_p.h @@ -74,7 +74,7 @@ template class QTypeModuleInfo { public: - enum Module { + enum Module : bool { IsCore = false, IsWidget = false, IsGui = false, -- cgit v1.2.3 From 854ddb0301ae677a31286da4476318f4e6b74e3f Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 24 Jun 2019 10:22:50 +0200 Subject: rhi: Enhance line width and point size support ...but this will vary between backends, or in some cases even between implementations of the same API. Point size is settable only via the vertex shader (gl_PointSize). It is silently ignored with D3D and HLSL. Line widths other than 1 are supported only on OpenGL and Vulkan. (but this is in fact deprecated with GL and optional with Vulkan) Add QRhi::Feature values for both. The line width is now settable on QRhiGraphicsPipeline. It is not a dynamic state since the static, per-pipeline width is good enough for most cases. (and the feature is not supported on half of the backends anyways so it will get limited use in practice). Change-Id: I6d3a32269527c452b794b2cb8b0f03101eab40b2 Reviewed-by: Lars Knoll --- src/gui/rhi/qrhi.cpp | 18 +++++++++++++++++- src/gui/rhi/qrhi_p.h | 8 +++++++- src/gui/rhi/qrhid3d11.cpp | 4 ++++ src/gui/rhi/qrhigles2.cpp | 7 +++++++ src/gui/rhi/qrhimetal.mm | 4 ++++ src/gui/rhi/qrhivulkan.cpp | 18 +++++++++++------- src/gui/rhi/qrhivulkan_p_p.h | 2 ++ 7 files changed, 52 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp index 334fa1064a..681a6ddfb8 100644 --- a/src/gui/rhi/qrhi.cpp +++ b/src/gui/rhi/qrhi.cpp @@ -494,7 +494,23 @@ QT_BEGIN_NAMESPACE extension. When false, only 16-bit unsigned elements are supported in the index buffer. - \value Compute Indicates that compute shaders are supported. + \value Compute Indicates that compute shaders, image load/store, and + storage buffers are supported. + + \value WideLines Indicates that lines with a width other than 1 are + supported. When reported as not supported, the line width set on the + graphics pipeline state is ignored. This can always be false with some + backends (D3D11, Metal). With Vulkan, the value depends on the + implementation. + + \value VertexShaderPointSize Indicates that the size of rasterized points + set via \c{gl_PointSize} in the vertex shader is taken into account. When + reported as not supported, drawing points with a size other than 1 is not + supported. Setting \c{gl_PointSize} in the shader is still valid then, but + is ignored. (for example, when generating HLSL, the assignment is silently + dropped from the generated code) Note that some APIs (Metal, Vulkan) + require the point size to be set in the shader explicitly whenever drawing + points, even when the size is 1, as they do not automatically default to 1. */ /*! diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h index 0d296d370c..fb8727b265 100644 --- a/src/gui/rhi/qrhi_p.h +++ b/src/gui/rhi/qrhi_p.h @@ -1068,6 +1068,9 @@ public: int sampleCount() const { return m_sampleCount; } void setSampleCount(int s) { m_sampleCount = s; } + float lineWidth() const { return m_lineWidth; } + void setLineWidth(float width) { m_lineWidth = width; } + QVector shaderStages() const { return m_shaderStages; } void setShaderStages(const QVector &stages) { m_shaderStages = stages; } @@ -1098,6 +1101,7 @@ protected: quint32 m_stencilReadMask = 0xFF; quint32 m_stencilWriteMask = 0xFF; int m_sampleCount = 1; + float m_lineWidth = 1.0f; QVector m_shaderStages; QRhiVertexInputLayout m_vertexInputLayout; QRhiShaderResourceBindings *m_shaderResourceBindings = nullptr; @@ -1312,7 +1316,9 @@ public: NPOTTextureRepeat, RedOrAlpha8IsRed, ElementIndexUint, - Compute + Compute, + WideLines, + VertexShaderPointSize }; enum BeginFrameFlag { diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index 6b05d68fb5..dc69f50cc8 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -378,6 +378,10 @@ bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const return true; case QRhi::Compute: return true; + case QRhi::WideLines: + return false; + case QRhi::VertexShaderPointSize: + return false; default: Q_UNREACHABLE(); return false; diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp index 7c40a36701..51862cad86 100644 --- a/src/gui/rhi/qrhigles2.cpp +++ b/src/gui/rhi/qrhigles2.cpp @@ -618,6 +618,10 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const return caps.elementIndexUint; case QRhi::Compute: return false; + case QRhi::WideLines: + return true; + case QRhi::VertexShaderPointSize: + return true; default: Q_UNREACHABLE(); return false; @@ -1849,6 +1853,9 @@ void QRhiGles2::executeBindGraphicsPipeline(QRhiGraphicsPipeline *ps) f->glDisable(GL_STENCIL_TEST); } + if (psD->topology() == QRhiGraphicsPipeline::Lines || psD->topology() == QRhiGraphicsPipeline::LineStrip) + f->glLineWidth(psD->m_lineWidth); + f->glUseProgram(psD->program); } diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm index 214374e0c6..09b80c831d 100644 --- a/src/gui/rhi/qrhimetal.mm +++ b/src/gui/rhi/qrhimetal.mm @@ -523,6 +523,10 @@ bool QRhiMetal::isFeatureSupported(QRhi::Feature feature) const return true; case QRhi::Compute: return true; + case QRhi::WideLines: + return false; + case QRhi::VertexShaderPointSize: + return true; default: Q_UNREACHABLE(); return false; diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp index f6ecd7c00e..8598e5869a 100644 --- a/src/gui/rhi/qrhivulkan.cpp +++ b/src/gui/rhi/qrhivulkan.cpp @@ -489,6 +489,9 @@ bool QRhiVulkan::create(QRhi::Flags flags) // elsewhere states that the minimum bufferOffset is 4... texbufAlign = qMax(4, physDevProperties.limits.optimalBufferCopyOffsetAlignment); + f->vkGetPhysicalDeviceFeatures(physDev, &physDevFeatures); + hasWideLines = physDevFeatures.wideLines; + if (!importedAllocator) { VmaVulkanFunctions afuncs; afuncs.vkGetPhysicalDeviceProperties = wrap_vkGetPhysicalDeviceProperties; @@ -3489,24 +3492,21 @@ QMatrix4x4 QRhiVulkan::clipSpaceCorrMatrix() const bool QRhiVulkan::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const { - VkPhysicalDeviceFeatures features; - f->vkGetPhysicalDeviceFeatures(physDev, &features); - // Note that with some SDKs the validation layer gives an odd warning about // BC not being supported, even when our check here succeeds. Not much we // can do about that. if (format >= QRhiTexture::BC1 && format <= QRhiTexture::BC7) { - if (!features.textureCompressionBC) + if (!physDevFeatures.textureCompressionBC) return false; } if (format >= QRhiTexture::ETC2_RGB8 && format <= QRhiTexture::ETC2_RGBA8) { - if (!features.textureCompressionETC2) + if (!physDevFeatures.textureCompressionETC2) return false; } if (format >= QRhiTexture::ASTC_4x4 && format <= QRhiTexture::ASTC_12x12) { - if (!features.textureCompressionASTC_LDR) + if (!physDevFeatures.textureCompressionASTC_LDR) return false; } @@ -3545,6 +3545,10 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const return true; case QRhi::Compute: return hasCompute; + case QRhi::WideLines: + return hasWideLines; + case QRhi::VertexShaderPointSize: + return true; default: Q_UNREACHABLE(); return false; @@ -5612,7 +5616,7 @@ bool QVkGraphicsPipeline::build() rastInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; rastInfo.cullMode = toVkCullMode(m_cullMode); rastInfo.frontFace = toVkFrontFace(m_frontFace); - rastInfo.lineWidth = 1.0f; + rastInfo.lineWidth = rhiD->hasWideLines ? m_lineWidth : 1.0f; pipelineInfo.pRasterizationState = &rastInfo; VkPipelineMultisampleStateCreateInfo msInfo; diff --git a/src/gui/rhi/qrhivulkan_p_p.h b/src/gui/rhi/qrhivulkan_p_p.h index cec9016603..31e0eaa585 100644 --- a/src/gui/rhi/qrhivulkan_p_p.h +++ b/src/gui/rhi/qrhivulkan_p_p.h @@ -783,9 +783,11 @@ public: QVkAllocator allocator = nullptr; QVulkanFunctions *f = nullptr; QVulkanDeviceFunctions *df = nullptr; + VkPhysicalDeviceFeatures physDevFeatures; VkPhysicalDeviceProperties physDevProperties; VkDeviceSize ubufAlign; VkDeviceSize texbufAlign; + bool hasWideLines = false; bool debugMarkersAvailable = false; bool vertexAttribDivisorAvailable = false; -- cgit v1.2.3 From 13a7777bbf99ef18cf17727d69ec1f63763c85cc Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Thu, 27 Jun 2019 10:34:33 +0200 Subject: rhi: gl: Make fb attachment logic ES 2.0 and WebGL compatible So that we can now operate on plain ES 2.0 implementations like the Broadcom stack on the Raspberry Pi (which does not even have packed depth stencil). Also add the WebGL-specific combined attach logic to enable WebAssembly. This has not been tested in practice. Change-Id: I21219319062f295c1e88e3057c0c2ede724ac664 Reviewed-by: Lars Knoll --- src/gui/rhi/qrhigles2.cpp | 80 +++++++++++++++++++++++++++++++++++---------- src/gui/rhi/qrhigles2_p_p.h | 7 +++- 2 files changed, 68 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp index 51862cad86..fff3ff737c 100644 --- a/src/gui/rhi/qrhigles2.cpp +++ b/src/gui/rhi/qrhigles2.cpp @@ -175,14 +175,34 @@ QT_BEGIN_NAMESPACE #define GL_DEPTH_COMPONENT16 0x81A5 #endif +#ifndef GL_DEPTH_COMPONENT24 +#define GL_DEPTH_COMPONENT24 0x81A6 +#endif + #ifndef GL_DEPTH_COMPONENT32F #define GL_DEPTH_COMPONENT32F 0x8CAC #endif +#ifndef GL_STENCIL_INDEX +#define GL_STENCIL_INDEX 0x1901 +#endif + +#ifndef GL_STENCIL_INDEX8 +#define GL_STENCIL_INDEX8 0x8D48 +#endif + #ifndef GL_DEPTH24_STENCIL8 #define GL_DEPTH24_STENCIL8 0x88F0 #endif +#ifndef GL_DEPTH_STENCIL_ATTACHMENT +#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A +#endif + +#ifndef GL_DEPTH_STENCIL +#define GL_DEPTH_STENCIL 0x84F9 +#endif + #ifndef GL_PRIMITIVE_RESTART_FIXED_INDEX #define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69 #endif @@ -394,10 +414,17 @@ bool QRhiGles2::create(QRhi::Flags flags) caps.floatFormats = caps.ctxMajor >= 3; caps.depthTexture = caps.ctxMajor >= 3; caps.packedDepthStencil = f->hasOpenGLExtension(QOpenGLExtensions::PackedDepthStencil); +#ifdef Q_OS_WASM + caps.needsDepthStencilCombinedAttach = true; +#else + caps.needsDepthStencilCombinedAttach = false; +#endif caps.srgbCapableDefaultFramebuffer = f->hasOpenGLExtension(QOpenGLExtensions::SRGBFrameBuffer); caps.coreProfile = actualFormat.profile() == QSurfaceFormat::CoreProfile; caps.uniformBuffers = caps.ctxMajor >= 3 && (caps.gles || caps.ctxMinor >= 1); caps.elementIndexUint = f->hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint); + caps.depth24 = f->hasOpenGLExtension(QOpenGLExtensions::Depth24); + caps.rgba8Format = f->hasOpenGLExtension(QOpenGLExtensions::Sized8Formats); nativeHandlesStruct.context = ctx; @@ -2261,21 +2288,30 @@ bool QGles2RenderBuffer::build() switch (m_type) { case QRhiRenderBuffer::DepthStencil: if (rhiD->caps.msaaRenderBuffer && samples > 1) { - rhiD->f->glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_DEPTH24_STENCIL8, + const GLenum storage = rhiD->caps.needsDepthStencilCombinedAttach ? GL_DEPTH_STENCIL : GL_DEPTH24_STENCIL8; + rhiD->f->glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, storage, m_pixelSize.width(), m_pixelSize.height()); + stencilRenderbuffer = 0; + } else if (rhiD->caps.packedDepthStencil || rhiD->caps.needsDepthStencilCombinedAttach) { + const GLenum storage = rhiD->caps.needsDepthStencilCombinedAttach ? GL_DEPTH_STENCIL : GL_DEPTH24_STENCIL8; + rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, storage, + m_pixelSize.width(), m_pixelSize.height()); + stencilRenderbuffer = 0; } else { - if (rhiD->caps.packedDepthStencil) { - rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, - m_pixelSize.width(), m_pixelSize.height()); - stencilRenderbuffer = 0; - } else { - rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_ATTACHMENT, - m_pixelSize.width(), m_pixelSize.height()); - rhiD->f->glGenRenderbuffers(1, &stencilRenderbuffer); - rhiD->f->glBindRenderbuffer(GL_RENDERBUFFER, stencilRenderbuffer); - rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_ATTACHMENT, - m_pixelSize.width(), m_pixelSize.height()); + GLenum depthStorage = GL_DEPTH_COMPONENT; + if (rhiD->caps.gles) { + if (rhiD->caps.depth24) + depthStorage = GL_DEPTH_COMPONENT24; + else + depthStorage = GL_DEPTH_COMPONENT16; // plain ES 2.0 only has this } + const GLenum stencilStorage = rhiD->caps.gles ? GL_STENCIL_INDEX8 : GL_STENCIL_INDEX; + rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, depthStorage, + m_pixelSize.width(), m_pixelSize.height()); + rhiD->f->glGenRenderbuffers(1, &stencilRenderbuffer); + rhiD->f->glBindRenderbuffer(GL_RENDERBUFFER, stencilRenderbuffer); + rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, stencilStorage, + m_pixelSize.width(), m_pixelSize.height()); } QRHI_PROF_F(newRenderBuffer(this, false, false, samples)); break; @@ -2284,7 +2320,7 @@ bool QGles2RenderBuffer::build() rhiD->f->glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_RGBA8, m_pixelSize.width(), m_pixelSize.height()); else - rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, + rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, rhiD->caps.rgba8Format ? GL_RGBA8 : GL_RGBA4, m_pixelSize.width(), m_pixelSize.height()); QRHI_PROF_F(newRenderBuffer(this, false, false, samples)); break; @@ -2667,11 +2703,19 @@ bool QGles2TextureRenderTarget::build() if (hasDepthStencil) { if (m_desc.depthStencilBuffer()) { QGles2RenderBuffer *depthRbD = QRHI_RES(QGles2RenderBuffer, m_desc.depthStencilBuffer()); - rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRbD->renderbuffer); - if (depthRbD->stencilRenderbuffer) - rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthRbD->stencilRenderbuffer); - else // packed - rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthRbD->renderbuffer); + if (rhiD->caps.needsDepthStencilCombinedAttach) { + rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, + depthRbD->renderbuffer); + } else { + rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, + depthRbD->renderbuffer); + if (depthRbD->stencilRenderbuffer) + rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, + depthRbD->stencilRenderbuffer); + else // packed + rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, + depthRbD->renderbuffer); + } if (d.colorAttCount == 0) { d.pixelSize = depthRbD->pixelSize(); d.sampleCount = depthRbD->samples; diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h index fe74e2e75b..e02c883190 100644 --- a/src/gui/rhi/qrhigles2_p_p.h +++ b/src/gui/rhi/qrhigles2_p_p.h @@ -638,10 +638,12 @@ public: floatFormats(false), depthTexture(false), packedDepthStencil(false), + needsDepthStencilCombinedAttach(false), srgbCapableDefaultFramebuffer(false), coreProfile(false), uniformBuffers(false), - elementIndexUint(false) + elementIndexUint(false), + depth24(false) { } int ctxMajor; int ctxMinor; @@ -662,10 +664,13 @@ public: uint floatFormats : 1; uint depthTexture : 1; uint packedDepthStencil : 1; + uint needsDepthStencilCombinedAttach : 1; uint srgbCapableDefaultFramebuffer : 1; uint coreProfile : 1; uint uniformBuffers : 1; uint elementIndexUint : 1; + uint depth24 : 1; + uint rgba8Format : 1; } caps; QGles2SwapChain *currentSwapChain = nullptr; QVector supportedCompressedFormats; -- cgit v1.2.3 From 03ebad7bfdd0d359a9d46540f7e4035bd72fc9db Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Thu, 27 Jun 2019 15:23:20 +0200 Subject: rhi: gl: Adjust texture and renderbuffer sizes automatically Enforce the maximum texture size limit both for textures and renderbuffers, showing warnings and adjusting the size automatically. On the low end ES 2.0 devices we can easily hit a maximum size of 2048x2048. For example a Raspberry Pi running in full HD with the Controls 2 Material style breaks right away as it tries to create slightly wider than 2048 texture for its ShaderEffect. Instead of breaking with an obscure framebuffer incomplete warning at best, show something more informative and adjust the sizes so that something shows up on the screen still. (naturally applications not taking the maximum sizes returned from QRhi into account are technically incorrect and may well end up with incorrect rendering, but we should still try handling this as gracefully as possible) Change-Id: Ib75eb893ab4774e1a3c49ed2d14c6bf9be8c807a Reviewed-by: Lars Knoll --- src/gui/rhi/qrhigles2.cpp | 64 ++++++++++++++++++++++++++++++++------------- src/gui/rhi/qrhigles2_p_p.h | 1 + 2 files changed, 47 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp index fff3ff737c..34052c5a9c 100644 --- a/src/gui/rhi/qrhigles2.cpp +++ b/src/gui/rhi/qrhigles2.cpp @@ -497,6 +497,43 @@ int QRhiGles2::effectiveSampleCount(int sampleCount) const return s; } +static inline bool isPowerOfTwo(int x) +{ + // Assumption: x >= 1 + return x == (x & -x); +} + +QSize QRhiGles2::safeTextureSize(const QSize &pixelSize) const +{ + QSize size = pixelSize.isEmpty() ? QSize(1, 1) : pixelSize; + + if (!caps.npotTexture) { + if (!isPowerOfTwo(size.width())) { + qWarning("Texture width %d is not a power of two, adjusting", + size.width()); + size.setWidth(qNextPowerOfTwo(size.width())); + } + if (!isPowerOfTwo(size.height())) { + qWarning("Texture height %d is not a power of two, adjusting", + size.height()); + size.setHeight(qNextPowerOfTwo(size.height())); + } + } + + if (size.width() > caps.maxTextureSize) { + qWarning("Texture width %d exceeds maximum width %d, adjusting", + size.width(), caps.maxTextureSize); + size.setWidth(caps.maxTextureSize); + } + if (size.height() > caps.maxTextureSize) { + qWarning("Texture height %d exceeds maximum height %d, adjusting", + size.height(), caps.maxTextureSize); + size.setHeight(caps.maxTextureSize); + } + + return size; +} + QRhiSwapChain *QRhiGles2::createSwapChain() { return new QGles2SwapChain(this); @@ -2276,26 +2313,25 @@ bool QGles2RenderBuffer::build() qWarning("RenderBuffer: UsedWithSwapChainOnly is meaningless in combination with Color"); } - if (m_pixelSize.isEmpty()) - return false; - if (!rhiD->ensureContext()) return false; rhiD->f->glGenRenderbuffers(1, &renderbuffer); rhiD->f->glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); + const QSize size = rhiD->safeTextureSize(m_pixelSize); + switch (m_type) { case QRhiRenderBuffer::DepthStencil: if (rhiD->caps.msaaRenderBuffer && samples > 1) { const GLenum storage = rhiD->caps.needsDepthStencilCombinedAttach ? GL_DEPTH_STENCIL : GL_DEPTH24_STENCIL8; rhiD->f->glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, storage, - m_pixelSize.width(), m_pixelSize.height()); + size.width(), size.height()); stencilRenderbuffer = 0; } else if (rhiD->caps.packedDepthStencil || rhiD->caps.needsDepthStencilCombinedAttach) { const GLenum storage = rhiD->caps.needsDepthStencilCombinedAttach ? GL_DEPTH_STENCIL : GL_DEPTH24_STENCIL8; rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, storage, - m_pixelSize.width(), m_pixelSize.height()); + size.width(), size.height()); stencilRenderbuffer = 0; } else { GLenum depthStorage = GL_DEPTH_COMPONENT; @@ -2307,21 +2343,21 @@ bool QGles2RenderBuffer::build() } const GLenum stencilStorage = rhiD->caps.gles ? GL_STENCIL_INDEX8 : GL_STENCIL_INDEX; rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, depthStorage, - m_pixelSize.width(), m_pixelSize.height()); + size.width(), size.height()); rhiD->f->glGenRenderbuffers(1, &stencilRenderbuffer); rhiD->f->glBindRenderbuffer(GL_RENDERBUFFER, stencilRenderbuffer); rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, stencilStorage, - m_pixelSize.width(), m_pixelSize.height()); + size.width(), size.height()); } QRHI_PROF_F(newRenderBuffer(this, false, false, samples)); break; case QRhiRenderBuffer::Color: if (rhiD->caps.msaaRenderBuffer && samples > 1) rhiD->f->glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_RGBA8, - m_pixelSize.width(), m_pixelSize.height()); + size.width(), size.height()); else rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, rhiD->caps.rgba8Format ? GL_RGBA8 : GL_RGBA4, - m_pixelSize.width(), m_pixelSize.height()); + size.width(), size.height()); QRHI_PROF_F(newRenderBuffer(this, false, false, samples)); break; default: @@ -2371,12 +2407,6 @@ void QGles2Texture::release() rhiD->unregisterResource(this); } -static inline bool isPowerOfTwo(int x) -{ - // Assumption: x >= 1 - return x == (x & -x); -} - bool QGles2Texture::prepareBuild(QSize *adjustedSize) { if (texture) @@ -2386,9 +2416,7 @@ bool QGles2Texture::prepareBuild(QSize *adjustedSize) if (!rhiD->ensureContext()) return false; - QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize; - if (!rhiD->caps.npotTexture && (!isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height()))) - size = QSize(qNextPowerOfTwo(size.width()), qNextPowerOfTwo(size.height())); + const QSize size = rhiD->safeTextureSize(m_pixelSize); const bool isCube = m_flags.testFlag(CubeMap); const bool hasMipMaps = m_flags.testFlag(MipMapped); diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h index e02c883190..dff12d626b 100644 --- a/src/gui/rhi/qrhigles2_p_p.h +++ b/src/gui/rhi/qrhigles2_p_p.h @@ -611,6 +611,7 @@ public: QGles2RenderTargetData *enqueueBindFramebuffer(QRhiRenderTarget *rt, QGles2CommandBuffer *cbD, bool *wantsColorClear = nullptr, bool *wantsDsClear = nullptr); int effectiveSampleCount(int sampleCount) const; + QSize safeTextureSize(const QSize &size) const; QOpenGLContext *ctx = nullptr; bool importedContext = false; -- cgit v1.2.3 From 201a22a4d2a3906a2f6cbba1e50d5b83a21a10f6 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 28 Jun 2019 12:36:35 +0200 Subject: rhi: gl: Add support for instanced drawing ...when having a 3.3+ or ES 3.0+ context. Change-Id: Ie92815263e190912d8f0b7b92ae6da55e062af05 Reviewed-by: Lars Knoll --- src/gui/rhi/qrhigles2.cpp | 54 ++++++++++++++++++++++++++++++++------------- src/gui/rhi/qrhigles2_p_p.h | 7 +++++- 2 files changed, 45 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp index 34052c5a9c..60bbda2d60 100644 --- a/src/gui/rhi/qrhigles2.cpp +++ b/src/gui/rhi/qrhigles2.cpp @@ -425,6 +425,7 @@ bool QRhiGles2::create(QRhi::Flags flags) 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); nativeHandlesStruct.context = ctx; @@ -665,7 +666,7 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const case QRhi::Timestamps: return false; case QRhi::Instancing: - return false; + return caps.instancing; case QRhi::CustomInstanceStepRate: return false; case QRhi::PrimitiveRestart: @@ -933,7 +934,6 @@ void QRhiGles2::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) void QRhiGles2::draw(QRhiCommandBuffer *cb, quint32 vertexCount, quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) { - Q_UNUSED(instanceCount); // no instancing Q_UNUSED(firstInstance); QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass); @@ -943,13 +943,13 @@ void QRhiGles2::draw(QRhiCommandBuffer *cb, quint32 vertexCount, cmd.args.draw.ps = cbD->currentPipeline; cmd.args.draw.vertexCount = vertexCount; cmd.args.draw.firstVertex = firstVertex; + cmd.args.draw.instanceCount = instanceCount; cbD->commands.append(cmd); } void QRhiGles2::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance) { - Q_UNUSED(instanceCount); // no instancing Q_UNUSED(firstInstance); Q_UNUSED(vertexOffset); // no glDrawElementsBaseVertex QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); @@ -960,6 +960,7 @@ void QRhiGles2::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, cmd.args.drawIndexed.ps = cbD->currentPipeline; cmd.args.drawIndexed.indexCount = indexCount; cmd.args.drawIndexed.firstIndex = firstIndex; + cmd.args.drawIndexed.instanceCount = instanceCount; cbD->commands.append(cmd); } @@ -1613,13 +1614,14 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb) const QVector bindings = psD->m_vertexInputLayout.bindings(); const QVector attributes = psD->m_vertexInputLayout.attributes(); for (const QRhiVertexInputAttribute &a : attributes) { - if (a.binding() != cmd.args.bindVertexBuffer.binding) + const int bindingIdx = a.binding(); + if (bindingIdx != cmd.args.bindVertexBuffer.binding) continue; // we do not support more than one vertex buffer f->glBindBuffer(GL_ARRAY_BUFFER, cmd.args.bindVertexBuffer.buffer); - const int stride = bindings[a.binding()].stride(); + const int stride = bindings[bindingIdx].stride(); int size = 1; GLenum type = GL_FLOAT; bool normalize = false; @@ -1658,10 +1660,17 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb) default: break; } + + const int locationIdx = a.location(); quint32 ofs = a.offset() + cmd.args.bindVertexBuffer.offset; - f->glVertexAttribPointer(a.location(), size, type, normalize, stride, + f->glVertexAttribPointer(locationIdx, size, type, normalize, stride, reinterpret_cast(quintptr(ofs))); - f->glEnableVertexAttribArray(a.location()); + f->glEnableVertexAttribArray(locationIdx); + if (bindings[bindingIdx].classification() == QRhiVertexInputBinding::PerInstance + && caps.instancing) + { + f->glVertexAttribDivisor(locationIdx, bindings[bindingIdx].instanceStepRate()); + } } } else { qWarning("No graphics pipeline active for setVertexInput; ignored"); @@ -1677,21 +1686,36 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb) case QGles2CommandBuffer::Command::Draw: { QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.draw.ps); - if (psD) - f->glDrawArrays(psD->drawMode, cmd.args.draw.firstVertex, cmd.args.draw.vertexCount); - else + if (psD) { + if (cmd.args.draw.instanceCount == 1 || !caps.instancing) { + f->glDrawArrays(psD->drawMode, cmd.args.draw.firstVertex, cmd.args.draw.vertexCount); + } else { + f->glDrawArraysInstanced(psD->drawMode, cmd.args.draw.firstVertex, cmd.args.draw.vertexCount, + cmd.args.draw.instanceCount); + } + } else { qWarning("No graphics pipeline active for draw; ignored"); + } } break; case QGles2CommandBuffer::Command::DrawIndexed: { QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.drawIndexed.ps); if (psD) { - quint32 ofs = cmd.args.drawIndexed.firstIndex * indexStride + indexOffset; - f->glDrawElements(psD->drawMode, - cmd.args.drawIndexed.indexCount, - indexType, - reinterpret_cast(quintptr(ofs))); + const GLvoid *ofs = reinterpret_cast( + quintptr(cmd.args.drawIndexed.firstIndex * indexStride + indexOffset)); + if (cmd.args.drawIndexed.instanceCount == 1 || !caps.instancing) { + f->glDrawElements(psD->drawMode, + cmd.args.drawIndexed.indexCount, + indexType, + ofs); + } else { + f->glDrawElementsInstanced(psD->drawMode, + cmd.args.drawIndexed.indexCount, + indexType, + ofs, + cmd.args.drawIndexed.instanceCount); + } } else { qWarning("No graphics pipeline active for drawIndexed; ignored"); } diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h index dff12d626b..b758ec40b8 100644 --- a/src/gui/rhi/qrhigles2_p_p.h +++ b/src/gui/rhi/qrhigles2_p_p.h @@ -324,11 +324,13 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer QRhiGraphicsPipeline *ps; quint32 vertexCount; quint32 firstVertex; + quint32 instanceCount; } draw; struct { QRhiGraphicsPipeline *ps; quint32 indexCount; quint32 firstIndex; + quint32 instanceCount; } drawIndexed; struct { QRhiGraphicsPipeline *ps; @@ -644,7 +646,9 @@ public: coreProfile(false), uniformBuffers(false), elementIndexUint(false), - depth24(false) + depth24(false), + rgba8Format(false), + instancing(false) { } int ctxMajor; int ctxMinor; @@ -672,6 +676,7 @@ public: uint elementIndexUint : 1; uint depth24 : 1; uint rgba8Format : 1; + uint instancing : 1; } caps; QGles2SwapChain *currentSwapChain = nullptr; QVector supportedCompressedFormats; -- cgit v1.2.3 From 058c52fc2a6476f688d4b07d6f24516e26d0a8f5 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 28 Jun 2019 15:06:06 +0200 Subject: rhi: Improve base vertex/instance support Have feature flags as appropriate. OpenGL is causing a mess here but let's support what we can since some of this will become relevant in more sophisticated mesh drawing cases with 3D in particular. Change-Id: Idfa7b4642ec87147978e03d78d6233efbd151491 Reviewed-by: Lars Knoll --- src/gui/rhi/qrhi.cpp | 42 ++++++++++++++++++++++++++++++++--------- src/gui/rhi/qrhi_p.h | 4 +++- src/gui/rhi/qrhid3d11.cpp | 4 ++++ src/gui/rhi/qrhigles2.cpp | 46 +++++++++++++++++++++++++++++++++------------ src/gui/rhi/qrhigles2_p_p.h | 7 ++++++- src/gui/rhi/qrhimetal.mm | 4 ++++ src/gui/rhi/qrhivulkan.cpp | 8 ++++++-- 7 files changed, 90 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp index 681a6ddfb8..646f0aef13 100644 --- a/src/gui/rhi/qrhi.cpp +++ b/src/gui/rhi/qrhi.cpp @@ -511,6 +511,14 @@ QT_BEGIN_NAMESPACE dropped from the generated code) Note that some APIs (Metal, Vulkan) require the point size to be set in the shader explicitly whenever drawing points, even when the size is 1, as they do not automatically default to 1. + + \value BaseVertex Indicates that \l{QRhiCommandBuffer::drawIndexed()}{drawIndexed()} + supports the \c vertexOffset argument. When reported as not supported, the + vertexOffset value in an indexed draw is ignored. + + \value BaseInstance Indicates that instanced draw commands support the \c + firstInstance argument. When reported as not supported, the firstInstance + value is ignored and the instance ID starts from 0. */ /*! @@ -4483,15 +4491,21 @@ void QRhiCommandBuffer::setStencilRef(quint32 refValue) Records a non-indexed draw. The number of vertices is specified in \a vertexCount. For instanced - drawing set \a instanceCount to a value other than 1. \a firstVertex is - the index of the first vertex to draw. \a firstInstance is the instance ID - of the first instance to draw. + drawing set \a instanceCount to a value other than 1. \a firstVertex is the + index of the first vertex to draw. When drawing multiple instances, the + first instance ID is specified by \a firstInstance. + + \note \a firstInstance may not be supported, and is ignored when the + QRhi::BaseInstance feature is reported as not supported. The first ID is + always 0 in that case. \note This function can only be called inside a render pass, meaning between a beginPass() and endPass() call. */ void QRhiCommandBuffer::draw(quint32 vertexCount, - quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) + quint32 instanceCount, + quint32 firstVertex, + quint32 firstInstance) { m_rhi->draw(this, vertexCount, instanceCount, firstVertex, firstInstance); } @@ -4509,17 +4523,27 @@ void QRhiCommandBuffer::draw(quint32 vertexCount, \l{QRhi::NonFourAlignedEffectiveIndexBufferOffset}{NonFourAlignedEffectiveIndexBufferOffset} feature will be reported as not-supported. - For instanced drawing set \a instanceCount to a value other than 1. \a - firstInstance is the instance ID of the first instance to draw. + For instanced drawing set \a instanceCount to a value other than 1. When + drawing multiple instances, the first instance ID is specified by \a + firstInstance. + + \note \a firstInstance may not be supported, and is ignored when the + QRhi::BaseInstance feature is reported as not supported. The first ID is + always 0 in that case. - \a vertexOffset is added to the vertex index. + \a vertexOffset (also called \c{base vertex}) is a signed value that is + added to the element index before indexing into the vertex buffer. Support + for this is not always available, and the value is ignored when the feature + QRhi::BaseVertex is reported as unsupported. \note This function can only be called inside a render pass, meaning between a beginPass() and endPass() call. */ void QRhiCommandBuffer::drawIndexed(quint32 indexCount, - quint32 instanceCount, quint32 firstIndex, - qint32 vertexOffset, quint32 firstInstance) + quint32 instanceCount, + quint32 firstIndex, + qint32 vertexOffset, + quint32 firstInstance) { m_rhi->drawIndexed(this, indexCount, instanceCount, firstIndex, vertexOffset, firstInstance); } diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h index fb8727b265..df30817ef4 100644 --- a/src/gui/rhi/qrhi_p.h +++ b/src/gui/rhi/qrhi_p.h @@ -1318,7 +1318,9 @@ public: ElementIndexUint, Compute, WideLines, - VertexShaderPointSize + VertexShaderPointSize, + BaseVertex, + BaseInstance }; enum BeginFrameFlag { diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index dc69f50cc8..49e90693be 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -382,6 +382,10 @@ bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const return false; case QRhi::VertexShaderPointSize: return false; + case QRhi::BaseVertex: + return true; + case QRhi::BaseInstance: + return true; default: Q_UNREACHABLE(); return false; diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp index 60bbda2d60..22cb030c27 100644 --- a/src/gui/rhi/qrhigles2.cpp +++ b/src/gui/rhi/qrhigles2.cpp @@ -426,6 +426,7 @@ bool QRhiGles2::create(QRhi::Flags flags) 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; nativeHandlesStruct.context = ctx; @@ -687,6 +688,10 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const return true; case QRhi::VertexShaderPointSize: return true; + case QRhi::BaseVertex: + return caps.baseVertex; + case QRhi::BaseInstance: + return false; // not in ES 3.2, so won't bother default: Q_UNREACHABLE(); return false; @@ -934,7 +939,6 @@ void QRhiGles2::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) void QRhiGles2::draw(QRhiCommandBuffer *cb, quint32 vertexCount, quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) { - Q_UNUSED(firstInstance); QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass); @@ -944,14 +948,13 @@ void QRhiGles2::draw(QRhiCommandBuffer *cb, quint32 vertexCount, cmd.args.draw.vertexCount = vertexCount; cmd.args.draw.firstVertex = firstVertex; cmd.args.draw.instanceCount = instanceCount; + cmd.args.draw.baseInstance = firstInstance; cbD->commands.append(cmd); } void QRhiGles2::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance) { - Q_UNUSED(firstInstance); - Q_UNUSED(vertexOffset); // no glDrawElementsBaseVertex QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass); @@ -961,6 +964,8 @@ void QRhiGles2::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, cmd.args.drawIndexed.indexCount = indexCount; cmd.args.drawIndexed.firstIndex = firstIndex; cmd.args.drawIndexed.instanceCount = instanceCount; + cmd.args.drawIndexed.baseInstance = firstInstance; + cmd.args.drawIndexed.baseVertex = vertexOffset; cbD->commands.append(cmd); } @@ -1705,16 +1710,33 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb) const GLvoid *ofs = reinterpret_cast( quintptr(cmd.args.drawIndexed.firstIndex * indexStride + indexOffset)); if (cmd.args.drawIndexed.instanceCount == 1 || !caps.instancing) { - f->glDrawElements(psD->drawMode, - cmd.args.drawIndexed.indexCount, - indexType, - ofs); + if (cmd.args.drawIndexed.baseVertex != 0 && caps.baseVertex) { + f->glDrawElementsBaseVertex(psD->drawMode, + cmd.args.drawIndexed.indexCount, + indexType, + ofs, + cmd.args.drawIndexed.baseVertex); + } else { + f->glDrawElements(psD->drawMode, + cmd.args.drawIndexed.indexCount, + indexType, + ofs); + } } else { - f->glDrawElementsInstanced(psD->drawMode, - cmd.args.drawIndexed.indexCount, - indexType, - ofs, - cmd.args.drawIndexed.instanceCount); + if (cmd.args.drawIndexed.baseVertex != 0 && caps.baseVertex) { + f->glDrawElementsInstancedBaseVertex(psD->drawMode, + cmd.args.drawIndexed.indexCount, + indexType, + ofs, + cmd.args.drawIndexed.instanceCount, + cmd.args.drawIndexed.baseVertex); + } else { + f->glDrawElementsInstanced(psD->drawMode, + cmd.args.drawIndexed.indexCount, + indexType, + ofs, + cmd.args.drawIndexed.instanceCount); + } } } else { qWarning("No graphics pipeline active for drawIndexed; ignored"); diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h index b758ec40b8..d6682977ff 100644 --- a/src/gui/rhi/qrhigles2_p_p.h +++ b/src/gui/rhi/qrhigles2_p_p.h @@ -325,12 +325,15 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer quint32 vertexCount; quint32 firstVertex; quint32 instanceCount; + quint32 baseInstance; } draw; struct { QRhiGraphicsPipeline *ps; quint32 indexCount; quint32 firstIndex; quint32 instanceCount; + quint32 baseInstance; + qint32 baseVertex; } drawIndexed; struct { QRhiGraphicsPipeline *ps; @@ -648,7 +651,8 @@ public: elementIndexUint(false), depth24(false), rgba8Format(false), - instancing(false) + instancing(false), + baseVertex(false) { } int ctxMajor; int ctxMinor; @@ -677,6 +681,7 @@ public: uint depth24 : 1; uint rgba8Format : 1; uint instancing : 1; + uint baseVertex : 1; } caps; QGles2SwapChain *currentSwapChain = nullptr; QVector supportedCompressedFormats; diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm index 09b80c831d..fa537a504b 100644 --- a/src/gui/rhi/qrhimetal.mm +++ b/src/gui/rhi/qrhimetal.mm @@ -527,6 +527,10 @@ bool QRhiMetal::isFeatureSupported(QRhi::Feature feature) const return false; case QRhi::VertexShaderPointSize: return true; + case QRhi::BaseVertex: + return true; + case QRhi::BaseInstance: + return true; default: Q_UNREACHABLE(); return false; diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp index 8598e5869a..7c4eeaf226 100644 --- a/src/gui/rhi/qrhivulkan.cpp +++ b/src/gui/rhi/qrhivulkan.cpp @@ -3549,6 +3549,10 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const return hasWideLines; case QRhi::VertexShaderPointSize: return true; + case QRhi::BaseVertex: + return true; + case QRhi::BaseInstance: + return true; default: Q_UNREACHABLE(); return false; @@ -4063,7 +4067,7 @@ void QRhiVulkan::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) } void QRhiVulkan::draw(QRhiCommandBuffer *cb, quint32 vertexCount, - quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) + quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) { QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass); @@ -4078,7 +4082,7 @@ void QRhiVulkan::draw(QRhiCommandBuffer *cb, quint32 vertexCount, } void QRhiVulkan::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, - quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance) + quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance) { QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass); -- cgit v1.2.3 From 2c289c6c1e04c419eefb890f19872a5bddc2fe78 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 28 Jun 2019 15:55:52 +0200 Subject: rhi: d3d: Fix resource binding mismatch with multiple passes The resetShaderResources() done when starting a render or compute pass purges all vertex, index, SRV, and UAV bindings. This will be optimized at a later point, but until then the command buffer's state should be updated accordingly, otherwise certain use cases end up with incorrect rendering results due to not binding vertex/index/shader resources as appropriate. This fixes the shadowmap example on d3d. Change-Id: I4d07929b8664b64bc608c6c8df474b0ee7c2ddbb Reviewed-by: Lars Knoll --- src/gui/rhi/qrhid3d11.cpp | 4 ++++ src/gui/rhi/qrhid3d11_p_p.h | 3 +++ 2 files changed, 7 insertions(+) (limited to 'src') diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index 49e90693be..eea2a13d8c 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -1480,6 +1480,8 @@ void QRhiD3D11::beginPass(QRhiCommandBuffer *cb, cbD->recordingPass = QD3D11CommandBuffer::RenderPass; cbD->currentTarget = rt; + + cbD->resetCachedShaderResourceState(); } void QRhiD3D11::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) @@ -1556,6 +1558,8 @@ void QRhiD3D11::beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch cbD->commands.append(cmd); cbD->recordingPass = QD3D11CommandBuffer::ComputePass; + + cbD->resetCachedShaderResourceState(); } void QRhiD3D11::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) diff --git a/src/gui/rhi/qrhid3d11_p_p.h b/src/gui/rhi/qrhid3d11_p_p.h index 688f79b3b7..34c9ff70f8 100644 --- a/src/gui/rhi/qrhid3d11_p_p.h +++ b/src/gui/rhi/qrhid3d11_p_p.h @@ -482,6 +482,9 @@ struct QD3D11CommandBuffer : public QRhiCommandBuffer currentGraphicsPipeline = nullptr; currentComputePipeline = nullptr; currentPipelineGeneration = 0; + resetCachedShaderResourceState(); + } + void resetCachedShaderResourceState() { currentGraphicsSrb = nullptr; currentComputeSrb = nullptr; currentSrbGeneration = 0; -- cgit v1.2.3 From da0f40f9acc1afc3d640ca03062356ca4d9b1272 Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Sun, 19 May 2019 16:25:52 +0200 Subject: QCharRef/QByteRef: schedule for Qt 7 removal With all of their special behavior deprecated, QCharRef and QByteRef can simply be removed. Add a comment. Change-Id: I8bad95424207ae281b5edf348b9ad81c6807dc12 Reviewed-by: Marc Mutz --- src/corelib/tools/qbytearray.h | 2 +- src/corelib/tools/qstring.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/corelib/tools/qbytearray.h b/src/corelib/tools/qbytearray.h index a81051d309..7c571706d8 100644 --- a/src/corelib/tools/qbytearray.h +++ b/src/corelib/tools/qbytearray.h @@ -548,7 +548,7 @@ class #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) Q_CORE_EXPORT #endif -QByteRef { +QByteRef { // ### Qt 7: remove QByteArray &a; int i; inline QByteRef(QByteArray &array, int idx) diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index 37bc8d91c9..6788e53057 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -1103,7 +1103,7 @@ class #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) Q_CORE_EXPORT #endif -QCharRef { +QCharRef { // ### Qt 7: remove QString &s; int i; inline QCharRef(QString &str, int idx) -- cgit v1.2.3 From 7993cada3961cca417431a392fc047a3d7909657 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Wed, 26 Jun 2019 21:40:27 +0200 Subject: QAccessibleWidget: fix UB (invalid cast) Casting an object to a subclass that is not the dynamic type of the object constitutes undefined behavior. Fix by befriending QObject. Change-Id: Ib70dbef9095df31a6d89449d82a02cef9fccd348 Reviewed-by: Frederik Gladhorn --- src/corelib/kernel/qobject.h | 2 ++ src/widgets/accessible/qaccessiblewidget.cpp | 20 ++------------------ 2 files changed, 4 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/corelib/kernel/qobject.h b/src/corelib/kernel/qobject.h index 240ace1e27..540b8b32c1 100644 --- a/src/corelib/kernel/qobject.h +++ b/src/corelib/kernel/qobject.h @@ -71,6 +71,7 @@ class QObjectPrivate; class QObject; class QThread; class QWidget; +class QAccessibleWidget; #ifndef QT_NO_REGEXP class QRegExp; #endif @@ -459,6 +460,7 @@ protected: friend class QCoreApplication; friend class QCoreApplicationPrivate; friend class QWidget; + friend class QAccessibleWidget; friend class QThreadData; private: diff --git a/src/widgets/accessible/qaccessiblewidget.cpp b/src/widgets/accessible/qaccessiblewidget.cpp index c96d213e7b..27e6b09dc7 100644 --- a/src/widgets/accessible/qaccessiblewidget.cpp +++ b/src/widgets/accessible/qaccessiblewidget.cpp @@ -273,22 +273,6 @@ QRect QAccessibleWidget::rect() const return QRect(wpos.x(), wpos.y(), w->width(), w->height()); } -QT_BEGIN_INCLUDE_NAMESPACE -#include -QT_END_INCLUDE_NAMESPACE - -class QACConnectionObject : public QObject -{ - Q_DECLARE_PRIVATE(QObject) -public: - inline bool isSender(const QObject *receiver, const char *signal) const - { return d_func()->isSender(receiver, signal); } - inline QObjectList receiverList(const char *signal) const - { return d_func()->receiverList(signal); } - inline QObjectList senderList() const - { return d_func()->senderList(); } -}; - /*! Registers \a signal as a controlling signal. @@ -347,9 +331,9 @@ QAccessibleWidget::relations(QAccessible::Relation match /*= QAccessible::AllRel if (match & QAccessible::Controlled) { QObjectList allReceivers; - QACConnectionObject *connectionObject = (QACConnectionObject*)object(); + QObject *connectionObject = object(); for (int sig = 0; sig < d->primarySignals.count(); ++sig) { - const QObjectList receivers = connectionObject->receiverList(d->primarySignals.at(sig).toLatin1()); + const QObjectList receivers = connectionObject->d_func()->receiverList(d->primarySignals.at(sig).toLatin1()); allReceivers += receivers; } -- cgit v1.2.3 From a4f5f25eb05f49474eaef665abdbcaad9203ba48 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Wed, 5 Jun 2019 10:50:28 +0200 Subject: Move YearRange to QDateTime from its Private As planned when adding YearRange: now that it's merged up to dev, move it to QDateTime, since we can add to the API at 5.14.0. This follows up on commit 82ad4be4a2e0c2bccb6cd8ea2440aefee4ec48ec. Change-Id: I81b6c2331121a71e2592514781c02c5756e70c52 Reviewed-by: Thiago Macieira --- src/corelib/time/qdatetime.cpp | 23 +++++++++++++++++++++-- src/corelib/time/qdatetime.h | 4 ++++ src/corelib/time/qdatetime_p.h | 4 ---- src/corelib/time/qtimezoneprivate_tz.cpp | 5 ++--- 4 files changed, 27 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/corelib/time/qdatetime.cpp b/src/corelib/time/qdatetime.cpp index b3e747c588..31206f779d 100644 --- a/src/corelib/time/qdatetime.cpp +++ b/src/corelib/time/qdatetime.cpp @@ -386,7 +386,7 @@ static constexpr int daysInUsualMonth(int month) // (February isn't usual.) for technical reasons limited to between -784350574879 and 784354017364, which means from before 2 billion BCE to after 2 billion CE. - \sa QTime, QDateTime, QDateEdit, QDateTimeEdit, QCalendarWidget + \sa QTime, QDateTime, QDateTime::YearRange, QDateEdit, QDateTimeEdit, QCalendarWidget */ /*! @@ -3387,6 +3387,25 @@ inline qint64 QDateTimePrivate::zoneMSecsToEpochMSecs(qint64 zoneMSecs, const QT \sa QDate, QTime, QDateTimeEdit, QTimeZone */ +/*! + \enum QDateTime::YearRange + + This enumerated type describes the range of years (in the Gregorian + calendar) representable by QDateTime: + + \value First The later parts of this year are representable + \value Last The earlier parts of this year are representable + + All dates strictly between these two years are also representable. + Note, however, that the Gregorian Calendar has no year zero. + + \note QDate can describe dates in a wider range of years. For most + purposes, this makes little difference, as the range of years that QDateTime + can support reaches 292 million years either side of 1970. + + \sa isValid(), QDate +*/ + /*! Constructs a null datetime (i.e. null date and null time). A null datetime is invalid, since the date is invalid. @@ -3535,7 +3554,7 @@ bool QDateTime::isNull() const hour, i.e. if the transition is at 2am and the clock goes forward to 3am then the time from 02:00:00 to 02:59:59.999 is considered to be invalid. - \sa QDate::isValid(), QTime::isValid() + \sa QDateTime::YearRange, QDate::isValid(), QTime::isValid() */ bool QDateTime::isValid() const diff --git a/src/corelib/time/qdatetime.h b/src/corelib/time/qdatetime.h index 3e3b953b52..89ea4ee24a 100644 --- a/src/corelib/time/qdatetime.h +++ b/src/corelib/time/qdatetime.h @@ -384,6 +384,10 @@ public: NSDate *toNSDate() const Q_DECL_NS_RETURNS_AUTORELEASED; #endif + // (1<<63) ms is 292277024.6 (average Gregorian) years, counted from the start of 1970, so + // Last is floor(1970 + 292277024.6); no year 0, so First is floor(1970 - 1 - 292277024.6) + enum class YearRange : qint32 { First = -292275056, Last = +292278994 }; + private: friend class QDateTimePrivate; diff --git a/src/corelib/time/qdatetime_p.h b/src/corelib/time/qdatetime_p.h index 6018f8f7b0..6e4120d762 100644 --- a/src/corelib/time/qdatetime_p.h +++ b/src/corelib/time/qdatetime_p.h @@ -140,10 +140,6 @@ public: // Inlined for its one caller in qdatetime.cpp inline void setUtcOffsetByTZ(qint64 atMSecsSinceEpoch); #endif // timezone - - // ### Qt 5.14: expose publicly in QDateTime - // The first and last years of which QDateTime can represent some part: - enum class YearRange : qint32 { First = -292275056, Last = +292278994 }; }; QT_END_NAMESPACE diff --git a/src/corelib/time/qtimezoneprivate_tz.cpp b/src/corelib/time/qtimezoneprivate_tz.cpp index f62d7998c8..3c2695a789 100644 --- a/src/corelib/time/qtimezoneprivate_tz.cpp +++ b/src/corelib/time/qtimezoneprivate_tz.cpp @@ -39,7 +39,6 @@ #include "qtimezone.h" #include "qtimezoneprivate_p.h" -#include "qdatetime_p.h" // ### Qt 5.14: remove once YearRange is on QDateTime #include "private/qlocale_tools_p.h" #include @@ -589,8 +588,8 @@ static QVector calculatePosixTransitions(const QByteArra stdTime = QTime(2, 0, 0); // Limit year to the range QDateTime can represent: - const int minYear = int(QDateTimePrivate::YearRange::First); - const int maxYear = int(QDateTimePrivate::YearRange::Last); + const int minYear = int(QDateTime::YearRange::First); + const int maxYear = int(QDateTime::YearRange::Last); startYear = qBound(minYear, startYear, maxYear); endYear = qBound(minYear, endYear, maxYear); Q_ASSERT(startYear <= endYear); -- cgit v1.2.3 From 8f4baa3a223ad6f12101d5c4c10756a98d340ae6 Mon Sep 17 00:00:00 2001 From: Mat Sutcliffe Date: Mon, 1 Jul 2019 11:16:55 +0000 Subject: QStringList: utilize QStringMatcher's support for QStringView This opportunity was missed when adding the QStringView overload of QStringList::filter() in 2a99f60cfbe8a10c8b64b2178573dc8da3d27abe. Change-Id: I8d679b92de6cc76c4d59fd54f01a25579ab3488f Reviewed-by: Anton Kudryavtsev Reviewed-by: Marc Mutz --- src/corelib/tools/qstringlist.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/corelib/tools/qstringlist.cpp b/src/corelib/tools/qstringlist.cpp index 3bb0e3c886..4bbe424ed2 100644 --- a/src/corelib/tools/qstringlist.cpp +++ b/src/corelib/tools/qstringlist.cpp @@ -313,7 +313,7 @@ void QtPrivate::QStringList_sort(QStringList *that, Qt::CaseSensitivity cs) QStringList QtPrivate::QStringList_filter(const QStringList *that, QStringView str, Qt::CaseSensitivity cs) { - QStringMatcher matcher(str.data(), str.length(), cs); + QStringMatcher matcher(str, cs); QStringList res; for (int i = 0; i < that->size(); ++i) if (matcher.indexIn(that->at(i)) != -1) -- cgit v1.2.3 From b727d2f0e152243c1c46e2916dcb3c8481f03ebe Mon Sep 17 00:00:00 2001 From: Tasuku Suzuki Date: Thu, 30 May 2019 23:44:44 +0900 Subject: Fix build without feature.dockwidget and tabwidget Change-Id: I621664f646475c1b5cfde47b7087c0c9f5ad8e4d Reviewed-by: Richard Moe Gustavsen --- src/plugins/styles/mac/qmacstyle_mac.mm | 7 ++++++- src/widgets/widgets/qmainwindowlayout.cpp | 8 +++++--- 2 files changed, 11 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/plugins/styles/mac/qmacstyle_mac.mm b/src/plugins/styles/mac/qmacstyle_mac.mm index 6c4605b5d2..675f37c4c7 100644 --- a/src/plugins/styles/mac/qmacstyle_mac.mm +++ b/src/plugins/styles/mac/qmacstyle_mac.mm @@ -233,6 +233,7 @@ static QLinearGradient titlebarGradientInactive() return qt_mac_applicationIsInDarkMode() ? darkGradient : lightGradient; } +#if QT_CONFIG(tabwidget) static void clipTabBarFrame(const QStyleOption *option, const QMacStyle *style, CGContextRef ctx) { Q_ASSERT(option); @@ -252,6 +253,7 @@ static void clipTabBarFrame(const QStyleOption *option, const QMacStyle *style, CGContextClipToRects(ctx, &cgRects[0], size_t(cgRects.size())); } } +#endif static const QColor titlebarSeparatorLineActive(111, 111, 111); static const QColor titlebarSeparatorLineInactive(131, 131, 131); @@ -2949,7 +2951,7 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai p->restore(); return; } - +#if QT_CONFIG(tabwidget) QRegion region(tbb->rect); region -= tbb->tabBarRect; p->save(); @@ -2973,6 +2975,7 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai } proxy()->drawPrimitive(PE_FrameTabWidget, &twf, p, w); p->restore(); +#endif } break; #endif @@ -3019,8 +3022,10 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai needTranslation = true; } d->drawNSViewInRect(box, adjustedRect, p, ^(CGContextRef ctx, const CGRect &rect) { +#if QT_CONFIG(tabwidget) if (QTabWidget *tabWidget = qobject_cast(opt->styleObject)) clipTabBarFrame(opt, this, ctx); +#endif CGContextTranslateCTM(ctx, 0, rect.origin.y + rect.size.height); CGContextScaleCTM(ctx, 1, -1); if (QOperatingSystemVersion::current() < QOperatingSystemVersion::MacOSMojave diff --git a/src/widgets/widgets/qmainwindowlayout.cpp b/src/widgets/widgets/qmainwindowlayout.cpp index b8f997b782..0e67c56afd 100644 --- a/src/widgets/widgets/qmainwindowlayout.cpp +++ b/src/widgets/widgets/qmainwindowlayout.cpp @@ -2413,7 +2413,7 @@ static bool unplugGroup(QMainWindowLayout *layout, QLayoutItem **item, */ QLayoutItem *QMainWindowLayout::unplug(QWidget *widget, bool group) { -#if QT_CONFIG(dockwidget) && QT_CONFIG(tabbar) +#if QT_CONFIG(dockwidget) && QT_CONFIG(tabwidget) auto *groupWindow = qobject_cast(widget->parentWidget()); if (!widget->isWindow() && groupWindow) { if (group && groupWindow->tabLayoutInfo()) { @@ -2525,10 +2525,10 @@ void QMainWindowLayout::updateGapIndicator() #endif // QT_CONFIG(rubberband) } +#if QT_CONFIG(dockwidget) && QT_CONFIG(tabwidget) static QTabBar::Shape tabwidgetPositionToTabBarShape(QWidget *w) { QTabBar::Shape result = QTabBar::RoundedSouth; -#if QT_CONFIG(tabwidget) if (qobject_cast(w)) { switch (static_cast(qt_widget_private(w))->tabPosition) { case QTabWidget::North: @@ -2545,9 +2545,9 @@ static QTabBar::Shape tabwidgetPositionToTabBarShape(QWidget *w) break; } } -#endif // tabwidget return result; } +#endif // QT_CONFIG(dockwidget) && QT_CONFIG(tabwidget) void QMainWindowLayout::hover(QLayoutItem *widgetItem, const QPoint &mousePos) { @@ -2591,6 +2591,7 @@ void QMainWindowLayout::hover(QLayoutItem *widgetItem, const QPoint &mousePos) if (!w->geometry().contains(mousePos)) continue; +#if QT_CONFIG(tabwidget) if (auto dropTo = qobject_cast(w)) { // dropping to a normal widget, we mutate it in a QDockWidgetGroupWindow with two // tabs @@ -2612,6 +2613,7 @@ void QMainWindowLayout::hover(QLayoutItem *widgetItem, const QPoint &mousePos) w = floatingTabs; widget->raise(); // raise, as our newly created drop target is now on top } +#endif Q_ASSERT(qobject_cast(w)); auto group = static_cast(w); if (group->hover(widgetItem, group->mapFromGlobal(mousePos))) { -- cgit v1.2.3 From 377ffbd21d372193c8fa0d05dc02d6f606a78a35 Mon Sep 17 00:00:00 2001 From: Tasuku Suzuki Date: Fri, 31 May 2019 00:58:50 +0900 Subject: Fix build without feature.tabbar Change-Id: I0891f8f6054382407f5ce2fdb3ead0203d255945 Reviewed-by: Richard Moe Gustavsen --- src/plugins/styles/mac/qmacstyle_mac.mm | 10 +++++- src/widgets/widgets/qdockarealayout.cpp | 9 +++--- src/widgets/widgets/qdockarealayout_p.h | 4 +++ src/widgets/widgets/qdockwidget.cpp | 4 +++ src/widgets/widgets/qmainwindow.cpp | 6 ++-- src/widgets/widgets/qmainwindow.h | 2 ++ src/widgets/widgets/qmainwindowlayout.cpp | 53 ++++++++++++++++++------------- src/widgets/widgets/qmainwindowlayout_p.h | 8 +++-- 8 files changed, 61 insertions(+), 35 deletions(-) (limited to 'src') diff --git a/src/plugins/styles/mac/qmacstyle_mac.mm b/src/plugins/styles/mac/qmacstyle_mac.mm index 675f37c4c7..85bf71be3f 100644 --- a/src/plugins/styles/mac/qmacstyle_mac.mm +++ b/src/plugins/styles/mac/qmacstyle_mac.mm @@ -278,6 +278,7 @@ static const qreal titleBarButtonSpacing = 8; // hovered: tab is hovered bool isDarkMode() { return qt_mac_applicationIsInDarkMode(); } +#if QT_CONFIG(tabbar) static const QColor lightTabBarTabBackgroundActive(190, 190, 190); static const QColor darkTabBarTabBackgroundActive(38, 38, 38); static const QColor tabBarTabBackgroundActive() { return isDarkMode() ? darkTabBarTabBackgroundActive : lightTabBarTabBackgroundActive; } @@ -323,6 +324,7 @@ static const QColor tabBarCloseButtonCrossSelected(115, 115, 115); static const int closeButtonSize = 14; static const qreal closeButtonCornerRadius = 2.0; +#endif // QT_CONFIG(tabbar) static const int headerSectionArrowHeight = 6; static const int headerSectionSeparatorInset = 2; @@ -467,6 +469,7 @@ static bool isInMacUnifiedToolbarArea(QWindow *window, int windowY) } +#if QT_CONFIG(tabbar) static void drawTabCloseButton(QPainter *p, bool hover, bool selected, bool pressed, bool documentMode) { p->setRenderHints(QPainter::Antialiasing); @@ -504,7 +507,6 @@ static void drawTabCloseButton(QPainter *p, bool hover, bool selected, bool pres p->drawLine(margin, height - margin, width - margin, margin); } -#if QT_CONFIG(tabbar) QRect rotateTabPainter(QPainter *p, QTabBar::Shape shape, QRect tabRect) { const auto tabDirection = QMacStylePrivate::tabDirection(shape); @@ -2131,10 +2133,12 @@ int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QW int ret = 0; switch (metric) { +#if QT_CONFIG(tabbar) case PM_TabCloseIndicatorWidth: case PM_TabCloseIndicatorHeight: ret = closeButtonSize; break; +#endif case PM_ToolBarIconSize: ret = proxy()->pixelMetric(PM_LargeIconSize); break; @@ -2291,10 +2295,12 @@ int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QW ret = 16; break; case QStyleHelper::SizeDefault: +#if QT_CONFIG(tabbar) const QStyleOptionTab *tb = qstyleoption_cast(opt); if (tb && tb->documentMode) ret = 30; else +#endif ret = QCommonStyle::pixelMetric(metric, opt, widget); break; } @@ -3319,6 +3325,7 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai } break; case PE_FrameStatusBarItem: break; +#if QT_CONFIG(tabbar) case PE_IndicatorTabClose: { // Make close button visible only on the hovered tab. QTabBar *tabBar = qobject_cast(w->parentWidget()); @@ -3343,6 +3350,7 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai } } } break; +#endif // QT_CONFIG(tabbar) case PE_PanelStatusBar: { // Fill the status bar with the titlebar gradient. QLinearGradient linearGrad; diff --git a/src/widgets/widgets/qdockarealayout.cpp b/src/widgets/widgets/qdockarealayout.cpp index 54504d124b..55ae42db04 100644 --- a/src/widgets/widgets/qdockarealayout.cpp +++ b/src/widgets/widgets/qdockarealayout.cpp @@ -1678,12 +1678,9 @@ int QDockAreaLayoutInfo::prev(int index) const return -1; } +#if QT_CONFIG(tabbar) void QDockAreaLayoutInfo::tab(int index, QLayoutItem *dockWidgetItem) { -#if !QT_CONFIG(tabbar) - Q_UNUSED(index); - Q_UNUSED(dockWidgetItem); -#else if (tabbed) { item_list.append(QDockAreaLayoutItem(dockWidgetItem)); updateTabBar(); @@ -1699,8 +1696,8 @@ void QDockAreaLayoutInfo::tab(int index, QLayoutItem *dockWidgetItem) new_info->updateTabBar(); new_info->setCurrentTab(dockWidgetItem->widget()); } -#endif // QT_CONFIG(tabbar) } +#endif // QT_CONFIG(tabbar) void QDockAreaLayoutInfo::split(int index, Qt::Orientation orientation, QLayoutItem *dockWidgetItem) @@ -3137,6 +3134,7 @@ void QDockAreaLayout::addDockWidget(QInternal::DockPosition pos, QDockWidget *do removePlaceHolder(dockWidget->objectName()); } +#if QT_CONFIG(tabbar) void QDockAreaLayout::tabifyDockWidget(QDockWidget *first, QDockWidget *second) { const QList path = indexOf(first); @@ -3149,6 +3147,7 @@ void QDockAreaLayout::tabifyDockWidget(QDockWidget *first, QDockWidget *second) removePlaceHolder(second->objectName()); } +#endif // QT_CONFIG(tabbar) void QDockAreaLayout::resizeDocks(const QList &docks, const QList &sizes, Qt::Orientation o) diff --git a/src/widgets/widgets/qdockarealayout_p.h b/src/widgets/widgets/qdockarealayout_p.h index ab9c0c476c..81384bd1b7 100644 --- a/src/widgets/widgets/qdockarealayout_p.h +++ b/src/widgets/widgets/qdockarealayout_p.h @@ -144,7 +144,9 @@ public: void remove(const QList &path); void unnest(int index); void split(int index, Qt::Orientation orientation, QLayoutItem *dockWidgetItem); +#if QT_CONFIG(tabbar) void tab(int index, QLayoutItem *dockWidgetItem); +#endif QDockAreaLayoutItem &item(const QList &path); QDockAreaLayoutInfo *info(const QList &path); QDockAreaLayoutInfo *info(QWidget *widget); @@ -275,7 +277,9 @@ public: bool restoreDockWidget(QDockWidget *dockWidget); void splitDockWidget(QDockWidget *after, QDockWidget *dockWidget, Qt::Orientation orientation); +#if QT_CONFIG(tabbar) void tabifyDockWidget(QDockWidget *first, QDockWidget *second); +#endif void resizeDocks(const QList &docks, const QList &sizes, Qt::Orientation o); void apply(bool animate); diff --git a/src/widgets/widgets/qdockwidget.cpp b/src/widgets/widgets/qdockwidget.cpp index dc3b77b7bc..28a7cee2a0 100644 --- a/src/widgets/widgets/qdockwidget.cpp +++ b/src/widgets/widgets/qdockwidget.cpp @@ -267,9 +267,11 @@ QDockWidgetLayout::~QDockWidgetLayout() bool QDockWidgetLayout::nativeWindowDeco() const { bool floating = parentWidget()->isWindow(); +#if QT_CONFIG(tabbar) if (auto groupWindow = qobject_cast(parentWidget()->parentWidget())) floating = floating || groupWindow->tabLayoutInfo(); +#endif return nativeWindowDeco(floating); } @@ -1556,8 +1558,10 @@ bool QDockWidget::event(QEvent *event) const QObjectList &siblings = win->children(); onTop = siblings.count() > 0 && siblings.last() == (QObject*)this; } +#if QT_CONFIG(tabbar) if (!isFloating() && layout != 0 && onTop) layout->raise(this); +#endif break; } case QEvent::WindowActivate: diff --git a/src/widgets/widgets/qmainwindow.cpp b/src/widgets/widgets/qmainwindow.cpp index 9c4c46f2d6..16ed699137 100644 --- a/src/widgets/widgets/qmainwindow.cpp +++ b/src/widgets/widgets/qmainwindow.cpp @@ -1138,6 +1138,7 @@ void QMainWindow::splitDockWidget(QDockWidget *after, QDockWidget *dockwidget, d_func()->layout->splitDockWidget(after, dockwidget, orientation); } +#if QT_CONFIG(tabbar) /*! \fn void QMainWindow::tabifyDockWidget(QDockWidget *first, QDockWidget *second) @@ -1164,9 +1165,6 @@ void QMainWindow::tabifyDockWidget(QDockWidget *first, QDockWidget *second) QList QMainWindow::tabifiedDockWidgets(QDockWidget *dockwidget) const { QList ret; -#if !QT_CONFIG(tabbar) - Q_UNUSED(dockwidget); -#else const QDockAreaLayoutInfo *info = d_func()->layout->layoutState.dockAreaLayout.info(dockwidget); if (info && info->tabbed && info->tabBar) { for(int i = 0; i < info->item_list.count(); ++i) { @@ -1180,9 +1178,9 @@ QList QMainWindow::tabifiedDockWidgets(QDockWidget *dockwidget) co } } } -#endif return ret; } +#endif // QT_CONFIG(tabbar) /*! diff --git a/src/widgets/widgets/qmainwindow.h b/src/widgets/widgets/qmainwindow.h index 85e3f87d77..c69451fa3e 100644 --- a/src/widgets/widgets/qmainwindow.h +++ b/src/widgets/widgets/qmainwindow.h @@ -171,8 +171,10 @@ public: Qt::Orientation orientation); void splitDockWidget(QDockWidget *after, QDockWidget *dockwidget, Qt::Orientation orientation); +#if QT_CONFIG(tabbar) void tabifyDockWidget(QDockWidget *first, QDockWidget *second); QList tabifiedDockWidgets(QDockWidget *dockwidget) const; +#endif void removeDockWidget(QDockWidget *dockwidget); bool restoreDockWidget(QDockWidget *dockwidget); diff --git a/src/widgets/widgets/qmainwindowlayout.cpp b/src/widgets/widgets/qmainwindowlayout.cpp index 0e67c56afd..0fb3a86cf8 100644 --- a/src/widgets/widgets/qmainwindowlayout.cpp +++ b/src/widgets/widgets/qmainwindowlayout.cpp @@ -292,25 +292,31 @@ bool QDockWidgetGroupWindow::event(QEvent *e) switch (e->type()) { case QEvent::Close: +#if QT_CONFIG(tabbar) // Forward the close to the QDockWidget just as if its close button was pressed if (QDockWidget *dw = activeTabbedDockWidget()) { e->ignore(); dw->close(); adjustFlags(); } +#endif return true; case QEvent::Move: +#if QT_CONFIG(tabbar) // Let QDockWidgetPrivate::moseEvent handle the dragging if (QDockWidget *dw = activeTabbedDockWidget()) static_cast(QObjectPrivate::get(dw))->moveEvent(static_cast(e)); +#endif return true; case QEvent::NonClientAreaMouseMove: case QEvent::NonClientAreaMouseButtonPress: case QEvent::NonClientAreaMouseButtonRelease: case QEvent::NonClientAreaMouseButtonDblClick: +#if QT_CONFIG(tabbar) // Let the QDockWidgetPrivate of the currently visible dock widget handle the drag and drop if (QDockWidget *dw = activeTabbedDockWidget()) static_cast(QObjectPrivate::get(dw))->nonClientAreaMouseEvent(static_cast(e)); +#endif return true; case QEvent::ChildAdded: if (qobject_cast(static_cast(e)->child())) @@ -347,6 +353,7 @@ QDockAreaLayoutInfo *QDockWidgetGroupWindow::layoutInfo() const return static_cast(layout())->dockAreaLayoutInfo(); } +#if QT_CONFIG(tabbar) /*! \internal If this is a floating tab bar returns the currently the QDockWidgetGroupWindow that contains tab, otherwise, return nullptr; @@ -383,7 +390,6 @@ const QDockAreaLayoutInfo *QDockWidgetGroupWindow::tabLayoutInfo() const QDockWidget *QDockWidgetGroupWindow::activeTabbedDockWidget() const { QDockWidget *dw = nullptr; -#if QT_CONFIG(tabbar) const QDockAreaLayoutInfo *info = tabLayoutInfo(); if (!info) return nullptr; @@ -405,9 +411,9 @@ QDockWidget *QDockWidgetGroupWindow::activeTabbedDockWidget() const dw = qobject_cast(item.widgetItem->widget()); } } -#endif return dw; } +#endif // QT_CONFIG(tabbar) /*! \internal Destroy or hide this window if there is no more QDockWidget in it. @@ -461,7 +467,11 @@ void QDockWidgetGroupWindow::adjustFlags() Qt::WindowFlags oldFlags = windowFlags(); Qt::WindowFlags flags = oldFlags; +#if QT_CONFIG(tabbar) QDockWidget *top = activeTabbedDockWidget(); +#else + QDockWidget *top = nullptr; +#endif if (!top) { // nested tabs, show window decoration flags = ((oldFlags & ~Qt::FramelessWindowHint) | Qt::CustomizeWindowHint | Qt::WindowTitleHint); @@ -507,6 +517,7 @@ void QDockWidgetGroupWindow::adjustFlags() bool QDockWidgetGroupWindow::hasNativeDecos() const { +#if QT_CONFIG(tabbar) QDockWidget *dw = activeTabbedDockWidget(); if (!dw) // We have a group of nested QDockWidgets (not just floating tabs) return true; @@ -515,6 +526,9 @@ bool QDockWidgetGroupWindow::hasNativeDecos() const return false; return dw->titleBarWidget() == nullptr; +#else + return true; +#endif } /* @@ -531,16 +545,18 @@ bool QDockWidgetGroupWindow::hover(QLayoutItem *widgetItem, const QPoint &mouseP savedState = *layoutInfo(); QMainWindow::DockOptions opts = static_cast(parentWidget())->dockOptions(); + QDockAreaLayoutInfo newState = savedState; bool nestingEnabled = (opts & QMainWindow::AllowNestedDocks) && !(opts & QMainWindow::ForceTabbedDocks); QDockAreaLayoutInfo::TabMode tabMode = +#if !QT_CONFIG(tabbar) + QDockAreaLayoutInfo::NoTabs; +#else nestingEnabled ? QDockAreaLayoutInfo::AllowTabs : QDockAreaLayoutInfo::ForceTabs; if (auto group = qobject_cast(widgetItem->widget())) { if (!group->tabLayoutInfo()) tabMode = QDockAreaLayoutInfo::NoTabs; } - - QDockAreaLayoutInfo newState = savedState; if (newState.tabbed) { // insertion into a top-level tab newState.item_list = { QDockAreaLayoutItem(new QDockAreaLayoutInfo(newState)) }; @@ -548,6 +564,7 @@ bool QDockWidgetGroupWindow::hover(QLayoutItem *widgetItem, const QPoint &mouseP newState.tabbed = false; newState.tabBar = nullptr; } +#endif auto newGapPos = newState.gapIndex(mousePos, nestingEnabled, tabMode); Q_ASSERT(!newGapPos.isEmpty()); @@ -1498,14 +1515,6 @@ void QMainWindowLayout::addDockWidget(Qt::DockWidgetArea area, invalidate(); } -void QMainWindowLayout::tabifyDockWidget(QDockWidget *first, QDockWidget *second) -{ - addChildWidget(second); - layoutState.dockAreaLayout.tabifyDockWidget(first, second); - emit second->dockLocationChanged(dockWidgetArea(first)); - invalidate(); -} - bool QMainWindowLayout::restoreDockWidget(QDockWidget *dockwidget) { addChildWidget(dockwidget); @@ -1517,6 +1526,14 @@ bool QMainWindowLayout::restoreDockWidget(QDockWidget *dockwidget) } #if QT_CONFIG(tabbar) +void QMainWindowLayout::tabifyDockWidget(QDockWidget *first, QDockWidget *second) +{ + addChildWidget(second); + layoutState.dockAreaLayout.tabifyDockWidget(first, second); + emit second->dockLocationChanged(dockWidgetArea(first)); + invalidate(); +} + bool QMainWindowLayout::documentMode() const { return _documentMode; @@ -1535,20 +1552,15 @@ void QMainWindowLayout::setDocumentMode(bool enabled) for (QTabBar *bar : qAsConst(unusedTabBars)) bar->setDocumentMode(_documentMode); } -#endif // QT_CONFIG(tabbar) void QMainWindowLayout::setVerticalTabsEnabled(bool enabled) { -#if !QT_CONFIG(tabbar) - Q_UNUSED(enabled); -#else if (verticalTabsEnabled == enabled) return; verticalTabsEnabled = enabled; updateTabBarShapes(); -#endif // QT_CONFIG(tabbar) } #if QT_CONFIG(tabwidget) @@ -1609,7 +1621,6 @@ static inline QTabBar::Shape tabBarShapeFrom(QTabWidget::TabShape shape, QTabWid } #endif // QT_CONFIG(tabwidget) -#if QT_CONFIG(tabbar) void QMainWindowLayout::updateTabBarShapes() { #if QT_CONFIG(tabwidget) @@ -1851,19 +1862,17 @@ void QMainWindowLayout::tabMoved(int from, int to) info->moveTab(from, to); } -#endif // QT_CONFIG(tabbar) void QMainWindowLayout::raise(QDockWidget *widget) { -#if QT_CONFIG(tabbar) QDockAreaLayoutInfo *info = dockInfo(widget); if (info == 0) return; if (!info->tabbed) return; info->setCurrentTab(widget); -#endif } +#endif // QT_CONFIG(tabbar) #endif // QT_CONFIG(dockwidget) @@ -2329,7 +2338,7 @@ void QMainWindowLayout::setDockOptions(QMainWindow::DockOptions opts) dockOptions = opts; -#if QT_CONFIG(dockwidget) +#if QT_CONFIG(dockwidget) && QT_CONFIG(tabbar) setVerticalTabsEnabled(opts & QMainWindow::VerticalTabs); #endif diff --git a/src/widgets/widgets/qmainwindowlayout_p.h b/src/widgets/widgets/qmainwindowlayout_p.h index 7cdb8ead2f..967b713096 100644 --- a/src/widgets/widgets/qmainwindowlayout_p.h +++ b/src/widgets/widgets/qmainwindowlayout_p.h @@ -337,8 +337,10 @@ public: explicit QDockWidgetGroupWindow(QWidget* parent = nullptr, Qt::WindowFlags f = nullptr) : QWidget(parent, f) {} QDockAreaLayoutInfo *layoutInfo() const; +#if QT_CONFIG(tabbar) const QDockAreaLayoutInfo *tabLayoutInfo() const; QDockWidget *activeTabbedDockWidget() const; +#endif void destroyOrHideIfEmpty(); void adjustFlags(); bool hasNativeDecos() const; @@ -494,13 +496,13 @@ public: void splitDockWidget(QDockWidget *after, QDockWidget *dockwidget, Qt::Orientation orientation); - void tabifyDockWidget(QDockWidget *first, QDockWidget *second); Qt::DockWidgetArea dockWidgetArea(QWidget* widget) const; + bool restoreDockWidget(QDockWidget *dockwidget); +#if QT_CONFIG(tabbar) + void tabifyDockWidget(QDockWidget *first, QDockWidget *second); void raise(QDockWidget *widget); void setVerticalTabsEnabled(bool enabled); - bool restoreDockWidget(QDockWidget *dockwidget); -#if QT_CONFIG(tabbar) QDockAreaLayoutInfo *dockInfo(QWidget *w); bool _documentMode; bool documentMode() const; -- cgit v1.2.3 From fdef9c80391c23875208d8576096c0d5366fedc4 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Thu, 27 Jun 2019 15:07:01 +0200 Subject: uic: Implement form window setting to disable QObject::connectSlotsByName() Task-number: QTBUG-76375 Change-Id: I16ad147366aa7d52b7a0e17ae240127d8ac34b3c Reviewed-by: Jarek Kobus --- src/tools/uic/cpp/cppwriteinitialization.cpp | 5 ++++- src/tools/uic/cpp/cppwriteinitialization.h | 1 + src/tools/uic/ui4.cpp | 7 +++++++ src/tools/uic/ui4.h | 8 ++++++++ 4 files changed, 20 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/tools/uic/cpp/cppwriteinitialization.cpp b/src/tools/uic/cpp/cppwriteinitialization.cpp index 4185d3ba70..d16c859eed 100644 --- a/src/tools/uic/cpp/cppwriteinitialization.cpp +++ b/src/tools/uic/cpp/cppwriteinitialization.cpp @@ -467,6 +467,9 @@ void WriteInitialization::acceptUI(DomUI *node) m_widgetChain.push(nullptr); m_layoutChain.push(nullptr); + if (node->hasAttributeConnectslotsbyname()) + m_connectSlotsByName = node->attributeConnectslotsbyname(); + acceptLayoutDefault(node->elementLayoutDefault()); acceptLayoutFunction(node->elementLayoutFunction()); @@ -536,7 +539,7 @@ void WriteInitialization::acceptUI(DomUI *node) if (!m_delayedInitialization.isEmpty()) m_output << "\n" << m_delayedInitialization << "\n"; - if (m_option.autoConnection) { + if (m_option.autoConnection && m_connectSlotsByName) { m_output << "\n" << m_indent << "QMetaObject" << language::qualifier << "connectSlotsByName(" << varName << ')' << language::eol; } diff --git a/src/tools/uic/cpp/cppwriteinitialization.h b/src/tools/uic/cpp/cppwriteinitialization.h index a28dfc1b25..6f8e352f6a 100644 --- a/src/tools/uic/cpp/cppwriteinitialization.h +++ b/src/tools/uic/cpp/cppwriteinitialization.h @@ -311,6 +311,7 @@ private: bool m_layoutWidget = false; bool m_firstThemeIcon = true; + bool m_connectSlotsByName = true; }; } // namespace CPP diff --git a/src/tools/uic/ui4.cpp b/src/tools/uic/ui4.cpp index 984ef36274..334ced276d 100644 --- a/src/tools/uic/ui4.cpp +++ b/src/tools/uic/ui4.cpp @@ -76,6 +76,10 @@ void DomUI::read(QXmlStreamReader &reader) setAttributeIdbasedtr(attribute.value() == QLatin1String("true")); continue; } + if (name == QLatin1String("connectslotsbyname")) { + setAttributeConnectslotsbyname(attribute.value() == QLatin1String("true")); + continue; + } if (name == QLatin1String("stdsetdef")) { setAttributeStdsetdef(attribute.value().toInt()); continue; @@ -209,6 +213,9 @@ void DomUI::write(QXmlStreamWriter &writer, const QString &tagName) const if (hasAttributeIdbasedtr()) writer.writeAttribute(QStringLiteral("idbasedtr"), (attributeIdbasedtr() ? QLatin1String("true") : QLatin1String("false"))); + if (hasAttributeConnectslotsbyname()) + writer.writeAttribute(QStringLiteral("connectslotsbyname"), (attributeConnectslotsbyname() ? QLatin1String("true") : QLatin1String("false"))); + if (hasAttributeStdsetdef()) writer.writeAttribute(QStringLiteral("stdsetdef"), QString::number(attributeStdsetdef())); diff --git a/src/tools/uic/ui4.h b/src/tools/uic/ui4.h index 1710147342..08a3abf490 100644 --- a/src/tools/uic/ui4.h +++ b/src/tools/uic/ui4.h @@ -169,6 +169,11 @@ public: inline void setAttributeIdbasedtr(bool a) { m_attr_idbasedtr = a; m_has_attr_idbasedtr = true; } inline void clearAttributeIdbasedtr() { m_has_attr_idbasedtr = false; } + inline bool hasAttributeConnectslotsbyname() const { return m_has_attr_connectslotsbyname; } + inline bool attributeConnectslotsbyname() const { return m_attr_connectslotsbyname; } + inline void setAttributeConnectslotsbyname(bool a) { m_attr_connectslotsbyname = a; m_has_attr_connectslotsbyname = true; } + inline void clearAttributeConnectslotsbyname() { m_has_attr_connectslotsbyname = false; } + inline bool hasAttributeStdsetdef() const { return m_has_attr_stdsetdef; } inline int attributeStdsetdef() const { return m_attr_stdsetdef; } inline void setAttributeStdsetdef(int a) { m_attr_stdsetdef = a; m_has_attr_stdsetdef = true; } @@ -285,6 +290,9 @@ private: bool m_attr_idbasedtr = false; bool m_has_attr_idbasedtr = false; + bool m_attr_connectslotsbyname = false; + bool m_has_attr_connectslotsbyname = false; + int m_attr_stdsetdef = 0; bool m_has_attr_stdsetdef = false; -- cgit v1.2.3 From c34242c679aaea6ee1badf6c1e5f274f925f5f50 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 1 Jul 2019 14:10:43 +0200 Subject: Make the default ctor of QVarLengthArray implicit Otherwise "QVarLengthArray x = {};" gives a warning. Also, some compilers get confused about "QVarLengthArray()" this way. Task-number: QTBUG-76199 Change-Id: I4296586c0181d3e6e82ca8b7b79aeb9a21645d1f Reviewed-by: Marc Mutz --- src/corelib/tools/qvarlengtharray.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/corelib/tools/qvarlengtharray.h b/src/corelib/tools/qvarlengtharray.h index a49e0af687..ba65ae7ef2 100644 --- a/src/corelib/tools/qvarlengtharray.h +++ b/src/corelib/tools/qvarlengtharray.h @@ -61,7 +61,9 @@ template class QVarLengthArray { public: - inline explicit QVarLengthArray(int size = 0); + QVarLengthArray() : QVarLengthArray(0) {} + + inline explicit QVarLengthArray(int size); inline QVarLengthArray(const QVarLengthArray &other) : a(Prealloc), s(0), ptr(reinterpret_cast(array)) -- cgit v1.2.3 From 8c8eae279d57ff73b69620f5710b8290d6268051 Mon Sep 17 00:00:00 2001 From: Sona Kurazyan Date: Wed, 26 Jun 2019 12:48:21 +0200 Subject: Remove usages of deprecated APIs from QDateTime - Replaced the usages of: * QDateTime::toTime_t() -> QDateTime::toSecsSinceEpoch(). * QDateTime::fromTime_t() -> QDateTime::fromSecsSinceEpoch(). * QDate::shortDayName() -> QLocale::system().dayName(). * QTime by QElapsedTimer, where the deprecated methods of QTime were used. - Modified the tests for the deprecated methods to be enabled only when the corresponding methods are enabled: when the deprecated APIs are disabled, the tests will be also disabled, and the compilation won't be broken. Task-number: QTBUG-76491 Change-Id: I4d565db2329e580c567aae511696eb1efe120843 Reviewed-by: Volker Hilsheimer --- src/gui/doc/snippets/textblock-fragments/mainwindow.cpp | 2 +- src/gui/doc/snippets/textdocument-blocks/mainwindow.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/gui/doc/snippets/textblock-fragments/mainwindow.cpp b/src/gui/doc/snippets/textblock-fragments/mainwindow.cpp index bf864ce48d..236d6952e6 100644 --- a/src/gui/doc/snippets/textblock-fragments/mainwindow.cpp +++ b/src/gui/doc/snippets/textblock-fragments/mainwindow.cpp @@ -114,7 +114,7 @@ void MainWindow::insertCalendar() int year = date.year(), month = date.month(); for (int weekDay = 1; weekDay <= 7; ++weekDay) { - cursor.insertText(QString("%1 ").arg(QDate::shortDayName(weekDay), 3), + cursor.insertText(QString("%1 ").arg(QLocale::system().dayName(weekDay), 3), boldFormat); } diff --git a/src/gui/doc/snippets/textdocument-blocks/mainwindow.cpp b/src/gui/doc/snippets/textdocument-blocks/mainwindow.cpp index a5801da67e..849f0e957f 100644 --- a/src/gui/doc/snippets/textdocument-blocks/mainwindow.cpp +++ b/src/gui/doc/snippets/textdocument-blocks/mainwindow.cpp @@ -117,7 +117,7 @@ void MainWindow::insertCalendar() int year = date.year(), month = date.month(); for (int weekDay = 1; weekDay <= 7; ++weekDay) { - cursor.insertText(QString("%1 ").arg(QDate::shortDayName(weekDay), 3), + cursor.insertText(QString("%1 ").arg(QLocale::system().dayName(weekDay), 3), boldFormat); } -- cgit v1.2.3 From d5f65d8814e85611f8eff85c6a7d64813c08206d Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Fri, 21 Jun 2019 12:36:49 +0200 Subject: Remove hook handling code from QEventDispatcherWin32 Since commit 6a7cea64 qt_GetMessageHook is effectively a no-op, and we can remove the complete hook handling code. Change-Id: I90383c0c09c2b0f1d715872de5f9519a879d9bae Reviewed-by: Alex Trotsenko Reviewed-by: Oliver Wolff --- src/corelib/kernel/qeventdispatcher_win.cpp | 43 +--------------------- src/corelib/kernel/qeventdispatcher_win_p.h | 4 -- .../direct2d/qwindowsdirect2dintegration.cpp | 1 - 3 files changed, 1 insertion(+), 47 deletions(-) (limited to 'src') diff --git a/src/corelib/kernel/qeventdispatcher_win.cpp b/src/corelib/kernel/qeventdispatcher_win.cpp index dea6cf6389..c15d740f9e 100644 --- a/src/corelib/kernel/qeventdispatcher_win.cpp +++ b/src/corelib/kernel/qeventdispatcher_win.cpp @@ -100,7 +100,7 @@ LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPA QEventDispatcherWin32Private::QEventDispatcherWin32Private() : threadId(GetCurrentThreadId()), interrupt(false), internalHwnd(0), - getMessageHook(0), wakeUps(0), activateNotifiersPosted(false), + wakeUps(0), activateNotifiersPosted(false), winEventNotifierActivatedEvent(NULL) { } @@ -269,14 +269,6 @@ static inline UINT inputTimerMask() return result; } -LRESULT QT_WIN_CALLBACK qt_GetMessageHook(int code, WPARAM wp, LPARAM lp) -{ - QEventDispatcherWin32 *q = qobject_cast(QAbstractEventDispatcher::instance()); - Q_ASSERT(q != 0); - - return q->d_func()->getMessageHook ? CallNextHookEx(0, code, wp, lp) : 0; -} - // Provide class name and atom for the message window used by // QEventDispatcherWin32Private via Q_GLOBAL_STATIC shared between threads. struct QWindowsMessageWindowClassContext @@ -455,38 +447,11 @@ void QEventDispatcherWin32::createInternalHwnd() return; d->internalHwnd = qt_create_internal_window(this); - installMessageHook(); - // start all normal timers for (int i = 0; i < d->timerVec.count(); ++i) d->registerTimer(d->timerVec.at(i)); } -void QEventDispatcherWin32::installMessageHook() -{ - Q_D(QEventDispatcherWin32); - - if (d->getMessageHook) - return; - - // setup GetMessage hook needed to drive our posted events - d->getMessageHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC) qt_GetMessageHook, NULL, GetCurrentThreadId()); - if (Q_UNLIKELY(!d->getMessageHook)) { - int errorCode = GetLastError(); - qFatal("Qt: INTERNAL ERROR: failed to install GetMessage hook: %d, %ls", - errorCode, qUtf16Printable(qt_error_string(errorCode))); - } -} - -void QEventDispatcherWin32::uninstallMessageHook() -{ - Q_D(QEventDispatcherWin32); - - if (d->getMessageHook) - UnhookWindowsHookEx(d->getMessageHook); - d->getMessageHook = 0; -} - QEventDispatcherWin32::QEventDispatcherWin32(QObject *parent) : QAbstractEventDispatcher(*new QEventDispatcherWin32Private, parent) { @@ -582,10 +547,6 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags) } } if (haveMessage) { - // The Direct2d integration unsets getMessageHook. See QTBUG-42428 - if (!d->getMessageHook) - (void) qt_GetMessageHook(0, PM_REMOVE, reinterpret_cast(&msg)); - if (d->internalHwnd == msg.hwnd && msg.message == WM_QT_SENDPOSTEDEVENTS) { // Set result to 'true', if the message was sent by wakeUp(). if (msg.wParam == WMWP_QT_FROMWAKEUP) { @@ -1043,8 +1004,6 @@ void QEventDispatcherWin32::closingDown() d->timerDict.clear(); d->closingDown = true; - - uninstallMessageHook(); } bool QEventDispatcherWin32::event(QEvent *e) diff --git a/src/corelib/kernel/qeventdispatcher_win_p.h b/src/corelib/kernel/qeventdispatcher_win_p.h index f672530ff8..697c07f912 100644 --- a/src/corelib/kernel/qeventdispatcher_win_p.h +++ b/src/corelib/kernel/qeventdispatcher_win_p.h @@ -73,8 +73,6 @@ class Q_CORE_EXPORT QEventDispatcherWin32 : public QAbstractEventDispatcher protected: void createInternalHwnd(); - void installMessageHook(); - void uninstallMessageHook(); public: explicit QEventDispatcherWin32(QObject *parent = 0); @@ -115,7 +113,6 @@ protected: private: friend LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp); - friend LRESULT QT_WIN_CALLBACK qt_GetMessageHook(int, WPARAM, LPARAM); }; struct QSockNot { @@ -169,7 +166,6 @@ public: // internal window handle used for socketnotifiers/timers/etc HWND internalHwnd; - HHOOK getMessageHook; // for controlling when to send posted events QAtomicInt wakeUps; diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.cpp index 0cf05cb0ac..e637bd13fd 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.cpp +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.cpp @@ -63,7 +63,6 @@ public: QWindowsDirect2DEventDispatcher(QObject *parent = nullptr) : QWindowsGuiEventDispatcher(parent) { - uninstallMessageHook(); // ### Workaround for QTBUG-42428 } }; -- cgit v1.2.3 From 5342805920064dfe64c23690cd1d895a95478154 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Tue, 18 Jun 2019 08:56:16 +0200 Subject: QCoreWlanEngine: port away from Java-style iterators (and other fixes) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The main goal of this patch was to port away from Java-style iterators, to make QtBase QT_NO_JAVA_STYLE_ITERATORS-clean. And this the patch achieves. But I couldn't resist a few drive-by fixes, too, to wit: - Use qDeleteAll() instead of while(!isEmpty()) delete takeFirst() - Use QMap::last() instead of iterating to the end, remembering the last-seen value - Use QMap::contains() instead of QMap::keys().contains() - Use qExchange() instead of copy+clear - Make some functions const (requires the mutex member to be marked as mutable, which is common for mutex members) I am almost certain that getSsidFromNetworkName() cannot work correctly. But this patch does not attempt to change the algorithm. Change-Id: Ifa04d7837bdc0837036c3a7a73f8c51f4e681f42 Reviewed-by: Tor Arne Vestbø Reviewed-by: Timur Pocheptsov --- src/plugins/bearer/corewlan/qcorewlanengine.h | 8 ++-- src/plugins/bearer/corewlan/qcorewlanengine.mm | 61 +++++++------------------- 2 files changed, 20 insertions(+), 49 deletions(-) (limited to 'src') diff --git a/src/plugins/bearer/corewlan/qcorewlanengine.h b/src/plugins/bearer/corewlan/qcorewlanengine.h index 4a431b886e..6dddee66a4 100644 --- a/src/plugins/bearer/corewlan/qcorewlanengine.h +++ b/src/plugins/bearer/corewlan/qcorewlanengine.h @@ -122,9 +122,9 @@ public: QString interfaceName; QMap configurationInterface; void getUserConfigurations(); - QString getNetworkNameFromSsid(const QString &ssid); - QString getSsidFromNetworkName(const QString &name); - bool isKnownSsid(const QString &ssid); + QString getNetworkNameFromSsid(const QString &ssid) const; + QString getSsidFromNetworkName(const QString &name) const; + bool isKnownSsid(const QString &ssid) const; QMap > userProfiles; signals: @@ -135,7 +135,7 @@ protected: private: QList fetchedConfigurations; - QMutex mutex; + mutable QMutex mutex; QStringList foundNetwork(const QString &id, const QString &ssid, const QNetworkConfiguration::StateFlags state, const QString &interfaceName, const QNetworkConfiguration::Purpose purpose); }; diff --git a/src/plugins/bearer/corewlan/qcorewlanengine.mm b/src/plugins/bearer/corewlan/qcorewlanengine.mm index c3dd49ff3e..4644b5af9f 100644 --- a/src/plugins/bearer/corewlan/qcorewlanengine.mm +++ b/src/plugins/bearer/corewlan/qcorewlanengine.mm @@ -203,9 +203,7 @@ void QScanThread::run() } } // add known configurations that are not around. - QMapIterator > i(userProfiles); - while (i.hasNext()) { - i.next(); + for (auto i = userProfiles.cbegin(), end = userProfiles.cend(); i != end; ++i) { QString networkName = i.key(); const QString id = QString::number(qHash(QLatin1String("corewlan:") + networkName)); @@ -215,11 +213,8 @@ void QScanThread::run() const QString ssidId = QString::number(qHash(QLatin1String("corewlan:") + networkSsid)); QNetworkConfiguration::StateFlags state = QNetworkConfiguration::Undefined; QString interfaceName; - QMapIterator ij(i.value()); - while (ij.hasNext()) { - ij.next(); - interfaceName = ij.value(); - } + if (!i.value().isEmpty()) + interfaceName = i.value().last(); if (currentInterfaceServiceActive) { if (networkSsid == QString::fromNSString([currentInterface ssid])) { @@ -269,11 +264,7 @@ QStringList QScanThread::foundNetwork(const QString &id, const QString &name, co QList QScanThread::getConfigurations() { QMutexLocker locker(&mutex); - - QList foundConfigurations = fetchedConfigurations; - fetchedConfigurations.clear(); - - return foundConfigurations; + return qExchange(fetchedConfigurations, {}); } void QScanThread::getUserConfigurations() @@ -363,17 +354,12 @@ void QScanThread::getUserConfigurations() } } -QString QScanThread::getSsidFromNetworkName(const QString &name) +QString QScanThread::getSsidFromNetworkName(const QString &name) const { QMutexLocker locker(&mutex); - QMapIterator > i(userProfiles); - while (i.hasNext()) { - i.next(); - QMap map = i.value(); - QMapIterator ij(i.value()); - while (ij.hasNext()) { - ij.next(); + for (auto i = userProfiles.cbegin(), end = userProfiles.cend(); i != end; ++i) { + for (auto ij = i.value().cbegin(), end = i.value().cend(); ij != end; ++ij) { const QString networkNameHash = QString::number(qHash(QLatin1String("corewlan:") +i.key())); if(name == i.key() || name == networkNameHash) { return ij.key(); @@ -383,36 +369,24 @@ QString QScanThread::getSsidFromNetworkName(const QString &name) return QString(); } -QString QScanThread::getNetworkNameFromSsid(const QString &ssid) +QString QScanThread::getNetworkNameFromSsid(const QString &ssid) const { QMutexLocker locker(&mutex); - QMapIterator > i(userProfiles); - while (i.hasNext()) { - i.next(); - QMap map = i.value(); - QMapIterator ij(i.value()); - while (ij.hasNext()) { - ij.next(); - if(ij.key() == ssid) { - return i.key(); - } - } + for (auto i = userProfiles.cbegin(), end = userProfiles.cend(); i != end; ++i) { + if (i.value().contains(ssid)) + return i.key(); } return QString(); } -bool QScanThread::isKnownSsid(const QString &ssid) +bool QScanThread::isKnownSsid(const QString &ssid) const { QMutexLocker locker(&mutex); - QMapIterator > i(userProfiles); - while (i.hasNext()) { - i.next(); - QMap map = i.value(); - if(map.keys().contains(ssid)) { + for (auto i = userProfiles.cbegin(), end = userProfiles.cend(); i != end; ++i) { + if (i.value().contains(ssid)) return true; - } } return false; } @@ -430,8 +404,7 @@ QCoreWlanEngine::~QCoreWlanEngine() { scanThread->wait(); - while (!foundConfigurations.isEmpty()) - delete foundConfigurations.takeFirst(); + qDeleteAll(qExchange(foundConfigurations, {})); [listener remove]; [listener release]; } @@ -486,9 +459,7 @@ void QCoreWlanEngine::connectToId(const QString &id) const QString idHash2 = QString::number(qHash(QLatin1String("corewlan:") + scanThread->getNetworkNameFromSsid(ptr->name))); QString wantedNetwork; - QMapIterator > i(scanThread->userProfiles); - while (i.hasNext()) { - i.next(); + for (auto i = scanThread->userProfiles.cbegin(), end = scanThread->userProfiles.cend(); i != end; ++i) { wantedNetwork = i.key(); const QString networkNameHash = QString::number(qHash(QLatin1String("corewlan:") + wantedNetwork)); if (id == networkNameHash) { -- cgit v1.2.3 From 5e0a45e932424c5ed661ae518ca21d2ab627db2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Nordheim?= Date: Fri, 21 Jun 2019 13:02:34 +0200 Subject: QSocks5SocketEngine: pass data to application when connected If we end up in the connected state then we should pass on any remaining data immediately instead of waiting until the next time we get a read notification. The other `case`s in the switch might be able to do something similar, but I don't want to introduce that logic now in case it breaks something else, the Connected branch is small and simple to deal with. Should severely reduce flakiness with socks proxy in CI under pressure. Task-number: QTBUG-76367 Change-Id: I0965d4c62a29a25ce6b8dd60862a464279aef0b4 Reviewed-by: Timur Pocheptsov Reviewed-by: Ryan Chu --- src/network/socket/qsocks5socketengine.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp index 6791b85273..d2328f8536 100644 --- a/src/network/socket/qsocks5socketengine.cpp +++ b/src/network/socket/qsocks5socketengine.cpp @@ -1188,6 +1188,8 @@ void QSocks5SocketEnginePrivate::_q_controlSocketReadNotification() break; case RequestMethodSent: parseRequestMethodReply(); + if (socks5State == Connected && data->controlSocket->bytesAvailable()) + _q_controlSocketReadNotification(); break; case Connected: { QByteArray buf; -- cgit v1.2.3 From 1eeab2d27f61485cb53364d40339a8d832a076e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Nordheim?= Date: Mon, 24 Jun 2019 18:55:36 +0200 Subject: QSocks5SocketEngine: account for in-transit signal when waiting for read When calling waitFor{ReadyRead,Disconnected} it will wait for data but if the data is already received and the read notification has been queued (and there's no more data coming in) it will return false. By checking if a read notification has been queued and then handling this we can easily take care of this scenario. Fixes some flaky tests which missed the read data in waitForDisconnect and similar. Fixes: QTBUG-38385 Change-Id: Ic05d59883c1175783e56ff1822b6636c35aec874 Reviewed-by: Volker Hilsheimer --- src/network/socket/qsocks5socketengine.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp index d2328f8536..e7e4d64c32 100644 --- a/src/network/socket/qsocks5socketengine.cpp +++ b/src/network/socket/qsocks5socketengine.cpp @@ -1753,6 +1753,11 @@ bool QSocks5SocketEngine::waitForRead(int msecs, bool *timedOut) return false; if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState) return true; + if (bytesAvailable() && d->readNotificationPending) { + // We've got some data incoming, but the queued call hasn't been performed yet. + // The data is where we expect it to be already, so just return true. + return true; + } // we're connected if (d->mode == QSocks5SocketEnginePrivate::ConnectMode || -- cgit v1.2.3 From 555661b625c40f21a6a3e4c73e928a6e8a46db20 Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Fri, 21 Jun 2019 16:31:04 +0200 Subject: Update visible window's alpha when toggling WA_TranslucentBackground MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QWidgetPrivate::updateIsTranslucent sets the surface format of the window with the alpha based on the translucency attribute, so we need to call this function when the attribute value changes. The test can confirm that the window's requested surface format has changed, we can't rely on what is actually set, and don't have to rely on hard-coded values like 8bit alpha. While WA_NoSystemBackground needs to be set for WA_TranslucentBackground to have an effect, we can't clear the attribute when clearing translucency (as it might have been set explicitly). Change-Id: I238d6930b7e0488397467a4e035b5f530566a1ff Fixes: QTBUG-60822 Reviewed-by: Friedemann Kleint Reviewed-by: Tor Arne Vestbø --- src/widgets/kernel/qwidget.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 6ef3a4f163..50745175b4 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -11474,10 +11474,9 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) } break; case Qt::WA_TranslucentBackground: - if (on) { + if (on) setAttribute(Qt::WA_NoSystemBackground); - d->updateIsTranslucent(); - } + d->updateIsTranslucent(); break; case Qt::WA_AcceptTouchEvents: -- cgit v1.2.3 From 13997cccc9dd9a1242b7707b5e934deae84d253a Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Fri, 21 Jun 2019 12:44:33 +0200 Subject: Remove QWindowsDirect2DEventDispatcher This class was introduced to modify the hook handling of QWindowsGuiEventDispatcher. As the hook handling code is removed now, we can remove QWindowsDirect2DEventDispatcher entirely. Change-Id: I56491a67f163784f43b1025225e536d386cead1d Reviewed-by: Alex Trotsenko Reviewed-by: Oliver Wolff --- .../platforms/direct2d/qwindowsdirect2dintegration.cpp | 15 --------------- .../platforms/direct2d/qwindowsdirect2dintegration.h | 1 - 2 files changed, 16 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.cpp index e637bd13fd..e074f87eb4 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.cpp +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.cpp @@ -51,21 +51,11 @@ #include #include #include -#include #include QT_BEGIN_NAMESPACE -class QWindowsDirect2DEventDispatcher : public QWindowsGuiEventDispatcher -{ -public: - QWindowsDirect2DEventDispatcher(QObject *parent = nullptr) - : QWindowsGuiEventDispatcher(parent) - { - } -}; - class QWindowsDirect2DIntegrationPrivate { public: @@ -187,11 +177,6 @@ QPlatformBackingStore *QWindowsDirect2DIntegration::createPlatformBackingStore(Q return new QWindowsDirect2DBackingStore(window); } -QAbstractEventDispatcher *QWindowsDirect2DIntegration::createEventDispatcher() const -{ - return new QWindowsDirect2DEventDispatcher; -} - QWindowsDirect2DContext *QWindowsDirect2DIntegration::direct2DContext() const { return &d->m_d2dContext; diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.h b/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.h index 19c7521eb7..5ea36e04bc 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.h +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.h @@ -61,7 +61,6 @@ public: QPlatformNativeInterface *nativeInterface() const override; QPlatformPixmap *createPlatformPixmap(QPlatformPixmap::PixelType type) const override; QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const override; - QAbstractEventDispatcher *createEventDispatcher() const override; QWindowsDirect2DContext *direct2DContext() const; -- cgit v1.2.3 From dce7dbecb0135501eb8641c8f5ec8e98bddc808c Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Thu, 2 May 2019 19:56:29 +0200 Subject: qlalr: replace QLinkedList with std::list This is in preparation of deprecating QLinkedList. Most is straight-forward, except where operator+ was used on linked-list iterators. In one case, replaced this with std::next, in the other, with prefix increments, since the advancement was always equal to the loop control variable. Since advancing a linked-list iterator is a linear operation, this removes a source of quadratic complexity. Another obstacle was the overloaded op< set, which was in the Qt namespace while the iterator is from std and the payload, as before, was global. This breaks ADL, so move these operators to namespace std. This violates the standard, but the functions are tagged with our distinct types, so it shouldn't cause any trouble. Change-Id: Ifec0a927bfdabb002838cdf86fb8d23b32a38ff7 Reviewed-by: Giuseppe D'Angelo --- src/tools/bootstrap/bootstrap.pro | 1 - src/tools/qlalr/cppgenerator.cpp | 17 +++++++++-------- src/tools/qlalr/lalr.cpp | 10 ++++++---- src/tools/qlalr/lalr.h | 34 +++++++++++----------------------- src/tools/qlalr/main.cpp | 2 +- 5 files changed, 27 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/tools/bootstrap/bootstrap.pro b/src/tools/bootstrap/bootstrap.pro index 8598fc2721..757460393e 100644 --- a/src/tools/bootstrap/bootstrap.pro +++ b/src/tools/bootstrap/bootstrap.pro @@ -84,7 +84,6 @@ SOURCES += \ ../../corelib/tools/qcryptographichash.cpp \ ../../corelib/tools/qhash.cpp \ ../../corelib/tools/qlist.cpp \ - ../../corelib/tools/qlinkedlist.cpp \ ../../corelib/tools/qlocale.cpp \ ../../corelib/tools/qlocale_tools.cpp \ ../../corelib/tools/qmap.cpp \ diff --git a/src/tools/qlalr/cppgenerator.cpp b/src/tools/qlalr/cppgenerator.cpp index ee17be041e..95f70dc988 100644 --- a/src/tools/qlalr/cppgenerator.cpp +++ b/src/tools/qlalr/cppgenerator.cpp @@ -36,6 +36,8 @@ #include #include +#include + namespace { void generateSeparator(int i, QTextStream &out) @@ -126,7 +128,7 @@ QString CppGenerator::endIncludeGuard(const QString &fileName) void CppGenerator::operator () () { // action table... - state_count = aut.states.size (); + state_count = static_cast(aut.states.size()); terminal_count = static_cast(grammar.terminals.size()); non_terminal_count = static_cast(grammar.non_terminals.size()); @@ -156,7 +158,7 @@ void CppGenerator::operator () () if (grammar.isNonTerminal (a.key ())) { - Q_ASSERT (symbol >= terminal_count && symbol < grammar.names.size ()); + Q_ASSERT(symbol >= terminal_count && symbol < static_cast(grammar.names.size())); GOTO (q, symbol - terminal_count) = r; } @@ -245,7 +247,7 @@ void CppGenerator::operator () () << Qt::endl; } - QBitArray used_rules (grammar.rules.count ()); + QBitArray used_rules{static_cast(grammar.rules.size())}; int q = 0; for (StatePointer state = aut.states.begin (); state != aut.states.end (); ++state, ++q) @@ -259,12 +261,11 @@ void CppGenerator::operator () () } } - for (int i = 0; i < used_rules.count (); ++i) + auto rule = grammar.rules.begin(); + for (int i = 0; i < used_rules.count (); ++i, ++rule) { if (! used_rules.testBit (i)) { - RulePointer rule = grammar.rules.begin () + i; - if (rule != grammar.goal) qerr() << "*** Warning: Rule ``" << *rule << "'' is useless!" << Qt::endl; } @@ -280,7 +281,7 @@ void CppGenerator::operator () () if (u >= 0) continue; - RulePointer rule = grammar.rules.begin () + (- u - 1); + RulePointer rule = std::next(grammar.rules.begin(), - u - 1); if (state->defaultReduce == rule) u = 0; @@ -619,7 +620,7 @@ void CppGenerator::generateImpl (QTextStream &out) out << "const int " << grammar.table_name << "::rule_index [] = {"; idx = 0; - int offset = 0; + size_t offset = 0; for (RulePointer rule = grammar.rules.begin (); rule != grammar.rules.end (); ++rule, ++idx) { generateSeparator(idx, out); diff --git a/src/tools/qlalr/lalr.cpp b/src/tools/qlalr/lalr.cpp index 8af3b3c0db..b9a9cf264f 100644 --- a/src/tools/qlalr/lalr.cpp +++ b/src/tools/qlalr/lalr.cpp @@ -51,7 +51,9 @@ QTextStream &qout() static QTextStream result(stdout, QIODevice::WriteOnly); return result; } +QT_END_NAMESPACE +namespace std { bool operator < (Name a, Name b) { return *a < *b; @@ -66,7 +68,7 @@ bool operator < (StatePointer a, StatePointer b) { return &*a < &*b; } -QT_END_NAMESPACE +} bool Read::operator < (const Read &other) const { @@ -329,7 +331,7 @@ QPair Automaton::internState (const State &state) struct _Bucket { - QLinkedList items; + std::list items; void insert (ItemPointer item) { items.push_back (item); } @@ -338,8 +340,8 @@ struct _Bucket { State st (aut->_M_grammar); - for (QLinkedList::iterator item = items.begin (); item != items.end (); ++item) - st.insert ((*item)->next ()); + for (auto &item : items) + st.insert(item->next()); return st; } diff --git a/src/tools/qlalr/lalr.h b/src/tools/qlalr/lalr.h index 55b65a640d..473ea89769 100644 --- a/src/tools/qlalr/lalr.h +++ b/src/tools/qlalr/lalr.h @@ -51,34 +51,22 @@ class Automaton; // names -typedef QLinkedList::iterator Name; -QT_BEGIN_NAMESPACE -Q_DECLARE_TYPEINFO(QLinkedList::iterator, Q_PRIMITIVE_TYPE); -QT_END_NAMESPACE -typedef QLinkedList NameList; +typedef std::list::iterator Name; +typedef std::list NameList; typedef std::set NameSet; // items -typedef QLinkedList ItemList; +typedef std::list ItemList; typedef ItemList::iterator ItemPointer; -QT_BEGIN_NAMESPACE -Q_DECLARE_TYPEINFO(ItemList::iterator, Q_PRIMITIVE_TYPE); -QT_END_NAMESPACE // rules -typedef QLinkedList debug_infot; +typedef std::list debug_infot; typedef debug_infot::iterator RulePointer; -QT_BEGIN_NAMESPACE -Q_DECLARE_TYPEINFO(debug_infot::iterator, Q_PRIMITIVE_TYPE); -QT_END_NAMESPACE typedef QMultiMap RuleMap; // states -typedef QLinkedList StateList; +typedef std::list StateList; typedef StateList::iterator StatePointer; -QT_BEGIN_NAMESPACE -Q_DECLARE_TYPEINFO(StateList::iterator, Q_PRIMITIVE_TYPE); -QT_END_NAMESPACE // arrows typedef QMap Bundle; @@ -175,7 +163,7 @@ class Node public: typedef std::set > Repository; typedef typename Repository::iterator iterator; - typedef typename QLinkedList::iterator edge_iterator; + typedef typename std::list::iterator edge_iterator; public: static iterator get (_Tp data); @@ -213,7 +201,7 @@ public: // attributes mutable bool root; mutable int dfn; mutable _Tp data; - mutable QLinkedList outs; + mutable std::list outs; protected: inline Node () {} @@ -235,7 +223,7 @@ typename Node<_Tp>::iterator Node<_Tp>::get (_Tp data) } template -QPair::iterator>::iterator, bool> Node<_Tp>::insertEdge (typename Node<_Tp>::iterator other) const +QPair::iterator>::iterator, bool> Node<_Tp>::insertEdge(typename Node<_Tp>::iterator other) const { edge_iterator it = std::find (outs.begin (), outs.end (), other); @@ -272,7 +260,7 @@ public: QString decl_file_name; QString impl_file_name; QString token_prefix; - QLinkedList names; + std::list names; Name start; NameSet terminals; NameSet non_terminals; @@ -401,11 +389,11 @@ private: int _M_includes_dfn; }; -QT_BEGIN_NAMESPACE +namespace std { bool operator < (Name a, Name b); bool operator < (StatePointer a, StatePointer b); bool operator < (ItemPointer a, ItemPointer b); -QT_END_NAMESPACE +} QTextStream &operator << (QTextStream &out, const Name &n); QTextStream &operator << (QTextStream &out, const Rule &r); diff --git a/src/tools/qlalr/main.cpp b/src/tools/qlalr/main.cpp index a920b13c85..6a57c7aa7a 100644 --- a/src/tools/qlalr/main.cpp +++ b/src/tools/qlalr/main.cpp @@ -106,7 +106,7 @@ int main (int argc, char *argv[]) if (! p.parse (file_name)) exit (EXIT_FAILURE); - if (grammar.rules.isEmpty ()) + if (grammar.rules.empty()) { qerr() << "*** Fatal. No rules!" << Qt::endl; exit (EXIT_FAILURE); -- cgit v1.2.3 From 3d87ea91af766d99dffc17f233b35e941eeefaf1 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Tue, 2 Jul 2019 16:20:06 +0200 Subject: RHI/D3D11: Remove hard dependency on D3DCompiler library Varying versions of the library may be available depending on build tool chain or Windows version. Try to dynamically resolve the D3DCompile function. Fixes: QTBUG-76845 Change-Id: Ib7eb3b8c454e9c25731eb2ba9ba45e54fe3f1283 Reviewed-by: Laszlo Agocs --- src/gui/rhi/qrhid3d11.cpp | 21 ++++++++++++++++++++- src/gui/rhi/rhi.pri | 2 +- 2 files changed, 21 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index eea2a13d8c..a8a490eb5c 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -3218,6 +3219,18 @@ static inline D3D11_BLEND_OP toD3DBlendOp(QRhiGraphicsPipeline::BlendOp op) } } +static pD3DCompile resolveD3DCompile() +{ + for (const wchar_t *libraryName : {L"D3DCompiler_47", L"D3DCompiler_43"}) { + QSystemLibrary library(libraryName); + if (library.load()) { + if (auto symbol = library.resolve("D3DCompile")) + return reinterpret_cast(symbol); + } + } + return nullptr; +} + static QByteArray compileHlslShaderSource(const QShader &shader, QShader::Variant shaderVariant, QString *error) { QShaderCode dxbc = shader.shader({ QShader::DxbcShader, 50, shaderVariant }); @@ -3255,9 +3268,15 @@ static QByteArray compileHlslShaderSource(const QShader &shader, QShader::Varian return QByteArray(); } + static const pD3DCompile d3dCompile = resolveD3DCompile(); + if (d3dCompile == nullptr) { + qWarning("Unable to resolve function D3DCompile()"); + return QByteArray(); + } + ID3DBlob *bytecode = nullptr; ID3DBlob *errors = nullptr; - HRESULT hr = D3DCompile(hlslSource.shader().constData(), hlslSource.shader().size(), + HRESULT hr = d3dCompile(hlslSource.shader().constData(), hlslSource.shader().size(), nullptr, nullptr, nullptr, hlslSource.entryPoint().constData(), target, 0, 0, &bytecode, &errors); if (FAILED(hr) || !bytecode) { diff --git a/src/gui/rhi/rhi.pri b/src/gui/rhi/rhi.pri index d8607f1024..4297a5602b 100644 --- a/src/gui/rhi/rhi.pri +++ b/src/gui/rhi/rhi.pri @@ -40,7 +40,7 @@ win32 { SOURCES += \ rhi/qrhid3d11.cpp - LIBS += -ld3d11 -ldxgi -ldxguid -ld3dcompiler + LIBS += -ld3d11 -ldxgi -ldxguid } # darwin { -- cgit v1.2.3 From 9ea53c4a98281e4be12164929a7ca5dc7c014280 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Mon, 1 Jul 2019 14:35:27 +0200 Subject: QShortcut: Brush up the code, preparing the extraction of a base class to QtGui - Use member initialization - Introduce nullptr - Use auto where applicable - Use range-based for Task-number: QTBUG-76493 Change-Id: Ic4dbee2d76a65be1f8a4c25f4ca7e4f032443579 Reviewed-by: Frederik Gladhorn --- src/widgets/kernel/qshortcut.cpp | 68 ++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/widgets/kernel/qshortcut.cpp b/src/widgets/kernel/qshortcut.cpp index c418db14ff..a4ebcdfc84 100644 --- a/src/widgets/kernel/qshortcut.cpp +++ b/src/widgets/kernel/qshortcut.cpp @@ -96,8 +96,7 @@ bool qWidgetShortcutContextMatcher(QObject *object, Qt::ShortcutContext context) QWindow *qwindow = QGuiApplication::focusWindow(); if (qwindow && qwindow->isActive()) { while (qwindow) { - QWidgetWindow *widgetWindow = qobject_cast(qwindow); - if (widgetWindow) { + if (auto widgetWindow = qobject_cast(qwindow)) { active_window = widgetWindow->widget(); break; } @@ -110,27 +109,25 @@ bool qWidgetShortcutContextMatcher(QObject *object, Qt::ShortcutContext context) return false; #ifndef QT_NO_ACTION - if (QAction *a = qobject_cast(object)) + if (auto a = qobject_cast(object)) return correctActionContext(context, a, active_window); #endif #if QT_CONFIG(graphicsview) - if (QGraphicsWidget *gw = qobject_cast(object)) + if (auto gw = qobject_cast(object)) return correctGraphicsWidgetContext(context, gw, active_window); #endif - QWidget *w = qobject_cast(object); + auto w = qobject_cast(object); if (!w) { - QShortcut *s = qobject_cast(object); - if (s) + if (auto s = qobject_cast(object)) w = s->parentWidget(); } if (!w) { - QWindow *qwindow = qobject_cast(object); + auto qwindow = qobject_cast(object); while (qwindow) { - QWidgetWindow *widget_window = qobject_cast(qwindow); - if (widget_window) { + if (auto widget_window = qobject_cast(qwindow)) { w = widget_window->widget(); break; } @@ -148,7 +145,7 @@ static bool correctWidgetContext(Qt::ShortcutContext context, QWidget *w, QWidge { bool visible = w->isVisible(); #if QT_CONFIG(menubar) - if (QMenuBar *menuBar = qobject_cast(w)) { + if (auto menuBar = qobject_cast(w)) { if (auto *pmb = menuBar->platformMenuBar()) { if (menuBar->parentWidget()) { visible = true; @@ -166,7 +163,7 @@ static bool correctWidgetContext(Qt::ShortcutContext context, QWidget *w, QWidge return false; if (context == Qt::ApplicationShortcut) - return QApplicationPrivate::tryModalHelper(w, 0); // true, unless w is shadowed by a modal dialog + return QApplicationPrivate::tryModalHelper(w, nullptr); // true, unless w is shadowed by a modal dialog if (context == Qt::WidgetShortcut) return w == QApplication::focusWidget(); @@ -181,9 +178,9 @@ static bool correctWidgetContext(Qt::ShortcutContext context, QWidget *w, QWidge // Below is Qt::WindowShortcut context QWidget *tlw = w->window(); #if QT_CONFIG(graphicsview) - if (QWExtra *topData = static_cast(QObjectPrivate::get(tlw))->extra) { + if (auto topData = static_cast(QObjectPrivate::get(tlw))->extra) { if (topData->proxyWidget) { - bool res = correctGraphicsWidgetContext(context, (QGraphicsWidget *)topData->proxyWidget, active_window); + bool res = correctGraphicsWidgetContext(context, topData->proxyWidget, active_window); return res; } } @@ -244,9 +241,9 @@ static bool correctGraphicsWidgetContext(Qt::ShortcutContext context, QGraphicsW // Applicationwide shortcuts are always reachable unless their owner // is shadowed by modality. In QGV there's no modality concept, but we // must still check if all views are shadowed. - QList views = w->scene()->views(); - for (int i = 0; i < views.size(); ++i) { - if (QApplicationPrivate::tryModalHelper(views.at(i), 0)) + const auto &views = w->scene()->views(); + for (auto view : views) { + if (QApplicationPrivate::tryModalHelper(view, nullptr)) return true; } return false; @@ -258,7 +255,7 @@ static bool correctGraphicsWidgetContext(Qt::ShortcutContext context, QGraphicsW if (context == Qt::WidgetWithChildrenShortcut) { const QGraphicsItem *ti = w->scene()->focusItem(); if (ti && ti->isWidget()) { - const QGraphicsWidget *tw = static_cast(ti); + const auto *tw = static_cast(ti); while (tw && tw != w && (tw->windowType() == Qt::Widget || tw->windowType() == Qt::Popup)) tw = tw->parentWidget(); return tw == w; @@ -269,10 +266,9 @@ static bool correctGraphicsWidgetContext(Qt::ShortcutContext context, QGraphicsW // Below is Qt::WindowShortcut context // Find the active view (if any). - QList views = w->scene()->views(); - QGraphicsView *activeView = 0; - for (int i = 0; i < views.size(); ++i) { - QGraphicsView *view = views.at(i); + const auto &views = w->scene()->views(); + QGraphicsView *activeView = nullptr; + for (auto view : views) { if (view->window() == active_window) { activeView = view; break; @@ -291,15 +287,14 @@ static bool correctGraphicsWidgetContext(Qt::ShortcutContext context, QGraphicsW #ifndef QT_NO_ACTION static bool correctActionContext(Qt::ShortcutContext context, QAction *a, QWidget *active_window) { - const QList &widgets = static_cast(QObjectPrivate::get(a))->widgets; + const QWidgetList &widgets = static_cast(QObjectPrivate::get(a))->widgets; #if defined(DEBUG_QSHORTCUTMAP) if (widgets.isEmpty()) qDebug() << a << "not connected to any widgets; won't trigger"; #endif - for (int i = 0; i < widgets.size(); ++i) { - QWidget *w = widgets.at(i); + for (auto w : widgets) { #if QT_CONFIG(menu) - if (QMenu *menu = qobject_cast(w)) { + if (auto menu = qobject_cast(w)) { #ifdef Q_OS_DARWIN // On Mac, menu item shortcuts are processed before reaching any window. // That means that if a menu action shortcut has not been already processed @@ -325,14 +320,13 @@ static bool correctActionContext(Qt::ShortcutContext context, QAction *a, QWidge } #if QT_CONFIG(graphicsview) - const QList &graphicsWidgets = static_cast(QObjectPrivate::get(a))->graphicsWidgets; + const auto &graphicsWidgets = static_cast(QObjectPrivate::get(a))->graphicsWidgets; #if defined(DEBUG_QSHORTCUTMAP) if (graphicsWidgets.isEmpty()) qDebug() << a << "not connected to any widgets; won't trigger"; #endif - for (int i = 0; i < graphicsWidgets.size(); ++i) { - QGraphicsWidget *w = graphicsWidgets.at(i); - if (correctGraphicsWidgetContext(context, w, active_window)) + for (auto graphicsWidget : graphicsWidgets) { + if (correctGraphicsWidgetContext(context, graphicsWidget, active_window)) return true; } #endif @@ -433,12 +427,12 @@ class QShortcutPrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QShortcut) public: - QShortcutPrivate() : sc_context(Qt::WindowShortcut), sc_enabled(true), sc_autorepeat(true), sc_id(0) {} + QShortcutPrivate() = default; QKeySequence sc_sequence; - Qt::ShortcutContext sc_context; - bool sc_enabled; - bool sc_autorepeat; - int sc_id; + Qt::ShortcutContext sc_context = Qt::WindowShortcut; + bool sc_enabled = true; + bool sc_autorepeat = true; + int sc_id = 0; QString sc_whatsthis; void redoGrab(QShortcutMap &map); }; @@ -472,7 +466,7 @@ void QShortcutPrivate::redoGrab(QShortcutMap &map) QShortcut::QShortcut(QWidget *parent) : QObject(*new QShortcutPrivate, parent) { - Q_ASSERT(parent != 0); + Q_ASSERT(parent != nullptr); } /*! @@ -667,7 +661,7 @@ bool QShortcut::event(QEvent *e) Q_D(QShortcut); bool handled = false; if (d->sc_enabled && e->type() == QEvent::Shortcut) { - QShortcutEvent *se = static_cast(e); + auto se = static_cast(e); if (se->shortcutId() == d->sc_id && se->key() == d->sc_sequence){ #if QT_CONFIG(whatsthis) if (QWhatsThis::inWhatsThisMode()) { -- cgit v1.2.3 From 833519f2debf963ec198760475ba26319303d0e0 Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Tue, 2 Jul 2019 13:20:03 +0200 Subject: Fix compiler warnings when building with DEBUG defines set Change-Id: I51fc7aae246916e585b21b4e7da1fc5a4ac392fd Reviewed-by: Timur Pocheptsov --- src/network/socket/qnativesocketengine_win.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp index c999bd2088..986b0b4861 100644 --- a/src/network/socket/qnativesocketengine_win.cpp +++ b/src/network/socket/qnativesocketengine_win.cpp @@ -608,13 +608,13 @@ bool QNativeSocketEnginePrivate::fetchConnectionParameters() socketType = qt_socket_getType(socketDescriptor); #if defined (QNATIVESOCKETENGINE_DEBUG) - QString socketProtocolStr = "UnknownProtocol"; - if (socketProtocol == QAbstractSocket::IPv4Protocol) socketProtocolStr = "IPv4Protocol"; - else if (socketProtocol == QAbstractSocket::IPv6Protocol) socketProtocolStr = "IPv6Protocol"; + QString socketProtocolStr = QStringLiteral("UnknownProtocol"); + if (socketProtocol == QAbstractSocket::IPv4Protocol) socketProtocolStr = QStringLiteral("IPv4Protocol"); + else if (socketProtocol == QAbstractSocket::IPv6Protocol) socketProtocolStr = QStringLiteral("IPv6Protocol"); - QString socketTypeStr = "UnknownSocketType"; - if (socketType == QAbstractSocket::TcpSocket) socketTypeStr = "TcpSocket"; - else if (socketType == QAbstractSocket::UdpSocket) socketTypeStr = "UdpSocket"; + QString socketTypeStr = QStringLiteral("UnknownSocketType"); + if (socketType == QAbstractSocket::TcpSocket) socketTypeStr = QStringLiteral("TcpSocket"); + else if (socketType == QAbstractSocket::UdpSocket) socketTypeStr = QStringLiteral("UdpSocket"); qDebug("QNativeSocketEnginePrivate::fetchConnectionParameters() localAddress == %s, localPort = %i, peerAddress == %s, peerPort = %i, socketProtocol == %s, socketType == %s", localAddress.toString().toLatin1().constData(), localPort, peerAddress.toString().toLatin1().constData(), peerPort, socketProtocolStr.toLatin1().constData(), socketTypeStr.toLatin1().constData()); #endif @@ -1487,8 +1487,8 @@ qint64 QNativeSocketEnginePrivate::nativeWrite(const char *data, qint64 len) } #if defined (QNATIVESOCKETENGINE_DEBUG) - qDebug("QNativeSocketEnginePrivate::nativeWrite(%p \"%s\", %li) == %li", - data, qt_prettyDebug(data, qMin((int)ret, 16), (int)ret).data(), (int)len, (int)ret); + qDebug("QNativeSocketEnginePrivate::nativeWrite(%p \"%s\", %lli) == %lli", + data, qt_prettyDebug(data, qMin(int(ret), 16), int(ret)).data(), len, ret); #endif return ret; @@ -1530,11 +1530,11 @@ qint64 QNativeSocketEnginePrivate::nativeRead(char *data, qint64 maxLength) #if defined (QNATIVESOCKETENGINE_DEBUG) if (ret != -2) { - qDebug("QNativeSocketEnginePrivate::nativeRead(%p \"%s\", %li) == %li", - data, qt_prettyDebug(data, qMin((int)bytesRead, 16), (int)bytesRead).data(), (int)maxLength, (int)ret); + qDebug("QNativeSocketEnginePrivate::nativeRead(%p \"%s\", %lli) == %lli", + data, qt_prettyDebug(data, qMin(int(bytesRead), 16), int(bytesRead)).data(), maxLength, ret); } else { - qDebug("QNativeSocketEnginePrivate::nativeRead(%p, %li) == -2 (WOULD BLOCK)", - data, int(maxLength)); + qDebug("QNativeSocketEnginePrivate::nativeRead(%p, %lli) == -2 (WOULD BLOCK)", + data, maxLength); } #endif -- cgit v1.2.3 From 1e4e006c3f6e8cbd0092fe882bc23a2280352a91 Mon Sep 17 00:00:00 2001 From: Christian Ehrlicher Date: Sat, 11 May 2019 13:37:12 +0200 Subject: QPainter: mark obsolete RenderHints as deprecated RenderHint::HighQualityAntialiasing and NonCosmeticDefaultPen are obsolete since Qt5 but not marked as such. Therefore add Q_DECL_ENUMERATOR_DEPRECATED_X now so those two enumerations can be removed with Qt6. [ChangeLog][QtGui][QPainter] HighQualityAntialiasing and NonCosmeticDefaultPen are marked as deprecated and don't have an effect anymore Change-Id: Ib0c966a078a1d23d492d0255288e2066c50e87b6 Reviewed-by: Allan Sandfeld Jensen Reviewed-by: Eirik Aavitsland --- src/gui/opengl/qopenglpaintengine.cpp | 5 ++++- src/gui/painting/qpaintengine_raster.cpp | 6 +++++- src/gui/painting/qpainter.h | 6 ++++-- src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp | 5 ++++- src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp | 6 +++++- 5 files changed, 22 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/gui/opengl/qopenglpaintengine.cpp b/src/gui/opengl/qopenglpaintengine.cpp index 042b9ebd79..c087326068 100644 --- a/src/gui/opengl/qopenglpaintengine.cpp +++ b/src/gui/opengl/qopenglpaintengine.cpp @@ -1475,7 +1475,10 @@ void QOpenGL2PaintEngineEx::renderHintsChanged() if (!QOpenGLContext::currentContext()->isOpenGLES()) { Q_D(QOpenGL2PaintEngineEx); if ((state()->renderHints & QPainter::Antialiasing) - || (state()->renderHints & QPainter::HighQualityAntialiasing)) +#if QT_DEPRECATED_SINCE(5, 14) + || (state()->renderHints & QPainter::HighQualityAntialiasing) +#endif + ) d->funcs.glEnable(GL_MULTISAMPLE); else d->funcs.glDisable(GL_MULTISAMPLE); diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp index 64183c2be6..461ad51200 100644 --- a/src/gui/painting/qpaintengine_raster.cpp +++ b/src/gui/painting/qpaintengine_raster.cpp @@ -904,7 +904,11 @@ void QRasterPaintEngine::renderHintsChanged() bool was_aa = s->flags.antialiased; bool was_bilinear = s->flags.bilinear; - s->flags.antialiased = bool(s->renderHints & (QPainter::Antialiasing | QPainter::HighQualityAntialiasing)); + s->flags.antialiased = bool(s->renderHints & QPainter::Antialiasing); +#if QT_DEPRECATED_SINCE(5, 14) + if (s->renderHints & QPainter::HighQualityAntialiasing) + s->flags.antialiased = true; +#endif s->flags.bilinear = bool(s->renderHints & QPainter::SmoothPixmapTransform); s->flags.legacy_rounding = !bool(s->renderHints & QPainter::Antialiasing) && bool(s->renderHints & QPainter::Qt4CompatiblePainting); diff --git a/src/gui/painting/qpainter.h b/src/gui/painting/qpainter.h index 843f24e3e1..3394da63c7 100644 --- a/src/gui/painting/qpainter.h +++ b/src/gui/painting/qpainter.h @@ -89,8 +89,10 @@ public: Antialiasing = 0x01, TextAntialiasing = 0x02, SmoothPixmapTransform = 0x04, - HighQualityAntialiasing = 0x08, - NonCosmeticDefaultPen = 0x10, +#if QT_DEPRECATED_SINCE(5, 14) + HighQualityAntialiasing Q_DECL_ENUMERATOR_DEPRECATED_X("Use Antialiasing instead") = 0x08, + NonCosmeticDefaultPen Q_DECL_ENUMERATOR_DEPRECATED_X("Default pen is non-cosmetic now") = 0x10, +#endif Qt4CompatiblePainting = 0x20, LosslessImageRendering = 0x40, }; diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp index cc97bed3f7..e8ff40304e 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp @@ -1393,7 +1393,10 @@ void QGL2PaintEngineEx::renderHintsChanged() #if !defined(QT_OPENGL_ES_2) if (!d->ctx->contextHandle()->isOpenGLES()) { if ((state()->renderHints & QPainter::Antialiasing) - || (state()->renderHints & QPainter::HighQualityAntialiasing)) +#if QT_DEPRECATED_SINCE(5, 14) + || (state()->renderHints & QPainter::HighQualityAntialiasing) +#endif + ) d->glEnable(GL_MULTISAMPLE); else d->glDisable(GL_MULTISAMPLE); diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp index cf3e88d54e..d7aba66b2f 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp @@ -943,7 +943,11 @@ public: // Default path (no optimization) if (!(path.shape() == QVectorPath::LinesHint || path.shape() == QVectorPath::PolygonHint) - || !pen.dashBrush || q->state()->renderHints.testFlag(QPainter::HighQualityAntialiasing)) { + || !pen.dashBrush +#if QT_DEPRECATED_SINCE(5, 14) + || q->state()->renderHints.testFlag(QPainter::HighQualityAntialiasing) +#endif + || q->state()->renderHints.testFlag(QPainter::Antialiasing)) { ComPtr geometry = vectorPathToID2D1PathGeometry(path); if (!geometry) { qWarning("%s: Could not convert path to d2d geometry", __FUNCTION__); -- cgit v1.2.3 From ee6b19042f9a1a1e4d240e8216224e17dba93079 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Arve=20S=C3=A6ther?= Date: Fri, 21 Jun 2019 10:31:30 +0200 Subject: Fix 'make check' for android-mingw32 g_options.buildPath did not have native separators. This caused INSTALL_ROOT to not have native path separators, which again made 'mingw32-make check' fail to deploy the .apk. (The actual failure was that mkdir failed because it did not accept paths with forward-slashes, so the install_target rule failed. For the record, the command of that rule had the following call to mkdir: mkdir $(INSTALL_ROOT:@msyshack@%=%)\libs\x86 Change-Id: Id792c36986b52a527546d48aa9f7d9587e7a18d9 Reviewed-by: BogDan Vatra Reviewed-by: Frederik Gladhorn --- src/tools/androidtestrunner/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/tools/androidtestrunner/main.cpp b/src/tools/androidtestrunner/main.cpp index bb69b7b914..e6d6f72354 100644 --- a/src/tools/androidtestrunner/main.cpp +++ b/src/tools/androidtestrunner/main.cpp @@ -454,7 +454,7 @@ int main(int argc, char *argv[]) if (!g_options.makeCommand.isEmpty()) { // we need to run make INSTALL_ROOT=path install to install the application file(s) first if (!execCommand(QStringLiteral("%1 INSTALL_ROOT=%2 install") - .arg(g_options.makeCommand, g_options.buildPath), nullptr, g_options.verbose)) { + .arg(g_options.makeCommand, QDir::toNativeSeparators(g_options.buildPath)), nullptr, g_options.verbose)) { return 1; } } -- cgit v1.2.3 From fb353592261e04896ad417ce5ace8adda1fe9fdf Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Mon, 1 Jul 2019 10:30:14 +0200 Subject: [docs] Fix issues in QRect/QMargin API docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix grammar in op-(QRect, QMargin) - Correct shunk for grown in op-(QRectF, QMarginsF) Change-Id: Ia0dbd933cc9f6ed5e0dad05a27794c1135c794ed Reviewed-by: Mårten Nordheim --- src/corelib/tools/qrect.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/corelib/tools/qrect.cpp b/src/corelib/tools/qrect.cpp index 6e51deebea..c8301e2590 100644 --- a/src/corelib/tools/qrect.cpp +++ b/src/corelib/tools/qrect.cpp @@ -1194,7 +1194,7 @@ bool QRect::intersects(const QRect &r) const Q_DECL_NOTHROW \fn QRect operator-(const QRect &lhs, const QMargins &rhs) \relates QRect - Returns the \a lhs rectangle shrunken by the \a rhs margins. + Returns the \a lhs rectangle shrunk by the \a rhs margins. \since 5.3 */ @@ -2417,7 +2417,7 @@ QRect QRectF::toAlignedRect() const Q_DECL_NOTHROW \relates QRectF \since 5.3 - Returns the \a lhs rectangle grown by the \a rhs margins. + Returns the \a lhs rectangle shrunk by the \a rhs margins. */ /*! -- cgit v1.2.3 From 8d414e67941fdc45ab682b763a119ecc71291483 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Wed, 26 Jun 2019 15:32:50 +0200 Subject: Mark QObject::deleteLater() as \threadsafe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because it is. It's just QCoreApplication::postEvent(), which is thread-safe. It also _has_ to be, because we recommend to use deleteLater() to delete QObjects that live in another thread: Quoting the ~QObject() docs: > Warning: Deleting a QObject while pending events are waiting to be delivered > can cause a crash. You must not delete the QObject directly if it exists in > a different thread than the one currently executing. Use deleteLater() > instead, which will cause the event loop to delete the object after all > pending events have been delivered to it. If deleteLater() is not thread-safe, it cannot be used for one of its intended purposes. Change-Id: I333d506b42bdfcdff00fe6cefa234c21865625a6 Reviewed-by: Mårten Nordheim --- src/corelib/kernel/qobject.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 68c5460b6a..d685eaf27f 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -2163,6 +2163,8 @@ void QObject::removeEventFilter(QObject *obj) */ /*! + \threadsafe + Schedules this object for deletion. The object will be deleted when control returns to the event -- cgit v1.2.3 From 6ff0614b61b8645ec7ca4ea40501a0b6719a92fd Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Thu, 20 Jun 2019 13:07:04 +0200 Subject: Handle multiple font-families in face attribute This seems to be a common use case, and to be expected from pastes of MSWord documents. Change-Id: I5849d7f51408e76f15a0b03c2118649f118af1d6 Fixes: QTBUG-66794 Reviewed-by: Friedemann Kleint Reviewed-by: Lars Knoll --- src/gui/text/qtexthtmlparser.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/gui/text/qtexthtmlparser.cpp b/src/gui/text/qtexthtmlparser.cpp index 895232e4c7..929db40e5d 100644 --- a/src/gui/text/qtexthtmlparser.cpp +++ b/src/gui/text/qtexthtmlparser.cpp @@ -1505,7 +1505,16 @@ void QTextHtmlParser::applyAttributes(const QStringList &attributes) n -= 3; node->charFormat.setProperty(QTextFormat::FontSizeAdjustment, n); } else if (key == QLatin1String("face")) { - node->charFormat.setFontFamily(value); + if (value.contains(QLatin1Char(','))) { + const QStringList values = value.split(QLatin1Char(',')); + QStringList families; + for (const QString &family : values) + families << family.trimmed(); + node->charFormat.setFontFamilies(families); + node->charFormat.setFontFamily(families.at(0)); + } else { + node->charFormat.setFontFamily(value); + } } else if (key == QLatin1String("color")) { QColor c; c.setNamedColor(value); if (!c.isValid()) -- cgit v1.2.3 From 0f92f2f35d55f2de545e8472c6b240c8fd0bb53f Mon Sep 17 00:00:00 2001 From: Felix Barz Date: Sun, 30 Jun 2019 20:43:03 +0200 Subject: Add custom verb support for WASM HTTP requests The QNetworkReply implementation for Qt for WebAssembly now supports usage of the QNetworkAccessManager::sendCustomRequest, making it possible to send requests with custom verbs. [ChangeLog][QtNetwork][QNetworkAccessManager] Fixed QNetworkAccessManager::sendCustomRequest for Qt For WebAssembly. Fixes: QTBUG-76775 Change-Id: I9394ffef110fce4ed2c877893631bedc7631f71e Reviewed-by: Lorn Potter --- src/network/access/qnetworkreplywasmimpl.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/network/access/qnetworkreplywasmimpl.cpp b/src/network/access/qnetworkreplywasmimpl.cpp index 9f6422a107..ee91dc20b3 100644 --- a/src/network/access/qnetworkreplywasmimpl.cpp +++ b/src/network/access/qnetworkreplywasmimpl.cpp @@ -236,6 +236,7 @@ QNetworkReplyWasmImpl::~QNetworkReplyWasmImpl() QByteArray QNetworkReplyWasmImpl::methodName() const { + const Q_D( QNetworkReplyWasmImpl); switch (operation()) { case QNetworkAccessManager::HeadOperation: return "HEAD"; @@ -247,6 +248,8 @@ QByteArray QNetworkReplyWasmImpl::methodName() const return "POST"; case QNetworkAccessManager::DeleteOperation: return "DELETE"; + case QNetworkAccessManager::CustomOperation: + return d->request.attribute(QNetworkRequest::CustomVerbAttribute).toByteArray(); default: break; } -- cgit v1.2.3 From e40a139abf6bc191ad10c22734848a5d1176cc76 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Tue, 2 Jul 2019 15:28:20 +0200 Subject: QtWidgets: Fix wrong screen returned by newly added QWidgetPrivate::associatedScreen() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QWidgetPrivate::windowHandle() should not return nullptr from the WindowHandleMode::Direct branch in case WindowHandleMode::Closest was passed. Ameds eed9a8fbd300dafb2802b6c09c018fbda4e13ef1. Task-number: QTBUG-62094 Task-number: QTBUG-73231 Change-Id: Ia55fff15f0a499cef9525e53111ddd55b2e012d0 Reviewed-by: Tor Arne Vestbø --- src/widgets/kernel/qwidget.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index f686341f6e..9f4098a88a 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -1255,8 +1255,10 @@ void QWidgetPrivate::createRecursively() QWindow *QWidgetPrivate::windowHandle(WindowHandleMode mode) const { if (mode == WindowHandleMode::Direct || mode == WindowHandleMode::Closest) { - if (QTLWExtra *x = maybeTopData()) - return x->window; + if (QTLWExtra *x = maybeTopData()) { + if (x->window != nullptr || mode == WindowHandleMode::Direct) + return x->window; + } } if (mode == WindowHandleMode::Closest) { if (auto nativeParent = q_func()->nativeParentWidget()) { -- cgit v1.2.3 From e1c9276c778b66d5f7fd71f01c26338f3d6f2c5e Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Tue, 2 Jul 2019 10:16:43 +0200 Subject: Optimize QSet set operations Replace the identity check with a check for the underlying QHash objects being shared and replace backwards iteration, which is really forwards iteration with wrapping at bucket boundaries, with forward iteration. QSet cannot contain duplicates, so the order in which the RHS elements are presented to the algorithms does not matter. Change-Id: Iad8528e3a9501b14cb85601b221a848aad91480c Reviewed-by: Lars Knoll --- src/corelib/tools/qset.h | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/corelib/tools/qset.h b/src/corelib/tools/qset.h index 83e574bf1c..98caabfb57 100644 --- a/src/corelib/tools/qset.h +++ b/src/corelib/tools/qset.h @@ -284,11 +284,9 @@ Q_INLINE_TEMPLATE void QSet::reserve(int asize) { q_hash.reserve(asize); } template Q_INLINE_TEMPLATE QSet &QSet::unite(const QSet &other) { - QSet copy(other); - typename QSet::const_iterator i = copy.constEnd(); - while (i != copy.constBegin()) { - --i; - insert(*i); + if (!q_hash.isSharedWith(other.q_hash)) { + for (const T &e : other) + insert(e); } return *this; } @@ -306,11 +304,9 @@ Q_INLINE_TEMPLATE QSet &QSet::intersect(const QSet &other) copy2 = *this; *this = copy1; } - typename QSet::const_iterator i = copy1.constEnd(); - while (i != copy1.constBegin()) { - --i; - if (!copy2.contains(*i)) - remove(*i); + for (const auto &e : qAsConst(copy1)) { + if (!copy2.contains(e)) + remove(e); } return *this; } @@ -346,14 +342,11 @@ Q_INLINE_TEMPLATE bool QSet::intersects(const QSet &other) const template Q_INLINE_TEMPLATE QSet &QSet::subtract(const QSet &other) { - if (&other == this) { + if (q_hash.isSharedWith(other.q_hash)) { clear(); } else { - auto i = other.constEnd(); - while (i != other.constBegin()) { - --i; - remove(*i); - } + for (const auto &e : other) + remove(e); } return *this; } -- cgit v1.2.3 From 33fdd5c83331290a52fdefcff56e49e6c0409711 Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Wed, 3 Jul 2019 10:40:30 +0200 Subject: Fix a warning treated as error - 'importDevice not used' Change-Id: Id48ff52d6532cf3585648addd498cdddccbcb994 Reviewed-by: Laszlo Agocs --- src/gui/rhi/qrhi.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp index 646f0aef13..d7c1607e57 100644 --- a/src/gui/rhi/qrhi.cpp +++ b/src/gui/rhi/qrhi.cpp @@ -3829,6 +3829,7 @@ QRhi *QRhi::create(Implementation impl, QRhiInitParams *params, Flags flags, QRh static_cast(importDevice)); break; #else + Q_UNUSED(importDevice); qWarning("This build of Qt has no Vulkan support"); break; #endif -- cgit v1.2.3 From f99bbfb9ea7a7c96b2fbb7aee2d1d1a9056ca4e7 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Sun, 19 May 2019 23:25:12 +0200 Subject: QFseventsFileSystemWatcherEngine: port some Java-style iterators to ranged-for MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Amends b03385f9cff7acc2b37933f493e3eff2d8bbef59. Java-style iterators are scheduled to be deprecated. The general pattern used in the patch is that instead of copying an input list, then iterating over the copy with some calls to it.remove() (which leads to quadratic-complexity loops), we simply copy conditionally (a la remove_copy_if instead of remove_if). To make clearer what's going on, rename the outgoing list to 'unhandled'. To avoid having to touch too much of the loops' structure, which sometimes is quite convoluted, use qScopeGuard to do the append to 'unhandled', unless the original code removed the element. Change-Id: I808a939b9c816b329ee87620e0a3461fee6e3e40 Reviewed-by: Thiago Macieira Reviewed-by: Mårten Nordheim --- src/corelib/io/qfilesystemwatcher_fsevents.mm | 30 +++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/corelib/io/qfilesystemwatcher_fsevents.mm b/src/corelib/io/qfilesystemwatcher_fsevents.mm index fb3b6f86fd..6194bf7c71 100644 --- a/src/corelib/io/qfilesystemwatcher_fsevents.mm +++ b/src/corelib/io/qfilesystemwatcher_fsevents.mm @@ -50,6 +50,7 @@ #include #include #include +#include #undef FSEVENT_DEBUG #ifdef FSEVENT_DEBUG @@ -338,10 +339,10 @@ QStringList QFseventsFileSystemWatcherEngine::addPaths(const QStringList &paths, bool needsRestart = false; WatchingState oldState = watchingState; - QStringList p = paths; - QMutableListIterator it(p); - while (it.hasNext()) { - QString origPath = it.next().normalized(QString::NormalizationForm_C); + QStringList unhandled; + for (const QString &path : paths) { + auto sg = qScopeGuard([&]{ unhandled.push_back(path); }); + QString origPath = path.normalized(QString::NormalizationForm_C); QString realPath = origPath; if (realPath.endsWith(QDir::separator())) realPath = realPath.mid(0, realPath.size() - 1); @@ -362,17 +363,17 @@ QStringList QFseventsFileSystemWatcherEngine::addPaths(const QStringList &paths, continue; directories->append(origPath); watchedPath = realPath; - it.remove(); } else { if (files->contains(origPath)) continue; files->append(origPath); - it.remove(); watchedPath = fi.path(); parentPath = watchedPath; } + sg.dismiss(); + for (PathRefCounts::const_iterator i = watchingState.watchedPaths.begin(), ei = watchingState.watchedPaths.end(); i != ei; ++i) { if (watchedPath.startsWith(i.key() % QDir::separator())) { @@ -409,14 +410,14 @@ QStringList QFseventsFileSystemWatcherEngine::addPaths(const QStringList &paths, // ok, something went wrong, let's try to restore the previous state watchingState = std::move(oldState); // and because we don't know which path caused the issue (if any), fail on all of them - p = paths; + unhandled = paths; if (wasRunning) startStream(); } } - return p; + return unhandled; } QStringList QFseventsFileSystemWatcherEngine::removePaths(const QStringList &paths, @@ -430,10 +431,9 @@ QStringList QFseventsFileSystemWatcherEngine::removePaths(const QStringList &pat bool needsRestart = false; WatchingState oldState = watchingState; - QStringList p = paths; - QMutableListIterator it(p); - while (it.hasNext()) { - QString origPath = it.next(); + QStringList unhandled; + for (const QString &origPath : paths) { + auto sg = qScopeGuard([&]{ unhandled.push_back(origPath); }); QString realPath = origPath; if (realPath.endsWith(QDir::separator())) realPath = realPath.mid(0, realPath.size() - 1); @@ -447,7 +447,7 @@ QStringList QFseventsFileSystemWatcherEngine::removePaths(const QStringList &pat needsRestart |= derefPath(dirIt->dirInfo.watchedPath); watchingState.watchedDirectories.erase(dirIt); directories->removeAll(origPath); - it.remove(); + sg.dismiss(); DEBUG("Removed directory '%s'", qPrintable(realPath)); } } else { @@ -463,7 +463,7 @@ QStringList QFseventsFileSystemWatcherEngine::removePaths(const QStringList &pat if (filesInDir.isEmpty()) watchingState.watchedFiles.erase(pIt); files->removeAll(origPath); - it.remove(); + sg.dismiss(); DEBUG("Removed file '%s'", qPrintable(realPath)); } } @@ -479,7 +479,7 @@ QStringList QFseventsFileSystemWatcherEngine::removePaths(const QStringList &pat } } - return p; + return unhandled; } // Returns false if FSEventStream* calls failed for some mysterious reason, true if things got a -- cgit v1.2.3 From f70905448f612f52d4c3034faee61fc103b9eb7c Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Thu, 23 May 2019 13:55:07 +0200 Subject: Add QT_NO_JAVA_STYLE_ITERATORS and mark QtBase free of it ... except for tests, which manually undefine the macro. Like QT_NO_FOREACH, this is a technical way to keep JSI-free modules JSI-free going forward. Change-Id: Icf1342da00a700f42f9e32a253d1cdb94c38dd7e Reviewed-by: Lars Knoll --- src/corelib/tools/qbytearraylist.h | 3 +++ src/corelib/tools/qiterator.h | 9 +++++++++ src/corelib/tools/qset.h | 2 ++ src/corelib/tools/qstringlist.h | 2 ++ 4 files changed, 16 insertions(+) (limited to 'src') diff --git a/src/corelib/tools/qbytearraylist.h b/src/corelib/tools/qbytearraylist.h index 1261e1757c..0250b649b8 100644 --- a/src/corelib/tools/qbytearraylist.h +++ b/src/corelib/tools/qbytearraylist.h @@ -48,8 +48,11 @@ QT_BEGIN_NAMESPACE +#if !defined(QT_NO_JAVA_STYLE_ITERATORS) typedef QListIterator QByteArrayListIterator; typedef QMutableListIterator QMutableByteArrayListIterator; +#endif + #ifndef Q_CLANG_QDOC typedef QList QByteArrayList; diff --git a/src/corelib/tools/qiterator.h b/src/corelib/tools/qiterator.h index 449d1049a0..84a0e116ca 100644 --- a/src/corelib/tools/qiterator.h +++ b/src/corelib/tools/qiterator.h @@ -44,6 +44,8 @@ QT_BEGIN_NAMESPACE +#if !defined(QT_NO_JAVA_STYLE_ITERATORS) + #define Q_DECLARE_SEQUENTIAL_ITERATOR(C) \ \ template \ @@ -179,6 +181,13 @@ public: \ n = c->end(); return false; } \ }; +#else // QT_NO_JAVA_STYLE_ITERATORS +#define Q_DECLARE_SEQUENTIAL_ITERATOR(C) +#define Q_DECLARE_MUTABLE_SEQUENTIAL_ITERATOR(C) +#define Q_DECLARE_ASSOCIATIVE_ITERATOR(C) +#define Q_DECLARE_MUTABLE_ASSOCIATIVE_ITERATOR(C) +#endif // QT_NO_JAVA_STYLE_ITERATORS + template class QKeyValueIterator { diff --git a/src/corelib/tools/qset.h b/src/corelib/tools/qset.h index 98caabfb57..19d6982133 100644 --- a/src/corelib/tools/qset.h +++ b/src/corelib/tools/qset.h @@ -402,6 +402,7 @@ QList QList::fromSet(const QSet &set) Q_DECLARE_SEQUENTIAL_ITERATOR(Set) +#if !defined(QT_NO_JAVA_STYLE_ITERATORS) template class QMutableSetIterator { @@ -433,6 +434,7 @@ public: { while (c->constBegin() != i) if (*(n = --i) == t) return true; n = c->end(); return false; } }; +#endif // QT_NO_JAVA_STYLE_ITERATORS QT_END_NAMESPACE diff --git a/src/corelib/tools/qstringlist.h b/src/corelib/tools/qstringlist.h index 45f51bfcc6..a464d443dc 100644 --- a/src/corelib/tools/qstringlist.h +++ b/src/corelib/tools/qstringlist.h @@ -54,8 +54,10 @@ QT_BEGIN_NAMESPACE class QRegExp; class QRegularExpression; +#if !defined(QT_NO_JAVA_STYLE_ITERATORS) typedef QListIterator QStringListIterator; typedef QMutableListIterator QMutableStringListIterator; +#endif class QStringList; -- cgit v1.2.3 From f883f8409f6867a9f16d1916bebc29f1debfaff0 Mon Sep 17 00:00:00 2001 From: Mikhail Svetkin Date: Tue, 11 Jun 2019 15:20:03 +0200 Subject: qtlite: Fix build with -no-feature-commandlineparser Change-Id: Ie7b7b119073691c0d3b6358f876298fc86cfe9e9 Reviewed-by: Tasuku Suzuki Reviewed-by: Timo Aarnipuro Reviewed-by: Kari Oikarinen --- src/corelib/kernel/qcoreapplication.cpp | 2 ++ src/corelib/kernel/qcoreapplication_p.h | 4 ++++ src/gui/kernel/qguiapplication.cpp | 2 ++ src/gui/kernel/qguiapplication_p.h | 2 ++ 4 files changed, 10 insertions(+) (limited to 'src') diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index e5b88c3c26..9d8da70e47 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -227,12 +227,14 @@ bool QCoreApplicationPrivate::checkInstance(const char *function) return b; } +#if QT_CONFIG(commandlineparser) void QCoreApplicationPrivate::addQtOptions(QList *options) { options->append(QCommandLineOption(QStringLiteral("qmljsdebugger"), QStringLiteral("Activates the QML/JS debugger with a specified port. The value must be of format port:1234[,block]. \"block\" makes the application wait for a connection."), QStringLiteral("value"))); } +#endif void QCoreApplicationPrivate::processCommandLineArguments() { diff --git a/src/corelib/kernel/qcoreapplication_p.h b/src/corelib/kernel/qcoreapplication_p.h index 0b9029b5fe..3bad42d076 100644 --- a/src/corelib/kernel/qcoreapplication_p.h +++ b/src/corelib/kernel/qcoreapplication_p.h @@ -52,7 +52,9 @@ // #include "QtCore/qcoreapplication.h" +#if QT_CONFIG(commandlineparser) #include "QtCore/qcommandlineoption.h" +#endif #include "QtCore/qtranslator.h" #if QT_CONFIG(settings) #include "QtCore/qsettings.h" @@ -105,7 +107,9 @@ public: static bool checkInstance(const char *method); +#if QT_CONFIG(commandlineparser) virtual void addQtOptions(QList *options); +#endif #ifndef QT_NO_QOBJECT bool sendThroughApplicationEventFilters(QObject *, QEvent *); diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index b8bfad4f16..ceb5055a9d 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -1306,6 +1306,7 @@ static void init_plugins(const QList &pluginList) } } +#if QT_CONFIG(commandlineparser) void QGuiApplicationPrivate::addQtOptions(QList *options) { QCoreApplicationPrivate::addQtOptions(options); @@ -1357,6 +1358,7 @@ void QGuiApplicationPrivate::addQtOptions(QList *options) QGuiApplication::tr("Alias for --windowtitle."), QStringLiteral("title"))); } } +#endif // QT_CONFIG(commandlineparser) void QGuiApplicationPrivate::createPlatformIntegration() { diff --git a/src/gui/kernel/qguiapplication_p.h b/src/gui/kernel/qguiapplication_p.h index 2ecde0354a..afca7579ea 100644 --- a/src/gui/kernel/qguiapplication_p.h +++ b/src/gui/kernel/qguiapplication_p.h @@ -92,7 +92,9 @@ public: virtual void notifyLayoutDirectionChange(); virtual void notifyActiveWindowChange(QWindow *previous); +#if QT_CONFIG(commandlineparser) void addQtOptions(QList *options) override; +#endif virtual bool shouldQuit() override; bool shouldQuitInternal(const QWindowList &processedWindows); -- cgit v1.2.3 From 1e4a973b7e048f15c8a331ab5085091db23c8a4e Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 10 Dec 2018 10:40:54 +0100 Subject: Use QRegularExpression for filtering in QDirIterator Reduce our usage of QRegExp in preparation towards deprecating it. This also brings it in line with QDir that already uses QRegularExpression for filtering. Keep the old QRegExp based code around in bootstrapped mode, since qmake uses this functionality. Change-Id: I98b9d2875c30e17e406b6711dfe3265ba37624ac Reviewed-by: Samuel Gaist --- src/corelib/io/qdiriterator.cpp | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/corelib/io/qdiriterator.cpp b/src/corelib/io/qdiriterator.cpp index 21214ee273..303caf29a4 100644 --- a/src/corelib/io/qdiriterator.cpp +++ b/src/corelib/io/qdiriterator.cpp @@ -97,6 +97,9 @@ #include #include #include +#if QT_CONFIG(regularexpression) +#include +#endif #include #include @@ -136,8 +139,11 @@ public: const QDir::Filters filters; const QDirIterator::IteratorFlags iteratorFlags; -#ifndef QT_NO_REGEXP +#if defined(QT_BOOTSTRAPPED) + // ### Qt6: Get rid of this once we don't bootstrap qmake anymore QVector nameRegExps; +#elif QT_CONFIG(regularexpression) + QVector nameRegExps; #endif QDirIteratorPrivateIteratorStack fileEngineIterators; @@ -162,13 +168,21 @@ QDirIteratorPrivate::QDirIteratorPrivate(const QFileSystemEntry &entry, const QS , filters(QDir::NoFilter == filters ? QDir::AllEntries : filters) , iteratorFlags(flags) { -#ifndef QT_NO_REGEXP +#if defined(QT_BOOTSTRAPPED) nameRegExps.reserve(nameFilters.size()); - for (int i = 0; i < nameFilters.size(); ++i) + for (const auto &filter : nameFilters) { nameRegExps.append( - QRegExp(nameFilters.at(i), + QRegExp(filter, (filters & QDir::CaseSensitive) ? Qt::CaseSensitive : Qt::CaseInsensitive, QRegExp::Wildcard)); + } +#elif QT_CONFIG(regularexpression) + nameRegExps.reserve(nameFilters.size()); + for (const auto &filter : nameFilters) { + QString re = QRegularExpression::anchoredPattern(QRegularExpression::wildcardToRegularExpression(filter)); + nameRegExps.append( + QRegularExpression(re, (filters & QDir::CaseSensitive) ? QRegularExpression::NoPatternOption : QRegularExpression::CaseInsensitiveOption)); + } #endif QFileSystemMetaData metaData; if (resolveEngine) @@ -335,19 +349,23 @@ bool QDirIteratorPrivate::matchesFilters(const QString &fileName, const QFileInf return false; // name filter -#ifndef QT_NO_REGEXP +#if QT_CONFIG(regularexpression) || defined(QT_BOOTSTRAPPED) // Pass all entries through name filters, except dirs if the AllDirs if (!nameFilters.isEmpty() && !((filters & QDir::AllDirs) && fi.isDir())) { bool matched = false; - for (QVector::const_iterator iter = nameRegExps.constBegin(), - end = nameRegExps.constEnd(); - iter != end; ++iter) { - - QRegExp copy = *iter; + for (const auto &re : nameRegExps) { +#if defined(QT_BOOTSTRAPPED) + QRegExp copy = re; if (copy.exactMatch(fileName)) { matched = true; break; } +#else + if (re.match(fileName).hasMatch()) { + matched = true; + break; + } +#endif } if (!matched) return false; -- cgit v1.2.3 From c50362597a6f3a3541479b2a84937f406999b954 Mon Sep 17 00:00:00 2001 From: Tasuku Suzuki Date: Thu, 4 Jul 2019 01:37:24 +0900 Subject: Fix build without features.cursor Change-Id: Id7617e03f86a4dd7d1ada37c91cf792bca7f1d6d Reviewed-by: Volker Hilsheimer --- src/widgets/widgets/qtextedit.cpp | 4 ++++ src/widgets/widgets/qtextedit.h | 2 ++ 2 files changed, 6 insertions(+) (limited to 'src') diff --git a/src/widgets/widgets/qtextedit.cpp b/src/widgets/widgets/qtextedit.cpp index 8c1d7e7a03..8599573e5a 100644 --- a/src/widgets/widgets/qtextedit.cpp +++ b/src/widgets/widgets/qtextedit.cpp @@ -167,7 +167,9 @@ void QTextEditPrivate::init(const QString &html) QObject::connect(control, SIGNAL(copyAvailable(bool)), q, SIGNAL(copyAvailable(bool))); QObject::connect(control, SIGNAL(selectionChanged()), q, SIGNAL(selectionChanged())); QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SLOT(_q_cursorPositionChanged())); +#if QT_CONFIG(cursor) QObject::connect(control, SIGNAL(blockMarkerHovered(QTextBlock)), q, SLOT(_q_hoveredBlockWithMarkerChanged(QTextBlock))); +#endif QObject::connect(control, SIGNAL(textChanged()), q, SLOT(updateMicroFocus())); @@ -230,6 +232,7 @@ void QTextEditPrivate::_q_cursorPositionChanged() #endif } +#if QT_CONFIG(cursor) void QTextEditPrivate::_q_hoveredBlockWithMarkerChanged(const QTextBlock &block) { Q_Q(QTextEdit); @@ -244,6 +247,7 @@ void QTextEditPrivate::_q_hoveredBlockWithMarkerChanged(const QTextBlock &block) } viewport->setCursor(cursor); } +#endif void QTextEditPrivate::pageUpDown(QTextCursor::MoveOperation op, QTextCursor::MoveMode moveMode) { diff --git a/src/widgets/widgets/qtextedit.h b/src/widgets/widgets/qtextedit.h index 09ef44b7b2..5c8a3c7793 100644 --- a/src/widgets/widgets/qtextedit.h +++ b/src/widgets/widgets/qtextedit.h @@ -331,7 +331,9 @@ private: Q_PRIVATE_SLOT(d_func(), void _q_adjustScrollbars()) Q_PRIVATE_SLOT(d_func(), void _q_ensureVisible(const QRectF &)) Q_PRIVATE_SLOT(d_func(), void _q_cursorPositionChanged()) +#if QT_CONFIG(cursor) Q_PRIVATE_SLOT(d_func(), void _q_hoveredBlockWithMarkerChanged(const QTextBlock &)) +#endif friend class QTextEditControl; friend class QTextDocument; friend class QWidgetTextControl; -- cgit v1.2.3 From 1bddb4ad7d33ba92ce4387bcaba55c2489dc6615 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Mon, 30 Oct 2017 15:31:25 +0100 Subject: QApplication::topLevelWidgets(): Avoid allocation of list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the QSet QWidgetPrivate::allWidgets directly instead of calling QApplication::allWidgets(), which allocates a QList. Change-Id: I16d289030cecefae7811d4b2c94f865f46f700d5 Reviewed-by: Marc Mutz Reviewed-by: Mårten Nordheim --- src/widgets/kernel/qapplication.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index 58f63c0fed..c922aecceb 100644 --- a/src/widgets/kernel/qapplication.cpp +++ b/src/widgets/kernel/qapplication.cpp @@ -111,6 +111,9 @@ #include +#include +#include + //#define ALIEN_DEBUG static void initResources() @@ -1658,12 +1661,12 @@ void QApplicationPrivate::notifyWindowIconChanged() QWidgetList QApplication::topLevelWidgets() { QWidgetList list; - QWidgetList all = allWidgets(); - - for (QWidgetList::ConstIterator it = all.constBegin(), cend = all.constEnd(); it != cend; ++it) { - QWidget *w = *it; - if (w->isWindow() && w->windowType() != Qt::Desktop) - list.append(w); + if (QWidgetPrivate::allWidgets != nullptr) { + const auto isTopLevelWidget = [] (const QWidget *w) { + return w->isWindow() && w->windowType() != Qt::Desktop; + }; + std::copy_if(QWidgetPrivate::allWidgets->cbegin(), QWidgetPrivate::allWidgets->cend(), + std::back_inserter(list), isTopLevelWidget); } return list; } -- cgit v1.2.3 From 656117100b52bb404828d02106fd0dee760b6019 Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Sun, 30 Jun 2019 12:02:24 +0200 Subject: QSet docs: don't use std::find for lookups That code would normally call for QSet::find instead (Uniform Container Find cannot come soon enough). Since the code is showcasing a STL algorithm usage, port it to std::find_if to showcase a real use case. As a drive-by: fix the usage of endl with std::cout. (Ok, it's just a variable called "cout", and I'd argue that in example code "cout" is not the name of a QTextStream). Change-Id: I8686178b33c31552eb4d909a4089453d60994b79 Reviewed-by: Marc Mutz --- src/corelib/doc/snippets/code/doc_src_qset.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/corelib/doc/snippets/code/doc_src_qset.cpp b/src/corelib/doc/snippets/code/doc_src_qset.cpp index 4248c49642..96ef07738b 100644 --- a/src/corelib/doc/snippets/code/doc_src_qset.cpp +++ b/src/corelib/doc/snippets/code/doc_src_qset.cpp @@ -131,9 +131,10 @@ while (i != set.end()) { //! [10] QSet set; ... -QSet::iterator it = std::find(set.begin(), set.end(), "Jeanette"); +const auto predicate = [](const QString &s) { return s.compare("Jeanette", Qt::CaseInsensitive) == 0; }; +QSet::iterator it = std::find_if(set.begin(), set.end(), predicate); if (it != set.end()) - cout << "Found Jeanette" << Qt::endl; + cout << "Found Jeanette" << endl; //! [10] @@ -150,9 +151,10 @@ for (i = set.begin(); i != set.end(); ++i) //! [12] QSet set; ... -QSet::iterator it = std::find(set.begin(), set.end(), "Jeanette"); +const auto predicate = [](const QString &s) { return s.compare("Jeanette", Qt::CaseInsensitive) == 0; }; +QSet::const_iterator it = std::find_if(set.cbegin(), set.cend(), predicate); if (it != set.constEnd()) - cout << "Found Jeanette" << Qt::endl; + cout << "Found Jeanette" << endl; //! [12] -- cgit v1.2.3 From ff561baabdf2f807ba4867e11f0dbed17ad4f8bf Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Thu, 4 Jul 2019 11:26:59 +0200 Subject: Testlib: Reset the global benchmark data only if it matches You can create multiple instances of QBenchmarkGlobalData as the ctor is public. The qmltest plugin does create a static one, but only conditionally assigns it to the singleton pointer. On shutdown the plugin is removed and the QBenchmarkGlobalData::current should only be reset by the dtor if it's actually pointing to the same object. Change-Id: I616c1ccf6f7a00abf1de342094da88481510bc7b Reviewed-by: Fabian Kosmale Reviewed-by: Simon Hausmann --- src/testlib/qbenchmark.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/testlib/qbenchmark.cpp b/src/testlib/qbenchmark.cpp index 8d5543a0fc..cbc009c993 100644 --- a/src/testlib/qbenchmark.cpp +++ b/src/testlib/qbenchmark.cpp @@ -66,7 +66,8 @@ QBenchmarkGlobalData::QBenchmarkGlobalData() QBenchmarkGlobalData::~QBenchmarkGlobalData() { delete measurer; - QBenchmarkGlobalData::current = 0; + if (QBenchmarkGlobalData::current == this) + QBenchmarkGlobalData::current = nullptr; } void QBenchmarkGlobalData::setMode(Mode mode) -- cgit v1.2.3 From 7b17113e49b44ba5eec86831e3f697e98010f228 Mon Sep 17 00:00:00 2001 From: Tasuku Suzuki Date: Thu, 4 Jul 2019 14:01:32 +0900 Subject: Fix build without features.inotify -no-feature-inotify was ignored on Linux Change-Id: I65bab5b31740f10eacea6735119702a79df5c263 Reviewed-by: Volker Hilsheimer --- src/corelib/io/qfilesystemwatcher.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/corelib/io/qfilesystemwatcher.cpp b/src/corelib/io/qfilesystemwatcher.cpp index e3e4433a6b..64c422c55a 100644 --- a/src/corelib/io/qfilesystemwatcher.cpp +++ b/src/corelib/io/qfilesystemwatcher.cpp @@ -47,7 +47,7 @@ #include #include -#if defined(Q_OS_LINUX) || (defined(Q_OS_QNX) && !defined(QT_NO_INOTIFY)) +#if (defined(Q_OS_LINUX) || defined(Q_OS_QNX)) && QT_CONFIG(inotify) #define USE_INOTIFY #endif -- cgit v1.2.3 From be1e682572733ca5a0c3c65d049551daf8546f41 Mon Sep 17 00:00:00 2001 From: Tasuku Suzuki Date: Mon, 3 Jun 2019 21:24:15 +0900 Subject: Fix build without feature.xmlstreamreader MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit introduces a new feature called vkgen for the tool which depends on xmlstreamreader. Note that when features.vkgen is disabled, vulkan.pri will generate dummy qvulkanfunctions.h and qvulkanfunctions_p.h because buildsystem needs them. Task-number: QTBUG-76159 Change-Id: I03d526a0fd76a2d8b531940f37538cead109d9d3 Reviewed-by: Jörg Bornemann --- src/gui/configure.json | 7 ++++- src/gui/vulkan/vulkan.pri | 72 +++++++++++++++++++++++++---------------------- src/src.pro | 8 ++++-- 3 files changed, 50 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/gui/configure.json b/src/gui/configure.json index c7bafb8925..764e92a729 100644 --- a/src/gui/configure.json +++ b/src/gui/configure.json @@ -1357,9 +1357,14 @@ "condition": "features.opengl-desktop || features.opengl-dynamic || features.opengles2", "output": [ "publicFeature", "feature" ] }, + "vkgen": { + "label": "vkgen", + "condition": "features.xmlstreamreader", + "output": [ "privateFeature" ] + }, "vulkan": { "label": "Vulkan", - "condition": "libs.vulkan", + "condition": "features.vkgen && libs.vulkan", "output": [ "publicFeature" ] }, "openvg": { diff --git a/src/gui/vulkan/vulkan.pri b/src/gui/vulkan/vulkan.pri index 9bd7391235..5c902e8b82 100644 --- a/src/gui/vulkan/vulkan.pri +++ b/src/gui/vulkan/vulkan.pri @@ -17,39 +17,45 @@ qtConfig(vulkan) { QMAKE_USE += vulkan/nolink } -# Generate qvulkanfunctions.h, qvulkanfunctions_p.h, qvulkanfunctions_p.cpp -QMAKE_QVKGEN_INPUT = vulkan/vk.xml -QMAKE_QVKGEN_LICENSE_HEADER = $$QT_SOURCE_TREE/header.LGPL -qtPrepareTool(QMAKE_QVKGEN, qvkgen) - -qvkgen_h.commands = $$QMAKE_QVKGEN ${QMAKE_FILE_IN} $$shell_quote($$QMAKE_QVKGEN_LICENSE_HEADER) ${QMAKE_FILE_OUT_PATH}/${QMAKE_FILE_OUT_BASE} -qvkgen_h.output = $$OUT_PWD/vulkan/qvulkanfunctions.h -qvkgen_h.input = QMAKE_QVKGEN_INPUT -qtConfig(vulkan): \ - qvkgen_h.variable_out = HEADERS -else: \ - qvkgen_h.CONFIG += target_predeps no_link -QMAKE_EXTRA_COMPILERS += qvkgen_h - -qvkgen_ph.commands = $$escape_expand(\\n) -qvkgen_ph.output = $$OUT_PWD/vulkan/qvulkanfunctions_p.h -qvkgen_ph.input = QMAKE_QVKGEN_INPUT -qvkgen_ph.depends += $$OUT_PWD/vulkan/qvulkanfunctions.h -qtConfig(vulkan): \ - qvkgen_ph.variable_out = HEADERS -else: \ - qvkgen_ph.CONFIG += target_predeps no_link -QMAKE_EXTRA_COMPILERS += qvkgen_ph - -qvkgen_pimpl.commands = $$escape_expand(\\n) -qvkgen_pimpl.output = $$OUT_PWD/vulkan/qvulkanfunctions_p.cpp -qvkgen_pimpl.input = QMAKE_QVKGEN_INPUT -qvkgen_pimpl.depends += $$OUT_PWD/vulkan/qvulkanfunctions_p.h -qtConfig(vulkan): \ - qvkgen_pimpl.variable_out = SOURCES -else: \ - qvkgen_pimpl.CONFIG += target_predeps no_link -QMAKE_EXTRA_COMPILERS += qvkgen_pimpl +qtConfig(vkgen) { + # Generate qvulkanfunctions.h, qvulkanfunctions_p.h, qvulkanfunctions_p.cpp + QMAKE_QVKGEN_INPUT = vulkan/vk.xml + QMAKE_QVKGEN_LICENSE_HEADER = $$QT_SOURCE_TREE/header.LGPL + qtPrepareTool(QMAKE_QVKGEN, qvkgen) + + qvkgen_h.commands = $$QMAKE_QVKGEN ${QMAKE_FILE_IN} $$shell_quote($$QMAKE_QVKGEN_LICENSE_HEADER) ${QMAKE_FILE_OUT_PATH}/${QMAKE_FILE_OUT_BASE} + qvkgen_h.output = $$OUT_PWD/vulkan/qvulkanfunctions.h + qvkgen_h.input = QMAKE_QVKGEN_INPUT + qtConfig(vulkan): \ + qvkgen_h.variable_out = HEADERS + else: \ + qvkgen_h.CONFIG += target_predeps no_link + QMAKE_EXTRA_COMPILERS += qvkgen_h + + qvkgen_ph.commands = $$escape_expand(\\n) + qvkgen_ph.output = $$OUT_PWD/vulkan/qvulkanfunctions_p.h + qvkgen_ph.input = QMAKE_QVKGEN_INPUT + qvkgen_ph.depends += $$OUT_PWD/vulkan/qvulkanfunctions.h + qtConfig(vulkan): \ + qvkgen_ph.variable_out = HEADERS + else: \ + qvkgen_ph.CONFIG += target_predeps no_link + QMAKE_EXTRA_COMPILERS += qvkgen_ph + + qvkgen_pimpl.commands = $$escape_expand(\\n) + qvkgen_pimpl.output = $$OUT_PWD/vulkan/qvulkanfunctions_p.cpp + qvkgen_pimpl.input = QMAKE_QVKGEN_INPUT + qvkgen_pimpl.depends += $$OUT_PWD/vulkan/qvulkanfunctions_p.h + qtConfig(vulkan): \ + qvkgen_pimpl.variable_out = SOURCES + else: \ + qvkgen_pimpl.CONFIG += target_predeps no_link + QMAKE_EXTRA_COMPILERS += qvkgen_pimpl +} else { + # generate dummy files to make qmake happy + write_file($$OUT_PWD/vulkan/qvulkanfunctions.h) + write_file($$OUT_PWD/vulkan/qvulkanfunctions_p.h) +} # Ensure qvulkanfunctions.h gets installed correctly targ_headers.CONFIG += no_check_exist diff --git a/src/src.pro b/src/src.pro index b1afdd27a5..9ccc1e8f65 100644 --- a/src/src.pro +++ b/src/src.pro @@ -214,9 +214,11 @@ qtConfig(gui) { SUBDIRS += src_3rdparty_freetype src_platformsupport.depends += src_3rdparty_freetype } - SUBDIRS += src_tools_qvkgen - src_gui.depends += src_tools_qvkgen - TOOLS += src_tools_qvkgen + qtConfig(vkgen) { + SUBDIRS += src_tools_qvkgen + src_gui.depends += src_tools_qvkgen + TOOLS += src_tools_qvkgen + } SUBDIRS += src_gui src_platformsupport src_platformheaders qtConfig(opengl): SUBDIRS += src_openglextensions src_plugins.depends += src_gui src_platformsupport src_platformheaders -- cgit v1.2.3 From c4932f93d53a08401fc61460e1ab2fe3919d9e3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Arve=20S=C3=A6ther?= Date: Tue, 25 Jun 2019 11:39:52 +0200 Subject: Do not access invalid QAccessibleInterface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Noticed this when running Qt Creator against dev Change-Id: Ie75b2e0f2a77033ce0455d7ee3304193aa6e511c Reviewed-by: André de la Rocha --- src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp index b276798316..943c1ff368 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp @@ -153,7 +153,7 @@ void QWindowsUiaMainProvider::notifyValueChange(QAccessibleValueChangeEvent *eve int count = listacc->childCount(); for (int i = 0; i < count; ++i) { QAccessibleInterface *item = listacc->child(i); - if (item && item->text(QAccessible::Name) == event->value()) { + if (item && item->isValid() && item->text(QAccessible::Name) == event->value()) { if (!item->state().selected) { if (QAccessibleActionInterface *actionInterface = item->actionInterface()) actionInterface->doAction(QAccessibleActionInterface::toggleAction()); -- cgit v1.2.3 From bcd4b14026094d0077ad1069054676cc6da96251 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Arve=20S=C3=A6ther?= Date: Tue, 2 Jul 2019 17:11:20 +0200 Subject: Fix qFindTestData() to work with relative resource paths If the resource path 'base' was relative it would enter condition 3) in qFindTestData() and it would actually find the nonsensical "://data" as a viable candidate. We don't want to enter that case, but rather enter the subsequent ('Try resources') case Change-Id: I1928ba02c941e23fee4fec9052a1981e46fa59b7 Task-number: QTBUG-73512 Reviewed-by: Friedemann Kleint --- src/testlib/qtestcase.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index 10477238cb..619376fd33 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -2206,7 +2206,7 @@ QString QTest::qFindTestData(const QString& base, const char *file, int line, co } // 3. relative to test source. - if (found.isEmpty()) { + if (found.isEmpty() && qstrncmp(file, ":/", 2) != 0) { // srcdir is the directory containing the calling source file. QFileInfo srcdir = QFileInfo(QFile::decodeName(file)).path(); -- cgit v1.2.3 From 6061e6820b97c45100d2e761a9bb302c71ce3e62 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 23 May 2018 11:52:14 +0200 Subject: Initialize variables to make coverity happy Change-Id: Id574a114cbed04927ae380c6d97027cf4b351032 Reviewed-by: Thiago Macieira --- src/corelib/io/qtemporaryfile_p.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/corelib/io/qtemporaryfile_p.h b/src/corelib/io/qtemporaryfile_p.h index 0fec88d3cd..6bcff936b4 100644 --- a/src/corelib/io/qtemporaryfile_p.h +++ b/src/corelib/io/qtemporaryfile_p.h @@ -145,10 +145,10 @@ public: bool isUnnamedFile() const override final; const QString &templateName; - quint32 fileMode; + quint32 fileMode = 0; int flags = 0; - bool filePathIsTemplate; - bool filePathWasTemplate; + bool filePathIsTemplate = true; + bool filePathWasTemplate = true; bool unnamedFile = false; }; -- cgit v1.2.3 From 9b3e8b32f2657948591f6bdffea9d48291b8a69b Mon Sep 17 00:00:00 2001 From: Sona Kurazyan Date: Thu, 27 Jun 2019 15:48:30 +0200 Subject: Remove usages of deprecated APIs of corelib - Replaced the usages of deprecated APIs of corelib by corresponding alternatives in the library code and documentation. - Modified the tests to make them build when deprecated APIs disabled: * Made the the parts of the tests testing the deprecated APIs to be compiled conditionally, only when the corresponding methods are enabled. * If the test-case tests only the deprecated API, but not the corresponding replacement, added tests for the replacement. Task-number: QTBUG-76491 Task-number: QTBUG-76539 Task-number: QTBUG-76541 Change-Id: I62ed4a5b530a965ec3f6502c6480808f938921aa Reviewed-by: Volker Hilsheimer --- src/corelib/io/qsettings.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/corelib/io/qsettings.cpp b/src/corelib/io/qsettings.cpp index 9234a23f3a..accde01f16 100644 --- a/src/corelib/io/qsettings.cpp +++ b/src/corelib/io/qsettings.cpp @@ -2048,8 +2048,8 @@ void QConfFileSettingsPrivate::ensureSectionParsed(QConfFile *confFile, QPixmap, which are part of Qt GUI. In other words, there is no \c toColor(), \c toImage(), or \c toPixmap() functions in QVariant. - Instead, you can use the QVariant::value() or the qVariantValue() - template function. For example: + Instead, you can use the QVariant::value() template function. + For example: \snippet code/src_corelib_io_qsettings.cpp 0 -- cgit v1.2.3 From b0cd007335853f283c47ffb0f5611d14e6dbe84b Mon Sep 17 00:00:00 2001 From: Sona Kurazyan Date: Thu, 4 Jul 2019 15:06:13 +0200 Subject: Remove usages of QSysInfo's deprecated APIs - Replaced QOperatingSystemVersion::WindowsVersion, QSysInfo::windowsVersion(), QSysInfo::macVersion(), QSysInfo::MacintoshVersion with QOperatingSystemVersion::current(). - Added QOperatingSystemVersion::WindowsVista for convenience, as it is used in lots of places. Change-Id: If9c4ac496005b2e70b5c70be160747afa74b98c1 Reviewed-by: Timur Pocheptsov --- src/corelib/global/qoperatingsystemversion.cpp | 8 ++++++++ src/corelib/global/qoperatingsystemversion.h | 1 + src/network/ssl/qsslsocket_openssl11.cpp | 3 ++- src/network/ssl/qsslsocket_winrt.cpp | 1 + src/plugins/bearer/nativewifi/qnativewifiengine.cpp | 4 +++- src/plugins/styles/windowsvista/qwindowsvistastyle.cpp | 5 ++--- 6 files changed, 17 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/corelib/global/qoperatingsystemversion.cpp b/src/corelib/global/qoperatingsystemversion.cpp index 42a1275621..9084ad933c 100644 --- a/src/corelib/global/qoperatingsystemversion.cpp +++ b/src/corelib/global/qoperatingsystemversion.cpp @@ -355,6 +355,14 @@ bool QOperatingSystemVersion::isAnyOfType(std::initializer_list types) c return false; } +/*! + \variable QOperatingSystemVersion::WindowsVista + \brief a version corresponding to Windows Vista (version 6.0). + \since 6.0 + */ +const QOperatingSystemVersion QOperatingSystemVersion::WindowsVista = + QOperatingSystemVersion(QOperatingSystemVersion::Windows, 6, 0); + /*! \variable QOperatingSystemVersion::Windows7 \brief a version corresponding to Windows 7 (version 6.1). diff --git a/src/corelib/global/qoperatingsystemversion.h b/src/corelib/global/qoperatingsystemversion.h index e99e4f8997..75c54b60df 100644 --- a/src/corelib/global/qoperatingsystemversion.h +++ b/src/corelib/global/qoperatingsystemversion.h @@ -60,6 +60,7 @@ public: Android }; + static const QOperatingSystemVersion WindowsVista; static const QOperatingSystemVersion Windows7; static const QOperatingSystemVersion Windows8; static const QOperatingSystemVersion Windows8_1; diff --git a/src/network/ssl/qsslsocket_openssl11.cpp b/src/network/ssl/qsslsocket_openssl11.cpp index b60b8be41f..cc2d6ea2d9 100644 --- a/src/network/ssl/qsslsocket_openssl11.cpp +++ b/src/network/ssl/qsslsocket_openssl11.cpp @@ -68,6 +68,7 @@ #include #include #include +#include QT_BEGIN_NAMESPACE @@ -147,7 +148,7 @@ void QSslSocketPrivate::ensureCiphersAndCertsLoaded() //its own cert bundle rather than the system one. //Same logic that disables the unix on demand cert loading. //Unlike unix, we do preload the certificates from the cert store. - if ((QSysInfo::windowsVersion() & QSysInfo::WV_NT_based) >= QSysInfo::WV_6_0) + if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::WindowsVista) s_loadRootCertsOnDemand = true; #endif } diff --git a/src/network/ssl/qsslsocket_winrt.cpp b/src/network/ssl/qsslsocket_winrt.cpp index d54ac2ad73..39c1ce55e3 100644 --- a/src/network/ssl/qsslsocket_winrt.cpp +++ b/src/network/ssl/qsslsocket_winrt.cpp @@ -177,6 +177,7 @@ void QSslSocketPrivate::ensureInitialized() long QSslSocketPrivate::sslLibraryVersionNumber() { + // ### Qt 6: Find a proper replacement for the deprecated method below. return QSysInfo::windowsVersion(); } diff --git a/src/plugins/bearer/nativewifi/qnativewifiengine.cpp b/src/plugins/bearer/nativewifi/qnativewifiengine.cpp index bb43072aba..777b4eea59 100644 --- a/src/plugins/bearer/nativewifi/qnativewifiengine.cpp +++ b/src/plugins/bearer/nativewifi/qnativewifiengine.cpp @@ -45,6 +45,7 @@ #include #include +#include #include @@ -612,7 +613,8 @@ bool QNativeWifiEngine::requiresPolling() const { // On Windows XP SP2 and SP3 only connection and disconnection notifications are available. // We need to poll for changes in available wireless networks. - return QSysInfo::WindowsVersion <= QSysInfo::WV_2003; + return QOperatingSystemVersion::current() + <= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 5, 2); } QT_END_NAMESPACE diff --git a/src/plugins/styles/windowsvista/qwindowsvistastyle.cpp b/src/plugins/styles/windowsvista/qwindowsvistastyle.cpp index 8a3ae17b1d..6881ea8f16 100644 --- a/src/plugins/styles/windowsvista/qwindowsvistastyle.cpp +++ b/src/plugins/styles/windowsvista/qwindowsvistastyle.cpp @@ -84,9 +84,8 @@ static const int windowsRightBorder = 15; // right border on windows */ bool QWindowsVistaStylePrivate::useVista() { - return (QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA - && (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based)) - && QWindowsVistaStylePrivate::useXP(); + return QOperatingSystemVersion::current() >= QOperatingSystemVersion::WindowsVista + && QWindowsVistaStylePrivate::useXP(); } /* \internal -- cgit v1.2.3 From 6b26b2ee03bd67fbda4cd343c461560d9c376321 Mon Sep 17 00:00:00 2001 From: Tasuku Suzuki Date: Thu, 4 Jul 2019 11:10:44 +0900 Subject: Fix build without features.action Change-Id: Ia776cdcb36d07bb89f39c631029458adf2187d90 Reviewed-by: Volker Hilsheimer --- src/widgets/styles/qwindowsstyle.cpp | 1 + src/widgets/widgets/qmdisubwindow.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/widgets/styles/qwindowsstyle.cpp b/src/widgets/styles/qwindowsstyle.cpp index 1d5934e3f7..c13f6e637d 100644 --- a/src/widgets/styles/qwindowsstyle.cpp +++ b/src/widgets/styles/qwindowsstyle.cpp @@ -84,6 +84,7 @@ #include #include #include +#include #include #if QT_CONFIG(animation) diff --git a/src/widgets/widgets/qmdisubwindow.cpp b/src/widgets/widgets/qmdisubwindow.cpp index 77692930fc..474cce983c 100644 --- a/src/widgets/widgets/qmdisubwindow.cpp +++ b/src/widgets/widgets/qmdisubwindow.cpp @@ -1986,7 +1986,7 @@ void QMdiSubWindowPrivate::updateActions() for (int i = 0; i < NumWindowStateActions; ++i) setVisible(WindowStateAction(i), false); -#ifdef Q_OS_MACOS +#if defined(Q_OS_MACOS) && QT_CONFIG(action) if (q_func()->style()->inherits("QMacStyle")) for (int i = 0; i < NumWindowStateActions; ++i) if (QAction *action = actions[i]) -- cgit v1.2.3 From 5d79f38370a22443bccd271dea89a4344dcbc2e6 Mon Sep 17 00:00:00 2001 From: Tasuku Suzuki Date: Fri, 5 Jul 2019 07:51:28 +0900 Subject: Fix build without features.library Change-Id: I3ce4a8e4c6488737115f2f16bfd8f0e1fc5fc4fa Reviewed-by: Volker Hilsheimer --- src/network/ssl/qsslsocket_opensslpre11.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/network/ssl/qsslsocket_opensslpre11.cpp b/src/network/ssl/qsslsocket_opensslpre11.cpp index f5aab821ea..2af437f0fa 100644 --- a/src/network/ssl/qsslsocket_opensslpre11.cpp +++ b/src/network/ssl/qsslsocket_opensslpre11.cpp @@ -67,7 +67,6 @@ #include #include #include -#include QT_BEGIN_NAMESPACE -- cgit v1.2.3 From e36247754e9a1f1f6626fa042d03a9751fcf2167 Mon Sep 17 00:00:00 2001 From: Tasuku Suzuki Date: Fri, 5 Jul 2019 07:50:24 +0900 Subject: Fix build without features.mimetype Change-Id: I8c5521c5cfbc6c13c78d2bc8805fa5a021675b6c Reviewed-by: Simon Hausmann --- src/widgets/widgets/qtextbrowser.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/widgets/widgets/qtextbrowser.cpp b/src/widgets/widgets/qtextbrowser.cpp index bee1021950..2f992b1cff 100644 --- a/src/widgets/widgets/qtextbrowser.cpp +++ b/src/widgets/widgets/qtextbrowser.cpp @@ -57,7 +57,6 @@ #endif #include #include -#include QT_BEGIN_NAMESPACE -- cgit v1.2.3 From a393ea072982fa706bb6585295ed6c22e7cbca06 Mon Sep 17 00:00:00 2001 From: Tasuku Suzuki Date: Fri, 5 Jul 2019 06:13:07 +0900 Subject: Fix build without features.itemmodel Change-Id: I1235bed395a47438bc18571e2331a3432e274dec Reviewed-by: Christian Ehrlicher Reviewed-by: Shawn Rutledge --- src/gui/text/qtextmarkdownwriter.cpp | 5 +++++ src/gui/text/qtextmarkdownwriter_p.h | 5 ++++- src/testlib/qtest.h | 4 ++++ 3 files changed, 13 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/gui/text/qtextmarkdownwriter.cpp b/src/gui/text/qtextmarkdownwriter.cpp index f351c8d20b..cbfb092485 100644 --- a/src/gui/text/qtextmarkdownwriter.cpp +++ b/src/gui/text/qtextmarkdownwriter.cpp @@ -47,6 +47,9 @@ #include "qtextcursor.h" #include "qtextimagehandler_p.h" #include "qloggingcategory.h" +#if QT_CONFIG(itemmodel) +#include "qabstractitemmodel.h" +#endif QT_BEGIN_NAMESPACE @@ -70,6 +73,7 @@ bool QTextMarkdownWriter::writeAll(const QTextDocument *document) return true; } +#if QT_CONFIG(itemmodel) void QTextMarkdownWriter::writeTable(const QAbstractItemModel *table) { QVector tableColumnWidths(table->columnCount()); @@ -101,6 +105,7 @@ void QTextMarkdownWriter::writeTable(const QAbstractItemModel *table) } m_listInfo.clear(); } +#endif void QTextMarkdownWriter::writeFrame(const QTextFrame *frame) { diff --git a/src/gui/text/qtextmarkdownwriter_p.h b/src/gui/text/qtextmarkdownwriter_p.h index 90310250ac..c3076155d0 100644 --- a/src/gui/text/qtextmarkdownwriter_p.h +++ b/src/gui/text/qtextmarkdownwriter_p.h @@ -56,16 +56,19 @@ #include "qtextdocument_p.h" #include "qtextdocumentwriter.h" -#include "QAbstractTableModel" QT_BEGIN_NAMESPACE +class QAbstractItemModel; + class Q_GUI_EXPORT QTextMarkdownWriter { public: QTextMarkdownWriter(QTextStream &stream, QTextDocument::MarkdownFeatures features); bool writeAll(const QTextDocument *document); +#if QT_CONFIG(itemmodel) void writeTable(const QAbstractItemModel *table); +#endif int writeBlock(const QTextBlock &block, bool table, bool ignoreFormat, bool ignoreEmpty); void writeFrame(const QTextFrame *frame); diff --git a/src/testlib/qtest.h b/src/testlib/qtest.h index 89abc616d9..7affdcb8b4 100644 --- a/src/testlib/qtest.h +++ b/src/testlib/qtest.h @@ -51,7 +51,9 @@ #include #include #include +#if QT_CONFIG(itemmodel) #include +#endif #include #include #include @@ -129,12 +131,14 @@ template<> inline char *toString(const QChar &c) return qstrdup(qPrintable(QString::fromLatin1("QChar: '%1' (0x%2)").arg(c).arg(QString::number(static_cast(c.unicode()), 16)))); } +#if QT_CONFIG(itemmodel) template<> inline char *toString(const QModelIndex &idx) { char msg[128]; qsnprintf(msg, sizeof(msg), "QModelIndex(%d,%d,%p,%p)", idx.row(), idx.column(), idx.internalPointer(), idx.model()); return qstrdup(msg); } +#endif template<> inline char *toString(const QPoint &p) { -- cgit v1.2.3 From 95310aac6d38f222da5ce0ca2bd52b4afa261f13 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Wed, 26 Jun 2019 23:41:40 +0200 Subject: Short live QRecursiveMutex! Move the recursive mutex use case out of QMutex into a separate class, unsurprisingly called QRecursiveMutex. As an immediate benefit, 90% of the QMutex users now enjoy a constexpr QMutex ctor. This change prepares for a real split in Qt 6, so that both use-cases are no longer bundled up in one class. [ChangeLog][QtCore][QMutex] Added QRecursiveMutex as a replacement of QMutex(QMutex::Recursive). Change-Id: I79b8724e8a8ee65e4bd0f06acd76103fe4197b8c Reviewed-by: Volker Hilsheimer --- src/corelib/thread/qmutex.cpp | 100 +++++++++++++++++++++++++++++++++++++----- src/corelib/thread/qmutex.h | 34 +++++++++++++- 2 files changed, 122 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/corelib/thread/qmutex.cpp b/src/corelib/thread/qmutex.cpp index bd3a0fa7ba..9e52f286ee 100644 --- a/src/corelib/thread/qmutex.cpp +++ b/src/corelib/thread/qmutex.cpp @@ -147,7 +147,7 @@ public: It is constructed and destroyed with almost no overhead, which means it is fine to have many mutexes as part of other classes. - \sa QMutexLocker, QReadWriteLock, QSemaphore, QWaitCondition + \sa QRecursiveMutex, QMutexLocker, QReadWriteLock, QSemaphore, QWaitCondition */ /*! @@ -156,12 +156,19 @@ public: \value Recursive In this mode, a thread can lock the same mutex multiple times and the mutex won't be unlocked until a corresponding number of unlock() calls - have been made. + have been made. You should use QRecursiveMutex + for this use-case. \value NonRecursive In this mode, a thread may only lock a mutex once. - \sa QMutex() + \sa QMutex(), QRecursiveMutex +*/ + +/*! + \fn QMutex::QMutex() + + Constructs a new mutex. The mutex is created in an unlocked state. */ /*! @@ -205,13 +212,15 @@ QMutex::~QMutex() } /*! \fn void QMutex::lock() + \fn QRecursiveMutex::lock() + Locks the mutex. If another thread has locked the mutex then this call will block until that thread has unlocked it. Calling this function multiple times on the same mutex from the same thread is allowed if this mutex is a - \l{QMutex::Recursive}{recursive mutex}. If this mutex is a - \l{QMutex::NonRecursive}{non-recursive mutex}, this function will + \l{QRecursiveMutex}{recursive mutex}. If this mutex is a + \l{QMutex}{non-recursive mutex}, this function will \e dead-lock when the mutex is locked recursively. \sa unlock() @@ -228,6 +237,7 @@ void QMutex::lock() QT_MUTEX_LOCK_NOEXCEPT } /*! \fn bool QMutex::tryLock(int timeout) + \fn bool QRecursiveMutex::tryLock(int timeout) Attempts to lock the mutex. This function returns \c true if the lock was obtained; otherwise it returns \c false. If another thread has @@ -243,8 +253,8 @@ void QMutex::lock() QT_MUTEX_LOCK_NOEXCEPT Calling this function multiple times on the same mutex from the same thread is allowed if this mutex is a - \l{QMutex::Recursive}{recursive mutex}. If this mutex is a - \l{QMutex::NonRecursive}{non-recursive mutex}, this function will + \l{QRecursiveMutex}{recursive mutex}. If this mutex is a + \l{QMutex}{non-recursive mutex}, this function will \e always return false when attempting to lock the mutex recursively. @@ -262,6 +272,7 @@ bool QMutex::tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT } /*! \fn bool QMutex::try_lock() + \fn bool QRecursiveMutex::try_lock() \since 5.8 Attempts to lock the mutex. This function returns \c true if the lock @@ -275,6 +286,7 @@ bool QMutex::tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT */ /*! \fn template bool QMutex::try_lock_for(std::chrono::duration duration) + \fn template bool QRecursiveMutex::try_lock_for(std::chrono::duration duration) \since 5.8 Attempts to lock the mutex. This function returns \c true if the lock @@ -290,8 +302,8 @@ bool QMutex::tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT Calling this function multiple times on the same mutex from the same thread is allowed if this mutex is a - \l{QMutex::Recursive}{recursive mutex}. If this mutex is a - \l{QMutex::NonRecursive}{non-recursive mutex}, this function will + \l{QRecursiveMutex}{recursive mutex}. If this mutex is a + \l{QMutex}{non-recursive mutex}, this function will \e always return false when attempting to lock the mutex recursively. @@ -299,6 +311,7 @@ bool QMutex::tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT */ /*! \fn template bool QMutex::try_lock_until(std::chrono::time_point timePoint) + \fn template bool QRecursiveMutex::try_lock_until(std::chrono::time_point timePoint) \since 5.8 Attempts to lock the mutex. This function returns \c true if the lock @@ -314,8 +327,8 @@ bool QMutex::tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT Calling this function multiple times on the same mutex from the same thread is allowed if this mutex is a - \l{QMutex::Recursive}{recursive mutex}. If this mutex is a - \l{QMutex::NonRecursive}{non-recursive mutex}, this function will + \l{QRecursiveMutex}{recursive mutex}. If this mutex is a + \l{QMutex}{non-recursive mutex}, this function will \e always return false when attempting to lock the mutex recursively. @@ -323,6 +336,8 @@ bool QMutex::tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT */ /*! \fn void QMutex::unlock() + \fn void QRecursiveMutex::unlock() + Unlocks the mutex. Attempting to unlock a mutex in a different thread to the one that locked it results in an error. Unlocking a mutex that is not locked results in undefined behavior. @@ -363,6 +378,58 @@ bool QBasicMutex::isRecursive() const noexcept return QT_PREPEND_NAMESPACE(isRecursive)(d_ptr.loadAcquire()); } +/*! + \class QRecursiveMutex + \inmodule QtCore + \since 5.14 + \brief The QRecursiveMutex class provides access serialization between threads. + + \threadsafe + + \ingroup thread + + The QRecursiveMutex class is a mutex, like QMutex, with which it is + API-compatible. It differs from QMutex by accepting lock() calls from + the same thread any number of times. QMutex would deadlock in this situation. + + QRecursiveMutex is much more expensive to construct and operate on, so + use a plain QMutex whenever you can. Sometimes, one public function, + however, calls another public function, and they both need to lock the + same mutex. In this case, you have two options: + + \list + \li Factor the code that needs mutex protection into private functions, + which assume that the mutex is held when they are called, and lock a + plain QMutex in the public functions before you call the private + implementation ones. + \li Or use a recursive mutex, so it doesn't matter that the first public + function has already locked the mutex when the second one wishes to do so. + \endlist + + \sa QMutex, QMutexLocker, QReadWriteLock, QSemaphore, QWaitCondition +*/ + +/*! + Constructs a new recursive mutex. The mutex is created in an unlocked state. + + \sa lock(), unlock() +*/ +QRecursiveMutex::QRecursiveMutex() + : QMutex() +{ + d_ptr.storeRelaxed(new QRecursiveMutexPrivate); +} + +/*! + Destroys the mutex. + + \warning Destroying a locked mutex may result in undefined behavior. +*/ +QRecursiveMutex::~QRecursiveMutex() +{ + delete static_cast(d_ptr.fetchAndStoreAcquire(nullptr)); +} + /*! \class QMutexLocker \inmodule QtCore @@ -426,6 +493,17 @@ bool QBasicMutex::isRecursive() const noexcept \sa QMutex::lock() */ +/*! + \fn QMutexLocker::QMutexLocker(QRecursiveMutex *mutex) + \since 5.14 + + Constructs a QMutexLocker and locks \a mutex. The mutex will be + unlocked (unlock() called) when the QMutexLocker is destroyed. + If \a mutex is \nullptr, QMutexLocker does nothing. + + \sa QMutex::lock() +*/ + /*! \fn QMutexLocker::~QMutexLocker() diff --git a/src/corelib/thread/qmutex.h b/src/corelib/thread/qmutex.h index 0de0869cb2..c693ff65d8 100644 --- a/src/corelib/thread/qmutex.h +++ b/src/corelib/thread/qmutex.h @@ -62,6 +62,8 @@ QT_BEGIN_NAMESPACE # define QT_MUTEX_LOCK_NOEXCEPT #endif +class QMutex; +class QRecursiveMutex; class QMutexData; class Q_CORE_EXPORT QBasicMutex @@ -120,14 +122,20 @@ private: } friend class QMutex; + friend class QRecursiveMutex; friend class QMutexData; }; class Q_CORE_EXPORT QMutex : public QBasicMutex { public: +#if defined(Q_COMPILER_CONSTEXPR) + constexpr QMutex() = default; +#else + QMutex() { d_ptr.storeRelaxed(nullptr); } +#endif enum RecursionMode { NonRecursive, Recursive }; - explicit QMutex(RecursionMode mode = NonRecursive); + explicit QMutex(RecursionMode mode); ~QMutex(); // BasicLockable concept @@ -164,6 +172,7 @@ public: private: Q_DISABLE_COPY(QMutex) friend class QMutexLocker; + friend class QRecursiveMutex; friend class ::tst_QMutex; #if QT_HAS_INCLUDE() @@ -192,6 +201,24 @@ private: #endif }; +class QRecursiveMutex : private QMutex +{ + // ### Qt 6: make it independent of QMutex + friend class QMutexLocker; +public: + Q_CORE_EXPORT QRecursiveMutex(); + Q_CORE_EXPORT ~QRecursiveMutex(); + + using QMutex::lock; + using QMutex::tryLock; + using QMutex::unlock; + using QMutex::try_lock; +#if QT_HAS_INCLUDE() + using QMutex::try_lock_for; + using QMutex::try_lock_until; +#endif +}; + class Q_CORE_EXPORT QMutexLocker { public: @@ -207,8 +234,11 @@ public: val |= 1; } } + explicit QMutexLocker(QRecursiveMutex *m) QT_MUTEX_LOCK_NOEXCEPT + : QMutexLocker{static_cast(m)} {} #else QMutexLocker(QMutex *) { } + QMutexLocker(QRecursiveMutex *) {} #endif inline ~QMutexLocker() { unlock(); } @@ -285,6 +315,8 @@ private: Q_DISABLE_COPY(QMutex) }; +class QRecursiveMutex : public QMutex {}; + class Q_CORE_EXPORT QMutexLocker { public: -- cgit v1.2.3 From cd113d0dcb5e6ff71300ed67af5e296dc6f5dccf Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Thu, 27 Jun 2019 15:47:16 +0200 Subject: Port some trivial cases from QMutex to QRecursiveMutex In all of these cases, the effect of the change is local to one file. Change-Id: I3bda3aadee3b42e7797183c2330183390b92d1f2 Reviewed-by: Thiago Macieira --- src/corelib/io/qresource.cpp | 4 ++-- src/corelib/kernel/qcoreapplication.cpp | 2 +- src/corelib/plugin/qfactoryloader.cpp | 2 +- src/opengl/qgl.cpp | 7 +------ src/platformsupport/fontdatabases/freetype/qfontengine_ft_p.h | 4 ++-- src/plugins/platforms/android/androidjnimenu.cpp | 4 ++-- 6 files changed, 9 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/corelib/io/qresource.cpp b/src/corelib/io/qresource.cpp index d143f7fda3..fcc5b69179 100644 --- a/src/corelib/io/qresource.cpp +++ b/src/corelib/io/qresource.cpp @@ -197,13 +197,13 @@ Q_DECLARE_TYPEINFO(QResourceRoot, Q_MOVABLE_TYPE); typedef QList ResourceList; struct QResourceGlobalData { - QMutex resourceMutex{QMutex::Recursive}; + QRecursiveMutex resourceMutex; ResourceList resourceList; QStringList resourceSearchPaths; }; Q_GLOBAL_STATIC(QResourceGlobalData, resourceGlobalData) -static inline QMutex *resourceMutex() +static inline QRecursiveMutex *resourceMutex() { return &resourceGlobalData->resourceMutex; } static inline ResourceList *resourceList() diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index 9d8da70e47..87dae896fa 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -2657,7 +2657,7 @@ QString QCoreApplication::applicationVersion() #if QT_CONFIG(library) -Q_GLOBAL_STATIC_WITH_ARGS(QMutex, libraryPathMutex, (QMutex::Recursive)) +Q_GLOBAL_STATIC(QRecursiveMutex, libraryPathMutex) /*! Returns a list of paths that the application will search when diff --git a/src/corelib/plugin/qfactoryloader.cpp b/src/corelib/plugin/qfactoryloader.cpp index 8e349f23ce..6737aeccd2 100644 --- a/src/corelib/plugin/qfactoryloader.cpp +++ b/src/corelib/plugin/qfactoryloader.cpp @@ -170,7 +170,7 @@ public: Q_GLOBAL_STATIC(QList, qt_factory_loaders) -Q_GLOBAL_STATIC_WITH_ARGS(QMutex, qt_factoryloader_mutex, (QMutex::Recursive)) +Q_GLOBAL_STATIC(QRecursiveMutex, qt_factoryloader_mutex) QFactoryLoaderPrivate::~QFactoryLoaderPrivate() { diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index 2b4af3ef9f..2c5a40a992 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -1667,11 +1667,6 @@ bool operator!=(const QGLFormat& a, const QGLFormat& b) } struct QGLContextGroupList { - QGLContextGroupList() - : m_mutex(QMutex::Recursive) - { - } - void append(QGLContextGroup *group) { QMutexLocker locker(&m_mutex); m_list.append(group); @@ -1683,7 +1678,7 @@ struct QGLContextGroupList { } QList m_list; - QMutex m_mutex; + QRecursiveMutex m_mutex; }; Q_GLOBAL_STATIC(QGLContextGroupList, qt_context_groups) diff --git a/src/platformsupport/fontdatabases/freetype/qfontengine_ft_p.h b/src/platformsupport/fontdatabases/freetype/qfontengine_ft_p.h index 2d1d5e6572..2765db2946 100644 --- a/src/platformsupport/fontdatabases/freetype/qfontengine_ft_p.h +++ b/src/platformsupport/fontdatabases/freetype/qfontengine_ft_p.h @@ -116,11 +116,11 @@ private: friend class QFontEngineFT; friend class QtFreetypeData; friend struct QScopedPointerDeleter; - QFreetypeFace() : _lock(QMutex::Recursive) {} + QFreetypeFace() = default; ~QFreetypeFace() {} void cleanup(); QAtomicInt ref; - QMutex _lock; + QRecursiveMutex _lock; QByteArray fontData; QFontEngine::Holder hbFace; diff --git a/src/plugins/platforms/android/androidjnimenu.cpp b/src/plugins/platforms/android/androidjnimenu.cpp index 6f548aba52..f51a3920b8 100644 --- a/src/plugins/platforms/android/androidjnimenu.cpp +++ b/src/plugins/platforms/android/androidjnimenu.cpp @@ -60,12 +60,12 @@ namespace QtAndroidMenu { static QList pendingContextMenus; static QAndroidPlatformMenu *visibleMenu = 0; - static QMutex visibleMenuMutex(QMutex::Recursive); + static QRecursiveMutex visibleMenuMutex; static QSet menuBars; static QAndroidPlatformMenuBar *visibleMenuBar = 0; static QWindow *activeTopLevelWindow = 0; - static QMutex menuBarMutex(QMutex::Recursive); + static QRecursiveMutex menuBarMutex; static jmethodID openContextMenuMethodID = 0; -- cgit v1.2.3 From 5de9dedbc739aeb43546f23b45061231e34b184e Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Thu, 27 Jun 2019 19:21:42 +0200 Subject: QtNetwork: bearer: port from QMutex::Recursive to QRecursiveMutex Change-Id: I691ecbaf9ea0796decbb48fd62b25d0a2941d979 Reviewed-by: Thiago Macieira Reviewed-by: Timur Pocheptsov --- src/network/bearer/qbearerengine.cpp | 2 +- src/network/bearer/qbearerengine_p.h | 2 +- src/network/bearer/qnetworkconfigmanager_p.cpp | 2 +- src/network/bearer/qnetworkconfigmanager_p.h | 2 +- src/network/bearer/qnetworkconfiguration_p.h | 3 +-- src/network/bearer/qnetworksession_p.h | 4 ++-- 6 files changed, 7 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/network/bearer/qbearerengine.cpp b/src/network/bearer/qbearerengine.cpp index dbcf1e530e..a7c139fe63 100644 --- a/src/network/bearer/qbearerengine.cpp +++ b/src/network/bearer/qbearerengine.cpp @@ -62,7 +62,7 @@ static bool hasUsedConfiguration(const QHash snapConfigurations; QHash userChoiceConfigurations; - mutable QMutex mutex; + mutable QRecursiveMutex mutex; }; QT_END_NAMESPACE diff --git a/src/network/bearer/qnetworkconfigmanager_p.cpp b/src/network/bearer/qnetworkconfigmanager_p.cpp index a903ecda5f..91ea063af1 100644 --- a/src/network/bearer/qnetworkconfigmanager_p.cpp +++ b/src/network/bearer/qnetworkconfigmanager_p.cpp @@ -58,7 +58,7 @@ QT_BEGIN_NAMESPACE QNetworkConfigurationManagerPrivate::QNetworkConfigurationManagerPrivate() - : QObject(), pollTimer(0), mutex(QMutex::Recursive), + : QObject(), pollTimer(0), loader(QBearerEngineFactoryInterface_iid, QLatin1String("/bearer")), forcedPolling(0), firstUpdate(true) { diff --git a/src/network/bearer/qnetworkconfigmanager_p.h b/src/network/bearer/qnetworkconfigmanager_p.h index 380e25c22f..4819c2027c 100644 --- a/src/network/bearer/qnetworkconfigmanager_p.h +++ b/src/network/bearer/qnetworkconfigmanager_p.h @@ -117,7 +117,7 @@ private: QThread *bearerThread; private: - mutable QMutex mutex; + mutable QRecursiveMutex mutex; QFactoryLoader loader; QList sessionEngines; diff --git a/src/network/bearer/qnetworkconfiguration_p.h b/src/network/bearer/qnetworkconfiguration_p.h index 2213d2ae14..96854fe831 100644 --- a/src/network/bearer/qnetworkconfiguration_p.h +++ b/src/network/bearer/qnetworkconfiguration_p.h @@ -65,7 +65,6 @@ class QNetworkConfigurationPrivate : public QSharedData { public: QNetworkConfigurationPrivate() : - mutex(QMutex::Recursive), type(QNetworkConfiguration::Invalid), purpose(QNetworkConfiguration::UnknownPurpose), bearerType(QNetworkConfiguration::BearerUnknown), @@ -73,7 +72,7 @@ public: timeout(DefaultTimeout) {} - mutable QMutex mutex; + mutable QRecursiveMutex mutex; QString name; QString id; diff --git a/src/network/bearer/qnetworksession_p.h b/src/network/bearer/qnetworksession_p.h index 661587603c..7c1ff63b68 100644 --- a/src/network/bearer/qnetworksession_p.h +++ b/src/network/bearer/qnetworksession_p.h @@ -68,7 +68,7 @@ class Q_NETWORK_EXPORT QNetworkSessionPrivate : public QObject public: QNetworkSessionPrivate() : QObject(), - state(QNetworkSession::Invalid), isOpen(false), mutex(QMutex::Recursive) + state(QNetworkSession::Invalid), isOpen(false) {} virtual ~QNetworkSessionPrivate() {} @@ -147,7 +147,7 @@ protected: QNetworkSession::State state; bool isOpen; - QMutex mutex; + QRecursiveMutex mutex; }; QT_END_NAMESPACE -- cgit v1.2.3 From aadf64f08486766bb6d40bcaae47e9b4d6411c28 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Thu, 27 Jun 2019 19:01:31 +0200 Subject: QtGui: port from QMutex::Recursive to QRecursiveMutex Change-Id: I1ce4fcfa1bfb9a89fe3ebe61d049b9b3100fd700 Reviewed-by: Thiago Macieira --- src/gui/kernel/qopenglcontext.cpp | 3 +-- src/gui/kernel/qopenglcontext_p.h | 5 ++--- src/gui/text/qfont.cpp | 2 +- src/gui/text/qfontdatabase.cpp | 6 +++--- src/gui/util/qdesktopservices.cpp | 4 ++-- 5 files changed, 9 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/gui/kernel/qopenglcontext.cpp b/src/gui/kernel/qopenglcontext.cpp index a3b2ea5f86..326d2823eb 100644 --- a/src/gui/kernel/qopenglcontext.cpp +++ b/src/gui/kernel/qopenglcontext.cpp @@ -1610,8 +1610,7 @@ void QOpenGLSharedResourceGuard::freeResource(QOpenGLContext *context) QOpenGLMultiGroupSharedResource instance. */ QOpenGLMultiGroupSharedResource::QOpenGLMultiGroupSharedResource() - : active(0), - m_mutex(QMutex::Recursive) + : active(0) { #ifdef QT_GL_CONTEXT_RESOURCE_DEBUG qDebug("Creating context group resource object %p.", this); diff --git a/src/gui/kernel/qopenglcontext_p.h b/src/gui/kernel/qopenglcontext_p.h index c6ad893ee6..833cfb20c3 100644 --- a/src/gui/kernel/qopenglcontext_p.h +++ b/src/gui/kernel/qopenglcontext_p.h @@ -132,7 +132,6 @@ class Q_GUI_EXPORT QOpenGLContextGroupPrivate : public QObjectPrivate public: QOpenGLContextGroupPrivate() : m_context(nullptr) - , m_mutex(QMutex::Recursive) , m_refs(0) { } @@ -147,7 +146,7 @@ public: QOpenGLContext *m_context; QList m_shares; - QMutex m_mutex; + QRecursiveMutex m_mutex; QHash m_resources; QAtomicInt m_refs; @@ -186,7 +185,7 @@ public: private: QAtomicInt active; QList m_groups; - QMutex m_mutex; + QRecursiveMutex m_mutex; }; class QPaintEngineEx; diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp index 2a1d207702..97e73f0723 100644 --- a/src/gui/text/qfont.cpp +++ b/src/gui/text/qfont.cpp @@ -208,7 +208,7 @@ QFontPrivate::~QFontPrivate() scFont = 0; } -extern QMutex *qt_fontdatabase_mutex(); +extern QRecursiveMutex *qt_fontdatabase_mutex(); #define QT_FONT_ENGINE_FROM_DATA(data, script) data->engines[script] diff --git a/src/gui/text/qfontdatabase.cpp b/src/gui/text/qfontdatabase.cpp index 562ee3e2b1..ce6bb0c347 100644 --- a/src/gui/text/qfontdatabase.cpp +++ b/src/gui/text/qfontdatabase.cpp @@ -705,7 +705,7 @@ static QStringList familyList(const QFontDef &req) } Q_GLOBAL_STATIC(QFontDatabasePrivate, privateDb) -Q_GLOBAL_STATIC_WITH_ARGS(QMutex, fontDatabaseMutex, (QMutex::Recursive)) +Q_GLOBAL_STATIC(QRecursiveMutex, fontDatabaseMutex) // used in qguiapplication.cpp void qt_cleanupFontDatabase() @@ -717,8 +717,8 @@ void qt_cleanupFontDatabase() } } -// used in qfontengine_x11.cpp -QMutex *qt_fontdatabase_mutex() +// used in qfont.cpp +QRecursiveMutex *qt_fontdatabase_mutex() { return fontDatabaseMutex(); } diff --git a/src/gui/util/qdesktopservices.cpp b/src/gui/util/qdesktopservices.cpp index 8c7cf8682c..ee0ff4c6ef 100644 --- a/src/gui/util/qdesktopservices.cpp +++ b/src/gui/util/qdesktopservices.cpp @@ -60,9 +60,9 @@ class QOpenUrlHandlerRegistry : public QObject { Q_OBJECT public: - inline QOpenUrlHandlerRegistry() : mutex(QMutex::Recursive) {} + QOpenUrlHandlerRegistry() = default; - QMutex mutex; + QRecursiveMutex mutex; struct Handler { -- cgit v1.2.3 From 619b92385ead871d1a220bf4bed5a6d2dd09a4fc Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Thu, 27 Jun 2019 19:24:49 +0200 Subject: QtNetwork: port remaining uses of QMutex::Recursive to QRecursiveMutex In one case, added NSDMI to avoid churning the ctor-init-list. Change-Id: I5587d5cb7e393f60ad29cb5186127304d27d1d46 Reviewed-by: Thiago Macieira --- src/network/access/qnetworkaccessbackend.cpp | 4 ++-- src/network/kernel/qhostinfo.cpp | 2 +- src/network/kernel/qhostinfo_p.h | 2 +- src/network/kernel/qnetworkproxy.cpp | 5 ++--- src/network/socket/qsocks5socketengine.cpp | 6 ++---- 5 files changed, 8 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/network/access/qnetworkaccessbackend.cpp b/src/network/access/qnetworkaccessbackend.cpp index 848fc84de7..566e410051 100644 --- a/src/network/access/qnetworkaccessbackend.cpp +++ b/src/network/access/qnetworkaccessbackend.cpp @@ -58,7 +58,7 @@ QT_BEGIN_NAMESPACE class QNetworkAccessBackendFactoryData: public QList { public: - QNetworkAccessBackendFactoryData() : mutex(QMutex::Recursive) + QNetworkAccessBackendFactoryData() { valid.ref(); } @@ -68,7 +68,7 @@ public: valid.deref(); } - QMutex mutex; + QRecursiveMutex mutex; //this is used to avoid (re)constructing factory data from destructors of other global classes static QBasicAtomicInt valid; }; diff --git a/src/network/kernel/qhostinfo.cpp b/src/network/kernel/qhostinfo.cpp index 9374728244..25ff873307 100644 --- a/src/network/kernel/qhostinfo.cpp +++ b/src/network/kernel/qhostinfo.cpp @@ -891,7 +891,7 @@ void QHostInfoRunnable::run() // thread goes back to QThreadPool } -QHostInfoLookupManager::QHostInfoLookupManager() : mutex(QMutex::Recursive), wasDeleted(false) +QHostInfoLookupManager::QHostInfoLookupManager() : wasDeleted(false) { moveToThread(QCoreApplicationPrivate::mainThread()); #if QT_CONFIG(thread) diff --git a/src/network/kernel/qhostinfo_p.h b/src/network/kernel/qhostinfo_p.h index 3c0ee2a0d8..7df3f5414c 100644 --- a/src/network/kernel/qhostinfo_p.h +++ b/src/network/kernel/qhostinfo_p.h @@ -252,7 +252,7 @@ protected: #if QT_CONFIG(thread) QThreadPool threadPool; #endif - QMutex mutex; + QRecursiveMutex mutex; bool wasDeleted; diff --git a/src/network/kernel/qnetworkproxy.cpp b/src/network/kernel/qnetworkproxy.cpp index 2f840e9e13..110550c423 100644 --- a/src/network/kernel/qnetworkproxy.cpp +++ b/src/network/kernel/qnetworkproxy.cpp @@ -254,8 +254,7 @@ class QGlobalNetworkProxy { public: QGlobalNetworkProxy() - : mutex(QMutex::Recursive) - , applicationLevelProxy(0) + : applicationLevelProxy(0) , applicationLevelProxyFactory(0) #if QT_CONFIG(socks5) , socks5SocketEngineHandler(0) @@ -338,7 +337,7 @@ public: QList proxyForQuery(const QNetworkProxyQuery &query); private: - QMutex mutex; + QRecursiveMutex mutex; QNetworkProxy *applicationLevelProxy; QNetworkProxyFactory *applicationLevelProxyFactory; #if QT_CONFIG(socks5) diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp index 0457d77111..8a030601dc 100644 --- a/src/network/socket/qsocks5socketengine.cpp +++ b/src/network/socket/qsocks5socketengine.cpp @@ -322,8 +322,8 @@ public: protected: void timerEvent(QTimerEvent * event) override; - QMutex mutex; - int sweepTimerId; + QRecursiveMutex mutex; + int sweepTimerId = -1; //socket descriptor, data, timestamp QHash store; }; @@ -331,8 +331,6 @@ protected: Q_GLOBAL_STATIC(QSocks5BindStore, socks5BindStore) QSocks5BindStore::QSocks5BindStore() - : mutex(QMutex::Recursive) - , sweepTimerId(-1) { QCoreApplication *app = QCoreApplication::instance(); if (app && app->thread() != thread()) -- cgit v1.2.3 From eea99e1e8f3eb67fda35dd3a656fe9b5a9be84f2 Mon Sep 17 00:00:00 2001 From: Andreas Hartmetz Date: Mon, 10 Jun 2019 14:39:04 +0200 Subject: Fontconfig font database: Short-circuit matching by filename If the filename matches, no other matching is necessary. Fontconfig doesn't have a fast path for that, so implement one here. Fontconfig is unlikely to add that fast path, see here: https://gitlab.freedesktop.org/fontconfig/fontconfig/issues/103 With -O1 builds of Qt and KDE stack, 358 fonts installed according to KDE systemsetting, on a Ryzen 1800X, startup time of kwrite decreases as following according to perf stat: msec task-clock: ~480 ms to ~455 ms cycles: ~1.73e9 to ~1.65e9 Change-Id: I630a80e4bed2647d5bbd95247005aab7d0cb0363 Reviewed-by: Allan Sandfeld Jensen --- .../fontconfig/qfontconfigdatabase.cpp | 72 +++++++++++++++------- 1 file changed, 50 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp b/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp index 7abf295782..e28b40c240 100644 --- a/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp +++ b/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp @@ -927,38 +927,66 @@ void QFontconfigDatabase::setupFontEngine(QFontEngineFT *engine, const QFontDef antialias = antialiasingEnabled - 1; } - QFontEngine::GlyphFormat format; - // try and get the pattern + // try to find a match for fid + const QFontEngine::FaceId fid = engine->faceId(); FcPattern *pattern = FcPatternCreate(); + FcPattern *match = nullptr; + + // try a trivial match by filename - FC_FILE is highest priority, so if it matches, FcFontMatch + // will just find the file (fine) and spend a millisecond or so doing unnecessary work (bad). + if (!fid.filename.isEmpty() && QFile::exists(QString::fromUtf8(fid.filename))) { + FcBlanks *blanks = FcConfigGetBlanks(nullptr); + int count = 0; + FcPattern *fileMatch = FcFreeTypeQuery((const FcChar8 *)fid.filename.data(), fid.index, + blanks, &count); + if (fileMatch) { + // Apply Fontconfig configuration - FcFreeTypeQuery only returns information stored in + // the font file, we also want to respect system and user settings. + FcConfigSubstitute(0, pattern, FcMatchPattern); + FcDefaultSubstitute(pattern); + match = FcFontRenderPrepare(0, pattern, fileMatch); + FcPatternDestroy(fileMatch); + } + } - FcValue value; - value.type = FcTypeString; - QByteArray cs = fontDef.family.toUtf8(); - value.u.s = (const FcChar8 *)cs.data(); - FcPatternAdd(pattern,FC_FAMILY,value,true); + if (!match) { + FcValue value; - QFontEngine::FaceId fid = engine->faceId(); + // Fontconfig rules might process this information for arbitrary purposes, so add it, + // even though we already know that it doesn't match an existing file. + if (!fid.filename.isEmpty()) { + value.type = FcTypeString; + value.u.s = (const FcChar8 *)fid.filename.data(); + FcPatternAdd(pattern, FC_FILE, value, true); - if (!fid.filename.isEmpty()) { - value.u.s = (const FcChar8 *)fid.filename.data(); - FcPatternAdd(pattern,FC_FILE,value,true); + value.type = FcTypeInteger; + value.u.i = fid.index; + FcPatternAdd(pattern, FC_INDEX, value, true); + } - value.type = FcTypeInteger; - value.u.i = fid.index; - FcPatternAdd(pattern,FC_INDEX,value,true); - } + const QByteArray cs = fontDef.family.toUtf8(); + value.type = FcTypeString; + value.u.s = (const FcChar8 *)cs.data(); + FcPatternAdd(pattern, FC_FAMILY, value, true); - if (fontDef.pixelSize > 0.1) - FcPatternAddDouble(pattern, FC_PIXEL_SIZE, fontDef.pixelSize); + if (fontDef.pixelSize > 0.1) { + value.type = FcTypeDouble; + value.u.d = fontDef.pixelSize; + FcPatternAdd(pattern, FC_PIXEL_SIZE, value, true); + } - FcResult result; + FcResult result; - FcConfigSubstitute(0, pattern, FcMatchPattern); - FcDefaultSubstitute(pattern); + FcConfigSubstitute(0, pattern, FcMatchPattern); + FcDefaultSubstitute(pattern); - FcPattern *match = FcFontMatch(0, pattern, &result); + match = FcFontMatch(0, pattern, &result); + } + + QFontEngine::GlyphFormat format; if (match) { - engine->setDefaultHintStyle(defaultHintStyleFromMatch((QFont::HintingPreference)fontDef.hintingPreference, match, useXftConf)); + engine->setDefaultHintStyle(defaultHintStyleFromMatch( + (QFont::HintingPreference)fontDef.hintingPreference, match, useXftConf)); FcBool fc_autohint; if (FcPatternGetBool(match, FC_AUTOHINT,0, &fc_autohint) == FcResultMatch) -- cgit v1.2.3 From 3a1f9dec7cdc0f6504eed8157f6fcb0a9638c109 Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Mon, 1 Jul 2019 20:55:46 +0200 Subject: Q_ARRAY_LITERAL: protect the check for literal types Some compilers (hello, MSVC) do not produce literal types in Qt because their constexpr support has been blacklisted. Therefore, amend the check for literal types in Q_ARRAY_LITERAL: only do the check if the compiler supports constexpr. Change-Id: I7cffe00dde447d975aa6a7d02248df9c351508ff Reviewed-by: Marc Mutz --- src/corelib/tools/qarraydata.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/corelib/tools/qarraydata.h b/src/corelib/tools/qarraydata.h index 074072b987..dcd95924c1 100644 --- a/src/corelib/tools/qarraydata.h +++ b/src/corelib/tools/qarraydata.h @@ -323,8 +323,14 @@ struct QArrayDataPointerRef }()) \ /**/ +#ifdef Q_COMPILER_CONSTEXPR +#define Q_ARRAY_LITERAL_CHECK_LITERAL_TYPE(Type) Q_STATIC_ASSERT(std::is_literal_type::value) +#else +#define Q_ARRAY_LITERAL_CHECK_LITERAL_TYPE(Type) do {} while (0) +#endif + #define Q_ARRAY_LITERAL_IMPL(Type, ...) \ - Q_STATIC_ASSERT(std::is_literal_type::value); \ + Q_ARRAY_LITERAL_CHECK_LITERAL_TYPE(Type); \ \ /* Portable compile-time array size computation */ \ Q_CONSTEXPR Type data[] = { __VA_ARGS__ }; Q_UNUSED(data); \ -- cgit v1.2.3 From 777c98ad9f3700ea259ea88faba872d1f974880a Mon Sep 17 00:00:00 2001 From: Kai Uwe Broulik Date: Tue, 11 Jun 2019 09:21:30 +0200 Subject: QDBusConnectionInterface: Add activatableServiceNames This allows to query all names that can be activated on the bus. Change-Id: I8f894bf858eb18b67a074ca666ad3200ed99c373 Reviewed-by: Thiago Macieira --- src/dbus/qdbusconnectioninterface.cpp | 12 ++++++++++++ src/dbus/qdbusconnectioninterface.h | 2 ++ 2 files changed, 14 insertions(+) (limited to 'src') diff --git a/src/dbus/qdbusconnectioninterface.cpp b/src/dbus/qdbusconnectioninterface.cpp index ebb3803489..a2335a1795 100644 --- a/src/dbus/qdbusconnectioninterface.cpp +++ b/src/dbus/qdbusconnectioninterface.cpp @@ -198,6 +198,18 @@ QDBusReply QDBusConnectionInterface::registeredServiceNames() const return internalConstCall(QDBus::AutoDetect, QLatin1String("ListNames")); } +/*! + \property QDBusConnectionInterface::activatableServiceNames + \brief holds the activatable service names + \since 5.14 + + Lists all names that can be activated on the bus. +*/ +QDBusReply QDBusConnectionInterface::activatableServiceNames() const +{ + return internalConstCall(QDBus::AutoDetect, QLatin1String("ListActivatableNames")); +} + /*! Returns \c true if the service name \a serviceName has is currently registered. diff --git a/src/dbus/qdbusconnectioninterface.h b/src/dbus/qdbusconnectioninterface.h index c7b1573722..d19e116c53 100644 --- a/src/dbus/qdbusconnectioninterface.h +++ b/src/dbus/qdbusconnectioninterface.h @@ -68,6 +68,7 @@ class Q_DBUS_EXPORT QDBusConnectionInterface: public QDBusAbstractInterface ~QDBusConnectionInterface(); Q_PROPERTY(QDBusReply registeredServiceNames READ registeredServiceNames) + Q_PROPERTY(QDBusReply activatableServiceNames READ activatableServiceNames) public: enum ServiceQueueOptions { @@ -90,6 +91,7 @@ public: public Q_SLOTS: QDBusReply registeredServiceNames() const; + QDBusReply activatableServiceNames() const; QDBusReply isServiceRegistered(const QString &serviceName) const; QDBusReply serviceOwner(const QString &name) const; QDBusReply unregisterService(const QString &serviceName); -- cgit v1.2.3 From 07553d03534e66b13602bf41937e9221020e70d9 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Sat, 6 Jul 2019 13:13:42 +0200 Subject: QTextBrowser: assume Markdown is UTF-8 That's how CommonMark specifies it. The HTML codec-guessing algorithm was making it fall back to Latin1 in practice, which was screwing up any Unicode characters found in the markdown source. Change-Id: I4021adc4a68591ecfd56ef24971af53ce3e9c96d Reviewed-by: Gatis Paeglis --- src/widgets/widgets/qtextbrowser.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/widgets/widgets/qtextbrowser.cpp b/src/widgets/widgets/qtextbrowser.cpp index 2f992b1cff..7a77f86de2 100644 --- a/src/widgets/widgets/qtextbrowser.cpp +++ b/src/widgets/widgets/qtextbrowser.cpp @@ -312,13 +312,17 @@ void QTextBrowserPrivate::setSource(const QUrl &url, QTextDocument::ResourceType if (data.type() == QVariant::String) { txt = data.toString(); } else if (data.type() == QVariant::ByteArray) { + if (type == QTextDocument::HtmlResource) { #if QT_CONFIG(textcodec) - QByteArray ba = data.toByteArray(); - QTextCodec *codec = Qt::codecForHtml(ba); - txt = codec->toUnicode(ba); + QByteArray ba = data.toByteArray(); + QTextCodec *codec = Qt::codecForHtml(ba); + txt = codec->toUnicode(ba); #else - txt = data.toString(); + txt = data.toString(); #endif + } else { + txt = QString::fromUtf8(data.toByteArray()); + } } if (Q_UNLIKELY(txt.isEmpty())) qWarning("QTextBrowser: No document for %s", url.toString().toLatin1().constData()); -- cgit v1.2.3 From c39910993eaf6a7d68fbbe366d990f3f972533ca Mon Sep 17 00:00:00 2001 From: Tasuku Suzuki Date: Mon, 8 Jul 2019 10:21:46 +0900 Subject: Fix build without features.textmarkdownwriter Change-Id: I30b39bb6ac4c24281c7fc87573936f62512ce015 Reviewed-by: Shawn Rutledge --- src/widgets/widgets/qwidgettextcontrol.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/widgets/widgets/qwidgettextcontrol.cpp b/src/widgets/widgets/qwidgettextcontrol.cpp index ccf02da219..70e1c148a1 100644 --- a/src/widgets/widgets/qwidgettextcontrol.cpp +++ b/src/widgets/widgets/qwidgettextcontrol.cpp @@ -3175,7 +3175,7 @@ QString QWidgetTextControl::toHtml() const } #endif -#ifndef QT_NO_TEXTHTMLPARSER +#if QT_CONFIG(textmarkdownwriter) QString QWidgetTextControl::toMarkdown(QTextDocument::MarkdownFeatures features) const { return document()->toMarkdown(features); -- cgit v1.2.3