summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@qt.io>2019-06-26 11:32:55 +0200
committerSimon Hausmann <simon.hausmann@qt.io>2019-06-26 11:33:19 +0200
commit7810209ba32fcebfa90b081670595daaa5333a78 (patch)
tree98410d5cc7d9d762bbde0cb4eae8041aa6e2e542
parent6f2eabb54e3f827ebd2b90f68144b86342933869 (diff)
parent88625709058b386ee74cca536c6c5556159c99fa (diff)
Merge remote-tracking branch 'origin/dev' into wip/qt6
-rw-r--r--.gitignore2
-rw-r--r--configure.json3
-rw-r--r--dist/changes-5.12.483
-rw-r--r--dist/changes-5.13.0301
-rw-r--r--examples/network/downloadmanager/downloadmanager.cpp4
-rw-r--r--examples/network/downloadmanager/downloadmanager.h2
-rw-r--r--examples/network/secureudpserver/server.cpp23
-rw-r--r--examples/network/secureudpserver/server.h8
-rw-r--r--examples/network/torrent/ratecontroller.cpp4
-rw-r--r--examples/network/torrent/ratecontroller.h4
-rw-r--r--examples/opengl/qopenglwidget/glwidget.h4
-rw-r--r--examples/qtconcurrent/wordcount/main.cpp14
-rw-r--r--examples/widgets/doc/src/application.qdoc9
-rw-r--r--examples/widgets/doc/src/classwizard.qdoc2
-rw-r--r--examples/widgets/graphicsview/boxes/scene.cpp2
-rw-r--r--examples/widgets/graphicsview/boxes/scene.h2
-rw-r--r--examples/widgets/mainwindows/application/mainwindow.cpp32
-rw-r--r--examples/widgets/mainwindows/mdi/mdichild.cpp27
-rw-r--r--examples/widgets/mainwindows/sdi/mainwindow.cpp27
-rw-r--r--examples/widgets/painting/deform/pathdeform.h4
-rw-r--r--mkspecs/android-clang/qmake.conf2
-rw-r--r--mkspecs/devices/linux-rasp-pi3-vc4-g++/qmake.conf2
-rw-r--r--mkspecs/features/android/android.prf18
-rw-r--r--mkspecs/features/data/cmake/Qt5BasicConfig.cmake.in11
-rw-r--r--mkspecs/features/exclusive_builds.prf4
-rw-r--r--mkspecs/features/lex.prf50
-rw-r--r--mkspecs/features/ltcg.prf9
-rw-r--r--mkspecs/features/qmltestcase.prf10
-rw-r--r--mkspecs/features/qt_build_config.prf4
-rw-r--r--mkspecs/features/qt_common.prf10
-rw-r--r--mkspecs/features/qt_configure.prf10
-rw-r--r--mkspecs/features/qt_module.prf5
-rw-r--r--mkspecs/features/qt_module_headers.prf4
-rw-r--r--mkspecs/features/qt_prefix_build_check.prf21
-rw-r--r--mkspecs/features/sanitizer.prf25
-rw-r--r--mkspecs/features/testcase.prf16
-rw-r--r--mkspecs/features/yacc.prf59
-rw-r--r--mkspecs/wasm-emscripten/qmake.conf6
-rw-r--r--qmake/generators/mac/pbuilder_pbx.cpp2
-rw-r--r--qmake/generators/makefile.cpp102
-rw-r--r--qmake/generators/makefiledeps.cpp2
-rw-r--r--qmake/generators/projectgenerator.h2
-rw-r--r--qmake/generators/unix/unixmake.cpp2
-rw-r--r--qmake/generators/unix/unixmake2.cpp30
-rw-r--r--qmake/generators/win32/msvc_objectmodel.cpp1
-rw-r--r--qmake/generators/win32/msvc_vcproj.cpp28
-rw-r--r--qmake/generators/win32/msvc_vcproj.h2
-rw-r--r--qmake/generators/win32/winmakefile.cpp2
-rw-r--r--qmake/library/qmakeevaluator.cpp41
-rw-r--r--src/3rdparty/VulkanMemoryAllocator.pri1
-rw-r--r--src/3rdparty/VulkanMemoryAllocator/LICENSE.txt19
-rw-r--r--src/3rdparty/VulkanMemoryAllocator/patches/0001-Avoid-compiler-warnings.patch402
-rw-r--r--src/3rdparty/VulkanMemoryAllocator/patches/0002-Fix-gcc8-warning.patch14
-rw-r--r--src/3rdparty/VulkanMemoryAllocator/patches/0003-Disable-srwlock-for-mingw.patch13
-rw-r--r--src/3rdparty/VulkanMemoryAllocator/qt_attribution.json16
-rw-r--r--src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h16790
-rw-r--r--src/3rdparty/angle/src/libEGL/libEGL_mingw32.def142
-rw-r--r--src/3rdparty/angle/src/libEGL/libEGLd_mingw32.def142
-rw-r--r--src/3rdparty/angle/src/libGLESv2/libGLESv2_mingw32.def805
-rw-r--r--src/3rdparty/angle/src/libGLESv2/libGLESv2d_mingw32.def805
-rw-r--r--src/3rdparty/sqlite/qt_attribution.json4
-rw-r--r--src/3rdparty/sqlite/sqlite3.c8689
-rw-r--r--src/3rdparty/sqlite/sqlite3.h61
-rw-r--r--src/android/templates/build.gradle2
-rw-r--r--src/concurrent/qtconcurrentiteratekernel.h8
-rw-r--r--src/concurrent/qtconcurrentrun.cpp16
-rw-r--r--src/concurrent/qtconcurrentthreadengine.cpp10
-rw-r--r--src/corelib/Qt5CoreMacros.cmake5
-rw-r--r--src/corelib/animation/qabstractanimation.cpp4
-rw-r--r--src/corelib/animation/qanimationgroup.cpp25
-rw-r--r--src/corelib/animation/qanimationgroup_p.h2
-rw-r--r--src/corelib/animation/qsequentialanimationgroup.cpp3
-rw-r--r--src/corelib/animation/qvariantanimation.cpp4
-rw-r--r--src/corelib/codecs/qlatincodec.cpp4
-rw-r--r--src/corelib/codecs/qsimplecodec.cpp6
-rw-r--r--src/corelib/codecs/qtextcodec.cpp24
-rw-r--r--src/corelib/doc/snippets/code/src_corelib_tools_qregularexpression.cpp12
-rw-r--r--src/corelib/doc/src/cmake-macros.qdoc2
-rw-r--r--src/corelib/doc/src/dontdocument.qdoc41
-rw-r--r--src/corelib/global/global.pri15
-rw-r--r--src/corelib/global/qfloat16.cpp1
-rw-r--r--src/corelib/global/qfloat16tables.cpp3266
-rw-r--r--src/corelib/global/qglobal.cpp11
-rw-r--r--src/corelib/global/qglobal.h2
-rw-r--r--src/corelib/global/qglobalstatic.h14
-rw-r--r--src/corelib/global/qlibraryinfo.cpp35
-rw-r--r--src/corelib/global/qlibraryinfo.h1
-rw-r--r--src/corelib/global/qlogging.cpp79
-rw-r--r--src/corelib/global/qmalloc.cpp8
-rw-r--r--src/corelib/global/qrandom.cpp4
-rw-r--r--src/corelib/io/qabstractfileengine.cpp10
-rw-r--r--src/corelib/io/qbuffer.cpp2
-rw-r--r--src/corelib/io/qdebug.cpp36
-rw-r--r--src/corelib/io/qdebug.h9
-rw-r--r--src/corelib/io/qdir.cpp4
-rw-r--r--src/corelib/io/qfile.cpp8
-rw-r--r--src/corelib/io/qfiledevice.cpp4
-rw-r--r--src/corelib/io/qfileinfo.cpp16
-rw-r--r--src/corelib/io/qfileselector.cpp11
-rw-r--r--src/corelib/io/qfilesystemengine.cpp4
-rw-r--r--src/corelib/io/qfilesystemengine_unix.cpp4
-rw-r--r--src/corelib/io/qfilesystemiterator_unix.cpp6
-rw-r--r--src/corelib/io/qfilesystemwatcher_polling.cpp17
-rw-r--r--src/corelib/io/qfilesystemwatcher_win.cpp6
-rw-r--r--src/corelib/io/qfsfileengine.cpp16
-rw-r--r--src/corelib/io/qfsfileengine_unix.cpp20
-rw-r--r--src/corelib/io/qiodevice.cpp2
-rw-r--r--src/corelib/io/qipaddress.cpp10
-rw-r--r--src/corelib/io/qipaddress_p.h2
-rw-r--r--src/corelib/io/qloggingcategory.cpp18
-rw-r--r--src/corelib/io/qloggingcategory.h16
-rw-r--r--src/corelib/io/qloggingregistry.cpp4
-rw-r--r--src/corelib/io/qprocess_p.h2
-rw-r--r--src/corelib/io/qresource.cpp18
-rw-r--r--src/corelib/io/qsavefile.cpp12
-rw-r--r--src/corelib/io/qstandardpaths_android.cpp2
-rw-r--r--src/corelib/io/qstorageinfo_unix.cpp3
-rw-r--r--src/corelib/io/qtemporaryfile.cpp6
-rw-r--r--src/corelib/io/qurl.cpp67
-rw-r--r--src/corelib/io/qurlidna.cpp6
-rw-r--r--src/corelib/io/qurlquery.cpp14
-rw-r--r--src/corelib/io/qurlrecode.cpp2
-rw-r--r--src/corelib/itemmodels/qabstractitemmodel.cpp2
-rw-r--r--src/corelib/kernel/qabstracteventdispatcher.cpp2
-rw-r--r--src/corelib/kernel/qbasictimer.cpp2
-rw-r--r--src/corelib/kernel/qcore_mac_objc.mm31
-rw-r--r--src/corelib/kernel/qcore_mac_p.h71
-rw-r--r--src/corelib/kernel/qcore_unix_p.h4
-rw-r--r--src/corelib/kernel/qcoreapplication.cpp47
-rw-r--r--src/corelib/kernel/qcoreapplication_win.cpp12
-rw-r--r--src/corelib/kernel/qcoreevent.cpp6
-rw-r--r--src/corelib/kernel/qcoreglobaldata.cpp4
-rw-r--r--src/corelib/kernel/qelapsedtimer.cpp2
-rw-r--r--src/corelib/kernel/qeventdispatcher_glib.cpp6
-rw-r--r--src/corelib/kernel/qeventdispatcher_unix.cpp8
-rw-r--r--src/corelib/kernel/qeventdispatcher_win.cpp14
-rw-r--r--src/corelib/kernel/qeventloop.cpp10
-rw-r--r--src/corelib/kernel/qeventloop_p.h4
-rw-r--r--src/corelib/kernel/qjni.cpp8
-rw-r--r--src/corelib/kernel/qmetatype.cpp74
-rw-r--r--src/corelib/kernel/qmetatype.h9
-rw-r--r--src/corelib/kernel/qmetatype_p.h16
-rw-r--r--src/corelib/kernel/qmetatypeswitcher_p.h2
-rw-r--r--src/corelib/kernel/qobject.cpp282
-rw-r--r--src/corelib/kernel/qobject.h7
-rw-r--r--src/corelib/kernel/qobject_p.h28
-rw-r--r--src/corelib/kernel/qobjectdefs.h4
-rw-r--r--src/corelib/kernel/qpointer.h3
-rw-r--r--src/corelib/kernel/qpoll.cpp9
-rw-r--r--src/corelib/kernel/qsharedmemory.cpp19
-rw-r--r--src/corelib/kernel/qsharedmemory.h29
-rw-r--r--src/corelib/kernel/qsharedmemory_p.h12
-rw-r--r--src/corelib/kernel/qsharedmemory_unix.cpp7
-rw-r--r--src/corelib/kernel/qsharedmemory_win.cpp5
-rw-r--r--src/corelib/kernel/qsocketnotifier.cpp6
-rw-r--r--src/corelib/kernel/qsystemerror.cpp2
-rw-r--r--src/corelib/kernel/qsystemsemaphore_systemv.cpp6
-rw-r--r--src/corelib/kernel/qvariant.cpp46
-rw-r--r--src/corelib/kernel/qvariant.h2
-rw-r--r--src/corelib/kernel/qwineventnotifier.cpp6
-rw-r--r--src/corelib/mimetypes/qmimetype.cpp26
-rw-r--r--src/corelib/mimetypes/qmimetype.h2
-rw-r--r--src/corelib/plugin/qlibrary.cpp16
-rw-r--r--src/corelib/plugin/qlibrary_p.h2
-rw-r--r--src/corelib/plugin/qlibrary_unix.cpp12
-rw-r--r--src/corelib/serialization/qcborvalue.cpp16
-rw-r--r--src/corelib/serialization/qdatastream.cpp14
-rw-r--r--src/corelib/serialization/qjson_p.h2
-rw-r--r--src/corelib/serialization/qjsonarray.cpp8
-rw-r--r--src/corelib/serialization/qjsoncbor.cpp10
-rw-r--r--src/corelib/serialization/qjsondocument.cpp10
-rw-r--r--src/corelib/serialization/qjsonobject.cpp10
-rw-r--r--src/corelib/serialization/qjsonparser.cpp4
-rw-r--r--src/corelib/serialization/qjsonvalue.cpp16
-rw-r--r--src/corelib/serialization/qtextstream.cpp26
-rw-r--r--src/corelib/serialization/qxmlstream.cpp26
-rw-r--r--src/corelib/serialization/qxmlstream.g2
-rw-r--r--src/corelib/thread/qatomic.cpp75
-rw-r--r--src/corelib/thread/qatomic.h6
-rw-r--r--src/corelib/thread/qatomic_cxx11.h18
-rw-r--r--src/corelib/thread/qbasicatomic.h18
-rw-r--r--src/corelib/thread/qfuture.h82
-rw-r--r--src/corelib/thread/qfuture.qdoc8
-rw-r--r--src/corelib/thread/qfutureinterface.cpp42
-rw-r--r--src/corelib/thread/qfutureinterface_p.h4
-rw-r--r--src/corelib/thread/qfuturewatcher.cpp16
-rw-r--r--src/corelib/thread/qgenericatomic.h22
-rw-r--r--src/corelib/thread/qmutex.cpp44
-rw-r--r--src/corelib/thread/qmutex.h2
-rw-r--r--src/corelib/thread/qmutex_linux.cpp4
-rw-r--r--src/corelib/thread/qmutex_p.h10
-rw-r--r--src/corelib/thread/qmutexpool.cpp6
-rw-r--r--src/corelib/thread/qmutexpool_p.h2
-rw-r--r--src/corelib/thread/qreadwritelock.cpp12
-rw-r--r--src/corelib/thread/qsemaphore.cpp6
-rw-r--r--src/corelib/thread/qthread.cpp10
-rw-r--r--src/corelib/thread/qthread_p.h7
-rw-r--r--src/corelib/thread/qthread_unix.cpp45
-rw-r--r--src/corelib/thread/qthread_win.cpp12
-rw-r--r--src/corelib/thread/qthreadstorage.cpp6
-rw-r--r--src/corelib/time/qdatetime.cpp298
-rw-r--r--src/corelib/time/qtimezoneprivate_tz.cpp47
-rw-r--r--src/corelib/tools/qarraydata.cpp6
-rw-r--r--src/corelib/tools/qarraydata.h4
-rw-r--r--src/corelib/tools/qarraydataops.h4
-rw-r--r--src/corelib/tools/qbytearray.cpp6
-rw-r--r--src/corelib/tools/qbytearraymatcher.cpp10
-rw-r--r--src/corelib/tools/qchar.cpp2
-rw-r--r--src/corelib/tools/qcollator.cpp2
-rw-r--r--src/corelib/tools/qcontiguouscache.h14
-rw-r--r--src/corelib/tools/qfreelist_p.h14
-rw-r--r--src/corelib/tools/qhash.cpp16
-rw-r--r--src/corelib/tools/qlinkedlist.h2
-rw-r--r--src/corelib/tools/qlist.cpp2
-rw-r--r--src/corelib/tools/qlocale.cpp8
-rw-r--r--src/corelib/tools/qlocale_p.h6
-rw-r--r--src/corelib/tools/qlocale_tools.cpp4
-rw-r--r--src/corelib/tools/qmap.cpp30
-rw-r--r--src/corelib/tools/qrefcount.h14
-rw-r--r--src/corelib/tools/qregexp.cpp24
-rw-r--r--src/corelib/tools/qregularexpression.cpp29
-rw-r--r--src/corelib/tools/qringbuffer.cpp2
-rw-r--r--src/corelib/tools/qshareddata.h4
-rw-r--r--src/corelib/tools/qsharedpointer.cpp10
-rw-r--r--src/corelib/tools/qsharedpointer_impl.h44
-rw-r--r--src/corelib/tools/qsimd.cpp4
-rw-r--r--src/corelib/tools/qsimd_p.h4
-rw-r--r--src/corelib/tools/qstring.cpp503
-rw-r--r--src/corelib/tools/qstring.h99
-rw-r--r--src/corelib/tools/qstringalgorithms.h5
-rw-r--r--src/corelib/tools/qstringmatcher.cpp6
-rw-r--r--src/corelib/tools/qstringview.cpp36
-rw-r--r--src/corelib/tools/qstringview.h9
-rw-r--r--src/corelib/tools/qvarlengtharray.h8
-rw-r--r--src/corelib/tools/qvarlengtharray.qdoc8
-rw-r--r--src/corelib/tools/qvector.h1
-rw-r--r--src/corelib/tools/qvector.qdoc5
-rw-r--r--src/dbus/doc/snippets/cmake/examples.cmake3
-rw-r--r--src/dbus/doc/src/dontdocument.qdoc30
-rw-r--r--src/dbus/doc/src/qtdbus-cmake.qdoc224
-rw-r--r--src/dbus/qdbusabstractinterface.cpp127
-rw-r--r--src/dbus/qdbusabstractinterface.h94
-rw-r--r--src/dbus/qdbusargument.cpp4
-rw-r--r--src/dbus/qdbusintegrator.cpp33
-rw-r--r--src/dbus/qdbusinternalfilters.cpp20
-rw-r--r--src/dbus/qdbusmarshaller.cpp12
-rw-r--r--src/dbus/qdbusmetaobject.cpp2
-rw-r--r--src/dbus/qdbusmetatype.cpp4
-rw-r--r--src/dbus/qdbuspendingcall.cpp6
-rw-r--r--src/dbus/qdbusreply.cpp4
-rw-r--r--src/dbus/qdbusserver.cpp2
-rw-r--r--src/dbus/qdbusunixfiledescriptor.cpp10
-rw-r--r--src/dbus/qdbusutil_p.h12
-rw-r--r--src/dbus/qdbusxmlgenerator.cpp14
-rw-r--r--src/gui/accessible/qaccessible.cpp1
-rw-r--r--src/gui/accessible/qaccessible.h1
-rw-r--r--src/gui/doc/snippets/code/doc_src_coordsys.cpp2
-rw-r--r--src/gui/doc/src/coordsys.qdoc9
-rw-r--r--src/gui/doc/src/dontdocument.qdoc66
-rw-r--r--src/gui/gui.pro1
-rw-r--r--src/gui/image/qicon.cpp4
-rw-r--r--src/gui/image/qimage.cpp8
-rw-r--r--src/gui/image/qimagereader.cpp10
-rw-r--r--src/gui/image/qmovie.cpp5
-rw-r--r--src/gui/image/qpicture.cpp2
-rw-r--r--src/gui/image/qpixmap.cpp10
-rw-r--r--src/gui/kernel/qevent.cpp62
-rw-r--r--src/gui/kernel/qevent_p.h2
-rw-r--r--src/gui/kernel/qguiapplication_p.h2
-rw-r--r--src/gui/kernel/qhighdpiscaling.cpp2
-rw-r--r--src/gui/kernel/qhighdpiscaling_p.h264
-rw-r--r--src/gui/kernel/qkeysequence.cpp2
-rw-r--r--src/gui/kernel/qopenglcontext.cpp2
-rw-r--r--src/gui/kernel/qopenglwindow.cpp2
-rw-r--r--src/gui/kernel/qpalette.cpp7
-rw-r--r--src/gui/kernel/qsurfaceformat.cpp2
-rw-r--r--src/gui/kernel/qwindow.cpp6
-rw-r--r--src/gui/kernel/qwindowsysteminterface.cpp4
-rw-r--r--src/gui/math3d/qvector3d.cpp2
-rw-r--r--src/gui/opengl/qopenglframebufferobject.cpp2
-rw-r--r--src/gui/opengl/qopenglfunctions_1_0.cpp4
-rw-r--r--src/gui/opengl/qopenglfunctions_1_1.cpp8
-rw-r--r--src/gui/opengl/qopenglfunctions_1_2.cpp12
-rw-r--r--src/gui/opengl/qopenglfunctions_1_3.cpp16
-rw-r--r--src/gui/opengl/qopenglfunctions_1_4.cpp20
-rw-r--r--src/gui/opengl/qopenglfunctions_1_5.cpp22
-rw-r--r--src/gui/opengl/qopenglfunctions_2_0.cpp24
-rw-r--r--src/gui/opengl/qopenglfunctions_2_1.cpp26
-rw-r--r--src/gui/opengl/qopenglfunctions_3_0.cpp28
-rw-r--r--src/gui/opengl/qopenglfunctions_3_1.cpp20
-rw-r--r--src/gui/opengl/qopenglfunctions_3_2_compatibility.cpp32
-rw-r--r--src/gui/opengl/qopenglfunctions_3_2_core.cpp22
-rw-r--r--src/gui/opengl/qopenglfunctions_3_3_compatibility.cpp36
-rw-r--r--src/gui/opengl/qopenglfunctions_3_3_core.cpp24
-rw-r--r--src/gui/opengl/qopenglfunctions_4_0_compatibility.cpp38
-rw-r--r--src/gui/opengl/qopenglfunctions_4_0_core.cpp26
-rw-r--r--src/gui/opengl/qopenglfunctions_4_1_compatibility.cpp40
-rw-r--r--src/gui/opengl/qopenglfunctions_4_1_core.cpp28
-rw-r--r--src/gui/opengl/qopenglfunctions_4_2_compatibility.cpp42
-rw-r--r--src/gui/opengl/qopenglfunctions_4_2_core.cpp30
-rw-r--r--src/gui/opengl/qopenglfunctions_4_3_compatibility.cpp44
-rw-r--r--src/gui/opengl/qopenglfunctions_4_3_core.cpp32
-rw-r--r--src/gui/opengl/qopenglfunctions_4_4_compatibility.cpp46
-rw-r--r--src/gui/opengl/qopenglfunctions_4_4_core.cpp34
-rw-r--r--src/gui/opengl/qopenglfunctions_4_5_compatibility.cpp50
-rw-r--r--src/gui/opengl/qopenglfunctions_4_5_core.cpp36
-rw-r--r--src/gui/opengl/qopengltextureblitter.cpp3
-rw-r--r--src/gui/painting/qbrush.cpp8
-rw-r--r--src/gui/painting/qbrush.h4
-rw-r--r--src/gui/painting/qcolor.cpp7
-rw-r--r--src/gui/painting/qcolor.h42
-rw-r--r--src/gui/painting/qcolorspace_p.h2
-rw-r--r--src/gui/painting/qcolortransform.cpp4
-rw-r--r--src/gui/painting/qdrawhelper.cpp37
-rw-r--r--src/gui/painting/qpaintengine_raster.cpp50
-rw-r--r--src/gui/painting/qpainter.cpp28
-rw-r--r--src/gui/painting/qpainter_p.h1
-rw-r--r--src/gui/painting/qpainterpath.cpp59
-rw-r--r--src/gui/painting/qpainterpath_p.h4
-rw-r--r--src/gui/painting/qpen.cpp6
-rw-r--r--src/gui/painting/qstroker.cpp2
-rw-r--r--src/gui/painting/qtextureglyphcache.cpp8
-rw-r--r--src/gui/rhi/qrhi.cpp5292
-rw-r--r--src/gui/rhi/qrhi_p.h1426
-rw-r--r--src/gui/rhi/qrhi_p_p.h570
-rw-r--r--src/gui/rhi/qrhid3d11.cpp3771
-rw-r--r--src/gui/rhi/qrhid3d11_p.h76
-rw-r--r--src/gui/rhi/qrhid3d11_p_p.h690
-rw-r--r--src/gui/rhi/qrhigles2.cpp3036
-rw-r--r--src/gui/rhi/qrhigles2_p.h84
-rw-r--r--src/gui/rhi/qrhigles2_p_p.h716
-rw-r--r--src/gui/rhi/qrhimetal.mm3558
-rw-r--r--src/gui/rhi/qrhimetal_p.h80
-rw-r--r--src/gui/rhi/qrhimetal_p_p.h450
-rw-r--r--src/gui/rhi/qrhinull.cpp764
-rw-r--r--src/gui/rhi/qrhinull_p.h69
-rw-r--r--src/gui/rhi/qrhinull_p_p.h294
-rw-r--r--src/gui/rhi/qrhiprofiler.cpp603
-rw-r--r--src/gui/rhi/qrhiprofiler_p.h120
-rw-r--r--src/gui/rhi/qrhiprofiler_p_p.h121
-rw-r--r--src/gui/rhi/qrhivulkan.cpp6063
-rw-r--r--src/gui/rhi/qrhivulkan_p.h85
-rw-r--r--src/gui/rhi/qrhivulkan_p_p.h915
-rw-r--r--src/gui/rhi/qrhivulkanext_p.h81
-rw-r--r--src/gui/rhi/qshader.cpp585
-rw-r--r--src/gui/rhi/qshader_p.h224
-rw-r--r--src/gui/rhi/qshader_p_p.h84
-rw-r--r--src/gui/rhi/qshaderdescription.cpp1116
-rw-r--r--src/gui/rhi/qshaderdescription_p.h281
-rw-r--r--src/gui/rhi/qshaderdescription_p_p.h98
-rw-r--r--src/gui/rhi/rhi.pri57
-rw-r--r--src/gui/text/qcssparser.cpp1
-rw-r--r--src/gui/text/qcssparser_p.h1
-rw-r--r--src/gui/text/qdistancefield.cpp7
-rw-r--r--src/gui/text/qdistancefield_p.h1
-rw-r--r--src/gui/text/qfont.cpp30
-rw-r--r--src/gui/text/qfontdatabase.cpp7
-rw-r--r--src/gui/text/qglyphrun.cpp2
-rw-r--r--src/gui/text/qplatformfontdatabase.cpp2
-rw-r--r--src/gui/text/qrawfont_p.h2
-rw-r--r--src/gui/text/qstatictext.cpp4
-rw-r--r--src/gui/text/qtextdocument.cpp18
-rw-r--r--src/gui/text/qtextdocument.h2
-rw-r--r--src/gui/text/qtextdocument_p.h1
-rw-r--r--src/gui/text/qtextdocumentlayout.cpp12
-rw-r--r--src/gui/text/qtexthtmlparser.cpp42
-rw-r--r--src/gui/text/qtexthtmlparser_p.h1
-rw-r--r--src/gui/text/qtextmarkdownimporter.cpp2
-rw-r--r--src/network/access/qnetworkaccessbackend.cpp6
-rw-r--r--src/network/access/qnetworkaccesscache.cpp32
-rw-r--r--src/network/access/qnetworkreplywasmimpl.cpp53
-rw-r--r--src/network/bearer/qbearerengine.cpp2
-rw-r--r--src/network/bearer/qnetworkconfigmanager.cpp27
-rw-r--r--src/network/bearer/qnetworkconfiguration.cpp29
-rw-r--r--src/network/bearer/qnetworkconfiguration_p.h7
-rw-r--r--src/network/bearer/qnetworksession.cpp3
-rw-r--r--src/network/doc/src/dontdocument.qdoc30
-rw-r--r--src/network/doc/src/ssl.qdoc15
-rw-r--r--src/network/kernel/qnetworkproxy.cpp4
-rw-r--r--src/network/ssl/qdtls.cpp2
-rw-r--r--src/network/ssl/qdtls_openssl.cpp2
-rw-r--r--src/network/ssl/qsslsocket.cpp4
-rw-r--r--src/network/ssl/qsslsocket_openssl.cpp191
-rw-r--r--src/network/ssl/qsslsocket_openssl11_symbols_p.h8
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols.cpp12
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols_p.h1
-rw-r--r--src/network/ssl/qsslsocket_opensslpre11_symbols_p.h2
-rw-r--r--src/opengl/qgl.cpp4
-rw-r--r--src/opengl/qglcolormap.cpp2
-rw-r--r--src/opengl/qglcolormap.h2
-rw-r--r--src/opengl/qglframebufferobject.cpp2
-rw-r--r--src/platformheaders/doc/src/dontdocument.qdoc30
-rw-r--r--src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp4
-rw-r--r--src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp2
-rw-r--r--src/platformsupport/fontdatabases/windows/qwindowsfontdatabase.cpp6
-rw-r--r--src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp27
-rw-r--r--src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler_p.h4
-rw-r--r--src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp76
-rw-r--r--src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager_p.h6
-rw-r--r--src/platformsupport/input/evdevmouse/qevdevmousehandler.cpp6
-rw-r--r--src/platformsupport/input/evdevmouse/qevdevmousehandler_p.h4
-rw-r--r--src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp65
-rw-r--r--src/platformsupport/input/evdevmouse/qevdevmousemanager_p.h6
-rw-r--r--src/platformsupport/input/evdevtablet/qevdevtablethandler.cpp22
-rw-r--r--src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp61
-rw-r--r--src/platformsupport/input/evdevtablet/qevdevtabletmanager_p.h7
-rw-r--r--src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp24
-rw-r--r--src/platformsupport/input/evdevtouch/qevdevtouchmanager.cpp60
-rw-r--r--src/platformsupport/input/evdevtouch/qevdevtouchmanager_p.h5
-rw-r--r--src/platformsupport/input/shared/devicehandlerlist_p.h95
-rw-r--r--src/platformsupport/input/shared/qevdevutil.cpp70
-rw-r--r--src/platformsupport/input/shared/qevdevutil_p.h76
-rw-r--r--src/platformsupport/input/shared/shared.pri3
-rw-r--r--src/platformsupport/linuxaccessibility/atspiadaptor.cpp9
-rw-r--r--src/platformsupport/linuxaccessibility/bridge.cpp2
-rw-r--r--src/platformsupport/vkconvenience/qvkconvenience.cpp2
-rw-r--r--src/platformsupport/vkconvenience/qvkconvenience_p.h2
-rw-r--r--src/plugins/bearer/networkmanager/qnetworkmanagerservice.cpp37
-rw-r--r--src/plugins/bearer/qnetworksession_impl.cpp13
-rw-r--r--src/plugins/platforms/android/qandroideventdispatcher.cpp4
-rw-r--r--src/plugins/platforms/android/qandroideventdispatcher.h2
-rw-r--r--src/plugins/platforms/android/qandroidinputcontext.cpp739
-rw-r--r--src/plugins/platforms/android/qandroidinputcontext.h6
-rw-r--r--src/plugins/platforms/cocoa/qcocoabackingstore.h1
-rw-r--r--src/plugins/platforms/cocoa/qcocoabackingstore.mm24
-rw-r--r--src/plugins/platforms/cocoa/qcocoaeventdispatcher.h1
-rw-r--r--src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm31
-rw-r--r--src/plugins/platforms/cocoa/qcocoaglcontext.h2
-rw-r--r--src/plugins/platforms/cocoa/qcocoaglcontext.mm8
-rw-r--r--src/plugins/platforms/cocoa/qcocoahelpers.mm5
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenu.h4
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenu.mm10
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenuitem.mm8
-rw-r--r--src/plugins/platforms/cocoa/qcocoansmenu.mm2
-rw-r--r--src/plugins/platforms/cocoa/qcocoatheme.h5
-rw-r--r--src/plugins/platforms/cocoa/qcocoatheme.mm62
-rw-r--r--src/plugins/platforms/cocoa/qcocoavulkaninstance.mm2
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.mm20
-rw-r--r--src/plugins/platforms/cocoa/qnsview_mouse.mm7
-rw-r--r--src/plugins/platforms/direct2d/qwindowsdirect2dhelpers.h7
-rw-r--r--src/plugins/platforms/direct2d/qwindowsdirect2dintegration.cpp4
-rw-r--r--src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp16
-rw-r--r--src/plugins/platforms/direct2d/qwindowsdirect2dwindow.cpp2
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.cpp2
-rw-r--r--src/plugins/platforms/ios/quiview_accessibility.mm8
-rw-r--r--src/plugins/platforms/wasm/qwasmbackingstore.cpp6
-rw-r--r--src/plugins/platforms/wasm/qwasmbackingstore.h1
-rw-r--r--src/plugins/platforms/wasm/qwasmcompositor.cpp36
-rw-r--r--src/plugins/platforms/wasm/qwasmcompositor.h1
-rw-r--r--src/plugins/platforms/wasm/qwasmeventtranslator.cpp31
-rw-r--r--src/plugins/platforms/wasm/qwasmeventtranslator.h2
-rw-r--r--src/plugins/platforms/wasm/qwasmintegration.cpp32
-rw-r--r--src/plugins/platforms/wasm/qwasmintegration.h2
-rw-r--r--src/plugins/platforms/wasm/qwasmoffscreensurface.cpp41
-rw-r--r--src/plugins/platforms/wasm/qwasmoffscreensurface.h49
-rw-r--r--src/plugins/platforms/wasm/qwasmopenglcontext.cpp59
-rw-r--r--src/plugins/platforms/wasm/qwasmopenglcontext.h6
-rw-r--r--src/plugins/platforms/wasm/qwasmscreen.cpp5
-rw-r--r--src/plugins/platforms/wasm/qwasmscreen.h1
-rw-r--r--src/plugins/platforms/wasm/qwasmwindow.cpp6
-rw-r--r--src/plugins/platforms/wasm/qwasmwindow.h1
-rw-r--r--src/plugins/platforms/wasm/wasm.pro6
-rw-r--r--src/plugins/platforms/windows/qwin10helpers.cpp2
-rw-r--r--src/plugins/platforms/windows/qwindowsbackingstore.cpp2
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.cpp30
-rw-r--r--src/plugins/platforms/windows/qwindowscursor.cpp2
-rw-r--r--src/plugins/platforms/windows/qwindowsdialoghelpers.cpp12
-rw-r--r--src/plugins/platforms/windows/qwindowsdrag.cpp6
-rw-r--r--src/plugins/platforms/windows/qwindowseglcontext.cpp8
-rw-r--r--src/plugins/platforms/windows/qwindowsgdinativeinterface.cpp2
-rw-r--r--src/plugins/platforms/windows/qwindowsglcontext.cpp10
-rw-r--r--src/plugins/platforms/windows/qwindowsinputcontext.cpp2
-rw-r--r--src/plugins/platforms/windows/qwindowsintegration.cpp4
-rw-r--r--src/plugins/platforms/windows/qwindowskeymapper.cpp6
-rw-r--r--src/plugins/platforms/windows/qwindowsmenu.cpp6
-rw-r--r--src/plugins/platforms/windows/qwindowsmime.cpp20
-rw-r--r--src/plugins/platforms/windows/qwindowsmousehandler.cpp6
-rw-r--r--src/plugins/platforms/windows/qwindowsnativeinterface.cpp10
-rw-r--r--src/plugins/platforms/windows/qwindowsole.cpp10
-rw-r--r--src/plugins/platforms/windows/qwindowsopengltester.cpp2
-rw-r--r--src/plugins/platforms/windows/qwindowspointerhandler.cpp17
-rw-r--r--src/plugins/platforms/windows/qwindowsscreen.cpp6
-rw-r--r--src/plugins/platforms/windows/qwindowsservices.cpp2
-rw-r--r--src/plugins/platforms/windows/qwindowstheme.cpp10
-rw-r--r--src/plugins/platforms/windows/qwindowswindow.cpp24
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp2
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.cpp6
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.cpp6
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp2
-rw-r--r--src/plugins/platforms/winrt/qwinrtscreen.cpp8
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp6
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.h1
-rw-r--r--src/plugins/platforms/xcb/qxcbscreen.cpp20
-rw-r--r--src/plugins/platforms/xcb/qxcbscreen.h1
-rw-r--r--src/plugins/platforms/xcb/qxcbwindow.h4
-rw-r--r--src/plugins/styles/mac/qmacstyle_mac.mm97
-rw-r--r--src/plugins/styles/mac/qmacstyle_mac_p_p.h4
-rw-r--r--src/printsupport/doc/src/dontdocument.qdoc30
-rw-r--r--src/sql/doc/src/dontdocument.qdoc30
-rw-r--r--src/sql/kernel/qsqldatabase.cpp2
-rw-r--r--src/sql/kernel/qsqlquery.cpp4
-rw-r--r--src/src.pro20
-rw-r--r--src/testlib/doc/src/dontdocument.qdoc32
-rw-r--r--src/testlib/qtestcase.cpp10
-rw-r--r--src/testlib/qtestlog.cpp2
-rw-r--r--src/tools/androiddeployqt/main.cpp157
-rw-r--r--src/tools/androidtestrunner/androidtestrunner.pro13
-rw-r--r--src/tools/androidtestrunner/main.cpp490
-rw-r--r--src/tools/bootstrap/bootstrap.pro16
-rw-r--r--src/tools/moc/generator.cpp2
-rw-r--r--src/tools/qfloat16-tables/qfloat16-tables.pro9
-rw-r--r--src/tools/rcc/rcc.cpp5
-rw-r--r--src/tools/rcc/rcc.h1
-rw-r--r--src/tools/uic/cpp/cppwriteincludes.h4
-rw-r--r--src/tools/uic/cpp/cppwriteinitialization.cpp14
-rw-r--r--src/tools/uic/cpp/cppwriteinitialization.h16
-rw-r--r--src/tools/uic/customwidgetsinfo.h2
-rw-r--r--src/tools/uic/databaseinfo.cpp9
-rw-r--r--src/tools/uic/driver.cpp2
-rw-r--r--src/tools/uic/main.cpp2
-rw-r--r--src/tools/uic/treewalker.h5
-rw-r--r--src/tools/uic/uic.cpp8
-rw-r--r--src/widgets/accessible/qaccessiblemenu.cpp13
-rw-r--r--src/widgets/dialogs/qfileinfogatherer.cpp16
-rw-r--r--src/widgets/dialogs/qmessagebox.cpp11
-rw-r--r--src/widgets/doc/src/dontdocument.qdoc30
-rw-r--r--src/widgets/itemviews/qabstractitemview.cpp11
-rw-r--r--src/widgets/itemviews/qheaderview.cpp5
-rw-r--r--src/widgets/kernel/qaction.cpp5
-rw-r--r--src/widgets/kernel/qactiongroup.cpp96
-rw-r--r--src/widgets/kernel/qactiongroup.h11
-rw-r--r--src/widgets/kernel/qapplication.cpp59
-rw-r--r--src/widgets/kernel/qdesktopwidget.cpp12
-rw-r--r--src/widgets/kernel/qopenglwidget.cpp7
-rw-r--r--src/widgets/kernel/qwidget.cpp131
-rw-r--r--src/widgets/kernel/qwidget_p.h19
-rw-r--r--src/widgets/kernel/qwidgetwindow.cpp5
-rw-r--r--src/widgets/styles/qfusionstyle.cpp10
-rw-r--r--src/widgets/styles/qstylesheetstyle.cpp54
-rw-r--r--src/widgets/styles/qwindowsstyle.cpp17
-rw-r--r--src/widgets/widgets/qcombobox.cpp28
-rw-r--r--src/widgets/widgets/qcombobox.h5
-rw-r--r--src/widgets/widgets/qdatetimeedit.cpp6
-rw-r--r--src/widgets/widgets/qdockwidget.cpp8
-rw-r--r--src/widgets/widgets/qlineedit_p.cpp4
-rw-r--r--src/widgets/widgets/qmainwindowlayout.cpp6
-rw-r--r--src/widgets/widgets/qmdisubwindow.cpp16
-rw-r--r--src/widgets/widgets/qmenu.cpp12
-rw-r--r--src/widgets/widgets/qmenu_p.h2
-rw-r--r--src/widgets/widgets/qmenubar.cpp15
-rw-r--r--src/widgets/widgets/qplaintextedit.cpp1
-rw-r--r--src/widgets/widgets/qtextbrowser.cpp84
-rw-r--r--src/widgets/widgets/qtextbrowser.h11
-rw-r--r--src/widgets/widgets/qwidgettextcontrol.cpp14
-rw-r--r--src/xml/doc/src/dontdocument.qdoc30
-rw-r--r--src/xml/dom/qdom.cpp222
-rw-r--r--src/xml/sax/qxml.cpp110
-rwxr-xr-xtests/auto/android/runtests_androiddeployqt.pl550
-rw-r--r--tests/auto/cmake/CMakeLists.txt7
-rw-r--r--tests/auto/cmake/test_add_big_resource/CMakeLists.txt2
-rw-r--r--tests/auto/concurrent/qtconcurrentiteratekernel/tst_qtconcurrentiteratekernel.cpp24
-rw-r--r--tests/auto/concurrent/qtconcurrentmap/tst_qtconcurrentmap.cpp32
-rw-r--r--tests/auto/concurrent/qtconcurrentrun/tst_qtconcurrentrun.cpp12
-rw-r--r--tests/auto/concurrent/qtconcurrentthreadengine/tst_qtconcurrentthreadengine.cpp11
-rw-r--r--tests/auto/corelib/animation/qabstractanimation/tst_qabstractanimation.cpp15
-rw-r--r--tests/auto/corelib/global/qfloat16/tst_qfloat16.cpp4
-rw-r--r--tests/auto/corelib/global/qglobalstatic/tst_qglobalstatic.cpp18
-rw-r--r--tests/auto/corelib/global/qlogging/tst_qlogging.cpp14
-rw-r--r--tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp2
-rw-r--r--tests/auto/corelib/io/qfileselector/tst_qfileselector.cpp8
-rw-r--r--tests/auto/corelib/io/qprocess/tst_qprocess.cpp7
-rw-r--r--tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp8
-rw-r--r--tests/auto/corelib/io/qurl/tst_qurl.cpp15
-rw-r--r--tests/auto/corelib/itemmodels/qitemmodel/modelstotest.cpp2
-rw-r--r--tests/auto/corelib/itemmodels/qitemselectionmodel/tst_qitemselectionmodel.cpp4
-rw-r--r--tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp22
-rw-r--r--tests/auto/corelib/kernel/qeventloop/tst_qeventloop.cpp6
-rw-r--r--tests/auto/corelib/kernel/qmetatype/tst_qmetatype.h2
-rw-r--r--tests/auto/corelib/kernel/qsharedmemory/tst_qsharedmemory.cpp3
-rw-r--r--tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp15
-rw-r--r--tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp2
-rw-r--r--tests/auto/corelib/plugin/qlibrary/tst_qlibrary.cpp8
-rw-r--r--tests/auto/corelib/serialization/qdatastream/tst_qdatastream.cpp128
-rw-r--r--tests/auto/corelib/serialization/qtextstream/tst_qtextstream.cpp3
-rw-r--r--tests/auto/corelib/statemachine/qstatemachine/tst_qstatemachine.cpp4
-rw-r--r--tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp53
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/tst_qatomicinteger.cpp194
-rw-r--r--tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp176
-rw-r--r--tests/auto/corelib/thread/qfuture/tst_qfuture.cpp67
-rw-r--r--tests/auto/corelib/thread/qfuturewatcher/tst_qfuturewatcher.cpp3
-rw-r--r--tests/auto/corelib/thread/qmutex/tst_qmutex.cpp24
-rw-r--r--tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp27
-rw-r--r--tests/auto/corelib/thread/qthread/tst_qthread.cpp30
-rw-r--r--tests/auto/corelib/thread/qthreadonce/tst_qthreadonce.cpp8
-rw-r--r--tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp84
-rw-r--r--tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp12
-rw-r--r--tests/auto/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp32
-rw-r--r--tests/auto/corelib/tools/collections/tst_collections.cpp14
-rw-r--r--tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp36
-rw-r--r--tests/auto/corelib/tools/qbytearray/tst_qbytearray.cpp12
-rw-r--r--tests/auto/corelib/tools/qcontiguouscache/tst_qcontiguouscache.cpp2
-rw-r--r--tests/auto/corelib/tools/qlatin1string/tst_qlatin1string.cpp42
-rw-r--r--tests/auto/corelib/tools/qregexp/tst_qregexp.cpp7
-rw-r--r--tests/auto/corelib/tools/qscopedpointer/tst_qscopedpointer.cpp42
-rw-r--r--tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp108
-rw-r--r--tests/auto/corelib/tools/qstringapisymmetry/tst_qstringapisymmetry.cpp154
-rw-r--r--tests/auto/corelib/tools/qstringview/tst_qstringview.cpp43
-rw-r--r--tests/auto/corelib/tools/qvector/tst_qvector.cpp2
-rw-r--r--tests/auto/dbus/qdbusthreading/tst_qdbusthreading.cpp4
-rw-r--r--tests/auto/gui/gui.pro1
-rw-r--r--tests/auto/gui/image/qimagereader/images/qticon16.pngbin0 -> 1884 bytes
-rw-r--r--tests/auto/gui/image/qimagereader/images/qticon16@2x.pngbin0 -> 3187 bytes
-rw-r--r--tests/auto/gui/image/qimagereader/images/qticon16@3x.pngbin0 -> 5307 bytes
-rw-r--r--tests/auto/gui/image/qimagereader/tst_qimagereader.cpp25
-rw-r--r--tests/auto/gui/rhi/qrhi/data/texture.frag12
-rw-r--r--tests/auto/gui/rhi/qrhi/data/texture.vert18
-rw-r--r--tests/auto/gui/rhi/qrhi/qrhi.pro8
-rw-r--r--tests/auto/gui/rhi/qrhi/qrhi.qrc5
-rw-r--r--tests/auto/gui/rhi/qrhi/tst_qrhi.cpp246
-rw-r--r--tests/auto/gui/rhi/qshader/data/color.vert18
-rw-r--r--tests/auto/gui/rhi/qshader/data/color.vert.qsbbin0 -> 1847 bytes
-rw-r--r--tests/auto/gui/rhi/qshader/data/color_simple.vert.qsbbin0 -> 813 bytes
-rw-r--r--tests/auto/gui/rhi/qshader/qshader.pro8
-rw-r--r--tests/auto/gui/rhi/qshader/qshader.qrc5
-rw-r--r--tests/auto/gui/rhi/qshader/tst_qshader.cpp233
-rw-r--r--tests/auto/gui/rhi/rhi.pro4
-rw-r--r--tests/auto/gui/text/qfont/tst_qfont.cpp18
-rw-r--r--tests/auto/gui/text/qfontcache/tst_qfontcache.cpp2
-rw-r--r--tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp9
-rw-r--r--tests/auto/gui/text/qtextdocumentfragment/tst_qtextdocumentfragment.cpp3
-rw-r--r--tests/auto/network-settings.h4
-rw-r--r--tests/auto/network/access/http2/tst_http2.cpp2
-rw-r--r--tests/auto/network/access/qftp/tst_qftp.cpp7
-rw-r--r--tests/auto/network/access/qnetworkreply/certs/qt-test-server-cacert.pem30
-rw-r--r--tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp63
-rw-r--r--tests/auto/network/socket/platformsocketengine/tst_platformsocketengine.cpp6
-rw-r--r--tests/auto/network/socket/qhttpsocketengine/tst_qhttpsocketengine.cpp5
-rw-r--r--tests/auto/network/socket/qlocalsocket/tst_qlocalsocket.cpp3
-rw-r--r--tests/auto/network/socket/qtcpsocket/tst_qtcpsocket.cpp11
-rw-r--r--tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp3
-rw-r--r--tests/auto/network/ssl/qsslcertificate/tst_qsslcertificate.cpp4
-rw-r--r--tests/auto/network/ssl/qsslsocket/certs/qt-test-server-cacert.pem30
-rw-r--r--tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp16
-rw-r--r--tests/auto/opengl/qglthreads/tst_qglthreads.cpp19
-rw-r--r--tests/auto/other/compiler/tst_compiler.cpp2
-rw-r--r--tests/auto/other/lancelot/paintcommands.h1
-rw-r--r--tests/auto/other/lancelot/scripts/degeneratebeziers.qps11
-rw-r--r--tests/auto/other/networkselftest/tst_networkselftest.cpp5
-rw-r--r--tests/auto/other/qabstractitemmodelutils/dynamictreemodel.cpp17
-rw-r--r--tests/auto/other/qabstractitemmodelutils/dynamictreemodel.h2
-rw-r--r--tests/auto/other/qobjectrace/tst_qobjectrace.cpp8
-rw-r--r--tests/auto/printsupport/kernel/qprinterinfo/tst_qprinterinfo.cpp23
-rw-r--r--tests/auto/shared/highdpi.h109
-rw-r--r--tests/auto/sql/kernel/qsqlthread/tst_qsqlthread.cpp2
-rw-r--r--tests/auto/sql/models/qsqltablemodel/tst_qsqltablemodel.cpp7
-rw-r--r--tests/auto/testlib/qabstractitemmodeltester/tst_qabstractitemmodeltester.cpp2
-rw-r--r--tests/auto/testlib/selftests/sleep/tst_sleep.cpp3
-rw-r--r--tests/auto/widgets/dialogs/qdialog/tst_qdialog.cpp5
-rw-r--r--tests/auto/widgets/dialogs/qwizard/tst_qwizard.cpp2
-rw-r--r--tests/auto/widgets/graphicsview/qgraphicsview/BLACKLIST1
-rw-r--r--tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp38
-rw-r--r--tests/auto/widgets/itemviews/qitemdelegate/tst_qitemdelegate.cpp11
-rw-r--r--tests/auto/widgets/kernel/qactiongroup/tst_qactiongroup.cpp47
-rw-r--r--tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp141
-rw-r--r--tests/auto/widgets/util/qcompleter/tst_qcompleter.cpp255
-rw-r--r--tests/auto/widgets/widgets/qabstractslider/tst_qabstractslider.cpp10
-rw-r--r--tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp8
-rw-r--r--tests/auto/widgets/widgets/qfontcombobox/tst_qfontcombobox.cpp2
-rw-r--r--tests/auto/widgets/widgets/qsplitter/tst_qsplitter.cpp1
-rw-r--r--tests/auto/widgets/widgets/qtextbrowser/heading.html2
-rw-r--r--tests/auto/widgets/widgets/qtextbrowser/markdown.really2
-rw-r--r--tests/auto/widgets/widgets/qtextbrowser/qtextbrowser.pro2
-rw-r--r--tests/auto/widgets/widgets/qtextbrowser/tst_qtextbrowser.cpp45
-rw-r--r--tests/auto/widgets/widgets/qtextedit/tst_qtextedit.cpp31
-rw-r--r--tests/auto/xml/sax/qxmlinputsource/tst_qxmlinputsource.cpp3
-rw-r--r--tests/auto/xml/sax/qxmlsimplereader/parser/parser.cpp12
-rw-r--r--tests/benchmarks/gui/painting/qtransform/tst_qtransform.cpp12
-rw-r--r--tests/manual/diaglib/debugproxystyle.cpp23
-rw-r--r--tests/manual/diaglib/eventfilter.cpp32
-rw-r--r--tests/manual/diaglib/eventfilter.h13
-rw-r--r--tests/manual/highdpi/dragwidget.cpp2
-rw-r--r--tests/manual/manual.pro3
-rw-r--r--tests/manual/rhi/compressedtexture_bc1/compressedtexture_bc1.cpp198
-rw-r--r--tests/manual/rhi/compressedtexture_bc1/compressedtexture_bc1.pro8
-rw-r--r--tests/manual/rhi/compressedtexture_bc1/compressedtexture_bc1.qrc7
-rw-r--r--tests/manual/rhi/compressedtexture_bc1_subupload/compressedtexture_bc1_subupload.cpp213
-rw-r--r--tests/manual/rhi/compressedtexture_bc1_subupload/compressedtexture_bc1_subupload.pro8
-rw-r--r--tests/manual/rhi/compressedtexture_bc1_subupload/compressedtexture_bc1_subupload.qrc8
-rw-r--r--tests/manual/rhi/computebuffer/buffer.comp41
-rw-r--r--tests/manual/rhi/computebuffer/buffer.comp.qsbbin0 -> 2160 bytes
-rwxr-xr-xtests/manual/rhi/computebuffer/buildshaders.bat3
-rw-r--r--tests/manual/rhi/computebuffer/computebuffer.cpp202
-rw-r--r--tests/manual/rhi/computebuffer/computebuffer.pro8
-rw-r--r--tests/manual/rhi/computebuffer/computebuffer.qrc7
-rw-r--r--tests/manual/rhi/computebuffer/main.frag8
-rw-r--r--tests/manual/rhi/computebuffer/main.frag.qsbbin0 -> 672 bytes
-rw-r--r--tests/manual/rhi/computebuffer/main.vert11
-rw-r--r--tests/manual/rhi/computebuffer/main.vert.qsbbin0 -> 852 bytes
-rwxr-xr-xtests/manual/rhi/computeimage/buildshaders.bat1
-rw-r--r--tests/manual/rhi/computeimage/computeimage.cpp228
-rw-r--r--tests/manual/rhi/computeimage/computeimage.pro8
-rw-r--r--tests/manual/rhi/computeimage/computeimage.qrc8
-rw-r--r--tests/manual/rhi/computeimage/image.comp20
-rw-r--r--tests/manual/rhi/computeimage/image.comp.qsbbin0 -> 1811 bytes
-rw-r--r--tests/manual/rhi/cubemap/buildshaders.bat2
-rw-r--r--tests/manual/rhi/cubemap/c.pngbin0 -> 27923 bytes
-rw-r--r--tests/manual/rhi/cubemap/cubemap.cpp179
-rw-r--r--tests/manual/rhi/cubemap/cubemap.frag10
-rw-r--r--tests/manual/rhi/cubemap/cubemap.frag.qsbbin0 -> 1311 bytes
-rw-r--r--tests/manual/rhi/cubemap/cubemap.pro8
-rw-r--r--tests/manual/rhi/cubemap/cubemap.qrc7
-rw-r--r--tests/manual/rhi/cubemap/cubemap.vert15
-rw-r--r--tests/manual/rhi/cubemap/cubemap.vert.qsbbin0 -> 1499 bytes
-rw-r--r--tests/manual/rhi/floattexture/floattexture.cpp328
-rw-r--r--tests/manual/rhi/floattexture/floattexture.pro8
-rw-r--r--tests/manual/rhi/floattexture/floattexture.qrc7
-rw-r--r--tests/manual/rhi/hellominimalcrossgfxtriangle/hellominimalcrossgfxtriangle.cpp554
-rw-r--r--tests/manual/rhi/hellominimalcrossgfxtriangle/hellominimalcrossgfxtriangle.pro8
-rw-r--r--tests/manual/rhi/hellominimalcrossgfxtriangle/hellominimalcrossgfxtriangle.qrc6
-rw-r--r--tests/manual/rhi/mrt/buildshaders.bat2
-rw-r--r--tests/manual/rhi/mrt/mrt.cpp296
-rw-r--r--tests/manual/rhi/mrt/mrt.frag22
-rw-r--r--tests/manual/rhi/mrt/mrt.frag.qsbbin0 -> 1934 bytes
-rw-r--r--tests/manual/rhi/mrt/mrt.pro8
-rw-r--r--tests/manual/rhi/mrt/mrt.qrc8
-rw-r--r--tests/manual/rhi/mrt/mrt.vert19
-rw-r--r--tests/manual/rhi/mrt/mrt.vert.qsbbin0 -> 1633 bytes
-rw-r--r--tests/manual/rhi/msaarenderbuffer/msaarenderbuffer.cpp259
-rw-r--r--tests/manual/rhi/msaarenderbuffer/msaarenderbuffer.pro8
-rw-r--r--tests/manual/rhi/msaarenderbuffer/msaarenderbuffer.qrc8
-rw-r--r--tests/manual/rhi/msaatexture/msaatexture.cpp329
-rw-r--r--tests/manual/rhi/msaatexture/msaatexture.pro8
-rw-r--r--tests/manual/rhi/msaatexture/msaatexture.qrc9
-rw-r--r--tests/manual/rhi/multiwindow/multiwindow.cpp644
-rw-r--r--tests/manual/rhi/multiwindow/multiwindow.pro8
-rw-r--r--tests/manual/rhi/multiwindow/multiwindow.qrc6
-rw-r--r--tests/manual/rhi/multiwindow_threaded/multiwindow_threaded.cpp829
-rw-r--r--tests/manual/rhi/multiwindow_threaded/multiwindow_threaded.pro12
-rw-r--r--tests/manual/rhi/multiwindow_threaded/multiwindow_threaded.qrc7
-rw-r--r--tests/manual/rhi/multiwindow_threaded/window.cpp142
-rw-r--r--tests/manual/rhi/multiwindow_threaded/window.h86
-rw-r--r--tests/manual/rhi/offscreen/offscreen.cpp366
-rw-r--r--tests/manual/rhi/offscreen/offscreen.pro9
-rw-r--r--tests/manual/rhi/offscreen/offscreen.qrc6
-rw-r--r--tests/manual/rhi/qrhiprof/qrhiprof.cpp671
-rw-r--r--tests/manual/rhi/qrhiprof/qrhiprof.pro6
-rw-r--r--tests/manual/rhi/rhi.pro24
-rw-r--r--tests/manual/rhi/shadowmap/buildshaders.bat4
-rwxr-xr-xtests/manual/rhi/shadowmap/buildshaders.sh4
-rw-r--r--tests/manual/rhi/shadowmap/main.frag30
-rw-r--r--tests/manual/rhi/shadowmap/main.frag.qsbbin0 -> 2489 bytes
-rw-r--r--tests/manual/rhi/shadowmap/main.vert20
-rw-r--r--tests/manual/rhi/shadowmap/main.vert.qsbbin0 -> 1595 bytes
-rw-r--r--tests/manual/rhi/shadowmap/shadowmap.cpp304
-rw-r--r--tests/manual/rhi/shadowmap/shadowmap.frag5
-rw-r--r--tests/manual/rhi/shadowmap/shadowmap.frag.qsbbin0 -> 408 bytes
-rw-r--r--tests/manual/rhi/shadowmap/shadowmap.pro8
-rw-r--r--tests/manual/rhi/shadowmap/shadowmap.qrc8
-rw-r--r--tests/manual/rhi/shadowmap/shadowmap.vert14
-rw-r--r--tests/manual/rhi/shadowmap/shadowmap.vert.qsbbin0 -> 1215 bytes
-rw-r--r--tests/manual/rhi/shared/OpenfootageNET_fieldairport-512.hdrbin0 -> 385303 bytes
-rw-r--r--tests/manual/rhi/shared/buildshaders.bat5
-rw-r--r--tests/manual/rhi/shared/bwqt224_64.pngbin0 -> 6339 bytes
-rw-r--r--tests/manual/rhi/shared/bwqt224_64_nomips.ddsbin0 -> 9712 bytes
-rw-r--r--tests/manual/rhi/shared/color.frag15
-rw-r--r--tests/manual/rhi/shared/color.frag.qsbbin0 -> 1483 bytes
-rw-r--r--tests/manual/rhi/shared/color.vert19
-rw-r--r--tests/manual/rhi/shared/color.vert.qsbbin0 -> 1648 bytes
-rw-r--r--tests/manual/rhi/shared/cube.h119
-rw-r--r--tests/manual/rhi/shared/dds_bc1.h137
-rw-r--r--tests/manual/rhi/shared/examplefw.h548
-rw-r--r--tests/manual/rhi/shared/qt256.pngbin0 -> 6208 bytes
-rw-r--r--tests/manual/rhi/shared/qt256_bc1_9mips.ddsbin0 -> 43832 bytes
-rw-r--r--tests/manual/rhi/shared/texture.frag18
-rw-r--r--tests/manual/rhi/shared/texture.frag.qsbbin0 -> 1710 bytes
-rw-r--r--tests/manual/rhi/shared/texture.vert21
-rw-r--r--tests/manual/rhi/shared/texture.vert.qsbbin0 -> 1897 bytes
-rw-r--r--tests/manual/rhi/shared/texture_ms4.frag20
-rw-r--r--tests/manual/rhi/shared/texture_ms4.frag.qsbbin0 -> 1847 bytes
-rw-r--r--tests/manual/rhi/texuploads/texuploads.cpp314
-rw-r--r--tests/manual/rhi/texuploads/texuploads.pro8
-rw-r--r--tests/manual/rhi/texuploads/texuploads.qrc7
-rw-r--r--tests/manual/rhi/triquadcube/quadrenderer.cpp148
-rw-r--r--tests/manual/rhi/triquadcube/quadrenderer.h87
-rw-r--r--tests/manual/rhi/triquadcube/texturedcuberenderer.cpp222
-rw-r--r--tests/manual/rhi/triquadcube/texturedcuberenderer.h86
-rw-r--r--tests/manual/rhi/triquadcube/triangleoncuberenderer.cpp292
-rw-r--r--tests/manual/rhi/triquadcube/triangleoncuberenderer.h95
-rw-r--r--tests/manual/rhi/triquadcube/trianglerenderer.cpp202
-rw-r--r--tests/manual/rhi/triquadcube/trianglerenderer.h93
-rw-r--r--tests/manual/rhi/triquadcube/triquadcube.cpp272
-rw-r--r--tests/manual/rhi/triquadcube/triquadcube.pro18
-rw-r--r--tests/manual/rhi/triquadcube/triquadcube.qrc9
-rw-r--r--util/qfloat16-tables/gen_qfloat16_tables.cpp (renamed from src/tools/qfloat16-tables/gen_qfloat16_tables.cpp)110
-rw-r--r--util/qfloat16-tables/qfloat16-tables.pro2
795 files changed, 76520 insertions, 10417 deletions
diff --git a/.gitignore b/.gitignore
index b9188d84f7..e4079c6b4d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -56,8 +56,6 @@ qt*-config.pri
/src/angle/src/QtANGLE/libANGLE/
/src/angle/src/libGLESv2/libANGLE/
-/src/corelib/global/qfloat16tables.cpp
-
/examples/*/*/*
!/examples/*/*/*[.]*
!/examples/*/*/README
diff --git a/configure.json b/configure.json
index bee813d161..f817101de1 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/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/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/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/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/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/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/data/cmake/Qt5BasicConfig.cmake.in b/mkspecs/features/data/cmake/Qt5BasicConfig.cmake.in
index aca8703238..1c4994c30f 100644
--- a/mkspecs/features/data/cmake/Qt5BasicConfig.cmake.in
+++ b/mkspecs/features/data/cmake/Qt5BasicConfig.cmake.in
@@ -59,6 +59,7 @@ function(_qt5_$${CMAKE_MODULE_NAME}_process_prl_file prl_file_location Configura
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})
+ 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 +67,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/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..43ba482954 100644
--- a/mkspecs/features/ltcg.prf
+++ b/mkspecs/features/ltcg.prf
@@ -28,7 +28,10 @@ CONFIG(release, debug|release) {
}
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..cb3707cf8c 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
}
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/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/mac/pbuilder_pbx.cpp b/qmake/generators/mac/pbuilder_pbx.cpp
index 3acc351b12..28cf02344d 100644
--- a/qmake/generators/mac/pbuilder_pbx.cpp
+++ b/qmake/generators/mac/pbuilder_pbx.cpp
@@ -513,7 +513,6 @@ bool
ProjectBuilderMakefileGenerator::writeMakeParts(QTextStream &t)
{
ProStringList tmp;
- bool did_preprocess = false;
//HEADER
const int pbVersion = pbuilderVersion();
@@ -731,7 +730,6 @@ ProjectBuilderMakefileGenerator::writeMakeParts(QTextStream &t)
QFile mkf(mkfile);
if(mkf.open(QIODevice::WriteOnly | QIODevice::Text)) {
writingUnixMakefileGenerator = true;
- did_preprocess = true;
debug_msg(1, "pbuilder: Creating file: %s", mkfile.toLatin1().constData());
QTextStream mkt(&mkf);
writeHeader(mkt);
diff --git a/qmake/generators/makefile.cpp b/qmake/generators/makefile.cpp
index f911f81020..aa1b50f2ef 100644
--- a/qmake/generators/makefile.cpp
+++ b/qmake/generators/makefile.cpp
@@ -892,7 +892,7 @@ MakefileGenerator::processPrlFile(QString &file, bool baseOnly)
bool
MakefileGenerator::processPrlFileBase(QString &origFile, const QStringRef &origName,
- const QStringRef &fixedBase, int slashOff)
+ const QStringRef &fixedBase, int /*slashOff*/)
{
return processPrlFileCore(origFile, origName, fixedBase + Option::prl_ext);
}
@@ -1112,7 +1112,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);
@@ -2369,8 +2370,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))
@@ -3355,42 +3363,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
@@ -3433,19 +3443,23 @@ QString MakefileGenerator::installMetaFile(const ProKey &replace_rule, const QSt
|| project->isActiveConfig("no_sed_meta_install")) {
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..1995cf63ba 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] == '"') {
diff --git a/qmake/generators/projectgenerator.h b/qmake/generators/projectgenerator.h
index e9b050cc74..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_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/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/generators/win32/winmakefile.cpp b/qmake/generators/win32/winmakefile.cpp
index 8e0c62e13f..b79fd8f250 100644
--- a/qmake/generators/win32/winmakefile.cpp
+++ b/qmake/generators/win32/winmakefile.cpp
@@ -645,7 +645,7 @@ void Win32MakefileGenerator::writeObjectsPart(QTextStream &t)
t << "OBJECTS = " << valList(escapeDependencyPaths(project->values("OBJECTS"))) << Qt::endl;
}
-void Win32MakefileGenerator::writeImplicitRulesPart(QTextStream &t)
+void Win32MakefileGenerator::writeImplicitRulesPart(QTextStream &)
{
}
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.
+
+![Default allocation algorithm](../gfx/Linear_allocator_1_algo_default.png)
+
+Sometimes there is a need to use simpler, linear allocation algorithm. You can
+create custom pool that uses such algorithm by adding flag
+#VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating
+#VmaPool object. Then an alternative metadata management is used. It always
+creates new allocations after last one and doesn't reuse free regions after
+allocations freed in the middle. It results in better allocation performance and
+less memory consumed by metadata.
+
+![Linear allocation algorithm](../gfx/Linear_allocator_2_algo_linear.png)
+
+With this one flag, you can create a custom pool that can be used in many ways:
+free-at-once, stack, double stack, and ring buffer. See below for details.
+
+\subsection linear_algorithm_free_at_once Free-at-once
+
+In a pool that uses linear algorithm, you still need to free all the allocations
+individually, e.g. by using vmaFreeMemory() or vmaDestroyBuffer(). You can free
+them in any order. New allocations are always made after last one - free space
+in the middle is not reused. However, when you release all the allocation and
+the pool becomes empty, allocation starts from the beginning again. This way you
+can use linear algorithm to speed up creation of allocations that you are going
+to release all at once.
+
+![Free-at-once](../gfx/Linear_allocator_3_free_at_once.png)
+
+This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
+value that allows multiple memory blocks.
+
+\subsection linear_algorithm_stack Stack
+
+When you free an allocation that was created last, its space can be reused.
+Thanks to this, if you always release allocations in the order opposite to their
+creation (LIFO - Last In First Out), you can achieve behavior of a stack.
+
+![Stack](../gfx/Linear_allocator_4_stack.png)
+
+This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
+value that allows multiple memory blocks.
+
+\subsection linear_algorithm_double_stack Double stack
+
+The space reserved by a custom pool with linear algorithm may be used by two
+stacks:
+
+- First, default one, growing up from offset 0.
+- Second, "upper" one, growing down from the end towards lower offsets.
+
+To make allocation from upper stack, add flag #VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT
+to VmaAllocationCreateInfo::flags.
+
+![Double stack](../gfx/Linear_allocator_7_double_stack.png)
+
+Double stack is available only in pools with one memory block -
+VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
+
+When the two stacks' ends meet so there is not enough space between them for a
+new allocation, such allocation fails with usual
+`VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
+
+\subsection linear_algorithm_ring_buffer Ring buffer
+
+When you free some allocations from the beginning and there is not enough free space
+for a new one at the end of a pool, allocator's "cursor" wraps around to the
+beginning and starts allocation there. Thanks to this, if you always release
+allocations in the same order as you created them (FIFO - First In First Out),
+you can achieve behavior of a ring buffer / queue.
+
+![Ring buffer](../gfx/Linear_allocator_5_ring_buffer.png)
+
+Pools with linear algorithm support [lost allocations](@ref lost_allocations) when used as ring buffer.
+If there is not enough free space for a new allocation, but existing allocations
+from the front of the queue can become lost, they become lost and the allocation
+succeeds.
+
+![Ring buffer with lost allocations](../gfx/Linear_allocator_6_ring_buffer_lost.png)
+
+Ring buffer is available only in pools with one memory block -
+VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
+
+\section buddy_algorithm Buddy allocation algorithm
+
+There is another allocation algorithm that can be used with custom pools, called
+"buddy". Its internal data structure is based on a tree of blocks, each having
+size that is a power of two and a half of its parent's size. When you want to
+allocate memory of certain size, a free node in the tree is located. If it's too
+large, it is recursively split into two halves (called "buddies"). However, if
+requested allocation size is not a power of two, the size of a tree node is
+aligned up to the nearest power of two and the remaining space is wasted. When
+two buddy nodes become free, they are merged back into one larger node.
+
+![Buddy allocator](../gfx/Buddy_allocator.png)
+
+The advantage of buddy allocation algorithm over default algorithm is faster
+allocation and deallocation, as well as smaller external fragmentation. The
+disadvantage is more wasted space (internal fragmentation).
+
+For more information, please read ["Buddy memory allocation" on Wikipedia](https://en.wikipedia.org/wiki/Buddy_memory_allocation)
+or other sources that describe this concept in general.
+
+To use buddy allocation algorithm with a custom pool, add flag
+#VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating
+#VmaPool object.
+
+Several limitations apply to pools that use buddy algorithm:
+
+- It is recommended to use VmaPoolCreateInfo::blockSize that is a power of two.
+ Otherwise, only largest power of two smaller than the size is used for
+ allocations. The remaining space always stays unused.
+- [Margins](@ref debugging_memory_usage_margins) and
+ [corruption detection](@ref debugging_memory_usage_corruption_detection)
+ don't work in such pools.
+- [Lost allocations](@ref lost_allocations) don't work in such pools. You can
+ use them, but they never become lost. Support may be added in the future.
+- [Defragmentation](@ref defragmentation) doesn't work with allocations made from
+ such pool.
+
+\page defragmentation Defragmentation
+
+Interleaved allocations and deallocations of many objects of varying size can
+cause fragmentation over time, which can lead to a situation where the library is unable
+to find a continuous range of free memory for a new allocation despite there is
+enough free space, just scattered across many small free ranges between existing
+allocations.
+
+To mitigate this problem, you can use defragmentation feature:
+structure #VmaDefragmentationInfo2, function vmaDefragmentationBegin(), vmaDefragmentationEnd().
+Given set of allocations,
+this function can move them to compact used memory, ensure more continuous free
+space and possibly also free some `VkDeviceMemory` blocks.
+
+What the defragmentation does is:
+
+- Updates #VmaAllocation objects to point to new `VkDeviceMemory` and offset.
+ After allocation has been moved, its VmaAllocationInfo::deviceMemory and/or
+ VmaAllocationInfo::offset changes. You must query them again using
+ vmaGetAllocationInfo() if you need them.
+- Moves actual data in memory.
+
+What it doesn't do, so you need to do it yourself:
+
+- Recreate buffers and images that were bound to allocations that were defragmented and
+ bind them with their new places in memory.
+ You must use `vkDestroyBuffer()`, `vkDestroyImage()`,
+ `vkCreateBuffer()`, `vkCreateImage()` for that purpose and NOT vmaDestroyBuffer(),
+ vmaDestroyImage(), vmaCreateBuffer(), vmaCreateImage(), because you don't need to
+ destroy or create allocation objects!
+- Recreate views and update descriptors that point to these buffers and images.
+
+\section defragmentation_cpu Defragmenting CPU memory
+
+Following example demonstrates how you can run defragmentation on CPU.
+Only allocations created in memory types that are `HOST_VISIBLE` can be defragmented.
+Others are ignored.
+
+The way it works is:
+
+- It temporarily maps entire memory blocks when necessary.
+- It moves data using `memmove()` function.
+
+\code
+// Given following variables already initialized:
+VkDevice device;
+VmaAllocator allocator;
+std::vector<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`).
+
+![Allocations without margin](../gfx/Margins_1.png)
+
+Define macro `VMA_DEBUG_MARGIN` to some non-zero value (e.g. 16) to enforce specified
+number of bytes as a margin before and after every allocation.
+
+\code
+#define VMA_DEBUG_MARGIN 16
+#include "vk_mem_alloc.h"
+\endcode
+
+![Allocations with margin](../gfx/Margins_2.png)
+
+If your bug goes away after enabling margins, it means it may be caused by memory
+being overwritten outside of allocation boundaries. It is not 100% certain though.
+Change in application behavior may also be caused by different order and distribution
+of allocations across memory blocks after margins are applied.
+
+The margin is applied also before first and after last allocation in a block.
+It may occur only once between two adjacent allocations.
+
+Margins work with all types of memory.
+
+Margin is applied only to allocations made out of memory blocks and not to dedicated
+allocations, which have their own memory block of specific size.
+It is thus not applied to allocations made using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT flag
+or those automatically decided to put into dedicated allocations, e.g. due to its
+large size or recommended by VK_KHR_dedicated_allocation extension.
+Margins are also not active in custom pools created with #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag.
+
+Margins appear in [JSON dump](@ref statistics_json_dump) as part of free space.
+
+Note that enabling margins increases memory usage and fragmentation.
+
+\section debugging_memory_usage_corruption_detection Corruption detection
+
+You can additionally define macro `VMA_DEBUG_DETECT_CORRUPTION` to 1 to enable validation
+of contents of the margins.
+
+\code
+#define VMA_DEBUG_MARGIN 16
+#define VMA_DEBUG_DETECT_CORRUPTION 1
+#include "vk_mem_alloc.h"
+\endcode
+
+When this feature is enabled, number of bytes specified as `VMA_DEBUG_MARGIN`
+(it must be multiply of 4) before and after every allocation is filled with a magic number.
+This idea is also know as "canary".
+Memory is automatically mapped and unmapped if necessary.
+
+This number is validated automatically when the allocation is destroyed.
+If it's not equal to the expected value, `VMA_ASSERT()` is executed.
+It clearly means that either CPU or GPU overwritten the memory outside of boundaries of the allocation,
+which indicates a serious bug.
+
+You can also explicitly request checking margins of all allocations in all memory blocks
+that belong to specified memory types by using function vmaCheckCorruption(),
+or in memory blocks that belong to specified custom pool, by using function
+vmaCheckPoolCorruption().
+
+Margin validation (corruption detection) works only for memory types that are
+`HOST_VISIBLE` and `HOST_COHERENT`.
+
+
+\page record_and_replay Record and replay
+
+\section record_and_replay_introduction Introduction
+
+While using the library, sequence of calls to its functions together with their
+parameters can be recorded to a file and later replayed using standalone player
+application. It can be useful to:
+
+- Test correctness - check if same sequence of calls will not cause crash or
+ failures on a target platform.
+- Gather statistics - see number of allocations, peak memory usage, number of
+ calls etc.
+- Benchmark performance - see how much time it takes to replay the whole
+ sequence.
+
+\section record_and_replay_usage Usage
+
+<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 DedicatedAllocCalcStatsInfo(VmaStatInfo& outInfo)
+ {
+ VMA_ASSERT(m_Type == ALLOCATION_TYPE_DEDICATED);
+ outInfo.blockCount = 1;
+ outInfo.allocationCount = 1;
+ outInfo.unusedRangeCount = 0;
+ outInfo.usedBytes = m_Size;
+ outInfo.unusedBytes = 0;
+ outInfo.allocationSizeMin = outInfo.allocationSizeMax = m_Size;
+ outInfo.unusedRangeSizeMin = UINT64_MAX;
+ outInfo.unusedRangeSizeMax = 0;
+ }
+
+ void BlockAllocMap();
+ void BlockAllocUnmap();
+ VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData);
+ void DedicatedAllocUnmap(VmaAllocator hAllocator);
+
+#if VMA_STATS_STRING_ENABLED
+ uint32_t GetCreationFrameIndex() const { return m_CreationFrameIndex; }
+ uint32_t GetBufferImageUsage() const { return m_BufferImageUsage; }
+
+ void InitBufferImageUsage(uint32_t bufferImageUsage)
+ {
+ VMA_ASSERT(m_BufferImageUsage == 0);
+ m_BufferImageUsage = bufferImageUsage;
+ }
+
+ void PrintParameters(class VmaJsonWriter& json) const;
+#endif
+
+private:
+ VkDeviceSize m_Alignment;
+ VkDeviceSize m_Size;
+ void* m_pUserData;
+ VMA_ATOMIC_UINT32 m_LastUseFrameIndex;
+ uint8_t m_Type; // ALLOCATION_TYPE
+ uint8_t m_SuballocationType; // VmaSuballocationType
+ // Bit 0x80 is set when allocation was created with VMA_ALLOCATION_CREATE_MAPPED_BIT.
+ // Bits with mask 0x7F are reference counter for vmaMapMemory()/vmaUnmapMemory().
+ uint8_t m_MapCount;
+ uint8_t m_Flags; // enum FLAGS
+
+ // Allocation out of VmaDeviceMemoryBlock.
+ struct BlockAllocation
+ {
+ VmaPool m_hPool; // Null if belongs to general memory.
+ VmaDeviceMemoryBlock* m_Block;
+ VkDeviceSize m_Offset;
+ bool m_CanBecomeLost;
+ };
+
+ // Allocation for an object that has its own private VkDeviceMemory.
+ struct DedicatedAllocation
+ {
+ uint32_t m_MemoryTypeIndex;
+ VkDeviceMemory m_hMemory;
+ void* m_pMappedData; // Not null means memory is mapped.
+ };
+
+ union
+ {
+ // Allocation out of VmaDeviceMemoryBlock.
+ BlockAllocation m_BlockAllocation;
+ // Allocation for an object that has its own private VkDeviceMemory.
+ DedicatedAllocation m_DedicatedAllocation;
+ };
+
+#if VMA_STATS_STRING_ENABLED
+ uint32_t m_CreationFrameIndex;
+ uint32_t m_BufferImageUsage; // 0 if unknown.
+#endif
+
+ void FreeUserDataString(VmaAllocator hAllocator);
+};
+
+/*
+Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as
+allocated memory block or free.
+*/
+struct VmaSuballocation
+{
+ VkDeviceSize offset;
+ VkDeviceSize size;
+ VmaAllocation hAllocation;
+ VmaSuballocationType type;
+};
+
+// Comparator for offsets.
+struct VmaSuballocationOffsetLess
+{
+ bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
+ {
+ return lhs.offset < rhs.offset;
+ }
+};
+struct VmaSuballocationOffsetGreater
+{
+ bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
+ {
+ return lhs.offset > rhs.offset;
+ }
+};
+
+typedef VmaList< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > VmaSuballocationList;
+
+// Cost of one additional allocation lost, as equivalent in bytes.
+static const VkDeviceSize VMA_LOST_ALLOCATION_COST = 1048576;
+
+/*
+Parameters of planned allocation inside a VmaDeviceMemoryBlock.
+
+If canMakeOtherLost was false:
+- item points to a FREE suballocation.
+- itemsToMakeLostCount is 0.
+
+If canMakeOtherLost was true:
+- item points to first of sequence of suballocations, which are either FREE,
+ or point to VmaAllocations that can become lost.
+- itemsToMakeLostCount is the number of VmaAllocations that need to be made lost for
+ the requested allocation to succeed.
+*/
+struct VmaAllocationRequest
+{
+ VkDeviceSize offset;
+ VkDeviceSize sumFreeSize; // Sum size of free items that overlap with proposed allocation.
+ VkDeviceSize sumItemSize; // Sum size of items to make lost that overlap with proposed allocation.
+ VmaSuballocationList::iterator item;
+ size_t itemsToMakeLostCount;
+ void* customData;
+
+ VkDeviceSize CalcCost() const
+ {
+ return sumItemSize + itemsToMakeLostCount * VMA_LOST_ALLOCATION_COST;
+ }
+};
+
+/*
+Data structure used for bookkeeping of allocations and unused ranges of memory
+in a single VkDeviceMemory block.
+*/
+class VmaBlockMetadata
+{
+public:
+ VmaBlockMetadata(VmaAllocator hAllocator);
+ virtual ~VmaBlockMetadata() { }
+ virtual void Init(VkDeviceSize size) { m_Size = size; }
+
+ // Validates all data structures inside this object. If not valid, returns false.
+ virtual bool Validate() const = 0;
+ VkDeviceSize GetSize() const { return m_Size; }
+ virtual size_t GetAllocationCount() const = 0;
+ virtual VkDeviceSize GetSumFreeSize() const = 0;
+ virtual VkDeviceSize GetUnusedRangeSizeMax() const = 0;
+ // Returns true if this block is empty - contains only single free suballocation.
+ virtual bool IsEmpty() const = 0;
+
+ virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const = 0;
+ // Shouldn't modify blockCount.
+ virtual void AddPoolStats(VmaPoolStats& inoutStats) const = 0;
+
+#if VMA_STATS_STRING_ENABLED
+ virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0;
+#endif
+
+ // Tries to find a place for suballocation with given parameters inside this block.
+ // If succeeded, fills pAllocationRequest and returns true.
+ // If failed, returns false.
+ virtual bool CreateAllocationRequest(
+ uint32_t currentFrameIndex,
+ uint32_t frameInUseCount,
+ VkDeviceSize bufferImageGranularity,
+ VkDeviceSize allocSize,
+ VkDeviceSize allocAlignment,
+ bool upperAddress,
+ VmaSuballocationType allocType,
+ bool canMakeOtherLost,
+ // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags.
+ uint32_t strategy,
+ VmaAllocationRequest* pAllocationRequest) = 0;
+
+ virtual bool MakeRequestedAllocationsLost(
+ uint32_t currentFrameIndex,
+ uint32_t frameInUseCount,
+ VmaAllocationRequest* pAllocationRequest) = 0;
+
+ virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) = 0;
+
+ virtual VkResult CheckCorruption(const void* pBlockData) = 0;
+
+ // Makes actual allocation based on request. Request must already be checked and valid.
+ virtual void Alloc(
+ const VmaAllocationRequest& request,
+ VmaSuballocationType type,
+ VkDeviceSize allocSize,
+ bool upperAddress,
+ VmaAllocation hAllocation) = 0;
+
+ // Frees suballocation assigned to given memory region.
+ virtual void Free(const VmaAllocation allocation) = 0;
+ virtual void FreeAtOffset(VkDeviceSize offset) = 0;
+
+ // Tries to resize (grow or shrink) space for given allocation, in place.
+ virtual bool ResizeAllocation(const VmaAllocation /*alloc*/, VkDeviceSize /*newSize*/) { return false; }
+
+protected:
+ const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; }
+
+#if VMA_STATS_STRING_ENABLED
+ void PrintDetailedMap_Begin(class VmaJsonWriter& json,
+ VkDeviceSize unusedBytes,
+ size_t allocationCount,
+ size_t unusedRangeCount) const;
+ void PrintDetailedMap_Allocation(class VmaJsonWriter& json,
+ VkDeviceSize offset,
+ VmaAllocation hAllocation) const;
+ void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
+ VkDeviceSize offset,
+ VkDeviceSize size) const;
+ void PrintDetailedMap_End(class VmaJsonWriter& json) const;
+#endif
+
+private:
+ VkDeviceSize m_Size;
+ const VkAllocationCallbacks* m_pAllocationCallbacks;
+};
+
+#define VMA_VALIDATE(cond) do { if(!(cond)) { \
+ VMA_ASSERT(0 && "Validation failed: " #cond); \
+ return false; \
+ } } while(false)
+
+class VmaBlockMetadata_Generic : public VmaBlockMetadata
+{
+ VMA_CLASS_NO_COPY(VmaBlockMetadata_Generic)
+public:
+ VmaBlockMetadata_Generic(VmaAllocator hAllocator);
+ virtual ~VmaBlockMetadata_Generic();
+ virtual void Init(VkDeviceSize size);
+
+ virtual bool Validate() const;
+ virtual size_t GetAllocationCount() const { return m_Suballocations.size() - m_FreeCount; }
+ virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
+ virtual VkDeviceSize GetUnusedRangeSizeMax() const;
+ virtual bool IsEmpty() const;
+
+ virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
+ virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
+
+#if VMA_STATS_STRING_ENABLED
+ virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
+#endif
+
+ virtual bool CreateAllocationRequest(
+ uint32_t currentFrameIndex,
+ uint32_t frameInUseCount,
+ VkDeviceSize bufferImageGranularity,
+ VkDeviceSize allocSize,
+ VkDeviceSize allocAlignment,
+ bool upperAddress,
+ VmaSuballocationType allocType,
+ bool canMakeOtherLost,
+ uint32_t strategy,
+ VmaAllocationRequest* pAllocationRequest);
+
+ virtual bool MakeRequestedAllocationsLost(
+ uint32_t currentFrameIndex,
+ uint32_t frameInUseCount,
+ VmaAllocationRequest* pAllocationRequest);
+
+ virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
+
+ virtual VkResult CheckCorruption(const void* pBlockData);
+
+ virtual void Alloc(
+ const VmaAllocationRequest& request,
+ VmaSuballocationType type,
+ VkDeviceSize allocSize,
+ bool upperAddress,
+ VmaAllocation hAllocation);
+
+ virtual void Free(const VmaAllocation allocation);
+ virtual void FreeAtOffset(VkDeviceSize offset);
+
+ virtual bool ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize);
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // For defragmentation
+
+ bool IsBufferImageGranularityConflictPossible(
+ VkDeviceSize bufferImageGranularity,
+ VmaSuballocationType& inOutPrevSuballocType) const;
+
+private:
+ friend class VmaDefragmentationAlgorithm_Generic;
+ friend class VmaDefragmentationAlgorithm_Fast;
+
+ uint32_t m_FreeCount;
+ VkDeviceSize m_SumFreeSize;
+ VmaSuballocationList m_Suballocations;
+ // Suballocations that are free and have size greater than certain threshold.
+ // Sorted by size, ascending.
+ VmaVector< VmaSuballocationList::iterator, VmaStlAllocator< VmaSuballocationList::iterator > > m_FreeSuballocationsBySize;
+
+ bool ValidateFreeSuballocationList() const;
+
+ // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.
+ // If yes, fills pOffset and returns true. If no, returns false.
+ bool CheckAllocation(
+ uint32_t currentFrameIndex,
+ uint32_t frameInUseCount,
+ VkDeviceSize bufferImageGranularity,
+ VkDeviceSize allocSize,
+ VkDeviceSize allocAlignment,
+ VmaSuballocationType allocType,
+ VmaSuballocationList::const_iterator suballocItem,
+ bool canMakeOtherLost,
+ VkDeviceSize* pOffset,
+ size_t* itemsToMakeLostCount,
+ VkDeviceSize* pSumFreeSize,
+ VkDeviceSize* pSumItemSize) const;
+ // Given free suballocation, it merges it with following one, which must also be free.
+ void MergeFreeWithNext(VmaSuballocationList::iterator item);
+ // Releases given suballocation, making it free.
+ // Merges it with adjacent free suballocations if applicable.
+ // Returns iterator to new free suballocation at this place.
+ VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem);
+ // Given free suballocation, it inserts it into sorted list of
+ // m_FreeSuballocationsBySize if it's suitable.
+ void RegisterFreeSuballocation(VmaSuballocationList::iterator item);
+ // Given free suballocation, it removes it from sorted list of
+ // m_FreeSuballocationsBySize if it's suitable.
+ void UnregisterFreeSuballocation(VmaSuballocationList::iterator item);
+};
+
+/*
+Allocations and their references in internal data structure look like this:
+
+if(m_2ndVectorMode == SECOND_VECTOR_EMPTY):
+
+ 0 +-------+
+ | |
+ | |
+ | |
+ +-------+
+ | Alloc | 1st[m_1stNullItemsBeginCount]
+ +-------+
+ | Alloc | 1st[m_1stNullItemsBeginCount + 1]
+ +-------+
+ | ... |
+ +-------+
+ | Alloc | 1st[1st.size() - 1]
+ +-------+
+ | |
+ | |
+ | |
+GetSize() +-------+
+
+if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER):
+
+ 0 +-------+
+ | Alloc | 2nd[0]
+ +-------+
+ | Alloc | 2nd[1]
+ +-------+
+ | ... |
+ +-------+
+ | Alloc | 2nd[2nd.size() - 1]
+ +-------+
+ | |
+ | |
+ | |
+ +-------+
+ | Alloc | 1st[m_1stNullItemsBeginCount]
+ +-------+
+ | Alloc | 1st[m_1stNullItemsBeginCount + 1]
+ +-------+
+ | ... |
+ +-------+
+ | Alloc | 1st[1st.size() - 1]
+ +-------+
+ | |
+GetSize() +-------+
+
+if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK):
+
+ 0 +-------+
+ | |
+ | |
+ | |
+ +-------+
+ | Alloc | 1st[m_1stNullItemsBeginCount]
+ +-------+
+ | Alloc | 1st[m_1stNullItemsBeginCount + 1]
+ +-------+
+ | ... |
+ +-------+
+ | Alloc | 1st[1st.size() - 1]
+ +-------+
+ | |
+ | |
+ | |
+ +-------+
+ | Alloc | 2nd[2nd.size() - 1]
+ +-------+
+ | ... |
+ +-------+
+ | Alloc | 2nd[1]
+ +-------+
+ | Alloc | 2nd[0]
+GetSize() +-------+
+
+*/
+class VmaBlockMetadata_Linear : public VmaBlockMetadata
+{
+ VMA_CLASS_NO_COPY(VmaBlockMetadata_Linear)
+public:
+ VmaBlockMetadata_Linear(VmaAllocator hAllocator);
+ virtual ~VmaBlockMetadata_Linear();
+ virtual void Init(VkDeviceSize size);
+
+ virtual bool Validate() const;
+ virtual size_t GetAllocationCount() const;
+ virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
+ virtual VkDeviceSize GetUnusedRangeSizeMax() const;
+ virtual bool IsEmpty() const { return GetAllocationCount() == 0; }
+
+ virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
+ virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
+
+#if VMA_STATS_STRING_ENABLED
+ virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
+#endif
+
+ virtual bool CreateAllocationRequest(
+ uint32_t currentFrameIndex,
+ uint32_t frameInUseCount,
+ VkDeviceSize bufferImageGranularity,
+ VkDeviceSize allocSize,
+ VkDeviceSize allocAlignment,
+ bool upperAddress,
+ VmaSuballocationType allocType,
+ bool canMakeOtherLost,
+ uint32_t strategy,
+ VmaAllocationRequest* pAllocationRequest);
+
+ virtual bool MakeRequestedAllocationsLost(
+ uint32_t currentFrameIndex,
+ uint32_t frameInUseCount,
+ VmaAllocationRequest* pAllocationRequest);
+
+ virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
+
+ virtual VkResult CheckCorruption(const void* pBlockData);
+
+ virtual void Alloc(
+ const VmaAllocationRequest& request,
+ VmaSuballocationType type,
+ VkDeviceSize allocSize,
+ bool upperAddress,
+ VmaAllocation hAllocation);
+
+ virtual void Free(const VmaAllocation allocation);
+ virtual void FreeAtOffset(VkDeviceSize offset);
+
+private:
+ /*
+ There are two suballocation vectors, used in ping-pong way.
+ The one with index m_1stVectorIndex is called 1st.
+ The one with index (m_1stVectorIndex ^ 1) is called 2nd.
+ 2nd can be non-empty only when 1st is not empty.
+ When 2nd is not empty, m_2ndVectorMode indicates its mode of operation.
+ */
+ typedef VmaVector< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > SuballocationVectorType;
+
+ enum SECOND_VECTOR_MODE
+ {
+ SECOND_VECTOR_EMPTY,
+ /*
+ Suballocations in 2nd vector are created later than the ones in 1st, but they
+ all have smaller offset.
+ */
+ SECOND_VECTOR_RING_BUFFER,
+ /*
+ Suballocations in 2nd vector are upper side of double stack.
+ They all have offsets higher than those in 1st vector.
+ Top of this stack means smaller offsets, but higher indices in this vector.
+ */
+ SECOND_VECTOR_DOUBLE_STACK,
+ };
+
+ VkDeviceSize m_SumFreeSize;
+ SuballocationVectorType m_Suballocations0, m_Suballocations1;
+ uint32_t m_1stVectorIndex;
+ SECOND_VECTOR_MODE m_2ndVectorMode;
+
+ SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
+ SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
+ const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
+ const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
+
+ // Number of items in 1st vector with hAllocation = null at the beginning.
+ size_t m_1stNullItemsBeginCount;
+ // Number of other items in 1st vector with hAllocation = null somewhere in the middle.
+ size_t m_1stNullItemsMiddleCount;
+ // Number of items in 2nd vector with hAllocation = null.
+ size_t m_2ndNullItemsCount;
+
+ bool ShouldCompact1st() const;
+ void CleanupAfterFree();
+};
+
+/*
+- GetSize() is the original size of allocated memory block.
+- m_UsableSize is this size aligned down to a power of two.
+ All allocations and calculations happen relative to m_UsableSize.
+- GetUnusableSize() is the difference between them.
+ It is repoted as separate, unused range, not available for allocations.
+
+Node at level 0 has size = m_UsableSize.
+Each next level contains nodes with size 2 times smaller than current level.
+m_LevelCount is the maximum number of levels to use in the current object.
+*/
+class VmaBlockMetadata_Buddy : public VmaBlockMetadata
+{
+ VMA_CLASS_NO_COPY(VmaBlockMetadata_Buddy)
+public:
+ VmaBlockMetadata_Buddy(VmaAllocator hAllocator);
+ virtual ~VmaBlockMetadata_Buddy();
+ virtual void Init(VkDeviceSize size);
+
+ virtual bool Validate() const;
+ virtual size_t GetAllocationCount() const { return m_AllocationCount; }
+ virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize + GetUnusableSize(); }
+ virtual VkDeviceSize GetUnusedRangeSizeMax() const;
+ virtual bool IsEmpty() const { return m_Root->type == Node::TYPE_FREE; }
+
+ virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
+ virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
+
+#if VMA_STATS_STRING_ENABLED
+ virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
+#endif
+
+ virtual bool CreateAllocationRequest(
+ uint32_t currentFrameIndex,
+ uint32_t frameInUseCount,
+ VkDeviceSize bufferImageGranularity,
+ VkDeviceSize allocSize,
+ VkDeviceSize allocAlignment,
+ bool upperAddress,
+ VmaSuballocationType allocType,
+ bool canMakeOtherLost,
+ uint32_t strategy,
+ VmaAllocationRequest* pAllocationRequest);
+
+ virtual bool MakeRequestedAllocationsLost(
+ uint32_t currentFrameIndex,
+ uint32_t frameInUseCount,
+ VmaAllocationRequest* pAllocationRequest);
+
+ virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
+
+ virtual VkResult CheckCorruption(const void* /*pBlockData*/) { return VK_ERROR_FEATURE_NOT_PRESENT; }
+
+ virtual void Alloc(
+ const VmaAllocationRequest& request,
+ VmaSuballocationType type,
+ VkDeviceSize allocSize,
+ bool upperAddress,
+ VmaAllocation hAllocation);
+
+ virtual void Free(const VmaAllocation allocation) { FreeAtOffset(allocation, allocation->GetOffset()); }
+ virtual void FreeAtOffset(VkDeviceSize offset) { FreeAtOffset(VMA_NULL, offset); }
+
+private:
+ static const VkDeviceSize MIN_NODE_SIZE = 32;
+ static const size_t MAX_LEVELS = 30;
+
+ struct ValidationContext
+ {
+ size_t calculatedAllocationCount;
+ size_t calculatedFreeCount;
+ VkDeviceSize calculatedSumFreeSize;
+
+ ValidationContext() :
+ calculatedAllocationCount(0),
+ calculatedFreeCount(0),
+ calculatedSumFreeSize(0) { }
+ };
+
+ struct Node
+ {
+ VkDeviceSize offset;
+ enum TYPE
+ {
+ TYPE_FREE,
+ TYPE_ALLOCATION,
+ TYPE_SPLIT,
+ TYPE_COUNT
+ } type;
+ Node* parent;
+ Node* buddy;
+
+ union
+ {
+ struct
+ {
+ Node* prev;
+ Node* next;
+ } free;
+ struct
+ {
+ VmaAllocation alloc;
+ } allocation;
+ struct
+ {
+ Node* leftChild;
+ } split;
+ };
+ };
+
+ // Size of the memory block aligned down to a power of two.
+ VkDeviceSize m_UsableSize;
+ uint32_t m_LevelCount;
+
+ Node* m_Root;
+ struct {
+ Node* front;
+ Node* back;
+ } m_FreeList[MAX_LEVELS];
+ // Number of nodes in the tree with type == TYPE_ALLOCATION.
+ size_t m_AllocationCount;
+ // Number of nodes in the tree with type == TYPE_FREE.
+ size_t m_FreeCount;
+ // This includes space wasted due to internal fragmentation. Doesn't include unusable size.
+ VkDeviceSize m_SumFreeSize;
+
+ VkDeviceSize GetUnusableSize() const { return GetSize() - m_UsableSize; }
+ void DeleteNode(Node* node);
+ bool ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const;
+ uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const;
+ inline VkDeviceSize LevelToNodeSize(uint32_t level) const { return m_UsableSize >> level; }
+ // Alloc passed just for validation. Can be null.
+ void FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset);
+ void CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const;
+ // Adds node to the front of FreeList at given level.
+ // node->type must be FREE.
+ // node->free.prev, next can be undefined.
+ void AddToFreeListFront(uint32_t level, Node* node);
+ // Removes node from FreeList at given level.
+ // node->type must be FREE.
+ // node->free.prev, next stay untouched.
+ void RemoveFromFreeList(uint32_t level, Node* node);
+
+#if VMA_STATS_STRING_ENABLED
+ void PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const;
+#endif
+};
+
+/*
+Represents a single block of device memory (`VkDeviceMemory`) with all the
+data about its regions (aka suballocations, #VmaAllocation), assigned and free.
+
+Thread-safety: This class must be externally synchronized.
+*/
+class VmaDeviceMemoryBlock
+{
+ VMA_CLASS_NO_COPY(VmaDeviceMemoryBlock)
+public:
+ VmaBlockMetadata* m_pMetadata;
+
+ VmaDeviceMemoryBlock(VmaAllocator hAllocator);
+
+ ~VmaDeviceMemoryBlock()
+ {
+ VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped.");
+ VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
+ }
+
+ // Always call after construction.
+ void Init(
+ VmaAllocator hAllocator,
+ uint32_t newMemoryTypeIndex,
+ VkDeviceMemory newMemory,
+ VkDeviceSize newSize,
+ uint32_t id,
+ uint32_t algorithm);
+ // Always call before destruction.
+ void Destroy(VmaAllocator allocator);
+
+ VkDeviceMemory GetDeviceMemory() const { return m_hMemory; }
+ uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
+ uint32_t GetId() const { return m_Id; }
+ void* GetMappedData() const { return m_pMappedData; }
+
+ // Validates all data structures inside this object. If not valid, returns false.
+ bool Validate() const;
+
+ VkResult CheckCorruption(VmaAllocator hAllocator);
+
+ // ppData can be null.
+ VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);
+ void Unmap(VmaAllocator hAllocator, uint32_t count);
+
+ VkResult WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
+ VkResult ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
+
+ VkResult BindBufferMemory(
+ const VmaAllocator hAllocator,
+ const VmaAllocation hAllocation,
+ VkBuffer hBuffer);
+ VkResult BindImageMemory(
+ const VmaAllocator hAllocator,
+ const VmaAllocation hAllocation,
+ VkImage hImage);
+
+private:
+ uint32_t m_MemoryTypeIndex;
+ uint32_t m_Id;
+ VkDeviceMemory m_hMemory;
+
+ /*
+ Protects access to m_hMemory so it's not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory.
+ Also protects m_MapCount, m_pMappedData.
+ Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex.
+ */
+ VMA_MUTEX m_Mutex;
+ uint32_t m_MapCount;
+ void* m_pMappedData;
+};
+
+struct VmaPointerLess
+{
+ bool operator()(const void* lhs, const void* rhs) const
+ {
+ return lhs < rhs;
+ }
+};
+
+struct VmaDefragmentationMove
+{
+ size_t srcBlockIndex;
+ size_t dstBlockIndex;
+ VkDeviceSize srcOffset;
+ VkDeviceSize dstOffset;
+ VkDeviceSize size;
+};
+
+class VmaDefragmentationAlgorithm;
+
+/*
+Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific
+Vulkan memory type.
+
+Synchronized internally with a mutex.
+*/
+struct VmaBlockVector
+{
+ VMA_CLASS_NO_COPY(VmaBlockVector)
+public:
+ VmaBlockVector(
+ VmaAllocator hAllocator,
+ uint32_t memoryTypeIndex,
+ VkDeviceSize preferredBlockSize,
+ size_t minBlockCount,
+ size_t maxBlockCount,
+ VkDeviceSize bufferImageGranularity,
+ uint32_t frameInUseCount,
+ bool isCustomPool,
+ bool explicitBlockSize,
+ uint32_t algorithm);
+ ~VmaBlockVector();
+
+ VkResult CreateMinBlocks();
+
+ uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
+ VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }
+ VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
+ uint32_t GetFrameInUseCount() const { return m_FrameInUseCount; }
+ uint32_t GetAlgorithm() const { return m_Algorithm; }
+
+ void GetPoolStats(VmaPoolStats* pStats);
+
+ bool IsEmpty() const { return m_Blocks.empty(); }
+ bool IsCorruptionDetectionEnabled() const;
+
+ VkResult Allocate(
+ VmaPool hCurrentPool,
+ uint32_t currentFrameIndex,
+ VkDeviceSize size,
+ VkDeviceSize alignment,
+ const VmaAllocationCreateInfo& createInfo,
+ VmaSuballocationType suballocType,
+ size_t allocationCount,
+ VmaAllocation* pAllocations);
+
+ void Free(
+ VmaAllocation hAllocation);
+
+ // Adds statistics of this BlockVector to pStats.
+ void AddStats(VmaStats* pStats);
+
+#if VMA_STATS_STRING_ENABLED
+ void PrintDetailedMap(class VmaJsonWriter& json);
+#endif
+
+ void MakePoolAllocationsLost(
+ uint32_t currentFrameIndex,
+ size_t* pLostAllocationCount);
+ VkResult CheckCorruption();
+
+ // Saves results in pCtx->res.
+ void Defragment(
+ class VmaBlockVectorDefragmentationContext* pCtx,
+ VmaDefragmentationStats* pStats,
+ VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
+ VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
+ VkCommandBuffer commandBuffer);
+ void DefragmentationEnd(
+ class VmaBlockVectorDefragmentationContext* pCtx,
+ VmaDefragmentationStats* pStats);
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // To be used only while the m_Mutex is locked. Used during defragmentation.
+
+ size_t GetBlockCount() const { return m_Blocks.size(); }
+ VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; }
+ size_t CalcAllocationCount() const;
+ bool IsBufferImageGranularityConflictPossible() const;
+
+private:
+ friend class VmaDefragmentationAlgorithm_Generic;
+
+ const VmaAllocator m_hAllocator;
+ const uint32_t m_MemoryTypeIndex;
+ const VkDeviceSize m_PreferredBlockSize;
+ const size_t m_MinBlockCount;
+ const size_t m_MaxBlockCount;
+ const VkDeviceSize m_BufferImageGranularity;
+ const uint32_t m_FrameInUseCount;
+ const bool m_IsCustomPool;
+ const bool m_ExplicitBlockSize;
+ const uint32_t m_Algorithm;
+ /* There can be at most one allocation that is completely empty - a
+ hysteresis to avoid pessimistic case of alternating creation and destruction
+ of a VkDeviceMemory. */
+ bool m_HasEmptyBlock;
+ VMA_RW_MUTEX m_Mutex;
+ // Incrementally sorted by sumFreeSize, ascending.
+ VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks;
+ uint32_t m_NextBlockId;
+
+ VkDeviceSize CalcMaxBlockSize() const;
+
+ // Finds and removes given block from vector.
+ void Remove(VmaDeviceMemoryBlock* pBlock);
+
+ // Performs single step in sorting m_Blocks. They may not be fully sorted
+ // after this call.
+ void IncrementallySortBlocks();
+
+ VkResult AllocatePage(
+ VmaPool hCurrentPool,
+ uint32_t currentFrameIndex,
+ VkDeviceSize size,
+ VkDeviceSize alignment,
+ const VmaAllocationCreateInfo& createInfo,
+ VmaSuballocationType suballocType,
+ VmaAllocation* pAllocation);
+
+ // To be used only without CAN_MAKE_OTHER_LOST flag.
+ VkResult AllocateFromBlock(
+ VmaDeviceMemoryBlock* pBlock,
+ VmaPool hCurrentPool,
+ uint32_t currentFrameIndex,
+ VkDeviceSize size,
+ VkDeviceSize alignment,
+ VmaAllocationCreateFlags allocFlags,
+ void* pUserData,
+ VmaSuballocationType suballocType,
+ uint32_t strategy,
+ VmaAllocation* pAllocation);
+
+ VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
+
+ // Saves result to pCtx->res.
+ void ApplyDefragmentationMovesCpu(
+ class VmaBlockVectorDefragmentationContext* pDefragCtx,
+ const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves);
+ // Saves result to pCtx->res.
+ void ApplyDefragmentationMovesGpu(
+ class VmaBlockVectorDefragmentationContext* pDefragCtx,
+ const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
+ VkCommandBuffer commandBuffer);
+
+ /*
+ Used during defragmentation. pDefragmentationStats is optional. It's in/out
+ - updated with new data.
+ */
+ void FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats);
+};
+
+struct VmaPool_T
+{
+ VMA_CLASS_NO_COPY(VmaPool_T)
+public:
+ VmaBlockVector m_BlockVector;
+
+ VmaPool_T(
+ VmaAllocator hAllocator,
+ const VmaPoolCreateInfo& createInfo,
+ VkDeviceSize preferredBlockSize);
+ ~VmaPool_T();
+
+ uint32_t GetId() const { return m_Id; }
+ void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; }
+
+#if VMA_STATS_STRING_ENABLED
+ //void PrintDetailedMap(class VmaStringBuilder& sb);
+#endif
+
+private:
+ uint32_t m_Id;
+};
+
+/*
+Performs defragmentation:
+
+- Updates `pBlockVector->m_pMetadata`.
+- Updates allocations by calling ChangeBlockAllocation() or ChangeOffset().
+- Does not move actual data, only returns requested moves as `moves`.
+*/
+class VmaDefragmentationAlgorithm
+{
+ VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)
+public:
+ VmaDefragmentationAlgorithm(
+ VmaAllocator hAllocator,
+ VmaBlockVector* pBlockVector,
+ uint32_t currentFrameIndex) :
+ m_hAllocator(hAllocator),
+ m_pBlockVector(pBlockVector),
+ m_CurrentFrameIndex(currentFrameIndex)
+ {
+ }
+ virtual ~VmaDefragmentationAlgorithm()
+ {
+ }
+
+ virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) = 0;
+ virtual void AddAll() = 0;
+
+ virtual VkResult Defragment(
+ VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
+ VkDeviceSize maxBytesToMove,
+ uint32_t maxAllocationsToMove) = 0;
+
+ virtual VkDeviceSize GetBytesMoved() const = 0;
+ virtual uint32_t GetAllocationsMoved() const = 0;
+
+protected:
+ VmaAllocator const m_hAllocator;
+ VmaBlockVector* const m_pBlockVector;
+ const uint32_t m_CurrentFrameIndex;
+
+ struct AllocationInfo
+ {
+ VmaAllocation m_hAllocation;
+ VkBool32* m_pChanged;
+
+ AllocationInfo() :
+ m_hAllocation(VK_NULL_HANDLE),
+ m_pChanged(VMA_NULL)
+ {
+ }
+ AllocationInfo(VmaAllocation hAlloc, VkBool32* pChanged) :
+ m_hAllocation(hAlloc),
+ m_pChanged(pChanged)
+ {
+ }
+ };
+};
+
+class VmaDefragmentationAlgorithm_Generic : public VmaDefragmentationAlgorithm
+{
+ VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Generic)
+public:
+ VmaDefragmentationAlgorithm_Generic(
+ VmaAllocator hAllocator,
+ VmaBlockVector* pBlockVector,
+ uint32_t currentFrameIndex,
+ bool overlappingMoveSupported);
+ virtual ~VmaDefragmentationAlgorithm_Generic();
+
+ virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
+ virtual void AddAll() { m_AllAllocations = true; }
+
+ virtual VkResult Defragment(
+ VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
+ VkDeviceSize maxBytesToMove,
+ uint32_t maxAllocationsToMove);
+
+ virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
+ virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
+
+private:
+ uint32_t m_AllocationCount;
+ bool m_AllAllocations;
+
+ VkDeviceSize m_BytesMoved;
+ uint32_t m_AllocationsMoved;
+
+ struct AllocationInfoSizeGreater
+ {
+ bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
+ {
+ return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize();
+ }
+ };
+
+ struct AllocationInfoOffsetGreater
+ {
+ bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
+ {
+ return lhs.m_hAllocation->GetOffset() > rhs.m_hAllocation->GetOffset();
+ }
+ };
+
+ struct BlockInfo
+ {
+ size_t m_OriginalBlockIndex;
+ VmaDeviceMemoryBlock* m_pBlock;
+ bool m_HasNonMovableAllocations;
+ VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations;
+
+ BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) :
+ m_OriginalBlockIndex(SIZE_MAX),
+ m_pBlock(VMA_NULL),
+ m_HasNonMovableAllocations(true),
+ m_Allocations(pAllocationCallbacks)
+ {
+ }
+
+ void CalcHasNonMovableAllocations()
+ {
+ const size_t blockAllocCount = m_pBlock->m_pMetadata->GetAllocationCount();
+ const size_t defragmentAllocCount = m_Allocations.size();
+ m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount;
+ }
+
+ void SortAllocationsBySizeDescending()
+ {
+ VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater());
+ }
+
+ void SortAllocationsByOffsetDescending()
+ {
+ VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoOffsetGreater());
+ }
+ };
+
+ struct BlockPointerLess
+ {
+ bool operator()(const BlockInfo* pLhsBlockInfo, const VmaDeviceMemoryBlock* pRhsBlock) const
+ {
+ return pLhsBlockInfo->m_pBlock < pRhsBlock;
+ }
+ bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
+ {
+ return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock;
+ }
+ };
+
+ // 1. Blocks with some non-movable allocations go first.
+ // 2. Blocks with smaller sumFreeSize go first.
+ struct BlockInfoCompareMoveDestination
+ {
+ bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
+ {
+ if(pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations)
+ {
+ return true;
+ }
+ if(!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations)
+ {
+ return false;
+ }
+ if(pLhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize() < pRhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize())
+ {
+ return true;
+ }
+ return false;
+ }
+ };
+
+ typedef VmaVector< BlockInfo*, VmaStlAllocator<BlockInfo*> > BlockInfoVector;
+ BlockInfoVector m_Blocks;
+
+ VkResult DefragmentRound(
+ VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
+ VkDeviceSize maxBytesToMove,
+ uint32_t maxAllocationsToMove);
+
+ size_t CalcBlocksWithNonMovableCount() const;
+
+ static bool MoveMakesSense(
+ size_t dstBlockIndex, VkDeviceSize dstOffset,
+ size_t srcBlockIndex, VkDeviceSize srcOffset);
+};
+
+class VmaDefragmentationAlgorithm_Fast : public VmaDefragmentationAlgorithm
+{
+ VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Fast)
+public:
+ VmaDefragmentationAlgorithm_Fast(
+ VmaAllocator hAllocator,
+ VmaBlockVector* pBlockVector,
+ uint32_t currentFrameIndex,
+ bool overlappingMoveSupported);
+ virtual ~VmaDefragmentationAlgorithm_Fast();
+
+ virtual void AddAllocation(VmaAllocation /*hAlloc*/, VkBool32* /*pChanged*/) { ++m_AllocationCount; }
+ virtual void AddAll() { m_AllAllocations = true; }
+
+ virtual VkResult Defragment(
+ VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
+ VkDeviceSize maxBytesToMove,
+ uint32_t maxAllocationsToMove);
+
+ virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
+ virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
+
+private:
+ struct BlockInfo
+ {
+ size_t origBlockIndex;
+ };
+
+ class FreeSpaceDatabase
+ {
+ public:
+ FreeSpaceDatabase()
+ {
+ FreeSpace s = {};
+ s.blockInfoIndex = SIZE_MAX;
+ for(size_t i = 0; i < MAX_COUNT; ++i)
+ {
+ m_FreeSpaces[i] = s;
+ }
+ }
+
+ void Register(size_t blockInfoIndex, VkDeviceSize offset, VkDeviceSize size)
+ {
+ if(size < VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
+ {
+ return;
+ }
+
+ // Find first invalid or the smallest structure.
+ size_t bestIndex = SIZE_MAX;
+ for(size_t i = 0; i < MAX_COUNT; ++i)
+ {
+ // Empty structure.
+ if(m_FreeSpaces[i].blockInfoIndex == SIZE_MAX)
+ {
+ bestIndex = i;
+ break;
+ }
+ if(m_FreeSpaces[i].size < size &&
+ (bestIndex == SIZE_MAX || m_FreeSpaces[bestIndex].size > m_FreeSpaces[i].size))
+ {
+ bestIndex = i;
+ }
+ }
+
+ if(bestIndex != SIZE_MAX)
+ {
+ m_FreeSpaces[bestIndex].blockInfoIndex = blockInfoIndex;
+ m_FreeSpaces[bestIndex].offset = offset;
+ m_FreeSpaces[bestIndex].size = size;
+ }
+ }
+
+ bool Fetch(VkDeviceSize alignment, VkDeviceSize size,
+ size_t& outBlockInfoIndex, VkDeviceSize& outDstOffset)
+ {
+ size_t bestIndex = SIZE_MAX;
+ VkDeviceSize bestFreeSpaceAfter = 0;
+ for(size_t i = 0; i < MAX_COUNT; ++i)
+ {
+ // Structure is valid.
+ if(m_FreeSpaces[i].blockInfoIndex != SIZE_MAX)
+ {
+ const VkDeviceSize dstOffset = VmaAlignUp(m_FreeSpaces[i].offset, alignment);
+ // Allocation fits into this structure.
+ if(dstOffset + size <= m_FreeSpaces[i].offset + m_FreeSpaces[i].size)
+ {
+ const VkDeviceSize freeSpaceAfter = (m_FreeSpaces[i].offset + m_FreeSpaces[i].size) -
+ (dstOffset + size);
+ if(bestIndex == SIZE_MAX || freeSpaceAfter > bestFreeSpaceAfter)
+ {
+ bestIndex = i;
+ bestFreeSpaceAfter = freeSpaceAfter;
+ }
+ }
+ }
+ }
+
+ if(bestIndex != SIZE_MAX)
+ {
+ outBlockInfoIndex = m_FreeSpaces[bestIndex].blockInfoIndex;
+ outDstOffset = VmaAlignUp(m_FreeSpaces[bestIndex].offset, alignment);
+
+ if(bestFreeSpaceAfter >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
+ {
+ // Leave this structure for remaining empty space.
+ const VkDeviceSize alignmentPlusSize = (outDstOffset - m_FreeSpaces[bestIndex].offset) + size;
+ m_FreeSpaces[bestIndex].offset += alignmentPlusSize;
+ m_FreeSpaces[bestIndex].size -= alignmentPlusSize;
+ }
+ else
+ {
+ // This structure becomes invalid.
+ m_FreeSpaces[bestIndex].blockInfoIndex = SIZE_MAX;
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ private:
+ static const size_t MAX_COUNT = 4;
+
+ struct FreeSpace
+ {
+ size_t blockInfoIndex; // SIZE_MAX means this structure is invalid.
+ VkDeviceSize offset;
+ VkDeviceSize size;
+ } m_FreeSpaces[MAX_COUNT];
+ };
+
+ const bool m_OverlappingMoveSupported;
+
+ uint32_t m_AllocationCount;
+ bool m_AllAllocations;
+
+ VkDeviceSize m_BytesMoved;
+ uint32_t m_AllocationsMoved;
+
+ VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> > m_BlockInfos;
+
+ void PreprocessMetadata();
+ void PostprocessMetadata();
+ void InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc);
+};
+
+struct VmaBlockDefragmentationContext
+{
+ enum BLOCK_FLAG
+ {
+ BLOCK_FLAG_USED = 0x00000001,
+ };
+ uint32_t flags;
+ VkBuffer hBuffer;
+
+ VmaBlockDefragmentationContext() :
+ flags(0),
+ hBuffer(VK_NULL_HANDLE)
+ {
+ }
+};
+
+class VmaBlockVectorDefragmentationContext
+{
+ VMA_CLASS_NO_COPY(VmaBlockVectorDefragmentationContext)
+public:
+ VkResult res;
+ bool mutexLocked;
+ VmaVector< VmaBlockDefragmentationContext, VmaStlAllocator<VmaBlockDefragmentationContext> > blockContexts;
+
+ VmaBlockVectorDefragmentationContext(
+ VmaAllocator hAllocator,
+ VmaPool hCustomPool, // Optional.
+ VmaBlockVector* pBlockVector,
+ uint32_t currFrameIndex,
+ uint32_t flags);
+ ~VmaBlockVectorDefragmentationContext();
+
+ VmaPool GetCustomPool() const { return m_hCustomPool; }
+ VmaBlockVector* GetBlockVector() const { return m_pBlockVector; }
+ VmaDefragmentationAlgorithm* GetAlgorithm() const { return m_pAlgorithm; }
+
+ void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
+ void AddAll() { m_AllAllocations = true; }
+
+ void Begin(bool overlappingMoveSupported);
+
+private:
+ const VmaAllocator m_hAllocator;
+ // Null if not from custom pool.
+ const VmaPool m_hCustomPool;
+ // Redundant, for convenience not to fetch from m_hCustomPool->m_BlockVector or m_hAllocator->m_pBlockVectors.
+ VmaBlockVector* const m_pBlockVector;
+ const uint32_t m_CurrFrameIndex;
+ /*const uint32_t m_AlgorithmFlags;*/
+ // Owner of this object.
+ VmaDefragmentationAlgorithm* m_pAlgorithm;
+
+ struct AllocInfo
+ {
+ VmaAllocation hAlloc;
+ VkBool32* pChanged;
+ };
+ // Used between constructor and Begin.
+ VmaVector< AllocInfo, VmaStlAllocator<AllocInfo> > m_Allocations;
+ bool m_AllAllocations;
+};
+
+struct VmaDefragmentationContext_T
+{
+private:
+ VMA_CLASS_NO_COPY(VmaDefragmentationContext_T)
+public:
+ VmaDefragmentationContext_T(
+ VmaAllocator hAllocator,
+ uint32_t currFrameIndex,
+ uint32_t flags,
+ VmaDefragmentationStats* pStats);
+ ~VmaDefragmentationContext_T();
+
+ void AddPools(uint32_t poolCount, VmaPool* pPools);
+ void AddAllocations(
+ uint32_t allocationCount,
+ VmaAllocation* pAllocations,
+ VkBool32* pAllocationsChanged);
+
+ /*
+ Returns:
+ - `VK_SUCCESS` if succeeded and object can be destroyed immediately.
+ - `VK_NOT_READY` if succeeded but the object must remain alive until vmaDefragmentationEnd().
+ - Negative value if error occured and object can be destroyed immediately.
+ */
+ VkResult Defragment(
+ VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
+ VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
+ VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats);
+
+private:
+ const VmaAllocator m_hAllocator;
+ const uint32_t m_CurrFrameIndex;
+ const uint32_t m_Flags;
+ VmaDefragmentationStats* const m_pStats;
+ // Owner of these objects.
+ VmaBlockVectorDefragmentationContext* m_DefaultPoolContexts[VK_MAX_MEMORY_TYPES];
+ // Owner of these objects.
+ VmaVector< VmaBlockVectorDefragmentationContext*, VmaStlAllocator<VmaBlockVectorDefragmentationContext*> > m_CustomPoolContexts;
+};
+
+#if VMA_RECORDING_ENABLED
+
+class VmaRecorder
+{
+public:
+ VmaRecorder();
+ VkResult Init(const VmaRecordSettings& settings, bool useMutex);
+ void WriteConfiguration(
+ const VkPhysicalDeviceProperties& devProps,
+ const VkPhysicalDeviceMemoryProperties& memProps,
+ bool dedicatedAllocationExtensionEnabled);
+ ~VmaRecorder();
+
+ void RecordCreateAllocator(uint32_t frameIndex);
+ void RecordDestroyAllocator(uint32_t frameIndex);
+ void RecordCreatePool(uint32_t frameIndex,
+ const VmaPoolCreateInfo& createInfo,
+ VmaPool pool);
+ void RecordDestroyPool(uint32_t frameIndex, VmaPool pool);
+ void RecordAllocateMemory(uint32_t frameIndex,
+ const VkMemoryRequirements& vkMemReq,
+ const VmaAllocationCreateInfo& createInfo,
+ VmaAllocation allocation);
+ void RecordAllocateMemoryPages(uint32_t frameIndex,
+ const VkMemoryRequirements& vkMemReq,
+ const VmaAllocationCreateInfo& createInfo,
+ uint64_t allocationCount,
+ const VmaAllocation* pAllocations);
+ void RecordAllocateMemoryForBuffer(uint32_t frameIndex,
+ const VkMemoryRequirements& vkMemReq,
+ bool requiresDedicatedAllocation,
+ bool prefersDedicatedAllocation,
+ const VmaAllocationCreateInfo& createInfo,
+ VmaAllocation allocation);
+ void RecordAllocateMemoryForImage(uint32_t frameIndex,
+ const VkMemoryRequirements& vkMemReq,
+ bool requiresDedicatedAllocation,
+ bool prefersDedicatedAllocation,
+ const VmaAllocationCreateInfo& createInfo,
+ VmaAllocation allocation);
+ void RecordFreeMemory(uint32_t frameIndex,
+ VmaAllocation allocation);
+ void RecordFreeMemoryPages(uint32_t frameIndex,
+ uint64_t allocationCount,
+ const VmaAllocation* pAllocations);
+ void RecordResizeAllocation(
+ uint32_t frameIndex,
+ VmaAllocation allocation,
+ VkDeviceSize newSize);
+ void RecordSetAllocationUserData(uint32_t frameIndex,
+ VmaAllocation allocation,
+ const void* pUserData);
+ void RecordCreateLostAllocation(uint32_t frameIndex,
+ VmaAllocation allocation);
+ void RecordMapMemory(uint32_t frameIndex,
+ VmaAllocation allocation);
+ void RecordUnmapMemory(uint32_t frameIndex,
+ VmaAllocation allocation);
+ void RecordFlushAllocation(uint32_t frameIndex,
+ VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
+ void RecordInvalidateAllocation(uint32_t frameIndex,
+ VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
+ void RecordCreateBuffer(uint32_t frameIndex,
+ const VkBufferCreateInfo& bufCreateInfo,
+ const VmaAllocationCreateInfo& allocCreateInfo,
+ VmaAllocation allocation);
+ void RecordCreateImage(uint32_t frameIndex,
+ const VkImageCreateInfo& imageCreateInfo,
+ const VmaAllocationCreateInfo& allocCreateInfo,
+ VmaAllocation allocation);
+ void RecordDestroyBuffer(uint32_t frameIndex,
+ VmaAllocation allocation);
+ void RecordDestroyImage(uint32_t frameIndex,
+ VmaAllocation allocation);
+ void RecordTouchAllocation(uint32_t frameIndex,
+ VmaAllocation allocation);
+ void RecordGetAllocationInfo(uint32_t frameIndex,
+ VmaAllocation allocation);
+ void RecordMakePoolAllocationsLost(uint32_t frameIndex,
+ VmaPool pool);
+ void RecordDefragmentationBegin(uint32_t frameIndex,
+ const VmaDefragmentationInfo2& info,
+ VmaDefragmentationContext ctx);
+ void RecordDefragmentationEnd(uint32_t frameIndex,
+ VmaDefragmentationContext ctx);
+
+private:
+ struct CallParams
+ {
+ uint32_t threadId;
+ double time;
+ };
+
+ class UserDataString
+ {
+ public:
+ UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData);
+ const char* GetString() const { return m_Str; }
+
+ private:
+ char m_PtrStr[17];
+ const char* m_Str;
+ };
+
+ bool m_UseMutex;
+ VmaRecordFlags m_Flags;
+ FILE* m_File;
+ VMA_MUTEX m_FileMutex;
+ int64_t m_Freq;
+ int64_t m_StartCounter;
+
+ void GetBasicParams(CallParams& outParams);
+
+ // T must be a pointer type, e.g. VmaAllocation, VmaPool.
+ template<typename T>
+ void PrintPointerList(uint64_t count, const T* pItems)
+ {
+ if(count)
+ {
+ fprintf(m_File, "%p", pItems[0]);
+ for(uint64_t i = 1; i < count; ++i)
+ {
+ fprintf(m_File, " %p", pItems[i]);
+ }
+ }
+ }
+
+ void PrintPointerList(uint64_t count, const VmaAllocation* pItems);
+ void Flush();
+};
+
+#endif // #if VMA_RECORDING_ENABLED
+
+// Main allocator object.
+struct VmaAllocator_T
+{
+ VMA_CLASS_NO_COPY(VmaAllocator_T)
+public:
+ bool m_UseMutex;
+ bool m_UseKhrDedicatedAllocation;
+ VkDevice m_hDevice;
+ bool m_AllocationCallbacksSpecified;
+ VkAllocationCallbacks m_AllocationCallbacks;
+ VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;
+
+ // Number of bytes free out of limit, or VK_WHOLE_SIZE if no limit for that heap.
+ VkDeviceSize m_HeapSizeLimit[VK_MAX_MEMORY_HEAPS];
+ VMA_MUTEX m_HeapSizeLimitMutex;
+
+ VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
+ VkPhysicalDeviceMemoryProperties m_MemProps;
+
+ // Default pools.
+ VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
+
+ // Each vector is sorted by memory (handle value).
+ typedef VmaVector< VmaAllocation, VmaStlAllocator<VmaAllocation> > AllocationVectorType;
+ AllocationVectorType* m_pDedicatedAllocations[VK_MAX_MEMORY_TYPES];
+ VMA_RW_MUTEX m_DedicatedAllocationsMutex[VK_MAX_MEMORY_TYPES];
+
+ VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
+ VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo);
+ ~VmaAllocator_T();
+
+ const VkAllocationCallbacks* GetAllocationCallbacks() const
+ {
+ return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : 0;
+ }
+ const VmaVulkanFunctions& GetVulkanFunctions() const
+ {
+ return m_VulkanFunctions;
+ }
+
+ VkDeviceSize GetBufferImageGranularity() const
+ {
+ return VMA_MAX(
+ static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
+ m_PhysicalDeviceProperties.limits.bufferImageGranularity);
+ }
+
+ uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
+ uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
+
+ uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const
+ {
+ VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);
+ return m_MemProps.memoryTypes[memTypeIndex].heapIndex;
+ }
+ // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT.
+ bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const
+ {
+ return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
+ }
+ // Minimum alignment for all allocations in specific memory type.
+ VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const
+ {
+ return IsMemoryTypeNonCoherent(memTypeIndex) ?
+ VMA_MAX((VkDeviceSize)VMA_DEBUG_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) :
+ (VkDeviceSize)VMA_DEBUG_ALIGNMENT;
+ }
+
+ bool IsIntegratedGpu() const
+ {
+ return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
+ }
+
+#if VMA_RECORDING_ENABLED
+ VmaRecorder* GetRecorder() const { return m_pRecorder; }
+#endif
+
+ void GetBufferMemoryRequirements(
+ VkBuffer hBuffer,
+ VkMemoryRequirements& memReq,
+ bool& requiresDedicatedAllocation,
+ bool& prefersDedicatedAllocation) const;
+ void GetImageMemoryRequirements(
+ VkImage hImage,
+ VkMemoryRequirements& memReq,
+ bool& requiresDedicatedAllocation,
+ bool& prefersDedicatedAllocation) const;
+
+ // Main allocation function.
+ VkResult AllocateMemory(
+ const VkMemoryRequirements& vkMemReq,
+ bool requiresDedicatedAllocation,
+ bool prefersDedicatedAllocation,
+ VkBuffer dedicatedBuffer,
+ VkImage dedicatedImage,
+ const VmaAllocationCreateInfo& createInfo,
+ VmaSuballocationType suballocType,
+ size_t allocationCount,
+ VmaAllocation* pAllocations);
+
+ // Main deallocation function.
+ void FreeMemory(
+ size_t allocationCount,
+ const VmaAllocation* pAllocations);
+
+ VkResult ResizeAllocation(
+ const VmaAllocation alloc,
+ VkDeviceSize newSize);
+
+ void CalculateStats(VmaStats* pStats);
+
+#if VMA_STATS_STRING_ENABLED
+ void PrintDetailedMap(class VmaJsonWriter& json);
+#endif
+
+ VkResult DefragmentationBegin(
+ const VmaDefragmentationInfo2& info,
+ VmaDefragmentationStats* pStats,
+ VmaDefragmentationContext* pContext);
+ VkResult DefragmentationEnd(
+ VmaDefragmentationContext context);
+
+ void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);
+ bool TouchAllocation(VmaAllocation hAllocation);
+
+ VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);
+ void DestroyPool(VmaPool pool);
+ void GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats);
+
+ void SetCurrentFrameIndex(uint32_t frameIndex);
+ uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }
+
+ void MakePoolAllocationsLost(
+ VmaPool hPool,
+ size_t* pLostAllocationCount);
+ VkResult CheckPoolCorruption(VmaPool hPool);
+ VkResult CheckCorruption(uint32_t memoryTypeBits);
+
+ void CreateLostAllocation(VmaAllocation* pAllocation);
+
+ VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);
+ void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);
+
+ VkResult Map(VmaAllocation hAllocation, void** ppData);
+ void Unmap(VmaAllocation hAllocation);
+
+ VkResult BindBufferMemory(VmaAllocation hAllocation, VkBuffer hBuffer);
+ VkResult BindImageMemory(VmaAllocation hAllocation, VkImage hImage);
+
+ void FlushOrInvalidateAllocation(
+ VmaAllocation hAllocation,
+ VkDeviceSize offset, VkDeviceSize size,
+ VMA_CACHE_OPERATION op);
+
+ void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern);
+
+private:
+ VkDeviceSize m_PreferredLargeHeapBlockSize;
+
+ VkPhysicalDevice m_PhysicalDevice;
+ VMA_ATOMIC_UINT32 m_CurrentFrameIndex;
+
+ VMA_RW_MUTEX m_PoolsMutex;
+ // Protected by m_PoolsMutex. Sorted by pointer value.
+ VmaVector<VmaPool, VmaStlAllocator<VmaPool> > m_Pools;
+ uint32_t m_NextPoolId;
+
+ VmaVulkanFunctions m_VulkanFunctions;
+
+#if VMA_RECORDING_ENABLED
+ VmaRecorder* m_pRecorder;
+#endif
+
+ void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);
+
+ VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
+
+ VkResult AllocateMemoryOfType(
+ VkDeviceSize size,
+ VkDeviceSize alignment,
+ bool dedicatedAllocation,
+ VkBuffer dedicatedBuffer,
+ VkImage dedicatedImage,
+ const VmaAllocationCreateInfo& createInfo,
+ uint32_t memTypeIndex,
+ VmaSuballocationType suballocType,
+ size_t allocationCount,
+ VmaAllocation* pAllocations);
+
+ // Helper function only to be used inside AllocateDedicatedMemory.
+ VkResult AllocateDedicatedMemoryPage(
+ VkDeviceSize size,
+ VmaSuballocationType suballocType,
+ uint32_t memTypeIndex,
+ const VkMemoryAllocateInfo& allocInfo,
+ bool map,
+ bool isUserDataString,
+ void* pUserData,
+ VmaAllocation* pAllocation);
+
+ // Allocates and registers new VkDeviceMemory specifically for dedicated allocations.
+ VkResult AllocateDedicatedMemory(
+ VkDeviceSize size,
+ VmaSuballocationType suballocType,
+ uint32_t memTypeIndex,
+ bool map,
+ bool isUserDataString,
+ void* pUserData,
+ VkBuffer dedicatedBuffer,
+ VkImage dedicatedImage,
+ size_t allocationCount,
+ VmaAllocation* pAllocations);
+
+ // Tries to free pMemory as Dedicated Memory. Returns true if found and freed.
+ void FreeDedicatedMemory(VmaAllocation allocation);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// Memory allocation #2 after VmaAllocator_T definition
+
+static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
+{
+ return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);
+}
+
+static void VmaFree(VmaAllocator hAllocator, void* ptr)
+{
+ VmaFree(&hAllocator->m_AllocationCallbacks, ptr);
+}
+
+template<typename T>
+static T* VmaAllocate(VmaAllocator hAllocator)
+{
+ return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
+}
+
+template<typename T>
+static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
+{
+ return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
+}
+
+template<typename T>
+static void vma_delete(VmaAllocator hAllocator, T* ptr)
+{
+ if(ptr != VMA_NULL)
+ {
+ ptr->~T();
+ VmaFree(hAllocator, ptr);
+ }
+}
+
+template<typename T>
+static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
+{
+ if(ptr != VMA_NULL)
+ {
+ for(size_t i = count; i--; )
+ ptr[i].~T();
+ VmaFree(hAllocator, ptr);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// VmaStringBuilder
+
+#if VMA_STATS_STRING_ENABLED
+
+class VmaStringBuilder
+{
+public:
+ VmaStringBuilder(VmaAllocator alloc) : m_Data(VmaStlAllocator<char>(alloc->GetAllocationCallbacks())) { }
+ size_t GetLength() const { return m_Data.size(); }
+ const char* GetData() const { return m_Data.data(); }
+
+ void Add(char ch) { m_Data.push_back(ch); }
+ void Add(const char* pStr);
+ void AddNewLine() { Add('\n'); }
+ void AddNumber(uint32_t num);
+ void AddNumber(uint64_t num);
+ void AddPointer(const void* ptr);
+
+private:
+ VmaVector< char, VmaStlAllocator<char> > m_Data;
+};
+
+void VmaStringBuilder::Add(const char* pStr)
+{
+ const size_t strLen = strlen(pStr);
+ if(strLen > 0)
+ {
+ const size_t oldCount = m_Data.size();
+ m_Data.resize(oldCount + strLen);
+ memcpy(m_Data.data() + oldCount, pStr, strLen);
+ }
+}
+
+void VmaStringBuilder::AddNumber(uint32_t num)
+{
+ char buf[11];
+ VmaUint32ToStr(buf, sizeof(buf), num);
+ Add(buf);
+}
+
+void VmaStringBuilder::AddNumber(uint64_t num)
+{
+ char buf[21];
+ VmaUint64ToStr(buf, sizeof(buf), num);
+ Add(buf);
+}
+
+void VmaStringBuilder::AddPointer(const void* ptr)
+{
+ char buf[21];
+ VmaPtrToStr(buf, sizeof(buf), ptr);
+ Add(buf);
+}
+
+#endif // #if VMA_STATS_STRING_ENABLED
+
+////////////////////////////////////////////////////////////////////////////////
+// VmaJsonWriter
+
+#if VMA_STATS_STRING_ENABLED
+
+class VmaJsonWriter
+{
+ VMA_CLASS_NO_COPY(VmaJsonWriter)
+public:
+ VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);
+ ~VmaJsonWriter();
+
+ void BeginObject(bool singleLine = false);
+ void EndObject();
+
+ void BeginArray(bool singleLine = false);
+ void EndArray();
+
+ void WriteString(const char* pStr);
+ void BeginString(const char* pStr = VMA_NULL);
+ void ContinueString(const char* pStr);
+ void ContinueString(uint32_t n);
+ void ContinueString(uint64_t n);
+ void ContinueString_Pointer(const void* ptr);
+ void EndString(const char* pStr = VMA_NULL);
+
+ void WriteNumber(uint32_t n);
+ void WriteNumber(uint64_t n);
+ void WriteBool(bool b);
+ void WriteNull();
+
+private:
+ static const char* const INDENT;
+
+ enum COLLECTION_TYPE
+ {
+ COLLECTION_TYPE_OBJECT,
+ COLLECTION_TYPE_ARRAY,
+ };
+ struct StackItem
+ {
+ COLLECTION_TYPE type;
+ uint32_t valueCount;
+ bool singleLineMode;
+ };
+
+ VmaStringBuilder& m_SB;
+ VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;
+ bool m_InsideString;
+
+ void BeginValue(bool isString);
+ void WriteIndent(bool oneLess = false);
+};
+
+const char* const VmaJsonWriter::INDENT = " ";
+
+VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) :
+ m_SB(sb),
+ m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),
+ m_InsideString(false)
+{
+}
+
+VmaJsonWriter::~VmaJsonWriter()
+{
+ VMA_ASSERT(!m_InsideString);
+ VMA_ASSERT(m_Stack.empty());
+}
+
+void VmaJsonWriter::BeginObject(bool singleLine)
+{
+ VMA_ASSERT(!m_InsideString);
+
+ BeginValue(false);
+ m_SB.Add('{');
+
+ StackItem item;
+ item.type = COLLECTION_TYPE_OBJECT;
+ item.valueCount = 0;
+ item.singleLineMode = singleLine;
+ m_Stack.push_back(item);
+}
+
+void VmaJsonWriter::EndObject()
+{
+ VMA_ASSERT(!m_InsideString);
+
+ WriteIndent(true);
+ m_SB.Add('}');
+
+ VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
+ m_Stack.pop_back();
+}
+
+void VmaJsonWriter::BeginArray(bool singleLine)
+{
+ VMA_ASSERT(!m_InsideString);
+
+ BeginValue(false);
+ m_SB.Add('[');
+
+ StackItem item;
+ item.type = COLLECTION_TYPE_ARRAY;
+ item.valueCount = 0;
+ item.singleLineMode = singleLine;
+ m_Stack.push_back(item);
+}
+
+void VmaJsonWriter::EndArray()
+{
+ VMA_ASSERT(!m_InsideString);
+
+ WriteIndent(true);
+ m_SB.Add(']');
+
+ VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
+ m_Stack.pop_back();
+}
+
+void VmaJsonWriter::WriteString(const char* pStr)
+{
+ BeginString(pStr);
+ EndString();
+}
+
+void VmaJsonWriter::BeginString(const char* pStr)
+{
+ VMA_ASSERT(!m_InsideString);
+
+ BeginValue(true);
+ m_SB.Add('"');
+ m_InsideString = true;
+ if(pStr != VMA_NULL && pStr[0] != '\0')
+ {
+ ContinueString(pStr);
+ }
+}
+
+void VmaJsonWriter::ContinueString(const char* pStr)
+{
+ VMA_ASSERT(m_InsideString);
+
+ const size_t strLen = strlen(pStr);
+ for(size_t i = 0; i < strLen; ++i)
+ {
+ char ch = pStr[i];
+ if(ch == '\\')
+ {
+ m_SB.Add("\\\\");
+ }
+ else if(ch == '"')
+ {
+ m_SB.Add("\\\"");
+ }
+ else if(ch >= 32)
+ {
+ m_SB.Add(ch);
+ }
+ else switch(ch)
+ {
+ case '\b':
+ m_SB.Add("\\b");
+ break;
+ case '\f':
+ m_SB.Add("\\f");
+ break;
+ case '\n':
+ m_SB.Add("\\n");
+ break;
+ case '\r':
+ m_SB.Add("\\r");
+ break;
+ case '\t':
+ m_SB.Add("\\t");
+ break;
+ default:
+ VMA_ASSERT(0 && "Character not currently supported.");
+ break;
+ }
+ }
+}
+
+void VmaJsonWriter::ContinueString(uint32_t n)
+{
+ VMA_ASSERT(m_InsideString);
+ m_SB.AddNumber(n);
+}
+
+void VmaJsonWriter::ContinueString(uint64_t n)
+{
+ VMA_ASSERT(m_InsideString);
+ m_SB.AddNumber(n);
+}
+
+void VmaJsonWriter::ContinueString_Pointer(const void* ptr)
+{
+ VMA_ASSERT(m_InsideString);
+ m_SB.AddPointer(ptr);
+}
+
+void VmaJsonWriter::EndString(const char* pStr)
+{
+ VMA_ASSERT(m_InsideString);
+ if(pStr != VMA_NULL && pStr[0] != '\0')
+ {
+ ContinueString(pStr);
+ }
+ m_SB.Add('"');
+ m_InsideString = false;
+}
+
+void VmaJsonWriter::WriteNumber(uint32_t n)
+{
+ VMA_ASSERT(!m_InsideString);
+ BeginValue(false);
+ m_SB.AddNumber(n);
+}
+
+void VmaJsonWriter::WriteNumber(uint64_t n)
+{
+ VMA_ASSERT(!m_InsideString);
+ BeginValue(false);
+ m_SB.AddNumber(n);
+}
+
+void VmaJsonWriter::WriteBool(bool b)
+{
+ VMA_ASSERT(!m_InsideString);
+ BeginValue(false);
+ m_SB.Add(b ? "true" : "false");
+}
+
+void VmaJsonWriter::WriteNull()
+{
+ VMA_ASSERT(!m_InsideString);
+ BeginValue(false);
+ m_SB.Add("null");
+}
+
+void VmaJsonWriter::BeginValue(bool isString)
+{
+ if(!m_Stack.empty())
+ {
+ StackItem& currItem = m_Stack.back();
+ if(currItem.type == COLLECTION_TYPE_OBJECT &&
+ currItem.valueCount % 2 == 0)
+ {
+ (void) isString;
+ VMA_ASSERT(isString);
+ }
+
+ if(currItem.type == COLLECTION_TYPE_OBJECT &&
+ currItem.valueCount % 2 != 0)
+ {
+ m_SB.Add(": ");
+ }
+ else if(currItem.valueCount > 0)
+ {
+ m_SB.Add(", ");
+ WriteIndent();
+ }
+ else
+ {
+ WriteIndent();
+ }
+ ++currItem.valueCount;
+ }
+}
+
+void VmaJsonWriter::WriteIndent(bool oneLess)
+{
+ if(!m_Stack.empty() && !m_Stack.back().singleLineMode)
+ {
+ m_SB.AddNewLine();
+
+ size_t count = m_Stack.size();
+ if(count > 0 && oneLess)
+ {
+ --count;
+ }
+ for(size_t i = 0; i < count; ++i)
+ {
+ m_SB.Add(INDENT);
+ }
+ }
+}
+
+#endif // #if VMA_STATS_STRING_ENABLED
+
+////////////////////////////////////////////////////////////////////////////////
+
+void VmaAllocation_T::SetUserData(VmaAllocator hAllocator, void* pUserData)
+{
+ if(IsUserDataString())
+ {
+ VMA_ASSERT(pUserData == VMA_NULL || pUserData != m_pUserData);
+
+ FreeUserDataString(hAllocator);
+
+ if(pUserData != VMA_NULL)
+ {
+ const char* const newStrSrc = (char*)pUserData;
+ const size_t newStrLen = strlen(newStrSrc);
+ char* const newStrDst = vma_new_array(hAllocator, char, newStrLen + 1);
+ memcpy(newStrDst, newStrSrc, newStrLen + 1);
+ m_pUserData = newStrDst;
+ }
+ }
+ else
+ {
+ m_pUserData = pUserData;
+ }
+}
+
+void VmaAllocation_T::ChangeBlockAllocation(
+ VmaAllocator hAllocator,
+ VmaDeviceMemoryBlock* block,
+ VkDeviceSize offset)
+{
+ VMA_ASSERT(block != VMA_NULL);
+ VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
+
+ // Move mapping reference counter from old block to new block.
+ if(block != m_BlockAllocation.m_Block)
+ {
+ uint32_t mapRefCount = m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP;
+ if(IsPersistentMap())
+ ++mapRefCount;
+ m_BlockAllocation.m_Block->Unmap(hAllocator, mapRefCount);
+ block->Map(hAllocator, mapRefCount, VMA_NULL);
+ }
+
+ m_BlockAllocation.m_Block = block;
+ m_BlockAllocation.m_Offset = offset;
+}
+
+void VmaAllocation_T::ChangeSize(VkDeviceSize newSize)
+{
+ VMA_ASSERT(newSize > 0);
+ m_Size = newSize;
+}
+
+void VmaAllocation_T::ChangeOffset(VkDeviceSize newOffset)
+{
+ VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
+ m_BlockAllocation.m_Offset = newOffset;
+}
+
+VkDeviceSize VmaAllocation_T::GetOffset() const
+{
+ switch(m_Type)
+ {
+ case ALLOCATION_TYPE_BLOCK:
+ return m_BlockAllocation.m_Offset;
+ case ALLOCATION_TYPE_DEDICATED:
+ return 0;
+ default:
+ VMA_ASSERT(0);
+ return 0;
+ }
+}
+
+VkDeviceMemory VmaAllocation_T::GetMemory() const
+{
+ switch(m_Type)
+ {
+ case ALLOCATION_TYPE_BLOCK:
+ return m_BlockAllocation.m_Block->GetDeviceMemory();
+ case ALLOCATION_TYPE_DEDICATED:
+ return m_DedicatedAllocation.m_hMemory;
+ default:
+ VMA_ASSERT(0);
+ return VK_NULL_HANDLE;
+ }
+}
+
+uint32_t VmaAllocation_T::GetMemoryTypeIndex() const
+{
+ switch(m_Type)
+ {
+ case ALLOCATION_TYPE_BLOCK:
+ return m_BlockAllocation.m_Block->GetMemoryTypeIndex();
+ case ALLOCATION_TYPE_DEDICATED:
+ return m_DedicatedAllocation.m_MemoryTypeIndex;
+ default:
+ VMA_ASSERT(0);
+ return UINT32_MAX;
+ }
+}
+
+void* VmaAllocation_T::GetMappedData() const
+{
+ switch(m_Type)
+ {
+ case ALLOCATION_TYPE_BLOCK:
+ if(m_MapCount != 0)
+ {
+ void* pBlockData = m_BlockAllocation.m_Block->GetMappedData();
+ VMA_ASSERT(pBlockData != VMA_NULL);
+ return (char*)pBlockData + m_BlockAllocation.m_Offset;
+ }
+ else
+ {
+ return VMA_NULL;
+ }
+ break;
+ case ALLOCATION_TYPE_DEDICATED:
+ VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0));
+ return m_DedicatedAllocation.m_pMappedData;
+ default:
+ VMA_ASSERT(0);
+ return VMA_NULL;
+ }
+}
+
+bool VmaAllocation_T::CanBecomeLost() const
+{
+ switch(m_Type)
+ {
+ case ALLOCATION_TYPE_BLOCK:
+ return m_BlockAllocation.m_CanBecomeLost;
+ case ALLOCATION_TYPE_DEDICATED:
+ return false;
+ default:
+ VMA_ASSERT(0);
+ return false;
+ }
+}
+
+VmaPool VmaAllocation_T::GetPool() const
+{
+ VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
+ return m_BlockAllocation.m_hPool;
+}
+
+bool VmaAllocation_T::MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
+{
+ VMA_ASSERT(CanBecomeLost());
+
+ /*
+ Warning: This is a carefully designed algorithm.
+ Do not modify unless you really know what you're doing :)
+ */
+ uint32_t localLastUseFrameIndex = GetLastUseFrameIndex();
+ for(;;)
+ {
+ if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
+ {
+ VMA_ASSERT(0);
+ return false;
+ }
+ else if(localLastUseFrameIndex + frameInUseCount >= currentFrameIndex)
+ {
+ return false;
+ }
+ else // Last use time earlier than current time.
+ {
+ if(CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, VMA_FRAME_INDEX_LOST))
+ {
+ // Setting hAllocation.LastUseFrameIndex atomic to VMA_FRAME_INDEX_LOST is enough to mark it as LOST.
+ // Calling code just needs to unregister this allocation in owning VmaDeviceMemoryBlock.
+ return true;
+ }
+ }
+ }
+}
+
+#if VMA_STATS_STRING_ENABLED
+
+// Correspond to values of enum VmaSuballocationType.
+static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = {
+ "FREE",
+ "UNKNOWN",
+ "BUFFER",
+ "IMAGE_UNKNOWN",
+ "IMAGE_LINEAR",
+ "IMAGE_OPTIMAL",
+};
+
+void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
+{
+ json.WriteString("Type");
+ json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]);
+
+ json.WriteString("Size");
+ json.WriteNumber(m_Size);
+
+ if(m_pUserData != VMA_NULL)
+ {
+ json.WriteString("UserData");
+ if(IsUserDataString())
+ {
+ json.WriteString((const char*)m_pUserData);
+ }
+ else
+ {
+ json.BeginString();
+ json.ContinueString_Pointer(m_pUserData);
+ json.EndString();
+ }
+ }
+
+ json.WriteString("CreationFrameIndex");
+ json.WriteNumber(m_CreationFrameIndex);
+
+ json.WriteString("LastUseFrameIndex");
+ json.WriteNumber(GetLastUseFrameIndex());
+
+ if(m_BufferImageUsage != 0)
+ {
+ json.WriteString("Usage");
+ json.WriteNumber(m_BufferImageUsage);
+ }
+}
+
+#endif
+
+void VmaAllocation_T::FreeUserDataString(VmaAllocator hAllocator)
+{
+ VMA_ASSERT(IsUserDataString());
+ if(m_pUserData != VMA_NULL)
+ {
+ char* const oldStr = (char*)m_pUserData;
+ const size_t oldStrLen = strlen(oldStr);
+ vma_delete_array(hAllocator, oldStr, oldStrLen + 1);
+ m_pUserData = VMA_NULL;
+ }
+}
+
+void VmaAllocation_T::BlockAllocMap()
+{
+ VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
+
+ if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
+ {
+ ++m_MapCount;
+ }
+ else
+ {
+ VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");
+ }
+}
+
+void VmaAllocation_T::BlockAllocUnmap()
+{
+ VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
+
+ if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
+ {
+ --m_MapCount;
+ }
+ else
+ {
+ VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");
+ }
+}
+
+VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)
+{
+ VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
+
+ if(m_MapCount != 0)
+ {
+ if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
+ {
+ VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
+ *ppData = m_DedicatedAllocation.m_pMappedData;
+ ++m_MapCount;
+ return VK_SUCCESS;
+ }
+ else
+ {
+ VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");
+ return VK_ERROR_MEMORY_MAP_FAILED;
+ }
+ }
+ else
+ {
+ VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
+ hAllocator->m_hDevice,
+ m_DedicatedAllocation.m_hMemory,
+ 0, // offset
+ VK_WHOLE_SIZE,
+ 0, // flags
+ ppData);
+ if(result == VK_SUCCESS)
+ {
+ m_DedicatedAllocation.m_pMappedData = *ppData;
+ m_MapCount = 1;
+ }
+ return result;
+ }
+}
+
+void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
+{
+ VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
+
+ if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
+ {
+ --m_MapCount;
+ if(m_MapCount == 0)
+ {
+ m_DedicatedAllocation.m_pMappedData = VMA_NULL;
+ (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
+ hAllocator->m_hDevice,
+ m_DedicatedAllocation.m_hMemory);
+ }
+ }
+ else
+ {
+ VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");
+ }
+}
+
+#if VMA_STATS_STRING_ENABLED
+
+static void VmaPrintStatInfo(VmaJsonWriter& json, const VmaStatInfo& stat)
+{
+ json.BeginObject();
+
+ json.WriteString("Blocks");
+ json.WriteNumber(stat.blockCount);
+
+ json.WriteString("Allocations");
+ json.WriteNumber(stat.allocationCount);
+
+ json.WriteString("UnusedRanges");
+ json.WriteNumber(stat.unusedRangeCount);
+
+ json.WriteString("UsedBytes");
+ json.WriteNumber(stat.usedBytes);
+
+ json.WriteString("UnusedBytes");
+ json.WriteNumber(stat.unusedBytes);
+
+ if(stat.allocationCount > 1)
+ {
+ json.WriteString("AllocationSize");
+ json.BeginObject(true);
+ json.WriteString("Min");
+ json.WriteNumber(stat.allocationSizeMin);
+ json.WriteString("Avg");
+ json.WriteNumber(stat.allocationSizeAvg);
+ json.WriteString("Max");
+ json.WriteNumber(stat.allocationSizeMax);
+ json.EndObject();
+ }
+
+ if(stat.unusedRangeCount > 1)
+ {
+ json.WriteString("UnusedRangeSize");
+ json.BeginObject(true);
+ json.WriteString("Min");
+ json.WriteNumber(stat.unusedRangeSizeMin);
+ json.WriteString("Avg");
+ json.WriteNumber(stat.unusedRangeSizeAvg);
+ json.WriteString("Max");
+ json.WriteNumber(stat.unusedRangeSizeMax);
+ json.EndObject();
+ }
+
+ json.EndObject();
+}
+
+#endif // #if VMA_STATS_STRING_ENABLED
+
+struct VmaSuballocationItemSizeLess
+{
+ bool operator()(
+ const VmaSuballocationList::iterator lhs,
+ const VmaSuballocationList::iterator rhs) const
+ {
+ return lhs->size < rhs->size;
+ }
+ bool operator()(
+ const VmaSuballocationList::iterator lhs,
+ VkDeviceSize rhsSize) const
+ {
+ return lhs->size < rhsSize;
+ }
+};
+
+
+////////////////////////////////////////////////////////////////////////////////
+// class VmaBlockMetadata
+
+VmaBlockMetadata::VmaBlockMetadata(VmaAllocator hAllocator) :
+ m_Size(0),
+ m_pAllocationCallbacks(hAllocator->GetAllocationCallbacks())
+{
+}
+
+#if VMA_STATS_STRING_ENABLED
+
+void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,
+ VkDeviceSize unusedBytes,
+ size_t allocationCount,
+ size_t unusedRangeCount) const
+{
+ json.BeginObject();
+
+ json.WriteString("TotalBytes");
+ json.WriteNumber(GetSize());
+
+ json.WriteString("UnusedBytes");
+ json.WriteNumber(unusedBytes);
+
+ json.WriteString("Allocations");
+ json.WriteNumber((uint64_t)allocationCount);
+
+ json.WriteString("UnusedRanges");
+ json.WriteNumber((uint64_t)unusedRangeCount);
+
+ json.WriteString("Suballocations");
+ json.BeginArray();
+}
+
+void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json,
+ VkDeviceSize offset,
+ VmaAllocation hAllocation) const
+{
+ json.BeginObject(true);
+
+ json.WriteString("Offset");
+ json.WriteNumber(offset);
+
+ hAllocation->PrintParameters(json);
+
+ json.EndObject();
+}
+
+void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
+ VkDeviceSize offset,
+ VkDeviceSize size) const
+{
+ json.BeginObject(true);
+
+ json.WriteString("Offset");
+ json.WriteNumber(offset);
+
+ json.WriteString("Type");
+ json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]);
+
+ json.WriteString("Size");
+ json.WriteNumber(size);
+
+ json.EndObject();
+}
+
+void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const
+{
+ json.EndArray();
+ json.EndObject();
+}
+
+#endif // #if VMA_STATS_STRING_ENABLED
+
+////////////////////////////////////////////////////////////////////////////////
+// class VmaBlockMetadata_Generic
+
+VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(VmaAllocator hAllocator) :
+ VmaBlockMetadata(hAllocator),
+ m_FreeCount(0),
+ m_SumFreeSize(0),
+ m_Suballocations(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
+ m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(hAllocator->GetAllocationCallbacks()))
+{
+}
+
+VmaBlockMetadata_Generic::~VmaBlockMetadata_Generic()
+{
+}
+
+void VmaBlockMetadata_Generic::Init(VkDeviceSize size)
+{
+ VmaBlockMetadata::Init(size);
+
+ m_FreeCount = 1;
+ m_SumFreeSize = size;
+
+ VmaSuballocation suballoc = {};
+ suballoc.offset = 0;
+ suballoc.size = size;
+ suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
+ suballoc.hAllocation = VK_NULL_HANDLE;
+
+ VMA_ASSERT(size > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
+ m_Suballocations.push_back(suballoc);
+ VmaSuballocationList::iterator suballocItem = m_Suballocations.end();
+ --suballocItem;
+ m_FreeSuballocationsBySize.push_back(suballocItem);
+}
+
+bool VmaBlockMetadata_Generic::Validate() const
+{
+ VMA_VALIDATE(!m_Suballocations.empty());
+
+ // Expected offset of new suballocation as calculated from previous ones.
+ VkDeviceSize calculatedOffset = 0;
+ // Expected number of free suballocations as calculated from traversing their list.
+ uint32_t calculatedFreeCount = 0;
+ // Expected sum size of free suballocations as calculated from traversing their list.
+ VkDeviceSize calculatedSumFreeSize = 0;
+ // Expected number of free suballocations that should be registered in
+ // m_FreeSuballocationsBySize calculated from traversing their list.
+ size_t freeSuballocationsToRegister = 0;
+ // True if previous visited suballocation was free.
+ bool prevFree = false;
+
+ for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
+ suballocItem != m_Suballocations.cend();
+ ++suballocItem)
+ {
+ const VmaSuballocation& subAlloc = *suballocItem;
+
+ // Actual offset of this suballocation doesn't match expected one.
+ VMA_VALIDATE(subAlloc.offset == calculatedOffset);
+
+ const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);
+ // Two adjacent free suballocations are invalid. They should be merged.
+ VMA_VALIDATE(!prevFree || !currFree);
+
+ VMA_VALIDATE(currFree == (subAlloc.hAllocation == VK_NULL_HANDLE));
+
+ if(currFree)
+ {
+ calculatedSumFreeSize += subAlloc.size;
+ ++calculatedFreeCount;
+ if(subAlloc.size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
+ {
+ ++freeSuballocationsToRegister;
+ }
+
+ // Margin required between allocations - every free space must be at least that large.
+#if VMA_DEBUG_MARGIN
+ VMA_VALIDATE(subAlloc.size >= VMA_DEBUG_MARGIN);
+#endif
+ }
+ else
+ {
+ VMA_VALIDATE(subAlloc.hAllocation->GetOffset() == subAlloc.offset);
+ VMA_VALIDATE(subAlloc.hAllocation->GetSize() == subAlloc.size);
+
+ // Margin required between allocations - previous allocation must be free.
+ VMA_VALIDATE(VMA_DEBUG_MARGIN == 0 || prevFree);
+ }
+
+ calculatedOffset += subAlloc.size;
+ prevFree = currFree;
+ }
+
+ // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
+ // match expected one.
+ VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);
+
+ VkDeviceSize lastSize = 0;
+ for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
+ {
+ VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
+
+ // Only free suballocations can be registered in m_FreeSuballocationsBySize.
+ VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE);
+ // They must be sorted by size ascending.
+ VMA_VALIDATE(suballocItem->size >= lastSize);
+
+ lastSize = suballocItem->size;
+ }
+
+ // Check if totals match calculacted values.
+ VMA_VALIDATE(ValidateFreeSuballocationList());
+ VMA_VALIDATE(calculatedOffset == GetSize());
+ VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);
+ VMA_VALIDATE(calculatedFreeCount == m_FreeCount);
+
+ return true;
+}
+
+VkDeviceSize VmaBlockMetadata_Generic::GetUnusedRangeSizeMax() const
+{
+ if(!m_FreeSuballocationsBySize.empty())
+ {
+ return m_FreeSuballocationsBySize.back()->size;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+bool VmaBlockMetadata_Generic::IsEmpty() const
+{
+ return (m_Suballocations.size() == 1) && (m_FreeCount == 1);
+}
+
+void VmaBlockMetadata_Generic::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
+{
+ outInfo.blockCount = 1;
+
+ const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
+ outInfo.allocationCount = rangeCount - m_FreeCount;
+ outInfo.unusedRangeCount = m_FreeCount;
+
+ outInfo.unusedBytes = m_SumFreeSize;
+ outInfo.usedBytes = GetSize() - outInfo.unusedBytes;
+
+ outInfo.allocationSizeMin = UINT64_MAX;
+ outInfo.allocationSizeMax = 0;
+ outInfo.unusedRangeSizeMin = UINT64_MAX;
+ outInfo.unusedRangeSizeMax = 0;
+
+ for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
+ suballocItem != m_Suballocations.cend();
+ ++suballocItem)
+ {
+ const VmaSuballocation& suballoc = *suballocItem;
+ if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
+ {
+ outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
+ outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, suballoc.size);
+ }
+ else
+ {
+ outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, suballoc.size);
+ outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, suballoc.size);
+ }
+ }
+}
+
+void VmaBlockMetadata_Generic::AddPoolStats(VmaPoolStats& inoutStats) const
+{
+ const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
+
+ inoutStats.size += GetSize();
+ inoutStats.unusedSize += m_SumFreeSize;
+ inoutStats.allocationCount += rangeCount - m_FreeCount;
+ inoutStats.unusedRangeCount += m_FreeCount;
+ inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
+}
+
+#if VMA_STATS_STRING_ENABLED
+
+void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json) const
+{
+ PrintDetailedMap_Begin(json,
+ m_SumFreeSize, // unusedBytes
+ m_Suballocations.size() - (size_t)m_FreeCount, // allocationCount
+ m_FreeCount); // unusedRangeCount
+
+ size_t i = 0;
+ for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
+ suballocItem != m_Suballocations.cend();
+ ++suballocItem, ++i)
+ {
+ if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
+ {
+ PrintDetailedMap_UnusedRange(json, suballocItem->offset, suballocItem->size);
+ }
+ else
+ {
+ PrintDetailedMap_Allocation(json, suballocItem->offset, suballocItem->hAllocation);
+ }
+ }
+
+ PrintDetailedMap_End(json);
+}
+
+#endif // #if VMA_STATS_STRING_ENABLED
+
+bool VmaBlockMetadata_Generic::CreateAllocationRequest(
+ uint32_t currentFrameIndex,
+ uint32_t frameInUseCount,
+ VkDeviceSize bufferImageGranularity,
+ VkDeviceSize allocSize,
+ VkDeviceSize allocAlignment,
+ bool upperAddress,
+ VmaSuballocationType allocType,
+ bool canMakeOtherLost,
+ uint32_t strategy,
+ VmaAllocationRequest* pAllocationRequest)
+{
+ VMA_ASSERT(allocSize > 0);
+ VMA_ASSERT(!upperAddress);
+ (void) upperAddress;
+ VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
+ VMA_ASSERT(pAllocationRequest != VMA_NULL);
+ VMA_HEAVY_ASSERT(Validate());
+
+ // There is not enough total free space in this block to fullfill the request: Early return.
+ if(canMakeOtherLost == false &&
+ m_SumFreeSize < allocSize + 2 * VMA_DEBUG_MARGIN)
+ {
+ return false;
+ }
+
+ // New algorithm, efficiently searching freeSuballocationsBySize.
+ const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
+ if(freeSuballocCount > 0)
+ {
+ if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
+ {
+ // Find first free suballocation with size not less than allocSize + 2 * VMA_DEBUG_MARGIN.
+ VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
+ m_FreeSuballocationsBySize.data(),
+ m_FreeSuballocationsBySize.data() + freeSuballocCount,
+ allocSize + 2 * VMA_DEBUG_MARGIN,
+ VmaSuballocationItemSizeLess());
+ size_t index = it - m_FreeSuballocationsBySize.data();
+ for(; index < freeSuballocCount; ++index)
+ {
+ if(CheckAllocation(
+ currentFrameIndex,
+ frameInUseCount,
+ bufferImageGranularity,
+ allocSize,
+ allocAlignment,
+ allocType,
+ m_FreeSuballocationsBySize[index],
+ false, // canMakeOtherLost
+ &pAllocationRequest->offset,
+ &pAllocationRequest->itemsToMakeLostCount,
+ &pAllocationRequest->sumFreeSize,
+ &pAllocationRequest->sumItemSize))
+ {
+ pAllocationRequest->item = m_FreeSuballocationsBySize[index];
+ return true;
+ }
+ }
+ }
+ else if(strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET)
+ {
+ for(VmaSuballocationList::iterator it = m_Suballocations.begin();
+ it != m_Suballocations.end();
+ ++it)
+ {
+ if(it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation(
+ currentFrameIndex,
+ frameInUseCount,
+ bufferImageGranularity,
+ allocSize,
+ allocAlignment,
+ allocType,
+ it,
+ false, // canMakeOtherLost
+ &pAllocationRequest->offset,
+ &pAllocationRequest->itemsToMakeLostCount,
+ &pAllocationRequest->sumFreeSize,
+ &pAllocationRequest->sumItemSize))
+ {
+ pAllocationRequest->item = it;
+ return true;
+ }
+ }
+ }
+ else // WORST_FIT, FIRST_FIT
+ {
+ // Search staring from biggest suballocations.
+ for(size_t index = freeSuballocCount; index--; )
+ {
+ if(CheckAllocation(
+ currentFrameIndex,
+ frameInUseCount,
+ bufferImageGranularity,
+ allocSize,
+ allocAlignment,
+ allocType,
+ m_FreeSuballocationsBySize[index],
+ false, // canMakeOtherLost
+ &pAllocationRequest->offset,
+ &pAllocationRequest->itemsToMakeLostCount,
+ &pAllocationRequest->sumFreeSize,
+ &pAllocationRequest->sumItemSize))
+ {
+ pAllocationRequest->item = m_FreeSuballocationsBySize[index];
+ return true;
+ }
+ }
+ }
+ }
+
+ if(canMakeOtherLost)
+ {
+ // Brute-force algorithm. TODO: Come up with something better.
+
+ pAllocationRequest->sumFreeSize = VK_WHOLE_SIZE;
+ pAllocationRequest->sumItemSize = VK_WHOLE_SIZE;
+
+ VmaAllocationRequest tmpAllocRequest = {};
+ for(VmaSuballocationList::iterator suballocIt = m_Suballocations.begin();
+ suballocIt != m_Suballocations.end();
+ ++suballocIt)
+ {
+ if(suballocIt->type == VMA_SUBALLOCATION_TYPE_FREE ||
+ suballocIt->hAllocation->CanBecomeLost())
+ {
+ if(CheckAllocation(
+ currentFrameIndex,
+ frameInUseCount,
+ bufferImageGranularity,
+ allocSize,
+ allocAlignment,
+ allocType,
+ suballocIt,
+ canMakeOtherLost,
+ &tmpAllocRequest.offset,
+ &tmpAllocRequest.itemsToMakeLostCount,
+ &tmpAllocRequest.sumFreeSize,
+ &tmpAllocRequest.sumItemSize))
+ {
+ tmpAllocRequest.item = suballocIt;
+
+ if(tmpAllocRequest.CalcCost() < pAllocationRequest->CalcCost() ||
+ strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
+ {
+ *pAllocationRequest = tmpAllocRequest;
+ }
+ }
+ }
+ }
+
+ if(pAllocationRequest->sumItemSize != VK_WHOLE_SIZE)
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool VmaBlockMetadata_Generic::MakeRequestedAllocationsLost(
+ uint32_t currentFrameIndex,
+ uint32_t frameInUseCount,
+ VmaAllocationRequest* pAllocationRequest)
+{
+ while(pAllocationRequest->itemsToMakeLostCount > 0)
+ {
+ if(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE)
+ {
+ ++pAllocationRequest->item;
+ }
+ VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
+ VMA_ASSERT(pAllocationRequest->item->hAllocation != VK_NULL_HANDLE);
+ VMA_ASSERT(pAllocationRequest->item->hAllocation->CanBecomeLost());
+ if(pAllocationRequest->item->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
+ {
+ pAllocationRequest->item = FreeSuballocation(pAllocationRequest->item);
+ --pAllocationRequest->itemsToMakeLostCount;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ VMA_HEAVY_ASSERT(Validate());
+ VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
+ VMA_ASSERT(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE);
+
+ return true;
+}
+
+uint32_t VmaBlockMetadata_Generic::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
+{
+ uint32_t lostAllocationCount = 0;
+ for(VmaSuballocationList::iterator it = m_Suballocations.begin();
+ it != m_Suballocations.end();
+ ++it)
+ {
+ if(it->type != VMA_SUBALLOCATION_TYPE_FREE &&
+ it->hAllocation->CanBecomeLost() &&
+ it->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
+ {
+ it = FreeSuballocation(it);
+ ++lostAllocationCount;
+ }
+ }
+ return lostAllocationCount;
+}
+
+VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData)
+{
+ for(VmaSuballocationList::iterator it = m_Suballocations.begin();
+ it != m_Suballocations.end();
+ ++it)
+ {
+ if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
+ {
+ if(!VmaValidateMagicValue(pBlockData, it->offset - VMA_DEBUG_MARGIN))
+ {
+ VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
+ return VK_ERROR_VALIDATION_FAILED_EXT;
+ }
+ if(!VmaValidateMagicValue(pBlockData, it->offset + it->size))
+ {
+ VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
+ return VK_ERROR_VALIDATION_FAILED_EXT;
+ }
+ }
+ }
+
+ return VK_SUCCESS;
+}
+
+void VmaBlockMetadata_Generic::Alloc(
+ const VmaAllocationRequest& request,
+ VmaSuballocationType type,
+ VkDeviceSize allocSize,
+ bool upperAddress,
+ VmaAllocation hAllocation)
+{
+ VMA_ASSERT(!upperAddress);
+ (void) upperAddress;
+ VMA_ASSERT(request.item != m_Suballocations.end());
+ VmaSuballocation& suballoc = *request.item;
+ // Given suballocation is a free block.
+ VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
+ // Given offset is inside this suballocation.
+ VMA_ASSERT(request.offset >= suballoc.offset);
+ const VkDeviceSize paddingBegin = request.offset - suballoc.offset;
+ VMA_ASSERT(suballoc.size >= paddingBegin + allocSize);
+ const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - allocSize;
+
+ // Unregister this free suballocation from m_FreeSuballocationsBySize and update
+ // it to become used.
+ UnregisterFreeSuballocation(request.item);
+
+ suballoc.offset = request.offset;
+ suballoc.size = allocSize;
+ suballoc.type = type;
+ suballoc.hAllocation = hAllocation;
+
+ // If there are any free bytes remaining at the end, insert new free suballocation after current one.
+ if(paddingEnd)
+ {
+ VmaSuballocation paddingSuballoc = {};
+ paddingSuballoc.offset = request.offset + allocSize;
+ paddingSuballoc.size = paddingEnd;
+ paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
+ VmaSuballocationList::iterator next = request.item;
+ ++next;
+ const VmaSuballocationList::iterator paddingEndItem =
+ m_Suballocations.insert(next, paddingSuballoc);
+ RegisterFreeSuballocation(paddingEndItem);
+ }
+
+ // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
+ if(paddingBegin)
+ {
+ VmaSuballocation paddingSuballoc = {};
+ paddingSuballoc.offset = request.offset - paddingBegin;
+ paddingSuballoc.size = paddingBegin;
+ paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
+ const VmaSuballocationList::iterator paddingBeginItem =
+ m_Suballocations.insert(request.item, paddingSuballoc);
+ RegisterFreeSuballocation(paddingBeginItem);
+ }
+
+ // Update totals.
+ m_FreeCount = m_FreeCount - 1;
+ if(paddingBegin > 0)
+ {
+ ++m_FreeCount;
+ }
+ if(paddingEnd > 0)
+ {
+ ++m_FreeCount;
+ }
+ m_SumFreeSize -= allocSize;
+}
+
+void VmaBlockMetadata_Generic::Free(const VmaAllocation allocation)
+{
+ for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
+ suballocItem != m_Suballocations.end();
+ ++suballocItem)
+ {
+ VmaSuballocation& suballoc = *suballocItem;
+ if(suballoc.hAllocation == allocation)
+ {
+ FreeSuballocation(suballocItem);
+ VMA_HEAVY_ASSERT(Validate());
+ return;
+ }
+ }
+ VMA_ASSERT(0 && "Not found!");
+}
+
+void VmaBlockMetadata_Generic::FreeAtOffset(VkDeviceSize offset)
+{
+ for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
+ suballocItem != m_Suballocations.end();
+ ++suballocItem)
+ {
+ VmaSuballocation& suballoc = *suballocItem;
+ if(suballoc.offset == offset)
+ {
+ FreeSuballocation(suballocItem);
+ return;
+ }
+ }
+ VMA_ASSERT(0 && "Not found!");
+}
+
+bool VmaBlockMetadata_Generic::ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize)
+{
+ typedef VmaSuballocationList::iterator iter_type;
+ for(iter_type suballocItem = m_Suballocations.begin();
+ suballocItem != m_Suballocations.end();
+ ++suballocItem)
+ {
+ VmaSuballocation& suballoc = *suballocItem;
+ if(suballoc.hAllocation == alloc)
+ {
+ iter_type nextItem = suballocItem;
+ ++nextItem;
+
+ // Should have been ensured on higher level.
+ VMA_ASSERT(newSize != alloc->GetSize() && newSize > 0);
+
+ // Shrinking.
+ if(newSize < alloc->GetSize())
+ {
+ const VkDeviceSize sizeDiff = suballoc.size - newSize;
+
+ // There is next item.
+ if(nextItem != m_Suballocations.end())
+ {
+ // Next item is free.
+ if(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE)
+ {
+ // Grow this next item backward.
+ UnregisterFreeSuballocation(nextItem);
+ nextItem->offset -= sizeDiff;
+ nextItem->size += sizeDiff;
+ RegisterFreeSuballocation(nextItem);
+ }
+ // Next item is not free.
+ else
+ {
+ // Create free item after current one.
+ VmaSuballocation newFreeSuballoc;
+ newFreeSuballoc.hAllocation = VK_NULL_HANDLE;
+ newFreeSuballoc.offset = suballoc.offset + newSize;
+ newFreeSuballoc.size = sizeDiff;
+ newFreeSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
+ iter_type newFreeSuballocIt = m_Suballocations.insert(nextItem, newFreeSuballoc);
+ RegisterFreeSuballocation(newFreeSuballocIt);
+
+ ++m_FreeCount;
+ }
+ }
+ // This is the last item.
+ else
+ {
+ // Create free item at the end.
+ VmaSuballocation newFreeSuballoc;
+ newFreeSuballoc.hAllocation = VK_NULL_HANDLE;
+ newFreeSuballoc.offset = suballoc.offset + newSize;
+ newFreeSuballoc.size = sizeDiff;
+ newFreeSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
+ m_Suballocations.push_back(newFreeSuballoc);
+
+ iter_type newFreeSuballocIt = m_Suballocations.end();
+ RegisterFreeSuballocation(--newFreeSuballocIt);
+
+ ++m_FreeCount;
+ }
+
+ suballoc.size = newSize;
+ m_SumFreeSize += sizeDiff;
+ }
+ // Growing.
+ else
+ {
+ const VkDeviceSize sizeDiff = newSize - suballoc.size;
+
+ // There is next item.
+ if(nextItem != m_Suballocations.end())
+ {
+ // Next item is free.
+ if(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE)
+ {
+ // There is not enough free space, including margin.
+ if(nextItem->size < sizeDiff + VMA_DEBUG_MARGIN)
+ {
+ return false;
+ }
+
+ // There is more free space than required.
+ if(nextItem->size > sizeDiff)
+ {
+ // Move and shrink this next item.
+ UnregisterFreeSuballocation(nextItem);
+ nextItem->offset += sizeDiff;
+ nextItem->size -= sizeDiff;
+ RegisterFreeSuballocation(nextItem);
+ }
+ // There is exactly the amount of free space required.
+ else
+ {
+ // Remove this next free item.
+ UnregisterFreeSuballocation(nextItem);
+ m_Suballocations.erase(nextItem);
+ --m_FreeCount;
+ }
+ }
+ // Next item is not free - there is no space to grow.
+ else
+ {
+ return false;
+ }
+ }
+ // This is the last item - there is no space to grow.
+ else
+ {
+ return false;
+ }
+
+ suballoc.size = newSize;
+ m_SumFreeSize -= sizeDiff;
+ }
+
+ // We cannot call Validate() here because alloc object is updated to new size outside of this call.
+ return true;
+ }
+ }
+ VMA_ASSERT(0 && "Not found!");
+ return false;
+}
+
+bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const
+{
+ VkDeviceSize lastSize = 0;
+ for(size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)
+ {
+ const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i];
+
+ VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE);
+ VMA_VALIDATE(it->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
+ VMA_VALIDATE(it->size >= lastSize);
+ lastSize = it->size;
+ }
+ return true;
+}
+
+bool VmaBlockMetadata_Generic::CheckAllocation(
+ uint32_t currentFrameIndex,
+ uint32_t frameInUseCount,
+ VkDeviceSize bufferImageGranularity,
+ VkDeviceSize allocSize,
+ VkDeviceSize allocAlignment,
+ VmaSuballocationType allocType,
+ VmaSuballocationList::const_iterator suballocItem,
+ bool canMakeOtherLost,
+ VkDeviceSize* pOffset,
+ size_t* itemsToMakeLostCount,
+ VkDeviceSize* pSumFreeSize,
+ VkDeviceSize* pSumItemSize) const
+{
+ VMA_ASSERT(allocSize > 0);
+ VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
+ VMA_ASSERT(suballocItem != m_Suballocations.cend());
+ VMA_ASSERT(pOffset != VMA_NULL);
+
+ *itemsToMakeLostCount = 0;
+ *pSumFreeSize = 0;
+ *pSumItemSize = 0;
+
+ if(canMakeOtherLost)
+ {
+ if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
+ {
+ *pSumFreeSize = suballocItem->size;
+ }
+ else
+ {
+ if(suballocItem->hAllocation->CanBecomeLost() &&
+ suballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
+ {
+ ++*itemsToMakeLostCount;
+ *pSumItemSize = suballocItem->size;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ // Remaining size is too small for this request: Early return.
+ if(GetSize() - suballocItem->offset < allocSize)
+ {
+ return false;
+ }
+
+ // Start from offset equal to beginning of this suballocation.
+ *pOffset = suballocItem->offset;
+
+ // Apply VMA_DEBUG_MARGIN at the beginning.
+ if(VMA_DEBUG_MARGIN > 0)
+ {
+ *pOffset += VMA_DEBUG_MARGIN;
+ }
+
+ // Apply alignment.
+ *pOffset = VmaAlignUp(*pOffset, allocAlignment);
+
+ // Check previous suballocations for BufferImageGranularity conflicts.
+ // Make bigger alignment if necessary.
+ if(bufferImageGranularity > 1)
+ {
+ bool bufferImageGranularityConflict = false;
+ VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
+ while(prevSuballocItem != m_Suballocations.cbegin())
+ {
+ --prevSuballocItem;
+ const VmaSuballocation& prevSuballoc = *prevSuballocItem;
+ if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
+ {
+ if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
+ {
+ bufferImageGranularityConflict = true;
+ break;
+ }
+ }
+ else
+ // Already on previous page.
+ break;
+ }
+ if(bufferImageGranularityConflict)
+ {
+ *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
+ }
+ }
+
+ // Now that we have final *pOffset, check if we are past suballocItem.
+ // If yes, return false - this function should be called for another suballocItem as starting point.
+ if(*pOffset >= suballocItem->offset + suballocItem->size)
+ {
+ return false;
+ }
+
+ // Calculate padding at the beginning based on current offset.
+ const VkDeviceSize paddingBegin = *pOffset - suballocItem->offset;
+
+ // Calculate required margin at the end.
+ const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
+
+ const VkDeviceSize totalSize = paddingBegin + allocSize + requiredEndMargin;
+ // Another early return check.
+ if(suballocItem->offset + totalSize > GetSize())
+ {
+ return false;
+ }
+
+ // Advance lastSuballocItem until desired size is reached.
+ // Update itemsToMakeLostCount.
+ VmaSuballocationList::const_iterator lastSuballocItem = suballocItem;
+ if(totalSize > suballocItem->size)
+ {
+ VkDeviceSize remainingSize = totalSize - suballocItem->size;
+ while(remainingSize > 0)
+ {
+ ++lastSuballocItem;
+ if(lastSuballocItem == m_Suballocations.cend())
+ {
+ return false;
+ }
+ if(lastSuballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
+ {
+ *pSumFreeSize += lastSuballocItem->size;
+ }
+ else
+ {
+ VMA_ASSERT(lastSuballocItem->hAllocation != VK_NULL_HANDLE);
+ if(lastSuballocItem->hAllocation->CanBecomeLost() &&
+ lastSuballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
+ {
+ ++*itemsToMakeLostCount;
+ *pSumItemSize += lastSuballocItem->size;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ remainingSize = (lastSuballocItem->size < remainingSize) ?
+ remainingSize - lastSuballocItem->size : 0;
+ }
+ }
+
+ // Check next suballocations for BufferImageGranularity conflicts.
+ // If conflict exists, we must mark more allocations lost or fail.
+ if(bufferImageGranularity > 1)
+ {
+ VmaSuballocationList::const_iterator nextSuballocItem = lastSuballocItem;
+ ++nextSuballocItem;
+ while(nextSuballocItem != m_Suballocations.cend())
+ {
+ const VmaSuballocation& nextSuballoc = *nextSuballocItem;
+ if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
+ {
+ if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
+ {
+ VMA_ASSERT(nextSuballoc.hAllocation != VK_NULL_HANDLE);
+ if(nextSuballoc.hAllocation->CanBecomeLost() &&
+ nextSuballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
+ {
+ ++*itemsToMakeLostCount;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+ else
+ {
+ // Already on next page.
+ break;
+ }
+ ++nextSuballocItem;
+ }
+ }
+ }
+ else
+ {
+ const VmaSuballocation& suballoc = *suballocItem;
+ VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
+
+ *pSumFreeSize = suballoc.size;
+
+ // Size of this suballocation is too small for this request: Early return.
+ if(suballoc.size < allocSize)
+ {
+ return false;
+ }
+
+ // Start from offset equal to beginning of this suballocation.
+ *pOffset = suballoc.offset;
+
+ // Apply VMA_DEBUG_MARGIN at the beginning.
+ if(VMA_DEBUG_MARGIN > 0)
+ {
+ *pOffset += VMA_DEBUG_MARGIN;
+ }
+
+ // Apply alignment.
+ *pOffset = VmaAlignUp(*pOffset, allocAlignment);
+
+ // Check previous suballocations for BufferImageGranularity conflicts.
+ // Make bigger alignment if necessary.
+ if(bufferImageGranularity > 1)
+ {
+ bool bufferImageGranularityConflict = false;
+ VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
+ while(prevSuballocItem != m_Suballocations.cbegin())
+ {
+ --prevSuballocItem;
+ const VmaSuballocation& prevSuballoc = *prevSuballocItem;
+ if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
+ {
+ if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
+ {
+ bufferImageGranularityConflict = true;
+ break;
+ }
+ }
+ else
+ // Already on previous page.
+ break;
+ }
+ if(bufferImageGranularityConflict)
+ {
+ *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
+ }
+ }
+
+ // Calculate padding at the beginning based on current offset.
+ const VkDeviceSize paddingBegin = *pOffset - suballoc.offset;
+
+ // Calculate required margin at the end.
+ const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
+
+ // Fail if requested size plus margin before and after is bigger than size of this suballocation.
+ if(paddingBegin + allocSize + requiredEndMargin > suballoc.size)
+ {
+ return false;
+ }
+
+ // Check next suballocations for BufferImageGranularity conflicts.
+ // If conflict exists, allocation cannot be made here.
+ if(bufferImageGranularity > 1)
+ {
+ VmaSuballocationList::const_iterator nextSuballocItem = suballocItem;
+ ++nextSuballocItem;
+ while(nextSuballocItem != m_Suballocations.cend())
+ {
+ const VmaSuballocation& nextSuballoc = *nextSuballocItem;
+ if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
+ {
+ if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ // Already on next page.
+ break;
+ }
+ ++nextSuballocItem;
+ }
+ }
+ }
+
+ // All tests passed: Success. pOffset is already filled.
+ return true;
+}
+
+void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item)
+{
+ VMA_ASSERT(item != m_Suballocations.end());
+ VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
+
+ VmaSuballocationList::iterator nextItem = item;
+ ++nextItem;
+ VMA_ASSERT(nextItem != m_Suballocations.end());
+ VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);
+
+ item->size += nextItem->size;
+ --m_FreeCount;
+ m_Suballocations.erase(nextItem);
+}
+
+VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem)
+{
+ // Change this suballocation to be marked as free.
+ VmaSuballocation& suballoc = *suballocItem;
+ suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
+ suballoc.hAllocation = VK_NULL_HANDLE;
+
+ // Update totals.
+ ++m_FreeCount;
+ m_SumFreeSize += suballoc.size;
+
+ // Merge with previous and/or next suballocation if it's also free.
+ bool mergeWithNext = false;
+ bool mergeWithPrev = false;
+
+ VmaSuballocationList::iterator nextItem = suballocItem;
+ ++nextItem;
+ if((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))
+ {
+ mergeWithNext = true;
+ }
+
+ VmaSuballocationList::iterator prevItem = suballocItem;
+ if(suballocItem != m_Suballocations.begin())
+ {
+ --prevItem;
+ if(prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)
+ {
+ mergeWithPrev = true;
+ }
+ }
+
+ if(mergeWithNext)
+ {
+ UnregisterFreeSuballocation(nextItem);
+ MergeFreeWithNext(suballocItem);
+ }
+
+ if(mergeWithPrev)
+ {
+ UnregisterFreeSuballocation(prevItem);
+ MergeFreeWithNext(prevItem);
+ RegisterFreeSuballocation(prevItem);
+ return prevItem;
+ }
+ else
+ {
+ RegisterFreeSuballocation(suballocItem);
+ return suballocItem;
+ }
+}
+
+void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item)
+{
+ VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
+ VMA_ASSERT(item->size > 0);
+
+ // You may want to enable this validation at the beginning or at the end of
+ // this function, depending on what do you want to check.
+ VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
+
+ if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
+ {
+ if(m_FreeSuballocationsBySize.empty())
+ {
+ m_FreeSuballocationsBySize.push_back(item);
+ }
+ else
+ {
+ VmaVectorInsertSorted<VmaSuballocationItemSizeLess>(m_FreeSuballocationsBySize, item);
+ }
+ }
+
+ //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
+}
+
+
+void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)
+{
+ VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
+ VMA_ASSERT(item->size > 0);
+
+ // You may want to enable this validation at the beginning or at the end of
+ // this function, depending on what do you want to check.
+ VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
+
+ if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
+ {
+ VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
+ m_FreeSuballocationsBySize.data(),
+ m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
+ item,
+ VmaSuballocationItemSizeLess());
+ for(size_t index = it - m_FreeSuballocationsBySize.data();
+ index < m_FreeSuballocationsBySize.size();
+ ++index)
+ {
+ if(m_FreeSuballocationsBySize[index] == item)
+ {
+ VmaVectorRemove(m_FreeSuballocationsBySize, index);
+ return;
+ }
+ VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
+ }
+ VMA_ASSERT(0 && "Not found.");
+ }
+
+ //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
+}
+
+bool VmaBlockMetadata_Generic::IsBufferImageGranularityConflictPossible(
+ VkDeviceSize bufferImageGranularity,
+ VmaSuballocationType& inOutPrevSuballocType) const
+{
+ if(bufferImageGranularity == 1 || IsEmpty())
+ {
+ return false;
+ }
+
+ VkDeviceSize minAlignment = VK_WHOLE_SIZE;
+ bool typeConflictFound = false;
+ for(VmaSuballocationList::const_iterator it = m_Suballocations.cbegin();
+ it != m_Suballocations.cend();
+ ++it)
+ {
+ const VmaSuballocationType suballocType = it->type;
+ if(suballocType != VMA_SUBALLOCATION_TYPE_FREE)
+ {
+ minAlignment = VMA_MIN(minAlignment, it->hAllocation->GetAlignment());
+ if(VmaIsBufferImageGranularityConflict(inOutPrevSuballocType, suballocType))
+ {
+ typeConflictFound = true;
+ }
+ inOutPrevSuballocType = suballocType;
+ }
+ }
+
+ return typeConflictFound || minAlignment >= bufferImageGranularity;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// class VmaBlockMetadata_Linear
+
+VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(VmaAllocator hAllocator) :
+ VmaBlockMetadata(hAllocator),
+ m_SumFreeSize(0),
+ m_Suballocations0(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
+ m_Suballocations1(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
+ m_1stVectorIndex(0),
+ m_2ndVectorMode(SECOND_VECTOR_EMPTY),
+ m_1stNullItemsBeginCount(0),
+ m_1stNullItemsMiddleCount(0),
+ m_2ndNullItemsCount(0)
<