diff options
author | Alexandru Croitor <alexandru.croitor@qt.io> | 2019-07-11 17:17:13 +0200 |
---|---|---|
committer | Alexandru Croitor <alexandru.croitor@qt.io> | 2019-07-11 17:17:51 +0200 |
commit | 4dac45c9ee59ff6586d90d423654da91523ab679 (patch) | |
tree | cd4a4adf2cbc9e77bf86d2d11e71ec66afdf3be4 | |
parent | 078cd61751aeaa310d35a3d596a21a36004a1a0f (diff) | |
parent | f44850b5c3464cdda0ee9b1ee858d95f3ffaa3e2 (diff) |
Merge remote-tracking branch 'origin/wip/qt6' into wip/cmake
Change-Id: I715b1d743d5f11560e7b3fbeb8fd64a5e5ddb277
1121 files changed, 80901 insertions, 12912 deletions
diff --git a/.gitignore b/.gitignore index 596812c715..95a6964dd7 100644 --- a/.gitignore +++ b/.gitignore @@ -58,8 +58,6 @@ qt*-config.pri /src/angle/src/QtANGLE/libANGLE/ /src/angle/src/libGLESv2/libANGLE/ -/src/corelib/global/qfloat16tables.cpp - # FIXME: Ignore these again? # /examples/*/*/* # !/examples/*/*/*[.]* diff --git a/.qmake.conf b/.qmake.conf index 1d049940d8..de7af300dc 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -1,6 +1,8 @@ load(qt_build_config) CONFIG += warning_clean +DEFINES += QT_NO_JAVA_STYLE_ITERATORS + QT_SOURCE_TREE = $$PWD QT_BUILD_TREE = $$shadowed($$PWD) diff --git a/configure.json b/configure.json index 069eb07ec7..33e429338b 100644 --- a/configure.json +++ b/configure.json @@ -82,7 +82,7 @@ "force-pkg-config": { "type": "void", "name": "pkg-config" }, "framework": "boolean", "gc-binaries": { "type": "boolean", "name": "gc_binaries" }, - "gdb-index": { "type": "boolean", "name": "gdb_index" }, + "gdb-index": { "type": "boolean", "name": "enable_gdb_index" }, "gcc-sysroot": "boolean", "gcov": "boolean", "gnumake": { "type": "boolean", "name": "GNUmake" }, @@ -705,6 +705,7 @@ }, "optimize_debug": { "label": "Optimize debug build", + "autoDetect": "!features.developer-build", "condition": "!config.msvc && !config.clang && (features.debug || features.debug_and_release) && tests.optimize_debug", "output": [ "privateConfig" ] }, diff --git a/dependencies.yaml b/dependencies.yaml new file mode 100644 index 0000000000..32cf5dda7e --- /dev/null +++ b/dependencies.yaml @@ -0,0 +1 @@ +dependencies: [] diff --git a/dist/changes-5.12.4 b/dist/changes-5.12.4 new file mode 100644 index 0000000000..781e9766b9 --- /dev/null +++ b/dist/changes-5.12.4 @@ -0,0 +1,83 @@ +Qt 5.12.4 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.12.0 through 5.12.3. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + +https://doc.qt.io/qt-5/index.html + +The Qt version 5.12 series is binary compatible with the 5.11.x series. +Applications compiled for 5.11 will continue to run with 5.12. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + +https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Platform Specific Changes * +**************************************************************************** + + - X11 / XCB: + * [QTBUG-45375] QTabletEvent coordinates now come from AbsX/AbsY + valuators in the X11 event, in more precise 32.32 fixed-point format, + scaled to fit the virtual desktop. It's possible to revert to using + the legacy 16.16-format event_x/event_y coordinates as in previous + releases by setting the QT_XCB_TABLET_LEGACY_COORDINATES environment + variable. + +**************************************************************************** +* QtCore * +**************************************************************************** + + - Global: + * [QTBUG-72073] Added the QT_NO_FLOAT16_OPERATORS macro in order to work + around a Microsoft <= VS2017 compiler bug that is exposed when using + std::bitset along with any Qt header that includes <qfloat16.h>. + + - QDeadlineTimer: + * [QTBUG-69750] Fixed integer overflows leading to immediate timeouts. + + - QFile: + * Made QFile::copy() issue a filesystem-synchronization system call, + which would make it less likely to result in incomplete or corrupt + files if the system reboots or uncleanly shuts down soon after the + function returns. New code is advised to use QSaveFile instead, which + also allows to display a progress report while copying. + + - QFileInfo: + * [QTBUG-63970][QTBUG-30401][QTBUG-20791] Fixed resolving of symbolic + links to UNC shares on NTFS file systems. + + - QMetaObject: + * Non-copyable lambdas can now be used with invokeMethod(). For + consistency reasons, the functor object is now always moved. + +**************************************************************************** +* QtGui * +**************************************************************************** + + - [QTBUG-73231] QWindow::mapToGlobal()/mapFromGlobal() now handle windows + spanning screens correctly. + + - [QTBUG-75522] QBezier: Fix possible endless loop when stroking curves + +**************************************************************************** +* QtWidgets * +**************************************************************************** + + - QMenu: + * Shortcuts are again shown by default in context menus, except on + macOS. They can be forced off by setting + AA_DontShowShortcutsInContextMenus to true. + +**************************************************************************** +* Third-Party Code * +**************************************************************************** + + - libpng was updated to version 1.6.37 + - Updated QLocale to CLDR v35.1 + - Updated bundled SQLite to version 3.28.0 diff --git a/dist/changes-5.13.0 b/dist/changes-5.13.0 index c6db991931..2891739fa4 100644 --- a/dist/changes-5.13.0 +++ b/dist/changes-5.13.0 @@ -1,3 +1,20 @@ +Qt 5.13 introduces many new features and improvements as well as bugfixes +over the 5.12.x series. For more details, refer to the online documentation +included in this distribution. The documentation is also available online: + +https://doc.qt.io/qt-5/index.html + +The Qt version 5.13 series is binary compatible with the 5.12.x series. +Applications compiled for 5.12 will continue to run with 5.13. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + +https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + **************************************************************************** * Important Source Incompatible Changes * **************************************************************************** @@ -5,3 +22,287 @@ - Moc generated files include the standard <memory> header file now. A side effect of this is that code including the Moc output from within a namespace will fail to compile. + +**************************************************************************** +* Important Behavior Changes * +**************************************************************************** + + - The Qt resource system now supports compressing content using the + Zstandard (https://zstd.net) algorithm. Compared to zlib, it compresses + better for the same CPU time, so this algorithm is the default. + QResource::isCompressed() returns true for either compression algorithm. + Use QResource::compressionAlgorithm() to find out which algorithm to + decompress. QFile will automatically decompress using the correct + algorithm. + + - QDataStream version bumped up to 19 to account for changes in the + serialization of QFont. + + - [QTBUG-73048] Qt now enables by default warnings when using APIs + marked as deprecated. It is possible to disable such warnings by + defining the QT_NO_DEPRECATED_WARNINGS macro. The old + QT_DEPRECATED_WARNINGS macro which was used to enable this warning + now has no effect (warnings are automatically enabled). + +**************************************************************************** +* Third-Party Code * +**************************************************************************** + + - Updated bundled SQLite to version 3.28.0. + - [QTBUG-72623] Updated DNS public suffix list + - [QTBUG-73883] libjpeg-turbo was updated to version 2.0.2 + - Bundled libpng was updated to version 1.6.37 + +**************************************************************************** +* Platform-Specific Changes * +**************************************************************************** + + - X11 / XCB: + * [QTBUG-45375] QTabletEvent coordinates now come from AbsX/AbsY + valuators in the X11 event, in more precise 32.32 fixed-point format, + scaled to fit the virtual desktop. It's possible to revert to using + the legacy 16.16-format event_x/event_y coordinates as in previous + releases by setting the QT_XCB_TABLET_LEGACY_COORDINATES environment + variable. + * [plugins] platforminputcontexts now use libxkbcommon-compose APIs for + compose key input, instead of Qt's own implementation. + + - Android + * Increased the minimum supported Android version to Android 5.0 + (API level 21). + + - Windows + * [QTBUG-47247][QTBUG-71855] Add a default setting for + hasBorderInFullScreen in QtPlatformHeaders, QWindowsWindowFunctions + * [QTBUG-57180] Removed confusing DirectWrite warning when loading + bitmap fonts. + + - macOS + * Accessibility: VoiceOver now reads all lines in multiline text + components when navigating by lines. + +**************************************************************************** +* Tools * +**************************************************************************** + + - configure & build system: + * Added --linker=[bfg,lld,gold] configure flag. + * The default OpenGL configuration changed from ANGLE to dynamic OpenGL. + * Added -schannel option to enable Schannel support on Desktop Windows. + * Added -coverage option for recording coverage data during execution + + - CMake + * Added support for automatic linking of transitive dependencies in + static builds + + - qmake + * [QTBUG-53654] Introduced the variables WINDOWS_TARGET_PLATFORM_VERSION + and WINDOWS_TARGET_PLATFORM_MIN_VERSION for overriding the default + values of WindowsTargetPlatformVersion and + WindowsTargetPlatformMinVersion in Visual Studio project files. + + - RCC now supports compressing content using the Zstandard + (https://zstd.net) algorithm. Compared to zlib, it compresses better for + the same CPU time, so this algorithm is the default. To go back to the + previous algorithm, pass command-line option --compress-algo=zlib. + Compression levels range from 1 (fastest, least compression) to 19 + (slowest, best compression). Level 0 tells the library to choose an + implementation-defined default. The default compression level is + "heuristic" (level -1): under this mode, RCC will attempt a very fast + compression (level 1) and check if the file was sufficiently compressed. + If it was, then RCC will compress again using an implementation-defined + level. + +**************************************************************************** +* QtCore * +**************************************************************************** + + - New class QConcatenateTablesProxyModel, to concatenate the rows from + multiple source models. + - New class QTransposeProxyModel to swap rows and columns of the source + model. + - Added macros Q_DISABLE_MOVE and Q_DISABLE_COPY_MOVE complementing + Q_DISABLE_COPY. + - Qt Containers and meta type system now use C++11 type traits + (std::is_trivial, std::is_trivially_copyable and + std::is_trivially_destructible) to detect the class of a type not + explicitly set by Q_DECLARE_TYPEINFO. (Q_DECLARE_TYPEINFO is still + needed for QList.) + + - QCommandLineParser: + * [QTBUG-58490] The application name (if set in QCoreApplication) is now + shown in error messages. + + - QDateTime: + * [QTBUG-71030] Fixed race conditions in getting the local time zone. Qt now + has a mutex lock around all calls to tzset() and mktime(). Application + code should avoid calling those functions directly. + + - QFile: + * Made QFile::copy() issue a filesystem-synchronization system call, + which would make it less likely to result in incomplete or corrupt + files if the system reboots or uncleanly shuts down soon after the + function returns. New code is advised to use QSaveFile instead, which + also allows display of a progress report while copying. + + - QFileInfo: + * [QTBUG-63970][QTBUG-30401][QTBUG-20791] Fixed resolving of symbolic + links to UNC shares on NTFS file systems. + + - QJsonArray: + * [QTBUG-32793] Added cbegin() and cend() methods for compatibility with + the Standard Library container concepts. + + - QJsonDocument: + * [QTBUG-71445] Added the ability to stream QJsonDocument into and from + QDataStream if it is contained in a QVariant. + + - QLocale: + * Added support for Western Balochi, Ido, Lojban, Sicilian and + Southern Kurdish. + * [QTBUG-71445] Added a const overload for formattedDataSize(). + * [QTBUG-782] Added toLong() and toULong(). + + - QMetaObject: + * Non-copyable lambdas can now be used with invokeMethod(). For + consistency reasons, the functor object is now always moved. + + - QMimeDatabase: + * Added configure option -no-mimetype-database that tells Qt not to + bundle its own copy of the XDG MIME database. If this option is + passed, QMimeDatabase will only work if there's a system copy in + $XDG_DATA_DIRS/mime. This option is useful for Linux distributions + that ensure the data is always present. + + - QSettings: + * Added QSettings(Scope...) constructor to avoid using #ifdef in Qt + applications. + + - QStringListModel: + * [QTBUG-69807] Implemented moveRows + + - QUrlQuery: + * QUrlQuery now provides an initializer list constructor. It can be + created using a list of key/value pairs. + + - QVector: + * QVector no longer requires a default constructor for its template + argument. + +**************************************************************************** +* QtDBus * +**************************************************************************** + + - Matching namespace prefixes using QDBusServiceWatcher (instead of full + service names) is now possible. For example, "com.example.backend1*" + will match "com.example.backend1", "com.example.backend1.foo" and + "com.example.backend1.foo.bar", but not "com.example.backend12". + +**************************************************************************** +* QtGui * +**************************************************************************** + + - CSS: + * Added support for HSL/HSLA colors. + * Fix the range of the hue parameter when parsing a color given in HSL + or HSV. + + - QFont: + * Add setFamilies()/families() to support use of font families with + commas and quotes in their name. + + - QImage: + * [QTBUG-41176] Added support for 16-bit grayscale format. + * A new method convertTo has been added to change the format of a QImage + in place. + + - QPainterPath: + * Added clear(), reserve(), capacity(). clear() removes allocated + QPainterPath elements but preserves allocated memory, which can be + useful for application with complex paths that are often recreated. + reserve() and capacity() follow QVector semantics. + + - QWindow: + * [QTBUG-67903][QTBUG-52944] QWindow::transientParent is now a property. + + - Text: + * [QTBUG-60813] Deprecated QFont::lastResortFamily() and + QFont::lastResortFont() which are not in use in Qt 5 and did not + provide any useful information. + * [QTBUG-22813] Added support for setting the font's style name in + QTextCharFormat. + +**************************************************************************** +* QtNetwork * +**************************************************************************** + + - TLS (SSL): + * Support for SSLv2 and SSLv3 sockets has been dropped, as per RFC 6176 + (2011) and RFC 7568 (2015). + * The Schannel backend now supports ALPN and thus HTTP/2. + * [QTBUG-62637] Added support for Schannel on Desktop Windows. To build + Qt with Schannel support use '-schannel' during configure. + * Added class QOcspResponse as a part of OCSP stapling support. + * [QTBUG-12812][QTBUG-17158] Added OCSP-stapling support for OpenSSL + backend + * [QTBUG-71828][QTBUG-73289] Fixed SSL verification when connecting to IP + address (no host name) if that IP address is listed in the certificate. + +**************************************************************************** +* QtTestLib * +**************************************************************************** + + - Added QTest::toString(const QModelIndex &idx) + + - Removed support for GPU_BLACKLIST files. Reimplementations or + equivalents of QTEST_MAIN() should remove their uses of + QTEST_ADD_GPU_BLACKLIST_SUPPORT and its _DEFS; they are still vacuously + defined, but serve no remaining purpose and shall be undefined in due + course. + +**************************************************************************** +* QtWidgets * +**************************************************************************** + + - QAbstractScrollArea: + * [QTBUG-69120] QTableView/QTreeView, when reporting their + viewportSizeHint(), now correctly take into account their scroll + bars' visibility and visibilityPolicy. + + - QHeaderView: + * dataChanged now respects the given roles to avoid useless + recomputations. + + - QLabel: + * Minor performance improvement when painting a label with an image. + + - QListWidget: + * [QTBUG-69807] Implemented moveRows in model + * [QTBUG-68977] Sped up handling of hidden items when working with large + data sets. + + - QPlainTextEdit: + * Added QRegularExpression find() method overload. + + - QTextEdit: + * Added QRegularExpression find() method overload. + + - QTreeView: + * Added expandRecursively() to expand all items below a given index. + + - QWidgetTextControlPrivate: + * [QTBUG-69735] Moved cursor selection to start. + +**************************************************************************** +* QtSql * +**************************************************************************** + + - SQLite: + * [QTBUG-63498] The json1-extension for the SQLite3 plugin is now enabled + by default. + + - PostgreSQL: + * QSqlDatabase is now stricter about table names when used with record() + and primaryIndex(). If the tablename was not quoted when it was + created, then the table name passed to record() and primaryIndex() + needs to be in lower case so that PostgreSQL is able to find it. diff --git a/doc/config/exampleurl-qt3d.qdocconf b/doc/config/exampleurl-qt3d.qdocconf new file mode 100644 index 0000000000..1a14151416 --- /dev/null +++ b/doc/config/exampleurl-qt3d.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qt3d.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtactiveqt.qdocconf b/doc/config/exampleurl-qtactiveqt.qdocconf new file mode 100644 index 0000000000..f1abbd165e --- /dev/null +++ b/doc/config/exampleurl-qtactiveqt.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtactiveqt.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtandroidextras.qdocconf b/doc/config/exampleurl-qtandroidextras.qdocconf new file mode 100644 index 0000000000..44984a5693 --- /dev/null +++ b/doc/config/exampleurl-qtandroidextras.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtandroidextras.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtbase.qdocconf b/doc/config/exampleurl-qtbase.qdocconf new file mode 100644 index 0000000000..b29df464ba --- /dev/null +++ b/doc/config/exampleurl-qtbase.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtbase.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtcharts.qdocconf b/doc/config/exampleurl-qtcharts.qdocconf new file mode 100644 index 0000000000..2e49a7d1d8 --- /dev/null +++ b/doc/config/exampleurl-qtcharts.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtcharts.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtconnectivity.qdocconf b/doc/config/exampleurl-qtconnectivity.qdocconf new file mode 100644 index 0000000000..42923eb673 --- /dev/null +++ b/doc/config/exampleurl-qtconnectivity.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtconnectivity.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtdatavis3d.qdocconf b/doc/config/exampleurl-qtdatavis3d.qdocconf new file mode 100644 index 0000000000..66f129a773 --- /dev/null +++ b/doc/config/exampleurl-qtdatavis3d.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtdatavis3d.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtdeclarative.qdocconf b/doc/config/exampleurl-qtdeclarative.qdocconf new file mode 100644 index 0000000000..4b1d751b60 --- /dev/null +++ b/doc/config/exampleurl-qtdeclarative.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtdeclarative.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtdoc.qdocconf b/doc/config/exampleurl-qtdoc.qdocconf new file mode 100644 index 0000000000..947a575772 --- /dev/null +++ b/doc/config/exampleurl-qtdoc.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtdoc.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtgamepad.qdocconf b/doc/config/exampleurl-qtgamepad.qdocconf new file mode 100644 index 0000000000..d55de4e3d9 --- /dev/null +++ b/doc/config/exampleurl-qtgamepad.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtgamepad.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtgraphicaleffects.qdocconf b/doc/config/exampleurl-qtgraphicaleffects.qdocconf new file mode 100644 index 0000000000..4d25ef4c94 --- /dev/null +++ b/doc/config/exampleurl-qtgraphicaleffects.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtgraphicaleffects.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtimageformats.qdocconf b/doc/config/exampleurl-qtimageformats.qdocconf new file mode 100644 index 0000000000..1070beaf12 --- /dev/null +++ b/doc/config/exampleurl-qtimageformats.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtimageformats.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtlocation.qdocconf b/doc/config/exampleurl-qtlocation.qdocconf new file mode 100644 index 0000000000..93d47fca13 --- /dev/null +++ b/doc/config/exampleurl-qtlocation.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtlocation.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtlottie.qdocconf b/doc/config/exampleurl-qtlottie.qdocconf new file mode 100644 index 0000000000..f60714027a --- /dev/null +++ b/doc/config/exampleurl-qtlottie.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtlottie.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtmacextras.qdocconf b/doc/config/exampleurl-qtmacextras.qdocconf new file mode 100644 index 0000000000..1e2d515e05 --- /dev/null +++ b/doc/config/exampleurl-qtmacextras.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtmacextras.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtmultimedia.qdocconf b/doc/config/exampleurl-qtmultimedia.qdocconf new file mode 100644 index 0000000000..b710693d31 --- /dev/null +++ b/doc/config/exampleurl-qtmultimedia.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtmultimedia.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtnetworkauth.qdocconf b/doc/config/exampleurl-qtnetworkauth.qdocconf new file mode 100644 index 0000000000..e71da43bb1 --- /dev/null +++ b/doc/config/exampleurl-qtnetworkauth.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtnetworkauth.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtpurchasing.qdocconf b/doc/config/exampleurl-qtpurchasing.qdocconf new file mode 100644 index 0000000000..fe232c02fe --- /dev/null +++ b/doc/config/exampleurl-qtpurchasing.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtpurchasing.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtquickcontrols.qdocconf b/doc/config/exampleurl-qtquickcontrols.qdocconf new file mode 100644 index 0000000000..e9cd8c49f3 --- /dev/null +++ b/doc/config/exampleurl-qtquickcontrols.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtquickcontrols.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtquickcontrols2.qdocconf b/doc/config/exampleurl-qtquickcontrols2.qdocconf new file mode 100644 index 0000000000..3e2749ec65 --- /dev/null +++ b/doc/config/exampleurl-qtquickcontrols2.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtquickcontrols2.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtremoteobjects.qdocconf b/doc/config/exampleurl-qtremoteobjects.qdocconf new file mode 100644 index 0000000000..40c4c68bff --- /dev/null +++ b/doc/config/exampleurl-qtremoteobjects.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtremoteobjects.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtscript.qdocconf b/doc/config/exampleurl-qtscript.qdocconf new file mode 100644 index 0000000000..b7b5324481 --- /dev/null +++ b/doc/config/exampleurl-qtscript.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtscript.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtscxml.qdocconf b/doc/config/exampleurl-qtscxml.qdocconf new file mode 100644 index 0000000000..2383f823ea --- /dev/null +++ b/doc/config/exampleurl-qtscxml.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtscxml.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtsensors.qdocconf b/doc/config/exampleurl-qtsensors.qdocconf new file mode 100644 index 0000000000..291a0e8085 --- /dev/null +++ b/doc/config/exampleurl-qtsensors.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtsensors.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtserialbus.qdocconf b/doc/config/exampleurl-qtserialbus.qdocconf new file mode 100644 index 0000000000..b263b51038 --- /dev/null +++ b/doc/config/exampleurl-qtserialbus.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtserialbus.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtserialport.qdocconf b/doc/config/exampleurl-qtserialport.qdocconf new file mode 100644 index 0000000000..6b1e66b1d3 --- /dev/null +++ b/doc/config/exampleurl-qtserialport.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtserialport.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtspeech.qdocconf b/doc/config/exampleurl-qtspeech.qdocconf new file mode 100644 index 0000000000..0cb3b209bd --- /dev/null +++ b/doc/config/exampleurl-qtspeech.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtspeech.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtsvg.qdocconf b/doc/config/exampleurl-qtsvg.qdocconf new file mode 100644 index 0000000000..2514710f11 --- /dev/null +++ b/doc/config/exampleurl-qtsvg.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtsvg.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qttools.qdocconf b/doc/config/exampleurl-qttools.qdocconf new file mode 100644 index 0000000000..12a79f303d --- /dev/null +++ b/doc/config/exampleurl-qttools.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qttools.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtvirtualkeyboard.qdocconf b/doc/config/exampleurl-qtvirtualkeyboard.qdocconf new file mode 100644 index 0000000000..f9080346b3 --- /dev/null +++ b/doc/config/exampleurl-qtvirtualkeyboard.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtvirtualkeyboard.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtwayland.qdocconf b/doc/config/exampleurl-qtwayland.qdocconf new file mode 100644 index 0000000000..65417434ce --- /dev/null +++ b/doc/config/exampleurl-qtwayland.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtwayland.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtwebchannel.qdocconf b/doc/config/exampleurl-qtwebchannel.qdocconf new file mode 100644 index 0000000000..e9926b29e6 --- /dev/null +++ b/doc/config/exampleurl-qtwebchannel.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtwebchannel.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtwebengine.qdocconf b/doc/config/exampleurl-qtwebengine.qdocconf new file mode 100644 index 0000000000..27b28536ac --- /dev/null +++ b/doc/config/exampleurl-qtwebengine.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtwebengine.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtwebsockets.qdocconf b/doc/config/exampleurl-qtwebsockets.qdocconf new file mode 100644 index 0000000000..814f97b3e1 --- /dev/null +++ b/doc/config/exampleurl-qtwebsockets.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtwebsockets.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtwebview.qdocconf b/doc/config/exampleurl-qtwebview.qdocconf new file mode 100644 index 0000000000..38a5735285 --- /dev/null +++ b/doc/config/exampleurl-qtwebview.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtwebview.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtwinextras.qdocconf b/doc/config/exampleurl-qtwinextras.qdocconf new file mode 100644 index 0000000000..daf3a71fee --- /dev/null +++ b/doc/config/exampleurl-qtwinextras.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtwinextras.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtx11extras.qdocconf b/doc/config/exampleurl-qtx11extras.qdocconf new file mode 100644 index 0000000000..046691d334 --- /dev/null +++ b/doc/config/exampleurl-qtx11extras.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtx11extras.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/config/exampleurl-qtxmlpatterns.qdocconf b/doc/config/exampleurl-qtxmlpatterns.qdocconf new file mode 100644 index 0000000000..b28a32767a --- /dev/null +++ b/doc/config/exampleurl-qtxmlpatterns.qdocconf @@ -0,0 +1 @@ +url.examples = "https://code.qt.io/cgit/qt/qtxmlpatterns.git/tree/examples/\1?h=$QT_VER" diff --git a/doc/global/qt-cpp-defines.qdocconf b/doc/global/qt-cpp-defines.qdocconf index a486470187..4cf895b05e 100644 --- a/doc/global/qt-cpp-defines.qdocconf +++ b/doc/global/qt-cpp-defines.qdocconf @@ -118,6 +118,7 @@ Cpp.ignoretokens += \ Q_POSITIONING_EXPORT \ Q_MULTIMEDIA_EXPORT \ Q_NAMESPACE \ + Q_NAMESPACE_EXPORT \ Q_NETWORK_EXPORT \ Q_NEVER_INLINE \ Q_NORETURN \ diff --git a/examples/network/downloadmanager/downloadmanager.cpp b/examples/network/downloadmanager/downloadmanager.cpp index e820b4ff70..9e0c03c6af 100644 --- a/examples/network/downloadmanager/downloadmanager.cpp +++ b/examples/network/downloadmanager/downloadmanager.cpp @@ -132,7 +132,7 @@ void DownloadManager::startNextDownload() // prepare the output printf("Downloading %s...\n", url.toEncoded().constData()); - downloadTime.start(); + downloadTimer.start(); } void DownloadManager::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) @@ -140,7 +140,7 @@ void DownloadManager::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) progressBar.setStatus(bytesReceived, bytesTotal); // calculate the download speed - double speed = bytesReceived * 1000.0 / downloadTime.elapsed(); + double speed = bytesReceived * 1000.0 / downloadTimer.elapsed(); QString unit; if (speed < 1024) { unit = "bytes/sec"; diff --git a/examples/network/downloadmanager/downloadmanager.h b/examples/network/downloadmanager/downloadmanager.h index 4bc6351ff9..818a774f1f 100644 --- a/examples/network/downloadmanager/downloadmanager.h +++ b/examples/network/downloadmanager/downloadmanager.h @@ -83,7 +83,7 @@ private: QQueue<QUrl> downloadQueue; QNetworkReply *currentDownload = nullptr; QFile output; - QTime downloadTime; + QElapsedTimer downloadTimer; TextProgressBar progressBar; int downloadedCount = 0; diff --git a/examples/network/secureudpserver/server.cpp b/examples/network/secureudpserver/server.cpp index 0d83fa9b51..450eb9e68d 100644 --- a/examples/network/secureudpserver/server.cpp +++ b/examples/network/secureudpserver/server.cpp @@ -62,7 +62,7 @@ QString peer_info(const QHostAddress &address, quint16 port) return info.arg(address.toString()).arg(port); } -QString connection_info(QSharedPointer<QDtls> connection) +QString connection_info(QDtls *connection) { QString info(DtlsServer::tr("Session cipher: ")); info += connection->sessionCipher().name(); @@ -157,7 +157,7 @@ void DtlsServer::readyRead() } const auto client = std::find_if(knownClients.begin(), knownClients.end(), - [&](const DtlsConnection &connection){ + [&](const std::unique_ptr<QDtls> &connection){ return connection->peerAddress() == peerAddress && connection->peerPort() == peerPort; }); @@ -170,7 +170,7 @@ void DtlsServer::readyRead() //! [6] if ((*client)->isConnectionEncrypted()) { - decryptDatagram(*client, dgram); + decryptDatagram(client->get(), dgram); if ((*client)->dtlsError() == QDtlsError::RemoteClosedConnectionError) knownClients.erase(client); return; @@ -178,7 +178,7 @@ void DtlsServer::readyRead() //! [6] //! [7] - doHandshake(*client, dgram); + doHandshake(client->get(), dgram); //! [7] } @@ -205,13 +205,13 @@ void DtlsServer::handleNewConnection(const QHostAddress &peerAddress, emit infoMessage(peerInfo + tr(": verified, starting a handshake")); //! [8] //! [9] - DtlsConnection newConnection(new QDtls(QSslSocket::SslServerMode)); + std::unique_ptr<QDtls> newConnection{new QDtls{QSslSocket::SslServerMode}}; newConnection->setDtlsConfiguration(serverConfiguration); newConnection->setPeer(peerAddress, peerPort); - newConnection->connect(newConnection.data(), &QDtls::pskRequired, + newConnection->connect(newConnection.get(), &QDtls::pskRequired, this, &DtlsServer::pskRequired); - knownClients.push_back(newConnection); - doHandshake(newConnection, clientHello); + knownClients.push_back(std::move(newConnection)); + doHandshake(knownClients.back().get(), clientHello); //! [9] } else if (cookieSender.dtlsError() != QDtlsError::NoError) { emit errorMessage(tr("DTLS error: ") + cookieSender.dtlsErrorString()); @@ -221,7 +221,7 @@ void DtlsServer::handleNewConnection(const QHostAddress &peerAddress, } //! [11] -void DtlsServer::doHandshake(DtlsConnection newConnection, const QByteArray &clientHello) +void DtlsServer::doHandshake(QDtls *newConnection, const QByteArray &clientHello) { const bool result = newConnection->doHandshake(&serverSocket, clientHello); if (!result) { @@ -246,7 +246,7 @@ void DtlsServer::doHandshake(DtlsConnection newConnection, const QByteArray &cli //! [11] //! [12] -void DtlsServer::decryptDatagram(DtlsConnection connection, const QByteArray &clientMessage) +void DtlsServer::decryptDatagram(QDtls *connection, const QByteArray &clientMessage) { Q_ASSERT(connection->isConnectionEncrypted()); @@ -266,10 +266,9 @@ void DtlsServer::decryptDatagram(DtlsConnection connection, const QByteArray &cl //! [14] void DtlsServer::shutdown() { - for (DtlsConnection &connection : knownClients) + for (const auto &connection : qExchange(knownClients, {})) connection->shutdown(&serverSocket); - knownClients.clear(); serverSocket.close(); } //! [14] diff --git a/examples/network/secureudpserver/server.h b/examples/network/secureudpserver/server.h index b720368e7b..1af8aef8a2 100644 --- a/examples/network/secureudpserver/server.h +++ b/examples/network/secureudpserver/server.h @@ -54,6 +54,7 @@ #include <QtNetwork> #include <vector> +#include <memory> QT_BEGIN_NAMESPACE @@ -86,9 +87,8 @@ private: void handleNewConnection(const QHostAddress &peerAddress, quint16 peerPort, const QByteArray &clientHello); - using DtlsConnection = QSharedPointer<QDtls>; - void doHandshake(DtlsConnection newConnection, const QByteArray &clientHello); - void decryptDatagram(DtlsConnection connection, const QByteArray &clientMessage); + void doHandshake(QDtls *newConnection, const QByteArray &clientHello); + void decryptDatagram(QDtls *connection, const QByteArray &clientMessage); void shutdown(); bool listening = false; @@ -96,7 +96,7 @@ private: QSslConfiguration serverConfiguration; QDtlsClientVerifier cookieSender; - QVector<DtlsConnection> knownClients; + std::vector<std::unique_ptr<QDtls>> knownClients; Q_DISABLE_COPY(DtlsServer) }; diff --git a/examples/network/torrent/ratecontroller.cpp b/examples/network/torrent/ratecontroller.cpp index 87c65096b6..96474806f5 100644 --- a/examples/network/torrent/ratecontroller.cpp +++ b/examples/network/torrent/ratecontroller.cpp @@ -96,8 +96,8 @@ void RateController::transfer() if (sockets.isEmpty()) return; - int msecs = 1000; - if (!stopWatch.isNull()) + qint64 msecs = 1000; + if (stopWatch.isValid()) msecs = qMin(msecs, stopWatch.elapsed()); qint64 bytesToWrite = (upLimit * msecs) / 1000; diff --git a/examples/network/torrent/ratecontroller.h b/examples/network/torrent/ratecontroller.h index a4aa596ce1..f8bff0cc36 100644 --- a/examples/network/torrent/ratecontroller.h +++ b/examples/network/torrent/ratecontroller.h @@ -53,7 +53,7 @@ #include <QObject> #include <QSet> -#include <QTime> +#include <QElapsedTimer> class PeerWireClient; @@ -79,7 +79,7 @@ public slots: void scheduleTransfer(); private: - QTime stopWatch; + QElapsedTimer stopWatch; QSet<PeerWireClient *> sockets; int upLimit; int downLimit; diff --git a/examples/opengl/qopenglwidget/glwidget.h b/examples/opengl/qopenglwidget/glwidget.h index de7805a907..0ad2581cb8 100644 --- a/examples/opengl/qopenglwidget/glwidget.h +++ b/examples/opengl/qopenglwidget/glwidget.h @@ -56,7 +56,7 @@ #include <QOpenGLBuffer> #include <QVector3D> #include <QMatrix4x4> -#include <QTime> +#include <QElapsedTimer> #include <QVector> #include <QPushButton> @@ -106,7 +106,7 @@ private: bool m_qtLogo; QList<Bubble *> m_bubbles; int m_frames; - QTime m_time; + QElapsedTimer m_time; QOpenGLShader *m_vshader1; QOpenGLShader *m_fshader1; QOpenGLShader *m_vshader2; diff --git a/examples/qtconcurrent/wordcount/main.cpp b/examples/qtconcurrent/wordcount/main.cpp index 32cb4d0e08..ae8542a761 100644 --- a/examples/qtconcurrent/wordcount/main.cpp +++ b/examples/qtconcurrent/wordcount/main.cpp @@ -54,7 +54,7 @@ #include <QString> #include <QStringList> #include <QDir> -#include <QTime> +#include <QElapsedTimer> #include <QApplication> #include <QDebug> @@ -146,19 +146,19 @@ int main(int argc, char** argv) int singleThreadTime = 0; { - QTime time; - time.start(); + QElapsedTimer timer; + timer.start(); WordCount total = singleThreadedWordCount(files); - singleThreadTime = time.elapsed(); + singleThreadTime = timer.elapsed(); qDebug() << "single thread" << singleThreadTime; } int mapReduceTime = 0; { - QTime time; - time.start(); + QElapsedTimer timer; + timer.start(); WordCount total = mappedReduced(files, countWords, reduce); - mapReduceTime = time.elapsed(); + mapReduceTime = timer.elapsed(); qDebug() << "MapReduce" << mapReduceTime; } qDebug() << "MapReduce speedup x" << ((double)singleThreadTime - (double)mapReduceTime) / (double)mapReduceTime + 1; diff --git a/examples/sql/books/initdb.h b/examples/sql/books/initdb.h index 44f74f0c37..773e3fb74c 100644 --- a/examples/sql/books/initdb.h +++ b/examples/sql/books/initdb.h @@ -79,6 +79,32 @@ QVariant addAuthor(QSqlQuery &q, const QString &name, const QDate &birthdate) return q.lastInsertId(); } +const auto BOOKS_SQL = QLatin1String(R"( + create table books(id integer primary key, title varchar, author integer, + genre integer, year integer, rating integer) + )"); + +const auto AUTHORS_SQL = QLatin1String(R"( + create table authors(id integer primary key, name varchar, birthdate date) + )"); + +const auto GENRES_SQL = QLatin1String(R"( + create table genres(id integer primary key, name varchar) + )"); + +const auto INSERT_AUTHOR_SQL = QLatin1String(R"( + insert into authors(name, birthdate) values(?, ?) + )"); + +const auto INSERT_BOOK_SQL = QLatin1String(R"( + insert into books(title, year, author, genre, rating) + values(?, ?, ?, ?, ?) + )"); + +const auto INSERT_GENRE_SQL = QLatin1String(R"( + insert into genres(name) values(?) + )"); + QSqlError initDb() { QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); @@ -93,26 +119,26 @@ QSqlError initDb() return QSqlError(); QSqlQuery q; - if (!q.exec(QLatin1String("create table books(id integer primary key, title varchar, author integer, genre integer, year integer, rating integer)"))) + if (!q.exec(BOOKS_SQL)) return q.lastError(); - if (!q.exec(QLatin1String("create table authors(id integer primary key, name varchar, birthdate date)"))) + if (!q.exec(AUTHORS_SQL)) return q.lastError(); - if (!q.exec(QLatin1String("create table genres(id integer primary key, name varchar)"))) + if (!q.exec(GENRES_SQL)) return q.lastError(); - if (!q.prepare(QLatin1String("insert into authors(name, birthdate) values(?, ?)"))) + if (!q.prepare(INSERT_AUTHOR_SQL)) return q.lastError(); QVariant asimovId = addAuthor(q, QLatin1String("Isaac Asimov"), QDate(1920, 2, 1)); QVariant greeneId = addAuthor(q, QLatin1String("Graham Greene"), QDate(1904, 10, 2)); QVariant pratchettId = addAuthor(q, QLatin1String("Terry Pratchett"), QDate(1948, 4, 28)); - if (!q.prepare(QLatin1String("insert into genres(name) values(?)"))) + if (!q.prepare(INSERT_GENRE_SQL)) return q.lastError(); QVariant sfiction = addGenre(q, QLatin1String("Science Fiction")); QVariant fiction = addGenre(q, QLatin1String("Fiction")); QVariant fantasy = addGenre(q, QLatin1String("Fantasy")); - if (!q.prepare(QLatin1String("insert into books(title, year, author, genre, rating) values(?, ?, ?, ?, ?)"))) + if (!q.prepare(INSERT_BOOK_SQL)) return q.lastError(); addBook(q, QLatin1String("Foundation"), 1951, asimovId, sfiction, 3); addBook(q, QLatin1String("Foundation and Empire"), 1952, asimovId, sfiction, 4); diff --git a/examples/widgets/doc/src/application.qdoc b/examples/widgets/doc/src/application.qdoc index 040aa171b7..6c37fa67bb 100644 --- a/examples/widgets/doc/src/application.qdoc +++ b/examples/widgets/doc/src/application.qdoc @@ -327,9 +327,12 @@ \snippet mainwindows/application/mainwindow.cpp 44 \snippet mainwindows/application/mainwindow.cpp 45 - Saving a file is very similar to loading one. Here, the - QFile::Text flag ensures that on Windows, "\\n" is converted into - "\\r\\n" to conform to the Windows convension. + Saving a file is similar to loading one. We use QSaveFile to ensure + all data are safely written and existing files are not damaged + should writing fail. + We use the QFile::Text flag to make sure that on Windows, "\\n" + is converted into "\\r\\n" to conform to the Windows convention. + \snippet mainwindows/application/mainwindow.cpp 46 \snippet mainwindows/application/mainwindow.cpp 47 diff --git a/examples/widgets/doc/src/classwizard.qdoc b/examples/widgets/doc/src/classwizard.qdoc index 6977cf5efa..7f3693b65e 100644 --- a/examples/widgets/doc/src/classwizard.qdoc +++ b/examples/widgets/doc/src/classwizard.qdoc @@ -30,7 +30,7 @@ \title Class Wizard Example \ingroup examples-dialogs - \brief The License Wizard example shows how to implement linear + \brief The Class Wizard example shows how to implement linear wizards using QWizard. \image classwizard.png Screenshot of the Class Wizard example diff --git a/examples/widgets/doc/src/stardelegate.qdoc b/examples/widgets/doc/src/stardelegate.qdoc index 0b91723a51..b33fa3550f 100644 --- a/examples/widgets/doc/src/stardelegate.qdoc +++ b/examples/widgets/doc/src/stardelegate.qdoc @@ -269,7 +269,7 @@ \codeline \snippet itemviews/stardelegate/main.cpp 4 - Notice the call to qVariantFromValue to convert a \c + Notice the call to QVariant::fromValue to convert a \c StarRating to a QVariant. \section1 Possible Extensions and Suggestions diff --git a/examples/widgets/graphicsview/boxes/scene.cpp b/examples/widgets/graphicsview/boxes/scene.cpp index 7f62ac894b..d51124aed7 100644 --- a/examples/widgets/graphicsview/boxes/scene.cpp +++ b/examples/widgets/graphicsview/boxes/scene.cpp @@ -533,8 +533,6 @@ Scene::Scene(int width, int height, int maxTextureSize) m_timer->setInterval(20); connect(m_timer, &QTimer::timeout, this, [this](){ update(); }); m_timer->start(); - - m_time.start(); } Scene::~Scene() diff --git a/examples/widgets/graphicsview/boxes/scene.h b/examples/widgets/graphicsview/boxes/scene.h index ccb6f368cd..ffff01358f 100644 --- a/examples/widgets/graphicsview/boxes/scene.h +++ b/examples/widgets/graphicsview/boxes/scene.h @@ -220,7 +220,7 @@ private: void initGL(); QPointF pixelPosToViewPos(const QPointF& p); - QTime m_time; + QTime m_time; // ### Qt 6: remove (unused) int m_lastTime; int m_mouseEventTime; int m_distExp; diff --git a/examples/widgets/mainwindows/application/mainwindow.cpp b/examples/widgets/mainwindows/application/mainwindow.cpp index 4b639ead18..7886c4afac 100644 --- a/examples/widgets/mainwindows/application/mainwindow.cpp +++ b/examples/widgets/mainwindows/application/mainwindow.cpp @@ -353,23 +353,27 @@ void MainWindow::loadFile(const QString &fileName) bool MainWindow::saveFile(const QString &fileName) //! [44] //! [45] { - QFile file(fileName); - if (!file.open(QFile::WriteOnly | QFile::Text)) { - QMessageBox::warning(this, tr("Application"), - tr("Cannot write file %1:\n%2.") - .arg(QDir::toNativeSeparators(fileName), - file.errorString())); - return false; - } + QString errorMessage; - QTextStream out(&file); -#ifndef QT_NO_CURSOR QGuiApplication::setOverrideCursor(Qt::WaitCursor); -#endif - out << textEdit->toPlainText(); -#ifndef QT_NO_CURSOR + QSaveFile file(fileName); + if (file.open(QFile::WriteOnly | QFile::Text)) { + QTextStream out(&file); + out << textEdit->toPlainText(); + if (!file.commit()) { + errorMessage = tr("Cannot write file %1:\n%2.") + .arg(QDir::toNativeSeparators(fileName), file.errorString()); + } + } else { + errorMessage = tr("Cannot open file %1 for writing:\n%2.") + .arg(QDir::toNativeSeparators(fileName), file.errorString()); + } QGuiApplication::restoreOverrideCursor(); -#endif + + if (!errorMessage.isEmpty()) { + QMessageBox::warning(this, tr("Application"), errorMessage); + return false; + } setCurrentFile(fileName); statusBar()->showMessage(tr("File saved"), 2000); diff --git a/examples/widgets/mainwindows/mdi/mdichild.cpp b/examples/widgets/mainwindows/mdi/mdichild.cpp index 16f2040de0..727d4f6cfd 100644 --- a/examples/widgets/mainwindows/mdi/mdichild.cpp +++ b/examples/widgets/mainwindows/mdi/mdichild.cpp @@ -115,19 +115,28 @@ bool MdiChild::saveAs() bool MdiChild::saveFile(const QString &fileName) { - QFile file(fileName); - if (!file.open(QFile::WriteOnly | QFile::Text)) { - QMessageBox::warning(this, tr("MDI"), - tr("Cannot write file %1:\n%2.") - .arg(QDir::toNativeSeparators(fileName), file.errorString())); - return false; - } + QString errorMessage; - QTextStream out(&file); QGuiApplication::setOverrideCursor(Qt::WaitCursor); - out << toPlainText(); + QSaveFile file(fileName); + if (file.open(QFile::WriteOnly | QFile::Text)) { + QTextStream out(&file); + out << toPlainText(); + if (!file.commit()) { + errorMessage = tr("Cannot write file %1:\n%2.") + .arg(QDir::toNativeSeparators(fileName), file.errorString()); + } + } else { + errorMessage = tr("Cannot open file %1 for writing:\n%2.") + .arg(QDir::toNativeSeparators(fileName), file.errorString()); + } QGuiApplication::restoreOverrideCursor(); + if (!errorMessage.isEmpty()) { + QMessageBox::warning(this, tr("MDI"), errorMessage); + return false; + } + setCurrentFile(fileName); return true; } diff --git a/examples/widgets/mainwindows/sdi/mainwindow.cpp b/examples/widgets/mainwindows/sdi/mainwindow.cpp index 62a74b26e6..a1fb42158e 100644 --- a/examples/widgets/mainwindows/sdi/mainwindow.cpp +++ b/examples/widgets/mainwindows/sdi/mainwindow.cpp @@ -425,19 +425,28 @@ void MainWindow::openRecentFile() bool MainWindow::saveFile(const QString &fileName) { - QFile file(fileName); - if (!file.open(QFile::WriteOnly | QFile::Text)) { - QMessageBox::warning(this, tr("SDI"), - tr("Cannot write file %1:\n%2.") - .arg(QDir::toNativeSeparators(fileName), file.errorString())); - return false; - } + QString errorMessage; - QTextStream out(&file); QGuiApplication::setOverrideCursor(Qt::WaitCursor); - out << textEdit->toPlainText(); + QSaveFile file(fileName); + if (file.open(QFile::WriteOnly | QFile::Text)) { + QTextStream out(&file); + out << textEdit->toPlainText(); + if (!file.commit()) { + errorMessage = tr("Cannot write file %1:\n%2.") + .arg(QDir::toNativeSeparators(fileName), file.errorString()); + } + } else { + errorMessage = tr("Cannot open file %1 for writing:\n%2.") + .arg(QDir::toNativeSeparators(fileName), file.errorString()); + } QGuiApplication::restoreOverrideCursor(); + if (!errorMessage.isEmpty()) { + QMessageBox::warning(this, tr("SDI"), errorMessage); + return false; + } + setCurrentFile(fileName); statusBar()->showMessage(tr("File saved"), 2000); return true; diff --git a/examples/widgets/painting/deform/pathdeform.h b/examples/widgets/painting/deform/pathdeform.h index b7c7386e2a..af869badc9 100644 --- a/examples/widgets/painting/deform/pathdeform.h +++ b/examples/widgets/painting/deform/pathdeform.h @@ -54,7 +54,7 @@ #include "arthurwidgets.h" #include <QBasicTimer> -#include <QDateTime> +#include <QElapsedTimer> #include <QPainterPath> class PathDeformRenderer : public ArthurFrame @@ -103,7 +103,7 @@ private: QBasicTimer m_repaintTimer; // QBasicTimer m_fpsTimer; // int m_fpsCounter; - QTime m_repaintTracker; + QElapsedTimer m_repaintTracker; QVector<QPainterPath> m_paths; QVector<QPointF> m_advances; diff --git a/examples/widgets/widgets/styles/norwegianwoodstyle.cpp b/examples/widgets/widgets/styles/norwegianwoodstyle.cpp index 1d7ef2637b..a178769bd8 100644 --- a/examples/widgets/widgets/styles/norwegianwoodstyle.cpp +++ b/examples/widgets/widgets/styles/norwegianwoodstyle.cpp @@ -64,9 +64,9 @@ void NorwegianWoodStyle::polish(QPalette &palette) QColor beige(236, 182, 120); QColor slightlyOpaqueBlack(0, 0, 0, 63); - QPixmap backgroundImage(":/images/woodbackground.png"); - QPixmap buttonImage(":/images/woodbutton.png"); - QPixmap midImage = buttonImage; + QImage backgroundImage(":/images/woodbackground.png"); + QImage buttonImage(":/images/woodbutton.png"); + QImage midImage = buttonImage.convertToFormat(QImage::Format_RGB32); QPainter painter; painter.begin(&midImage); @@ -311,11 +311,12 @@ void NorwegianWoodStyle::drawControl(ControlElement element, //! [37] void NorwegianWoodStyle::setTexture(QPalette &palette, QPalette::ColorRole role, //! [37] //! [38] - const QPixmap &pixmap) + const QImage &image) { for (int i = 0; i < QPalette::NColorGroups; ++i) { - QColor color = palette.brush(QPalette::ColorGroup(i), role).color(); - palette.setBrush(QPalette::ColorGroup(i), role, QBrush(color, pixmap)); + QBrush brush(image); + brush.setColor(palette.brush(QPalette::ColorGroup(i), role).color()); + palette.setBrush(QPalette::ColorGroup(i), role, brush); } } //! [38] diff --git a/examples/widgets/widgets/styles/norwegianwoodstyle.h b/examples/widgets/widgets/styles/norwegianwoodstyle.h index c41d81d23a..5a1783eb4d 100644 --- a/examples/widgets/widgets/styles/norwegianwoodstyle.h +++ b/examples/widgets/widgets/styles/norwegianwoodstyle.h @@ -80,7 +80,7 @@ public: private: static void setTexture(QPalette &palette, QPalette::ColorRole role, - const QPixmap &pixmap); + const QImage &image); static QPainterPath roundRectPath(const QRect &rect); }; //! [0] diff --git a/mkspecs/android-clang/qmake.conf b/mkspecs/android-clang/qmake.conf index bb02a3639f..8569c08348 100644 --- a/mkspecs/android-clang/qmake.conf +++ b/mkspecs/android-clang/qmake.conf @@ -27,7 +27,7 @@ else: equals(ANDROID_TARGET_ARCH, x86_64): \ QMAKE_CFLAGS += -gcc-toolchain $$NDK_TOOLCHAIN_PATH -fno-limit-debug-info -QMAKE_LINK = $$QMAKE_CXX $$QMAKE_CFLAGS -Wl,--exclude-libs,libgcc.a -Wl,--exclude-libs,libatomic.a +QMAKE_LINK = $$QMAKE_CXX $$QMAKE_CFLAGS -Wl,--exclude-libs,libgcc.a -Wl,--exclude-libs,libatomic.a -nostdlib++ equals(ANDROID_TARGET_ARCH, armeabi-v7a): QMAKE_LINK += -Wl,--exclude-libs,libunwind.a QMAKE_CFLAGS += -DANDROID_HAS_WSTRING --sysroot=$$NDK_ROOT/sysroot \ diff --git a/mkspecs/common/rtems/qplatformdefs.h b/mkspecs/common/rtems/qplatformdefs.h index 1baa7c7d74..b4ec1de87f 100644 --- a/mkspecs/common/rtems/qplatformdefs.h +++ b/mkspecs/common/rtems/qplatformdefs.h @@ -57,8 +57,10 @@ #include <pwd.h> #include <grp.h> -#include "../posix/qplatformdefs.h" +#define __LINUX_ERRNO_EXTENSIONS__ +#include <errno.h> +#include "../posix/qplatformdefs.h" #ifdef __STRICT_ANSI__ #undef __STRICT_ANSI__ diff --git a/mkspecs/devices/linux-rasp-pi3-g++/qmake.conf b/mkspecs/devices/linux-rasp-pi3-g++/qmake.conf index b215833486..d2e1a3a0ad 100644 --- a/mkspecs/devices/linux-rasp-pi3-g++/qmake.conf +++ b/mkspecs/devices/linux-rasp-pi3-g++/qmake.conf @@ -31,7 +31,7 @@ QMAKE_LIBS_OPENGL_ES2 = $${VC_LINK_LINE} -lGLESv2 # The official opt vc EGL references GLESv2 symbols: need to link it QMAKE_LIBS_EGL = $${VC_LINK_LINE} -lEGL -lGLESv2 -QMAKE_LIBDIR_BCM_HOST = $$VC_LIBRARY_PATH +QMAKE_LIBDIR_BCM_HOST = =$$VC_LIBRARY_PATH QMAKE_INCDIR_BCM_HOST = $$VC_INCLUDE_PATH QMAKE_LIBS_BCM_HOST = -lbcm_host diff --git a/mkspecs/devices/linux-rasp-pi3-vc4-g++/qmake.conf b/mkspecs/devices/linux-rasp-pi3-vc4-g++/qmake.conf index 75b6ad7db5..b4a4484594 100644 --- a/mkspecs/devices/linux-rasp-pi3-vc4-g++/qmake.conf +++ b/mkspecs/devices/linux-rasp-pi3-vc4-g++/qmake.conf @@ -20,7 +20,7 @@ # output check that "EGLFS GBM .......... yes" is present, otherwise # eglfs will not be functional. # -# ./configure -release -opengl es2 -device linux-rpi3-vc4-g++ \ +# ./configure -release -opengl es2 -device linux-rasp-pi3-vc4-g++ \ # -device-option CROSS_COMPILE=~/raspbian/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/arm-linux-gnueabihf- \ # -sysroot ~/raspbian/sysroot \ # -prefix /usr/local/qt5pi -extprefix ~/raspbian/qt5pi -hostprefix ~/raspbian/qt5 \ diff --git a/mkspecs/features/android/android.prf b/mkspecs/features/android/android.prf index 1dc8f87313..0e6f4a4592 100644 --- a/mkspecs/features/android/android.prf +++ b/mkspecs/features/android/android.prf @@ -1,3 +1,21 @@ +APK_PATH = $$shell_path($$OUT_PWD/android-build/$${TARGET}.apk) +!contains(TEMPLATE, subdirs): { + apk_install_target.target = apk_install_target + apk_install_target.depends = first + apk_install_target.commands = $(MAKE) -f $(MAKEFILE) INSTALL_ROOT=$$OUT_PWD/android-build install + + apk.target = apk + apk.depends = apk_install_target + qtPrepareTool(ANDROIDDEPLOYQT, androiddeployqt) + isEmpty(ANDROID_DEPLOYMENT_SETTINGS_FILE): ANDROID_DEPLOYMENT_SETTINGS_FILE = $$OUT_PWD/android-$$TARGET-deployment-settings.json + contains(QMAKE_HOST.os, Windows): extension = .exe + apk.commands = $$ANDROIDDEPLOYQT --input $$ANDROID_DEPLOYMENT_SETTINGS_FILE --output $$OUT_PWD/android-build --apk $$APK_PATH +} else { + prepareRecursiveTarget(apk) + prepareRecursiveTarget(apk_install_target) +} +QMAKE_EXTRA_TARGETS *= apk apk_install_target + contains(TEMPLATE, ".*app") { !android_app { !contains(TARGET, ".so"): TARGET = lib$${TARGET}.so diff --git a/mkspecs/features/android/spec_post.prf b/mkspecs/features/android/spec_post.prf new file mode 100644 index 0000000000..4d11efb2cf --- /dev/null +++ b/mkspecs/features/android/spec_post.prf @@ -0,0 +1,6 @@ +load(spec_post) + +# Work around idiosyncracy in Android NDK's make executable +# which tries to call the shell-builtin "move" as direct process +equals(QMAKE_HOST.os, Windows):equals(QMAKE_MOVE, move): \ + QMAKE_MOVE = cmd /c move diff --git a/mkspecs/features/create_cmake.prf b/mkspecs/features/create_cmake.prf index c9910dda53..00da9bd33f 100644 --- a/mkspecs/features/create_cmake.prf +++ b/mkspecs/features/create_cmake.prf @@ -34,10 +34,20 @@ CMAKE_INCLUDE_NAME = $$eval(QT.$${MODULE}.name) # TARGET here is the one changed at the end of qt_module.prf, # which already contains the Qt5 prefix and QT_LIBINFIX suffix : -# Qt5Core_suffix, Qt5Network_suffix, Foo_suffix -# (or QtCore_suffix, Foo_suffix on macos with -framework) +# Qt5Core_{libinfix_suffix}, Qt5Network_{libinfix_suffix}, Foo_{libinfix_suffix} +# (or QtCore_{libinfix_suffix}, Foo_{libinfix_suffix} on macos with -framework). CMAKE_QT_STEM = $${TARGET} +# On macOS when building just a debug configuration which is not part of debug_and_release, +# $${TARGET} already contains a _debug suffix, as per the following call chain: +# qt_module.prf -> qt5LibraryTarget -> qtLibraryTarget -> qtPlatformTargetSuffix. +# Remove the _debug suffix in the stem, to keep all further uses of CMAKE_QT_STEM consistent. +# The _debug suffix is then re-added where needed regardless if it's a debug_and_release build +# or just debug. +darwin:!qt_framework:!debug_and_release:CONFIG(debug, debug|release) { + CMAKE_QT_STEM = $$replace(CMAKE_QT_STEM, _debug$, ) +} + !generated_privates { isEmpty(SYNCQT.INJECTED_PRIVATE_HEADER_FILES):isEmpty(SYNCQT.PRIVATE_HEADER_FILES): \ CMAKE_NO_PRIVATE_INCLUDES = true @@ -211,18 +221,18 @@ CMAKE_INTERFACE_QT5_MODULE_DEPS = $$join(aux_lib_deps, ";") mac { !isEmpty(CMAKE_STATIC_TYPE) { - CMAKE_LIB_FILE_LOCATION_DEBUG = lib$${CMAKE_QT_STEM}.a + CMAKE_LIB_FILE_LOCATION_DEBUG = lib$${CMAKE_QT_STEM}_debug.a CMAKE_LIB_FILE_LOCATION_RELEASE = lib$${CMAKE_QT_STEM}.a - CMAKE_PRL_FILE_LOCATION_DEBUG = lib$${CMAKE_QT_STEM}.prl + CMAKE_PRL_FILE_LOCATION_DEBUG = lib$${CMAKE_QT_STEM}_debug.prl CMAKE_PRL_FILE_LOCATION_RELEASE = lib$${CMAKE_QT_STEM}.prl } else { qt_framework { - CMAKE_LIB_FILE_LOCATION_DEBUG = $${CMAKE_QT_STEM}.framework/$${CMAKE_QT_STEM} + CMAKE_LIB_FILE_LOCATION_DEBUG = $${CMAKE_QT_STEM}.framework/$${CMAKE_QT_STEM}_debug CMAKE_LIB_FILE_LOCATION_RELEASE = $${CMAKE_QT_STEM}.framework/$${CMAKE_QT_STEM} CMAKE_BUILD_IS_FRAMEWORK = "true" } else { - CMAKE_LIB_FILE_LOCATION_DEBUG = lib$${CMAKE_QT_STEM}.$$eval(QT.$${MODULE}.VERSION).dylib + CMAKE_LIB_FILE_LOCATION_DEBUG = lib$${CMAKE_QT_STEM}_debug.$$eval(QT.$${MODULE}.VERSION).dylib CMAKE_LIB_FILE_LOCATION_RELEASE = lib$${CMAKE_QT_STEM}.$$eval(QT.$${MODULE}.VERSION).dylib } } diff --git a/mkspecs/features/data/cmake/Qt5BasicConfig.cmake.in b/mkspecs/features/data/cmake/Qt5BasicConfig.cmake.in index aca8703238..ba7d9575c0 100644 --- a/mkspecs/features/data/cmake/Qt5BasicConfig.cmake.in +++ b/mkspecs/features/data/cmake/Qt5BasicConfig.cmake.in @@ -56,9 +56,17 @@ function(_qt5_$${CMAKE_MODULE_NAME}_process_prl_file prl_file_location Configura get_filename_component(_qt5_install_libs \"${_qt5$${CMAKE_MODULE_NAME}_install_prefix}/lib\" ABSOLUTE) if(EXISTS \"${prl_file_location}\") - file(STRINGS \"${prl_file_location}\" _prl_strings REGEX \"QMAKE_PRL_LIBS[ \\t]*=\") - string(REGEX REPLACE \"QMAKE_PRL_LIBS[ \\t]*=[ \\t]*([^\\n]*)\" \"\\\\1\" _static_depends ${_prl_strings}) - string(REGEX REPLACE \"[ \\t]+\" \";\" _static_depends ${_static_depends}) + file(STRINGS \"${prl_file_location}\" _prl_strings REGEX \"QMAKE_PRL_LIBS_FOR_CMAKE[ \\t]*=\") + + # file(STRINGS) replaces all semicolons read from the file with backslash semicolons. + # We need to do a reverse transformation in CMake. For that we replace all backslash + # semicolons with just semicolons, but due to the qmake substitution feature + # creating this file, we need to double the amount of backslashes, so the final file + # should have three backslashes and one semicolon. + string(REGEX REPLACE \"\\\\\\;\" \";\" _prl_strings \"${_prl_strings}\") + + string(REGEX REPLACE \"QMAKE_PRL_LIBS_FOR_CMAKE[ \\t]*=[ \\t]*([^\\n]*)\" \"\\\\1\" _static_depends \"${_prl_strings}\") + string(REGEX REPLACE \"[ \\t]+\" \";\" _standard_libraries \"${CMAKE_CXX_STANDARD_LIBRARIES}\") set(_search_paths) string(REPLACE \"\\$\\$[QT_INSTALL_LIBS]\" \"${_qt5_install_libs}\" _static_depends \"${_static_depends}\") foreach(_flag ${_static_depends}) @@ -66,7 +74,15 @@ function(_qt5_$${CMAKE_MODULE_NAME}_process_prl_file prl_file_location Configura if(_flag MATCHES \"^-l(.*)$\") # Handle normal libraries passed as -lfoo set(_lib \"${CMAKE_MATCH_1}\") - if(_lib MATCHES \"^pthread$\") + foreach(_standard_library ${_standard_libraries}) + if(_standard_library MATCHES \"^${_lib}(\\.lib)?$\") + set(_lib_is_default_linked TRUE) + break() + endif() + endforeach() + if (_lib_is_default_linked) + unset(_lib_is_default_linked) + elseif(_lib MATCHES \"^pthread$\") find_package(Threads REQUIRED) list(APPEND _lib_deps Threads::Threads) else() diff --git a/mkspecs/features/data/testserver/Dockerfile b/mkspecs/features/data/testserver/Dockerfile index 8fb664a1d2..a20d289d3f 100644 --- a/mkspecs/features/data/testserver/Dockerfile +++ b/mkspecs/features/data/testserver/Dockerfile @@ -21,7 +21,9 @@ RUN echo "#!/usr/bin/env bash\n" \ RUN chmod +x startup.sh # rewrite the default configurations of avahi-daemon +# Disable IPv6 of avahi-daemon to resolve the unstable connections on Windows ARG test_domain RUN sed -i -e "s,#domain-name=local,domain-name=${test_domain:-test-net.qt.local}," \ -e "s,#publish-aaaa-on-ipv4=yes,publish-aaaa-on-ipv4=no," \ + -e "s,use-ipv6=yes,use-ipv6=no," \ /etc/avahi/avahi-daemon.conf diff --git a/mkspecs/features/data/testserver/docker-compose-common.yml b/mkspecs/features/data/testserver/docker-compose-common.yml new file mode 100644 index 0000000000..58282c1273 --- /dev/null +++ b/mkspecs/features/data/testserver/docker-compose-common.yml @@ -0,0 +1,38 @@ +version: '2.1' + +# This is a template docker-compose file shared with all modules. It is based +# on 'extending services' feature of compose file version 2.1. +# See https://docs.docker.com/compose/extends/#extending-services for details. +# +# Example: testserver/docker-compose.yml +# services: +# foo: +# extends: +# file: ${SHARED_DATA}/docker-compose-common.yml +# service: ${SHARED_SERVICE} +# container_name: qt-test-server-foo +# hostname: ${HOST_NAME:-foo} +# build: +# context: . +# args: +# provisioningImage: qt-test-server-foo:537fe302f61851d1663... +# serviceDir: ./foo +# command: service/foo.sh + +x-services: + &default-service + domainname: ${TEST_DOMAIN} + build: + context: . + dockerfile: ${SHARED_DATA}/Dockerfile + args: + test_domain: ${TEST_DOMAIN} + entrypoint: ./startup.sh + +services: + bridge-network: *default-service + host-network: + << : *default-service + network_mode: "host" + extra_hosts: + - "qt-test-server.${TEST_DOMAIN}:${MACHINE_IP}" diff --git a/mkspecs/features/exclusive_builds.prf b/mkspecs/features/exclusive_builds.prf index 1ff9a04d42..b28e74146a 100644 --- a/mkspecs/features/exclusive_builds.prf +++ b/mkspecs/features/exclusive_builds.prf @@ -38,5 +38,5 @@ defineTest(addExclusiveBuilds) { } # Default directories to process -QMAKE_DIR_REPLACE = OBJECTS_DIR MOC_DIR RCC_DIR PRECOMPILED_DIR QGLTF_DIR DESTDIR TRACEGEN_DIR QMLCACHE_DIR LRELEASE_DIR -QMAKE_DIR_REPLACE_SANE += QGLTF_DIR TRACEGEN_DIR QMLCACHE_DIR LRELEASE_DIR +QMAKE_DIR_REPLACE_SANE += QGLTF_DIR TRACEGEN_DIR QMLCACHE_DIR LRELEASE_DIR LEX_DIR YACC_DIR +QMAKE_DIR_REPLACE = OBJECTS_DIR MOC_DIR RCC_DIR PRECOMPILED_DIR DESTDIR $$QMAKE_DIR_REPLACE_SANE diff --git a/mkspecs/features/lex.prf b/mkspecs/features/lex.prf index 7d8325bedb..ee06215103 100644 --- a/mkspecs/features/lex.prf +++ b/mkspecs/features/lex.prf @@ -2,37 +2,49 @@ # Lex extra-compiler for handling files specified in the LEXSOURCES variable # -{ - lex.name = Lex ${QMAKE_FILE_IN} - lex.input = LEXSOURCES - lex.dependency_type = TYPE_C - lex_included { - lex.CONFIG += no_link - } else { - lex.variable_out = GENERATED_SOURCES - } +isEmpty(LEX_DIR): LEX_DIR = . - isEmpty(QMAKE_LEXFLAGS_MANGLE):QMAKE_LEXFLAGS_MANGLE = -P${QMAKE_FILE_BASE} +defineReplace(lexCommands) { + input = $$relative_path($$absolute_path($$1, $$OUT_PWD), $$OUT_PWD/$$LEX_DIR) + output = $$basename(2) + input_base = $$basename(1) + input_base ~= s/\.[^.]*$// + + isEmpty(QMAKE_LEXFLAGS_MANGLE): QMAKE_LEXFLAGS_MANGLE = -P$${input_base} QMAKE_LEXEXTRAFLAGS = $$QMAKE_LEXFLAGS - !yacc_no_name_mangle:QMAKE_LEXEXTRAFLAGS += $$QMAKE_LEXFLAGS_MANGLE + !yacc_no_name_mangle: QMAKE_LEXEXTRAFLAGS += $$QMAKE_LEXFLAGS_MANGLE contains(QMAKE_LEX, .*flex) { # GNU flex, we can use -o outfile - lex.commands = $$QMAKE_LEX $$QMAKE_LEXEXTRAFLAGS --nounistd -o ${QMAKE_FILE_OUT} ${QMAKE_FILE_IN} + commands = $$QMAKE_LEX $$QMAKE_LEXEXTRAFLAGS --nounistd -o $$output $$input } else { # stupid POSIX lex, it only generates a file called lex.yy.c # or lex.prefix.c if the -P<prefix> option is active intermediate_file = lex.yy.c QMAKE_LEXEXTRAFLAGS = $$QMAKE_LEXFLAGS $$QMAKE_LEXFLAGS_MANGLE - lex.commands = \ - -$(DEL_FILE) ${QMAKE_FILE_OUT}$$escape_expand(\\n\\t) \ - $$QMAKE_LEX $$QMAKE_LEXEXTRAFLAGS ${QMAKE_FILE_IN}$$escape_expand(\\n\\t) \ - $(MOVE) $$intermediate_file ${QMAKE_FILE_OUT} $$escape_expand(\\n\\t) - unset(intermediate_file) + commands = \ + -$(DEL_FILE) $${output}$$escape_expand(\\n\\t) \ + $$QMAKE_LEX $$QMAKE_LEXEXTRAFLAGS $${input}$$escape_expand(\\n\\t) \ + $(MOVE) $$intermediate_file $$output $$escape_expand(\\n\\t) + } + !equals(LEX_DIR, .): \ + commands = cd $$LEX_DIR && $$commands + silent: commands = @echo Lex $$1 && $$commands + return($$commands) +} + +{ + lex.name = Lex ${QMAKE_FILE_IN} + lex.input = LEXSOURCES + lex.dependency_type = TYPE_C + lex_included { + lex.CONFIG += no_link + } else { + lex.variable_out = GENERATED_SOURCES } - lex.output = $${QMAKE_CPP_MOD_MOC}${QMAKE_FILE_BASE}$${QMAKE_MOD_LEX}$${first(QMAKE_EXT_CPP)} - silent:lex.commands = @echo Lex ${QMAKE_FILE_IN} && $$lex.commands + lex.commands = ${QMAKE_FUNC_lexCommands} + lex.output = $$LEX_DIR/$${QMAKE_CPP_MOD_MOC}${QMAKE_FILE_BASE}$${QMAKE_MOD_LEX}$${first(QMAKE_EXT_CPP)} QMAKE_EXTRA_COMPILERS += lex } diff --git a/mkspecs/features/ltcg.prf b/mkspecs/features/ltcg.prf index d113caf35c..10d14dfe85 100644 --- a/mkspecs/features/ltcg.prf +++ b/mkspecs/features/ltcg.prf @@ -1,4 +1,6 @@ -CONFIG(release, debug|release) { +static:no-static-ltcg { + # Static library but no-static-ltcg enabled: skip LTCG +} else: CONFIG(release, debug|release) { # We need fat object files when creating static libraries on some platforms # so the linker will know to load a particular object from the library # in the first place. On others, we have special ar and nm to create the symbol @@ -22,13 +24,16 @@ CONFIG(release, debug|release) { } } - fat-lto { + fat-lto|if(static:fat-static-lto) { QMAKE_CFLAGS_LTCG += $$QMAKE_CFLAGS_LTCG_FATOBJECTS QMAKE_CXXFLAGS_LTCG += $$QMAKE_CXXFLAGS_LTCG_FATOBJECTS } load(link_ltcg) - QMAKE_CFLAGS *= $$QMAKE_CFLAGS_LTCG - QMAKE_CXXFLAGS *= $$QMAKE_CXXFLAGS_LTCG - QMAKE_LFLAGS *= $$QMAKE_LFLAGS_LTCG + QMAKE_CFLAGS -= $$QMAKE_CFLAGS_LTCG + QMAKE_CFLAGS += $$QMAKE_CFLAGS_LTCG + QMAKE_CXXFLAGS -= $$QMAKE_CXXFLAGS_LTCG + QMAKE_CXXFLAGS += $$QMAKE_CXXFLAGS_LTCG + QMAKE_LFLAGS -= $$QMAKE_LFLAGS_LTCG + QMAKE_LFLAGS += $$QMAKE_LFLAGS_LTCG } diff --git a/mkspecs/features/qmltestcase.prf b/mkspecs/features/qmltestcase.prf index b4b1224781..ae4ebef513 100644 --- a/mkspecs/features/qmltestcase.prf +++ b/mkspecs/features/qmltestcase.prf @@ -1,8 +1,14 @@ !isEmpty(SOURCES) { QT += qml qmltest load(testcase) - contains(TEMPLATE, vc.*): DEFINES += QUICK_TEST_SOURCE_DIR=\"$$_PRO_FILE_PWD_\" - else: DEFINES += QUICK_TEST_SOURCE_DIR=$$shell_quote(\"$$_PRO_FILE_PWD_\") + !android { + contains(TEMPLATE, vc.*): DEFINES += QUICK_TEST_SOURCE_DIR=\"$$_PRO_FILE_PWD_\" + else: DEFINES += QUICK_TEST_SOURCE_DIR=$$shell_quote(\"$$_PRO_FILE_PWD_\") + } else { + !isEmpty(RESOURCES): warning("The RESOURCES qmake variable is empty, the test will probably fail to run") + DEFINES += QUICK_TEST_SOURCE_DIR=\":/\" + } + } else { # Allow a project to run tests without a CPP stub TEMPLATE = aux diff --git a/mkspecs/features/qt_build_config.prf b/mkspecs/features/qt_build_config.prf index 0c6c10dded..8a7c9c28d3 100644 --- a/mkspecs/features/qt_build_config.prf +++ b/mkspecs/features/qt_build_config.prf @@ -37,8 +37,10 @@ intel_icl { QMAKE_DIR_REPLACE_SANE = PRECOMPILED_DIR OBJECTS_DIR MOC_DIR RCC_DIR UI_DIR +load(qt_prefix_build_check) + # force_independent can be set externally. prefix_build not. -!exists($$[QT_HOST_DATA]/.qmake.cache): \ +qtIsPrefixBuild($$[QT_HOST_DATA]): \ CONFIG += prefix_build force_independent !build_pass:!isEmpty(_QMAKE_SUPER_CACHE_):force_independent { diff --git a/mkspecs/features/qt_common.prf b/mkspecs/features/qt_common.prf index e51aa4766b..c24f2c6062 100644 --- a/mkspecs/features/qt_common.prf +++ b/mkspecs/features/qt_common.prf @@ -51,6 +51,12 @@ contains(TEMPLATE, .*lib) { } lib_replace.CONFIG = path QMAKE_PRL_INSTALL_REPLACE += lib_replace + !equals(qt_libdir, $$rplbase/lib) { + qtlibdir_replace.match = $$qt_libdir + qtlibdir_replace.replace = $$qqt_libdir + qtlibdir_replace.CONFIG = path + QMAKE_PRL_INSTALL_REPLACE += qtlibdir_replace + } } # The remainder of this file must not apply to host tools/libraries, @@ -146,8 +152,8 @@ warnings_are_errors:warning_clean { android: QMAKE_CXXFLAGS_WARN_ON += -Wno-error=literal-suffix } } else:msvc:!intel_icl { - # enable for MSVC 2012, MSVC 2013, MSVC 2015 - contains(MSVC_VER, "1[124].0"): QMAKE_CXXFLAGS_WARN_ON += -WX + # enable for MSVC 2015, MSVC 2017 + contains(MSVC_VER, "1[45].0"): QMAKE_CXXFLAGS_WARN_ON += -WX } unset(ver) } diff --git a/mkspecs/features/qt_configure.prf b/mkspecs/features/qt_configure.prf index 7c48badfaf..2ac9de266f 100644 --- a/mkspecs/features/qt_configure.prf +++ b/mkspecs/features/qt_configure.prf @@ -2517,17 +2517,19 @@ logn("Configure summary:") logn() qtConfPrintReport() +load(qt_prefix_build_check) + # final notes for the user logn() logn("Qt is now configured for building. Just run '$$QMAKE_MAKE_NAME'.") pfx = $$[QT_INSTALL_PREFIX] -exists($$pfx/.qmake.cache) { +qtIsPrefixBuild($$pfx) { + logn("Once everything is built, you must run '$$QMAKE_MAKE_NAME install'.") + logn("Qt will be installed into '$$system_path($$pfx)'.") +} else { logn("Once everything is built, Qt is installed.") logn("You should NOT run '$$QMAKE_MAKE_NAME install'.") logn("Note that this build cannot be deployed to other machines or devices.") -} else { - logn("Once everything is built, you must run '$$QMAKE_MAKE_NAME install'.") - logn("Qt will be installed into '$$system_path($$pfx)'.") } logn() logn("Prior to reconfiguration, make sure you remove any leftovers from") diff --git a/mkspecs/features/qt_module.prf b/mkspecs/features/qt_module.prf index 213556904d..24512eeb76 100644 --- a/mkspecs/features/qt_module.prf +++ b/mkspecs/features/qt_module.prf @@ -82,6 +82,11 @@ header_module { CONFIG += force_qt # Needed for the headers_clean tests. !lib_bundle: \ CONFIG += qt_no_install_library + + # Allow creation of .prl, .la and .pc files. + target.path = $$[QT_INSTALL_LIBS] + target.CONFIG += dummy_install + INSTALLS += target } else { TEMPLATE = lib } @@ -286,6 +291,12 @@ load(qt_targets) QMAKE_PKGCONFIG_REQUIRES += $$replace(QT.$${i}.name, ^Qt, Qt$$section(QT.$${i}.VERSION, ., 0, 0))$$qtPlatformTargetSuffix() isEmpty(QMAKE_PKGCONFIG_DESCRIPTION): \ QMAKE_PKGCONFIG_DESCRIPTION = $$replace(TARGET, ^Qt, "Qt ") module + !isEmpty(lib_replace0.match) { + pclib_replace0.match = $$lib_replace0.match + pclib_replace0.replace = $$QMAKE_PKGCONFIG_LIBDIR/ + pclib_replace0.CONFIG = path + QMAKE_PKGCONFIG_INSTALL_REPLACE += pclib_replace0 + } pclib_replace.match = $$lib_replace.match !isEmpty(lib_replace.replace): \ pclib_replace.replace = $$QMAKE_PKGCONFIG_LIBDIR @@ -298,6 +309,12 @@ load(qt_targets) QMAKE_LIBTOOL_LIBDIR = $$[QT_HOST_LIBS] else: \ QMAKE_LIBTOOL_LIBDIR = "=$$[QT_INSTALL_LIBS/raw]" + !isEmpty(lib_replace0.match) { + ltlib_replace0.match = $$lib_replace0.match + ltlib_replace0.replace = $$QMAKE_LIBTOOL_LIBDIR/ + ltlib_replace0.CONFIG = path + QMAKE_LIBTOOL_INSTALL_REPLACE += ltlib_replace0 + } ltlib_replace.match = $$lib_replace.match !isEmpty(lib_replace.replace): \ ltlib_replace.replace = $$QMAKE_LIBTOOL_LIBDIR diff --git a/mkspecs/features/qt_module_headers.prf b/mkspecs/features/qt_module_headers.prf index 37b69e31c8..899a40103a 100644 --- a/mkspecs/features/qt_module_headers.prf +++ b/mkspecs/features/qt_module_headers.prf @@ -272,7 +272,7 @@ headersclean:!internal_module { hcleanFLAGS += -std=c++98 } - hcleanCOMMAND = $$QMAKE_CXX -c $(CXXFLAGS) $$hcleanFLAGS $(INCPATH) $$hcleanDEFS -xc++ ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT} + hcleanCOMMAND = $(CXX) -c $(CXXFLAGS) $$hcleanFLAGS $(INCPATH) $$hcleanDEFS -xc++ ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT} } else: msvc:!intel_icl { # 4180: qualifier applied to function type has no meaning; ignored # 4458: declaration of 'identifier' hides class member @@ -284,7 +284,7 @@ headersclean:!internal_module { # 4577: 'noexcept' used with no exception handling mode specified; termination on exception is not guaranteed. Specify /EHsc greaterThan(QMAKE_MSC_VER, 18): hcleanFLAGS += -wd4577 - hcleanCOMMAND = $$QMAKE_CXX -c $(CXXFLAGS) $$hcleanFLAGS $(INCPATH) $$hcleanDEFS -FI${QMAKE_FILE_IN} -Fo${QMAKE_FILE_OUT} \ + hcleanCOMMAND = $(CXX) -c $(CXXFLAGS) $$hcleanFLAGS $(INCPATH) $$hcleanDEFS -FI${QMAKE_FILE_IN} -Fo${QMAKE_FILE_OUT} \ $$[QT_INSTALL_DATA/src]/mkspecs/features/data/dummy.cpp } diff --git a/mkspecs/features/qt_prefix_build_check.prf b/mkspecs/features/qt_prefix_build_check.prf new file mode 100644 index 0000000000..3f98847de9 --- /dev/null +++ b/mkspecs/features/qt_prefix_build_check.prf @@ -0,0 +1,21 @@ +# +# W A R N I N G +# ------------- +# +# This file is not part of the Qt API. It exists purely as an +# implementation detail. It may change from version to version +# without notice, or even be removed. +# +# We mean it. +# + +defineTest(qtIsPrefixBuild) { + prefixdir = $$1 + # qtbase non-prefix build? + exists($$prefixdir/.qmake.cache): \ + return(false) + # top-level non-prefix build? + contains(prefixdir, .*/qtbase):exists($$dirname(prefixdir)/.qmake.super): \ + return(false) + return(true) +} diff --git a/mkspecs/features/sanitizer.prf b/mkspecs/features/sanitizer.prf index 9e7ff0218a..c7d72aec80 100644 --- a/mkspecs/features/sanitizer.prf +++ b/mkspecs/features/sanitizer.prf @@ -1,10 +1,33 @@ # Sanitizer flags - sanitize_address { QMAKE_CFLAGS += $$QMAKE_SANITIZE_ADDRESS_CFLAGS QMAKE_CXXFLAGS += $$QMAKE_SANITIZE_ADDRESS_CXXFLAGS QMAKE_LFLAGS += $$QMAKE_SANITIZE_ADDRESS_LFLAGS + android { + # ARM 32 (armeabi-v7a & arm5) are not supported because Qt must be rebuilt with -marm + equals(ANDROID_TARGET_ARCH, arm64-v8a): ANDROID_LIBCLANG_RT_FILE = "libclang_rt.asan-aarch64-android.so" + else: equals(ANDROID_TARGET_ARCH, x86): ANDROID_LIBCLANG_RT_FILE = "libclang_rt.asan-i686-android.so" + else: equals(ANDROID_TARGET_ARCH, x86_64): ANDROID_LIBCLANG_RT_FILE = "libclang_rt.asan-x86_64-android.so" + else: error("ASAN: Unsupported platform $${ANDROID_TARGET_ARCH}") + + ANDROID_LIBCLANG_RT_PATH = $${NDK_LLVM_PATH}/lib64/clang + ANDROID_CLANG_RT_VERSIONS = $$files($$ANDROID_LIBCLANG_RT_PATH/*) + for (VERSION, ANDROID_CLANG_RT_VERSIONS) { + greaterThan(VERSION, $$ANDROID_LIBCLANG_RT_PATH): ANDROID_LIBCLANG_RT_PATH = $$VERSION + } + ANDROID_LIBCLANG_RT_PATH = "$${ANDROID_LIBCLANG_RT_PATH}/lib/linux/" + ANDROID_WRAP_SH_CONTENT = "$$LITERAL_HASH!/system/bin/sh" + ANDROID_WRAP_SH_CONTENT += "HERE=\"$(cd \"$(dirname \"$0\")\" && pwd)\"" + isEmpty(ANDROID_ASAN_OPTIONS): ANDROID_ASAN_OPTIONS = "log_to_syslog=false,allow_user_segv_handler=1" + ANDROID_WRAP_SH_CONTENT += "export ASAN_OPTIONS=$${ANDROID_ASAN_OPTIONS}" + ANDROID_WRAP_SH_CONTENT += "export LD_PRELOAD=$HERE/$${ANDROID_LIBCLANG_RT_FILE}" + ANDROID_WRAP_SH_CONTENT += "exec \"$@\"" + write_file($$OUT_PWD/android-build/resources/lib/$${ANDROID_TARGET_ARCH}/wrap.sh, ANDROID_WRAP_SH_CONTENT) | error() + libclang_rt.path = /libs/$$ANDROID_TARGET_ARCH/ + libclang_rt.files = "$${ANDROID_LIBCLANG_RT_PATH}/$${ANDROID_LIBCLANG_RT_FILE}" + INSTALLS += libclang_rt + } } sanitize_memory { diff --git a/mkspecs/features/testcase.prf b/mkspecs/features/testcase.prf index b8102c26b5..d4f08835f1 100644 --- a/mkspecs/features/testcase.prf +++ b/mkspecs/features/testcase.prf @@ -52,14 +52,26 @@ debug_and_release:debug_and_release_target { } # Allow for a custom test runner script -$${type}.commands += $(TESTRUNNER) + +android: isEmpty($(TESTRUNNER)) { + APK_PATH = $$shell_path($$OUT_PWD/android-build/$${TARGET}.apk) + qtPrepareTool(ANDROIDTESTRUNNER, androidtestrunner) + qtPrepareTool(ANDROIDDEPLOYQT, androiddeployqt) + isEmpty(ANDROID_DEPLOYMENT_SETTINGS_FILE): ANDROID_DEPLOYMENT_SETTINGS_FILE = $$OUT_PWD/android-$$TARGET-deployment-settings.json + contains(QMAKE_HOST.os, Windows): extension = .exe + $${type}.commands = $$ANDROIDTESTRUNNER --androiddeployqt \"$$ANDROIDDEPLOYQT --input $$ANDROID_DEPLOYMENT_SETTINGS_FILE\" + $${type}.commands += --path \"$$OUT_PWD/android-build\" + $${type}.commands += --adb \"$$shell_path($${ANDROID_SDK_ROOT}$${QMAKE_DIR_SEP}platform-tools$${QMAKE_DIR_SEP}adb$${extension})\" + $${type}.commands += --make \"$(MAKE) -f $(MAKEFILE)\" + $${type}.commands += --apk $$APK_PATH +} else: $${type}.commands += $(TESTRUNNER) unix { isEmpty(TEST_TARGET_DIR): TEST_TARGET_DIR = . app_bundle: \ $${type}.commands += $${TEST_TARGET_DIR}/$(QMAKE_TARGET).app/Contents/MacOS/$(QMAKE_TARGET) - else: \ + else: !android: \ $${type}.commands += $${TEST_TARGET_DIR}/$(QMAKE_TARGET) } else { # Windows diff --git a/mkspecs/features/unsupported/testserver.prf b/mkspecs/features/unsupported/testserver.prf index 6507a360c5..32bd4df30c 100644 --- a/mkspecs/features/unsupported/testserver.prf +++ b/mkspecs/features/unsupported/testserver.prf @@ -94,29 +94,33 @@ isEmpty(TESTSERVER_VERSION) { # binds the same port on the host. An alternative solution is to deploy # the docker environment into VirtualBox using docker-machine. isEmpty(TESTSERVER_COMPOSE_FILE): TESTSERVER_COMPOSE_FILE = \ - $$dirname(_QMAKE_CONF_)/tests/testserver/docker-compose-for-macOS.yml + $$dirname(_QMAKE_CONF_)/tests/testserver/docker-compose-host-network.yml # The connection configuration for the target machine MACHINE_CONFIG = $(shell docker-machine config qt-test-server) # The environment variables passed to the docker-compose file TEST_ENV = 'MACHINE_IP=$(shell docker-machine ip qt-test-server)' + TEST_ENV += 'HOST_NAME=qt-test-server' TEST_ENV += 'TEST_DOMAIN=$$DNSDOMAIN' TEST_ENV += 'SHARED_DATA=$$PWD/../data/testserver' + TEST_ENV += 'SHARED_SERVICE=host-network' TEST_CMD = env } else:equals(QMAKE_HOST.os, Windows) { # There is no docker bridge on Windows. It is impossible to ping a container. # Use docker-machine to deploy the docker environment into VirtualBox. isEmpty(TESTSERVER_COMPOSE_FILE): TESTSERVER_COMPOSE_FILE = \ - $$dirname(_QMAKE_CONF_)/tests/testserver/docker-compose-for-windows.yml + $$dirname(_QMAKE_CONF_)/tests/testserver/docker-compose-host-network.yml # The connection configuration for the target machine MACHINE_CONFIG = (docker-machine config qt-test-server) # The environment variables passed to the docker-compose file TEST_ENV = '\$\$env:MACHINE_IP = docker-machine ip qt-test-server;' + TEST_ENV += '\$\$env:HOST_NAME = $$shell_quote(\"qt-test-server\");' TEST_ENV += '\$\$env:TEST_DOMAIN = $$shell_quote(\"$$DNSDOMAIN\");' TEST_ENV += '\$\$env:SHARED_DATA = $$shell_quote(\"$$PWD/../data/testserver\");' + TEST_ENV += '\$\$env:SHARED_SERVICE = $$shell_quote(\"host-network\");' # Docker-compose CLI environment variables: # Enable path conversion from Windows-style to Unix-style in volume definitions. @@ -126,12 +130,18 @@ isEmpty(TESTSERVER_VERSION) { CONFIG += PowerShell } else { isEmpty(TESTSERVER_COMPOSE_FILE): TESTSERVER_COMPOSE_FILE = \ - $$dirname(_QMAKE_CONF_)/tests/testserver/docker-compose.yml + $$dirname(_QMAKE_CONF_)/tests/testserver/docker-compose-bridge-network.yml # The environment variables passed to the docker-compose file TEST_ENV = 'TEST_DOMAIN=$$DNSDOMAIN' TEST_ENV += 'SHARED_DATA=$$PWD/../data/testserver' + TEST_ENV += 'SHARED_SERVICE=bridge-network' TEST_CMD = env } + + # If $$TESTSERVER_COMPOSE_FILE defined by platform doesn't exist, the default + # docker-compose.yml is used as a fallback. + !exists($$TESTSERVER_COMPOSE_FILE): TESTSERVER_COMPOSE_FILE = \ + $$dirname(_QMAKE_CONF_)/tests/testserver/docker-compose.yml !exists($$TESTSERVER_COMPOSE_FILE): error("Invalid TESTSERVER_COMPOSE_FILE specified") diff --git a/mkspecs/features/win32/dumpcpp.prf b/mkspecs/features/win32/dumpcpp.prf index c8cb0dd24b..d19da3d077 100644 --- a/mkspecs/features/win32/dumpcpp.prf +++ b/mkspecs/features/win32/dumpcpp.prf @@ -22,15 +22,15 @@ dumpcpp_impl.depends += ${QMAKE_FILE_BASE}.h QMAKE_EXTRA_COMPILERS += dumpcpp_impl -# Create dependencies from every object file to our generated header files. -if(isEmpty(BUILDS)|build_pass):have_target:!contains(TEMPLATE, vc.*) { +# Call dumpcpp the first time if the files do not exist to help find dependencies +!build_pass:have_target:!contains(TEMPLATE, vc.*) { for(tlb, TYPELIBS) { + tlbCopy = $$replace(tlb, \", ) hdr = $$basename(tlb) - hdr = $$section(hdr, ., 0, -2).h - TYPELIB_HEADERS += $$hdr + hdr = $$section(hdr, ., 0, -2) + tmp_command = $$QMAKE_DUMPCPP $$system_quote($$absolute_path($$tlb, $$_PRO_FILE_PWD_)) \ + -o $$system_quote($$OUT_PWD/$$hdr) + qaxcontainer_compat: tmp_command += -compat + !exists($$OUT_PWD/$${hdr}.h): system($$tmp_command) } - - objtgt.target = $(OBJECTS) - objtgt.depends += $$TYPELIB_HEADERS - QMAKE_EXTRA_TARGETS += objtgt } diff --git a/mkspecs/features/win32/windows_vulkan_sdk.prf b/mkspecs/features/win32/windows_vulkan_sdk.prf index 6c08e28fe9..2aebbd3b25 100644 --- a/mkspecs/features/win32/windows_vulkan_sdk.prf +++ b/mkspecs/features/win32/windows_vulkan_sdk.prf @@ -1,7 +1,7 @@ isEmpty(QMAKE_INCDIR_VULKAN) { # Pick up the VULKAN_SDK env var set by the LunarG SDK so that the Vulkan # headers are found out-of-the-box on typical Windows setups. - QMAKE_INCDIR_VULKAN = $$(VULKAN_SDK)\\include + QMAKE_INCDIR_VULKAN = $$(VULKAN_SDK)/include # Do not export the include dir but resolve it on every qmake call. QMAKE_EXPORT_INCDIR_VULKAN = - diff --git a/mkspecs/features/yacc.prf b/mkspecs/features/yacc.prf index 618f0668c2..0c7ff7321e 100644 --- a/mkspecs/features/yacc.prf +++ b/mkspecs/features/yacc.prf @@ -2,32 +2,49 @@ # Yacc extra-compiler for handling files specified in the YACCSOURCES variable # -{ - yacc_decl.name = Yacc header - yacc_decl.input = YACCSOURCES - yacc_decl.variable_out = GENERATED_FILES +isEmpty(YACC_DIR): YACC_DIR = . +defineReplace(yaccCommands) { + input = $$relative_path($$absolute_path($$1, $$OUT_PWD), $$OUT_PWD/$$YACC_DIR) + input_base = $$basename(1) + input_base ~= s/\.[^.]*$// + hpp_output = $$2 + cpp_output = $$hpp_output + cpp_output ~= s/$$re_escape($$first(QMAKE_EXT_H))$/$$first(QMAKE_EXT_CPP)/ isEmpty(QMAKE_YACCFLAGS_MANGLE) { - QMAKE_YACCFLAGS_MANGLE = -p ${QMAKE_FILE_BASE} -b ${QMAKE_FILE_BASE} - QMAKE_YACC_HEADER = ${QMAKE_FILE_BASE}.tab.h - QMAKE_YACC_SOURCE = ${QMAKE_FILE_BASE}.tab.c + QMAKE_YACCFLAGS_MANGLE = -p $${input_base} -b $${input_base} + QMAKE_YACC_HEADER = $${input_base}.tab.h + QMAKE_YACC_SOURCE = $${input_base}.tab.c } else { - QMAKE_YACCFLAGS_MANGLE ~= s/\\$base/${QMAKE_FILE_BASE}/g #backwards compat - QMAKE_YACC_HEADER ~= s/\\$base/${QMAKE_FILE_BASE}/g - QMAKE_YACC_SOURCE ~= s/\\$base/${QMAKE_FILE_BASE}/g + QMAKE_YACCFLAGS_MANGLE ~= s/\\$base/$${input_base}/g #backwards compat + QMAKE_YACC_HEADER ~= s/\\$base/$${input_base}/g + QMAKE_YACC_SOURCE ~= s/\\$base/$${input_base}/g } QMAKE_YACCDECLFLAGS = $$QMAKE_YACCFLAGS - !yacc_no_name_mangle:QMAKE_YACCDECLFLAGS += $$QMAKE_YACCFLAGS_MANGLE + !yacc_no_name_mangle: QMAKE_YACCDECLFLAGS += $$QMAKE_YACCFLAGS_MANGLE - yacc_decl.commands = \ - -$(DEL_FILE) $${QMAKE_CPP_MOD_MOC}${QMAKE_FILE_BASE}$${QMAKE_MOD_YACC}$${first(QMAKE_EXT_H)} $${QMAKE_CPP_MOD_MOC}${QMAKE_FILE_BASE}$${QMAKE_MOD_YACC}$${first(QMAKE_EXT_CPP)}$$escape_expand(\\n\\t) \ - $$QMAKE_YACC $$QMAKE_YACCDECLFLAGS ${QMAKE_FILE_IN}$$escape_expand(\\n\\t) \ - $(MOVE) $${QMAKE_YACC_HEADER} $${QMAKE_CPP_MOD_MOC}${QMAKE_FILE_BASE}$${QMAKE_MOD_YACC}$${first(QMAKE_EXT_H)}$$escape_expand(\\n\\t) \ - $(MOVE) $${QMAKE_YACC_SOURCE} $${QMAKE_CPP_MOD_MOC}${QMAKE_FILE_BASE}$${QMAKE_MOD_YACC}$${first(QMAKE_EXT_CPP)}$$escape_expand(\\n\\t) - yacc_decl.output = $${QMAKE_CPP_MOD_MOC}${QMAKE_FILE_BASE}$${QMAKE_MOD_YACC}$${first(QMAKE_EXT_H)} + !equals(YACC_DIR, .): yacc_call = cd $$YACC_DIR && + yacc_call += $$QMAKE_YACC $$QMAKE_YACCDECLFLAGS $${input} + + commands = \ + -$(DEL_FILE) $${hpp_output} $${cpp_output}$$escape_expand(\\n\\t) \ + $${yacc_call}$$escape_expand(\\n\\t) \ + $(MOVE) $${YACC_DIR}/$${QMAKE_YACC_HEADER} $${hpp_output}$$escape_expand(\\n\\t) \ + $(MOVE) $${YACC_DIR}/$${QMAKE_YACC_SOURCE} $${cpp_output}$$escape_expand(\\n\\t) + + silent: commands = @echo Yacc $$1 && $$commands + return($$commands) +} - silent:yacc_decl.commands = @echo Yacc ${QMAKE_FILE_IN} && $$yacc_decl.commands +yacc_output_base = $${YACC_DIR}/$${QMAKE_CPP_MOD_MOC}${QMAKE_FILE_BASE}$${QMAKE_MOD_YACC} + +{ + yacc_decl.name = Yacc header + yacc_decl.input = YACCSOURCES + yacc_decl.variable_out = GENERATED_FILES + yacc_decl.commands = ${QMAKE_FUNC_yaccCommands} + yacc_decl.output = $${yacc_output_base}$$first(QMAKE_EXT_H) QMAKE_EXTRA_COMPILERS += yacc_decl } @@ -37,7 +54,9 @@ yacc_impl.variable_out = GENERATED_SOURCES yacc_impl.dependency_type = TYPE_C yacc_impl.commands = $$escape_expand(\\n) # We don't want any commands where, but if command is empty no rules are created - yacc_impl.depends += $${QMAKE_CPP_MOD_MOC}${QMAKE_FILE_BASE}$${QMAKE_MOD_YACC}$${first(QMAKE_EXT_H)} # Make sure we depend on the step above - yacc_impl.output = $${QMAKE_CPP_MOD_MOC}${QMAKE_FILE_BASE}$${QMAKE_MOD_YACC}$${first(QMAKE_EXT_CPP)} # Faked output from this step, output really created in step above + yacc_impl.depends += $${yacc_output_base}$$first(QMAKE_EXT_H) # Make sure we depend on the step above + yacc_impl.output = $${yacc_output_base}$$first(QMAKE_EXT_CPP) # Faked output from this step, output really created in step above QMAKE_EXTRA_COMPILERS += yacc_impl } + +unset(yacc_output_base) diff --git a/mkspecs/wasm-emscripten/qmake.conf b/mkspecs/wasm-emscripten/qmake.conf index c80f2bfb92..c6c3af3622 100644 --- a/mkspecs/wasm-emscripten/qmake.conf +++ b/mkspecs/wasm-emscripten/qmake.conf @@ -94,6 +94,12 @@ QMAKE_PREFIX_STATICLIB = lib QMAKE_EXTENSION_STATICLIB = a # llvm bitcode QMAKE_AR = emar cqs +equals(QMAKE_HOST.os, Windows) { + QMAKE_AR_CMD = \ + "$(file >$(OBJECTS_DIR)/$(TARGET).rsp, $(subst \\,/,$(OBJECTS)))$$escape_expand(\\n\\t)" \ + "$(AR) $(DESTDIR)$(TARGET) @$(OBJECTS_DIR)/$(TARGET).rsp" +} + QMAKE_DISTCLEAN += *.html *.js *.wasm load(qt_config) diff --git a/qmake/generators/makefile.cpp b/qmake/generators/makefile.cpp index 6ef5c0c3dc..ff936bf1ee 100644 --- a/qmake/generators/makefile.cpp +++ b/qmake/generators/makefile.cpp @@ -892,9 +892,8 @@ MakefileGenerator::processPrlFile(QString &file, bool baseOnly) bool MakefileGenerator::processPrlFileBase(QString &origFile, const QStringRef &origName, - const QStringRef &fixedBase, int slashOff) + const QStringRef &fixedBase, int /*slashOff*/) { - Q_UNUSED(slashOff) return processPrlFileCore(origFile, origName, fixedBase + Option::prl_ext); } @@ -1016,6 +1015,14 @@ MakefileGenerator::writePrlFile(QTextStream &t) for (ProStringList::Iterator it = libs.begin(); it != libs.end(); ++it) t << qv(project->values((*it).toKey())); t << Qt::endl; + + t << "QMAKE_PRL_LIBS_FOR_CMAKE = "; + QString sep; + for (ProStringList::Iterator it = libs.begin(); it != libs.end(); ++it) { + t << sep << project->values((*it).toKey()).join(';').replace('\\', "\\\\"); + sep = ';'; + } + t << Qt::endl; } } @@ -1113,7 +1120,8 @@ MakefileGenerator::writePrlFile() && project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty() && project->isActiveConfig("create_prl") && (project->first("TEMPLATE") == "lib" - || project->first("TEMPLATE") == "vclib") + || project->first("TEMPLATE") == "vclib" + || project->first("TEMPLATE") == "aux") && (!project->isActiveConfig("plugin") || project->isActiveConfig("static"))) { //write prl file QString local_prl = prlFileName(); QString prl = fileFixify(local_prl); @@ -2370,8 +2378,15 @@ MakefileGenerator::findSubDirsSubTargets() const st->profile = file; } } else { - if(!file.isEmpty() && !project->isActiveConfig("subdir_first_pro")) - st->profile = file.section(Option::dir_sep, -1) + Option::pro_ext; + if (!file.isEmpty() && !project->isActiveConfig("subdir_first_pro")) { + const QString baseName = file.section(Option::dir_sep, -1); + if (baseName.isEmpty()) { + warn_msg(WarnLogic, "Ignoring invalid SUBDIRS entry %s", + subdirs[subdir].toLatin1().constData()); + continue; + } + st->profile = baseName + Option::pro_ext; + } st->in_directory = file; } while(st->in_directory.endsWith(Option::dir_sep)) @@ -3356,42 +3371,44 @@ MakefileGenerator::writePkgConfigFile() if (!version.isEmpty()) t << "Version: " << version << Qt::endl; - // libs - t << "Libs: "; - QString pkgConfiglibName; - if (target_mode == TARG_MAC_MODE && project->isActiveConfig("lib_bundle")) { - if (libDir != QLatin1String("/Library/Frameworks")) - t << "-F${libdir} "; - ProString bundle; - if (!project->isEmpty("QMAKE_FRAMEWORK_BUNDLE_NAME")) - bundle = project->first("QMAKE_FRAMEWORK_BUNDLE_NAME"); - else - bundle = project->first("TARGET"); - int suffix = bundle.lastIndexOf(".framework"); - if (suffix != -1) - bundle = bundle.left(suffix); - t << "-framework "; - pkgConfiglibName = bundle.toQString(); - } else { - if (!project->values("QMAKE_DEFAULT_LIBDIRS").contains(libDir)) - t << "-L${libdir} "; - pkgConfiglibName = "-l" + project->first("QMAKE_ORIG_TARGET"); - if (project->isActiveConfig("shared")) - pkgConfiglibName += project->first("TARGET_VERSION_EXT").toQString(); - } - t << shellQuote(pkgConfiglibName) << " \n"; - - if (project->isActiveConfig("staticlib")) { - ProStringList libs; - libs << "LIBS"; // FIXME: this should not be conditional on staticlib - libs << "LIBS_PRIVATE"; - libs << "QMAKE_LIBS"; // FIXME: this should not be conditional on staticlib - libs << "QMAKE_LIBS_PRIVATE"; - libs << "QMAKE_LFLAGS_THREAD"; //not sure about this one, but what about things like -pthread? - t << "Libs.private:"; - for (ProStringList::ConstIterator it = libs.cbegin(); it != libs.cend(); ++it) - t << ' ' << fixLibFlags((*it).toKey()).join(' '); - t << Qt::endl; + if (project->first("TEMPLATE") == "lib") { + // libs + t << "Libs: "; + QString pkgConfiglibName; + if (target_mode == TARG_MAC_MODE && project->isActiveConfig("lib_bundle")) { + if (libDir != QLatin1String("/Library/Frameworks")) + t << "-F${libdir} "; + ProString bundle; + if (!project->isEmpty("QMAKE_FRAMEWORK_BUNDLE_NAME")) + bundle = project->first("QMAKE_FRAMEWORK_BUNDLE_NAME"); + else + bundle = project->first("TARGET"); + int suffix = bundle.lastIndexOf(".framework"); + if (suffix != -1) + bundle = bundle.left(suffix); + t << "-framework "; + pkgConfiglibName = bundle.toQString(); + } else { + if (!project->values("QMAKE_DEFAULT_LIBDIRS").contains(libDir)) + t << "-L${libdir} "; + pkgConfiglibName = "-l" + project->first("QMAKE_ORIG_TARGET"); + if (project->isActiveConfig("shared")) + pkgConfiglibName += project->first("TARGET_VERSION_EXT").toQString(); + } + t << shellQuote(pkgConfiglibName) << " \n"; + + if (project->isActiveConfig("staticlib")) { + ProStringList libs; + libs << "LIBS"; // FIXME: this should not be conditional on staticlib + libs << "LIBS_PRIVATE"; + libs << "QMAKE_LIBS"; // FIXME: this should not be conditional on staticlib + libs << "QMAKE_LIBS_PRIVATE"; + libs << "QMAKE_LFLAGS_THREAD"; //not sure about this one, but what about things like -pthread? + t << "Libs.private:"; + for (ProStringList::ConstIterator it = libs.cbegin(); it != libs.cend(); ++it) + t << ' ' << fixLibFlags((*it).toKey()).join(' '); + t << Qt::endl; + } } // flags @@ -3432,21 +3449,25 @@ QString MakefileGenerator::installMetaFile(const ProKey &replace_rule, const QSt QString ret; if (project->isEmpty(replace_rule) || project->isActiveConfig("no_sed_meta_install")) { - ret += "-$(INSTALL_FILE) " + escapeFilePath(src) + ' ' + escapeFilePath(dst); + ret += "$(INSTALL_FILE) " + escapeFilePath(src) + ' ' + escapeFilePath(dst); } else { - ret += "-$(SED)"; + QString sedargs; const ProStringList &replace_rules = project->values(replace_rule); for (int r = 0; r < replace_rules.size(); ++r) { const ProString match = project->first(ProKey(replace_rules.at(r) + ".match")), replace = project->first(ProKey(replace_rules.at(r) + ".replace")); if (!match.isEmpty() /*&& match != replace*/) { - ret += " -e " + shellQuote("s," + match + "," + replace + ",g"); + sedargs += " -e " + shellQuote("s," + match + "," + replace + ",g"); if (isWindowsShell() && project->first(ProKey(replace_rules.at(r) + ".CONFIG")).contains("path")) - ret += " -e " + shellQuote("s," + windowsifyPath(match.toQString()) + sedargs += " -e " + shellQuote("s," + windowsifyPath(match.toQString()) + "," + windowsifyPath(replace.toQString()) + ",gi"); } } - ret += ' ' + escapeFilePath(src) + " > " + escapeFilePath(dst); + if (sedargs.isEmpty()) { + ret += "$(INSTALL_FILE) " + escapeFilePath(src) + ' ' + escapeFilePath(dst); + } else { + ret += "$(SED) " + sedargs + ' ' + escapeFilePath(src) + " > " + escapeFilePath(dst); + } } return ret; } diff --git a/qmake/generators/makefiledeps.cpp b/qmake/generators/makefiledeps.cpp index 1aab1987d2..69a3217723 100644 --- a/qmake/generators/makefiledeps.cpp +++ b/qmake/generators/makefiledeps.cpp @@ -815,7 +815,7 @@ bool QMakeSourceFileInfo::findDeps(SourceFile *file) break; } cpp_state = InCode; - // ... and fall through to handle buffer[x] as such. + Q_FALLTHROUGH(); // to handle buffer[x] as such. case InCode: // matching quotes (string literals and character literals) if (buffer[x] == '\'' || buffer[x] == '"') { @@ -946,10 +946,12 @@ bool QMakeSourceFileInfo::findMocs(SourceFile *file) debug_msg(2, "findMocs: %s", file->file.local().toLatin1().constData()); int line_count = 1; - bool ignore[3] = { false, false, false }; // [0] for Q_OBJECT, [1] for Q_GADGET, [2] for Q_NAMESPACE + // [0] for Q_OBJECT, [1] for Q_GADGET, [2] for Q_NAMESPACE, [3] for Q_NAMESPACE_EXPORT + bool ignore[4] = { false, false, false, false }; /* qmake ignore Q_GADGET */ /* qmake ignore Q_OBJECT */ /* qmake ignore Q_NAMESPACE */ + /* qmake ignore Q_NAMESPACE_EXPORT */ for(int x = 0; x < buffer_len; x++) { #define SKIP_BSNL(pos) skipEscapedLineEnds(buffer, buffer_len, (pos), &line_count) x = SKIP_BSNL(x); @@ -988,6 +990,12 @@ bool QMakeSourceFileInfo::findMocs(SourceFile *file) file->file.real().toLatin1().constData(), line_count); x += 23; ignore[2] = true; + } else if (buffer_len >= (x + 30) && + !strncmp(buffer + x + 1, "make ignore Q_NAMESPACE_EXPORT", 30)) { + debug_msg(2, "Mocgen: %s:%d Found \"qmake ignore Q_NAMESPACE_EXPORT\"", + file->file.real().toLatin1().constData(), line_count); + x += 30; + ignore[3] = true; } } else if (buffer[x] == '*') { extralines = 0; @@ -1015,8 +1023,8 @@ bool QMakeSourceFileInfo::findMocs(SourceFile *file) int morelines = 0; int y = skipEscapedLineEnds(buffer, buffer_len, x + 1, &morelines); if (buffer[y] == 'Q') { - static const char interesting[][12] = { "Q_OBJECT", "Q_GADGET", "Q_NAMESPACE"}; - for (int interest = 0; interest < 3; ++interest) { + static const char interesting[][19] = { "Q_OBJECT", "Q_GADGET", "Q_NAMESPACE", "Q_NAMESPACE_EXPORT" }; + for (int interest = 0; interest < 4; ++interest) { if (ignore[interest]) continue; diff --git a/qmake/generators/projectgenerator.h b/qmake/generators/projectgenerator.h index ef6a76f0d0..374bad98c7 100644 --- a/qmake/generators/projectgenerator.h +++ b/qmake/generators/projectgenerator.h @@ -43,7 +43,7 @@ protected: void init() override; bool writeMakefile(QTextStream &) override; - QString escapeFilePath(const QString &path) const override { Q_UNUSED(path); Q_ASSERT(false); return QString(); } + QString escapeFilePath(const QString &) const override { Q_ASSERT(false); return QString(); } public: bool supportsMetaBuild() override { return false; } diff --git a/qmake/generators/unix/unixmake.cpp b/qmake/generators/unix/unixmake.cpp index dbcf2a9a77..ef55af365c 100644 --- a/qmake/generators/unix/unixmake.cpp +++ b/qmake/generators/unix/unixmake.cpp @@ -725,7 +725,7 @@ UnixMakefileGenerator::defaultInstall(const QString &t) } } } - if(project->first("TEMPLATE") == "lib") { + if (isAux || project->first("TEMPLATE") == "lib") { QStringList types; types << "prl" << "libtool" << "pkgconfig"; for(int i = 0; i < types.size(); ++i) { diff --git a/qmake/generators/unix/unixmake2.cpp b/qmake/generators/unix/unixmake2.cpp index 7d72347d37..c8efd0680b 100644 --- a/qmake/generators/unix/unixmake2.cpp +++ b/qmake/generators/unix/unixmake2.cpp @@ -43,12 +43,15 @@ void UnixMakefileGenerator::writePrlFile(QTextStream &t) { MakefileGenerator::writePrlFile(t); + const ProString tmplt = project->first("TEMPLATE"); + if (tmplt != "lib" && tmplt != "aux") + return; // libtool support - if(project->isActiveConfig("create_libtool") && project->first("TEMPLATE") == "lib") { //write .la + if (project->isActiveConfig("create_libtool")) { writeLibtoolFile(); } // pkg-config support - if(project->isActiveConfig("create_pc") && project->first("TEMPLATE") == "lib") + if (project->isActiveConfig("create_pc")) writePkgConfigFile(); } @@ -164,6 +167,16 @@ static QString rfc1034Identifier(const QString &str) return s; } +static QString escapeDir(const QString &dir) +{ + // When building on non-MSys MinGW, the path ends with a backslash, which + // GNU make will interpret that as a line continuation. Doubling the backslash + // avoids the problem, at the cost of the variable containing *both* backslashes. + if (dir.endsWith('\\')) + return dir + '\\'; + return dir; +} + void UnixMakefileGenerator::writeMakeParts(QTextStream &t) { @@ -226,7 +239,7 @@ UnixMakefileGenerator::writeMakeParts(QTextStream &t) t << "####### Output directory\n\n"; // This is used in commands by some .prf files. if (! project->values("OBJECTS_DIR").isEmpty()) - t << "OBJECTS_DIR = " << fileVar("OBJECTS_DIR") << Qt::endl; + t << "OBJECTS_DIR = " << escapeDir(fileVar("OBJECTS_DIR")) << Qt::endl; else t << "OBJECTS_DIR = ./\n"; t << Qt::endl; @@ -272,13 +285,7 @@ UnixMakefileGenerator::writeMakeParts(QTextStream &t) t << "DIST = " << valList(fileFixify(project->values("DISTFILES").toQStringList())) << " " << fileVarList("HEADERS") << ' ' << fileVarList("SOURCES") << Qt::endl; t << "QMAKE_TARGET = " << fileVar("QMAKE_ORIG_TARGET") << Qt::endl; - QString destd = fileVar("DESTDIR"); - // When building on non-MSys MinGW, the path ends with a backslash, which - // GNU make will interpret that as a line continuation. Doubling the backslash - // avoids the problem, at the cost of the variable containing *both* backslashes. - if (destd.endsWith('\\')) - destd += '\\'; - t << "DESTDIR = " << destd << Qt::endl; + t << "DESTDIR = " << escapeDir(fileVar("DESTDIR")) << Qt::endl; t << "TARGET = " << fileVar("TARGET") << Qt::endl; if(project->isActiveConfig("plugin")) { t << "TARGETD = " << fileVar("TARGET") << Qt::endl; @@ -1194,7 +1201,8 @@ void UnixMakefileGenerator::init2() project->values("QMAKE_FRAMEWORK_VERSION").append(project->first("VER_MAJ")); if (project->first("TEMPLATE") == "aux") { - // nothing + project->values("PRL_TARGET") = + project->values("TARGET").first().prepend(project->first("QMAKE_PREFIX_STATICLIB")); } else if (!project->values("QMAKE_APP_FLAG").isEmpty()) { if(!project->isEmpty("QMAKE_BUNDLE")) { ProString bundle_loc = project->first("QMAKE_BUNDLE_LOCATION"); diff --git a/qmake/generators/win32/mingw_make.cpp b/qmake/generators/win32/mingw_make.cpp index 325823e1d9..6f80b93307 100644 --- a/qmake/generators/win32/mingw_make.cpp +++ b/qmake/generators/win32/mingw_make.cpp @@ -250,7 +250,7 @@ void MingwMakefileGenerator::init() } if(project->isActiveConfig("dll")) { - project->values("QMAKE_CLEAN").append(project->first("MINGW_IMPORT_LIB")); + project->values("QMAKE_DISTCLEAN").append(project->first("MINGW_IMPORT_LIB")); } } diff --git a/qmake/generators/win32/msvc_objectmodel.cpp b/qmake/generators/win32/msvc_objectmodel.cpp index 45f23e52bd..b6fe683d2c 100644 --- a/qmake/generators/win32/msvc_objectmodel.cpp +++ b/qmake/generators/win32/msvc_objectmodel.cpp @@ -1991,6 +1991,7 @@ bool VCMIDLTool::parseOption(const char* option) break; case 0x5eb7af2: // /header filename offset = 5; + Q_FALLTHROUGH(); case 0x0000358: // /h filename HeaderFileName = option + offset + 3; break; diff --git a/qmake/generators/win32/msvc_vcproj.cpp b/qmake/generators/win32/msvc_vcproj.cpp index fd53ec2a6e..b6f7f20564 100644 --- a/qmake/generators/win32/msvc_vcproj.cpp +++ b/qmake/generators/win32/msvc_vcproj.cpp @@ -1521,6 +1521,18 @@ void VcprojGenerator::initDistributionFiles() vcProject.DistributionFiles.Config = &(vcProject.Configuration); } +QString VcprojGenerator::extraCompilerName(const ProString &extraCompiler, + const QStringList &inputs, + const QStringList &outputs) +{ + QString name = project->values(ProKey(extraCompiler + ".name")).join(' '); + if (name.isEmpty()) + name = extraCompiler.toQString(); + else + name = replaceExtraCompilerVariables(name, inputs, outputs, NoShell); + return name; +} + void VcprojGenerator::initExtraCompilerOutputs() { ProStringList otherFilters; @@ -1538,13 +1550,16 @@ void VcprojGenerator::initExtraCompilerOutputs() << "YACCSOURCES"; const ProStringList &quc = project->values("QMAKE_EXTRA_COMPILERS"); for (ProStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) { - ProString extracompilerName = project->first(ProKey(*it + ".name")); - if (extracompilerName.isEmpty()) - extracompilerName = (*it); + const ProStringList &inputVars = project->values(ProKey(*it + ".input")); + ProStringList inputFiles; + for (auto var : inputVars) + inputFiles.append(project->values(var.toKey())); + const ProStringList &outputs = project->values(ProKey(*it + ".output")); // Create an extra compiler filter and add the files VCFilter extraCompile; - extraCompile.Name = extracompilerName.toQString(); + extraCompile.Name = extraCompilerName(it->toQString(), inputFiles.toQStringList(), + outputs.toQStringList()); extraCompile.ParseFiles = _False; extraCompile.Filter = ""; extraCompile.Guid = QString(_GUIDExtraCompilerFiles) + "-" + (*it); @@ -1557,14 +1572,14 @@ void VcprojGenerator::initExtraCompilerOutputs() if (!outputVar.isEmpty() && otherFilters.contains(outputVar)) continue; - QString tmp_out = project->first(ProKey(*it + ".output")).toQString(); + QString tmp_out = project->first(outputs.first().toKey()).toQString(); if (project->values(ProKey(*it + ".CONFIG")).indexOf("combine") != -1) { // Combined output, only one file result extraCompile.addFile(Option::fixPathToTargetOS( replaceExtraCompilerVariables(tmp_out, QString(), QString(), NoShell), false)); } else { // One output file per input - const ProStringList &tmp_in = project->values(project->first(ProKey(*it + ".input")).toKey()); + const ProStringList &tmp_in = project->values(inputVars.first().toKey()); for (int i = 0; i < tmp_in.count(); ++i) { const QString &filename = tmp_in.at(i).toQString(); if (extraCompilerSources.contains(filename) && !otherFiltersContain(filename)) @@ -1577,7 +1592,6 @@ void VcprojGenerator::initExtraCompilerOutputs() // build steps there. So, we turn it around and add it to the input files instead, // provided that the input file variable is not handled already (those in otherFilters // are handled, so we avoid them). - const ProStringList &inputVars = project->values(ProKey(*it + ".input")); for (const ProString &inputVar : inputVars) { if (!otherFilters.contains(inputVar)) { const ProStringList &tmp_in = project->values(inputVar.toKey()); diff --git a/qmake/generators/win32/msvc_vcproj.h b/qmake/generators/win32/msvc_vcproj.h index 55d36c3762..8f38252274 100644 --- a/qmake/generators/win32/msvc_vcproj.h +++ b/qmake/generators/win32/msvc_vcproj.h @@ -74,6 +74,8 @@ protected: bool doDepends() const override { return false; } // Never necessary using Win32MakefileGenerator::replaceExtraCompilerVariables; QString replaceExtraCompilerVariables(const QString &, const QStringList &, const QStringList &, ReplaceFor) override; + QString extraCompilerName(const ProString &extraCompiler, const QStringList &inputs, + const QStringList &outputs); bool supportsMetaBuild() override { return true; } bool supportsMergedBuilds() override { return true; } bool mergeBuildProject(MakefileGenerator *other) override; diff --git a/qmake/library/proitems.cpp b/qmake/library/proitems.cpp index 8bbde9f8c0..41bed69f00 100644 --- a/qmake/library/proitems.cpp +++ b/qmake/library/proitems.cpp @@ -517,4 +517,9 @@ ProKey ProFile::getHashStr(const ushort *&tPtr) return ret; } +QDebug operator<<(QDebug debug, const ProString &str) +{ + return debug << str.toQString(); +} + QT_END_NAMESPACE diff --git a/qmake/library/proitems.h b/qmake/library/proitems.h index 6882f2802f..cc65421556 100644 --- a/qmake/library/proitems.h +++ b/qmake/library/proitems.h @@ -31,6 +31,7 @@ #include "qmake_global.h" +#include <qdebug.h> #include <qstring.h> #include <qvector.h> #include <qhash.h> @@ -468,6 +469,8 @@ struct ProFunctionDefs { QHash<ProKey, ProFunctionDef> replaceFunctions; }; +QDebug operator<<(QDebug debug, const ProString &str); + QT_END_NAMESPACE #endif // PROITEMS_H diff --git a/qmake/library/qmakeevaluator.cpp b/qmake/library/qmakeevaluator.cpp index c9dc7bd80b..ba617932ce 100644 --- a/qmake/library/qmakeevaluator.cpp +++ b/qmake/library/qmakeevaluator.cpp @@ -1130,35 +1130,48 @@ bool QMakeEvaluator::prepareProject(const QString &inDir) } superdir = qdfi.path(); } - QString sdir = inDir; QString dir = m_outputDir; forever { - conffile = sdir + QLatin1String("/.qmake.conf"); - if (!m_vfs->exists(conffile, flags)) - conffile.clear(); cachefile = dir + QLatin1String("/.qmake.cache"); if (!m_vfs->exists(cachefile, flags)) cachefile.clear(); - if (!conffile.isEmpty() || !cachefile.isEmpty()) { - if (dir != sdir) - m_sourceRoot = sdir; + if (!cachefile.isEmpty()) { m_buildRoot = dir; break; } if (dir == superdir) goto no_cache; - QFileInfo qsdfi(sdir); QFileInfo qdfi(dir); - if (qsdfi.isRoot() || qdfi.isRoot()) - goto no_cache; - sdir = qsdfi.path(); + if (qdfi.isRoot()) { + cachefile.clear(); + break; + } dir = qdfi.path(); } + QString sdir = inDir; + forever { + conffile = sdir + QLatin1String("/.qmake.conf"); + if (!m_vfs->exists(conffile, flags)) + conffile.clear(); + if (!conffile.isEmpty()) { + if (sdir != m_buildRoot) + m_sourceRoot = sdir; + break; + } + QFileInfo qsdfi(sdir); + if (qsdfi.isRoot()) { + conffile.clear(); + break; + } + sdir = qsdfi.path(); + } } else { m_buildRoot = QFileInfo(cachefile).path(); } - m_conffile = QDir::cleanPath(conffile); - m_cachefile = QDir::cleanPath(cachefile); + if (!conffile.isEmpty()) + m_conffile = QDir::cleanPath(conffile); + if (!cachefile.isEmpty()) + m_cachefile = QDir::cleanPath(cachefile); } no_cache: @@ -1711,7 +1724,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFunction( if (m_valuemapStack.size() >= 100) { evalError(fL1S("Ran into infinite recursion (depth > 100).")); - vr = ReturnFalse; + vr = ReturnError; } else { m_valuemapStack.push(ProValueMap()); m_locationStack.push(m_current); 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<typename U> VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) { } + + T* allocate(size_t n) { return VmaAllocateArray<T>(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<typename U> + bool operator==(const VmaStlAllocator<U>& 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<BlockInfo*>(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<VmaBlockDefragmentationContext>(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<AllocInfo>(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<VmaPointerLess>(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<VmaPointerLess>(*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/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/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..2355de091f --- /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 + +<b>Version 2.2.0</b> (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 + +- <b>User guide</b> + - \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 `<vulkan/vulkan.h>`, which in turn +includes `<windows.h>` 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: + +<i>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.</i> + +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. + + + +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. + + + +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. + + + +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. + + + +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 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. + + + +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 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. + + + +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<VkBuffer> buffers; +std::vector<VmaAllocation> allocations; + + +const uint32_t allocCount = (uint32_t)allocations.size(); +std::vector<VkBool32> 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<VkBuffer> buffers; +std::vector<VmaAllocation> allocations; + + +const uint32_t allocCount = (uint32_t)allocations.size(); +std::vector<VkBool32> 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. + +<b>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?</b> + +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. + +<b>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?</b> + +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). + +<b>Q: How do you inform the library when new frame starts?</b> + +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. + +<b>Q: Can I touch allocation that cannot become lost?</b> + +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`). + + + +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 + + + +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 + +<b>To record sequence of calls to a file:</b> Fill in +VmaAllocatorCreateInfo::pRecordSettings member while creating #VmaAllocator +object. File is opened and written during whole lifetime of the allocator. + +<b>To replay file:</b> 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 + +<b>Documentation of file format</b> 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 + +<b>When:</b> +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)"). + +<b>What to do:</b> +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 + +<b>When:</b> +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. + +<b>What to do:</b> +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 + +<b>When:</b> +Any resources that change frequently (aka "dynamic"), e.g. every frame or every draw call, +written on CPU, read on GPU. + +<b>What to do:</b> +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 + +<b>When:</b> +Resources that contain data written by GPU that you want to read back on CPU, +e.g. results of some computations. + +<b>What to do:</b> +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 <vulkan/vulkan.h> +#endif + +#if VMA_RECORDING_ENABLED + #include <windows.h> +#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 <cstdint> +#include <cstdlib> +#include <cstring> + +/******************************************************************************* +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 <vector> +#endif + +#if VMA_USE_STL_UNORDERED_MAP + #include <unordered_map> +#endif + +#if VMA_USE_STL_LIST + #include <list> +#endif + +/* +Following headers are used in this CONFIGURATION section only, so feel free to +remove them if not needed. +*/ +#include <cassert> // for assert +#include <algorithm> // for min, max +#include <mutex> +#include <atomic> // 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 <cstdlib> +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 <cstdlib> +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 <malloc.h> + +// 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<unsigned int>(num)); + } + static inline void VmaUint64ToStr(char* outStr, size_t strLen, uint64_t num) + { + snprintf(outStr, strLen, "%llu", static_cast<unsigned long long>(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 <shared_mutex> + 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) && !defined(__MINGW32__) + // 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<uint32_t> +#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 <typename T> +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 <typename T> +static inline T VmaAlignDown(T val, T align) +{ + return val / align * align; +} + +// Division with mathematical rounding to nearest number. +template <typename T> +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 <typename T> +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<typename Iterator, typename Compare> +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<typename Iterator, typename Compare> +void VmaQuickSort(Iterator beg, Iterator end, Compare cmp) +{ + if(beg < end) + { + Iterator it = VmaQuickSortPartition<Iterator, Compare>(beg, end, cmp); + VmaQuickSort<Iterator, Compare>(beg, it, cmp); + VmaQuickSort<Iterator, Compare>(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 <typename CmpLess, typename IterT, typename KeyT> +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<typename T> +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<typename T> +static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks) +{ + return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T)); +} + +template<typename T> +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<type>(allocator))(type) + +#define vma_new_array(allocator, type, count) new(VmaAllocateArray<type>((allocator), (count)))(type) + +template<typename T> +static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr) +{ + ptr->~T(); + VmaFree(pAllocationCallbacks, ptr); +} + +template<typename T> +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<typename T> +class VmaStlAllocator +{ +public: + const VkAllocationCallbacks* const m_pCallbacks; + typedef T value_type; + + VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) { } + template<typename U> VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) { } + + T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); } + void deallocate(T* p, size_t /*n*/) { VmaFree(m_pCallbacks, p); } + + template<typename U> + bool operator==(const VmaStlAllocator<U>& rhs) const + { + return m_pCallbacks == rhs.m_pCallbacks; + } + template<typename U> + bool operator!=(const VmaStlAllocator<U>& rhs) const + { + return m_pCallbacks != rhs.m_pCallbacks; + } + + VmaStlAllocator& operator=(const VmaStlAllocator& x) = delete; +}; + +#if VMA_USE_STL_VECTOR + +#define VmaVector std::vector + +template<typename T, typename allocatorT> +static void VmaVectorInsert(std::vector<T, allocatorT>& vec, size_t index, const T& item) +{ + vec.insert(vec.begin() + index, item); +} + +template<typename T, typename allocatorT> +static void VmaVectorRemove(std::vector<T, allocatorT>& 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<typename T, typename AllocatorT> +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<T>(allocator.m_pCallbacks, count) : VMA_NULL), + m_Count(count), + m_Capacity(count) + { + } + + VmaVector(const VmaVector<T, AllocatorT>& src) : + m_Allocator(src.m_Allocator), + m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(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<T, AllocatorT>& 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<T>(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<T>(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<typename T, typename allocatorT> +static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item) +{ + vec.insert(index, item); +} + +template<typename T, typename allocatorT> +static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index) +{ + vec.remove(index); +} + +#endif // #if VMA_USE_STL_VECTOR + +template<typename CmpLess, typename VectorT> +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<typename CmpLess, typename VectorT> +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<typename CmpLess, typename IterT, typename KeyT> +IterT VmaVectorFindSorted(const IterT& beg, const IterT& end, const KeyT& value) +{ + CmpLess comparator; + IterT it = VmaBinaryFindFirstNotLess<CmpLess, IterT, KeyT>( + 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<typename T> +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<ItemBlock> > m_ItemBlocks; + + ItemBlock& CreateNewBlock(); +}; + +template<typename T> +VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock) : + m_pAllocationCallbacks(pAllocationCallbacks), + m_ItemsPerBlock(itemsPerBlock), + m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks)) +{ + VMA_ASSERT(itemsPerBlock > 0); +} + +template<typename T> +VmaPoolAllocator<T>::~VmaPoolAllocator() +{ + Clear(); +} + +template<typename T> +void VmaPoolAllocator<T>::Clear() +{ + for(size_t i = m_ItemBlocks.size(); i--; ) + vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemsPerBlock); + m_ItemBlocks.clear(); +} + +template<typename T> +T* VmaPoolAllocator<T>::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<typename T> +void VmaPoolAllocator<T>::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<uint32_t>(pItemPtr - block.pItems); + pItemPtr->NextFreeIndex = block.FirstFreeIndex; + block.FirstFreeIndex = index; + return; + } + } + VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool."); +} + +template<typename T> +typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::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<typename T> +struct VmaListItem +{ + VmaListItem* pPrev; + VmaListItem* pNext; + T Value; +}; + +// Doubly linked list. +template<typename T> +class VmaRawList +{ + VMA_CLASS_NO_COPY(VmaRawList) +public: + typedef VmaListItem<T> 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<ItemType> m_ItemAllocator; + ItemType* m_pFront; + ItemType* m_pBack; + size_t m_Count; +}; + +template<typename T> +VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) : + m_pAllocationCallbacks(pAllocationCallbacks), + m_ItemAllocator(pAllocationCallbacks, 128), + m_pFront(VMA_NULL), + m_pBack(VMA_NULL), + m_Count(0) +{ +} + +template<typename T> +VmaRawList<T>::~VmaRawList() +{ + // Intentionally not calling Clear, because that would be unnecessary + // computations to return all items to m_ItemAllocator as free. +} + +template<typename T> +void VmaRawList<T>::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<typename T> +VmaListItem<T>* VmaRawList<T>::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<typename T> +VmaListItem<T>* VmaRawList<T>::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<typename T> +VmaListItem<T>* VmaRawList<T>::PushBack(const T& value) +{ + ItemType* const pNewItem = PushBack(); + pNewItem->Value = value; + return pNewItem; +} + +template<typename T> +VmaListItem<T>* VmaRawList<T>::PushFront(const T& value) +{ + ItemType* const pNewItem = PushFront(); + pNewItem->Value = value; + return pNewItem; +} + +template<typename T> +void VmaRawList<T>::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<typename T> +void VmaRawList<T>::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<typename T> +void VmaRawList<T>::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<typename T> +VmaListItem<T>* VmaRawList<T>::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<typename T> +VmaListItem<T>* VmaRawList<T>::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<typename T> +VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value) +{ + ItemType* const newItem = InsertBefore(pItem); + newItem->Value = value; + return newItem; +} + +template<typename T> +VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value) +{ + ItemType* const newItem = InsertAfter(pItem); + newItem->Value = value; + return newItem; +} + +template<typename T, typename AllocatorT> +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<T>* m_pList; + VmaListItem<T>* m_pItem; + + iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) : + m_pList(pList), + m_pItem(pItem) + { + } + + friend class VmaList<T, AllocatorT>; + }; + + 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<T>* pList, const VmaListItem<T>* pItem) : + m_pList(pList), + m_pItem(pItem) + { + } + + const VmaRawList<T>* m_pList; + const VmaListItem<T>* m_pItem; + + friend class VmaList<T, AllocatorT>; + }; + + 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<T> 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<KeyT>, std::equal_to<KeyT>, VmaStlAllocator< std::pair<KeyT, ValueT> > > + +#else // #if VMA_USE_STL_UNORDERED_MAP + +template<typename T1, typename T2> +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<typename KeyT, typename ValueT> +class VmaMap +{ +public: + typedef VmaPair<KeyT, ValueT> PairType; + typedef PairType* iterator; + + VmaMap(const VmaStlAllocator<PairType>& 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<PairType> > m_Vector; +}; + +#define VMA_MAP_TYPE(KeyT, ValueT) VmaMap<KeyT, ValueT> + +template<typename FirstT, typename SecondT> +struct VmaPairFirstLess +{ + bool operator()(const VmaPair<FirstT, SecondT>& lhs, const VmaPair<FirstT, SecondT>& rhs) const + { + return lhs.first < rhs.first; + } + bool operator()(const VmaPair<FirstT, SecondT>& lhs, const FirstT& rhsFirst) const + { + return lhs.first < rhsFirst; + } +}; + +template<typename KeyT, typename ValueT> +void VmaMap<KeyT, ValueT>::insert(const PairType& pair) +{ + const size_t indexToInsert = VmaBinaryFindFirstNotLess( + m_Vector.data(), + m_Vector.data() + m_Vector.size(), + pair, + VmaPairFirstLess<KeyT, ValueT>()) - m_Vector.data(); + VmaVectorInsert(m_Vector, indexToInsert, pair); +} + +template<typename KeyT, typename ValueT> +VmaPair<KeyT, ValueT>* VmaMap<KeyT, ValueT>::find(const KeyT& key) +{ + PairType* it = VmaBinaryFindFirstNotLess( + m_Vector.data(), + m_Vector.data() + m_Vector.size(), + key, + VmaPairFirstLess<KeyT, ValueT>()); + if((it != m_Vector.end()) && (it->first == key)) + { + return it; + } + else + { + return m_Vector.end(); + } +} + +template<typename KeyT, typename ValueT> +void VmaMap<KeyT, ValueT>::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 De |